GameFrameX Server is a high-performance game server framework built on C# .NET 10.0, featuring an Actor Model architecture with hot-update support. The framework enforces strict separation between persistent state and business logic, designed specifically for multiplayer online game scenarios.
Design Philosophy: Simplicity over complexity
Core Features
Actor Model — Lock-free high-concurrency architecture based on TPL DataFlow, avoiding traditional lock contention through message passing
State-Logic Separation — Strict separation between persistent data (Apps layer) and hot-updatable business logic (Hotfix layer)
Zero-Downtime Hot Updates — Replace business logic assemblies at runtime without restarting the server
Multi-Protocol Networking — Support for TCP, WebSocket, and HTTP protocols with built-in message encoding/decoding and compression
MongoDB Persistence — Transparent ORM mapping based on CacheState with automatic serialization/deserialization
Source Generator — Roslyn-based Agent code generation for automatic Actor message queue scheduling
Config Table System — Integrated Luban config generation with JSON hot-reloading
OpenTelemetry — Built-in Prometheus metrics export, health checks, and performance monitoring
Inherit CacheState to define persistent data structures. The framework handles MongoDB serialization automatically.
// GameFrameX.Apps/Player/Bag/Entity/BagState.cs
public sealed class BagState : CacheState
{
public Dictionary<int, BagItemState> List { get; set; } = new();
}
public sealed class BagItemState
{
public long ItemId { get; set; }
public long Count { get; set; }
}
Step 2: Create Component (Apps Layer)
Inherit StateComponent<TState> to serve as the bridge between state and logic.
// GameFrameX.Apps/Player/Bag/Component/BagComponent.cs
[ComponentType(GlobalConst.ActorTypePlayer)]
public class BagComponent : StateComponent<BagState> { }
Step 3: Implement Business Logic (Hotfix Layer)
Inherit StateComponentAgent<TComponent, TState> to write hot-updatable business code.
// GameFrameX.Hotfix/Logic/Player/Bag/BagComponentAgent.cs
public class BagComponentAgent : StateComponentAgent<BagComponent, BagState>
{
public async Task OnAddBagItem(INetWorkChannel netWorkChannel,
ReqAddItem message, RespAddItem response)
{
// Validate item config exists
foreach (var item in message.ItemDic)
{
if (!ConfigComponent.Instance.GetConfig<TbItemConfig>()
.TryGet(item.Key, out var _))
{
response.ErrorCode = (int)OperationStatusCode.NotFound;
return;
}
}
await UpdateChanged(netWorkChannel, message.ItemDic);
}
public async Task<BagState> UpdateChanged(INetWorkChannel netWorkChannel,
Dictionary<int, long> itemDic)
{
var bagState = OwnerComponent.State;
var notify = new NotifyBagInfoChanged();
foreach (var item in itemDic)
{
if (bagState.List.TryGetValue(item.Key, out var value))
{
value.Count += item.Value;
}
else
{
bagState.List[item.Key] = new BagItemState
{
Count = item.Value, ItemId = item.Key
};
}
}
await netWorkChannel.WriteAsync(notify);
await OwnerComponent.WriteStateAsync(); // Auto-persist to MongoDB
return bagState;
}
}
HTTP Handler
Inherit BaseHttpHandler and register with [HttpMessageMapping].
// GameFrameX.Hotfix/Logic/Http/TestHttpHandler.cs
[HttpMessageMapping(typeof(TestHttpHandler))]
[HttpMessageResponse(typeof(HttpTestResponse))]
[Description("Test API endpoint")]
public sealed class TestHttpHandler : BaseHttpHandler
{
public override Task<string> Action(string ip, string url,
Dictionary<string, object> parameters)
{
var response = new HttpTestResponse { Message = "hello" };
return Task.FromResult(HttpJsonResult.SuccessString(response));
}
}
RPC Message Handler
The framework provides two RPC handler base classes with automatic ComponentAgent injection:
// Player-level message (bound to a specific player Actor)
[MessageMapping(typeof(ReqAddItem))]
internal sealed class AddItemHandler
: PlayerRpcComponentHandler<BagComponentAgent, ReqAddItem, RespAddItem>
{
protected override async Task ActionAsync(ReqAddItem request, RespAddItem response)
{
await ComponentAgent.OnAddBagItem(NetWorkChannel, request, response);
}
}
// Global-level message (bound to the server Actor)
[MessageMapping(typeof(ReqHeartBeat))]
internal sealed class HeartBeatHandler
: GlobalRpcComponentHandler<ServerComponentAgent, ReqHeartBeat, RespHeartBeat>
{
protected override Task ActionAsync(ReqHeartBeat request, RespHeartBeat response)
{
return Task.CompletedTask;
}
}
Event Handling
Use the [Event] attribute to bind event IDs. The framework automatically dispatches to the corresponding ComponentAgent.
public class ServerComponentAgent : StateComponentAgent<ServerComponent, ServerState>
{
// Enqueued to Actor message queue
[Service]
public virtual Task<bool> IsOnline(long roleId) { ... }
// Skip message queue, thread-safe direct call
[Service]
[ThreadSafe]
public virtual long FirstStartTime()
{
return State.FirstStartTime;
}
// Fire-and-forget, non-blocking
[Service]
[Discard]
public virtual ValueTask AddOnlineRole(long roleId)
{
OwnerComponent.OnlineSet.Add(roleId);
return ValueTask.CompletedTask;
}
}
Config Table Access
Use the ConfigComponent singleton to access Luban-generated config tables:
var config = ConfigComponent.Instance.GetConfig<TbItemConfig>();
// Safe query with TryGet
if (config.TryGet(itemId, out var itemConfig))
{
// Use itemConfig.Name, itemConfig.Type, etc.
}
Database Operations
Use the GameDb static class for MongoDB CRUD operations:
// Query
var state = await GameDb.FindAsync<LoginState>(
m => m.UserName == userName && m.Password == password);
// Add or update
await GameDb.AddOrUpdateAsync(loginState);
// List query
var list = await GameDb.FindListAsync<LoginState>(m => m.Id != 0);
// Delete
var count = await GameDb.DeleteAsync(state);
Hot Update Mechanism
The hot-update system allows replacing business logic without stopping the server.
Apps Layer (GameFrameX.Apps): Contains state definitions and component shells — not hot-updatable
Hotfix Layer (GameFrameX.Hotfix): Contains all business logic — hot-updatable
Hotfix assembly outputs to the hotfix/ directory, loaded at runtime by HotfixManager
# Trigger via HTTP endpoint (with version number)
curl "http://localhost:28080/game/api/Reload?version=1.0.1"
GameFrameX Server
All-in-One Solution for Indie Game Development · Empowering Indie Developers’ Dreams
📖 Documentation · 🚀 Quick Start · 💬 QQ Group: 467608841
🌐 Language: English | 简体中文 | 繁體中文 | 日本語 | 한국어
Project Overview
GameFrameX Server is a high-performance game server framework built on C# .NET 10.0, featuring an Actor Model architecture with hot-update support. The framework enforces strict separation between persistent state and business logic, designed specifically for multiplayer online game scenarios.
Core Features
Architecture
Quick Start
Prerequisites
Installation & Run
Verify Deployment
http://localhost:29090/healthhttp://localhost:29090/metricshttp://localhost:28080/game/api/testProject Structure
Usage Examples
Core Pattern: State - Component - Agent
GameFrameX uses a three-layer separation pattern that enables hot-updating business logic without server downtime.
Step 1: Define State (Apps Layer)
Inherit
CacheStateto define persistent data structures. The framework handles MongoDB serialization automatically.Step 2: Create Component (Apps Layer)
Inherit
StateComponent<TState>to serve as the bridge between state and logic.Step 3: Implement Business Logic (Hotfix Layer)
Inherit
StateComponentAgent<TComponent, TState>to write hot-updatable business code.HTTP Handler
Inherit
BaseHttpHandlerand register with[HttpMessageMapping].RPC Message Handler
The framework provides two RPC handler base classes with automatic ComponentAgent injection:
Event Handling
Use the
[Event]attribute to bind event IDs. The framework automatically dispatches to the corresponding ComponentAgent.Agent Method Attributes
The Roslyn source generator automatically handles Actor message queue scheduling. Control invocation behavior through attributes:
[Service][ThreadSafe][Discard][TimeOut(ms)]Config Table Access
Use the
ConfigComponentsingleton to access Luban-generated config tables:Database Operations
Use the
GameDbstatic class for MongoDB CRUD operations:Hot Update Mechanism
The hot-update system allows replacing business logic without stopping the server.
GameFrameX.Apps): Contains state definitions and component shells — not hot-updatableGameFrameX.Hotfix): Contains all business logic — hot-updatablehotfix/directory, loaded at runtime byHotfixManagerDocumentation & Resources
Configuration
ServerTypeGameServerId1000InnerPort29100HttpPort028080WsPort029110MetricsPort029090DataBaseUrlmongodb://localhost:27017DataBaseNamegameframexDocker Deployment
29090291002911028080Message Protocol
Message IDs are calculated by module ID shift:
(moduleId << 16) + seqIdPlayer_-10.cs,Service_-3.csBasic_10.csBag_100.csUser_300.csTech Stack
Community & Support
Contributing
git checkout -b feature/amazing-feature)git commit -m 'feat: add some feature')git push origin feature/amazing-feature)License
This project is dual-licensed under the MIT License and Apache License 2.0. See the LICENSE file for details.