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.