{"id":50115355,"url":"https://github.com/widgrensit/asobi-love2d","last_synced_at":"2026-05-23T14:34:10.284Z","repository":{"id":355109080,"uuid":"1226770303","full_name":"widgrensit/asobi-love2d","owner":"widgrensit","description":"LÖVE (love2d) client SDK for the Asobi game backend. Pure Lua — no LuaRocks deps, no native modules.","archived":false,"fork":false,"pushed_at":"2026-05-01T21:29:45.000Z","size":23,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-01T23:25:44.624Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/widgrensit.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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-01T20:16:06.000Z","updated_at":"2026-05-01T21:29:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/widgrensit/asobi-love2d","commit_stats":null,"previous_names":["widgrensit/asobi-love2d"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/widgrensit/asobi-love2d","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgrensit%2Fasobi-love2d","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgrensit%2Fasobi-love2d/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgrensit%2Fasobi-love2d/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgrensit%2Fasobi-love2d/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/widgrensit","download_url":"https://codeload.github.com/widgrensit/asobi-love2d/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgrensit%2Fasobi-love2d/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33400247,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","response_time":53,"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":[],"created_at":"2026-05-23T14:34:09.432Z","updated_at":"2026-05-23T14:34:10.279Z","avatar_url":"https://github.com/widgrensit.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# asobi-love2d\n\nLÖVE (love2d) client SDK for the [Asobi](https://github.com/widgrensit/asobi) game backend. Pure Lua — no LuaRocks dependencies, no compiled native modules. Works in any LÖVE 11.x project.\n\n## Run a backend first\n\nThe SDK talks to an Asobi server. The fastest way to get one is the canonical SDK demo backend:\n\n```bash\ngit clone https://github.com/widgrensit/sdk_demo_backend\ncd sdk_demo_backend \u0026\u0026 docker compose up -d\n```\n\nThat serves at `http://localhost:8084` (HTTP + WebSocket on `/ws`) with a 2-player `demo` mode (60-second movement-only round, 10 Hz tick rate). For the full reference game (arena shooter — boons, modifiers, voting, bots) see [`asobi_arena_lua`](https://github.com/widgrensit/asobi_arena_lua) on `:8085`.\n\n## Installation\n\nDrop the `asobi/` directory into your LÖVE project root, alongside `main.lua`:\n\n```\nmy_game/\n├── main.lua\n├── conf.lua\n└── asobi/\n    ├── init.lua\n    ├── auth.lua\n    ├── http.lua\n    ├── json.lua\n    ├── matchmaker.lua\n    ├── realtime.lua\n    └── websocket.lua\n```\n\nLÖVE bundles `luasocket` so HTTP and WebSocket transport work out of the box. For HTTPS / `wss://` you need `luasec` on the path (LÖVE does not bundle it).\n\n## Quick Start\n\n```lua\nlocal asobi = require(\"asobi\")\n\nlocal client\nlocal matched = false\n\nfunction love.load()\n    math.randomseed(os.time())\n    client = asobi.new({host = \"localhost\", port = 8084})\n\n    -- Register and grab a session token (synchronous HTTP).\n    local _, err = asobi.auth.register(\n        client,\n        \"player_\" .. math.random(1, 1e9),\n        \"pass1234\",\n        \"demo-player\"\n    )\n    if err then error(\"register failed: \" .. err.error) end\n\n    -- Wire callbacks BEFORE queueing.\n    client.realtime:on(\"match_matched\", function(payload)\n        print(\"matched! match_id = \" .. payload.match_id)\n        matched = true\n    end)\n\n    client.realtime:on(\"match_state\", function(state)\n        local me = (state.players or {})[client.player_id]\n        if me then\n            -- Render `me.x`, `me.y` — and any other entity in state.players.\n        end\n    end)\n\n    -- Connect WebSocket and queue for a match.\n    assert(client.realtime:connect())\n    client.realtime:add_to_matchmaker({mode = \"demo\"})\nend\n\nfunction love.update(dt)\n    -- Drains incoming WebSocket frames and dispatches to your callbacks.\n    client.realtime:update()\n\n    -- Send player input (10 Hz is plenty for most games).\n    if matched then\n        local mx = (love.keyboard.isDown(\"d\") and 1 or 0) - (love.keyboard.isDown(\"a\") and 1 or 0)\n        local my = (love.keyboard.isDown(\"s\") and 1 or 0) - (love.keyboard.isDown(\"w\") and 1 or 0)\n        client.realtime:send_match_input({\n            move_x = mx, move_y = my, shoot = false, aim_x = 0, aim_y = 0,\n        })\n    end\nend\n\nfunction love.quit()\n    client.realtime:disconnect()\nend\n```\n\n**Two players are required to match in `demo` mode** — open a second LÖVE instance (or run the smoke test) to fill the lobby.\n\n## Threading note\n\nLÖVE runs a single cooperative loop. `client.realtime:update()` does non-blocking I/O on the WebSocket and must be called every frame from `love.update(dt)` for callbacks to fire. HTTP calls (`asobi.auth.register`, `asobi.auth.login`) are **synchronous** and will block the frame for the duration of the request — call them at startup or on a deliberate user action, never in your main game loop.\n\n## API surface\n\n### `asobi.new(opts)` → client\n\n`opts = {host, port = 8084, use_ssl = false}`. Returns a client with `auth`, `matchmaker`, and `realtime` attached.\n\n### `asobi.auth`\n\nSynchronous. Returns `(data, err)` — `err` is `nil` on success or `{status_code, error}` on failure.\n\n```lua\nasobi.auth.register(client, username, password, display_name)\nasobi.auth.login(client, username, password)\nasobi.auth.refresh(client)\nasobi.auth.logout(client)\n```\n\n### `asobi.matchmaker`\n\nREST shape (most matchmaking happens over the realtime WebSocket; this is the HTTP fallback).\n\n```lua\nasobi.matchmaker.add(client, \"demo\")\nasobi.matchmaker.add(client, {mode = \"demo\", properties = {...}})\nasobi.matchmaker.status(client, ticket_id)\nasobi.matchmaker.cancel(client, ticket_id)\n```\n\n### `client.realtime` — WebSocket\n\n```lua\nclient.realtime:connect()                          -- handshake + session.connect\nclient.realtime:disconnect()\nclient.realtime:update()                           -- call every frame\n\nclient.realtime:on(event, fn)                      -- bind a callback\nclient.realtime:add_to_matchmaker({mode = \"demo\"})\nclient.realtime:remove_from_matchmaker(ticket_id)\nclient.realtime:send_match_input(input_table)\nclient.realtime:join_match(match_id)\nclient.realtime:leave_match()\nclient.realtime:find_or_create_world(mode, callback)\nclient.realtime:join_world(world_id, callback)\nclient.realtime:send_world_input(input_table)\nclient.realtime:leave_world()\nclient.realtime:send_chat_message(channel, content)\n```\n\n#### Events\n\n| Event                | Payload shape                                    |\n| -------------------- | ------------------------------------------------ |\n| `connected`          | `{player_id}`                                    |\n| `match_matched`      | `{match_id, players}`                            |\n| `match_joined`       | `{match_id, players}`                            |\n| `match_state`        | `{tick, players, ...}` (game-shaped)             |\n| `match_finished`     | game-shaped result                               |\n| `world_joined`       | `{world_id, ...}`                                |\n| `world_tick`         | `{tick, updates}` (entity diffs — auto-merged)   |\n| `entity_added`       | `(id, state)` after merge                        |\n| `entity_updated`     | `(id, state, changed_fields)` after merge        |\n| `entity_removed`     | `(id)` after merge                               |\n| `tick`               | `(tick, raw_payload)` after entity dispatch      |\n| `error`              | `{reason, ...}`                                  |\n\n\u003e ⚠️ Two events look similar but mean different things:\n\u003e\n\u003e - `match_matched` — server-pushed when the matchmaker pairs you. **This is what the smoke listens for.**\n\u003e - `match_joined` — reply to a client-initiated `match.join`.\n\n## Smoke test\n\n`smoke_tests/smoke.lua` is the canonical [SMOKE.md](https://github.com/widgrensit/sdk_demo_backend/blob/main/SMOKE.md) flow against `sdk_demo_backend`. It runs as a standalone Lua script — does **not** require `love` — so CI can validate the SDK end-to-end without installing LÖVE:\n\n```bash\n# In one terminal:\ncd sdk_demo_backend \u0026\u0026 docker compose up -d\n\n# In another:\ncd asobi-love2d\nASOBI_URL=http://localhost:8084 lua smoke_tests/smoke.lua\n```\n\nA passing smoke is a release prerequisite.\n\n## Limitations\n\n- **No HTTPS / `wss://` out of the box.** LÖVE does not bundle `luasec`. Add it to your path if you need TLS.\n- **API surface is intentionally minimal for v0.x.** Worlds, leaderboards, economy, social, etc. — most protocol verbs are reachable via `client.realtime:_send(...)` directly until typed wrappers land.\n- **Single-frame messages only.** No fragmented WebSocket messages, no per-message-deflate. Matches the asobi server's default frame shape.\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwidgrensit%2Fasobi-love2d","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwidgrensit%2Fasobi-love2d","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwidgrensit%2Fasobi-love2d/lists"}