diff --git a/scenes/multiplayer_connect.gd b/scenes/multiplayer_connect.gd index ef425b6..3ad34ef 100644 --- a/scenes/multiplayer_connect.gd +++ b/scenes/multiplayer_connect.gd @@ -1,6 +1,6 @@ extends Control -var server := "localhost" +var server := "seba-geek.de" var port := "3784" var ws := WebSocketPeer.new() @@ -20,9 +20,9 @@ class PlayerData: var player_data := [ PlayerData.new(0, Color( 0, 255, 0), 880), - PlayerData.new(1, Color(255, 0, 0), 880), - PlayerData.new(2, Color( 0, 0, 255), 880), - PlayerData.new(3, Color(255, 0, 255), 880), + PlayerData.new(1, Color(255, 0, 0), 1200), + PlayerData.new(2, Color( 0, 0, 255), 600), + PlayerData.new(3, Color(255, 0, 255), 500), ] func _ready() -> void: @@ -57,7 +57,7 @@ func _handle_packet() -> void: # fetch frequency list on first join _on_refresh_button_pressed() "join": - _join_freq(parsed["freq"]) + _join_freq(parsed["freq"], parsed["self_id"], parsed["other_players"]) "freq-list": %FreqList.clear() @@ -66,14 +66,53 @@ func _handle_packet() -> void: print("Adding ", text) var idx: int = %FreqList.add_item(text) %FreqList.set_item_metadata(idx, freq) + + "morse-state": + var from_player: String = parsed["from_player"] + if from_player not in mmb_others: + print("Error: Got morse state from unknown player ", from_player) + return + mmb_others[from_player].set_morse_state(parsed["state"]) -func _join_freq(freq: String, player_id: String, other_players: Array[String]): + "player-joined": + var new_player: String = parsed["player"] + if new_player in mmb_others: + print("Error: got player join message, but player ", new_player, " is already present") + return + + # find free player id + var player_n := 1 + while true: + var found := false + for other in mmb_others.values(): + if other.num == player_n: + print("New player, num ", player_n, " already taken") + found = true + break + if not found: + break + player_n += 1 + + make_player(player_n, new_player) + + "player-left": + var player: String = parsed["player"] + if player not in mmb_others: + print("Error: player not part of freq ", player) + return + remove_player(parsed["player"]) + + _: + print("Unhandled message: ", parsed["type"]) + + +func _join_freq(freq: String, player_id: String, other_players: Array): for child in %PlayerContainer.get_children(): %PlayerContainer.remove_child(child) - mmb_self = make_player(0) - for n, player_id in enumeerate(other_players): - make_player(n + 1) + mmb_self = make_player(0, player_id) + for n in range(other_players.size()): + make_player(n + 1, other_players[n]) %FreqLabel.text = "%s MHz" % freq %ConnectView.hide() @@ -99,7 +138,7 @@ func _on_freq_list_join(index: int, at_position: Vector2, mouse_button_index: in var join_cmd := {"cmd": "join", "freq": freq, "type": "cw-generator"} ws.send_text(JSON.stringify(join_cmd) + "\n") -func make_player(no: int): +func make_player(no: int, player_id: String): var pd: PlayerData if no < player_data.size(): pd = player_data[no] @@ -108,16 +147,28 @@ func make_player(no: int): var mmb = MultiMorseBanner.new_banner(pd.num, pd.color, pd.tone) %PlayerContainer.add_child(mmb) + mmb_others[player_id] = mmb return mmb +func remove_player(player_id: String): + mmb_others[player_id].set_morse_state(false) + %PlayerContainer.remove_child(mmb_others[player_id]) + mmb_others.erase(player_id) + +func set_morse_state(state: bool): + mmb_self.set_morse_state(state) + send_data({"cmd": "morse-state", "state": state}) + +func send_data(data: Dictionary): + var text := JSON.stringify(data) + "\n" + ws.send_text(text) func _on_morse_button_button_down() -> void: if not mmb_self: return - mmb_self.set_morse_state(true) - + set_morse_state(true) func _on_morse_button_button_up() -> void: if not mmb_self: return - mmb_self.set_morse_state(false) + set_morse_state(false) diff --git a/signalsrv/signalsrv.py b/signalsrv/signalsrv.py index 86edb8b..cdcecab 100644 --- a/signalsrv/signalsrv.py +++ b/signalsrv/signalsrv.py @@ -24,7 +24,9 @@ class Client: exc = e finally: # FIXME: basically handle disconnect / leave from room - print(f" <<< Client {self.client} disconnected: {exc}") + print(f" <<< Client {self.client} id {self.id} disconnected: {exc}") + if self.curr_freq: + await self._leave_room() @property def client(self): @@ -48,7 +50,7 @@ class Client: continue if not isinstance(data, dict) or "cmd" not in data: - self._send_error("Invalid format in json") + await self._send_error("Invalid format in json") continue print(f"{self.client} wrote:", data) @@ -67,8 +69,7 @@ class Client: case "disconnect": pass case "morse-state": - # FIXME: send to all other clients - pass + await self._handle_morse_state(data) case _: await self._send_error("Unknown command") @@ -92,7 +93,7 @@ class Client: self.curr_freq = freq self.freqs[freq] = [self] - await self._send(type="join", freq=self.curr_freq, self_id=self.id, others=[]) + await self._send(type="join", freq=self.curr_freq, self_id=self.id, other_players=[]) async def _join_room(self, data): if self.curr_freq: @@ -112,13 +113,36 @@ class Client: self.freqs[freq].append(self) # FIXME: do we need locking here? print("FREQ", self.curr_freq, freq, self.freqs) - await self._send(type="join", freq=self.curr_freq, self_id=self.id, players=[c.id for c in self._others(freq) - if not c.id == self.id]) + await self._send(type="join", freq=self.curr_freq, self_id=self.id, + other_players=[c.id for c in self._others(freq)]) for other in self._others(freq): - await self._send(type="player-joined", player=self.id) + await other._send(type="player-joined", player=self.id) + + async def _handle_morse_state(self, data): + if not self.curr_freq: + await self._send_error("No frequency selected") + return + + if "state" not in data or not isinstance(data["state"], bool): + await self._send_error("No state key with type bool in data") + return + + for other in self._others(self.curr_freq): + await other._send(type="morse-state", state=data["state"], from_player=self.id) + + async def _leave_room(self): + for other in self._others(self.curr_freq): + await other._send(type="player-left", player=self.id) + try: + self.freqs[self.curr_freq].remove(self) + except ValueError: + print(f"Warning: Player {self.id} was not in freq {self.curr_freq}") + if not self.freqs[self.curr_freq]: + del self.freqs[self.curr_freq] + self.curr_freq = None def _others(self, freq): - return [c for c in self.freqs[freq] if c != self.websocket] + return [c for c in self.freqs[freq] if c.id != self.id] async def _send(self, **kwargs): data = json.dumps(kwargs).encode() @@ -140,10 +164,11 @@ async def new_client(websocket): async def main(): - HOST, PORT = "localhost", 3784 + HOST, PORT = "0.0.0.0", 3784 async with serve(new_client, HOST, PORT) as server: await server.serve_forever() if __name__ == "__main__": + print("Starting server") asyncio.run(main())