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