...
 
Commits (4)
# POOker
Poker service in Python for the POOA course
Try it at http://18.223.33.251/homePage !
Try it at https://pooker.ml or https://18.223.33.251/ !
# Installation
......@@ -15,3 +15,45 @@ Launch the server:
``` bash
env FLASK_ENV=development FLASK_APP=server/server.py python3 -m flask run -p 5000
```
# Usage
Connect to the webpage, choose the game mode (a tournament has a blind raising over time, contrary to the cash game) and the number of players and generate a game ID.
Share the game ID with your friends, enter your name and the game ID, and join the game !
If you lack friends, use different browsers (you are identified by cookies, multiple tabs will not work).
Joining the game by copying the URL will set you as spectator.
# Fonctioning
The game engine is served via a Flask server.
The interaction with the client (in the browser) is mainly done via an HTTP API.
A socketIO websocket (we did not manage to get classic websockets working alongside Flask) is used to alert all of the clients when someone finishes his turn and update the game state.
Detailed file description:
- `server/`
- `server.py`: The Flask-SocketIO server
- `game.py`: The `Game` Class, consisting of a poker `Table` and a list of connected sockets
- `table/`
- `table.py`: main class representing a Poker table
- `tableTimer.py`: timer for automatic actions on the table: after a player timeout or the end of a hand.
- `deck.py`: the `Deck` of `Cards`
- `cards.py`: the `Card` object
- `player/`
- `player.py`: a `Player` in the `Table`
- `hand.py`: describe the poker hands formed by the `Cards`.
- `front/`
- `static/`: the static CCS/JS/Images files
- `templates`: the Jinja2 HTML templates used by Flask
# Known problems
These are known problems that we lacked time to address/fix.
If someone does not make any action during his time (30s), the server for some reason is not able to send the updates anymore via the socket.
Therefore the front-end calls /api/game/ every second to keep track of the state of the game in parallel of the websocket. The socket will only reconnect (as a new socket) on page reload.
As there is no server-side file/database storage, on a server restart or crash all the games are lost. We choosed to do so for quicker development, but it is obviously not production ready.
There is currently no logic to destroy the table when the game is finished, leading in an increased number of `Table` objects over time. The server should be restarted often to counter this.
js_poker
========
A simple, minimalist scaffold for a poker web app frontend
\ No newline at end of file
......@@ -16,7 +16,7 @@
<br>
<label>
<input type="radio" name="mode" value="1">
Tournoi</label>
Tournament</label>
<br>
<article>Choose number of players</article>
<select title="nbPlayers" name="nbPlayers" id="nbPlayers">
......
from flask_socketio import SocketIO
import threading
class Game:
def __init__(self, table, server):
......@@ -7,12 +7,26 @@ class Game:
self.__sockets = []
self.__userIds = []
self.__socketio = server
self.__mutex = threading.Lock()
def bCastState(self):
print("BROADCASTING")
for (i,socketId) in enumerate(self.__sockets):
self.__socketio.emit('state', self.table.state(self.__userIds[i]), room=socketId)
with self.__mutex:
# Sends a personalized message (thus not really broadcasting) to each connected socket
# print("BROADCASTING")
for (i,socketId) in enumerate(self.__sockets):
self.__socketio.emit('state', self.table.state(self.__userIds[i]), room=socketId)
def addSocket(self, socket, userId):
self.__userIds.append(userId)
self.__sockets.append(socket)
with self.__mutex:
self.__userIds.append(userId)
self.__sockets.append(socket)
def removeSocket(self, socket):
with self.__mutex:
# Remove the socket if it exists (the socket disconnected)
for (i, socketId) in enumerate(self.__sockets):
if socket == socketId:
# Remove the socket and the userID
del self.__sockets[i]
del self.__userIds[i]
return
......@@ -162,6 +162,9 @@ def connect():
@socketio.on('disconnect')
def disconnect():
print('Websocket disconnected')
# Remove the saved socket for its game
for game in games.values():
game.removeSocket(request.sid)
if __name__ == '__main__':
......