Compare commits

..

No commits in common. "main" and "multiplayer-test" have entirely different histories.

2 changed files with 30 additions and 28 deletions

View File

@ -1,9 +1,12 @@
# CW Generator
A simple application for generating self-made morse / CW audio files. All it features is a button as
a morse key and allows you to export this as a WAV. Optionally, a MIDI keyboard can be used as a
morse key (since godot 4.4 even on web).
morse key.
## Known Limitations / TODO
MIDI support only works locally for now.
* webmidi might be an option: https://gist.github.com/srejv/b7198e25587e2d8e0a66860781b56852
Currently we can only export wav, as Godot itself doesn't include any encoders. Having the ability
to export as mp3, ogg or opus would be nice, but external libraries are kind of a hassle. opus would
be the nicest format, but doesn't seem to work with older Iphones.

View File

@ -1,20 +1,18 @@
import asyncio
import datetime
import json
import logging
import re
from websockets.asyncio.connection import broadcast
from websockets.asyncio.server import serve
__VERSION__ = "0.0.1"
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s"
format="%(asctime)s %(name)s %(levelname)s %(message)s"
)
LOG = logging.getLogger()
class Client:
freqs = {}
@ -25,7 +23,7 @@ class Client:
self.curr_freq = None
async def handle(self):
LOG.info(">>> New client %s connected", self.client)
print(f" >>> New client {self.client} connected")
exc = None
try:
await self._handle_client()
@ -33,7 +31,7 @@ class Client:
exc = e
finally:
# FIXME: basically handle disconnect / leave from room
LOG.info("<<< Client %s id %s disconnected: %s", self.client, self.id, exc)
print(f" <<< Client {self.client} id {self.id} disconnected: {exc}")
if self.curr_freq:
await self._leave_room()
@ -51,19 +49,18 @@ class Client:
async def _handle_client(self):
await self._send(type="hello", name="LobbySrv 3000", version=__VERSION__)
async for data in self.websocket:
print(f" <-- client {self.client} sent {repr(data)}")
try:
data = json.loads(data)
except json.JSONDecodeError:
self._send_error("Could not decode message, invalid json")
LOG.error("client %s sent broken data %s", self.client, repr(data))
continue
if not isinstance(data, dict) or "cmd" not in data:
await self._send_error("Invalid format in json")
LOG.error("client %s sent broken data (no cmd key in data) %s", self.client, repr(data))
continue
LOG.info("client %s wrote: %s", self.client, data)
print(f"{datetime.datetime.now()} {self.client} wrote:", data)
match data["cmd"]:
case "quit":
@ -127,7 +124,7 @@ class Client:
self.curr_freq = freq
self.freqs[freq].append(self)
# FIXME: do we need locking here?
LOG.debug("FREQ %s %s %s", self.curr_freq, freq, self.freqs)
print("FREQ", self.curr_freq, freq, self.freqs)
await self._send(type="join", freq=self.curr_freq, self_id=self.id,
other_players=[c.id for c in self._others(freq)])
await self._send_to_group(self._others(freq), type="player-joined", player=self.id)
@ -145,18 +142,19 @@ class Client:
type="morse-state", state=data["state"], from_player=self.id)
async def _leave_room(self):
if self.curr_freq:
if not self.curr_freq:
self._send_error("You are not on a frequency")
return
await self._send_to_group(self._others(self.curr_freq),
type="player-left", player=self.id)
try:
self.freqs[self.curr_freq].remove(self)
except ValueError:
LOG.warning("Player %s was not in freq %s", self.id, self.curr_freq)
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
else:
LOG.warning("Client %s is not on a frequency, sending a 'leave' nontheless", self.client)
try:
await self._send(type="leave")
@ -168,17 +166,18 @@ class Client:
async def _send(self, ignore_exceptions=False, **kwargs):
data = json.dumps(kwargs).encode()
LOG.debug("--> sending out to %s: %s", self.client, data)
print(f" --> sending out to {self.client}: {data}")
try:
await self.websocket.send(json.dumps(kwargs).encode() + b"\n")
except Exception as e:
LOG.error("Error sending data to %s: %s", self.client, e)
print(f"Error sending data to {self.client}: {e}")
if not ignore_exceptions:
raise
async def _send_to_group(self, group, **kwargs):
LOG.info("broadcast() to %s clients: %s", len(group), kwargs)
broadcast([c.websocket for c in group], json.dumps(kwargs).encode() + b"\n")
async with asyncio.TaskGroup() as tg:
for member in group:
tg.create_task(member._send(ignore_exceptions=True, **kwargs))
async def _send_error(self, msg: str):
await self._send(type="error", message=msg)
@ -201,5 +200,5 @@ async def main():
if __name__ == "__main__":
LOG.info("Starting server")
print("Starting server")
asyncio.run(main())