This document provides details on how the backend of the software works, and provides "How to" for writing code to work with certain components. It is designed to supplement (and not repeat) the contents of the contributing document at the top-level of this project file hierarchy.
This document is targeted towards developers interested in contributing to the backend of this software project. See the External Documentation section for links to documents relevant to other areas of this project (e.g. frontend).
- This document covers the following backend aspects of the project:
- Server-sent Events.
If you are starting to contribute to this project for the first time, it is stongly recommended that you review the contributing document, which covers everything needed to start writing code for this project.
For developers interested in contributing to the project frontend, see the frontend README.
They're essentially a means for a server to send data to a client without the client having to make any requests.
- The client makes an event listener, just like any ol' event listener (click, mouseover, keypress, etc.).
- The server sends the event (which has the same name as what the client is listening for) to standard output along with data (which can be something like a bit of JSON).
- The client will receive this event and trigger the event callback.
This definition list shows which SSE events are being generated by the backend. Anything
in angle brackets (<>) is a placeholder – <gameID>
would be replaced by a
game id. An ellipsis (…) represents potentially more entries in the same format.
- playerJoin
[<username1>, <username2>, …]
- gameStart
{"gameID": <gameID>, "propertyPositions": [<prop1>, <prop2>, …]}
- playerMove
[[<playerId1>, <new position>, <old position>], [<pid2>, <new>, <old>], …]
- playerTurn
{"name": <username>, "id": <userID>}
- playerBalance
[[<userId1>, <newBalance>, <oldBalance>], [<uid2>, <new>, <old>], …]
- propertyOwnerChanges
- ``[{"newOwner": null | {"id": <id>, "name": <username>},
- "oldOwner": null | {"id": <id>, "name": <username>}, "property": {"name": <name>, "position": <position>}}, …]``
Write a new function that performs some "check" to decide whether an event should be generated. e.g.
def check_game_playing_status(output_stream, game): if game.state == "playing": generate_game_start_event(game.uid, output_stream)
See events.py for more examples, all of which are commented.
If the 'check' within the function you have written above passes, it should call another function (which you will write) to generate an event. e.g.
def generate_game_start_event(game_id, output_stream): output_stream.write('event: gameStart\n') output_stream.write('data: %s\n' % (game_id)) output_stream.write('\n\n')
See the comments in generate_game_start_event() for some guidance on the sending of event types and the data payload.
Finally, add the call to the function you wrote in 1. above, to start_sse_stream(). i.e.
def start_sse_stream(output_stream=sys.stdout): ... while True: ... check_game_playing_status(output_stream, game) ... time.sleep(3) output_stream.flush()
Make sure the call to your function is above the call to sleep()! There are a bunch of comments in start_sse_stream() which are worth a read, to supplement this README.