使用Lua的服务器渲染多人游戏(无需客户端代码)
Server-rendered multiplayer games with Lua (no client code)

原始链接: https://cleoselene.com/

该引擎通过处理状态同步和延迟等复杂问题,简化了多人游戏开发,使开发者能够专注于游戏玩法。它采用服务器渲染模型,结合客户端输入,几乎不可能作弊,并保护游戏逻辑免受逆向工程。 引擎使用原生Rust进行性能关键任务(物理、寻路),并利用WebRTC进行低延迟通信,它流式传输绘图图元而不是像素,优化带宽。游戏使用Lua脚本编写,只需要一个包含`init`、`update`、`draw`和网络事件处理函数的最小`main.lua`文件。 主要功能包括用于高效对象管理的空间数据库、内置物理引擎和A*寻路。该引擎使用固定的800x600虚拟坐标系统,并且免费用于个人和商业用途,提供对托管的完全控制,且没有每玩家费用。

相关文章

原文

🕹️ Multiplayer as Simplicity

Code multiplayer games as simple as single-player. No more worrying about client-server state sync or lag compensation. Just one loop, everywhere.

⚡ Primitive Streaming

We stream drawing primitives instead of heavy pixels or complex state objects. It's faster than cloud gaming and lighter than traditional networking.

🚀 High Performance

All performance-critical logic (Physics, Pathfinding) runs in Native Rust. Communication is powered by WebRTC for sub-frame latency.

🛡️ Impossible to Cheat

Since the game is entirely server-rendered, the client has zero knowledge of the game state. No wallhacks, no aimbots, no compromise.

🤫 Hide Your Secrets

Keep your game's internal logic and secrets safe. Since your script never leaves the server, players can't reverse-engineer your code or find hidden content.

🌍 Host Anywhere

Total freedom. This version of the engine is free for personal or commercial use on your own infrastructure. No lock-in, no per-player fees.

A Multiplayer-First Server-Rendered Game Engine with Lua Scripting.

Game Structure

A minimal game script (main.lua) must implement these callbacks:

-- Called once when the server starts
function init()
    -- Initialize physics, load assets, setup state
    db = api.new_spatial_db(250)
    phys = api.new_physics_world(db)
    api.load_sound("jump", "assets/jump.wav")
end

-- Called every server frame (typically 30 TPS)
function update(dt)
    -- Advance physics simulation
    phys:step(dt)

    -- Handle collisions
    local events = phys:get_collision_events()
    for _, pair in ipairs(events) do
        -- Handle game logic (damage, score)
    end
end

-- Called for EACH connected client to generate their frame
function draw(session_id)
    api.clear_screen(20, 20, 30)

    -- Draw player-specific view (camera, HUD)
    local p = players[session_id]
    if p then
        api.set_color(255, 255, 255)
        api.draw_text("HP: " .. p.hp, 10, 10)
        -- Use db:query_rect to draw visible entities
    end
end

-- Network Events
function on_connect(session_id)
    print("Player joined: " .. session_id)
    -- Spawn player entity
end

function on_disconnect(session_id)
    print("Player left: " .. session_id)
    -- Despawn entity
end

function on_input(session_id, key_code, is_down)
    -- Handle input (key_code is JS key code)
    -- 37=Left, 38=Up, 39=Right, 40=Down, 32=Space, 90=Z
    if players[session_id] then
        players[session_id].inputs[key_code] = is_down
    end
end

API Reference

Display & Coordinates

The engine uses a fixed virtual coordinate system of 800x600. All drawing commands (api.fill_rect, api.draw_line, etc.) use these coordinates. The engine automatically scales the output to fit the user's screen while maintaining the logical resolution and aspect ratio.

Graphics & Sound

Method Description
api.clear_screen(r, g, b) Clears the frame with a 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]) Draws a line.
api.draw_text(text, x, y) Draws text at position.
api.load_sound(name, url) Preloads a sound from a URL/path (relative to script).
api.play_sound(name, [loop]) Plays a loaded sound.
api.stop_sound(name) Stops a sound.
api.set_volume(name, volume) Sets volume (0.0 to 1.0).

Spatial DB (Geometry)

The engine provides a high-performance Spatial Hash Grid for broadphase queries.

Creation

local db = api.new_spatial_db(cell_size) -- e.g., 250

Object Management

Method Description Returns
db:add_circle(x, y, radius, tag) Registers a circular entity. id (int)
db:add_segment(x1, y1, x2, y2, tag) Registers a line segment (wall). id (int)
db:remove(id) Removes an entity from the DB. nil
db:update(id, x, y) Manually updates position (teleport). nil
db:get_position(id) Returns x, y of the entity. x, y

Queries (Sensors)

Method Description Returns
db:query_range(x, y, r, [tag]) Finds entity IDs within radius r. {id1, id2...}
db:query_rect(x1, y1, x2, y2, [tag]) Finds entity IDs within AABB (Culling). {id1, id2...}
db:cast_ray(x, y, angle, dist, [tag]) Casts a ray. id, frac, hit_x, hit_y or nil

Physics Engine (Simulation)

Handles rigid body dynamics, integration, and collision resolution.

Creation

local phys = api.new_physics_world(db)

Body Management

Method Description
phys:add_body(id, props) Adds physics to an entity. Props: {mass=1.0, restitution=0.5, drag=0.0}.
phys:set_velocity(id, vx, vy) Sets velocity.
phys:get_velocity(id) Returns vx, vy.
phys:set_gravity(x, y) Sets global gravity vector.
phys:step(dt) Advances simulation. Resolves collisions and updates db.
phys:get_collision_events() Returns list of collisions since last step: {{idA, idB}, ...}.

Graph Navigation (Pathfinding)

Native A* implementation on a custom graph.

Method Description
nav = api.new_graph() Creates a new navigation graph.
nav:add_node(id, x, y) Adds a node to the graph.
nav:add_edge(u, v) Adds an edge (connection) between nodes.
nav:find_path(start, end) Returns a list of node IDs forming the shortest path.
联系我们 contact @ memedata.com