Moon Game Server Framework
repository·master·Indexed 21 days ago
https://github.com/sniper00/moonA lightweight, high-performance game server framework based on the actor model. Moon utilizes ASIO for scheduling and Lua for logic scripting, providing an asynchronous environment for networking (TCP, UDP/KCP, Websockets, HTTP), database interaction (Redis, PostgreSQL, MongoDB, MySQL), and cross-platform compatibility across Windows, Linux, and MacOS.
What's inside Moon
- This directory contains a mirror of Lua development code. For official documentation, complete information, and the most up-to-date releases, visit the official Lua website at Lua.org.
What is Moon and how does it work?
masterMoon is a lightweight game server framework based on the actor model.
In Moon's architecture, a single worker thread can host one or more actors (services). These actors communicate with each other through message queues. The framework is designed for high performance and cross-platform compatibility (Windows, Linux, MacOS), utilizing ASIO for scheduling and Lua for logic scripting.
Key features include:
- Asynchronous Programming: Based on Lua coroutines for socket operations, timers, inter-service communication, inter-process communication, and async drivers for Redis, PostgreSQL, MongoDB, and MySQL.
- Optimized Networking: Support for TCP, UDP/KCP, Websockets, and HTTP.
- Built-in Libraries: High-performance Lua JSON, Lua Protobuf, Lua Filesystem, Recast Navigation, and a Lua zset library for ranklists.
How the Actor model architecture works in Moon
masterThe GuessGame example demonstrates Moon's Actor model architecture by separating concerns into specialized services:
service_center: A singleton service that manages the matchmaking process and assigns players to rooms.service_room: An instance-based service that handles the specific game logic within a single room.service_user: A service dedicated to processing messages and state for an individual player.
How `upvaluejoin` enables stateful hot-reloading
masterThe core mechanism for hot-reloading in Moon is
debug.upvaluejoin. This allows a new function to reference the exact same memory location as an old function'supvalue, enabling state sharing between old and new logic.Core Concept
- Update Implementation: The new function executes new code logic.
- Reference Old State: The new function's
upvaluepoints to the old module'supvaluememory address. - No Value Modification:
upvaluejoinonly changes the reference; it does not change the actual value stored in that memory.
Workflow
- Identify by Name: The system finds the
upvaluein the old module using its name. - Connect by Index: It uses
debug.upvaluejointo link the new function's specific index to the old function's specific index. Note that the index position may differ between the old and new versions.
Example Logic
If
old_functionhasshared_countat index 1, andnew_functionhasshared_countat index 2:-- Connect new_function's 2nd upvalue to old_function's 1st upvalue debug.upvaluejoin( new_function, 2, old_function, 1 )After this, modifying
shared_countin either function affects the same memory location.-- Example of the underlying mechanism -- Step 1: Collect old upvalue info local upvalues = { ["shared_count"] = { func = old_function, -- The function owning the upvalue index = 1, -- Position in that function id = 0x12345, -- Unique ID value = 100 -- Current value } } -- Step 2: Connect using upvaluejoin local uvname = "shared_count" local old_uv = upvalues[uvname] debug.upvaluejoin( new_function, 2, -- New function's index old_uv.func, old_uv.index -- Old function and its index )How state is preserved during hot-updates
masterThe Moon hot-update system is designed to update function behavior (implementation) without resetting or modifying the values of state variables (upvalues).
When you perform a hot-update:
- Values remain unchanged: If
shared_countwas105at the moment of the update, it remains105in the new version, even if the new code declareslocal shared_count = 100. - Shared memory: All functions (both original and newly added) reference the same memory location for these upvalues. Any modification to a shared variable by a new function will be visible to all other functions.
- Implementation vs. State: The system updates the code logic inside functions but uses
upvaluejointo connect new function implementations to the existing upvalue memory.
- Values remain unchanged: If
Hot-updating nested functions
masterThe Moon hot-update system supports recursive updates for nested functions. This includes:
- Functions stored as upvalues of other functions.
- Functions stored within tables (e.g.,
handlers.process = function() ... end).
When an update occurs, the system uses
collect_all_uvto recursively collect all nested functions and their upvalues. If a nested function's implementation changes, the system ensures the references are updated throughout the hierarchy.-- v2.lua example of updating nested logic local M = {} local function helper() return "new helper" end local handlers = {} handlers.process = function() return helper() .. " v2" end function M.run() return handlers.process() end return MRules for writing hot-updateable Lua modules
masterTo ensure a Lua module can be safely hot-updated using the Moon system, you must follow these five rules:
- Preserve old local variable declarations: The new version must declare all local variables from the old module (even if they are unused). The values assigned during declaration in the new version will be ignored; the system will use the actual runtime values from the old module.
- New functions can only reference existing upvalues: Any newly added function can reference variables like
shared_countorshared_prefixthat existed in the old module, but it cannot reference new local variables (e.g.,new_counter) introduced in the new version. - Existing functions can modify implementation: You can change the internal logic of a function and add or remove upvalue references, provided those upvalues existed in the old module.
- You can add functions, but you cannot delete them: Adding functions is supported, but deleting a function from the module table will cause the hot-update to fail because external code may still hold references to it.
- Modules must return a table: The hot-update system relies on updating function references within the module table.
-- Example of a valid hot-updateable module structure local M = {} -- State variables (upvalues) local shared_count = 100 local shared_prefix = "[Old] " function M.hello() return "Hello, World!" end return MCommunicate between services
masterMoon supports several inter-service communication patterns:
- Message Passing: Use
moon.send(type, service_handle, data)to send fire-and-forget messages. - Request-Response: Use
moon.call(type, service_handle, data)within anasyncblock to wait for a response. It returnsok, result. - Service Discovery: Use
moon.new_serviceto define a service andmoon.queryservice(name)to find services by name. - Unique Services: When creating a service with
moon.new_service, setunique = trueto make it a global singleton accessible viamoon.queryservice.
Example: Service A calling Service B
local moon = require("moon") -- Define Service B local service_b = moon.new_service { name = "service_b", file = "service_b.lua" } -- Send message to B moon.send("lua", service_b, {cmd = "hello"}) -- Request-Response pattern moon.async(function() local ok, result = moon.call("lua", service_b, {cmd = "ping"}) if ok then print("Service B response:", result.cmd) end end)- Message Passing: Use
How the Service Lifecycle works
masterThe Moon framework manages the lifecycle of services through a specific sequence of calls:
- Instantiation: The
register_func(provided duringregister_service) is called, returning astd::unique_ptr<service>. - Initialization: The framework calls
init(const moon::service_conf& conf). The service must returntrueto proceed. - Execution: The framework enters a loop where it calls
dispatch(moon::message* msg)whenever a message is routed to the service. - Destruction: When the service is removed or the server shuts down, the
~service()destructor is called to release resources.
- Instantiation: The
Multi-round hot-updates
masterThe system supports multiple successive updates (e.g., v1 $\rightarrow$ v2 $\rightarrow$ v3) using a weak table called
origin_functions.How it works:
- Every time
hotfix.updateis called, the system traces back to the original (v1) version to update the upvalue connections. - This ensures that even after many updates, the new functions are correctly linked to the original state variables.
- Intermediate versions (v2, v3) are managed via weak tables, allowing them to be garbage collected if they are no longer referenced.
- Every time
How Moon's Actor model and architecture work
masterMoon is a lightweight game server framework based on the Actor model.
Core Mental Model
- Services as Actors: Every
Serviceis an independent Actor. Services maintain isolated state and do not share memory. Communication between services is strictly handled via message passing through a message queue. - Workers: A
Workeris a thread unit that executes one or more Services. Each Worker has its own independent message queue and can be bound to specific CPU cores (CPU affinity). - Server: The
Serveris the top-level manager that initializes Workers, manages the lifecycle of Services, handles global timers, and manages environment variables.
Communication Flow
Services communicate by sending
Messageobjects. The architecture uses a layered approach: the Network Layer (ASIO) handles protocols (TCP, UDP, KCP, WebSocket, HTTP), which then feed into the Core Components (Server, Worker, Service, Message, Timer) to drive game logic.┌─────────────────────────────────────────────────────────────────┐ │ Moon Server │ ├─────────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Worker 1 │ │ Worker 2 │ │ Worker N │ ... │ │ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │ │ │ │Service│ │ │ │Service│ │ │ │Service│ │ │ │ │ ├───────┤ │ │ ├───────┤ │ │ ├───────┤ │ │ │ │ │Service│ │ │ │Service│ │ │ │Service│ │ │ │ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ └────────────────┴────────────────┘ │ │ │ │ │ ┌───────┴───────┐ │ │ │ Message Queue │ │ │ └───────────────┘ │ ├─────────────────────────────────────────────────────────────────┤ │ Core Components │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Server │ │ Worker │ │ Service │ │ Message │ │ Timer │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ ├─────────────────────────────────────────────────────────────────┤ │ Network Layer (ASIO) │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ TCP │ │ UDP │ │ KCP │ │WebSocket│ │ HTTP │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ └─────────────────────────────────────────────────────────────────┘- Services as Actors: Every
Supported Network Protocols in Moon
masterMoon provides high-performance network communication support via an ASIO-based asynchronous non-blocking I/O layer. The following protocols are supported:
Protocol Message Type Description TCP PTYPE_SOCKET_TCPReliable connection-oriented protocol UDP PTYPE_SOCKET_UDPConnectionless datagram protocol KCP - Reliable UDP protocol (optimized for games) WebSocket PTYPE_SOCKET_WSFull-duplex Web communication MoonSocket PTYPE_SOCKET_MOONMoon's custom protocol