diff --git a/main.py b/main.py index fcc9573..c7fc51e 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,8 @@ import aiohttp.web import traceback import urllib.parse +import os +import json from model import Model @@ -50,7 +52,7 @@ async def handler(request: aiohttp.web.Request): data = await request.json() - print(f'{client=} {data=}') + print(f'{method=} {client=} {data=}') try: assert method in model.ApiMethod.dict @@ -60,7 +62,8 @@ async def handler(request: aiohttp.web.Request): del e # unused? traceback.print_exc() return aiohttp.web.Response(status=400) - + finally: + await model.send_state(client) @routes.get(CLIENT_REGEX + '/ws') async def _(request: aiohttp.web.Request): @@ -110,7 +113,13 @@ if __name__ == '__main__': app = aiohttp.web.Application() app.add_routes(routes) - app['model'] = Model() + data = {} + filename = "tehsession.json" + if os.path.isfile(filename): + with open(filename) as f: + data = json.load(f) + + app['model'] = Model(model = data) aiohttp.web.run_app(app, port=42042) app['model'].save() diff --git a/model.py b/model.py index a6c45f8..a836c4e 100644 --- a/model.py +++ b/model.py @@ -22,28 +22,22 @@ class Model(object): def __contains__(self, item): return item in self.dict - def __init__(self, filename="tehmodel.json"): - self.sockets = {} # mapping: client -> socket - self.filename = filename - self.model = None - if os.path.isfile(filename): - with open(filename) as f: - try: - self.model = json.load(f) - except: - self.model = {} - else: - self.model = {} - self.assert_model() + def __init__(self, model = {}): + if "sessions" not in model: + model["sessions"] = [] + if "clients" not in model: + model["clients"] = [] + self.sockets = {} # mapping: clientid -> sockets + self.sessions = { session: Session(model = model["sessions"][session]) for session in model["sessions"] } + self.clients = { client: Client(model = model["clients"][client]) for client in model["clients"] } + self.filename = "tehsession.json" - def assert_model(self): - if not "clients" in self.model: - self.model["clients"] = {} - if not "sessions" in self.model: - self.model["sessions"] = {} - for session in self.model["sessions"]: - if not "name" in self.model["sessions"][session]: - self.model["sessions"][session]["name"] = "Defaultname" + def to_json(self): + model = { + "sessions": {session: self.sessions[session].to_json() for session in self.sessions }, + "clients": {client: self.clients[client].to_json() for client in self.clients }, + } + return model @ApiMethod async def test_api(self, clientid): @@ -55,79 +49,96 @@ class Model(object): @ApiMethod async def change_username(self, clientid, username) -> str: - self.model["clients"][clientid]["username"] = username - await self.send_state(clientid) + self.clients[clientid].name = username @ApiMethod async def create_session(self, clientid, sessionname) -> str: if not sessionname: - raise Exception("Cant be empty!") - sessionid = generate_random_id() - newsession = {"id": sessionid, "owner": clientid, "clients": [], "name": sessionname } - self.model["sessions"][sessionid] = newsession - await self.send_state(clientid) + raise Exception(f"Sessionname cant be empty!") + session = Session(name = sessionname, owner = clientid) + self.sessions[session.id] = session + print("create_session was called") @ApiMethod async def change_sessionname(self, clientid, sessionid, sessionname) -> str: if not sessionname: raise Exception("Cant be empty!") - if self.model["sessions"][sessionid]["owner"] == clientid: - self.model["sessions"][sessionid]["name"] = sessionname - await self.send_state(clientid) + session = self.sessions[sessionid] + if not (session.owner == clientid): + raise Exception("Ownly owner can change sessionname") + session.name = sessionname @ApiMethod async def leave_session(self, clientid): - sessionid = self.model["clients"][clientid]["session"] - del self.model["clients"][clientid]["session"] - self.model["sessions"][sessionid]["clients"].remove(clientid) - await self.send_state(clientid) + client = self.clients[clientid] + sessionid = client.session + if sessionid: + session = self.sessions[sessionid] + session.clients.remove(clientid) + self.clients[clientid].session = "" @ApiMethod async def join_session(self, clientid, sessionid): - if sessionid in self.model["sessions"]: - # session exists - self.model["clients"][clientid]["session"] = sessionid - if not clientid in self.model["sessions"][sessionid]["clients"]: - self.model["sessions"][sessionid]["clients"].append(clientid) - await self.send_state(clientid) + client = self.clients[clientid] + old_sessionid = client.session + # leave old session + if old_sessionid: + old_session = self.sessions[old_sessionid] + old_session.clients.remove(clientid) + session = self.sessions[sessionid] + client.session = sessionid + session.clients.append(client.id) + + async def send_lobby_view(self, clientid): + data = {} + client = self.clients[clientid] + + data["view"] = "lobby" + data["username"] = client.name + data["sessions"] = { + session.id: { + "id": session.id, + "name": session.name, + "owned": session.owner == client.id, + } for session in self.sessions.values() + } + for socket in self.sockets[clientid]: + await socket.send_json(data) + + async def send_session_view(self, clientid): + data = {} + client = self.clients[clientid] + + data["view"] = "session" + data["username"] = client.name + + for socket in self.sockets[clientid]: + await socket.send_json(data) async def send_state(self, clientid): # TODO: compute state, send to client data = {} - if "session" in self.model["clients"][clientid]: - sessionid = self.model["clients"][clientid]["session"] - data["session"] = {"id": sessionid, "name": self.model["sessions"][sessionid]["name"] } - allsessions = { name: { "id": name, "name": self.model["sessions"][name]["name"] } for name in self.model["sessions"] } - for session in allsessions: - if self.model["sessions"][session]["owner"] == clientid: - allsessions[session]["owner"] = True - data["allsessions"] = allsessions - data["username"] = self.model["clients"][clientid]["username"] if "username" in self.model["clients"][clientid] else "Joe" - for socket in self.sockets[clientid]: - await socket.send_json(data) + client = self.clients[clientid] + session = self.sessions[client.session] if client.session else None + if session: + await self.send_session_view(clientid) + else: + await self.send_lobby_view(clientid) def save(self): with open(self.filename, "w") as f: - json.dump(self.model, f) - if not os.path.isdir("backups"): - try: - os.mkdir("backups") - except FileExistsError: - print("backups is a file, no directory. Please delete yourself") - datestring = datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d-%H%M%S") - with open(f"backups/{datestring}_{self.filename}", "w") as f: - json.dump(self.model, f) + json.dump(self.to_json(), f) def exists_client(self, clientid: str) -> bool: - return clientid in self.model["clients"] + return clientid in self.clients def create_client(self, name="Joe") -> str: if not name: raise Exception("Username cannot be empty!") - clientname = generate_random_id() - newclient = {"id": clientname, "username": name, "sessions": []} - self.model["clients"][clientname] = newclient - return clientname + client = Client() + client.name = name + self.clients[client.id] = client + return client.id async def subscribe(self, clientid, socket): if not clientid in self.sockets: @@ -139,3 +150,50 @@ class Model(object): for client in self.sockets: if socket in self.sockets[client]: self.sockets[client].remove(socket) + +class Session: + + def __init__(self, model = None, owner = None, name = None): + if model: + self.id = model["id"] + self.name = model["name"] + self.clients = model["clients"] + self.owner = model["owner"] + elif owner and name: + self.id = generate_random_id() + self.clients = [] + self.owner = owner + self.name = name + else: + raise Exception("Illegal session constructor") + + def to_json(self): + model = { + "id": self.id, + "name": self.name, + "clients": self.clients, + "owner": self.owner, + } + return model + + + +class Client: + + def __init__(self, model = None): + if model: + self.id = model["id"] + self.name = model["name"] + self.session = model["session"] + else: + self.id = generate_random_id() + self.name = "Default Client Name" + self.session = "" + + def to_json(self): + model = { + "id": self.id, + "name": self.name, + "session": self.session, + } + return model diff --git a/static/renderer.js b/static/renderer.js index bf5a868..0b9e695 100644 --- a/static/renderer.js +++ b/static/renderer.js @@ -2,36 +2,23 @@ const ws_url = new URL('ws', window.location.href); ws_url.protocol = ws_url.protocol.replace('http', 'ws'); const ws = new WebSocket(ws_url.href); -ws.onmessage = function(event) { - const msg = JSON.parse(event.data); - - console.log(msg); - - if (msg.hasOwnProperty('session')) { - document.getElementById('active-session').innerText = msg.session["name"]; - document.getElementById('btn-leave-session').style.display = "inline-block"; - } else { - document.getElementById('active-session').innerText = "None"; - document.getElementById('btn-leave-session').style.display = "none"; - } +function draw_lobby(msg) { if (msg.hasOwnProperty('username')) { document.getElementById('label-username').innerText = msg.username; } + if (msg.hasOwnProperty('sessions')) { + const sessions = document.getElementById('sessions'); - if (msg.hasOwnProperty('allsessions')) { - const all_sessions = document.getElementById('all_sessions'); + while (sessions.children.length) sessions.lastChild.remove(); - while (all_sessions.children.length) all_sessions.lastChild.remove(); + Object.keys(msg.sessions).forEach( session => { + session = msg.sessions[session]; + var sessionid = session["id"]; + var sessionname = session["name"]; + var owned = session["owned"]; - //for (let session in msg.allsessions) { - Object.keys(msg.allsessions).forEach( session => { - - var sessionid = msg.allsessions[session]["id"] - var sessionname = msg.allsessions[session]["name"] const tehsession = document.createElement('div'); - const labelname = document.createElement('span'); - owned = ("owner" in msg.allsessions[session]); labelname.innerText = sessionname; tehsession.appendChild(labelname); @@ -99,5 +86,22 @@ ws.onmessage = function(event) { all_sessions.appendChild(tehsession); }) + } + */ +} + +ws.onmessage = function(event) { + const msg = JSON.parse(event.data); + + console.log(msg); + + var view = "lobby"; + if (msg.hasOwnProperty('view')) { + view = msg.view; + } + if (view == "lobby") { + draw_lobby(msg); + } else if (view == "session") { + console.log("cant draw session yet"); } }; diff --git a/tehsession.json b/tehsession.json new file mode 100644 index 0000000..3f8f43f --- /dev/null +++ b/tehsession.json @@ -0,0 +1 @@ +{"sessions": {"2b7jrklcn6eulwbw": {"id": "2b7jrklcn6eulwbw", "name": "die neue session", "clients": ["bkrqopf5j6q3tpta"], "owner": "bkrqopf5j6q3tpta"}}, "clients": {"bkrqopf5j6q3tpta": {"id": "bkrqopf5j6q3tpta", "name": "dominic", "session": ""}}} \ No newline at end of file diff --git a/ui.html b/ui.html index 6f9e4db..6471659 100644 --- a/ui.html +++ b/ui.html @@ -33,7 +33,7 @@ div {
Available sessions
-

+