temporary session are now available
This commit is contained in:
parent
ef5696f61d
commit
0c342eff7b
12
README.md
12
README.md
@ -39,6 +39,7 @@ communication with the web client is done by a (far from any standard and almost
|
||||
"data": {
|
||||
"id": "...",
|
||||
"revoke_time": <revoke_time>,
|
||||
"ranked": "<true | false">,
|
||||
"match_state": <null| <match_state>>
|
||||
}
|
||||
}
|
||||
@ -101,12 +102,18 @@ server response:
|
||||
|
||||
**login or register**:
|
||||
|
||||
|
||||
|
||||
keep pw null for requesting a temporary session (will be deleted after one hour of inactivity and matches are not ranked)
|
||||
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "login",
|
||||
"data": {
|
||||
"name": "<player_name>",
|
||||
"pw": "<password>"
|
||||
"pw": "<password> | null"
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -118,7 +125,8 @@ response:
|
||||
"type": "login_response",
|
||||
"data": {
|
||||
"success": <true|false>,
|
||||
"id": "<session-id>",
|
||||
"id": "<session-id>",
|
||||
"registered": <true | false>,
|
||||
"msg": "..."
|
||||
}
|
||||
}
|
||||
|
@ -141,11 +141,14 @@ class ConnectionHandler(object):
|
||||
"success": True,
|
||||
"id": conn.id,
|
||||
"user": conn.user_name,
|
||||
"registered": conn.is_registered_user,
|
||||
"msg": ""
|
||||
}
|
||||
}))
|
||||
|
||||
await self.send_elo(conn)
|
||||
await self.send_friends(conn)
|
||||
if conn.is_registered_user:
|
||||
await self.send_friends(conn)
|
||||
await self._on_match_state_req(conn, None)
|
||||
return conn
|
||||
await socket.send(json.dumps({
|
||||
@ -154,45 +157,12 @@ class ConnectionHandler(object):
|
||||
"success": False,
|
||||
"id": None,
|
||||
"user": None,
|
||||
"registered": False,
|
||||
"msg": "session not available"
|
||||
}
|
||||
}))
|
||||
return None
|
||||
|
||||
elif msg['type'] == 'temp_session':
|
||||
name = msg['data']['name'].lower()
|
||||
if valid_name(name) and len(self.session_manager.get_session_by_temp_user(name)) == 0:
|
||||
if len(self.user_manager.get_user(name)) == 0:
|
||||
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": "login_response",
|
||||
"data": {
|
||||
"success": True,
|
||||
"id": id,
|
||||
"msg": "logged in as temporary user " + name
|
||||
}
|
||||
}))
|
||||
|
||||
return conn
|
||||
|
||||
await socket.send(json.dumps({
|
||||
"type": "login_response",
|
||||
"data": {
|
||||
"success": False,
|
||||
"id": None,
|
||||
"msg": "user name not available"
|
||||
}
|
||||
}))
|
||||
return None
|
||||
|
||||
elif msg['type'] == 'login':
|
||||
response_msg = ""
|
||||
success = False
|
||||
@ -200,6 +170,7 @@ class ConnectionHandler(object):
|
||||
name = None
|
||||
pw = None
|
||||
conn = None
|
||||
registered_user = True
|
||||
|
||||
try:
|
||||
|
||||
@ -225,15 +196,32 @@ class ConnectionHandler(object):
|
||||
|
||||
else:
|
||||
response_msg = "invalid password for user " + name
|
||||
|
||||
elif valid_name(name) and len(name) <= 16 and len(pw) == 0:
|
||||
# no password -> temporary account
|
||||
registered_user = False
|
||||
|
||||
# check whether already a temporary session exists for that user:
|
||||
if (len(self.session_manager.get_session_by_temp_user(name)) > 0) or (len(self.user_manager.get_user(name)) > 0):
|
||||
response_msg = f"user '{name}' already exists"
|
||||
success = False
|
||||
|
||||
else:
|
||||
session_id = self.session_manager.create_session_for_temp_user(
|
||||
name)
|
||||
response_msg = f"created a temporary session for user {name}"
|
||||
success = True
|
||||
|
||||
else:
|
||||
response_msg = "invalid username or pw. Only usernames containing alphanumerical symbols (including '_','-') between 3 and 16 characters are allowed!"
|
||||
|
||||
except Exception as e:
|
||||
print("error: " + str(e))
|
||||
response_msg = "invalid username or pw. Only usernames containing alphanumerical symbols (including '_','-') between 3 and 16 characters are allowed!"
|
||||
|
||||
if success:
|
||||
conn = Connection(id=session_id, user_name=name,
|
||||
registered=True, websocket=socket)
|
||||
registered=registered_user, websocket=socket)
|
||||
self._add_connection(conn)
|
||||
|
||||
await socket.send(json.dumps({
|
||||
@ -241,6 +229,7 @@ class ConnectionHandler(object):
|
||||
"data": {
|
||||
"success": success,
|
||||
"id": session_id,
|
||||
"registered": registered_user,
|
||||
"msg": response_msg
|
||||
}
|
||||
}))
|
||||
@ -265,6 +254,7 @@ class ConnectionHandler(object):
|
||||
"data": {
|
||||
"id": m.id,
|
||||
"revoke_time": m.get_sql_revoke_time(),
|
||||
"ranked": m.ranked,
|
||||
"match_state": state
|
||||
}
|
||||
}
|
||||
@ -278,6 +268,7 @@ class ConnectionHandler(object):
|
||||
"data": {
|
||||
"id": m.id,
|
||||
"revoke_time": m.get_sql_revoke_time(),
|
||||
"ranked": m.ranked,
|
||||
"match_state": state
|
||||
}
|
||||
}
|
||||
@ -355,7 +346,7 @@ class ConnectionHandler(object):
|
||||
return
|
||||
try:
|
||||
if valid_name(opponent):
|
||||
if len(self.user_manager.get_user(opponent)) > 0:
|
||||
if len(self.user_manager.get_user(opponent)) > 0 or len(self.session_manager.get_session_by_temp_user(opponent)) > 0:
|
||||
|
||||
if len(self.match_manager.get_matches_for_user(opponent)) >= 5:
|
||||
await conn.websocket.send(
|
||||
@ -435,6 +426,7 @@ class ConnectionHandler(object):
|
||||
"data": {
|
||||
"id": db_match['id'],
|
||||
"revoke_time": match.get_sql_revoke_time(),
|
||||
"ranked": match.ranked,
|
||||
"match_state": json.loads(match.to_json_state())
|
||||
}
|
||||
}))
|
||||
@ -469,6 +461,7 @@ class ConnectionHandler(object):
|
||||
'data': {
|
||||
'id': match.id,
|
||||
'revoke_time': match.get_sql_revoke_time(),
|
||||
'ranked': match.ranked,
|
||||
'match_state': json.loads(match_state)
|
||||
}
|
||||
}))
|
||||
@ -487,6 +480,7 @@ class ConnectionHandler(object):
|
||||
'data': {
|
||||
'id': match.id,
|
||||
'revoke_time': match.get_sql_revoke_time(),
|
||||
'ranked': match.ranked,
|
||||
'match_state': json.loads(match_state)
|
||||
}
|
||||
}))
|
||||
@ -507,34 +501,37 @@ class ConnectionHandler(object):
|
||||
return
|
||||
|
||||
if not match.game_over:
|
||||
# check whether both player made a move. If so, the match is ranked as lost for the player who aborted the match
|
||||
if match.complete_field.__contains__(Match.FIELD_USER_A) and match.complete_field.__contains__(Match.FIELD_USER_B):
|
||||
if match.ranked:
|
||||
# check whether both player made a move. If so, the match is ranked as lost for the player who aborted the match
|
||||
if match.complete_field.__contains__(Match.FIELD_USER_A) and match.complete_field.__contains__(Match.FIELD_USER_B):
|
||||
|
||||
# update rankings:
|
||||
player_lost = conn.user_name
|
||||
player_won = match.player_a_name if player_lost == match.player_b_name else match.player_b_name
|
||||
# update rankings:
|
||||
player_lost = conn.user_name
|
||||
player_won = match.player_a_name if player_lost == match.player_b_name else match.player_b_name
|
||||
|
||||
elo_won = self.user_manager.get_elo(player_won)
|
||||
elo_lost = self.user_manager.get_elo(player_lost)
|
||||
elo_won = self.user_manager.get_elo(player_won)
|
||||
elo_lost = self.user_manager.get_elo(player_lost)
|
||||
|
||||
# calculate elo values:
|
||||
p_won = elo_p_win(elo_won, elo_lost)
|
||||
p_lost = 1 - p_won
|
||||
# calculate elo values:
|
||||
p_won = elo_p_win(elo_won, elo_lost)
|
||||
p_lost = 1 - p_won
|
||||
|
||||
new_elo_won = elo_update(elo_won, 1, p_won)
|
||||
new_elo_lost = elo_update(elo_lost, 0, p_lost)
|
||||
new_elo_won = elo_update(elo_won, 1, p_won)
|
||||
new_elo_lost = elo_update(elo_lost, 0, p_lost)
|
||||
|
||||
self.user_manager.update_elo(player_won, new_elo_won)
|
||||
self.user_manager.update_elo(player_lost, new_elo_lost)
|
||||
self.user_manager.update_elo(
|
||||
player_won, new_elo_won)
|
||||
self.user_manager.update_elo(
|
||||
player_lost, new_elo_lost)
|
||||
|
||||
await self.send_elo(conn)
|
||||
await self.send_elo(conn)
|
||||
|
||||
if player_won in self.open_connections_by_user:
|
||||
other_conn = self.open_connections_by_user[player_won]
|
||||
await self.send_elo(other_conn)
|
||||
if player_won in self.open_connections_by_user:
|
||||
other_conn = self.open_connections_by_user[player_won]
|
||||
await self.send_elo(other_conn)
|
||||
|
||||
debug(
|
||||
f"Match {match.id} is aborted by {player_lost} (against {player_won}). Update elo-rankings: {elo_won}->{new_elo_won} and {elo_lost}->{new_elo_lost}")
|
||||
debug(
|
||||
f"Match {match.id} is aborted by {player_lost} (against {player_won}). Update elo-rankings: {elo_won}->{new_elo_won} and {elo_lost}->{new_elo_lost}")
|
||||
|
||||
match.game_over = True
|
||||
|
||||
@ -547,6 +544,7 @@ class ConnectionHandler(object):
|
||||
'data': {
|
||||
'id': match_id,
|
||||
'revoke_time': match.get_sql_revoke_time(),
|
||||
'ranked': match.ranked,
|
||||
'match_state': json.loads(match_state)
|
||||
}
|
||||
})
|
||||
@ -562,7 +560,7 @@ class ConnectionHandler(object):
|
||||
match_state = None
|
||||
if match is not None:
|
||||
match_state = match.to_json_state()
|
||||
|
||||
|
||||
if match_state is not None:
|
||||
|
||||
await conn.send(json.dumps({
|
||||
@ -570,6 +568,7 @@ class ConnectionHandler(object):
|
||||
'data': {
|
||||
'id': match.id,
|
||||
'revoke_time': match.get_sql_revoke_time(),
|
||||
'ranked': match.ranked,
|
||||
'match_state': json.loads(match_state)
|
||||
}
|
||||
}))
|
||||
@ -656,6 +655,18 @@ class ConnectionHandler(object):
|
||||
|
||||
top_names, top_elos = self.user_manager.get_highscores(100, 0)
|
||||
|
||||
if not conn.is_registered_user:
|
||||
await conn.send(json.dumps({
|
||||
'type': 'elo_update',
|
||||
'data': {
|
||||
'elo': None,
|
||||
'rank': None,
|
||||
'top_names': top_names,
|
||||
'top_elos': top_elos
|
||||
}
|
||||
}))
|
||||
return
|
||||
|
||||
await conn.send(json.dumps({
|
||||
'type': 'elo_update',
|
||||
'data': {
|
||||
@ -687,7 +698,8 @@ class ConnectionHandler(object):
|
||||
|
||||
t = msg['type']
|
||||
|
||||
self.user_manager.touch_user(conn.user_name)
|
||||
if conn.is_registered_user:
|
||||
self.user_manager.touch_user(conn.user_name)
|
||||
self.session_manager.touch_session(conn.id)
|
||||
if t == "match_request":
|
||||
await self._on_match_req(conn, msg['data'])
|
||||
|
@ -17,10 +17,10 @@ def create_tables():
|
||||
queries = [
|
||||
"DROP TABLE IF EXISTS matches",
|
||||
"DROP TABLE IF EXISTS sessions",
|
||||
"DROP TABLE IF EXISTS users",
|
||||
"DROP TABLE IF EXISTS friends",
|
||||
"DROP TABLE IF EXISTS users",
|
||||
"CREATE TABLE users (name varchar(16) NOT NULL, pw_hash varchar(128) NOT NULL, pw_salt varchar(32) NOT NULL, last_seen datetime NOT NULL, elo int NOT NULL, PRIMARY KEY (name)) CHARACTER SET " + settings.db_charset,
|
||||
"CREATE TABLE matches (id varchar(32) NOT NULL, user_a varchar(16) NOT NULL, user_b varchar(16) NOT NULL, match_state varchar(4096) NOT NULL, active_user varchar(16), last_active datetime NOT NULL, FOREIGN KEY (user_a) REFERENCES users(name), FOREIGN KEY (user_b) REFERENCES users(name), FOREIGN KEY (active_user) REFERENCES users(name)) CHARACTER SET " + settings.db_charset,
|
||||
"CREATE TABLE matches (id varchar(32) NOT NULL, user_a varchar(16) NOT NULL, user_b varchar(16) NOT NULL, match_state varchar(4096) NOT NULL, active_user varchar(16), last_active datetime NOT NULL, ranked BOOLEAN NOT NULL) CHARACTER SET " + settings.db_charset,
|
||||
"CREATE TABLE sessions (id varchar(32) NOT NULL, registered_user varchar(16), temp_user varchar(16), last_seen datetime NOT NULL, PRIMARY KEY (id), FOREIGN KEY(registered_user) REFERENCES users(name)) CHARACTER SET " + settings.db_charset,
|
||||
"CREATE TABLE friends (user varchar(16) NOT NULL, friend varchar(16) NOT NULL, FOREIGN KEY(user) REFERENCES users(name), FOREIGN KEY(friend) REFERENCES users(name)) CHARACTER SET " + settings.db_charset
|
||||
]
|
||||
|
2
main.py
2
main.py
@ -15,7 +15,7 @@ import datetime
|
||||
from tools import debug
|
||||
|
||||
um = UserManager(account_revoke_time)
|
||||
sm = SessionManager(session_revove_time)
|
||||
sm = SessionManager(session_revove_time, temporary_session_revoke_time)
|
||||
mm = MatchManager(um, match_revoke_time)
|
||||
ch = ConnectionHandler(sm, um, mm, revoke_check_interval)
|
||||
|
||||
|
3
match.py
3
match.py
@ -38,7 +38,7 @@ class Match(object):
|
||||
FIELD_USER_B = 2
|
||||
FIELD_DRAW = 3
|
||||
|
||||
def __init__(self, n, match_id, revoke_time, player_a_name, player_b_name, json_state=None):
|
||||
def __init__(self, n, match_id, revoke_time, player_a_name, player_b_name, json_state=None, ranked=False):
|
||||
self.n = n
|
||||
self.id = match_id
|
||||
self.complete_field = np.zeros(shape=(n*n, n*n), dtype=int)
|
||||
@ -51,6 +51,7 @@ class Match(object):
|
||||
self.player_a_name = player_a_name
|
||||
self.player_b_name = player_b_name
|
||||
self.revoke_time = revoke_time
|
||||
self.ranked = ranked
|
||||
|
||||
if json_state is not None:
|
||||
self.from_json_state(json_state)
|
||||
|
@ -22,7 +22,7 @@ class MatchManager(object):
|
||||
|
||||
revoke_time = result[0]['last_active'] + self.match_lifespan_timedelta
|
||||
match = Match(n=settings.n, match_id=id, revoke_time=revoke_time, player_a_name=result[0]['user_a'],
|
||||
player_b_name=result[0]['user_b'], json_state=result[0]['match_state'])
|
||||
player_b_name=result[0]['user_b'], json_state=result[0]['match_state'], ranked=result[0]['ranked'])
|
||||
return match
|
||||
|
||||
def get_matches_for_user(self, user_name: str) -> list:
|
||||
@ -35,13 +35,16 @@ class MatchManager(object):
|
||||
if len(DatabaseConnection.global_single_query("SELECT id FROM matches WHERE id=%s", (match_id))) > 0:
|
||||
return self.create_new_match(user_a, user_b)
|
||||
|
||||
ranked = len(self.user_manager.get_user(user_a)) > 0 and len(
|
||||
self.user_manager.get_user(user_b)) > 0
|
||||
|
||||
now = datetime.datetime.now()
|
||||
match = Match(n=settings.n, match_id=match_id, revoke_time=now + self.match_lifespan_timedelta,
|
||||
player_a_name=user_a, player_b_name=user_b)
|
||||
|
||||
query = "INSERT INTO matches (id, user_a, user_b, match_state, active_user, last_active) VALUES (%s, %s, %s, %s, %s,%s)"
|
||||
player_a_name=user_a, player_b_name=user_b, ranked=ranked)
|
||||
|
||||
query = "INSERT INTO matches (id, user_a, user_b, match_state, active_user, last_active, ranked) VALUES (%s, %s, %s, %s, %s,%s, %s)"
|
||||
DatabaseConnection.global_single_execution(
|
||||
query, (match_id, user_a, user_b, match.to_json_state(), match.get_current_player(), get_sql_time(now)))
|
||||
query, (match_id, user_a, user_b, match.to_json_state(), match.get_current_player(), get_sql_time(now), ranked))
|
||||
return match
|
||||
|
||||
def update_match(self, match_id: str, match: Match, update_in_db=True) -> None:
|
||||
@ -53,7 +56,7 @@ class MatchManager(object):
|
||||
query, (match.to_json_state(), match.get_current_player(), now, match_id))
|
||||
|
||||
# check whether we have to update the elo values (game over triggered by the last move)
|
||||
if match.game_over:
|
||||
if match.game_over and match.ranked:
|
||||
if match.player_won is not None:
|
||||
|
||||
player_won = match.player_won
|
||||
|
@ -7,8 +7,9 @@ from tools import debug
|
||||
|
||||
|
||||
class SessionManager(object):
|
||||
def __init__(self, session_lifespan_timedelta):
|
||||
def __init__(self, session_lifespan_timedelta, temp_session_lifespan_timedelta):
|
||||
self.session_lifespan_timedelta = session_lifespan_timedelta
|
||||
self.temp_session_lifespan_timedelta = temp_session_lifespan_timedelta
|
||||
|
||||
def get_session_by_id(self, session_id):
|
||||
query = "SELECT * FROM sessions WHERE id=%s"
|
||||
@ -16,7 +17,8 @@ class SessionManager(object):
|
||||
|
||||
def touch_session(self, session_id):
|
||||
query = "UPDATE sessions SET last_seen=%s WHERE id=%s"
|
||||
DatabaseConnection.global_single_execution(query, (datetime.datetime.now(), session_id))
|
||||
DatabaseConnection.global_single_execution(
|
||||
query, (datetime.datetime.now(), session_id))
|
||||
|
||||
def get_session_by_registered_user(self, user_name):
|
||||
query = "SELECT * FROM sessions WHERE registered_user=%s"
|
||||
@ -38,7 +40,8 @@ class SessionManager(object):
|
||||
DatabaseConnection.global_single_execution(query, (user_name))
|
||||
|
||||
query = "INSERT INTO sessions (id, registered_user, temp_user, last_seen) VALUES ( %s, %s, NULL, %s)"
|
||||
DatabaseConnection.global_single_execution(query, (new_id, user_name, datetime.datetime.now()))
|
||||
DatabaseConnection.global_single_execution(
|
||||
query, (new_id, user_name, datetime.datetime.now()))
|
||||
|
||||
return new_id
|
||||
|
||||
@ -54,19 +57,38 @@ class SessionManager(object):
|
||||
DatabaseConnection.global_single_execution(query, (user_name))
|
||||
|
||||
query = "INSERT INTO sessions (id, registered_user, temp_user, last_seen) VALUES ( %s, NULL, %s, %s)"
|
||||
DatabaseConnection.global_single_execution(query, (new_id, user_name, datetime.datetime.now()))
|
||||
DatabaseConnection.global_single_execution(
|
||||
query, (new_id, user_name, datetime.datetime.now()))
|
||||
return new_id
|
||||
|
||||
def delete_session(self, session_id):
|
||||
query = "DELETE FROM sessions WHERE id=%s"
|
||||
DatabaseConnection.global_single_execution(query, (session_id))
|
||||
|
||||
def revoke_inactive_temporary_sessions(self):
|
||||
revoke_time = datetime.datetime.now() - self.temp_session_lifespan_timedelta
|
||||
query = "SELECT * from sessions WHERE last_seen < %s AND temp_user IS NOT NULL"
|
||||
revoked_temp_sessions = DatabaseConnection.global_single_query(
|
||||
query, (get_sql_time(revoke_time)))
|
||||
|
||||
query = "DELETE FROM sessions WHERE last_seen < %s AND temp_user IS NOT NULL"
|
||||
DatabaseConnection.global_single_execution(
|
||||
query, (get_sql_time(revoke_time)))
|
||||
|
||||
for session in revoked_temp_sessions:
|
||||
# remove from matches:
|
||||
query = "DELETE FROM matches WHERE user_a=%s OR user_b=%s"
|
||||
DatabaseConnection.global_single_execution(
|
||||
query, (session['temp_user'], session['temp_user']))
|
||||
|
||||
debug("delete revoked sessions: " + str(revoked_temp_sessions))
|
||||
|
||||
def revoke_inactive_sessions(self):
|
||||
revoke_time = datetime.datetime.now() - self.session_lifespan_timedelta
|
||||
query = "SELECT * from sessions WHERE last_seen < %s"
|
||||
revoked_sessions = DatabaseConnection.global_single_query(query, (get_sql_time(revoke_time)))
|
||||
revoked_sessions = DatabaseConnection.global_single_query(
|
||||
query, (get_sql_time(revoke_time)))
|
||||
query = "DELETE FROM sessions WHERE last_seen < %s"
|
||||
DatabaseConnection.global_single_execution(query, (get_sql_time(revoke_time)))
|
||||
DatabaseConnection.global_single_execution(
|
||||
query, (get_sql_time(revoke_time)))
|
||||
debug("delete revoked sessions: " + str(revoked_sessions))
|
||||
|
||||
|
||||
|
@ -61,8 +61,6 @@ class UserManager(object):
|
||||
query, (user_name, pw_hash, pw_salt, datetime.datetime.now(), elo_start_value))
|
||||
|
||||
def touch_user(self, user_name):
|
||||
matches = self.get_user(user_name)
|
||||
assert len(matches) == 1
|
||||
query = "UPDATE users SET last_seen=%s WHERE name=%s"
|
||||
DatabaseConnection.global_single_execution(
|
||||
query, (datetime.datetime.now(), user_name))
|
||||
|
Loading…
Reference in New Issue
Block a user