4 from re
import match
as re_match
5 from datetime
import date
7 from sys
import path
as sys_path
9 # Create a Socket.IO server (CORS arg required on server, not locally)
10 MODE
= getenv('RPSLS_MODE')
11 allowed_origin
= 'https://rpsls.auder.net' if MODE
=='production' else '*'
12 sio
= socketio
.Server(cors_allowed_origins
=allowed_origin
)
14 RPSLS_PATH
= sys_path
[0]
15 DB_PATH
= RPSLS_PATH
+ '/db/rpsls.sqlite'
17 searching
= {} #someone seeks a game? (uid + sid)
18 connected
= {} #map uid --> sid (seek stage)
20 # Avoid repeating DB connect/close code
21 def db_operation(func
):
22 con
= sqlite3
.connect(DB_PATH
)
30 """ Triggered at page reload or tab close """
31 global connected
, searching
33 key_idx
= list(connected
.values()).index(sid
)
34 del connected
[list(connected
.keys())[key_idx
]]
36 # If the user didn't seek, no key to find
38 if searching
and searching
["sid"] == sid
:
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
)
50 # Always try to insert (new) Users row
51 cur
.execute("insert into Users (name) values (?)", (data
,))
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]
60 sio
.emit("login", {"name": data, "uid": uid}
, room
=sid
)
65 """ When user click on 'Play' button """
66 global connected
, searching
67 connected
[data
["uid"]] = sid
69 searching
= {"uid": data["uid"], "sid": sid, "name": data["name"]}
71 # Active seek pending: create game
75 today
= (date
.today(),)
76 cur
.execute("insert into Games (created) values (?)", today
)
78 # To room == sid, opponent is me. To my room, it's him/her
80 {"gid":gid, "oppid":opponent["uid"], "oppname":opponent["name"]}
,
83 {"gid":gid, "oppid":data["uid"], "oppname":data["name"]}
,
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
)
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"]))
99 def 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"]))
107 '/': RPSLS_PATH
+ '/index.html',
108 '/rpsls.js': RPSLS_PATH
+ '/rpsls.js',
109 '/favicon.ico': RPSLS_PATH
+ '/favicon.ico',
110 '/assets': RPSLS_PATH
+ '/assets'
113 PORT
= getenv('RPSLS_PORT')
118 # Wrap with a WSGI application
119 app
= socketio
.WSGIApp(sio
, static_files
=static_files
)
120 eventlet
.wsgi
.server(eventlet
.listen(('127.0.0.1', PORT
)), app
)