{"id":27779219,"url":"https://github.com/cole-glotfelty/halligame","last_synced_at":"2026-03-01T04:07:51.045Z","repository":{"id":280043810,"uuid":"939057073","full_name":"cole-glotfelty/Halligame","owner":"cole-glotfelty","description":"A framework for running multiplayer games concurrently.","archived":false,"fork":false,"pushed_at":"2025-04-29T03:38:36.000Z","size":1714,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-10T15:47:44.926Z","etag":null,"topics":["concurrent-programming","erlang","framework","games","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cole-glotfelty.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,"zenodo":null}},"created_at":"2025-02-25T23:24:20.000Z","updated_at":"2025-04-29T03:38:40.000Z","dependencies_parsed_at":"2025-03-01T00:06:46.400Z","dependency_job_id":"64e7b4b2-8f86-4bc5-a8aa-5620c12041d1","html_url":"https://github.com/cole-glotfelty/Halligame","commit_stats":null,"previous_names":["cole-glotfelty/halligame"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/cole-glotfelty/Halligame","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cole-glotfelty%2FHalligame","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cole-glotfelty%2FHalligame/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cole-glotfelty%2FHalligame/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cole-glotfelty%2FHalligame/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cole-glotfelty","download_url":"https://codeload.github.com/cole-glotfelty/Halligame/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cole-glotfelty%2FHalligame/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29960235,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T01:47:18.291Z","status":"online","status_checked_at":"2026-03-01T02:00:07.437Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["concurrent-programming","erlang","framework","games","python"],"created_at":"2025-04-30T09:29:49.805Z","updated_at":"2026-03-01T04:07:51.027Z","avatar_url":"https://github.com/cole-glotfelty.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Halligame\nA framework for running multiplayer games concurrently.\n\n# Using Halligame\nIf you are a Tufts student with access to Halligan, you can run the following to\nadd `hg` to your path.\n\n(We recommend adding this to your .bashrc, .cshrc, or .zshrc!)\n\n```bash\n$ source /h/mdanie09/Public/hg.sh # For Bash/ZSH\n```\n\n```csh\n$ source /h/mdanie09/Public/hg.csh # For CSH\n```\n\nThen, run `hg` for usage.\n\n# System Architecture\n![](./topology/HalligameTopology.drawio.svg)\n\n## Game Module Architecture\nEach game module for halligame is structured as a sub-package of the games\nsub-package. The modules are stored in the games directory. Each game module \nshould aim to follow the same uniform interface so that the game server and\nvalidation server setup can be automated. The interface is as follows:\n\n![](./topology/ClassDiagrams.drawio.svg)\n\n# Addiing your Own Game to Halligame\nBelow is a list  of functions that must be implemented in order for your game\nto work with the Halligame framework. For an example of what this looks like\nin the [Example Game](./src/halligame/games/ExampleGame/). Additionally, you\nwill need to add your game to the `__init_.py` in in src/halligame/games for it \nto show in the CLI. From here, you can use the CLI `hg` to run and test your \ngame. Just note that it will not run until you have implemented all functions\nfound in the required section below.\n\n## Required Implementation for Adding Games\n#### Functions that are required to be implemented in gameServer.py\n- `__init__(comms)` : Called automatically when the server is started. comms is \nan instance of the ServerCommunicate class and gives the game server access to \nthe public ServerCommunicate functions (documented below)\n- `addClient(ClientPid)` : Called automatically by the client when a new client \nnode joins. Should call `confirmJoin(ClientPid, Message)` to confirm the request.\n- `removeClient(ClientPid)` : Called when the game client calls the function \n`ClientComms.shutdown()`\n- `gotClientMessage(ClientPid, Message)` : Called when the game client calls \n`sendMessage(Message)`\n\n#### Functions that are required to be implemented in gameClient.py\n- `__init__(comms)` : Called automatically when the client is started. comms is \nan instance of the ClientCommunicate class and gives the game client access to \nthe public ClientCommunicate functiosn (documented below)\n- `updateState(newState)` : Called with the provided state when the game server \ncalls `broadcastState(state)`\n- `gotServerMessage(Message)` : Called on the particular client node when the \ngame server calls `sendClientMessage(ClientPid, Message)`\n- `joinConfirmed(Message)` : Called when the game server responds to addClient \nby calling `confirmJoin(ClientPid, Message)`\n\n### Exported Functions available to Games\n\n#### Functions exported by ServerComms\n- `broadcastState(State)` : Sends the provided state to all clients connected \nto the server, with `updateState(newState)` being called when the client \nreceives the message. This assumes the usage of the provided gameState.py module.\n- `broadcastMessage(Message)` : Sends the provided message to all connected \nclients, with `gotServerMessage(Message)` being called in each client with \nthe message.\n- `confirmJoin(ClientPid, Message)` : When called by the server, \n`joinConfirmed(Message)` is called on the client node associated with ClientPid \n- `sendClientMessage(ClientPid, Message)` : Sends a message to a particular \nclient, with the `gotServerMessage(Message)` function being called when the \nclient receives it\n- `shutdown()` : Should be called when the game is over and the server should be \nshut down\n\n#### Functions exported by ClientComms\n- `sendMessage(Message)` : Sends a message to the server, with \n`gotClientMessage(ClientPid, Message)` being called when the server receives \nthe message\n- `shutdown()` : Should be called when the client leaves (or the game is over)\n\n### Screen (halligame.utils.screen)\n- `Screen(gotInputFunc, gotMouseClickFunc)` : \n    Initializes the screen class. Takes a callback function gotInputFunc that \n    has signature `gotInputFunc(input)` where input is the input from the user. \n    Input is either a normal char or a special character, which is handled by \n    [ncurses curses.KEY_* constants](https://docs.python.org/3/library/curses.html#curses.KEY_MIN). \n    Additionally, takes another callback function gotMouseClickFunc that is \n    called when the user clicks on the screen. gotMouseClickFunc \n    should have signature `gotMouseClickFunc(row, col, region, mouseEventType)` \n    where row and col are the row and column of the screen respectively, and \n    region is the region of the mouse click (as created by \n    `addClickableRegion`) or None if the click was not in a region. \n    mouseEventType is the type of the mouse event, either \"left_click\", \n    \"right_click\", \"middle_click\", or \"other\"\n- `write(row, col, toPrint)` : Prints the contents of toPrint starting at \n    (row, col) to the screen. toPrint must be convertible to string. Updates \n    made when refresh is called.\n- `print(toPrint, end=\"\\n\")` : Prints the contents of toPrint, starting at the \n    bottom left corner as if it were a normal terminal. end is appended to the \n    end of toPrint before printing. toPrint must be convertible to string. \n    Updates made when refresh is called.\n- `displayFullScreenMessage(message, font=None)` : Displays the contents of \n    message centered vertically and horizontall in the terminal window. \n    Clears and refreshes the screen.\n- `clearScreen()` : Removes everything from the screen. Updates made when \n    refresh is called.\n- `refresh()` : Refreshes the screen, making all pending changes visible to the \n    user\n\n- `terminalHeight()` : get the height of the terminal in pixels\n- `terminalWidth()` : get the width of the terminal in pixels\n- `getCenteredRow(toPrint)` : Takes toPrint, which must either be a string or \n    something convertible to a string. Returns the row number where if you \n    called write(toPrint), it would be centered vertically in the screen. \n    If the contents is taller than the screen, returns 0.\n- `getCenteredCol(toPrint)` : Similar to getCenteredRow. Takes toPrint, which \n    must either be a string or something convertible to a string. Returns the \n    column number where if you called write(toPrint), it would be centered \n    horizontally in the screen. If the contents is wider than the screen, \n    returns 0.\n\n- `addColor(r, g, b, colorId)` : Add a new color to the palette, where \n    r, g, and b are integers between 0 and 256 referring to the intensity of \n    the color. Predefined colors are black, blue, cyan, green, magenta, red, \nwhite, yellow\n- `addColorPair(foreground, background, pairId)` : Takes in two color IDs and \n    defines a new color pair with ID pairId. A color pair is two colors, \n    where the foreground color is the color of the text on the screen, and the \n    background is the color of the screen behind the text (the highlight color)\n- `setStyle(colorPairId)` : Takes a color pair ID and sets the style of the \n    terminal to the color pair defined by that ID, meaning the the background \n    of the terminal is now the background color of that color pair and all \n    printing will now default to that color pair.\n- `addClickableRegion(row, col, height, width, id)` : Add a clickable region \n    to the screen. The region starts in the top left at (row, col) and is \n    height pixels tall and width pixels wide. When calling the callback \n    gotMouseClickFunc, if the click is within this region, then the region \n    argument is set to id. Clicks in overlapping regions are decided based on \n    which region was defined more recently.\n- `clearClickableRegions()` : Remove all clickable regions from the screen\n\n- `shutdown()` : Must be called when the client is finished displaying to the \n    terminal. Closes the virtual window and restores the terminal appearance \n    to its normal state\n\n# Development\nDependencies: python 3.10+, rebar3, uv\n\nAfter pulling, you will first have to run the following in order to install \nthe submodules.\n\n```bash\n$ git submodule update --init\n```\n\nOnce the submodules are pulled and installed, you can simply run make to install \ndependencies and  build the python and erlang modules.\n\n```bash\n$ make\n```\n\nThe last step is enabling the development CLI. To do this first export `HG_ROOT`\n\n```bash\n$ export HG_ROOT=$(pwd) # when in the top level of Halligame\n```\n\nThen activate the CLI:\n\n```bash\n$ source src/cli/activate_dev.sh # for Bash/ZSH\n```\n\n```csh\n$ source src/cli/activate_dev.csh # for csh/tcsh\n```\n\nNow that everything is complied, you're ready to begin. You can run `hg` for\nusage.\n\n## For Nix/Devenv Users\nThere is a provided `devenv.nix` file which should enforce that all dependencies\nare installed and exposed properly provided you have devenv set up.\n\n## File Structure Overview – Where to Find Everything!\n**Top Level**\n- Makefile: run make to build/compile all erlang modules with rebar3 and update\npython modules\n- README.md: The thing you're reading right now that explains what's going on\n- devenv.nix: ~~For Brennan~~. If you're using nix devenv, this will \nautomatically load all requirements and run make on entering the directory.\n- pyproject.toml: Configuration of dependencies and build order for uv/python\n\n**src/cli**\n- activate_dev.sh: Script to expose `hg` command to users with Bash/ZSH shells.\nFor development purposes only, this allows you to define a separate path for \nwhere to source files from.\n- activate_dev.csh: Script to expose `hg` command to users with csh/tcsh shells.\nFor development purposes only, this allows you to define a separate path for \nwhere to source files from.\n- background.py: Allows for erlang messages to be sent and recieved from the\nserver broker, in the background.\n- cli.py: command line interface which calls/sends erlang functions/messages\n- sh.activate: the same as `activate_dev.sh` but sets the path of halligame \nsource code to production which is stored in Michael's public folder.\n- csh.activate: the same as `activate_dev.csh` but sets the path of halligame \nsource code to production which is stored in Michael's public folder.\n- hg_background.bash: helper to launch the `background.py` process.\n\n**src/cli/src**\n- handleCLIRequest.erl: Handles certain requests coming from the \"hg\" command \nline tool.\n\n**src/halligame/games**\n- Canvas: directory, contains a gameServer.py and a gameClient.py representing \nthe server and client of the Canvas game.\n- ExampleGame: directory, contains an example gameServer.py and gameClient.py\nthis is not a playable game, but rather a barebones example/template to copy\nwhen making additional games.\n- TicTacToe: directory, contains a gameServer.py and a gameClient.py \nrepresenting the server and client of TicTacToe.\n- Uno: directory, contains a gameServer.py and a gameClient.py representing the \nserver and client of Uno.\n\n**src/halligame/utils**\n- ServerComms.py: Module to facilitate communication from the game server to \nthe client.\n- clientComms.py: Communication Module for clients to communicate with the \ngame server.\n- misc.py: A collection of various functions used occasionally.\n- gameClientTemplate.py: Super class to be used when making a gameClient to \nenforce required functions\n- gameServerTemplate.py: Super class to be used when making a gameServer to \nenforce required functions\n- gameState.py: Serializes and deserializes game state for client/server \ncommunication.\n- screen.py: Utility for drawing and writing to terminals using NCurses.\n\n**src/serverbroker**\n- serverbroker.erl: Keeps track of who's online and who's playing what.\n- serverbroker_app.erl: Implements the application behavior for serverbroker.\n- serverbroker_sup.erl: Implements a top-level supervisor for serverbroker.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcole-glotfelty%2Fhalligame","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcole-glotfelty%2Fhalligame","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcole-glotfelty%2Fhalligame/lists"}