GameFrameX Server 是基于 C# .NET 10.0 开发的高性能、跨平台游戏服务器框架,采用 Actor 模型设计,支持热更新机制。专为多人在线游戏开发而设计,支持 Unity3D、Godot、LayaBox 等多种客户端平台集成。
设计理念:大道至简,以简化繁
┌─────────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ Unity3D / Godot / LayaBox / Cocos Creator │
├─────────────────────────────────────────────────────────────────┤
│ 网络层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ TCP │ │WebSocket │ │ HTTP │ │ KCP │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ 消息处理层 │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ TCP 消息处理器 │ │ HTTP 处理器 │ │ 跨进程消息路由 │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Actor 层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 玩家 │ │ 服务器 │ │ 账户 │ │ 全局 │ │
│ │ Actor │ │ Actor │ │ Actor │ │ Actor │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ 组件-代理层(热更新边界) │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ Apps 层 (不可热更) │ │ Hotfix 层 (可热更) │ │
│ │ StateComponent<T> │←→│ StateComponentAgent<T,TState>│ │
│ │ CacheState │ │ ComponentAgent │ │
│ └─────────────────────┘ └─────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ 数据库层 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ MongoDB │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Server/
├── GameFrameX.Launcher/ # 应用入口点
├── GameFrameX.StartUp/ # 启动编排和初始化
├── GameFrameX.Core/ # 核心框架(Actor 系统、组件、事件、热更新管理)
├── GameFrameX.Apps/ # 状态数据层(账户、玩家、服务器模块)— 不可热更
├── GameFrameX.Hotfix/ # 业务逻辑层(HTTP、玩家、服务器处理器)— 可热更
├── GameFrameX.Config/ # 游戏配置表(JSON 格式,LuBan 生成)
├── GameFrameX.Core.Config/ # 核心配置管理
├── GameFrameX.Proto/ # ProtoBuf 协议定义
├── GameFrameX.ProtoBuf.Net/ # ProtoBuf 序列化实现
├── GameFrameX.NetWork/ # 网络核心(消息对象、发送器、WebSocket)
├── GameFrameX.NetWork.Abstractions/ # 网络接口(IMessage、IMessageHandler、消息映射)
├── GameFrameX.NetWork.HTTP/ # HTTP 服务器(Swagger、Kestrel、BaseHttpHandler)
├── GameFrameX.NetWork.Kcp/ # KCP 协议支持(基于 UDP 的可靠传输)
├── GameFrameX.NetWork.Message/ # 消息管道和编解码
├── GameFrameX.NetWork.RemoteMessaging/ # 跨进程远程消息(断路器、重试、一致性哈希)
├── GameFrameX.DataBase/ # 数据库抽象层
├── GameFrameX.DataBase.Mongo/ # MongoDB 实现(健康监控、重试、批量操作)
├── GameFrameX.Localization/ # 本地化系统(Keys.*.cs + .resx 资源文件)
├── GameFrameX.Monitor/ # OpenTelemetry + Prometheus 指标集成
├── GameFrameX.Utility/ # 工具集(日志、压缩、对象池、Mapster、Harmony)
├── GameFrameX.Client/ # 测试客户端(TCP 连接)
├── GameFrameX.CodeGenerator/ # Roslyn 源码生成器(热更新代理包装类)
├── GameFrameX.AppHost/ # .NET Aspire 应用主机
├── GameFrameX.AppHost.ServiceDefaults/ # Aspire 共享默认配置(OTel、服务发现)
└── Tests/
└── GameFrameX.Tests/ # xUnit 测试套件
克隆仓库
git clone https://github.com/GameFrameX/GameFrameX.git
cd GameFrameX/Server
还原依赖
dotnet restore
构建项目
dotnet build
启动 MongoDB
# 本地安装方式
mongod --dbpath /path/to/data
# 或使用 Docker
docker run -d -p 27017:27017 --name mongo mongo:8.2
运行服务器
dotnet run --project GameFrameX.Launcher -- \
--ServerType=Game \
--ServerId=1000 \
--OuterPort=29100 \
--HttpPort=28080 \
--DataBaseUrl=mongodb://127.0.0.1:27017 \
--DataBaseName=gameframex
验证启动
http://localhost:28080/game/api/healthGameFrameX 使用命令行参数 (--Key=Value) 进行配置,所有配置项定义在 StartupOptions 类中。
| 配置项 | 说明 | 默认值 | 示例 |
|---|---|---|---|
ServerType | 服务器类型(必填) | 无 | Game、Social |
ServerId | 服务器唯一标识 ID | 无 | 1000 |
ServerInstanceId | 服务器实例 ID(区分同类型不同实例) | 0 | 1001 |
IsSingleMode | 是否单进程模式 | false | true |
MinModuleId | 业务模块起始 ID(模块分片) | 0 | 100 |
MaxModuleId | 业务模块结束 ID(模块分片) | 0 | 1000 |
TimeZone | 服务器时区 | Asia/Shanghai | UTC |
IsUseTimeZone | 是否启用自定义时区 | false | true |
Language | 语言设置 | 无 | zh-CN |
| 配置项 | 说明 | 默认值 | 示例 |
|---|---|---|---|
InnerHost | 内部通信 IP(集群间) | 0.0.0.0 | 0.0.0.0 |
InnerPort | 内部通信端口 | 8888 | 29100 |
OuterHost | 外部通信 IP(面向客户端) | 0.0.0.0 | 0.0.0.0 |
OuterPort | 外部通信端口 | 无 | 29100 |
IsEnableTcp | 是否启用 TCP 服务 | true | true |
IsEnableUdp | 是否启用 UDP 服务 | false | true |
IsEnableWebSocket | 是否启用 WebSocket | false | true |
WsPort | WebSocket 端口 | 8889 | 29300 |
IsEnableHttp | 是否启用 HTTP 服务 | true | true |
HttpPort | HTTP 服务端口 | 8080 | 28080 |
HttpsPort | HTTPS 服务端口 | 无 | 443 |
HttpUrl | API 接口根路径 | /game/api/ | /game/api/ |
HttpIsDevelopment | HTTP 开发模式(启用 Swagger) | false | true |
| 配置项 | 说明 | 默认值 | 示例 |
|---|---|---|---|
DataBaseUrl | MongoDB 连接字符串 | 无 | mongodb://localhost:27017 |
DataBaseName | 数据库名称 | 无 | gameframex |
DataBasePassword | 数据库密码 | 无 | your_password |
| 配置项 | 说明 | 默认值 | 示例 |
|---|---|---|---|
ActorTimeOut | Actor 任务执行超时(毫秒) | 30000 | 60000 |
ActorQueueTimeOut | Actor 队列超时(毫秒) | 30000 | 60000 |
ActorRecycleTime | Actor 空闲回收时间(分钟) | 15 | 30 |
SaveDataInterval | 数据保存间隔(毫秒) | 30000 | 60000 |
SaveDataBatchCount | 批量保存数量 | 500 | 1000 |
SaveDataBatchTimeOut | 批量保存超时(毫秒) | 30000 | 60000 |
| 配置项 | 说明 | 默认值 | 示例 |
|---|---|---|---|
IsDebug | 调试日志总开关 | false | true |
LogIsConsole | 输出到控制台 | true | false |
LogIsWriteToFile | 输出到文件 | true | false |
LogEventLevel | 日志级别 | Debug | Information |
LogRollingInterval | 日志滚动间隔 | Day | Hour |
LogIsFileSizeLimit | 限制单个文件大小 | true | false |
LogFileSizeLimitBytes | 文件大小限制 | 104857600 (100MB) | 52428800 |
LogRetainedFileCountLimit | 保留文件数量 | 31 | 90 |
LogIsGrafanaLoki | 输出到 Grafana Loki | false | true |
LogGrafanaLokiUrl | Grafana Loki 地址 | http://localhost:3100 | — |
| 配置项 | 说明 | 默认值 | 示例 |
|---|---|---|---|
IsOpenTelemetry | 启用 OpenTelemetry | false | true |
IsOpenTelemetryMetrics | 启用指标收集 | false | true |
IsOpenTelemetryTracing | 启用分布式追踪 | false | true |
MetricsPort | Prometheus 指标端口 | 0(复用 HTTP 端口) | 9090 |
IsMonitorMessageTimeOut | 监控消息处理超时 | false | true |
MonitorMessageTimeOutSeconds | 超时阈值(秒) | 1 | 5 |
| 配置项 | 说明 | 默认值 | 示例 |
|---|---|---|---|
WorkerId | 雪花 ID 工作节点 ID | 1 | 2 |
DataCenterId | 雪花 ID 数据中心 ID | 1 | 2 |
# 最小启动参数
dotnet GameFrameX.Launcher.dll \
--ServerType=Game \
--ServerId=1000 \
--DataBaseUrl=mongodb://127.0.0.1:27017 \
--DataBaseName=game_db
# 完整启动参数
dotnet GameFrameX.Launcher.dll \
--ServerType=Game \
--ServerId=1000 \
--ServerInstanceId=1 \
--InnerHost=0.0.0.0 \
--InnerPort=29100 \
--OuterHost=0.0.0.0 \
--OuterPort=29100 \
--HttpPort=28080 \
--IsEnableHttp=true \
--HttpIsDevelopment=true \
--IsEnableWebSocket=false \
--DataBaseUrl=mongodb://127.0.0.1:27017 \
--DataBaseName=gameframex \
--IsDebug=true \
--IsOpenTelemetry=true \
--IsOpenTelemetryMetrics=true \
--LogIsConsole=true \
--LogIsWriteToFile=true
框架的核心设计模式是状态-逻辑分离,将持久化状态(Apps 层,不可热更)与业务逻辑(Hotfix 层,可热更)严格分离。
1. 定义状态(Apps 层)
// GameFrameX.Apps/Player/BagState.cs
public class BagState : BaseCacheState
{
public List<ItemData> Items { get; set; } = new List<ItemData>();
public int MaxSlots { get; set; } = 50;
}
2. 创建组件(Apps 层)
// GameFrameX.Apps/Player/BagComponent.cs
public class BagComponent : StateComponent<BagState>
{
protected override async Task OnInit()
{
await base.OnInit();
// 初始化组件状态
}
}
3. 实现业务逻辑(Hotfix 层)
// GameFrameX.Hotfix/Logic/Player/BagComponentAgent.cs
public class BagComponentAgent : StateComponentAgent<BagComponent, BagState>
{
public async Task<bool> AddItem(int itemId, int count)
{
if (State.Items.Count >= State.MaxSlots)
{
return false;
}
var item = new ItemData { Id = itemId, Count = count };
State.Items.Add(item);
await Save();
return true;
}
}
4. 访问组件代理
// 通过 ActorManager 获取组件代理
var bagAgent = await ActorManager.GetComponentAgent<BagComponentAgent>(playerId);
var result = await bagAgent.AddItem(1001, 10);
HTTP 处理器继承 BaseHttpHandler,使用 [HttpMessageMapping] 特性注册路由。
[HttpMessageMapping(typeof(GetPlayerInfoHandler))]
[Description("获取玩家信息")]
public sealed class GetPlayerInfoHandler : BaseHttpHandler
{
public override async Task<MessageObject> Action(
string ip, string url,
Dictionary<string, object> parameters,
MessageObject messageObject)
{
var request = (GetPlayerInfoRequest)messageObject;
var response = new GetPlayerInfoResponse();
var agent = await ActorManager.GetComponentAgent<PlayerComponentAgent>(request.PlayerId);
if (agent == null)
{
response.ErrorCode = (int)ResultCode.PlayerNotFound;
return response;
}
response.PlayerInfo = await agent.GetPlayerInfo();
return response;
}
}
TCP 消息处理器负责处理客户端通过 TCP 连接发送的游戏消息。
单向消息处理器:
[MessageMapping(typeof(ReqChatMessage))]
internal sealed class ChatMessageHandler : PlayerComponentHandler<ChatComponentAgent, ReqChatMessage>
{
protected override async Task ActionAsync(ReqChatMessage request)
{
await ComponentAgent.ProcessChatMessage(request);
}
}
RPC 处理器(请求-响应):
[MessageMapping(typeof(ReqAddItem))]
internal sealed class AddItemHandler : PlayerRpcComponentHandler<BagComponentAgent, ReqAddItem, RespAddItem>
{
protected override async Task ActionAsync(ReqAddItem request, RespAddItem response)
{
try
{
// ComponentAgent 由基类自动注入
await ComponentAgent.AddItem(request, response);
}
catch (Exception e)
{
LogHelper.Fatal(e);
response.ErrorCode = (int)OperationStatusCode.InternalServerError;
}
}
}
事件系统用于 Actor 之间的松耦合通信。
[Event(EventId.PlayerLogin)]
internal sealed class PlayerLoginEventHandler : EventListener<PlayerComponentAgent>
{
protected override Task HandleEvent(PlayerComponentAgent agent, GameEventArgs gameEventArgs)
{
if (agent == null)
{
return Task.CompletedTask;
}
// 处理玩家登录事件
return agent.OnLogin();
}
}
热更新系统通过 AssemblyLoadContext(可回收)实现程序集的运行时加载和卸载:
┌───────────────────────────────────────────────────────┐
│ Apps 层(不可热更) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ StateComponent│ │ StateComponent│ │ StateComponent│ │
│ │ 持久化状态 │ │ 持久化状态 │ │ 持久化状态 │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
├─────────┼────────────────┼────────────────┼───────────┤
│ ▼ ▼ ▼ │
│ Hotfix 层(可热更)— 通过 AssemblyLoadContext 加载 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ComponentAgent│ │ComponentAgent│ │ComponentAgent│ │
│ │ 业务逻辑 │ │ 业务逻辑 │ │ 业务逻辑 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Msg Handler │ │ EventHandler│ │ HttpHandler │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└───────────────────────────────────────────────────────┘
GameFrameX.Hotfix.dllHotfixManager 使用可回收的 AssemblyLoadContext 加载新 DLLHotfixModule 扫描新程序集中的代理、处理器、事件监听器ActorManager.ClearAgent() 清除缓存的代理实例# 触发热更新(指定版本号)
curl -X POST "http://localhost:28080/game/api/Reload?version=1.7.2"
使用 docker-compose.yml 启动包含 MongoDB + Game + Social 的完整环境:
# 构建并启动
docker compose up -d --build
# 查看运行状态
docker compose ps
# 查看日志
docker compose logs -f game social
# 停止
docker compose down
服务端口映射:
| 服务 | 容器内端口 | 宿主机端口 | 说明 |
|---|---|---|---|
| MongoDB | 27017 | 37017 | 数据库 |
| Game TCP | 29100 | 39100 | 游戏服务器 |
| Game HTTP | 28080 | 38080 | 游戏服务器 HTTP API |
| Social TCP | 29400 | 39400 | 社交服务器 |
| Social HTTP | 28081 | 38081 | 社交服务器 HTTP API |
使用 docker-compose.multi.yml 启动包含 1 个 MongoDB + 2 个 Social + 10 个 Game 的集群环境:
# 构建并启动
docker compose -f docker-compose.multi.yml up -d --build
# 查看运行状态
docker compose -f docker-compose.multi.yml ps
# 停止
docker compose -f docker-compose.multi.yml down
集群拓扑:
| 组件 | 实例数 | 说明 |
|---|---|---|
| MongoDB | 1 | 共享数据库 |
| Social | 2 | 社交服务器(social-1, social-2) |
| Game | 10 | 游戏服务器(game-1 ~ game-10) |
所有实例通过 Aspire 风格的环境变量进行服务发现:
environment:
services__Social_2001__tcp__0: "tcp://social-1:29400"
services__Social_2002__tcp__0: "tcp://social-2:29401"
services__Game_1001__tcp__0: "tcp://game-1:29100"
# ...
# 构建镜像
docker build -t gameframex/server:custom .
# 运行
docker run -d \
--name my-game-server \
-p 29100:29100 \
-p 28080:28080 \
gameframex/server:custom \
--ServerType=Game \
--ServerId=2000 \
--DataBaseUrl=mongodb://mongo-host:27017 \
--DataBaseName=my_game
# 确保多实例环境已启动
docker compose -f docker-compose.multi.yml up -d --build
# 执行跨进程冒烟测试
./scripts/multi/smoke-cross-process.sh
脚本验证内容:
game-1 → social 跨进程调用game-2 → social 跨进程调用code=0 且 FriendCount >= 1模拟真实客户端反复"登录 → 在线 → 主动断开 → 重连登录":
# 默认参数运行
./scripts/multi/run-bots-rpc.sh
# 自定义参数
BOT_COUNT=200 \
TCP_PORT=49100 \
LOGIN_URL=http://127.0.0.1:48080/game/api/ \
DISCONNECT_AFTER_LOGIN_SECONDS=20 \
RUN_SECONDS=300 \
./scripts/multi/run-bots-rpc.sh
可选环境变量:
| 变量 | 说明 | 默认值 |
|---|---|---|
BOT_COUNT | 机器人数量 | — |
TCP_PORT | TCP 连接端口 | 49100 |
LOGIN_URL | 登录接口地址 | http://127.0.0.1:48080/game/api/ |
DISCONNECT_AFTER_LOGIN_SECONDS | 登录后断开延迟(秒) | 20 |
RUN_SECONDS | 总运行时长(秒) | 300 |
# 查看所有服务日志
docker compose -f docker-compose.multi.yml logs -f
# 查看指定服务日志
docker compose -f docker-compose.multi.yml logs -f game-1 game-2 social-1 social-2
# 重建并启动(代码变更后)
docker compose -f docker-compose.multi.yml up -d --build
| 端点 | 说明 |
|---|---|
http://<host>:<HttpPort>/game/api/health | 健康检查 |
http://<host>:<MetricsPort>/metrics | Prometheus 指标 |
db_operation_latency_ms)、重试次数(db_open_retry_total)、健康状态(db_health_status)# 运行所有测试
dotnet test
# 运行指定测试项目
dotnet test Tests/GameFrameX.Tests/GameFrameX.Tests.csproj
# 运行并显示详细输出
dotnet test --logger "console;verbosity=detailed"
测试项目基于 xUnit,覆盖以下模块:
| 测试目录 | 说明 |
|---|---|
Utility/ | 数学/定点数测试、压缩、随机数、ID 生成、单例 |
NetWork/Kcp/ | KCP 管道过滤器、会话管理、服务器集成测试 |
DataBase/ | MongoDB 连接和查询测试 |
ProtoBuff/ | Protobuf 序列化和对象池测试 |
Localization/ | 本地化键值解析测试 |
RemoteMessaging/ | 跨进程消息测试 |
UnifiedMessaging/ | 统一跨进程消息测试 |
StartUp/ | HTTP 服务器路由注册测试 |
我们欢迎任何形式的贡献!请遵循以下步骤:
git checkout -b feature/amazing-feature)git commit -m 'feat: 添加某个功能')git push origin feature/amazing-feature)提交信息请遵循 Angular 提交规范。
本项目采用 MIT 许可证 与 Apache License 2.0 双许可证分发。详见 LICENSE 文件。
如果这个项目对你有帮助,请给我们一个 Star
Made by GameFrameX Team