Cosmetics. Last quick commit fixed a bug in API at login to get uid
[rpsls-web.git] / server.py
... / ...
CommitLineData
1import socketio
2import eventlet
3import sqlite3
4from re import match as re_match
5from datetime import date
6from os import getenv
7from sys import path as sys_path
8
9# Create a Socket.IO server (CORS arg required on server, not locally)
10MODE = getenv('RPSLS_MODE')
11allowed_origin = 'https://rpsls.auder.net' if MODE=='production' else '*'
12sio = socketio.Server(cors_allowed_origins=allowed_origin)
13
14RPSLS_PATH = sys_path[0]
15DB_PATH = RPSLS_PATH + '/db/rpsls.sqlite'
16
17searching = {} #someone seeks a game? (uid + sid)
18connected = {} #map uid --> sid (seek stage)
19
20# Avoid repeating DB connect/close code
21def db_operation(func):
22 con = sqlite3.connect(DB_PATH)
23 cur = con.cursor()
24 func(cur)
25 con.commit()
26 con.close()
27
28@sio.event
29def disconnect(sid):
30 """ Triggered at page reload or tab close """
31 global connected, searching
32 try:
33 key_idx = list(connected.values()).index(sid)
34 del connected[list(connected.keys())[key_idx]]
35 except ValueError:
36 # If the user didn't seek, no key to find
37 pass
38 if searching and searching["sid"] == sid:
39 searching = {}
40
41@sio.event
42def login(sid, data):
43 """ When user sends name from /login page """
44 if not re_match(r"^[a-zA-Z]{3,}$", data):
45 sio.emit("login", {"err": "Name: letters only"}, room=sid)
46 return
47 def upsert(cur):
48 uid = 0
49 try:
50 # Always try to insert (new) Users row
51 cur.execute("insert into Users (name) values (?)", (data,))
52 uid = cur.lastrowid
53 except sqlite3.IntegrityError as err:
54 # If fails: user already exists, find its ID
55 if str(err) == "UNIQUE constraint failed: Users.name":
56 cur.execute("select id from Users where name = ?", (data,))
57 uid = cur.fetchone()[0]
58 else:
59 raise
60 sio.emit("login", {"name": data, "uid": uid}, room=sid)
61 db_operation(upsert)
62
63@sio.event
64def seek(sid, data):
65 """ When user click on 'Play' button """
66 global connected, searching
67 connected[data["uid"]] = sid
68 if not searching:
69 searching = {"uid": data["uid"], "sid": sid, "name": data["name"]}
70 else:
71 # Active seek pending: create game
72 opponent = searching
73 searching = {}
74 def create_game(cur):
75 today = (date.today(),)
76 cur.execute("insert into Games (created) values (?)", today)
77 gid = cur.lastrowid
78 # To room == sid, opponent is me. To my room, it's him/her
79 sio.emit("play",
80 {"gid":gid, "oppid":opponent["uid"], "oppname":opponent["name"]},
81 room=sid)
82 sio.emit("play",
83 {"gid":gid, "oppid":data["uid"], "oppname":data["name"]},
84 room=opponent["sid"])
85 id_list = [(data["uid"],gid), (opponent["uid"],gid)]
86 cur.executemany("insert into Players (uid,gid) values (?,?)", id_list)
87 db_operation(create_game)
88
89@sio.event
90def move(sid, data):
91 """ New move to DB + transmit to opponent """
92 sio.emit("move", data, room=connected[data["oppid"]])
93 db_operation(lambda cur:
94 cur.execute("insert into Moves (uid,gid,choice,mnum) values (?,?,?,?)",
95 (data["uid"],data["gid"],data["choice"],data["mnum"]))
96 )
97
98@sio.event
99def inc_pts(sid, data):
100 """ Add a point to the player (who won last round) """
101 db_operation(lambda cur:
102 cur.execute("update Players set points=points+1 where uid=? and gid=?",
103 (data["uid"],data["gid"]))
104 )
105
106static_files = {
107 '/': RPSLS_PATH + '/index.html',
108 '/rpsls.js': RPSLS_PATH + '/rpsls.js',
109 '/favicon.ico': RPSLS_PATH + '/favicon.ico',
110 '/assets': RPSLS_PATH + '/assets'
111}
112
113PORT = getenv('RPSLS_PORT')
114if PORT is None:
115 PORT = "8000"
116PORT = int(PORT)
117
118# Wrap with a WSGI application
119app = socketio.WSGIApp(sio, static_files=static_files)
120eventlet.wsgi.server(eventlet.listen(('127.0.0.1', PORT)), app)