diff --git a/server.py b/server.py deleted file mode 100644 index 65d0fdf..0000000 --- a/server.py +++ /dev/null @@ -1,406 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import asyncio -import websockets -import json -import random -import sys - -parser = argparse.ArgumentParser(description='Run the taiko-web multiplayer server.') -parser.add_argument('port', type=int, metavar='PORT', nargs='?', default=34802, help='Port to listen on.') -parser.add_argument('-b', '--bind-address', default='localhost', help='Bind server to address.') -parser.add_argument('-o', '--allow-origin', action='append', help='Limit incoming connections to the specified origin. Can be specified multiple times.') -args = parser.parse_args() - -server_status = { - "waiting": {}, - "users": [], - "invites": {} -} -consonants = "bcdfghjklmnpqrstvwxyz" - -def msgobj(msg_type, value=None): - if value == None: - return json.dumps({"type": msg_type}) - else: - return json.dumps({"type": msg_type, "value": value}) - -def status_event(): - value = [] - for id, userDiff in server_status["waiting"].items(): - value.append({ - "id": id, - "diff": userDiff["diff"] - }) - return msgobj("users", value) - -def get_invite(): - return "".join([random.choice(consonants) for x in range(5)]) - -async def notify_status(): - ready_users = [user for user in server_status["users"] if "ws" in user and user["action"] == "ready"] - if ready_users: - sent_msg = status_event() - await asyncio.wait([user["ws"].send(sent_msg) for user in ready_users]) - -async def connection(ws, path): - # User connected - user = { - "ws": ws, - "action": "ready", - "session": False, - "name": None, - "don": None - } - server_status["users"].append(user) - try: - # Notify user about other users - await ws.send(status_event()) - while True: - try: - message = await asyncio.wait_for(ws.recv(), timeout=10) - except asyncio.TimeoutError: - # Keep user connected - pong_waiter = await ws.ping() - try: - await asyncio.wait_for(pong_waiter, timeout=10) - except asyncio.TimeoutError: - # Disconnect - break - except websockets.exceptions.ConnectionClosed: - # Connection closed - break - else: - # Message received - try: - data = json.loads(message) - except json.decoder.JSONDecodeError: - data = {} - action = user["action"] - msg_type = data["type"] if "type" in data else None - value = data["value"] if "value" in data else None - if action == "ready": - # Not playing or waiting - if msg_type == "join": - if value == None: - continue - waiting = server_status["waiting"] - id = value["id"] if "id" in value else None - diff = value["diff"] if "diff" in value else None - user["name"] = value["name"] if "name" in value else None - user["don"] = value["don"] if "don" in value else None - if not id or not diff: - continue - if id not in waiting: - # Wait for another user - user["action"] = "waiting" - user["gameid"] = id - waiting[id] = { - "user": user, - "diff": diff - } - await ws.send(msgobj("waiting")) - else: - # Join the other user and start game - user["name"] = value["name"] if "name" in value else None - user["don"] = value["don"] if "don" in value else None - user["other_user"] = waiting[id]["user"] - waiting_diff = waiting[id]["diff"] - del waiting[id] - if "ws" in user["other_user"]: - user["action"] = "loading" - user["other_user"]["action"] = "loading" - user["other_user"]["other_user"] = user - user["other_user"]["player"] = 1 - user["player"] = 2 - await asyncio.wait([ - ws.send(msgobj("gameload", {"diff": waiting_diff, "player": 2})), - user["other_user"]["ws"].send(msgobj("gameload", {"diff": diff, "player": 1})), - ws.send(msgobj("name", { - "name": user["other_user"]["name"], - "don": user["other_user"]["don"] - })), - user["other_user"]["ws"].send(msgobj("name", { - "name": user["name"], - "don": user["don"] - })) - ]) - else: - # Wait for another user - del user["other_user"] - user["action"] = "waiting" - user["gameid"] = id - waiting[id] = { - "user": user, - "diff": diff - } - await ws.send(msgobj("waiting")) - # Update others on waiting players - await notify_status() - elif msg_type == "invite": - if value and "id" in value and value["id"] == None: - # Session invite link requested - invite = get_invite() - server_status["invites"][invite] = user - user["action"] = "invite" - user["session"] = invite - user["name"] = value["name"] if "name" in value else None - user["don"] = value["don"] if "don" in value else None - await ws.send(msgobj("invite", invite)) - elif value and "id" in value and value["id"] in server_status["invites"]: - # Join a session with the other user - user["name"] = value["name"] if "name" in value else None - user["don"] = value["don"] if "don" in value else None - user["other_user"] = server_status["invites"][value["id"]] - del server_status["invites"][value["id"]] - if "ws" in user["other_user"]: - user["other_user"]["other_user"] = user - user["action"] = "invite" - user["session"] = value["id"] - user["other_user"]["player"] = 1 - user["player"] = 2 - await asyncio.wait([ - ws.send(msgobj("session", {"player": 2})), - user["other_user"]["ws"].send(msgobj("session", {"player": 1})), - ws.send(msgobj("invite")), - ws.send(msgobj("name", { - "name": user["other_user"]["name"], - "don": user["other_user"]["don"] - })), - user["other_user"]["ws"].send(msgobj("name", { - "name": user["name"], - "don": user["don"] - })) - ]) - else: - del user["other_user"] - await ws.send(msgobj("gameend")) - else: - # Session code is invalid - await ws.send(msgobj("gameend")) - elif action == "waiting" or action == "loading" or action == "loaded": - # Waiting for another user - if msg_type == "leave": - # Stop waiting - if user["session"]: - if "other_user" in user and "ws" in user["other_user"]: - user["action"] = "songsel" - await asyncio.wait([ - ws.send(msgobj("left")), - user["other_user"]["ws"].send(msgobj("users", [])) - ]) - else: - user["action"] = "ready" - user["session"] = False - await asyncio.wait([ - ws.send(msgobj("gameend")), - ws.send(status_event()) - ]) - else: - del server_status["waiting"][user["gameid"]] - del user["gameid"] - user["action"] = "ready" - await asyncio.wait([ - ws.send(msgobj("left")), - notify_status() - ]) - if action == "loading": - if msg_type == "gamestart": - user["action"] = "loaded" - if user["other_user"]["action"] == "loaded": - user["action"] = "playing" - user["other_user"]["action"] = "playing" - sent_msg = msgobj("gamestart") - await asyncio.wait([ - ws.send(sent_msg), - user["other_user"]["ws"].send(sent_msg) - ]) - elif action == "playing": - # Playing with another user - if "other_user" in user and "ws" in user["other_user"]: - if msg_type == "note"\ - or msg_type == "drumroll"\ - or msg_type == "branch"\ - or msg_type == "gameresults": - await user["other_user"]["ws"].send(msgobj(msg_type, value)) - elif msg_type == "songsel" and user["session"]: - user["action"] = "songsel" - user["other_user"]["action"] = "songsel" - sent_msg1 = msgobj("songsel") - sent_msg2 = msgobj("users", []) - await asyncio.wait([ - ws.send(sent_msg1), - ws.send(sent_msg2), - user["other_user"]["ws"].send(sent_msg1), - user["other_user"]["ws"].send(sent_msg2) - ]) - elif msg_type == "gameend": - # User wants to disconnect - user["action"] = "ready" - user["other_user"]["action"] = "ready" - sent_msg1 = msgobj("gameend") - sent_msg2 = status_event() - await asyncio.wait([ - ws.send(sent_msg1), - ws.send(sent_msg2), - user["other_user"]["ws"].send(sent_msg1), - user["other_user"]["ws"].send(sent_msg2) - ]) - del user["other_user"]["other_user"] - del user["other_user"] - else: - # Other user disconnected - user["action"] = "ready" - user["session"] = False - await asyncio.wait([ - ws.send(msgobj("gameend")), - ws.send(status_event()) - ]) - elif action == "invite": - if msg_type == "leave": - # Cancel session invite - if user["session"] in server_status["invites"]: - del server_status["invites"][user["session"]] - user["action"] = "ready" - user["session"] = False - if "other_user" in user and "ws" in user["other_user"]: - user["other_user"]["action"] = "ready" - user["other_user"]["session"] = False - sent_msg = status_event() - await asyncio.wait([ - ws.send(msgobj("left")), - ws.send(sent_msg), - user["other_user"]["ws"].send(msgobj("gameend")), - user["other_user"]["ws"].send(sent_msg) - ]) - else: - await asyncio.wait([ - ws.send(msgobj("left")), - ws.send(status_event()) - ]) - elif msg_type == "songsel" and "other_user" in user: - if "ws" in user["other_user"]: - user["action"] = "songsel" - user["other_user"]["action"] = "songsel" - sent_msg = msgobj(msg_type) - await asyncio.wait([ - ws.send(sent_msg), - user["other_user"]["ws"].send(sent_msg) - ]) - else: - user["action"] = "ready" - user["session"] = False - await asyncio.wait([ - ws.send(msgobj("gameend")), - ws.send(status_event()) - ]) - elif action == "songsel": - # Session song selection - if "other_user" in user and "ws" in user["other_user"]: - if msg_type == "songsel" or msg_type == "catjump": - # Change song select position - if user["other_user"]["action"] == "songsel" and type(value) is dict: - value["player"] = user["player"] - sent_msg = msgobj(msg_type, value) - await asyncio.wait([ - ws.send(sent_msg), - user["other_user"]["ws"].send(sent_msg) - ]) - elif msg_type == "crowns" or msg_type == "getcrowns": - if user["other_user"]["action"] == "songsel": - sent_msg = msgobj(msg_type, value) - await asyncio.wait([ - user["other_user"]["ws"].send(sent_msg) - ]) - elif msg_type == "join": - # Start game - if value == None: - continue - id = value["id"] if "id" in value else None - diff = value["diff"] if "diff" in value else None - if not id or not diff: - continue - if user["other_user"]["action"] == "waiting": - user["action"] = "loading" - user["other_user"]["action"] = "loading" - await asyncio.wait([ - ws.send(msgobj("gameload", {"diff": user["other_user"]["gamediff"]})), - user["other_user"]["ws"].send(msgobj("gameload", {"diff": diff})) - ]) - else: - user["action"] = "waiting" - user["gamediff"] = diff - await user["other_user"]["ws"].send(msgobj("users", [{ - "id": id, - "diff": diff - }])) - elif msg_type == "gameend": - # User wants to disconnect - user["action"] = "ready" - user["session"] = False - user["other_user"]["action"] = "ready" - user["other_user"]["session"] = False - sent_msg1 = msgobj("gameend") - sent_msg2 = status_event() - await asyncio.wait([ - ws.send(sent_msg1), - user["other_user"]["ws"].send(sent_msg1) - ]) - await asyncio.wait([ - ws.send(sent_msg2), - user["other_user"]["ws"].send(sent_msg2) - ]) - del user["other_user"]["other_user"] - del user["other_user"] - else: - # Other user disconnected - user["action"] = "ready" - user["session"] = False - await asyncio.wait([ - ws.send(msgobj("gameend")), - ws.send(status_event()) - ]) - finally: - # User disconnected - del user["ws"] - del server_status["users"][server_status["users"].index(user)] - if "other_user" in user and "ws" in user["other_user"]: - user["other_user"]["action"] = "ready" - user["other_user"]["session"] = False - await asyncio.wait([ - user["other_user"]["ws"].send(msgobj("gameend")), - user["other_user"]["ws"].send(status_event()) - ]) - del user["other_user"]["other_user"] - if user["action"] == "waiting": - del server_status["waiting"][user["gameid"]] - await notify_status() - elif user["action"] == "invite" and user["session"] in server_status["invites"]: - del server_status["invites"][user["session"]] - -port = args.port -print('Starting server on port %d' % port) -loop = asyncio.get_event_loop() -tasks = asyncio.gather( - websockets.serve(connection, args.bind_address, port, origins=args.allow_origin) -) -try: - loop.run_until_complete(tasks) - loop.run_forever() -except KeyboardInterrupt: - print("Stopping server") - def shutdown_exception_handler(loop, context): - if "exception" not in context or not isinstance(context["exception"], asyncio.CancelledError): - loop.default_exception_handler(context) - loop.set_exception_handler(shutdown_exception_handler) - tasks = asyncio.gather(*asyncio.all_tasks(loop=loop), loop=loop, return_exceptions=True) - tasks.add_done_callback(lambda t: loop.stop()) - tasks.cancel() - while not tasks.done() and not loop.is_closed(): - loop.run_forever() -finally: - if hasattr(loop, "shutdown_asyncgens"): - loop.run_until_complete(loop.shutdown_asyncgens()) - loop.close() -