獨立遊戲前後端一體化解決方案 · 獨立遊戲開發者的圓夢大使
📖 文檔 · 🚀 快速開始 · 💬 QQ群: 467608841
🌐 語言: English | 简体中文 | 繁體中文 | 日本語 | 한국어
GameFrameX Server 是基於 C# .NET 10.0 開發的高效能遊戲伺服器框架,採用 Actor 模型設計,支援熱更新機制。框架將持久化狀態與業務邏輯嚴格分離,專為多人線上遊戲場景設計。
設計理念:大道至簡,以簡化繁
- Actor 模型 — 基於 TPL DataFlow 的無鎖高並行架構,透過訊息傳遞避免傳統鎖競爭
- 狀態-邏輯分離 — 持久化資料(Apps 層)與可熱更業務邏輯(Hotfix 層)嚴格分離
- 零停機熱更新 — 執行時替換業務邏輯組件,無需重啟伺服器
- 多協定網路 — 支援 TCP、WebSocket、HTTP 協定,內建訊息編解碼與壓縮
- MongoDB 持久化 — 基於 CacheState 的透明 ORM 映射,自動序列化/反序列化
- 原始碼產生器 — 基於 Roslyn 的 Agent 程式碼產生,自動處理 Actor 訊息佇列排程
- 設定表系統 — 整合 Luban 設定產生,支援 JSON 熱載入
- OpenTelemetry — 內建 Prometheus 指標匯出、健康檢查、效能監控
┌──────────────────────────────────────────────────────────────┐
│ 客戶端層 │
├──────────────────────────────────────────────────────────────┤
│ 網路層 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ TCP │ │WebSocket │ │ HTTP │ │ KCP │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├──────────────────────────────────────────────────────────────┤
│ Hotfix 層(可熱更) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ComponentAgent │ HttpHandler │ EventHandler │ │
│ └──────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│ Apps 層(不可熱更) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ State │ │Component │ │ActorType │ │
│ └──────────┘ └──────────┘ └──────────┘ │
├──────────────────────────────────────────────────────────────┤
│ 框架層(NuGet 套件) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Core │ │ NetWork │ │ DataBase │ │ Monitor │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├──────────────────────────────────────────────────────────────┤
│ 資料庫層 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ MongoDB │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
| 依賴 | 版本要求 |
|---|---|
| .NET SDK | 10.0+ |
| MongoDB | 4.x+ |
| IDE | Visual Studio 2022 / JetBrains Rider / VS Code |
# 1. 克隆倉庫
git clone https://github.com/GameFrameX/Server_main.git
cd Server_main
# 2. 還原依賴
dotnet restore
# 3. 建置
dotnet build
# 4. 執行遊戲伺服器
dotnet run --project GameFrameX.Launcher \
--ServerType=Game \
--ServerId=1000 \
--APMPort=29090| 端點 | 位址 | 說明 |
|---|---|---|
| 健康檢查 | http://localhost:29090/health |
服務健康狀態 |
| 指標監控 | http://localhost:29090/metrics |
Prometheus 指標 |
| 測試介面 | http://localhost:28080/game/api/test |
HTTP 連通性測試 |
Server_main/
├── GameFrameX.Launcher/ # 應用入口點(可執行程式)
│ ├── Program.cs # 啟動引導:註冊狀態類型與協定訊息
│ └── StartUp/
│ └── AppStartUpGame.cs # 遊戲伺服器啟動流程
│
├── GameFrameX.Hotfix/ # 熱更新層(業務邏輯,可執行時替換)
│ ├── Logic/
│ │ ├── Http/ # HTTP 請求處理器
│ │ │ ├── TestHttpHandler.cs
│ │ │ ├── ReloadHttpHandler.cs
│ │ │ ├── Player/ # 帳戶登入、線上查詢
│ │ │ └── Bag/ # 道具發放
│ │ ├── Player/
│ │ │ ├── Bag/ # 背包組件代理
│ │ │ ├── Login/ # 登入/登出邏輯
│ │ │ └── Pet/ # 寵物組件代理
│ │ ├── Account/ # 帳號組件代理
│ │ └── Server/ # 伺服器全域組件代理
│ └── StartUp/ # 熱更新啟動流程
│ ├── AppStartUpHotfixGameByEntry.cs # 載入入口
│ ├── AppStartUpHotfixGameByMain.cs # 網路/連線管理
│ ├── AppStartUpHotfixGameByHeart.cs # 心跳處理
│ └── AppStartUpHotfixGameByGateWay.cs # 網關通訊
│
├── GameFrameX.Apps/ # 應用狀態層(不可熱更)
│ ├── ActorType.cs # Actor 類型列舉定義
│ ├── Account/Login/ # 登入狀態 + 組件
│ ├── Player/
│ │ ├── Bag/ # 背包狀態 + 組件
│ │ ├── Player/ # 玩家狀態 + 組件
│ │ └── Pet/ # 寵物狀態 + 組件
│ ├── Server/ # 伺服器全域狀態 + 組件
│ └── Common/
│ ├── Session/ # 工作階段管理(SessionManager)
│ ├── Event/ # 事件 ID 定義
│ └── EventData/ # 事件參數
│
├── GameFrameX.Config/ # 設定表系統(Luban 產生)
│ ├── ConfigComponent.cs # 設定載入單例
│ ├── Tables/ # 產生的設定表類別
│ ├── TablesItem/ # 設定資料模型
│ └── json/ # JSON 設定資料檔案
│
├── GameFrameX.Proto/ # 網路協定定義
│ ├── Basic_10.cs # 基礎協定(心跳等)
│ ├── Bag_100.cs # 背包協定
│ ├── User_300.cs # 使用者/帳號協定
│ └── BuiltIn/ # 內建系統協定
│
├── GameFrameX.CodeGenerator/ # Roslyn 原始碼產生器
│ ├── AgentGenerator.cs # Agent 包裝程式碼產生
│ └── AgentTemplate.cs # 程式碼範本
│
├── Server.sln # Visual Studio 解決方案
├── Dockerfile # Docker 多階段建置
├── docker-compose.yml # Docker Compose 編排
└── LICENSE # MIT + Apache 2.0 雙授權條款
GameFrameX 採用三層分離的開發模式,確保業務邏輯可以在不停服的情況下熱更新。
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ State │────▶│ Component │◀────│ ComponentAgent│
│ (資料定義) │ │ (組件外殼) │ │ (業務邏輯) │
│ Apps 層 │ │ Apps 層 │ │ Hotfix 層 │
│ 不可熱更 │ │ 不可熱更 │ │ 可熱更 │
└──────────────┘ └──────────────┘ └──────────────┘
繼承 CacheState,定義持久化資料結構。框架自動處理 MongoDB 序列化。
// 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; }
}繼承 StateComponent<TState>,作為狀態和邏輯的橋樑。
// GameFrameX.Apps/Player/Bag/Component/BagComponent.cs
[ComponentType(GlobalConst.ActorTypePlayer)]
public class BagComponent : StateComponent<BagState> { }繼承 StateComponentAgent<TComponent, TState>,撰寫可熱更的業務程式碼。
// GameFrameX.Hotfix/Logic/Player/Bag/BagComponentAgent.cs
public class BagComponentAgent : StateComponentAgent<BagComponent, BagState>
{
public async Task OnAddBagItem(INetWorkChannel netWorkChannel,
ReqAddItem message, RespAddItem response)
{
// 校驗物品設定是否存在
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(); // 自動持久化到 MongoDB
return bagState;
}
}繼承 BaseHttpHandler,使用 [HttpMessageMapping] 註冊路由。
// GameFrameX.Hotfix/Logic/Http/TestHttpHandler.cs
[HttpMessageMapping(typeof(TestHttpHandler))]
[HttpMessageResponse(typeof(HttpTestResponse))]
[Description("測試通訊介面")]
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 處理器基類,自動注入 ComponentAgent:
// 玩家級別訊息(綁定特定玩家 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);
}
}
// 全域級別訊息(綁定伺服器 Actor)
[MessageMapping(typeof(ReqHeartBeat))]
internal sealed class HeartBeatHandler
: GlobalRpcComponentHandler<ServerComponentAgent, ReqHeartBeat, RespHeartBeat>
{
protected override Task ActionAsync(ReqHeartBeat request, RespHeartBeat response)
{
return Task.CompletedTask;
}
}使用 [Event] 特性綁定事件 ID,框架自動分發給對應的 ComponentAgent。
[Event(EventId.PlayerLogin)]
internal sealed class PlayerLoginEventHandler : EventListener<PlayerComponentAgent>
{
protected override Task HandleEvent(PlayerComponentAgent agent, GameEventArgs args)
{
return agent.OnLogin();
}
}使用 Roslyn 原始碼產生器自動處理 Actor 訊息佇列排程。透過特性標註控制呼叫行為:
| 特性 | 說明 | 適用場景 |
|---|---|---|
[Service] |
預設模式,方法呼叫進入 Actor 訊息佇列排隊執行 | 所有業務方法 |
[ThreadSafe] |
跳過訊息佇列,直接呼叫(要求方法執行緒安全) | 純讀操作、無狀態計算 |
[Discard] |
即發即棄,不等待傳回值 | 日誌、統計等不需要結果的場景 |
[TimeOut(ms)] |
為訊息佇列呼叫設定逾時時間 | 需要逾時控制的長操作 |
public class ServerComponentAgent : StateComponentAgent<ServerComponent, ServerState>
{
// 進入 Actor 訊息佇列排隊執行
[Service]
public virtual Task<bool> IsOnline(long roleId) { ... }
// 跳過訊息佇列,執行緒安全直接呼叫
[Service]
[ThreadSafe]
public virtual long FirstStartTime()
{
return State.FirstStartTime;
}
// 即發即棄,不阻塞呼叫方
[Service]
[Discard]
public virtual ValueTask AddOnlineRole(long roleId)
{
OwnerComponent.OnlineSet.Add(roleId);
return ValueTask.CompletedTask;
}
}使用 ConfigComponent 單例存取 Luban 產生的設定表:
var config = ConfigComponent.Instance.GetConfig<TbItemConfig>();
// 使用 TryGet 安全查詢
if (config.TryGet(itemId, out var itemConfig))
{
// 使用 itemConfig.Name, itemConfig.Type 等
}透過 GameDb 靜態類別進行 MongoDB CRUD 操作:
// 查詢
var state = await GameDb.FindAsync<LoginState>(
m => m.UserName == userName && m.Password == password);
// 新增/更新
await GameDb.AddOrUpdateAsync(loginState);
// 列表查詢
var list = await GameDb.FindListAsync<LoginState>(m => m.Id != 0);
// 刪除
var count = await GameDb.DeleteAsync(state);熱更新系統允許在不停服的情況下替換業務邏輯。
- Apps 層(
GameFrameX.Apps):包含狀態定義和組件外殼,不可熱更 - Hotfix 層(
GameFrameX.Hotfix):包含所有業務邏輯,可熱更 - Hotfix 組件輸出到
hotfix/目錄,執行時由HotfixManager載入
# 透過 HTTP 介面觸發(指定版本號)
curl "http://localhost:28080/game/api/Reload?version=1.0.1"| 設定項 | 說明 | 預設值 | 範例 |
|---|---|---|---|
ServerType |
伺服器類型 | — | Game |
ServerId |
伺服器唯一識別 | — | 1000 |
InnerPort |
TCP 內部通訊埠 | — | 29100 |
HttpPort |
HTTP 服務埠 | 0 |
28080 |
WsPort |
WebSocket 服務埠 | 0 |
29110 |
MetricsPort |
Prometheus 指標埠 | 0 |
29090 |
DataBaseUrl |
MongoDB 連線字串 | — | mongodb://localhost:27017 |
DataBaseName |
資料庫名稱 | — | gameframex |
dotnet GameFrameX.Launcher.dll \
--ServerType=Game \
--ServerId=1000 \
--InnerPort=29100 \
--HttpPort=28080 \
--WsPort=29110 \
--MetricsPort=29090 \
--DataBaseUrl=mongodb://127.0.0.1:27017 \
--DataBaseName=game_dbdocker-compose up --build| 埠 | 協定 | 說明 |
|---|---|---|
29090 |
HTTP | APM 指標 / 健康檢查 |
29100 |
TCP | 遊戲客戶端連線 |
29110 |
WebSocket | WebSocket 連線 |
28080 |
HTTP | HTTP API |
訊息 ID 由模組 ID 位移計算:(moduleId << 16) + seqId
| 模組 | ID 範圍 | 檔案 | 說明 |
|---|---|---|---|
| 系統 | -10 ~ -1 | Player_-10.cs, Service_-3.cs |
內建系統協定 |
| 基礎 | 10 | Basic_10.cs |
心跳、伺服器就緒通知 |
| 背包 | 100 | Bag_100.cs |
物品增刪改查、使用、合成 |
| 使用者 | 300 | User_300.cs |
登入、註冊、角色列表 |
| 元件 | 技術 |
|---|---|
| 執行時 | .NET 10.0 |
| 資料庫 | MongoDB |
| 網路框架 | SuperSocket |
| 序列化 | protobuf-net |
| 設定產生 | Luban |
| 程式碼產生 | Roslyn Source Generator |
| 監控 | OpenTelemetry + Prometheus |
| 物件映射 | Mapster |
| 容器化 | Docker + Docker Compose |
- Fork 本倉庫
- 建立功能分支(
git checkout -b feature/amazing-feature) - 提交變更(
git commit -m 'feat: 新增某個功能') - 推送到分支(
git push origin feature/amazing-feature) - 建立 Pull Request
本專案採用 MIT License 和 Apache License 2.0 雙授權條款分發。詳見 LICENSE 檔案。
