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