mirror of
https://github.com/shiroyashik/sculptor.git
synced 2025-12-06 04:51:13 +03:00
Upgrade/Migrate to newer dependencies (check diff Cargo.toml)
Some checks failed
Push Dev / docker (push) Has been cancelled
Some checks failed
Push Dev / docker (push) Has been cancelled
+ Rename: api/v1 to api/sculptor
This commit is contained in:
parent
59ca04d5f8
commit
c7c3bd881f
20 changed files with 470 additions and 390 deletions
646
Cargo.lock
generated
646
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
10
Cargo.toml
10
Cargo.toml
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "sculptor"
|
||||
authors = ["Shiroyashik <shiroyashik@shsr.ru>"]
|
||||
version = "0.4.0"
|
||||
version = "0.4.1-dev"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
|
@ -32,14 +32,14 @@ walkdir = "2.5"
|
|||
indexmap = { version = "2.6", features = ["serde"] }
|
||||
zip = "2.2"
|
||||
lazy_static = "1.5"
|
||||
notify = "7.0"
|
||||
notify = "8.0"
|
||||
|
||||
# Crypto
|
||||
ring = "0.17"
|
||||
rand = "0.8"
|
||||
rand = "0.9"
|
||||
|
||||
# Web framework
|
||||
axum = { version = "0.7", features = ["ws", "macros", "http2"] }
|
||||
# Web
|
||||
axum = { version = "0.8", features = ["ws", "macros", "http2"] }
|
||||
tower-http = { version = "0.6", features = ["trace"] }
|
||||
tokio = { version = "1.41", features = ["full"] }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
## Don't touch this if you running under Docker container
|
||||
## Use port 80 for HTTP or port 443 for HTTPS.
|
||||
## If running in a Docker container, leave this as default.
|
||||
listen = "0.0.0.0:6665"
|
||||
|
||||
## Don't touch if you don't know what you're doing
|
||||
|
|
@ -71,7 +72,7 @@ customText = """
|
|||
[limitations]
|
||||
maxAvatarSize = 100 # KB
|
||||
maxAvatars = 10 # It doesn't look like Figura has any actions implemented with this?
|
||||
# P.S. And it doesn't look like the current API allows anything like that...
|
||||
## P.S. And it doesn't look like the current API allows anything like that...
|
||||
|
||||
[advancedUsers.66004548-4de5-49de-bade-9c3933d8eb97]
|
||||
username = "Shiroyashik"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
## Chef
|
||||
# FROM clux/muslrust:stable AS chef
|
||||
FROM rust:1.82.0-alpine3.20 AS chef
|
||||
FROM rust:1.84-alpine3.21 AS chef
|
||||
USER root
|
||||
RUN apk add --no-cache musl-dev libressl-dev
|
||||
RUN cargo install cargo-chef
|
||||
|
|
@ -23,7 +23,7 @@ COPY src src
|
|||
RUN cargo build --release --target x86_64-unknown-linux-musl --bin sculptor
|
||||
|
||||
## Runtime
|
||||
FROM alpine:3.20.0 AS runtime
|
||||
FROM alpine:3.21 AS runtime
|
||||
WORKDIR /app
|
||||
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/sculptor /app/sculptor
|
||||
|
||||
|
|
|
|||
19
README.md
19
README.md
|
|
@ -35,7 +35,7 @@ To run it you will need a configured reverse proxy server.
|
|||
|
||||
Make sure that the reverse proxy you are using supports WebSocket and valid certificates are used for HTTPS connections.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> [!WARNING]
|
||||
> NGINX requires additional configuration to work with websocket!
|
||||
|
||||
### Docker
|
||||
|
|
@ -57,10 +57,10 @@ See the [pre-built archives](https://github.com/shiroyashik/sculptor/releases/la
|
|||
A pre-installed Rust will be required for the build
|
||||
|
||||
```sh
|
||||
# Clone the latest release
|
||||
# Clone the pre-release
|
||||
git clone https://github.com/shiroyashik/sculptor.git
|
||||
# or a dev release
|
||||
git clone --branch dev https://github.com/shiroyashik/sculptor.git
|
||||
# or clone specific version
|
||||
git clone --depth 1 --branch v0.4.0 https://github.com/shiroyashik/sculptor.git
|
||||
# Enter the folder
|
||||
cd sculptor
|
||||
# Copy Sculptor configuration file
|
||||
|
|
@ -73,6 +73,13 @@ cargo build --release
|
|||
cargo run --release
|
||||
```
|
||||
|
||||
#### Compiling from the `master` Branch
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Installing Sculptor directly from the `master` branch is **not recommended** for most users. This branch contains pre-release code that is actively being developed and may include broken or unstable features. Additionally, using the `master` branch could potentially cause issues with data migration when upgrading to future stable releases.
|
||||
>
|
||||
> If you still choose to use the `master` branch, please be aware that you may encounter bugs or unexpected behavior. Your feedback and bug reports are highly appreciated. However, for a more stable and reliable experience, we strongly advise using the **latest official release** instead.
|
||||
|
||||
## Contributing
|
||||

|
||||
on
|
||||
|
|
@ -92,10 +99,6 @@ If you are a Rust developer, you can modify the code yourself and request a Pull
|
|||
|
||||
Glad for any help from ideas to PRs. ❤
|
||||
|
||||
#### P.S.
|
||||
|
||||
The [“master”](https://github.com/shiroyashik/sculptor/tree/master) branch contains the source code of the latest release. [“dev”](https://github.com/shiroyashik/sculptor/tree/dev) branch is used for development.
|
||||
|
||||
## License
|
||||
|
||||
The Sculptor is licensed under [GPL-3.0](LICENSE)
|
||||
|
|
|
|||
19
README.ru.md
19
README.ru.md
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
Убедитесь, что используемый вами обратный прокси-сервер поддерживает WebSocket, а для HTTPS-соединений используются действительные сертификаты.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> [!WARNING]
|
||||
> NGINX требует дополнительной настройки для работы с websocket!
|
||||
|
||||
### Docker
|
||||
|
|
@ -55,10 +55,10 @@
|
|||
Для сборки потребуется предустановленный Rust
|
||||
|
||||
```sh
|
||||
# Клонируем последний релиз
|
||||
# Клонируем пре-релиз
|
||||
git clone https://github.com/shiroyashik/sculptor.git
|
||||
# или из dev ветки
|
||||
git clone --branch dev https://github.com/shiroyashik/sculptor.git
|
||||
# или из выбранного тега
|
||||
git clone --depth 1 --branch v0.4.0 https://github.com/shiroyashik/sculptor.git
|
||||
# Переходим в репу
|
||||
cd sculptor
|
||||
# Меняем имя конфиг файлу
|
||||
|
|
@ -71,6 +71,13 @@ cargo build --release
|
|||
cargo run --release
|
||||
```
|
||||
|
||||
#### Сборка из `master` ветки
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Сборка Sculptor непосредственно из ветки `master` **не рекомендуется** для большинства пользователей. Эта ветка содержит предрелизный код, который активно разрабатывается и может содержать неработающие или нестабильные функции. Кроме того, использование ветки `master` может привести к проблемам с миграцией данных при обновлении до будущих стабильных релизов.
|
||||
>
|
||||
> Если вы все же решили использовать ветку `master`, пожалуйста, имейте в виду, что вы можете столкнуться с ошибками или некорректным поведением. Тем не менее ваши сообщения об ошибках высоко ценятся. Однако для более стабильной и надежной работы настоятельно рекомендую использовать **последний официальный релиз**.
|
||||
|
||||
## Вклад в развитие
|
||||

|
||||
в
|
||||
|
|
@ -90,10 +97,6 @@ cargo run --release
|
|||
|
||||
Буду рад любой вашей помощи! ❤
|
||||
|
||||
#### Постскриптум
|
||||
|
||||
Ветка [“master”](https://github.com/shiroyashik/sculptor/tree/master) содержит код последнего релиза. А [“dev”](https://github.com/shiroyashik/sculptor/tree/dev) ветка дря разработки.
|
||||
|
||||
## License
|
||||
|
||||
The Sculptor is licensed under [GPL-3.0](LICENSE)
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ use crate::{api::errors::internal_and_log, ApiError, ApiResult, AppState, ASSETS
|
|||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/", get(versions))
|
||||
.route("/:version", get(hashes))
|
||||
.route("/:version/*key", get(download))
|
||||
.route("/{version}", get(hashes))
|
||||
.route("/{version}/{*path}", get(download))
|
||||
}
|
||||
|
||||
async fn versions() -> ApiResult<Json<Value>> {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use anyhow::bail;
|
||||
use axum::extract::{ws::{Message, WebSocket}, State};
|
||||
use axum::{body::Bytes, extract::{ws::{Message, WebSocket}, State}};
|
||||
use dashmap::DashMap;
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{auth::Userinfo, AppState};
|
||||
|
||||
use super::{processor::*, AuthModeError, S2CMessage, C2SMessage, WSSession, SessionMessage, RADError};
|
||||
use super::{AuthModeError, C2SMessage, RADError, RecvAndDecode, S2CMessage, SessionMessage, WSSession};
|
||||
|
||||
pub async fn initial(
|
||||
ws: axum::extract::WebSocketUpgrade,
|
||||
|
|
@ -42,9 +43,8 @@ async fn handle_socket(mut ws: WebSocket, state: AppState) {
|
|||
};
|
||||
|
||||
// Starting main worker
|
||||
match main_worker(&mut session, &mut ws, &state).await {
|
||||
Ok(_) => (),
|
||||
Err(kind) => tracing::error!("[WebSocket] Main worker halted due to {}.", kind),
|
||||
if let Err(kind) = main_worker(&mut session, &mut ws, &state).await {
|
||||
tracing::error!("[WebSocket] Main worker halted due to {}.", kind)
|
||||
}
|
||||
|
||||
for (_, handle) in session.sub_workers_aborthandles {
|
||||
|
|
@ -61,9 +61,10 @@ async fn handle_socket(mut ws: WebSocket, state: AppState) {
|
|||
}
|
||||
|
||||
// Closing connection
|
||||
if let Err(kind) = ws.close().await { tracing::trace!("[WebSocket] Closing fault: {}", kind) }
|
||||
if let Err(kind) = ws.send(Message::Close(None)).await { tracing::trace!("[WebSocket] Closing fault: {}", kind) }
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(nickname = %session.user.nickname))]
|
||||
async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppState) -> anyhow::Result<()> {
|
||||
tracing::debug!("WebSocket control for {} is transferred to the main worker", session.user.nickname);
|
||||
loop {
|
||||
|
|
@ -90,7 +91,7 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
|
|||
|
||||
// Echo check
|
||||
if echo {
|
||||
ws.send(Message::Binary(s2c_ping.clone())).await?
|
||||
ws.send(Message::Binary(s2c_ping.clone().into())).await?
|
||||
}
|
||||
// Sending to others
|
||||
let _ = session.subs_tx.send(s2c_ping);
|
||||
|
|
@ -127,7 +128,7 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
|
|||
let internal_msg = internal_msg.ok_or(anyhow::anyhow!("Unexpected error! Session channel broken!"))?;
|
||||
match internal_msg {
|
||||
SessionMessage::Ping(msg) => {
|
||||
ws.send(Message::Binary(msg)).await?
|
||||
ws.send(Message::Binary(msg.into())).await?
|
||||
},
|
||||
SessionMessage::Banned => {
|
||||
let _ = ban_action(ws).await
|
||||
|
|
@ -169,7 +170,7 @@ async fn authenticate(socket: &mut WebSocket, state: &AppState) -> Result<Userin
|
|||
let token = String::from_utf8(token.to_vec()).map_err(|_| AuthModeError::ConvertError)?;
|
||||
match state.user_manager.get(&token) {
|
||||
Some(user) => {
|
||||
if socket.send(Message::Binary(S2CMessage::Auth.into())).await.is_err() {
|
||||
if socket.send(Message::Binary(Bytes::from(Into::<Vec<u8>>::into(S2CMessage::Auth)))).await.is_err() {
|
||||
Err(AuthModeError::SendError)
|
||||
} else if !user.banned {
|
||||
Ok(user.clone())
|
||||
|
|
@ -204,7 +205,7 @@ async fn authenticate(socket: &mut WebSocket, state: &AppState) -> Result<Userin
|
|||
}
|
||||
|
||||
async fn ban_action(ws: &mut WebSocket) -> anyhow::Result<()> {
|
||||
ws.send(Message::Binary(S2CMessage::Toast(2, "You're banned!".to_string(), None).into())).await?;
|
||||
ws.send(Message::Binary(Into::<Vec<u8>>::into(S2CMessage::Toast(2, "You're banned!".to_string(), None)).into())).await?;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(6)).await;
|
||||
ws.send(Message::Close(Some(axum::extract::ws::CloseFrame { code: 4001, reason: "You're banned!".into() }))).await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// mod websocket;
|
||||
mod handler;
|
||||
mod processor;
|
||||
mod types;
|
||||
|
||||
// pub use websocket::*;
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
use axum::extract::ws::{Message, WebSocket};
|
||||
|
||||
use super::{C2SMessage, RADError};
|
||||
|
||||
pub trait RecvAndDecode {
|
||||
async fn recv_and_decode(&mut self) -> Result<C2SMessage, RADError>;
|
||||
}
|
||||
|
||||
impl RecvAndDecode for WebSocket {
|
||||
async fn recv_and_decode(&mut self) -> Result<C2SMessage, RADError> {
|
||||
if let Some(msg) = self.recv().await {
|
||||
match msg {
|
||||
Ok(msg) => {
|
||||
match msg {
|
||||
Message::Close(frame) => Err(RADError::Close(frame.map(|f| format!("code: {}, reason: {}", f.code, f.reason)))),
|
||||
_ => {
|
||||
match C2SMessage::try_from(msg.clone().into_data().as_slice()) {
|
||||
Ok(decoded) => Ok(decoded),
|
||||
Err(e) => {
|
||||
Err(RADError::DecodeError(e, faster_hex::hex_string(&msg.into_data())))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => Err(RADError::WebSocketError(e)),
|
||||
}
|
||||
} else {
|
||||
Err(RADError::StreamClosed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,3 +7,23 @@ pub use session::*;
|
|||
pub use errors::*;
|
||||
pub use c2s::*;
|
||||
pub use s2c::*;
|
||||
|
||||
use axum::extract::ws::{Message, WebSocket};
|
||||
|
||||
pub trait RecvAndDecode {
|
||||
async fn recv_and_decode(&mut self) -> Result<C2SMessage, RADError>;
|
||||
}
|
||||
|
||||
impl RecvAndDecode for WebSocket {
|
||||
async fn recv_and_decode(&mut self) -> Result<C2SMessage, RADError> {
|
||||
let msg = self.recv().await.ok_or(RADError::StreamClosed)??;
|
||||
|
||||
if let Message::Close(frame) = msg {
|
||||
return Err(RADError::Close(frame.map(|f| format!("code: {}, reason: {}", f.code, f.reason))));
|
||||
}
|
||||
|
||||
let data = msg.into_data();
|
||||
C2SMessage::try_from(data.as_ref())
|
||||
.map_err(|e| RADError::DecodeError(e, faster_hex::hex_string(&data)))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
pub mod figura;
|
||||
pub mod v1;
|
||||
pub mod sculptor;
|
||||
pub mod errors;
|
||||
|
|
@ -14,8 +14,8 @@ pub fn router(limit: usize) -> Router<AppState> {
|
|||
.route("/user/list", get(users::list))
|
||||
.route("/user/sessions", get(users::list_sessions))
|
||||
.route("/user/create", post(users::create_user))
|
||||
.route("/user/:uuid/ban", post(users::ban))
|
||||
.route("/user/:uuid/unban", post(users::unban))
|
||||
.route("/avatar/:uuid", put(avatars::upload_avatar).layer(DefaultBodyLimit::max(limit)))
|
||||
.route("/avatar/:uuid", delete(avatars::delete_avatar))
|
||||
.route("/user/{uuid}/ban", post(users::ban))
|
||||
.route("/user/{uuid}/unban", post(users::unban))
|
||||
.route("/avatar/{uuid}", put(avatars::upload_avatar).layer(DefaultBodyLimit::max(limit)))
|
||||
.route("/avatar/{uuid}", delete(avatars::delete_avatar))
|
||||
}
|
||||
|
|
@ -2,11 +2,11 @@ use std::sync::Arc;
|
|||
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{
|
||||
async_trait, extract::{FromRequestParts, State}, http::{request::Parts, StatusCode}
|
||||
extract::{FromRequestParts, OptionalFromRequestParts, State}, http::{request::Parts, StatusCode}
|
||||
};
|
||||
use dashmap::DashMap;
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
use tracing::{debug, error, instrument, trace, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{ApiError, ApiResult, AppState, TIMEOUT, USER_AGENT};
|
||||
|
|
@ -18,7 +18,7 @@ use super::types::*;
|
|||
pub struct Token(pub String);
|
||||
|
||||
impl Token {
|
||||
pub async fn check_auth(self, state: &AppState) -> ApiResult<()> {
|
||||
pub async fn _check_auth(self, state: &AppState) -> ApiResult<()> {
|
||||
if let Some(user) = state.user_manager.get(&self.0) {
|
||||
if !user.banned {
|
||||
Ok(())
|
||||
|
|
@ -31,7 +31,6 @@ impl Token {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<S> FromRequestParts<S> for Token
|
||||
where
|
||||
S: Send + Sync,
|
||||
|
|
@ -50,6 +49,22 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> OptionalFromRequestParts<S> for Token
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = StatusCode; // Not required
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Option<Self>, Self::Rejection> {
|
||||
let token = parts
|
||||
.headers
|
||||
.get("token")
|
||||
.and_then(|value| value.to_str().ok());
|
||||
trace!(token = ?token);
|
||||
Ok(token.map(|t| Self(t.to_string())))
|
||||
}
|
||||
}
|
||||
// End Extractor
|
||||
|
||||
// Work with external APIs
|
||||
|
|
@ -259,16 +274,33 @@ impl UManager {
|
|||
}
|
||||
// End of User manager
|
||||
|
||||
#[axum::debug_handler]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn check_auth(
|
||||
token: Option<Token>,
|
||||
State(state): State<AppState>,
|
||||
) -> ApiResult<&'static str> {
|
||||
debug!("Checking auth actuality...");
|
||||
match token {
|
||||
Some(token) => {
|
||||
token.check_auth(&state).await?;
|
||||
match state.user_manager.get(&token.0) {
|
||||
Some(user) => {
|
||||
if user.banned {
|
||||
debug!(nickname = user.nickname, status = "banned", "Token owner is banned");
|
||||
Err(ApiError::Unauthorized)
|
||||
} else {
|
||||
debug!(nickname = user.nickname, status = "ok", "Token verified successfully");
|
||||
Ok("ok")
|
||||
},
|
||||
None => Err(ApiError::BadRequest),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
debug!(token = token.0, status = "invalid", "Invalid token");
|
||||
Err(ApiError::Unauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
debug!(status = "not provided", "Token not provided");
|
||||
Err(ApiError::BadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/main.rs
11
src/main.rs
|
|
@ -107,7 +107,7 @@ async fn main() -> Result<()> {
|
|||
},
|
||||
}
|
||||
|
||||
// 4. Starting an app() that starts to serve. If app() returns true, the sculptor will be restarted. for future
|
||||
// 4. Starting an app() that starts to serve. If app() returns true, the sculptor will be restarted. TODO: for future
|
||||
loop {
|
||||
if !app().await? {
|
||||
break;
|
||||
|
|
@ -174,6 +174,7 @@ async fn app() -> Result<bool> {
|
|||
Arc::clone(&state.session),
|
||||
Arc::clone(&state.config)
|
||||
));
|
||||
// Blacklist auto update
|
||||
if state.config.read().await.mc_folder.exists() {
|
||||
tokio::spawn(update_bans_from_minecraft(
|
||||
state.config.read().await.mc_folder.clone(),
|
||||
|
|
@ -185,13 +186,13 @@ async fn app() -> Result<bool> {
|
|||
let api = Router::new()
|
||||
.nest("//auth", api_auth::router()) // => /api//auth ¯\_(ツ)_/¯
|
||||
.nest("//assets", api_assets::router())
|
||||
.nest("/v1", api::v1::router(limit))
|
||||
.nest("/v1", api::sculptor::router(limit))
|
||||
.route("/limits", get(api_info::limits))
|
||||
.route("/version", get(api_info::version))
|
||||
.route("/motd", get(api_info::motd))
|
||||
.route("/equip", post(api_profile::equip_avatar))
|
||||
.route("/:uuid", get(api_profile::user_info))
|
||||
.route("/:uuid/avatar", get(api_profile::download_avatar))
|
||||
.route("/{uuid}", get(api_profile::user_info))
|
||||
.route("/{uuid}/avatar", get(api_profile::download_avatar))
|
||||
.route("/avatar", put(api_profile::upload_avatar).layer(DefaultBodyLimit::max(limit)))
|
||||
.route("/avatar", delete(api_profile::delete_avatar));
|
||||
|
||||
|
|
@ -205,9 +206,11 @@ async fn app() -> Result<bool> {
|
|||
|
||||
let listener = tokio::net::TcpListener::bind(listen).await?;
|
||||
tracing::info!("Listening on {}", listener.local_addr()?);
|
||||
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(shutdown_signal())
|
||||
.await?;
|
||||
|
||||
tracing::info!("Serve stopped.");
|
||||
Ok(false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{fs::File, io::Read, path::{Path, PathBuf}, sync::Arc};
|
|||
use notify::{Event, Watcher};
|
||||
use tokio::{io::AsyncReadExt, sync::RwLock};
|
||||
use base64::prelude::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rand::{rng, Rng};
|
||||
use ring::digest::{self, digest};
|
||||
use uuid::Uuid;
|
||||
use chrono::prelude::*;
|
||||
|
|
@ -11,8 +11,8 @@ use chrono::prelude::*;
|
|||
use crate::{auth::Userinfo, state::{BannedPlayer, Config}, UManager};
|
||||
|
||||
pub fn rand() -> [u8; 50] {
|
||||
let mut rng = thread_rng();
|
||||
let distr = rand::distributions::Uniform::new_inclusive(0, 255);
|
||||
let mut rng = rng();
|
||||
let distr = rand::distr::Uniform::new_inclusive(0, 255).expect("rand() failure.");
|
||||
let mut nums: [u8; 50] = [0u8; 50];
|
||||
for x in &mut nums {
|
||||
*x = rng.sample(distr);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue