← BackJan 4, 2026

Introducing Cleoselene: Lua‑Scripting, Multiplayer‑First, Server‑Rendered Game Engine

Cleoselene is a cutting‑edge server‑side game engine that delivers multiplayer‑first gameplay with Lua scripting and a high‑performance spatial database. It delivers consistent physics, sound, and graphics across clients while scaling automatically to their displays. The engine is in early preview and invites feedback from developers.

# Overview Cleoselene is a lightweight, server‑rendered game engine designed for fast, multiplayer‑centric development. The engine runs Lua scripts on the server and streams final frames to connected clients, guaranteeing authoritative simulation and eliminating common client‑side cheating vectors. — Active Development: Cleoselene is currently in an early preview stage. Please reach out to help@cleoselene.com before using it for any serious projects or production work. # Core Concepts ## Virtual Coordinate System Cleoselene renders at a fixed logical resolution of 800×600. All drawing commands use this coordinate space; the runtime automatically scales the output to the player’s viewport while preserving aspect ratio. ## Multiplayer‑First Model Only a single authoritative server runs the game loop. Each client receives rendered frames via the engine’s network stack. This model simplifies state synchronization and reduces latency concerns. # Game Script Structure The minimal `main.lua` file must expose five core hooks: * `init()` – called once on server start. Initialize physics, load assets, and create state. * `update(dt)` – called every server tick (typical 30 TPS). Advance physics, resolve collisions, and apply game logic. * `draw(session_id)` – called for every connected client to compose its frame. Clear the screen, draw the player HUD, and render visible entities. * `on_connect(session_id)` – trigger when a player joins. Create or spawn a player entity. * `on_disconnect(session_id)` – trigger when a player leaves. Clean up the entity. An example snippet: ``` function init() db = api.new_spatial_db(250) phys = api.new_physics_world(db) api.load_sound("jump", "assets/jump.wav") end function update(dt) phys:step(dt) local events = phys:get_collision_events() for _, pair in ipairs(events) do -- handle damage, score, etc. end end function draw(session_id) api.clear_screen(20,20,30) local p = players[session_id] if p then api.set_color(255,255,255) api.draw_text("HP: " .. p.hp, 10,10) -- render entity culling via db:query_rect end end ``` # API Overview ## Rendering & Audio | Function | Purpose | |---|---| | `api.clear_screen(r,g,b)` | Clears the viewport with the specified background color. | | `api.set_color(r,g,b,a?)` | Sets the current drawing color. | | `api.fill_rect(x,y,w,h)` | Draws a filled rectangle. | | `api.draw_line(x1,y1,x2,y2,width?)` | Renders a line. | | `api.draw_text(text,x,y)` | Places text. | | `api.load_sound(name,url)` | Preloads a sound from a file path or URL. | | `api.play_sound(name, loop?)` | Plays a loaded sound. | | `api.stop_sound(name)` | Stops a currently playing sound. | | `api.set_volume(name, volume)` | Adjusts volume for a given sound. | ## Spatial Hash Grid The engine offers a spatial hash for efficient broad‑phase queries. Create it with `local db = api.new_spatial_db(cell_size)`. | Method | Returns | Description | |---|---|---| | `db:add_circle(x,y,radius,tag)` | entity id | Register a circular object. | | `db:add_segment(x1,y1,x2,y2,tag)` | entity id | Register a line segment (wall). | | `db:remove(id)` | nil | Remove an object. | | `db:update(id,x,y)` | nil | Teleport an object. | | `db:get_position(id)` | x,y | Retrieve current coordinates. | | `db:query_range(x,y,r,tag?)` | table of ids | Find objects within radius. | | `db:query_rect(x1,y1,x2,y2,tag?)` | table of ids | Find objects inside an axis‑aligned rectangle. | | `db:cast_ray(x,y,angle,dist,tag?)` | id, frac, hit_x, hit_y or nil | Ray‑cast query. | ## Physics World Create a physics simulation with `local phys = api.new_physics_world(db)`. | Method | Purpose | |---|---| | `phys:add_body(id, props)` | Attach a rigid body to an object. Props may include mass, restitution, and drag. | | `phys:set_velocity(id,vx,vy)` | Set linear velocity. | | `phys:get_velocity(id)` | Retrieve current velocity. | | `phys:set_gravity(x,y)` | Define a global gravity vector. | | `phys:step(dt)` | Advance simulation; resolves collisions and updates the spatial hash. | | `phys:get_collision_events()` | List collisions that occurred since the last step. | ## Navigation Graph An embedded A* implementation provides pathfinding on a custom graph. | Function | Purpose | |---|---| | `nav = api.new_graph()` | Instantiate the graph. | | `nav:add_node(id,x,y)` | Add a navigation node. | | `nav:add_edge(u,v)` | Connect two nodes. | | `nav:find_path(start,end)` | Returns a table of node ids for the shortest route. | # Getting Started 1. Download the Cleoselene distribution from the official repo. 2. Create a project folder and place `main.lua` inside. 3. Run the server: `cleoselene main.lua`. 4. Connect a client via the provided web demo or custom client. 5. Iterate on your game logic in Lua; the server will reload the script automatically on changes. For full reference, consult the `api` documentation bundled with the engine, and explore example projects included in the starter kit. Cleoselene invites developers to experiment, share feedback, and help shape the next generation of multiplayer game architecture.