{"id":19364541,"url":"https://github.com/math2001/nine43","last_synced_at":"2025-04-23T14:30:53.547Z","repository":{"id":74806485,"uuid":"182511335","full_name":"math2001/nine43","owner":"math2001","description":"On the path to understanding concurrency...","archived":true,"fork":false,"pushed_at":"2019-11-29T22:50:26.000Z","size":329,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-24T13:13:37.879Z","etag":null,"topics":["pygame","structured-concurrency","trio"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/math2001.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-21T08:55:30.000Z","updated_at":"2023-09-09T05:15:34.000Z","dependencies_parsed_at":"2024-02-08T09:00:09.399Z","dependency_job_id":null,"html_url":"https://github.com/math2001/nine43","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/math2001%2Fnine43","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/math2001%2Fnine43/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/math2001%2Fnine43/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/math2001%2Fnine43/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/math2001","download_url":"https://codeload.github.com/math2001/nine43/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250451624,"owners_count":21432858,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pygame","structured-concurrency","trio"],"created_at":"2024-11-10T07:37:40.188Z","updated_at":"2025-04-23T14:30:53.533Z","avatar_url":"https://github.com/math2001.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nine43 [![Build Status](https://travis-ci.com/math2001/nine43.svg?branch=master)](https://travis-ci.com/math2001/nine43)\n\n\u003e The successor of Nine42\n\nThis project is supposed to teach me concurrency. It's the start of game\nserver with a client, all in Python.\n\n## The server\n\nThe server is organized in sections that are completely independent. The only\nway they talk with one an other is through channels. This makes the server\nmuch easier to test because all those sections are pure functions (in the\nsense that they have no side effect, and aren't be affected by any).\n\nFirstly, we have the network section, the interface between the network and the \napplication. It accepts raw `trio.SocketStream`s, wraps them in `net.JSONStream`\nand sends them, through a channel called `connch` to the initiator. \n\nThe initiator has one task: find out the username of the connection (and make\nsure that there aren't any duplicates in the application). Once it has done\nthat, it can wrap the connection in a `Player` (which is just a connection and\na username). It then sends it off to a channel called `playerch`.\n\nAnd the `lobby` is listening to that channel. The lobby's role is very\nsimplistic: to stack up players in groups of N, and sends the `Group` on an\nother channel: `groupch`. \n\nNotice of up to now, all those section are exclusive on the application: there\nis only one `server` running, one`initiator` and one `lobby`.\n\nBut now that we have a group of players, we can ask them what they want to do,\nand then, depending on, it will start a chain of events: a sub. A sub is just\na select section, a game section, and an end section.\n\nThe server needs to be able to run multiple subs at the same time, but none of \nthose subs need to know about each other. So we have a sub manager which spawns\nsubs as it receives groups.\n\nIn this case, there are 3 subs running:\n\n![visualization of process explained above][server-flowchart]\n\n\u003e Made using [stackedit.io][] and [mermaid][]\n\nThere are a few key parts I've omitted on this graph because it makes look\nunnecessarily complex.\n\n1. Every section is connected to the `initiator` through a channel called\n`quitch`. As soon as a connection is closed, we assumed that the layer below\ntried it's best to recover the connection, and throw it away. But the username\nneeds to be released. This is the role of the `quitch`: every section can send\na player on it, and the `initiator` will release the username. `initiator` is\nthe sole consumer of this channel, every other section receives a clone of the\nsending end of the channel.\n2. As player leave a sub (leave the fin section), they get sent back to the\nlobby through a clone of the `playerch`. This is managed by the sub manager\n(the server doesn't anything about what's going on inside a `sub`).\n\n\u003e Every section is in fact just a proxy, which reads from a channel, and writes\n\u003e on another.\n\nThis means the sections are easy to test independently, and we'll know that\nthey'll behave likewise in the app, because those channels are the only way they\nhave to communicate.\n\n\u003e Only the sender should close the connection.\n\nAnother important part of the server is cascading: as soon a section detects\nthat its input channel has been closed, it cleans itself up, and closes its\noutput channel, creating a cascading effect, closing everything.\n\nYou might notice that since subs don't read from a channel (only the sub manager\ndoes, it then spawns one sub per group), it won't know when to stop.\n\nAnd this is an interesting behavior that could be desired: don't accept anymore\nconnections (close everything up to the sub manager), but finish the game you're\nrunning.\n\nIf we really want to close everything forcefully, it'd be quite easy to pass a\n`trio.Event` to every sub that will be triggered when it needs to be forcefully\nclosed.\n\n\u003e Note that this organization only works because when something is written on a\n\u003e channel, it is given up by the sender. For example, this wouldn't be allowed:\n\n```python\nasync def section1(ch: SendCh[Obj]):\n    obj = make_new_obj(arg='value')\n    await ch.send(obj)\n    obj.alter() # no!! You don't own obj anymore!\n```\n\nIt prevents us from thinking of every section as independent, and we have to \nconsider the whole application as a whole.\n\n## Client\n\nTODO\n\n\n[stackedit.io]: https://stackedit.io\n[mermaid]: https://mermaidjs.github.io\n[server-flowchart]: imgs/server-flowchart.png","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmath2001%2Fnine43","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmath2001%2Fnine43","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmath2001%2Fnine43/lists"}