diff --git a/.vscode/tags b/.vscode/tags new file mode 100644 index 0000000..6d8d9b2 --- /dev/null +++ b/.vscode/tags @@ -0,0 +1,100 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.9~svn20110310 // +Base64Decode ../match.py /^def Base64Decode(jsonDump):$/;" kind:function line:13 +Base64Encode ../match.py /^def Base64Encode(ndarray):$/;" kind:function line:9 +DatabaseConnection ../database_connection.py /^class DatabaseConnection(object):$/;" kind:class line:16 +FIELD_DRAW ../match.py /^FIELD_DRAW = 3$/;" kind:variable line:33 +FIELD_EMPTY ../match.py /^FIELD_EMPTY = 0$/;" kind:variable line:30 +FIELD_USER_A ../match.py /^FIELD_USER_A = 1$/;" kind:variable line:31 +FIELD_USER_B ../match.py /^FIELD_USER_B = 2$/;" kind:variable line:32 +GameManager ../game_manager.py /^class GameManager(object):$/;" kind:class line:69 +Match ../match.py /^class Match(object):$/;" kind:class line:36 +MatchManager ../match_manager.py /^class MatchManager(object):$/;" kind:class line:6 +SQLInjectionError ../database_connection.py /^class SQLInjectionError(Exception):$/;" kind:class line:9 +SessionManager ../session_manager.py /^class SessionManager(object):$/;" kind:class line:7 +SimpleDecode ../match.py /^def SimpleDecode(jsonDump):$/;" kind:function line:26 +SimpleEncode ../match.py /^def SimpleEncode(ndarray):$/;" kind:function line:22 +User ../user.py /^class User(object):$/;" kind:class line:1 +UserManager ../user_manager.py /^class UserManager(object):$/;" kind:class line:8 +__init__ ../database_connection.py /^ def __init__(self):$/;" kind:member line:10 +__init__ ../database_connection.py /^ def __init__(self,$/;" kind:member line:58 +__init__ ../game_manager.py /^ def __init__(self, player_a_id, player_b_id, start_player, socket_a, socket_b):$/;" kind:member line:70 +__init__ ../match.py /^ def __init__(self, n, a_begins, player_a_name, player_b_name, json_state=None):$/;" kind:member line:37 +__init__ ../match_manager.py /^ def __init__(self):$/;" kind:member line:7 +__init__ ../session_manager.py /^ def __init__(self, session_lifespan_timedelta):$/;" kind:member line:8 +__init__ ../user.py /^ def __init__(self, id, name):$/;" kind:member line:2 +__init__ ../user_manager.py /^ def __init__(self):$/;" kind:member line:9 +_on_end_game ../game_manager.py /^ async def _on_end_game(self, player_id):$/;" kind:member line:172 +_on_move ../game_manager.py /^ async def _on_move(self, player_id, move_data):$/;" kind:member line:129 +cert_file ../settings.py /^cert_file = None$/;" kind:variable line:1 +check_win ../match.py /^ def check_win(self, field, x, y):$/;" kind:member line:105 +close ../database_connection.py /^ def close(self):$/;" kind:member line:92 +commit ../database_connection.py /^ def commit(self):$/;" kind:member line:96 +create_database.py ../create_database.py 1;" kind:file line:1 +create_new_match ../game_manager.py /^async def create_new_match():$/;" kind:function line:42 +create_session_for_registered_user ../session_manager.py /^ def create_session_for_registered_user(self, user_name):$/;" kind:member line:27 +create_session_for_temp_user ../session_manager.py /^ def create_session_for_temp_user(self, user_name):$/;" kind:member line:38 +create_tables ../create_database.py /^def create_tables():$/;" kind:function line:8 +create_test_manager ../user_manager.py /^def create_test_manager():$/;" kind:function line:51 +create_user ../user_manager.py /^ def create_user(self, user_name, pw):$/;" kind:member line:33 +database_connection.py ../database_connection.py 1;" kind:file line:1 +db_charset ../settings.py /^db_charset = 'utf8mb4'$/;" kind:variable line:13 +db_db ../settings.py /^db_db = "tictactoe"$/;" kind:variable line:11 +db_host ../settings.py /^db_host = "127.0.0.1"$/;" kind:variable line:6 +db_port ../settings.py /^db_port = 3306$/;" kind:variable line:7 +db_pw ../settings.py /^db_pw = "T0eT4cT!c"$/;" kind:variable line:10 +db_user ../settings.py /^db_user = "tictactoe"$/;" kind:variable line:9 +delete_session ../session_manager.py /^ def delete_session(self, session_id):$/;" kind:member line:49 +delete_user ../user_manager.py /^ def delete_user(self, user_name):$/;" kind:member line:16 +from_json_state ../match.py /^ def from_json_state(self, json_state):$/;" kind:member line:51 +game_manager.py ../game_manager.py 1;" kind:file line:1 +get_all_users ../user_manager.py /^ def get_all_users(self):$/;" kind:member line:46 +get_current_player ../match.py /^ def get_current_player(self):$/;" kind:member line:72 +get_cursor ../database_connection.py /^ def get_cursor(self):$/;" kind:member line:89 +get_id ../user.py /^ def get_id(self):$/;" kind:member line:6 +get_match ../match_manager.py /^ def get_match(self, id):$/;" kind:member line:10 +get_name ../user.py /^ def get_name(self):$/;" kind:member line:9 +get_session_by_id ../session_manager.py /^ def get_session_by_id(self, session_id):$/;" kind:member line:11 +get_session_by_registered_user ../session_manager.py /^ def get_session_by_registered_user(self, user_name):$/;" kind:member line:19 +get_session_by_temp_user ../session_manager.py /^ def get_session_by_temp_user(self, user_name):$/;" kind:member line:23 +get_sql_time ../database_connection.py /^def get_sql_time(datetime_object):$/;" kind:function line:5 +get_user ../user_manager.py /^ def get_user(self, user_name):$/;" kind:member line:12 +global_close ../database_connection.py /^ def global_close():$/;" kind:member line:29 +global_commit ../database_connection.py /^ def global_commit():$/;" kind:member line:34 +global_cursor ../database_connection.py /^ def global_cursor():$/;" kind:member line:24 +global_single_execution ../database_connection.py /^ def global_single_execution(sql_statement):$/;" kind:member line:49 +global_single_query ../database_connection.py /^ def global_single_query(query):$/;" kind:member line:39 +instance ../database_connection.py /^ instance = None$/;" kind:variable line:21 +is_full ../match.py /^ def is_full(self, field):$/;" kind:member line:102 +is_move_valid ../match.py /^ def is_move_valid(self, sub_x, sub_y, x, y):$/;" kind:member line:75 +key_file ../settings.py /^key_file = None$/;" kind:variable line:2 +main.py ../main.py 1;" kind:file line:1 +match.py ../match.py 1;" kind:file line:1 +match_manager.py ../match_manager.py 1;" kind:file line:1 +matchmaking ../game_manager.py /^async def matchmaking():$/;" kind:function line:60 +move ../match.py /^ def move(self, move_dict):$/;" kind:member line:139 +player_games ../game_manager.py /^player_games = {}$/;" kind:variable line:10 +player_id_queue ../game_manager.py /^player_id_queue = set()$/;" kind:variable line:7 +processPlayerMessage ../game_manager.py /^ async def processPlayerMessage(self, player_id, json_str):$/;" kind:member line:108 +process_message ../game_manager.py /^async def process_message(id, json):$/;" kind:function line:37 +register_user ../game_manager.py /^async def register_user(id, socket):$/;" kind:function line:13 +revoke_inactive_sessions ../session_manager.py /^ def revoke_inactive_sessions(self):$/;" kind:member line:53 +server_port ../settings.py /^server_port = 5556$/;" kind:variable line:4 +session_manager.py ../session_manager.py 1;" kind:file line:1 +settings.py ../settings.py 1;" kind:file line:1 +socket_worker ../main.py /^async def socket_worker(websocket, path):$/;" kind:function line:9 +sockets ../game_manager.py /^sockets = {}$/;" kind:variable line:8 +ssl_context ../main.py /^ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)$/;" kind:variable line:64 +startMatch ../game_manager.py /^ async def startMatch(self):$/;" kind:member line:81 +start_server ../main.py /^start_server = websockets.serve(socket_worker, host='', port=server_port, ssl=ssl_context)$/;" kind:variable line:67 +to_json_state ../match.py /^ def to_json_state(self):$/;" kind:member line:60 +touch_session ../session_manager.py /^ def touch_session(self, session_id):$/;" kind:member line:15 +touch_user ../user_manager.py /^ def touch_user(self, user_name):$/;" kind:member line:40 +unregister_user ../game_manager.py /^async def unregister_user(id):$/;" kind:function line:26 +user.py ../user.py 1;" kind:file line:1 +user_manager.py ../user_manager.py 1;" kind:file line:1 +verify_user ../user_manager.py /^ def verify_user(self, user_name, pw):$/;" kind:member line:20 diff --git a/README.md b/README.md index 72e4525..c7c619a 100644 --- a/README.md +++ b/README.md @@ -109,9 +109,29 @@ response: game_over: , player_won: > current_player: > + player_a: "..." + player_b: "..." } ``` + + +**match**: + +```json +{ + "type": "match_update", + "data": { + "id": "...", + "match_state": > + } +} +``` + + + + + **new temp session** client @@ -129,11 +149,11 @@ server response: ```json { - "type": "temp_session_response", + "type": "login_response", "data": { "success": , "id": "", - "message": "..." + "msg": "..." } } ``` @@ -163,15 +183,38 @@ server response: } ``` -**register**: +**login or register**: + +```json +{ + "type": "login", + "data": { + "name": "", + "pw": "" + } +} +``` + +response: + +```json +{ + "type": "login_response", + "data": { + "success": , + "id": "", + "msg": "..." + } +} +``` + -TODO **match_request**: -client +client (or server for sending invites) ```json { @@ -194,8 +237,6 @@ server_response: } ``` - - **match_move**: client @@ -204,6 +245,7 @@ client { "type": "move", "data": { + "id": "match_id", "sub_x": "...", "sub_y": "...", "x": "...", @@ -212,18 +254,6 @@ client } ``` -server response: (maybe useless?) - -```json -{ - "type": "move_response", - "data": { - "success": true, - "msg": "..." - } -} -``` - **match update** @@ -255,3 +285,5 @@ client: } ``` + + diff --git a/connection_handler.py b/connection_handler.py index 1223d5e..405856c 100644 --- a/connection_handler.py +++ b/connection_handler.py @@ -22,11 +22,30 @@ class Connection(object): id, user_name: str, registered: bool, - websocket: websocket.WebSocketServerProtocol): + websocket: websockets.WebSocketServerProtocol): self.id = id self.user_name = user_name self.websocket = websocket self.is_registered_user = registered + self.is_closed = False + + async def send(self, msg): + try: + await self.websocket.send(msg) + except Exception as e: + print("error sending message to user " + + self.user_name + ". Reason: " + str(e)) + + async def close(self): + try: + if self.is_closed: + return + self.websocket.close() + self.is_closed = True + + except Exception as e: + print("error closing session to user " + + self.user_name + ". Reason: " + str(e)) class ConnectionHandler(object): @@ -71,7 +90,7 @@ class ConnectionHandler(object): if conn.id in self.open_connections_by_id: del(self.open_connections_by_id[conn.id]) del(self.open_connections_by_user[conn.user_name]) - conn.websocket.close() + conn.close() def close_session(self, id): tmp = self.session_manager.get_session_by_id(id) @@ -85,18 +104,18 @@ class ConnectionHandler(object): def _add_connection(self, conn): self.open_connections_by_id[conn.id] = conn - self.open_connections_by_user[conn.user] = conn + self.open_connections_by_user[conn.user_name] = conn def _del_connection(self, conn): del(self.open_connections_by_id[conn.id]) - del(self.open_connections_by_user[conn.user]) + del(self.open_connections_by_user[conn.user_name]) + async def new_connection(self, socket: websockets.WebSocketServerProtocol, login_msg: str): - msg = parse_message(login_msg) - + print(msg) if msg is None: return None @@ -104,7 +123,7 @@ class ConnectionHandler(object): conn = self.reconnect_session(socket, msg['data']['id']) if conn is not None: self._add_connection(conn) - await conn.websocket.send(json.dumps({ + await conn.send(json.dumps({ "type": "reconnect_response", "data": { "success": True, @@ -112,7 +131,7 @@ class ConnectionHandler(object): } })) return conn - await conn.websocket.send(json.dumps({ + await conn.send(json.dumps({ "type": "reconnect_response", "data": { "success": False, @@ -128,43 +147,100 @@ class ConnectionHandler(object): if len(msg['data']['name']) < 16 and ';' not in name and '\'' not in name and '\"' not in name: id = self.session_manager.create_session_for_temp_user( name) + conn = Connection( id=id, user_name=name, registered=False, websocket=socket) + self._add_connection(conn) + await socket.send(json.dumps({ - "type": "temp_session_response", + "type": "login_response", "data": { "success": True, "id": id, - "message": "logged in as temporary user " + name + "msg": "logged in as temporary user " + name } })) + + + return conn await socket.send(json.dumps({ - "type": "temp_session_response", + "type": "login_response", "data": { "success": False, "id": None, - "message": "user name not available" + "msg": "user name not available" } })) return None elif msg['type'] == 'login': - # TODO - pass + response_msg = "" + success = False + session_id = None + name = None + pw = None + conn = None - elif msg['type'] == 'register': - # TODO - pass + try: + + name = msg['data']['name'] + pw = msg['data']['pw'] + + if len(name) <= 16 and len(pw) <= 32 and len(name) > 0 and len(pw) > 0: + + users = self.user_manager.get_user(name) + if len(users) == 0: + # user does not exists: + self.user_manager.create_user(name, pw) + session_id = self.session_manager.create_session_for_registered_user( + name) + response_msg = "successful registered and logged in user " + name + success = True + + elif self.user_manager.verify_user(name, pw): + session_id = self.session_manager.create_session_for_registered_user( + name) + response_msg = "successful logged in as user " + name + success = True + + else: + response_msg = "invalid password for user " + name + else: + response_msg = "invalid username or pw" + + except Exception as e: + response_msg = "invalid username or pw" + + if success: + conn = Connection(id=session_id, user_name=name, + registered=True, websocket=socket) + self._add_connection(conn) + + await socket.send(json.dumps({ + "type": "login_response", + "data": { + "success": success, + "id": session_id, + "msg": response_msg + } + })) + + if success: + await self._on_match_state_req(conn, None) + + return conn + + return None async def _start_match(self, user_a, user_b): m = self.match_manager.create_new_match(user_a, user_b) - state = m.to_json_state() + state = json.loads(m.to_json_state()) if user_a in self.open_connections_by_user: await self.open_connections_by_user[user_a].websocket.send( - json.dumps({ + json.dumps( { "type": "match_update", "data": { @@ -172,11 +248,11 @@ class ConnectionHandler(object): "match_state": state } } - }) + ) ) if user_b in self.open_connections_by_user: await self.open_connections_by_user[user_b].websocket.send( - json.dumps({ + json.dumps( { "type": "match_update", "data": { @@ -184,23 +260,168 @@ class ConnectionHandler(object): "match_state": state } } - }) + ) ) async def _on_match_req(self, conn, data): - if len(self.match_queue) > 0: - # it's a match! - user_a = self.match_queue.pop() - self._start_match(user_a, conn.user_name) + if data['player'] is None: + if len(self.match_queue) > 0: + # it's a match! + user_a = self.match_queue.pop() + await self._start_match(user_a, conn.user_name) + + else: + if conn.user_name not in self.match_queue: + self.match_queue.add(conn.user_name) + + await conn.websocket.send( + json.dumps( + { + "type": "match_request_response", + "data": { + "success": True, + "msg": "created match request" + } + } + ) + ) else: - self.match_queue.append(conn.user_name) + opponent = data['player'] + try: + if len(opponent) <= 16 and '\'' not in opponent and '"' not in opponent: + if len(self.user_manager.get_user(opponent)) > 0: + await self._start_match(conn.user_name, opponent) + + await conn.websocket.send( + json.dumps( + { + "type": "match_request_response", + "data": { + "success": True, + "msg": "startet match against " + opponent + } + } + ) + ) + else: + await conn.websocket.send( + json.dumps( + { + "type": "match_request_response", + "data": { + "success": False, + "msg": "user " + opponent + " not found :(" + } + } + ) + ) + + + except Exception as e: + print("error processing match request: " + str(data) + str(e)) + + async def _on_match_state_req(self, conn, data): + db_matches = self.match_manager.get_matches_for_user(conn.user_name) + for db_match in db_matches: + match = self.match_manager.get_match(db_match['id']) + await conn.send(json.dumps({ + "type": "match_update", + "data": { + "id": db_match['id'], + "match_state": json.loads(match.to_json_state()) + } + })) + if match.game_over: + if match.player_won is None or match.player_won != conn.user_name: + self.match_manager.delete_match(match.id) async def _on_match_move(self, conn, data): - pass + + match = None + + try: + sub_x = int(data['sub_x']) + sub_y = int(data['sub_y']) + x = int(data['x']) + y = int(data['y']) + + if type(sub_x) is int and type(sub_y) is int: + if type(x) is int and type(y) is int: + if type(data['id']) is str: + match = self.match_manager.apply_move(data) + + finally: + match_state = None + if match is not None: + match_state = match.to_json_state() + + print(match_state) + + await conn.send(json.dumps({ + 'type': 'match_update', + 'data': { + 'id': match.id, + 'match_state': json.loads(match_state) + } + })) + + other_user = match.player_a_name if conn.user_name == match.player_b_name else match.player_b_name + + if other_user in self.open_connections_by_user: + other_conn = self.open_connections_by_user[other_user] + await other_conn.send(json.dumps({ + 'type': 'match_update', + 'data': { + 'id': match.id, + 'match_state': json.loads(match_state) + } + })) + if match.game_over: + self.match_manager.delete_match(match.id) async def _on_match_close(self, conn, data): - pass + match = None + try: + match_id = data['id'] + if type(match_id) is str: + match = self.match_manager.get_match(match_id) + + if (match is None): + return + + match.game_over = True + + match_state = match.to_json_state() + + opponent = match.player_a_name if match.player_a_name != conn.user_name else match.player_b_name + + response = json.dumps({ + 'type': 'match_update', + 'data': { + 'id': match_id, + 'match_state': json.loads(match_state) + } + }) + + if opponent in self.open_connections_by_user: + await self.open_connections_by_user[opponent].websocket.send(response) + + await conn.send(response) + + self.match_manager.delete_match(match_id) + + except Exception as e: + match_state = None + if match is not None: + match_state = match.to_json_state() + + conn.send(json.dumps({ + 'type': 'match_update', + 'data': { + 'match_state': json.loads(match_state) + } + })) async def disconnect(self, conn): self._del_connection(conn) @@ -208,6 +429,8 @@ class ConnectionHandler(object): async def handle_message(self, conn, msg_str): msg = parse_message(msg_str) + print(msg) + if msg is None: return None @@ -223,5 +446,8 @@ class ConnectionHandler(object): elif t == "end_match": await self._on_match_close(conn, msg['data']) + elif t == "match_states_request": + await self._on_match_state_req(conn, msg['data']) + else: print("could not interpret message: " + msg_str) diff --git a/main.py b/main.py index ac582ce..edb9f53 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,70 @@ import websockets from settings import * import ssl +import traceback + from game_manager import * +from session_manager import SessionManager +from connection_handler import Connection, ConnectionHandler +from match_manager import MatchManager +from user_manager import UserManager +from database_connection import DatabaseConnection +import datetime + +um = UserManager() +sm = SessionManager(datetime.timedelta(hours=12)) +mm = MatchManager() +ch = ConnectionHandler(sm, um, mm) + + +DatabaseConnection(db_host, + db_port, + db_user, + db_pw, + db_db, + db_charset) + + +async def new_socket_worker(websocket, path): + connection = None + + print("new incomin connection") + + try: + raw_msg = await websocket.recv() + + connection = await ch.new_connection(websocket, raw_msg) + + print(ch.open_connections_by_id) + print(ch.open_connections_by_user) + + if connection is None: + return + + async for m in websocket: + await ch.handle_message(connection, m) + + except Exception as e: + # TODO: each disconnect is an exception so far + if connection is not None: + print("catched exception in worker for user: " + + connection.user_name + ": " + str(e)) + else: + print("catched exception in worker for unknown user: " + str(e)) + + print(traceback.print_exc()) + + finally: + id = None + if connection: + id = connection.user_name + print(ch.open_connections_by_id) + await ch.disconnect(connection) + await connection.close() + + if connection is None: + id = "unknown_user" + print("close connection to user: " + id) async def socket_worker(websocket, path): @@ -52,6 +115,7 @@ async def socket_worker(websocket, path): print("catched exception in worker for user: " + id + ": " + str(e)) else: print("catched exception in worker for unknown user") + finally: if registered: @@ -61,10 +125,12 @@ async def socket_worker(websocket, path): id = "unknown_user" print("close connection to user: " + id) -ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) -ssl_context.load_cert_chain(cert_file , keyfile=key_file) -start_server = websockets.serve(socket_worker, host='', port=server_port, ssl=ssl_context) +#ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) +#ssl_context.load_cert_chain(cert_file, keyfile=key_file) + +start_server = websockets.serve( + new_socket_worker, host='', port=server_port) # , ssl=ssl_context) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() diff --git a/match.py b/match.py index 3a84ae7..e1fa5cf 100644 --- a/match.py +++ b/match.py @@ -65,7 +65,9 @@ class Match(object): 'last_move': self.last_move, 'game_over': self.game_over, 'player_won': self.player_won, - 'active_player': self.player_a_name if self.is_player_a else self.player_b_name + 'active_player': self.player_a_name if self.is_player_a else self.player_b_name, + 'player_a': self.player_a_name, + 'player_b': self.player_b_name } return json.dumps(match_obj) @@ -95,15 +97,17 @@ class Match(object): last_sub_x = self.last_move['sub_x'] last_sub_y = self.last_move['sub_y'] - if sub_x != last_x and self.global_field[last_sub_y, last_sub_x] == FIELD_EMPTY: + if sub_x != last_x and self.global_field[last_y, last_x] == FIELD_EMPTY: # user is not allowed to place everywhere! wrong move! return False - if sub_y != last_y and self.global_field[last_sub_y, last_sub_x] == FIELD_EMPTY: + if sub_y != last_y and self.global_field[last_y, last_x] == FIELD_EMPTY: return False if self.complete_field[sub_y * self.n + y][sub_x * self.n + x] != FIELD_EMPTY: return False + + return True def is_full(self, field): return not field.__contains__(FIELD_EMPTY) @@ -136,17 +140,17 @@ class Match(object): if x + y == self.n - 1: is_sec_diag = True for i in range(self.n): - if field[i, -i] != val: + if field[i, self.n - i - 1] != val: is_sec_diag = False break return is_col or is_row or is_main_diag or is_sec_diag def move(self, move_dict): - sub_x = move_dict['sub_x'] - sub_y = move_dict['sub_y'] - x = move_dict['x'] - y = move_dict['y'] + sub_x = int(move_dict['sub_x']) + sub_y = int(move_dict['sub_y']) + x = int(move_dict['x']) + y = int(move_dict['y']) abs_x = sub_x * self.n + x abs_y = sub_y * self.n + y @@ -154,34 +158,34 @@ class Match(object): player_mark = FIELD_USER_A if self.is_player_a else FIELD_USER_B if not self.is_move_valid(sub_x, sub_y, x, y): + print("invalid move") return False # else: move! self.complete_field[abs_y, abs_x] = player_mark # encode move: - self.last_move = move_dict + self.last_move = {'sub_x': sub_x, 'sub_y': sub_y, 'x': x, 'y': y} # check whether this indicates changes in the global field: - assert self.global_field[sub_y, sub_x] == FIELD_EMPTY + if self.global_field[sub_y, sub_x] != FIELD_EMPTY: + print("field not empty") + return False subgrid = self.complete_field[sub_y * self.n: ( sub_y + 1) * self.n, sub_x * self.n: (sub_x + 1) * self.n] if self.check_win(subgrid, x, y): - self.global_field[sub_x, sub_y] = player_mark + self.global_field[sub_y, sub_x] = player_mark + if self.check_win(self.global_field, sub_x, sub_y): + self.game_over = True + self.player_won = self.player_a_name if self.is_player_a else self.player_b_name elif self.is_full(subgrid): - self.global_field[sub_x, sub_y] = FIELD_DRAW - - # check global state: - if self.check_win(self.global_field, sub_x, sub_y): - self.game_over = True - self.player_won = self.player_a_name if self.is_player_a else self.player_b_name - - elif self.is_full(self.global_field): - self.game_over = True - self.player_won = None + self.global_field[sub_y, sub_x] = FIELD_DRAW + if self.is_full(self.global_field): + self.game_over = True + self.player_won = None self.is_player_a = not self.is_player_a diff --git a/match_manager.py b/match_manager.py index ab75d45..4d0c578 100644 --- a/match_manager.py +++ b/match_manager.py @@ -40,6 +40,20 @@ class MatchManager(object): query = f"UPDATE matches SET match_state='{match.to_json_state()}', active_user='{match.get_current_player()}', last_active='{now}' WHERE id='{match_id}'" DatabaseConnection.global_single_execution(query) + def apply_move(self, move_data): + match = self.get_match(move_data['id']) + if match is None: + return None + + if not match.move(move_data): + print("error applying match move") + return None + + + self.update_match(move_data['id'], match) + print("updated match") + return match + def delete_match(self, match_id): query = f"DELETE FROM matches WHERE id='{match_id}'" DatabaseConnection.global_single_execution(query) diff --git a/session_manager.py b/session_manager.py index a8d316e..543143f 100644 --- a/session_manager.py +++ b/session_manager.py @@ -29,7 +29,11 @@ class SessionManager(object): # check if already existent (but should not be the case) if len(DatabaseConnection.global_single_query(f"SELECT id FROM sessions WHERE id='{new_id}'")) > 0: # okay, next try: - return self.create_session_for_registered_user(self, user_name) + return self.create_session_for_registered_user(user_name) + + # delete other active sessions: + query = f"DELETE FROM sessions WHERE registered_user='{user_name}'" + DatabaseConnection.global_single_execution(query) query = f"INSERT INTO sessions (id, registered_user, temp_user, last_seen) VALUES ( '{new_id}', '{user_name}', NULL, '{datetime.datetime.now()}')" DatabaseConnection.global_single_execution(query) @@ -40,7 +44,11 @@ class SessionManager(object): # check if already existent (but should not be the case) if len(DatabaseConnection.global_single_query(f"SELECT id FROM sessions WHERE id='{new_id}'")) > 0: # okay, next try: - return self.create_session_for_registered_user(self, user_name) + return self.create_session_for_registered_user(user_name) + + # delete other active sessions: + query = f"DELETE FROM sessions WHERE temp_user='{user_name}'" + DatabaseConnection.global_single_execution(query) query = f"INSERT INTO sessions (id, registered_user, temp_user, last_seen) VALUES ( '{new_id}', NULL, '{user_name}', '{datetime.datetime.now()}')" DatabaseConnection.global_single_execution(query) diff --git a/user_manager.py b/user_manager.py index c5a4e4e..ae243b8 100644 --- a/user_manager.py +++ b/user_manager.py @@ -47,19 +47,3 @@ class UserManager(object): query = "SELECT name, last_seen FROM users" return DatabaseConnection.global_single_query(query) - -def create_test_manager(): - import settings - DatabaseConnection(settings.db_host, - settings.db_port, - settings.db_user, - settings.db_pw, - settings.db_db, - settings.db_charset) - um = UserManager() - return um - - -# test routines: -if __name__ == "__main__": - pass