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