taiko-web development has returned to GitHub. This Gitea instance will be shut down soon.
If your taiko-web fork is unavailable due to DMCA takedown, you can contact GitHub Support and ask for it to be deleted.
Taiko no Tatsujin simulator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

server.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. #!/usr/bin/env python
  2. import asyncio
  3. import websockets
  4. import json
  5. import random
  6. import sys
  7. server_status = {
  8. "waiting": {},
  9. "users": [],
  10. "invites": {}
  11. }
  12. consonants = "bcdfghjklmnpqrstvwxyz"
  13. def msgobj(type, value=None):
  14. if value == None:
  15. return json.dumps({"type": type})
  16. else:
  17. return json.dumps({"type": type, "value": value})
  18. def status_event():
  19. value = []
  20. for id, userDiff in server_status["waiting"].items():
  21. value.append({
  22. "id": id,
  23. "diff": userDiff["diff"]
  24. })
  25. return msgobj("users", value)
  26. def get_invite():
  27. return "".join([random.choice(consonants) for x in range(5)])
  28. async def notify_status():
  29. ready_users = [user for user in server_status["users"] if "ws" in user and user["action"] == "ready"]
  30. if ready_users:
  31. sent_msg = status_event()
  32. await asyncio.wait([user["ws"].send(sent_msg) for user in ready_users])
  33. async def connection(ws, path):
  34. # User connected
  35. user = {
  36. "ws": ws,
  37. "action": "ready",
  38. "session": False
  39. }
  40. server_status["users"].append(user)
  41. try:
  42. # Notify user about other users
  43. await ws.send(status_event())
  44. while True:
  45. try:
  46. message = await asyncio.wait_for(ws.recv(), timeout=5)
  47. except asyncio.TimeoutError:
  48. # Keep user connected
  49. pong_waiter = await ws.ping()
  50. try:
  51. await asyncio.wait_for(pong_waiter, timeout=5)
  52. except asyncio.TimeoutError:
  53. # Disconnect
  54. break
  55. except websockets.exceptions.ConnectionClosed:
  56. # Connection closed
  57. break
  58. else:
  59. # Message received
  60. try:
  61. data = json.loads(message)
  62. except json.decoder.JSONDecodeError:
  63. data = {}
  64. action = user["action"]
  65. type = data["type"] if "type" in data else None
  66. value = data["value"] if "value" in data else None
  67. if action == "ready":
  68. # Not playing or waiting
  69. if type == "join":
  70. if value == None:
  71. continue
  72. waiting = server_status["waiting"]
  73. id = value["id"] if "id" in value else None
  74. diff = value["diff"] if "diff" in value else None
  75. if not id or not diff:
  76. continue
  77. if id not in waiting:
  78. # Wait for another user
  79. user["action"] = "waiting"
  80. user["gameid"] = id
  81. waiting[id] = {
  82. "user": user,
  83. "diff": diff
  84. }
  85. await ws.send(msgobj("waiting"))
  86. else:
  87. # Join the other user and start game
  88. user["other_user"] = waiting[id]["user"]
  89. waiting_diff = waiting[id]["diff"]
  90. del waiting[id]
  91. if "ws" in user["other_user"]:
  92. user["action"] = "loading"
  93. user["other_user"]["action"] = "loading"
  94. user["other_user"]["other_user"] = user
  95. await asyncio.wait([
  96. ws.send(msgobj("gameload", waiting_diff)),
  97. user["other_user"]["ws"].send(msgobj("gameload", diff))
  98. ])
  99. else:
  100. # Wait for another user
  101. del user["other_user"]
  102. user["action"] = "waiting"
  103. user["gameid"] = id
  104. waiting[id] = {
  105. "user": user,
  106. "diff": diff
  107. }
  108. await ws.send(msgobj("waiting"))
  109. # Update others on waiting players
  110. await notify_status()
  111. elif type == "invite":
  112. if value == None:
  113. # Session invite link requested
  114. invite = get_invite()
  115. server_status["invites"][invite] = user
  116. user["action"] = "invite"
  117. user["session"] = invite
  118. await ws.send(msgobj("invite", invite))
  119. elif value in server_status["invites"]:
  120. # Join a session with the other user
  121. user["other_user"] = server_status["invites"][value]
  122. del server_status["invites"][value]
  123. if "ws" in user["other_user"]:
  124. user["other_user"]["other_user"] = user
  125. user["action"] = "invite"
  126. user["session"] = value
  127. sent_msg = msgobj("session")
  128. await asyncio.wait([
  129. ws.send(sent_msg),
  130. user["other_user"]["ws"].send(sent_msg)
  131. ])
  132. await ws.send(msgobj("invite"))
  133. else:
  134. del user["other_user"]
  135. await ws.send(msgobj("gameend"))
  136. else:
  137. # Session code is invalid
  138. await ws.send(msgobj("gameend"))
  139. elif action == "waiting" or action == "loading" or action == "loaded":
  140. # Waiting for another user
  141. if type == "leave":
  142. # Stop waiting
  143. if user["session"]:
  144. if "other_user" in user and "ws" in user["other_user"]:
  145. user["action"] = "songsel"
  146. await asyncio.wait([
  147. ws.send(msgobj("left")),
  148. user["other_user"]["ws"].send(msgobj("users", []))
  149. ])
  150. else:
  151. user["action"] = "ready"
  152. user["session"] = False
  153. await asyncio.wait([
  154. ws.send(msgobj("gameend")),
  155. ws.send(status_event())
  156. ])
  157. else:
  158. del server_status["waiting"][user["gameid"]]
  159. del user["gameid"]
  160. user["action"] = "ready"
  161. await asyncio.wait([
  162. ws.send(msgobj("left")),
  163. notify_status()
  164. ])
  165. if action == "loading":
  166. if type == "gamestart":
  167. user["action"] = "loaded"
  168. if user["other_user"]["action"] == "loaded":
  169. user["action"] = "playing"
  170. user["other_user"]["action"] = "playing"
  171. sent_msg = msgobj("gamestart")
  172. await asyncio.wait([
  173. ws.send(sent_msg),
  174. user["other_user"]["ws"].send(sent_msg)
  175. ])
  176. elif action == "playing":
  177. # Playing with another user
  178. if "other_user" in user and "ws" in user["other_user"]:
  179. if type == "note"\
  180. or type == "drumroll"\
  181. or type == "gameresults":
  182. await user["other_user"]["ws"].send(msgobj(type, value))
  183. elif type == "songsel" and user["session"]:
  184. user["action"] = "songsel"
  185. user["other_user"]["action"] = "songsel"
  186. sent_msg1 = msgobj("songsel")
  187. sent_msg2 = msgobj("users", [])
  188. await asyncio.wait([
  189. ws.send(sent_msg1),
  190. ws.send(sent_msg2),
  191. user["other_user"]["ws"].send(sent_msg1),
  192. user["other_user"]["ws"].send(sent_msg2)
  193. ])
  194. elif type == "gameend":
  195. # User wants to disconnect
  196. user["action"] = "ready"
  197. user["other_user"]["action"] = "ready"
  198. sent_msg1 = msgobj("gameend")
  199. sent_msg2 = status_event()
  200. await asyncio.wait([
  201. ws.send(sent_msg1),
  202. ws.send(sent_msg2),
  203. user["other_user"]["ws"].send(sent_msg1),
  204. user["other_user"]["ws"].send(sent_msg2)
  205. ])
  206. del user["other_user"]
  207. else:
  208. # Other user disconnected
  209. user["action"] = "ready"
  210. user["session"] = False
  211. await asyncio.wait([
  212. ws.send(msgobj("gameend")),
  213. ws.send(status_event())
  214. ])
  215. elif action == "invite":
  216. if type == "leave":
  217. # Cancel session invite
  218. if user["session"] in server_status["invites"]:
  219. del server_status["invites"][user["session"]]
  220. user["action"] = "ready"
  221. user["session"] = False
  222. if "other_user" in user and "ws" in user["other_user"]:
  223. user["other_user"]["action"] = "ready"
  224. user["other_user"]["session"] = False
  225. sent_msg = status_event()
  226. await asyncio.wait([
  227. ws.send(msgobj("left")),
  228. ws.send(sent_msg),
  229. user["other_user"]["ws"].send(msgobj("gameend")),
  230. user["other_user"]["ws"].send(sent_msg)
  231. ])
  232. else:
  233. await asyncio.wait([
  234. ws.send(msgobj("left")),
  235. ws.send(status_event())
  236. ])
  237. elif type == "songsel" and "other_user" in user:
  238. if "ws" in user["other_user"]:
  239. user["action"] = "songsel"
  240. user["other_user"]["action"] = "songsel"
  241. sent_msg = msgobj(type)
  242. await asyncio.wait([
  243. ws.send(sent_msg),
  244. user["other_user"]["ws"].send(sent_msg)
  245. ])
  246. else:
  247. user["action"] = "ready"
  248. user["session"] = False
  249. await asyncio.wait([
  250. ws.send(msgobj("gameend")),
  251. ws.send(status_event())
  252. ])
  253. elif action == "songsel":
  254. # Session song selection
  255. if "other_user" in user and "ws" in user["other_user"]:
  256. if type == "songsel":
  257. # Change song select position
  258. if user["other_user"]["action"] == "songsel":
  259. sent_msg = msgobj(type, value)
  260. await asyncio.wait([
  261. ws.send(sent_msg),
  262. user["other_user"]["ws"].send(sent_msg)
  263. ])
  264. elif type == "join":
  265. # Start game
  266. if value == None:
  267. continue
  268. id = value["id"] if "id" in value else None
  269. diff = value["diff"] if "diff" in value else None
  270. if not id or not diff:
  271. continue
  272. if user["other_user"]["action"] == "waiting":
  273. user["action"] = "loading"
  274. user["other_user"]["action"] = "loading"
  275. await asyncio.wait([
  276. ws.send(msgobj("gameload", user["other_user"]["gamediff"])),
  277. user["other_user"]["ws"].send(msgobj("gameload", diff))
  278. ])
  279. else:
  280. user["action"] = "waiting"
  281. user["gamediff"] = diff
  282. await user["other_user"]["ws"].send(msgobj("users", [{
  283. "id": id,
  284. "diff": diff
  285. }]))
  286. elif type == "gameend":
  287. # User wants to disconnect
  288. user["action"] = "ready"
  289. user["session"] = False
  290. user["other_user"]["action"] = "ready"
  291. user["other_user"]["session"] = False
  292. sent_msg1 = msgobj("gameend")
  293. sent_msg2 = status_event()
  294. await asyncio.wait([
  295. ws.send(sent_msg1),
  296. ws.send(sent_msg2),
  297. user["other_user"]["ws"].send(sent_msg1),
  298. user["other_user"]["ws"].send(sent_msg2)
  299. ])
  300. del user["other_user"]
  301. else:
  302. # Other user disconnected
  303. user["action"] = "ready"
  304. user["session"] = False
  305. await asyncio.wait([
  306. ws.send(msgobj("gameend")),
  307. ws.send(status_event())
  308. ])
  309. finally:
  310. # User disconnected
  311. del user["ws"]
  312. del server_status["users"][server_status["users"].index(user)]
  313. if "other_user" in user and "ws" in user["other_user"]:
  314. user["other_user"]["action"] = "ready"
  315. user["other_user"]["session"] = False
  316. await asyncio.wait([
  317. user["other_user"]["ws"].send(msgobj("gameend")),
  318. user["other_user"]["ws"].send(status_event())
  319. ])
  320. if user["action"] == "waiting":
  321. del server_status["waiting"][user["gameid"]]
  322. await notify_status()
  323. elif user["action"] == "invite" and user["session"] in server_status["invites"]:
  324. del server_status["invites"][user["session"]]
  325. port = int(sys.argv[1]) if len(sys.argv) > 1 else 34802
  326. print('Starting server on port %d' % port)
  327. asyncio.get_event_loop().run_until_complete(
  328. websockets.serve(connection, "localhost", port)
  329. )
  330. asyncio.get_event_loop().run_forever()