Commit | Line | Data |
---|---|---|
bbb90bba BA |
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) |