mirror of
https://github.com/shiroyashik/sculptor.git
synced 2025-12-06 13:01:12 +03:00
Pre-release
I don't remember exactly what was done in this commit anymore, so don't be discourteous
This commit is contained in:
parent
b280da2742
commit
59440154c1
18 changed files with 1016 additions and 185 deletions
65
src/auth.rs
65
src/auth.rs
|
|
@ -1,7 +1,9 @@
|
|||
use anyhow::anyhow;
|
||||
use axum::{async_trait, debug_handler, extract::{FromRequestParts, Query, State}, http::{request::Parts, StatusCode}, response::{IntoResponse, Response}, routing::get, Router};
|
||||
use log::debug;
|
||||
use log::{debug, info, trace};
|
||||
use serde::Deserialize;
|
||||
use ring::digest::{self, digest};
|
||||
use uuid::Uuid;
|
||||
use crate::utils::*;
|
||||
|
||||
use crate::AppState;
|
||||
|
|
@ -18,7 +20,7 @@ pub fn router() -> Router<AppState> {
|
|||
struct Id {username: String}
|
||||
|
||||
#[debug_handler]
|
||||
async fn id( // 1 этап аутентификации
|
||||
async fn id( // First stage of authentication
|
||||
Query(query): Query<Id>,
|
||||
State(state): State<AppState>,
|
||||
) -> String {
|
||||
|
|
@ -32,17 +34,18 @@ async fn id( // 1 этап аутентификации
|
|||
struct Verify {id: String}
|
||||
|
||||
#[debug_handler]
|
||||
async fn verify( // 2 этап аутентификации
|
||||
async fn verify( // Second stage of authentication
|
||||
Query(query): Query<Verify>,
|
||||
State(state): State<AppState>,
|
||||
) -> String {
|
||||
let server_id = query.id.clone();
|
||||
let username = state.pending.lock().await.remove(&server_id).unwrap().1;
|
||||
if let Some(uuid) = elyby_api::has_joined(&server_id, &username).await.unwrap() {
|
||||
if let Some((uuid, auth_system)) = has_joined(&server_id, &username).await.unwrap() {
|
||||
info!("[Authorization] {username} logged in using {auth_system:?}");
|
||||
let authenticated = state.authenticated.lock().await;
|
||||
let link = state.authenticated_link.lock().await;
|
||||
authenticated.insert(server_id.clone(), crate::Userinfo { username, uuid });
|
||||
link.insert(uuid, crate::AuthenticatedLink(server_id.clone()));
|
||||
// let link = state.authenticated_link.lock().await; // // Реализация поиска пользователя в HashMap по UUID
|
||||
authenticated.insert(server_id.clone(), crate::Userinfo { username, uuid, auth_system });
|
||||
// link.insert(uuid, crate::AuthenticatedLink(server_id.clone())); // Реализация поиска пользователя в HashMap по UUID
|
||||
return format!("{server_id}")
|
||||
} else {
|
||||
return String::from("failed to verify")
|
||||
|
|
@ -88,11 +91,55 @@ where
|
|||
.headers
|
||||
.get("token")
|
||||
.and_then(|value| value.to_str().ok());
|
||||
debug!("[Extractor Token] Данные: {token:?}");
|
||||
trace!("[Extractor Token] Data: {token:?}");
|
||||
match token {
|
||||
Some(token) => Ok(Self(Some(token.to_string()))),
|
||||
None => Ok(Self(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Конец экстрактора
|
||||
// Конец экстрактора
|
||||
|
||||
// Work with external APIs
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AuthSystem {
|
||||
ElyBy,
|
||||
Mojang,
|
||||
}
|
||||
|
||||
pub async fn has_joined(server_id: &str, username: &str) -> anyhow::Result<Option<(Uuid, AuthSystem)>> {
|
||||
let client = reqwest::Client::new();
|
||||
tokio::select! {
|
||||
Ok(Some(res)) = async {
|
||||
let res = client.clone().get(
|
||||
format!("http://minecraft.ely.by/session/hasJoined?serverId={server_id}&username={username}")).send().await?;
|
||||
debug!("{res:?}");
|
||||
match res.status().as_u16() {
|
||||
200 => {
|
||||
let json = serde_json::from_str::<serde_json::Value>(&res.text().await?)?;
|
||||
let uuid = Uuid::parse_str(json["id"].as_str().unwrap())?;
|
||||
Ok(Some((uuid, AuthSystem::ElyBy)))
|
||||
},
|
||||
401 => Ok(None),
|
||||
_ => Err(anyhow::anyhow!("Unknown code: {}", res.status().as_u16()))
|
||||
}
|
||||
} => {Ok(Some(res))}
|
||||
Ok(Some(res)) = async {
|
||||
let res = client.clone().get(
|
||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?serverId={server_id}&username={username}")).send().await?;
|
||||
debug!("{res:?}");
|
||||
match res.status().as_u16() {
|
||||
200 => {
|
||||
let json = serde_json::from_str::<serde_json::Value>(&res.text().await?)?;
|
||||
let uuid = Uuid::parse_str(json["id"].as_str().unwrap())?;
|
||||
Ok(Some((uuid, AuthSystem::Mojang)))
|
||||
},
|
||||
204 => Ok(None),
|
||||
_ => Err(anyhow::anyhow!("Unknown code: {}", res.status().as_u16()))
|
||||
}
|
||||
} => {Ok(Some(res))}
|
||||
else => {Err(anyhow!("Something went wrong in external apis request process"))}
|
||||
}
|
||||
}
|
||||
// End of work with external APIs
|
||||
23
src/config.rs
Normal file
23
src/config.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use std::{io::Read, path::PathBuf};
|
||||
|
||||
use serde::Deserialize;
|
||||
use toml::Table;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Config {
|
||||
pub listen: String,
|
||||
pub motd: String,
|
||||
pub advanced_users: Table,
|
||||
}
|
||||
|
||||
|
||||
impl Config {
|
||||
pub fn parse(path: PathBuf) -> Self {
|
||||
let mut file = std::fs::File::open(path).expect("Access denied or file doesn't exists!");
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data).unwrap();
|
||||
|
||||
toml::from_str(&data).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,8 @@ use serde_json::{json, Value};
|
|||
|
||||
pub async fn version() -> Json<Value> {
|
||||
Json(json!({
|
||||
"release": "1.7.1",
|
||||
"prerelease": "1.7.2"
|
||||
"release": "0.1.4",
|
||||
"prerelease": "0.1.4"
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ pub async fn limits() -> Json<Value> {
|
|||
Json(json!({
|
||||
"rate": {
|
||||
"pingSize": 1024,
|
||||
"pingRate": 32, // TODO: Проверить
|
||||
"pingRate": 32,
|
||||
"equip": 1,
|
||||
"download": 50,
|
||||
"upload": 1
|
||||
|
|
|
|||
53
src/main.rs
53
src/main.rs
|
|
@ -30,24 +30,27 @@ use profile as api_profile;
|
|||
// Utils
|
||||
mod utils;
|
||||
|
||||
// Config
|
||||
mod config;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Userinfo {
|
||||
username: String,
|
||||
uuid: Uuid,
|
||||
auth_system: api_auth::AuthSystem,
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AuthenticatedLink(String);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppState {
|
||||
// Пользователи с незаконченной аутентификацией
|
||||
// Users with incomplete authentication
|
||||
pending: Arc<Mutex<DashMap<String, String>>>, // <SHA1 serverId, USERNAME>
|
||||
// Аутентифицированные пользователи
|
||||
authenticated: Arc<Mutex<DashMap<String, Userinfo>>>, // <SHA1 serverId, Userinfo> NOTE: В будущем попробовать в отдельной ветке LockRw
|
||||
authenticated_link: Arc<Mutex<DashMap<Uuid, AuthenticatedLink>>>, // Получаем токен из Uuid
|
||||
// Трансляции Ping'ов для WebSocket соединений
|
||||
// Authenticated users
|
||||
authenticated: Arc<Mutex<DashMap<String, Userinfo>>>, // <SHA1 serverId, Userinfo> NOTE: In the future, try it in a separate LockRw branch
|
||||
// Ping broadcasts for WebSocket connections
|
||||
broadcasts: Arc<Mutex<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>>,
|
||||
// Advanced configured users
|
||||
advanced_users: Arc<Mutex<toml::Table>>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -68,41 +71,55 @@ async fn main() -> Result<()> {
|
|||
message
|
||||
))
|
||||
})
|
||||
.level(log::LevelFilter::Debug)
|
||||
.level(log::LevelFilter::Info)
|
||||
// .level_for("hyper", log::LevelFilter::Info)
|
||||
.chain(std::io::stdout())
|
||||
.chain(fern::log_file("output.log")?)
|
||||
.apply()?;
|
||||
|
||||
// Конфиг
|
||||
// TODO: Сделать Config.toml для установки настроек сервера
|
||||
let listen = "0.0.0.0:6665";
|
||||
// Config
|
||||
let config = config::Config::parse("Config.toml".into());
|
||||
let listen = config.listen.as_str();
|
||||
|
||||
// Состояние
|
||||
// TODO: Сделать usersStorage.toml как "временная" замена базе данных.
|
||||
// State
|
||||
let state = AppState {
|
||||
pending: Arc::new(Mutex::new(DashMap::new())),
|
||||
authenticated: Arc::new(Mutex::new(DashMap::new())),
|
||||
authenticated_link: Arc::new(Mutex::new(DashMap::new())),
|
||||
broadcasts: Arc::new(Mutex::new(DashMap::new())),
|
||||
advanced_users: Arc::new(Mutex::new(config.advanced_users)),
|
||||
};
|
||||
|
||||
// Automatic update of advanced_users while the server is running
|
||||
let advanced_users = state.advanced_users.clone();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||
|
||||
let new_config = config::Config::parse("Config.toml".into()).advanced_users;
|
||||
let mut config = advanced_users.lock().await;
|
||||
|
||||
if new_config != *config {
|
||||
*config = new_config;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let api = Router::new()
|
||||
.nest(
|
||||
"//auth",
|
||||
api_auth::router()
|
||||
) // check Auth; return 200 OK if token valid
|
||||
)
|
||||
.route(
|
||||
"/limits",
|
||||
get(api_info::limits)
|
||||
) // Need more info :( TODO:
|
||||
) // TODO:
|
||||
.route(
|
||||
"/version",
|
||||
get(api_info::version),
|
||||
)
|
||||
.route(
|
||||
"/motd",
|
||||
get(|| async { "\"written by an black cat :3 mew\"" }),
|
||||
get(|| async { config.motd }),
|
||||
)
|
||||
.route(
|
||||
"/equip",
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
use anyhow_http::{http_error_ret, response::Result};
|
||||
use axum::{body::Bytes, debug_handler, extract::{Path, State}, Json};
|
||||
use log::{debug, warn};
|
||||
use serde_json::{json, Value};
|
||||
use tokio::{fs, io::{AsyncReadExt, BufWriter, self}};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{utils::{calculate_file_sha256, format_uuid}, auth::Token, AppState};
|
||||
use crate::{auth::Token, utils::{calculate_file_sha256, format_uuid, get_correct_array}, ws::S2CMessage, AppState};
|
||||
|
||||
#[debug_handler]
|
||||
pub async fn user_info(
|
||||
Path(uuid): Path<Uuid>,
|
||||
State(_state): State<AppState>, // FIXME: Variable doesn't using!
|
||||
State(state): State<AppState>, // FIXME: Variable doesn't using!
|
||||
) -> Json<Value> {
|
||||
log::info!("Получение информации для {}",uuid);
|
||||
log::info!("Receiving profile information for {}",uuid);
|
||||
|
||||
let formatted_uuid = format_uuid(uuid);
|
||||
|
||||
|
|
@ -23,13 +24,23 @@ pub async fn user_info(
|
|||
"equipped": [],
|
||||
"lastUsed": "2024-05-11T22:20:48.884Z",
|
||||
"equippedBadges": {
|
||||
"special": [1,1,1,1,1,1],
|
||||
"pride": [0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
||||
"special": [0,0,0,0,0,0],
|
||||
"pride": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
||||
},
|
||||
"version": "0.1.4+1.20.1",
|
||||
"banned": false
|
||||
});
|
||||
|
||||
if let Some(settings) = state.advanced_users.lock().await.get(&formatted_uuid) {
|
||||
let pride = get_correct_array(settings.get("pride").unwrap());
|
||||
let special = get_correct_array(settings.get("special").unwrap());
|
||||
let badges = user_info_response.get_mut("equippedBadges").and_then(Value::as_object_mut).unwrap();
|
||||
badges.append(json!({
|
||||
"special": special,
|
||||
"pride": pride
|
||||
}).as_object_mut().unwrap())
|
||||
}
|
||||
|
||||
if fs::metadata(&avatar_file).await.is_ok() {
|
||||
if let Some(equipped) = user_info_response.get_mut("equipped").and_then(Value::as_array_mut){
|
||||
match calculate_file_sha256(&avatar_file){
|
||||
|
|
@ -54,11 +65,11 @@ pub async fn download_avatar(
|
|||
Path(uuid): Path<Uuid>,
|
||||
) -> Result<Vec<u8>> {
|
||||
let uuid = format_uuid(uuid);
|
||||
log::info!("Запрашиваем аватар: {}", uuid);
|
||||
log::info!("Requesting an avatar: {}", uuid);
|
||||
let mut file = if let Ok(file) = fs::File::open(format!("avatars/{}.moon", uuid)).await {
|
||||
file
|
||||
} else {
|
||||
http_error_ret!(NOT_FOUND, "Ошибка! Данный аватар не существует!");
|
||||
http_error_ret!(NOT_FOUND, "Error! This avatar does not exist!");
|
||||
};
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer).await?;
|
||||
|
|
@ -81,12 +92,12 @@ pub async fn upload_avatar(
|
|||
|
||||
let token = match token {
|
||||
Some(t) => t,
|
||||
None => http_error_ret!(UNAUTHORIZED, "Ошибка аутентификации!"),
|
||||
None => http_error_ret!(UNAUTHORIZED, "Authentication error!"),
|
||||
};
|
||||
let userinfos = state.authenticated.lock().await;
|
||||
|
||||
if let Some(user_info) = userinfos.get(token.as_str()) {
|
||||
log::info!("{} ({}) пытается загрузить аватар",user_info.uuid,user_info.username);
|
||||
log::info!("{} ({}) trying to upload an avatar",user_info.uuid,user_info.username);
|
||||
let avatar_file = format!("avatars/{}.moon",user_info.uuid);
|
||||
let mut file = BufWriter::new(fs::File::create(&avatar_file).await?);
|
||||
io::copy(&mut request_data.as_ref(), &mut file).await?;
|
||||
|
|
@ -94,7 +105,15 @@ pub async fn upload_avatar(
|
|||
Ok(format!("ok"))
|
||||
}
|
||||
|
||||
pub async fn equip_avatar() -> String {
|
||||
pub async fn equip_avatar(
|
||||
Token(token): Token,
|
||||
State(state): State<AppState>,
|
||||
) -> String {
|
||||
debug!("[API] S2C : Equip");
|
||||
let uuid = state.authenticated.lock().await.get(&token.unwrap()).unwrap().uuid;
|
||||
if state.broadcasts.lock().await.get(&uuid).unwrap().send(S2CMessage::Event(uuid).to_vec()).is_err() {
|
||||
warn!("[WebSocket] Failed to send Event! Maybe there is no one to send") // FIXME: Засунуть в Handler
|
||||
};
|
||||
format!("ok")
|
||||
}
|
||||
|
||||
|
|
@ -104,11 +123,11 @@ pub async fn delete_avatar(
|
|||
) -> Result<String> {
|
||||
let token = match token {
|
||||
Some(t) => t,
|
||||
None => http_error_ret!(UNAUTHORIZED, "Ошибка аутентификации!"),
|
||||
None => http_error_ret!(UNAUTHORIZED, "Authentication error!"),
|
||||
};
|
||||
let userinfos = state.authenticated.lock().await;
|
||||
if let Some(user_info) = userinfos.get(token.as_str()) {
|
||||
log::info!("{} ({}) пытается удалить аватар",user_info.uuid,user_info.username);
|
||||
log::info!("{} ({}) is trying to delete the avatar",user_info.uuid,user_info.username);
|
||||
let avatar_file = format!("avatars/{}.moon",user_info.uuid);
|
||||
fs::remove_file(avatar_file).await?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ pub fn _generate_hex_string(length: usize) -> String { // FIXME: Variable doesn'
|
|||
hex::encode(random_bytes)
|
||||
}
|
||||
|
||||
pub fn get_correct_array(value: &toml::Value) -> Vec<u8> {
|
||||
// let res: Vec<u8>;
|
||||
value.as_array().unwrap().iter().map(move |x| x.as_integer().unwrap() as u8).collect()
|
||||
}
|
||||
|
||||
pub fn format_uuid(uuid: Uuid) -> String {
|
||||
// let uuid = Uuid::parse_str(&uuid)?; TODO: Вероятно format_uuid стоит убрать
|
||||
// .map_err(|_| tide::Error::from_str(StatusCode::InternalServerError, "Failed to parse UUID"))?;
|
||||
|
|
|
|||
|
|
@ -88,19 +88,4 @@ impl<'a> Into<Box<[u8]>> for C2SMessage<'a> {
|
|||
};
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> C2SMessage<'a> {
|
||||
pub fn ping_data(self) -> Box<[u8]> {
|
||||
use std::iter;
|
||||
let a: Box<[u8]> = match self {
|
||||
C2SMessage::Ping(p, s, d) => iter::empty()
|
||||
.chain(p.to_be_bytes())
|
||||
.chain(iter::once(s.into()))
|
||||
.chain(d.into_iter().copied())
|
||||
.collect(),
|
||||
_ => todo!() // FIXME: Это всё нихеровых размеров костыль!
|
||||
};
|
||||
a
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||
|
||||
use axum::{extract::{ws::{Message, WebSocket}, State, WebSocketUpgrade}, response::Response};
|
||||
use dashmap::DashMap;
|
||||
use log::{debug, error, info, log, warn};
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use tokio::sync::{broadcast::{self, Receiver}, mpsc, Notify};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
|
@ -36,18 +36,18 @@ impl WSOwner {
|
|||
}
|
||||
|
||||
async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
||||
debug!("[WebSocket] New unknown connection!");
|
||||
let mut owner = WSOwner(None);
|
||||
let cutoff: DashMap<Uuid, Arc<Notify>> = DashMap::new();
|
||||
let (mtx, mut mrx) = mpsc::channel(64);
|
||||
// let (bctx, mut _bcrx) = broadcast::channel(64);
|
||||
let mut bctx: Option<broadcast::Sender<Vec<u8>>> = None;
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some(msg) = socket.recv() => {
|
||||
debug!("[WebSocket{}] Raw: {msg:?}", owner.name());
|
||||
trace!("[WebSocket{}] Raw: {msg:?}", owner.name());
|
||||
let mut msg = if let Ok(msg) = msg {
|
||||
if let Message::Close(_) = msg {
|
||||
info!("[WebSocket{}] Соединение удачно закрыто!", owner.name());
|
||||
info!("[WebSocket{}] Connection successfully closed!", owner.name());
|
||||
if let Some(u) = owner.0 {
|
||||
remove_broadcast(state.broadcasts.clone(), u.uuid).await;
|
||||
}
|
||||
|
|
@ -55,8 +55,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
}
|
||||
msg
|
||||
} else {
|
||||
// если попали сюда, значит вероятнее всего клиент отключился
|
||||
warn!("[WebSocket{}] Ошибка получения! Соединение разорвано!", owner.name());
|
||||
warn!("[WebSocket{}] Receive error! Connection terminated!", owner.name());
|
||||
if let Some(u) = owner.0 {
|
||||
remove_broadcast(state.broadcasts.clone(), u.uuid).await;
|
||||
}
|
||||
|
|
@ -69,7 +68,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
let newmsg = match C2SMessage::try_from(msg_array) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!("[WebSocket{}] Это сообщение не от Figura! {e:?}", owner.name());
|
||||
error!("[WebSocket{}] This message is not from Figura! {e:?}", owner.name());
|
||||
if let Some(u) = owner.0 {
|
||||
remove_broadcast(state.broadcasts.clone(), u.uuid).await;
|
||||
}
|
||||
|
|
@ -77,11 +76,11 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
},
|
||||
};
|
||||
|
||||
info!("[WebSocket{}] Данные: {newmsg:?}", owner.name());
|
||||
debug!("[WebSocket{}] Raw: {newmsg:?}", owner.name());
|
||||
|
||||
match newmsg {
|
||||
C2SMessage::Token(token) => { // FIXME: Написать переменную спомощью которой бужет проверяться авторизовался ли пользователь или нет
|
||||
info!("[WebSocket{}] Token", owner.name());
|
||||
debug!("[WebSocket{}] C2S : Token", owner.name());
|
||||
let token = String::from_utf8(token.to_vec()).unwrap();
|
||||
let authenticated = state.authenticated.lock().await;
|
||||
match authenticated.get(&token) { // Принцип прост: если токена в authenticated нет, значит это trash
|
||||
|
|
@ -102,7 +101,8 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
};
|
||||
},
|
||||
None => {
|
||||
warn!("[WebSocket] Ошибка авторизации! Соединение разорвано! {token}");
|
||||
warn!("[WebSocket] Authenticaton error! Connection terminated!");
|
||||
debug!("[WebSocket] Tried to log in with {token}"); // Tried to log in with token: {token}
|
||||
if let Some(u) = owner.0 {
|
||||
remove_broadcast(state.broadcasts.clone(), u.uuid).await;
|
||||
}
|
||||
|
|
@ -111,17 +111,16 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
};
|
||||
},
|
||||
C2SMessage::Ping(_, _, _) => {
|
||||
info!("[WebSocket{}] Ping", owner.name());
|
||||
debug!("[WebSocket{}] C2S : Ping", owner.name());
|
||||
let data = into_s2c_ping(msg_vec, owner.0.clone().unwrap().uuid);
|
||||
info!("Im gotcha homie! {:?}", data);
|
||||
match bctx.clone().unwrap().send(data) {
|
||||
Ok(_) => (),
|
||||
Err(_) => error!("[WebSocket{}] Не удалось отправить Пинг!", owner.name()),
|
||||
Err(_) => warn!("[WebSocket{}] Failed to send Ping! Maybe there's no one to send", owner.name()),
|
||||
};
|
||||
continue;
|
||||
},
|
||||
C2SMessage::Sub(uuid) => { // FIXME: Исключить возможность использования SUB без авторизации
|
||||
info!("[WebSocket{}] Sub", owner.name());
|
||||
debug!("[WebSocket{}] C2S : Sub", owner.name());
|
||||
// Отбрасываю Sub на самого себя
|
||||
if uuid == owner.0.clone().unwrap().uuid {
|
||||
continue;
|
||||
|
|
@ -131,7 +130,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
let rx = match broadcast.get(&uuid) {
|
||||
Some(rx) => rx.to_owned().subscribe(),
|
||||
None => {
|
||||
warn!("Внимание! Необходимый UUID для подписки не найден!");
|
||||
warn!("[WebSocket{}] Attention! The required UUID for subscription was not found!", owner.name());
|
||||
let (tx, rx) = broadcast::channel(64);
|
||||
broadcast.insert(uuid, tx);
|
||||
rx
|
||||
|
|
@ -144,7 +143,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
continue;
|
||||
},
|
||||
C2SMessage::Unsub(uuid) => {
|
||||
info!("[WebSocket{}] Unsub", owner.name());
|
||||
debug!("[WebSocket{}] C2S : Unsub", owner.name());
|
||||
// Отбрасываю Unsub на самого себя
|
||||
if uuid == owner.0.clone().unwrap().uuid {
|
||||
continue;
|
||||
|
|
@ -153,14 +152,12 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
shutdown.notify_one();
|
||||
continue;
|
||||
},
|
||||
// _ => continue
|
||||
}
|
||||
|
||||
// Отправка сообщения
|
||||
warn!("[WebSocket{}] Отвечаю: {msg:?}", owner.name());
|
||||
// Sending message
|
||||
debug!("[WebSocket{}] Answering: {msg:?}", owner.name());
|
||||
if socket.send(msg).await.is_err() {
|
||||
// если попали сюда, значит вероятнее всего клиент отключился
|
||||
warn!("[WebSocket{}] Ошибка отправки! Соединение разорвано!", owner.name());
|
||||
warn!("[WebSocket{}] Send error! Connection terminated!", owner.name());
|
||||
if let Some(u) = owner.0 {
|
||||
remove_broadcast(state.broadcasts.clone(), u.uuid).await;
|
||||
}
|
||||
|
|
@ -170,11 +167,10 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||
msg = mrx.recv() => {
|
||||
match socket.send(Message::Binary(msg.clone().unwrap())).await {
|
||||
Ok(_) => {
|
||||
warn!("[WebSocketSubscribe{}] Отвечаю: {}", owner.name(), hex::encode(msg.unwrap()));
|
||||
debug!("[WebSocketSubscribe{}] Answering: {}", owner.name(), hex::encode(msg.unwrap()));
|
||||
}
|
||||
Err(_) => {
|
||||
// если попали сюда, значит вероятнее всего клиент отключился
|
||||
warn!("[WebSocketSubscriber{}] Ошибка отправки! Соединение разорвано!", owner.name());
|
||||
warn!("[WebSocketSubscriber{}] Send error! Connection terminated!", owner.name());
|
||||
if let Some(u) = owner.0 {
|
||||
remove_broadcast(state.broadcasts.clone(), u.uuid).await;
|
||||
}
|
||||
|
|
@ -190,11 +186,14 @@ async fn subscribe(socket: mpsc::Sender<Vec<u8>>, mut rx: Receiver<Vec<u8>>, shu
|
|||
loop {
|
||||
tokio::select! {
|
||||
_ = shutdown.notified() => {
|
||||
debug!("Unsubscribing!");
|
||||
debug!("Shutdown SUB!");
|
||||
return;
|
||||
}
|
||||
msg = rx.recv() => {
|
||||
socket.send(msg.unwrap()).await.unwrap();
|
||||
if socket.send(msg.unwrap()).await.is_err() {
|
||||
error!("Forced shutdown SUB due error!");
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -202,15 +201,7 @@ async fn subscribe(socket: mpsc::Sender<Vec<u8>>, mut rx: Receiver<Vec<u8>>, shu
|
|||
|
||||
fn into_s2c_ping(buf: Vec<u8>, uuid: Uuid) -> Vec<u8> {
|
||||
use std::iter::once;
|
||||
// let mut vec = Vec::new();
|
||||
// vec
|
||||
//let uuid = uuid.as_u128();
|
||||
//let uuid = uuid.into_bytes();
|
||||
// info!("UUID {} UUID BE {}", hex::encode(uuid.into_bytes()), hex::encode(uuid128.to_be_bytes()));
|
||||
let res: Vec<u8> = once(1).chain(uuid.into_bytes().iter().copied()).chain(buf.as_slice()[1..].iter().copied()).collect();
|
||||
debug!("Sending ping: {}", hex::encode(res.clone()));
|
||||
res
|
||||
// vec
|
||||
once(1).chain(uuid.into_bytes().iter().copied()).chain(buf.as_slice()[1..].iter().copied()).collect()
|
||||
}
|
||||
|
||||
async fn remove_broadcast(broadcasts: Arc<tokio::sync::Mutex<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>>, uuid: Uuid) {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
use log::debug;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::MessageLoadError;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum S2CMessage<'a> {
|
||||
Auth = 0,
|
||||
Ping(Uuid, u32, bool, &'a [u8]) = 1,
|
||||
Event(Uuid) = 2, // UUID Обновляет аватар других игроков
|
||||
Event(Uuid) = 2, // Updates avatar for other players
|
||||
Toast(u8, &'a str, Option<&'a str>) = 3,
|
||||
Chat(&'a str) = 4,
|
||||
Notice(u8) = 5,
|
||||
|
|
@ -88,16 +87,6 @@ impl<'a> Into<Box<[u8]>> for S2CMessage<'a> {
|
|||
}
|
||||
|
||||
impl<'a> S2CMessage<'a> {
|
||||
pub fn to_s2c_ping(uuid: Uuid, buf: &'a [u8]) -> S2CMessage<'a> {
|
||||
use S2CMessage::Ping;
|
||||
debug!("!!! {buf:?}");
|
||||
Ping(
|
||||
uuid,
|
||||
u32::from_be_bytes((&buf[1..5]).try_into().unwrap()),
|
||||
buf[5] != 0, // Ping может быть короче чем ожидалось
|
||||
&buf[6..],
|
||||
)
|
||||
}
|
||||
pub fn to_array(self) -> Box<[u8]> {
|
||||
<S2CMessage as Into<Box<[u8]>>>::into(self)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue