{"id":37195846,"url":"https://github.com/jaredrhine/gomertime","last_synced_at":"2026-01-14T22:46:51.552Z","repository":{"id":164188691,"uuid":"623744206","full_name":"jaredrhine/gomertime","owner":"jaredrhine","description":"Playground for simulation and ECS in go","archived":false,"fork":false,"pushed_at":"2023-06-28T06:50:41.000Z","size":75,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-06-20T22:12:13.237Z","etag":null,"topics":["client-server","ecs","game-engine","go","golang","learning-exercise","simulation","websockets"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"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/jaredrhine.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":"2023-04-05T01:56:10.000Z","updated_at":"2023-05-09T21:00:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"ae11c0e7-ef9a-43da-be37-751d1190e898","html_url":"https://github.com/jaredrhine/gomertime","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jaredrhine/gomertime","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredrhine%2Fgomertime","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredrhine%2Fgomertime/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredrhine%2Fgomertime/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredrhine%2Fgomertime/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jaredrhine","download_url":"https://codeload.github.com/jaredrhine/gomertime/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredrhine%2Fgomertime/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28436808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T22:37:52.437Z","status":"ssl_error","status_checked_at":"2026-01-14T22:37:31.496Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["client-server","ecs","game-engine","go","golang","learning-exercise","simulation","websockets"],"created_at":"2026-01-14T22:46:50.814Z","updated_at":"2026-01-14T22:46:51.541Z","avatar_url":"https://github.com/jaredrhine.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gomertime: Baby's first ECS simulation in go\n\nAuthor: Jared Rhine \u003cjared@wordzoo.com\u003e\n\nLast update: April 2023\n\n## Usage\n\n1. Install `go` using your preferred approach.\n\n1. Get the codebase from github:\n\n   ```shell\n    go version\n    git clone https://github.com/jaredrhine/gomertime\n    cd gomertime\n    go mod download\n   ```\n\n1. Run the gomertime server in one window/tab:\n\n   ```shell\n   go run cmd/server/main.go\n   ```\n\n1. Run a gomertime client in another window/tab:\n\n   ```shell\n   go run cmd/textclient/main.go\n   ```\n\nText UI keys:\n\n- `q` - quit\n- `escape` - quit\n- `control+c` - quit\n- `space` - pause\n- `d` - dev/debug screen\n- arrow keys - move the viewport up/down/left/right\n\n## Design principles\n\nThis codebase and line of work is undertaken with these goals in mind:\n\n1. This is a learning exercise, not an attempt to build a real game or community.\n   - ...so the whole codebase will be modern go (as written and evolved by a beginner).\n2. I want to learn go and in particular concurrency patterns using go channel idioms.\n3. I want to learn a bit about ECS (Entity/Component/System) architecture commonly used in games.\n   - ...so we'll recreate an ECS architecture from first principles, rather than using an existing ECS library.\n   - ...so we'll genericize the implementation to support two or more simulations within this one codebase.\n   - ...so we'll not start with an \"archetype\"-based ECS architecture, which is commonly done as a performance optimization to group entities for faster lookup.\n4. I want to support multiple-client and persistent server scenarios.\n   - ...so we'll move towards client/server architecture early\n     - ...and use JSON and WebSockets to learn HTTP-oriented go development, even though those probably aren't the right choices for a high-performance game framework.\n**5. Don't get lost in the details\n   - ...so we'll minimize following of ECS literature.\n   - ...so won't be afraid to take shortcuts around hard problems that real games need to solve.\n   - ...so I'll minimize testing because the code structure is likely to change repeatedly due to my unfamilarity with the problem space.\n   - ...so the UIs will start as text-only.\n   - ...so we'll fail fast (let errors happen) and minimize focus on correctness and robustness.\n\n## Sketch architecture\n\nA \"component\" is a typed data bag, such as Position, Velocity, Food, Health that applies to one or more\n\nAn \"entity\" is really just a key/ID to represents an object or container which has multiple components.\n\nA \"system\" is a code module which performs operations on subsets of components (say, \"update position for all entities that have a velocity component\")\n\nOur first components will include:\n\n- Position\n- Velocity\n- Health\n\nBoth Position and Velocity are modeled as a three-tuple of float values.\n\nFor Position, the 3-tuple represents the positive or negative X, Y, and Z cartesian coordinates of the entity located in a world centered on (0, 0, 0). The units for coordinate values are meters.\n\nFor Velocity, the 3-tuple represents a 3D vector, pointed in a specific direction. The units for vector values are meters per second.\n\n## Work plan\n\n- Done\n  - ~~Project name~~\n  - ~~Basic go module~~\n  - ~~World tick update loop~~\n  - ~~Terminal clear and display shell~~\n  - ~~Prototype core with keyboard, display, world tick integrated~~\n  - ~~Detect keyboard press async~~\n  - ~~'q' to exit~~\n  - ~~Pause key~~\n  - ~~Decide that components will belong to single entities, rather than being able to be shared between entities~~\n  - ~~id generator (sequence for entity probably)~~\n  - ~~Component~~\n  - ~~Position, velocity components stored as 3D float~~\n  - ~~Entity~~\n  - ~~Temporary hard-coded world/entity creation/setup~~\n  - ~~Fake/inefficient ECS storage backend; defer archetype implementation~~\n  - ~~Debug dump state~~\n  - ~~Lookup console height/width on start~~\n  - ~~Position lookup/query~~\n  - ~~Dynamic terminal height/width~~\n  - ~~Display representation of world~~\n  - ~~Different characters for different entities~~\n  - ~~First test for position to text screen mapping~~\n  - ~~Display viewport can move around world~~\n  - ~~Move WorldStore from controller to world~~\n  - ~~Position updates based on velocity each tick~~\n  - ~~Split out main package from pkg code~~\n  - ~~Logging file is set by default, stderr avoided~~\n  - ~~WebSocket server framework, listens on socket~~\n  - ~~Text client is able to connect to websocket server, receive server tick, and display~~\n  - ~~Log level can be changed at start and at runtime~~\n  - ~~Enforce world boundaries during motion, worldXMin, worldXMax...~~\n  - ~~Toroidal world option to wrap around world~~\n  - ~~Text client is able to show updating server positions~~\n  - ~~Move position summary websockets push to array of objects, separate the x/y components while eliminating string key~~\n  - ~~Text client is able to move around its viewport independently~~\n\n- Shortlist\n  - Add Acceleration component, including a cyclical function to watch an entity cycle back and forth. Ooo, and then circles.\n  - Client can connect to custom hostname/port. Client connection over tailscale tunnel confirmed.\n  - Server is headless\n  - Utility function to count number of neighbors\n  - Rewrite server using `time.NewTicker`\n  - Split server and client fully\n  - Add Health component\n  - Find nearby entities\n  - Startup with origin in middle of screen. Display X,Y of cursor rather than\n  - Conway game of life rule implementation\n\n- Server/client\n  - Text client is able to send commands/updates to server\n  - Client registers its viewport, and server updates distinct caches for each client. Client at least receives some subset of every position in the world, as an optimization.\n  - Any clients is able to pause the server\n  - Clients are able to vote on pausing the server. When all clients have requested the server be paused, the server pauses until requested by any client to restart.\n  - Move to gob, flatbuffers, messagepack, protobufs on the wire\n\n- Display, UI\n  - Limit console vertical to a maximum height\n  - Camera follows entity\n  - Web canvas output\n  - Zoom in/out\n  - Text display dynamically resizes when window resizes\n  - Don't clear text screen every frame, only when needed. Minimize flashing.\n  - Dev console/palette\n  - Vertical/elevation support in text somehow\n\n- Core, game loop, golang\n  - Pass to examine and respond to errors; add error handling throughout\n  - Buildings which transform goods\n  - systems as goroutines. first: mover managing position\n  - Track wall clock time for stats\n  - loop over systems (for _, system := range world.Systems() ?)\n  - Cache viewport boundaries (xmin/xmax/ymin/ymax)\n  - Logging sophistication\n  - Genericize into IdType uint64\n  - Testing\n  - CI\n  - Benchmarking\n  - WebAssembly\n  - Optimization of PositionSummary. Perhaps only calculate all positions within a region\n  - Split code into multiple modules\n  - Data blob for component, independent of entity-specific data\n  - Lua engine embedded to write rules\n  - Rationalize the float/integer values used in position summary\n\n- Simulation\n  - Food consumption\n  - Pathfinding\n  - Force and acceleration\n  - Friction\n  - Destructability\n  - Optional rule/flag to not allow movement into same physical space?\n  - Toxins\n  - Light source\n  - Mass of iron\n  - Multiple components at same position (works now)\n  - Entity size/shape somehow. Entities can spread over multiple coordinates. Center of mass position.\n  - Edge of world handling. Bounce off walls. Hard stop.\n  - Fields, scalar (or vector) values at each position in the world. Magnetic, photon flux, gravity, ?\n  - Engineering with a company. Model dev motivation, daily work\n  - Reimplement \"puffball\" pro-forma ledger automation and modeling infrastructure as entities, and model ticks as the process over time.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaredrhine%2Fgomertime","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaredrhine%2Fgomertime","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaredrhine%2Fgomertime/lists"}