diff --git a/assets/public/tangtoc/.gitignore b/assets/public/tangtoc/.gitignore index c9dcb9b..3080a88 100644 --- a/assets/public/tangtoc/.gitignore +++ b/assets/public/tangtoc/.gitignore @@ -1 +1,4 @@ testvid.mp4 +*.png +*.mp4 +*.jpg diff --git a/assets/vcnv/key.png b/assets/vcnv/key.png index a8dab38..4c96390 100644 Binary files a/assets/vcnv/key.png and b/assets/vcnv/key.png differ diff --git a/chick.yaml b/chick.yaml index 0ff7e22..610c700 100644 --- a/chick.yaml +++ b/chick.yaml @@ -13,6 +13,8 @@ wasm: - deno run compileRustDev front: + - output=$(python3 syncroot.py $*) + - eval $output - cd frontend - deno run dev diff --git a/config.json b/config.json index 1f6a45b..c2f22c0 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,9 @@ { "listenOn": "localhost:6942", + "devServer": { + "host": "localhost", + "port": 6699 + }, "serveDir": "dist", "staticDir": "static", "checkRPCTypes": true, @@ -7,24 +11,24 @@ "tickSpeed": 60, "credentials": [ { - "fullName": "MrBeast", - "username": "mrbeast", - "accessKey": "joebiden" + "fullName": "Bảo Ngọc", + "username": "baongoc", + "accessKey": "1234" }, { - "fullName": "Dr Disrespect", - "username": "disrespect", - "accessKey": "ytshorts" + "fullName": "Thùy An", + "username": "thuyan", + "accessKey": "1234" }, { - "fullName": "Test One", - "username": "1", - "accessKey": "1" + "fullName": "Đại Phong", + "username": "daiphong", + "accessKey": "1234" }, { - "fullName": "ChatGPT", - "username": "elonmusk", - "accessKey": "chatgpt" + "fullName": "Test Player", + "username": "1", + "accessKey": "1" } ], "game": { diff --git a/engine/src/net/wspy.rs b/engine/src/net/wspy.rs index 3665b4e..c3e3fe0 100644 --- a/engine/src/net/wspy.rs +++ b/engine/src/net/wspy.rs @@ -72,8 +72,10 @@ async fn sws(call_mpsc: MessageHandler, listen_on: String, serve_dir: String, st get(move |ws, option, connect_info| ws_handler(ws, option, connect_info, call_mpsc)), ); - let listener = tokio::net::TcpListener::bind(listen_on).await.unwrap(); - logging::info("Serving on localhost:6942..."); + let listener = tokio::net::TcpListener::bind(listen_on.clone()) + .await + .unwrap(); + logging::info(format!("Serving on {}...", listen_on)); axum::serve( listener, app.into_make_service_with_connect_info::(), diff --git a/frontend/client/src/bridge/clientpacket.rs b/frontend/client/src/bridge/clientpacket.rs index a3d5a3e..a3cc347 100644 --- a/frontend/client/src/bridge/clientpacket.rs +++ b/frontend/client/src/bridge/clientpacket.rs @@ -5,8 +5,6 @@ use js_sys::{Object, Reflect}; use serde_repr::{Deserialize_repr, Serialize_repr}; use wasm_bindgen::prelude::*; -use crate::console_log; - #[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)] #[wasm_bindgen] #[allow(unused)] diff --git a/frontend/package.json b/frontend/package.json index b5d0c28..5b801ec 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,7 @@ "compileRustProd": "wasm-pack build ./client --target bundler --out-dir ../node_modules/client --out-name client", "compileRustDev": "wasm-pack build ./client --target bundler --out-dir ../node_modules/client --out-name client --debug", "watchRust": "cargo watch -i .gitignore -i src/ -C . -s \"deno run compileRust\"", - "dev": "deno run compileRustDev && vite dev --force", + "dev": "deno run compileRustDev && vite dev --force --host=$HOST --port=$PORT", "build": "deno run compileRustProd && vite build", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", diff --git a/frontend/src/components/BareTimer.svelte b/frontend/src/components/BareTimer.svelte index fa2c8f6..1134097 100644 --- a/frontend/src/components/BareTimer.svelte +++ b/frontend/src/components/BareTimer.svelte @@ -8,7 +8,12 @@ onMount(() => { progress.subscribe((v) => { - thumb.style.width = `${v * 100}%`; + if (thumb === null) return; + if (v >= 1) { + thumb.style.width = "100%"; + } else { + thumb.style.width = `${v * 100}%`; + } thumb.style.display = v <= 0 ? "none" : "block"; }); }); @@ -21,6 +26,7 @@ diff --git a/frontend/src/components/vcnv/VCNVLine.svelte b/frontend/src/components/vcnv/VCNVLine.svelte index 0422b11..f945a4c 100644 --- a/frontend/src/components/vcnv/VCNVLine.svelte +++ b/frontend/src/components/vcnv/VCNVLine.svelte @@ -2,6 +2,7 @@ import type { Writable } from "svelte/store"; export let line: Writable; + console.log($line);
diff --git a/frontend/src/lib/index.ts b/frontend/src/lib/index.ts index edebbd1..0f66f9a 100644 --- a/frontend/src/lib/index.ts +++ b/frontend/src/lib/index.ts @@ -43,7 +43,7 @@ export class Connection { Peeker = await import("client") let obj = new Connection(); Peeker.ClientHandle.set_panic_hook() - obj.ws = new WebSocket("ws://localhost:6942/harlem"); + obj.ws = new WebSocket(`ws://${import.meta.env.VITE_WSENDPOINT}/harlem`); obj.callbacks = new Map() obj.globalCB = [] obj.ws.onmessage = ((me) => { diff --git a/frontend/src/lib/player.ts b/frontend/src/lib/player.ts index f9ecba8..4135a6e 100644 --- a/frontend/src/lib/player.ts +++ b/frontend/src/lib/player.ts @@ -26,6 +26,15 @@ export class PlayerManager implements Readable> { return obj } + static getDisplayName(identifier: string, pl: any[]): string { + for (let p of pl) { + if (p.identifier == identifier) { + return p.name; + } + } + throw new Error(`Cannot find player with username ${identifier}`) + } + get list(): Player[] { return Array.from(this.storage.values()); } diff --git a/frontend/src/lib/state.ts b/frontend/src/lib/state.ts index f1b826d..742ca6d 100644 --- a/frontend/src/lib/state.ts +++ b/frontend/src/lib/state.ts @@ -63,8 +63,12 @@ export class StateManager implements Readable { return this.store.__init; } + flush() { + this.store = { __init: false }; + } async updateAll() { + this.flush() await this.connection.send(Peeker.Query.stateList()); } diff --git a/frontend/src/routes/display/+page.svelte b/frontend/src/routes/display/+page.svelte index f0f6c2a..54cb78c 100644 --- a/frontend/src/routes/display/+page.svelte +++ b/frontend/src/routes/display/+page.svelte @@ -20,6 +20,7 @@ players = gm.players; conn.on(Peeker.PacketType.State, async (update) => { if (update.value.name === "current_part") { + gm.states.flush(); await gm.updateAll(); } }); diff --git a/frontend/src/routes/game/+page.svelte b/frontend/src/routes/game/+page.svelte index 40277f1..219a1b8 100644 --- a/frontend/src/routes/game/+page.svelte +++ b/frontend/src/routes/game/+page.svelte @@ -9,6 +9,7 @@ import KhoiDong from "../../components/player/KhoiDong.svelte"; import Vcnv from "../../components/player/VCNV.svelte"; import TangToc from "../../components/player/TangToc.svelte"; + import VeDich from "../../components/player/VeDich.svelte"; let conn: Connection; let gm: GameMaster; @@ -22,6 +23,8 @@ players = gm.players; conn.on(Peeker.PacketType.State, async (update) => { if (update.value.name === "current_part") { + states.flush(); + console.log("afterstateflush", $states); await gm.updateAll(); } }); @@ -41,6 +44,6 @@ {:else if $states.available_parts[$states.current_part] == "tangtoc"} {:else if $states.available_parts[$states.current_part] == "vedich"} - + {/if} diff --git a/frontend/src/routes/panel/+page.svelte b/frontend/src/routes/panel/+page.svelte index 13e7863..8caddad 100644 --- a/frontend/src/routes/panel/+page.svelte +++ b/frontend/src/routes/panel/+page.svelte @@ -78,7 +78,7 @@
- +
diff --git a/server/logic/khoidong.py b/server/logic/khoidong.py index c08bcb3..914f4e9 100644 --- a/server/logic/khoidong.py +++ b/server/logic/khoidong.py @@ -77,6 +77,7 @@ def __init__(self) -> None: # Resets the highlighted list when game stage is changed. self.stage.subscribe(lambda _: self.highlighted.set([])) + self.stage.subscribe(lambda _: self.qid.set(-1)) def _seperated(self, _show: penguin.Show) -> engine.Status: return engine.Status.RUNNING diff --git a/server/logic/tangtoc.py b/server/logic/tangtoc.py index 55534f4..e23b380 100644 --- a/server/logic/tangtoc.py +++ b/server/logic/tangtoc.py @@ -50,6 +50,7 @@ def __init__(self) -> None: "answers", [], ) + self.max_time = self.rpc.use_state("max_time", -1) self.qid.subscribe(self.on_qid_change) self.rpc.add_procedures( [ @@ -85,7 +86,6 @@ def submit_answer( call.data.str_argno(1), call.data.float_argno(2), ) - print(self.show.session_manager.player_map) match self.show.session_manager.playername(token): case Some(name): elapsed: float = self.show.timer.get().time_elapsed().total_seconds() @@ -147,6 +147,8 @@ def on_qid_change(self, qid: int): q = self.show.qbank.get_question(qid) self.prompt.set(q.prompt) self.answers.set(self.DEFAULT_ANSWERS) + self.max_time = self.rpc.use_state("max_time", q.time) + self.show.timer.set(engine.Timer()) if q.media is None: self.media.set(None) else: diff --git a/server/logic/vcnv.py b/server/logic/vcnv.py index 04ddb8e..eb524d9 100644 --- a/server/logic/vcnv.py +++ b/server/logic/vcnv.py @@ -26,7 +26,7 @@ def __init__(self) -> None: "1": 36, "2": 37, "3": 38, - "4": 38, + "4": 39, "M": 40, } @@ -122,7 +122,6 @@ def submit_answer( call.data.str_argno(1), call.data.float_argno(2), ) - print(self.show.session_manager.player_map) match self.show.session_manager.playername(token): case Some(name): elapsed: float = self.show.timer.get().time_elapsed().total_seconds() @@ -222,7 +221,7 @@ def verdict( self.answers.set(mod) def bell(self, _, call: engine.Packet.CallProcedure, handle: engine.IOHandle, _2): - target = call.data.str_argno(0) + target = self.session_manager.playername(call.data.str_argno(0)).unwrap() # TODO - check timeMs also engine.log_info( f"{target} pressed bell on {datetime.time().isoformat("microseconds")}" diff --git a/server/logic/vedich.py b/server/logic/vedich.py index 1a8aadc..3b51a69 100644 --- a/server/logic/vedich.py +++ b/server/logic/vedich.py @@ -3,6 +3,7 @@ import datetime import penguin from config import config +import json STAGE_CHOOSE = 0 """Phần chọn gói câu hỏi""" @@ -62,6 +63,17 @@ def __init__(self) -> None: self.prompt = self.rpc.use_state("prompt", "") self.stage = self.rpc.use_state("stage", STAGE_CHOOSE) self.qid = self.rpc.use_state("qid", -1) + self.media = self.rpc.use_state( + "media", + None, + ) + self.media_status = self.rpc.use_state( + "media_status", + { + "visible": False, + "playback_paused": False, + }, + ) self.rpc.add_procedures( [ ( @@ -119,6 +131,16 @@ def on_qid_change(self, qid: int): ) self.max_time.set(q.time) self.show.timer.set(engine.Timer()) + if q.media is None: + self.media.set(None) + else: + self.media.set(json.loads(q.media.pack())) + self.media_status.set( + { + "visible": False, + "playback_paused": False, + } + ) def set_choice( self, _, call: engine.Packet.CallProcedure, handle: engine.IOHandle, _2 @@ -140,7 +162,7 @@ def set_choice( def bell(self, _, call: engine.Packet.CallProcedure, handle: engine.IOHandle, _2): target = call.data.str_argno(0) - clientTime = call.data.str_argno(1) + clientTime = call.data.int_argno(1) if self.bell_player != "": engine.log_info( f"{target} tried to ring bell, being late to the game. {datetime.time().isoformat("microseconds")}" diff --git a/server/main.py b/server/main.py index a2e784f..f885902 100644 --- a/server/main.py +++ b/server/main.py @@ -50,7 +50,7 @@ def main(): ) show.start( - "localhost:6942", + config.config().listenOn, "dist", "static", ) diff --git a/server/penguin/_option.py b/server/penguin/_option.py index 2a2d1b2..23992f0 100644 --- a/server/penguin/_option.py +++ b/server/penguin/_option.py @@ -74,8 +74,9 @@ def is_some(self): return False @typing.override - def unwrap(self): + def unwrap(self) -> typing.NoReturn: super().unwrap() + raise Exception() @typing.override def expect(self, msg: str): diff --git a/server/penguin/rpc.py b/server/penguin/rpc.py index 7b0aa08..2719863 100644 --- a/server/penguin/rpc.py +++ b/server/penguin/rpc.py @@ -394,7 +394,6 @@ async def handle( self.states_writable[update.name].set( self.deser_map[update.name](update.data) ) - print(show.session_manager.active_addr) await show.session_manager.broadcast( engine.Packet.State(self.states[update.name]).pack() ) diff --git a/server/penguin/session.py b/server/penguin/session.py index bd6679f..6764dde 100644 --- a/server/penguin/session.py +++ b/server/penguin/session.py @@ -32,7 +32,7 @@ def register_session(self, handle: engine.IOHandle) -> str: def link_player(self, identifier: str, handle: engine.IOHandle) -> str: token = self.register_session(handle) self.player_map[identifier] = token - print(f"Linked {identifier} -> {token}") + engine.log_debug(f"Linked {identifier} -> {token}") return token def playername(self, token: str) -> Option[str]: @@ -58,8 +58,8 @@ def purge(self, handle: engine.IOHandle): try: # del self.player_map[ident] del self.active_sessions[handle] - print(f"Purged {ident}") - traceback.print_stack() + engine.log_debug(f"Purged {ident}") + # traceback.print_stack() except KeyError: pass diff --git a/server/penguin/show.py b/server/penguin/show.py index b24d6a7..9158a70 100644 --- a/server/penguin/show.py +++ b/server/penguin/show.py @@ -1,3 +1,4 @@ +from traceback import print_stack from typing import final, override import typing import engine @@ -81,7 +82,6 @@ async def handle_webreq(self, req: engine.RawRequest): token = self.session_manager.link_player( detail.username, req.handle ) - print(self.session_manager.player_map) response = engine.Packet.AuthStatus( engine.AuthenticationStatus(True, "Authenticated.", token) ) @@ -189,6 +189,23 @@ def set_part( new_part = call.data.int_argno(0) self.current_part.set(new_part) + TASK_POOL.append( + self.session_manager.broadcast( + engine.Packet.StateList(list(self.rpc.states.values())).pack() + ) + ) + TASK_POOL.append( + self.session_manager.broadcast( + engine.Packet.StateList( + list( + self.parts[ + self.current_part.get() + ].implementation.rpc.states.values() # type: ignore[reportAttributeAccessIssue] + ) + ).pack() + ) + ) + def set_score( self, _: "Show", @@ -196,7 +213,6 @@ def set_score( handle: engine.IOHandle, addr: str, ): - print(call.pack()) target = call.data.str_argno(0) value = call.data.int_argno(1) pl = self.players.get() diff --git a/syncroot.py b/syncroot.py new file mode 100644 index 0000000..e184086 --- /dev/null +++ b/syncroot.py @@ -0,0 +1,11 @@ +from codecs import EncodedFile +import json + +if __name__ == "__main__": + with open("config.json", "r", encoding="utf8") as f: + cfg = json.load(f) + with open("frontend/.env", "w", encoding="utf8") as g: + g.write(f'VITE_WSENDPOINT="{cfg["listenOn"]}"') + print( + f'export HOST={cfg["devServer"]["host"]};export PORT={cfg["devServer"]["port"]}' + )