From 64d91c30ff43360c13fddca4142eb46ece8c3e2f Mon Sep 17 00:00:00 2001 From: yukirij Date: Wed, 2 Oct 2024 12:27:31 -0700 Subject: [PATCH] Add client/server version checking. --- server/build.rs | 12 ++++ server/src/app/mod.rs | 6 ++ server/src/config.rs | 1 + server/src/main.rs | 1 + server/src/manager/data.rs | 13 +++- server/src/manager/ws.rs | 10 ++- server/src/protocol/code.rs | 2 + server/src/protocol/mod.rs | 3 + server/src/protocol/packet/hello.rs | 28 +++++++++ server/src/protocol/packet/mod.rs | 1 + www/js/const.js | 2 + www/js/scene.js | 95 ++++++++++++++++------------- www/js/system.js | 21 +++++-- 13 files changed, 146 insertions(+), 49 deletions(-) create mode 100644 server/build.rs create mode 100644 server/src/protocol/packet/hello.rs diff --git a/server/build.rs b/server/build.rs new file mode 100644 index 0000000..e8d5bc2 --- /dev/null +++ b/server/build.rs @@ -0,0 +1,12 @@ +use std::process::Command; + +fn main() +{ + let output = Command::new("git") + .args(&["rev-parse", "HEAD"]) + .output() + .expect("Failed to acquire git HEAD"); + let git_hash = String::from_utf8(output.stdout).expect("Invalid UTF-8 output from git HEAD"); + let git_hash = git_hash.trim(); + println!("cargo:rustc-env=GIT_HASH={}", git_hash); +} diff --git a/server/src/app/mod.rs b/server/src/app/mod.rs index e9c4d74..0f6cbed 100644 --- a/server/src/app/mod.rs +++ b/server/src/app/mod.rs @@ -151,6 +151,12 @@ impl App { let mut socket = conn.stream.write().await; match response.data { + QRPacketData::RHello(response) => { + socket.send(Message::Binary( + encode_response(CODE_HELLO, response.encode()) + )).await.ok(); + } + QRPacketData::RRegister(response) => { socket.send(Message::Binary( encode_response(CODE_REGISTER, response.encode()) diff --git a/server/src/config.rs b/server/src/config.rs index 89c5b8b..de32930 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -1 +1,2 @@ +pub const VERSION :&str = env!("GIT_HASH"); pub const REGISTER_CODE :&str = "mountain"; diff --git a/server/src/main.rs b/server/src/main.rs index 642feae..d2e6004 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -77,6 +77,7 @@ async fn service_http(mut request:hyper::Request, args:Ht match request.uri().path() { "/.js" => { output = [ + format!("let CONFIG_VERSION = \"{}\";", config::VERSION).as_bytes().to_vec(), format!("let CONFIG_LANGUAGE = {};", language_code).as_bytes().to_vec(), output, ].concat(); diff --git a/server/src/manager/data.rs b/server/src/manager/data.rs index 2629bfc..e75b3eb 100644 --- a/server/src/manager/data.rs +++ b/server/src/manager/data.rs @@ -1,5 +1,6 @@ use bus::Bus; use crate::{ + config, app::{ authentication::Authentication, connection::Connection, @@ -58,10 +59,13 @@ pub async fn thread_system(mut app:App, bus:Bus) packet.from, QRPacket::new(id as u32, QRPacketData::RConn) ).ok(); + Some(QRPacket::new(0, QRPacketData::None)) } QRPacketData::QDisconn => { + println!("Disconnect: {}", qr.id); + // Uninitialize connection if if let Some(conn) = app.connections.get(qr.id as usize).cloned() { @@ -110,12 +114,17 @@ pub async fn thread_system(mut app:App, bus:Bus) true } else { false } { app.connections.remove(qr.id as usize).ok(); - - println!("Disconnect: {}", qr.id); } Some(QRPacket::new(0, QRPacketData::None)) } + QRPacketData::QHello => { + let response = PacketHelloResponse { + version:config::VERSION.to_string(), + }; + Some(QRPacket::new(qr.id, QRPacketData::RHello(response))) + } + QRPacketData::QRegister(request) => { let mut response = PacketRegisterResponse::new(); response.status = STATUS_SERVER_ERROR; diff --git a/server/src/manager/ws.rs b/server/src/manager/ws.rs index 838766f..dc7a71a 100644 --- a/server/src/manager/ws.rs +++ b/server/src/manager/ws.rs @@ -56,6 +56,11 @@ pub async fn handle_ws(ws:WebSocketStream>, args:HttpServiceAr println!("MESSAGE {:x}", code); match code { + CODE_HELLO => { + args.bus.send( + bus_ds, QRPacket::new(conn_id, QRPacketData::QHello) + ).ok(); + } CODE_REGISTER => match PacketRegister::decode(&data, &mut index) { Ok(packet) => { @@ -207,7 +212,10 @@ pub async fn handle_ws(ws:WebSocketStream>, args:HttpServiceAr true } } - Err(_) => false, + Err(e) => { + println!("WSS error: {}", e.to_string()); + false + }, } None => false, } { } diff --git a/server/src/protocol/code.rs b/server/src/protocol/code.rs index f7b2341..35a7a05 100644 --- a/server/src/protocol/code.rs +++ b/server/src/protocol/code.rs @@ -22,6 +22,8 @@ pub const STATUS_NOT_IMPL :u16 = 0x00FF; ** Operation Codes */ +pub const CODE_HELLO :u16 = 0x0001; + pub const CODE_REGISTER :u16 = 0x0010; pub const CODE_AUTH :u16 = 0x0011; pub const CODE_AUTH_RESUME :u16 = 0x0012; diff --git a/server/src/protocol/mod.rs b/server/src/protocol/mod.rs index 7c24563..99ea3b8 100644 --- a/server/src/protocol/mod.rs +++ b/server/src/protocol/mod.rs @@ -12,6 +12,9 @@ pub enum QRPacketData { QDisconn, + QHello, + RHello(PacketHelloResponse), + QRegister(PacketRegister), RRegister(PacketRegisterResponse), diff --git a/server/src/protocol/packet/hello.rs b/server/src/protocol/packet/hello.rs new file mode 100644 index 0000000..f49562b --- /dev/null +++ b/server/src/protocol/packet/hello.rs @@ -0,0 +1,28 @@ +use crate::util::pack::pack_u8; + +use super::Packet; + +#[derive(Clone)] +pub struct PacketHelloResponse { + pub version:String, +} +impl PacketHelloResponse { + pub fn new() -> Self + { + Self { + version:String::new(), + } + } +} +impl Packet for PacketHelloResponse { + type Data = Self; + + fn encode(&self) -> Vec + { + let version_bytes = self.version.as_bytes().to_vec(); + [ + pack_u8(version_bytes.len() as u8), + version_bytes, + ].concat() + } +} diff --git a/server/src/protocol/packet/mod.rs b/server/src/protocol/packet/mod.rs index 1092b6e..50fad86 100644 --- a/server/src/protocol/packet/mod.rs +++ b/server/src/protocol/packet/mod.rs @@ -1,3 +1,4 @@ +mod hello; pub use hello::*; mod connect; pub use connect::*; mod register; pub use register::*; diff --git a/www/js/const.js b/www/js/const.js index 752164a..7f3e55f 100644 --- a/www/js/const.js +++ b/www/js/const.js @@ -30,6 +30,8 @@ const Status = { }; const OpCode = { + Hello :0x0001, + Register :0x0010, Authenticate :0x0011, Resume :0x0012, diff --git a/www/js/scene.js b/www/js/scene.js index dd21030..1c8577a 100644 --- a/www/js/scene.js +++ b/www/js/scene.js @@ -13,11 +13,23 @@ const SCENES = { load() { UI.nav([ UI.button(LANG("reconnect"), () => { RECONNECT(); }), + UI.button(LANG("practice"), () => { LOAD(SCENES.GamePractice); }), ], []); return true; } - reconnect() { - LOAD_URL(); + message(code, data) { + switch(code) { + case OpCode.Hello: { + console.log("CLIENT VERSION: '" + CONFIG_VERSION + "'"); + console.log("SERVER VERSION: '" + data.version + "'"); + + if(CONFIG_VERSION == data.version) { + RESUME(); + } else { + location.reload(); + } + } break; + } } }, @@ -207,36 +219,40 @@ const SCENES = { this.pages = 1; } load() { - UI.mainmenu("browse"); + if(SOCKET !== null) { + UI.mainmenu("browse"); - let indicator_page = UI.div([]); - indicator_page.setAttribute("id", "indicator-page"); + let indicator_page = UI.div([]); + indicator_page.setAttribute("id", "indicator-page"); - UI.mainnav( - [ ], - [ - indicator_page, - UI.button("◀", () => { - if(SCENE.page < SCENE.pages) { SCENE.page += 1; } - SCENE.refresh(); - }), - UI.button("▶", () => { - if(SCENE.page > 1) { SCENE.page -= 1; } - SCENE.refresh(); - }), - UI.button(LANG("refresh"), () => { SCENE.referesh(); }), - ], - { auth:true, session:true } - ); - - let table = document.createElement("table"); - table.setAttribute("id", "content"); - table.setAttribute("class", "list session"); - MAIN.appendChild(table); + UI.mainnav( + [ ], + [ + indicator_page, + UI.button("◀", () => { + if(SCENE.page < SCENE.pages) { SCENE.page += 1; } + SCENE.refresh(); + }), + UI.button("▶", () => { + if(SCENE.page > 1) { SCENE.page -= 1; } + SCENE.refresh(); + }), + UI.button(LANG("refresh"), () => { SCENE.referesh(); }), + ], + { auth:true, session:true } + ); + + let table = document.createElement("table"); + table.setAttribute("id", "content"); + table.setAttribute("class", "list session"); + MAIN.appendChild(table); - SCENE.refresh(); + SCENE.refresh(); - history.pushState(null, "Dzura", "/"); + history.pushState(null, "Dzura", "/"); + } else { + LOAD(SCENES.Offline); + } return true; } refresh() { @@ -1010,7 +1026,6 @@ function LOAD(scene, data=null) { UI.rebuild(); SCENE = new scene(); if(!SCENE.load(data)) { LOAD(SCENES.Browse); } - UI.update_status(); } @@ -1023,13 +1038,13 @@ function LOAD_URL() { if(parts.length > 1) { switch(parts[1]) { - case "continue": LOAD(SCENES.Continue); break; - case "live": LOAD(SCENES.Live); break; - case "history": LOAD(SCENES.History); break; - case "practice": LOAD(SCENES.GamePractice); break; - case "guide": LOAD(SCENES.Guide); break; - case "about": LOAD(SCENES.About); break; - case "challenge": LOAD(SCENES.Challenge); break; + case "continue": LOAD(SCENES.Continue); return; + case "live": LOAD(SCENES.Live); return; + case "history": LOAD(SCENES.History); return; + case "practice": LOAD(SCENES.GamePractice); return; + case "guide": LOAD(SCENES.Guide); return; + case "about": LOAD(SCENES.About); return; + case "challenge": LOAD(SCENES.Challenge); return; case "game": { if(parts[2]) { @@ -1038,14 +1053,10 @@ function LOAD_URL() { token:token, mode:INTERFACE.Mode.Review, }); - } else { - LOAD(SCENES.Browse); + return; } } break; - - default: LOAD(SCENES.Browse); } - } else { - LOAD(SCENES.Browse); } + LOAD(SCENES.Browse); } diff --git a/www/js/system.js b/www/js/system.js index 93109fb..5968ca6 100644 --- a/www/js/system.js +++ b/www/js/system.js @@ -4,6 +4,7 @@ function RECONNECT() { SOCKET = new WebSocket("wss://" + location.hostname + ":38612"); SOCKET.binaryType = "arraybuffer"; SOCKET.addEventListener("error", () => { + console.log("Websocket error."); SOCKET = null; if(SCENE.disconnect !== undefined) { SCENE.disconnect(); } }); @@ -20,11 +21,11 @@ function RECONNECT() { RECONNECT(); }); - RESUME(); + MESSAGE_COMPOSE([ + PACK.u16(OpCode.Hello), + ]); } }); - } else { - RESUME(); } } @@ -40,7 +41,7 @@ function RESUME() { secret, ]); } else { - if(SCENE.reconnect !== undefined) { SCENE.reconnect(); } + LOAD_URL(); } } @@ -56,6 +57,18 @@ function MESSAGE(event) { } switch(code) { + case OpCode.Hello: { + console.log("RECV Hello"); + + data = { + version:"", + }; + + result = UNPACK.string(bytes, index, UNPACK.u8); + index = result.index; + data.version = result.data; + } break; + case OpCode.Register: { console.log("RECV Register");