almost working version
This commit is contained in:
parent
d6b68de008
commit
070a60a272
100
.vscode/tags
vendored
Normal file
100
.vscode/tags
vendored
Normal file
@ -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
|
70
README.md
70
README.md
@ -109,9 +109,29 @@ response:
|
|||||||
game_over: <true | false>,
|
game_over: <true | false>,
|
||||||
player_won: <null | <player_name>>
|
player_won: <null | <player_name>>
|
||||||
current_player: <null | <player_name>>
|
current_player: <null | <player_name>>
|
||||||
|
player_a: "..."
|
||||||
|
player_b: "..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**match**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "match_update",
|
||||||
|
"data": {
|
||||||
|
"id": "...",
|
||||||
|
"match_state": <null| <match_state>>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**new temp session**
|
**new temp session**
|
||||||
|
|
||||||
client
|
client
|
||||||
@ -129,11 +149,11 @@ server response:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "temp_session_response",
|
"type": "login_response",
|
||||||
"data": {
|
"data": {
|
||||||
"success": <true|false>,
|
"success": <true|false>,
|
||||||
"id": "<session-id>",
|
"id": "<session-id>",
|
||||||
"message": "..."
|
"msg": "..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -163,15 +183,38 @@ server response:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**register**:
|
**login or register**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "login",
|
||||||
|
"data": {
|
||||||
|
"name": "<player_name>",
|
||||||
|
"pw": "<password>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "login_response",
|
||||||
|
"data": {
|
||||||
|
"success": <true|false>,
|
||||||
|
"id": "<session-id>",
|
||||||
|
"msg": "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**match_request**:
|
**match_request**:
|
||||||
|
|
||||||
client
|
client (or server for sending invites)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -194,8 +237,6 @@ server_response:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**match_move**:
|
**match_move**:
|
||||||
|
|
||||||
client
|
client
|
||||||
@ -204,6 +245,7 @@ client
|
|||||||
{
|
{
|
||||||
"type": "move",
|
"type": "move",
|
||||||
"data": {
|
"data": {
|
||||||
|
"id": "match_id",
|
||||||
"sub_x": "...",
|
"sub_x": "...",
|
||||||
"sub_y": "...",
|
"sub_y": "...",
|
||||||
"x": "...",
|
"x": "...",
|
||||||
@ -212,18 +254,6 @@ client
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
server response: (maybe useless?)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "move_response",
|
|
||||||
"data": {
|
|
||||||
"success": true,
|
|
||||||
"msg": "..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**match update**
|
**match update**
|
||||||
@ -255,3 +285,5 @@ client:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,11 +22,30 @@ class Connection(object):
|
|||||||
id,
|
id,
|
||||||
user_name: str,
|
user_name: str,
|
||||||
registered: bool,
|
registered: bool,
|
||||||
websocket: websocket.WebSocketServerProtocol):
|
websocket: websockets.WebSocketServerProtocol):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.user_name = user_name
|
self.user_name = user_name
|
||||||
self.websocket = websocket
|
self.websocket = websocket
|
||||||
self.is_registered_user = registered
|
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):
|
class ConnectionHandler(object):
|
||||||
@ -71,7 +90,7 @@ class ConnectionHandler(object):
|
|||||||
if conn.id in self.open_connections_by_id:
|
if conn.id in self.open_connections_by_id:
|
||||||
del(self.open_connections_by_id[conn.id])
|
del(self.open_connections_by_id[conn.id])
|
||||||
del(self.open_connections_by_user[conn.user_name])
|
del(self.open_connections_by_user[conn.user_name])
|
||||||
conn.websocket.close()
|
conn.close()
|
||||||
|
|
||||||
def close_session(self, id):
|
def close_session(self, id):
|
||||||
tmp = self.session_manager.get_session_by_id(id)
|
tmp = self.session_manager.get_session_by_id(id)
|
||||||
@ -85,18 +104,18 @@ class ConnectionHandler(object):
|
|||||||
|
|
||||||
def _add_connection(self, conn):
|
def _add_connection(self, conn):
|
||||||
self.open_connections_by_id[conn.id] = 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):
|
def _del_connection(self, conn):
|
||||||
del(self.open_connections_by_id[conn.id])
|
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,
|
async def new_connection(self,
|
||||||
socket: websockets.WebSocketServerProtocol,
|
socket: websockets.WebSocketServerProtocol,
|
||||||
login_msg: str):
|
login_msg: str):
|
||||||
|
|
||||||
msg = parse_message(login_msg)
|
msg = parse_message(login_msg)
|
||||||
|
print(msg)
|
||||||
if msg is None:
|
if msg is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -104,7 +123,7 @@ class ConnectionHandler(object):
|
|||||||
conn = self.reconnect_session(socket, msg['data']['id'])
|
conn = self.reconnect_session(socket, msg['data']['id'])
|
||||||
if conn is not None:
|
if conn is not None:
|
||||||
self._add_connection(conn)
|
self._add_connection(conn)
|
||||||
await conn.websocket.send(json.dumps({
|
await conn.send(json.dumps({
|
||||||
"type": "reconnect_response",
|
"type": "reconnect_response",
|
||||||
"data": {
|
"data": {
|
||||||
"success": True,
|
"success": True,
|
||||||
@ -112,7 +131,7 @@ class ConnectionHandler(object):
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
return conn
|
return conn
|
||||||
await conn.websocket.send(json.dumps({
|
await conn.send(json.dumps({
|
||||||
"type": "reconnect_response",
|
"type": "reconnect_response",
|
||||||
"data": {
|
"data": {
|
||||||
"success": False,
|
"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:
|
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(
|
id = self.session_manager.create_session_for_temp_user(
|
||||||
name)
|
name)
|
||||||
|
|
||||||
conn = Connection(
|
conn = Connection(
|
||||||
id=id, user_name=name, registered=False, websocket=socket)
|
id=id, user_name=name, registered=False, websocket=socket)
|
||||||
|
|
||||||
self._add_connection(conn)
|
self._add_connection(conn)
|
||||||
|
|
||||||
await socket.send(json.dumps({
|
await socket.send(json.dumps({
|
||||||
"type": "temp_session_response",
|
"type": "login_response",
|
||||||
"data": {
|
"data": {
|
||||||
"success": True,
|
"success": True,
|
||||||
"id": id,
|
"id": id,
|
||||||
"message": "logged in as temporary user " + name
|
"msg": "logged in as temporary user " + name
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
await socket.send(json.dumps({
|
await socket.send(json.dumps({
|
||||||
"type": "temp_session_response",
|
"type": "login_response",
|
||||||
"data": {
|
"data": {
|
||||||
"success": False,
|
"success": False,
|
||||||
"id": None,
|
"id": None,
|
||||||
"message": "user name not available"
|
"msg": "user name not available"
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
elif msg['type'] == 'login':
|
elif msg['type'] == 'login':
|
||||||
# TODO
|
response_msg = ""
|
||||||
pass
|
success = False
|
||||||
|
session_id = None
|
||||||
|
name = None
|
||||||
|
pw = None
|
||||||
|
conn = None
|
||||||
|
|
||||||
elif msg['type'] == 'register':
|
try:
|
||||||
# TODO
|
|
||||||
pass
|
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):
|
async def _start_match(self, user_a, user_b):
|
||||||
m = self.match_manager.create_new_match(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:
|
if user_a in self.open_connections_by_user:
|
||||||
await self.open_connections_by_user[user_a].websocket.send(
|
await self.open_connections_by_user[user_a].websocket.send(
|
||||||
json.dumps({
|
json.dumps(
|
||||||
{
|
{
|
||||||
"type": "match_update",
|
"type": "match_update",
|
||||||
"data": {
|
"data": {
|
||||||
@ -172,11 +248,11 @@ class ConnectionHandler(object):
|
|||||||
"match_state": state
|
"match_state": state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
)
|
)
|
||||||
if user_b in self.open_connections_by_user:
|
if user_b in self.open_connections_by_user:
|
||||||
await self.open_connections_by_user[user_b].websocket.send(
|
await self.open_connections_by_user[user_b].websocket.send(
|
||||||
json.dumps({
|
json.dumps(
|
||||||
{
|
{
|
||||||
"type": "match_update",
|
"type": "match_update",
|
||||||
"data": {
|
"data": {
|
||||||
@ -184,23 +260,168 @@ class ConnectionHandler(object):
|
|||||||
"match_state": state
|
"match_state": state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _on_match_req(self, conn, data):
|
async def _on_match_req(self, conn, data):
|
||||||
|
if data['player'] is None:
|
||||||
if len(self.match_queue) > 0:
|
if len(self.match_queue) > 0:
|
||||||
# it's a match!
|
# it's a match!
|
||||||
user_a = self.match_queue.pop()
|
user_a = self.match_queue.pop()
|
||||||
self._start_match(user_a, conn.user_name)
|
await self._start_match(user_a, conn.user_name)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.match_queue.append(conn.user_name)
|
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:
|
||||||
|
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):
|
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):
|
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):
|
async def disconnect(self, conn):
|
||||||
self._del_connection(conn)
|
self._del_connection(conn)
|
||||||
@ -208,6 +429,8 @@ class ConnectionHandler(object):
|
|||||||
async def handle_message(self, conn, msg_str):
|
async def handle_message(self, conn, msg_str):
|
||||||
msg = parse_message(msg_str)
|
msg = parse_message(msg_str)
|
||||||
|
|
||||||
|
print(msg)
|
||||||
|
|
||||||
if msg is None:
|
if msg is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -223,5 +446,8 @@ class ConnectionHandler(object):
|
|||||||
elif t == "end_match":
|
elif t == "end_match":
|
||||||
await self._on_match_close(conn, msg['data'])
|
await self._on_match_close(conn, msg['data'])
|
||||||
|
|
||||||
|
elif t == "match_states_request":
|
||||||
|
await self._on_match_state_req(conn, msg['data'])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("could not interpret message: " + msg_str)
|
print("could not interpret message: " + msg_str)
|
||||||
|
72
main.py
72
main.py
@ -3,7 +3,70 @@ import websockets
|
|||||||
from settings import *
|
from settings import *
|
||||||
import ssl
|
import ssl
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
|
||||||
from game_manager import *
|
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):
|
async def socket_worker(websocket, path):
|
||||||
@ -53,6 +116,7 @@ async def socket_worker(websocket, path):
|
|||||||
else:
|
else:
|
||||||
print("catched exception in worker for unknown user")
|
print("catched exception in worker for unknown user")
|
||||||
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if registered:
|
if registered:
|
||||||
await unregister_user(id)
|
await unregister_user(id)
|
||||||
@ -61,10 +125,12 @@ async def socket_worker(websocket, path):
|
|||||||
id = "unknown_user"
|
id = "unknown_user"
|
||||||
print("close connection to user: " + id)
|
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_until_complete(start_server)
|
||||||
asyncio.get_event_loop().run_forever()
|
asyncio.get_event_loop().run_forever()
|
||||||
|
38
match.py
38
match.py
@ -65,7 +65,9 @@ class Match(object):
|
|||||||
'last_move': self.last_move,
|
'last_move': self.last_move,
|
||||||
'game_over': self.game_over,
|
'game_over': self.game_over,
|
||||||
'player_won': self.player_won,
|
'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)
|
return json.dumps(match_obj)
|
||||||
@ -95,16 +97,18 @@ class Match(object):
|
|||||||
last_sub_x = self.last_move['sub_x']
|
last_sub_x = self.last_move['sub_x']
|
||||||
last_sub_y = self.last_move['sub_y']
|
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!
|
# user is not allowed to place everywhere! wrong move!
|
||||||
return False
|
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
|
return False
|
||||||
|
|
||||||
if self.complete_field[sub_y * self.n + y][sub_x * self.n + x] != FIELD_EMPTY:
|
if self.complete_field[sub_y * self.n + y][sub_x * self.n + x] != FIELD_EMPTY:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def is_full(self, field):
|
def is_full(self, field):
|
||||||
return not field.__contains__(FIELD_EMPTY)
|
return not field.__contains__(FIELD_EMPTY)
|
||||||
|
|
||||||
@ -136,17 +140,17 @@ class Match(object):
|
|||||||
if x + y == self.n - 1:
|
if x + y == self.n - 1:
|
||||||
is_sec_diag = True
|
is_sec_diag = True
|
||||||
for i in range(self.n):
|
for i in range(self.n):
|
||||||
if field[i, -i] != val:
|
if field[i, self.n - i - 1] != val:
|
||||||
is_sec_diag = False
|
is_sec_diag = False
|
||||||
break
|
break
|
||||||
|
|
||||||
return is_col or is_row or is_main_diag or is_sec_diag
|
return is_col or is_row or is_main_diag or is_sec_diag
|
||||||
|
|
||||||
def move(self, move_dict):
|
def move(self, move_dict):
|
||||||
sub_x = move_dict['sub_x']
|
sub_x = int(move_dict['sub_x'])
|
||||||
sub_y = move_dict['sub_y']
|
sub_y = int(move_dict['sub_y'])
|
||||||
x = move_dict['x']
|
x = int(move_dict['x'])
|
||||||
y = move_dict['y']
|
y = int(move_dict['y'])
|
||||||
|
|
||||||
abs_x = sub_x * self.n + x
|
abs_x = sub_x * self.n + x
|
||||||
abs_y = sub_y * self.n + y
|
abs_y = sub_y * self.n + y
|
||||||
@ -154,32 +158,32 @@ class Match(object):
|
|||||||
player_mark = FIELD_USER_A if self.is_player_a else FIELD_USER_B
|
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):
|
if not self.is_move_valid(sub_x, sub_y, x, y):
|
||||||
|
print("invalid move")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# else: move!
|
# else: move!
|
||||||
self.complete_field[abs_y, abs_x] = player_mark
|
self.complete_field[abs_y, abs_x] = player_mark
|
||||||
|
|
||||||
# encode move:
|
# 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:
|
# 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: (
|
subgrid = self.complete_field[sub_y * self.n: (
|
||||||
sub_y + 1) * self.n, sub_x * self.n: (sub_x + 1) * self.n]
|
sub_y + 1) * self.n, sub_x * self.n: (sub_x + 1) * self.n]
|
||||||
|
|
||||||
if self.check_win(subgrid, x, y):
|
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
|
||||||
|
|
||||||
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):
|
if self.check_win(self.global_field, sub_x, sub_y):
|
||||||
self.game_over = True
|
self.game_over = True
|
||||||
self.player_won = self.player_a_name if self.is_player_a else self.player_b_name
|
self.player_won = self.player_a_name if self.is_player_a else self.player_b_name
|
||||||
|
|
||||||
elif self.is_full(self.global_field):
|
elif self.is_full(subgrid):
|
||||||
|
self.global_field[sub_y, sub_x] = FIELD_DRAW
|
||||||
|
if self.is_full(self.global_field):
|
||||||
self.game_over = True
|
self.game_over = True
|
||||||
self.player_won = None
|
self.player_won = None
|
||||||
|
|
||||||
|
@ -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}'"
|
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)
|
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):
|
def delete_match(self, match_id):
|
||||||
query = f"DELETE FROM matches WHERE id='{match_id}'"
|
query = f"DELETE FROM matches WHERE id='{match_id}'"
|
||||||
DatabaseConnection.global_single_execution(query)
|
DatabaseConnection.global_single_execution(query)
|
||||||
|
@ -29,7 +29,11 @@ class SessionManager(object):
|
|||||||
# check if already existent (but should not be the case)
|
# 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:
|
if len(DatabaseConnection.global_single_query(f"SELECT id FROM sessions WHERE id='{new_id}'")) > 0:
|
||||||
# okay, next try:
|
# 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()}')"
|
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)
|
DatabaseConnection.global_single_execution(query)
|
||||||
@ -40,7 +44,11 @@ class SessionManager(object):
|
|||||||
# check if already existent (but should not be the case)
|
# 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:
|
if len(DatabaseConnection.global_single_query(f"SELECT id FROM sessions WHERE id='{new_id}'")) > 0:
|
||||||
# okay, next try:
|
# 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()}')"
|
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)
|
DatabaseConnection.global_single_execution(query)
|
||||||
|
@ -47,19 +47,3 @@ class UserManager(object):
|
|||||||
query = "SELECT name, last_seen FROM users"
|
query = "SELECT name, last_seen FROM users"
|
||||||
return DatabaseConnection.global_single_query(query)
|
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
|
|
||||||
|
Loading…
Reference in New Issue
Block a user