### Basic Unity Startup Code Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/02-Unity/01-WritingStartupCode-Unity.md This is a basic example demonstrating how to initialize the Fantasy framework and create a client scene in Unity. It uses the `Entry.Initialize()` method for framework setup and `Scene.Create()` to establish a scene that runs on the Unity main thread. ```csharp using Fantasy; using Fantasy.Async; using Fantasy.Network; using UnityEngine; public class QuickStart : MonoBehaviour { private Scene _scene; private Session _session; private void Start() { StartAsync().Coroutine(); } private async FTask StartAsync() { // 1. Initialize Fantasy framework await Fantasy.Platform.Unity.Entry.Initialize(); // 2. Create a Scene (client scene) // Scene is the core container for the Fantasy framework, all features run under Scene // SceneRuntimeMode.MainThread indicates running on the Unity main thread _scene = await Scene.Create(SceneRuntimeMode.MainThread); Debug.Log("Fantasy framework initialization complete!"); } private void OnDestroy() { // Destroy the Scene, releasing all resources _scene?.Dispose(); } } ``` -------------------------------- ### Example: Simple Roaming Setup Source: https://github.com/qq362946/fantasy/blob/main/Docs/04-Advanced/NetworkDevelopment/08-Roaming.md Demonstrates how to use `CreateRoaming` to set up a roaming component and link it to a chat server. ```APIDOC ## Gate Server: Handling Client Login Request (Simple Version) This example shows a `C2G_LoginRequestHandler` that creates a roaming component and links it to the chat server. ```csharp public class C2G_LoginRequestHandler : MessageRPC { protected override async FTask Run( Session session, C2G_LoginRequest request, G2C_LoginResponse response, Action reply) { // Step 1: Create Roaming Component // roamingId: Unique identifier for roaming, typically player ID // isAutoDispose: Whether to automatically dispose roaming when the session disconnects // delayRemove: Delay in milliseconds before disposing roaming var roaming = await session.CreateRoaming( roamingId: request.PlayerId, isAutoDispose: true, delayRemove: 1000 ); if (roaming == null) { response.ErrorCode = ErrorCode.RoamingCreateFailed; return; } // Step 2: Link to Chat Server var chatConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Chat)[0]; var errorCode = await roaming.Link(session, chatConfig, RoamingType.ChatRoamingType); if (errorCode != 0) { response.ErrorCode = errorCode; return; } Log.Info($"✅ Established roaming route to Chat for player {request.PlayerId}"); await FTask.CompletedTask; } } ``` ``` -------------------------------- ### RuntimeMode Examples Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/03-CommandLineArguments.md Use the `--m` or `--RuntimeMode` argument to control the server's runtime mode. 'Develop' starts all processes, while 'Release' requires `--pid` to start a single process. ```bash dotnet YourServer.dll --m Develop # 发布模式 - 需要配合 ProcessId 使用 dotnet YourServer.dll --m Release --pid 1 ``` -------------------------------- ### StartupGroup Examples Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/03-CommandLineArguments.md Use the `-g` or `--StartupGroup` argument to start a batch of processes defined by a StartupGroup in Fantasy.config. This is useful for deploying server clusters or feature groups. ```bash # 启动组 1 的所有进程 dotnet YourServer.dll --m Release -g 1 # 启动组 2 的所有进程 dotnet YourServer.dll --m Release -g 2 ``` -------------------------------- ### Process Configuration Example for Startup Groups Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/01-ServerConfiguration.md Example XML configurations demonstrating process startup groups for controlling service startup order. ```xml ``` ```xml ``` ```xml ``` ```xml ``` ```xml ``` ```xml ``` -------------------------------- ### Minimal Connection Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/02-Unity/02-FantasyRuntime.md A basic example demonstrating how to connect to a server using the Runtime.Connect method with minimal configuration. ```APIDOC ### Usage Example #### Minimal Example ```csharp using Fantasy; using Fantasy.Async; using UnityEngine; public class SimpleConnection : MonoBehaviour { private async void Start() { // The simplest way to connect await Runtime.Connect( remoteIP: "127.0.0.1", remotePort: 20000, protocol: FantasyRuntime.NetworkProtocolType.TCP, isHttps: false, connectTimeout: 5000, enableHeartbeat: true, heartbeatInterval: 2000, heartbeatTimeOut: 30000, heartbeatTimeOutInterval: 5000, maxPingSamples: 4 ); // After successful connection, access via the Runtime static class Runtime.Session.Send(new MyMessage()); Log.Info($"Ping: {Runtime.PingMilliseconds} ms"); } private void OnDestroy() { Runtime.OnDestroy(); } } ``` --- ``` -------------------------------- ### Full Connection Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/02-Unity/02-FantasyRuntime.md A comprehensive example showing how to use the Runtime.Connect method with all optional callbacks for connection events. ```APIDOC ### Full Connection Example ```csharp using Fantasy; using Fantasy.Async; using UnityEngine; public class NetworkController : MonoBehaviour { [SerializeField] private string serverIP = "127.0.0.1"; [SerializeField] private int serverPort = 20000; private void Start() { ConnectAsync().Coroutine(); } private async FTask ConnectAsync() { try { Log.Info("Starting to connect to the server..."); var session = await Runtime.Connect( remoteIP: serverIP, remotePort: serverPort, protocol: FantasyRuntime.NetworkProtocolType.TCP, isHttps: false, connectTimeout: 5000, enableHeartbeat: true, heartbeatInterval: 2000, heartbeatTimeOut: 30000, heartbeatTimeOutInterval: 5000, maxPingSamples: 4, onConnectComplete: OnConnected, onConnectFail: OnConnectionFailed, onConnectDisconnect: OnDisconnected ); Log.Info($"Connection successful: {session.RemoteEndPoint}"); } catch (System.Exception ex) { Log.Error($"Connection exception: {ex.Message}"); } } private void OnConnected() { Log.Info("Callback: Connection successful"); // Send login message Runtime.Session.Send(new LoginRequest { Username = "Player123", Password = "password" }); } private void OnConnectionFailed() { Log.Error("Callback: Connection failed"); // Display retry UI ShowRetryDialog(); } private void OnDisconnected() { Log.Warning("Callback: Connection disconnected"); // Return to login screen ReturnToLoginScreen(); } private void ShowRetryDialog() { // TODO: Display retry dialog } private void ReturnToLoginScreen() { // TODO: Return to login screen UnityEngine.SceneManagement.SceneManager.LoadScene("LoginScene"); } private void OnDestroy() { Runtime.OnDestroy(); } } ``` --- ``` -------------------------------- ### Initialize and Start Server with NLog Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/06-LogSystem.md Demonstrates how to initialize the Fantasy framework and start the server using an NLog logger instance. Ensure AssemblyHelper is initialized before creating the logger. ```csharp using Fantasy; try { // 1. 初始化程序集 AssemblyHelper.Initialize(); // 2. 创建 NLog 日志实例 var logger = new Fantasy.NLog("Server"); // 3. 启动框架并传入日志实例 await Fantasy.Platform.Net.Entry.Start(logger); } catch (Exception ex) { Console.Error.WriteLine($"服务器启动失败:{ex}"); Environment.Exit(1); } ``` -------------------------------- ### Connect to a Single Game Server Source: https://github.com/qq362946/fantasy/blob/main/Docs/02-Unity/02-FantasyRuntime.md Example of connecting to a single game server and accessing the session globally. This setup is recommended for simple single-server games. ```csharp using Fantasy; using UnityEngine; public class GameClient : MonoBehaviour { public void Login() { Runtime.Session.Send(new LoginRequest()); } public void Attack() { Runtime.Session.Send(new AttackRequest()); } } ``` -------------------------------- ### Install Fantasy.Unity via Git URL Source: https://github.com/qq362946/fantasy/blob/main/Fantasy.Packages/Fantasy.Unity/README.md Alternative installation method for Fantasy.Unity using a Git URL in the Unity Package Manager. ```bash https://github.com/qq362946/Fantasy.git?path=Fantasy.Packages/Fantasy.Unity ``` -------------------------------- ### Example Implement Page Content Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/REFERENCE_GUIDELINES.md Implement pages should provide the shortest steps, minimal examples, key APIs, and essential notes for direct code or configuration. ```markdown ## Implementation - Shortest steps - Minimal example - Key APIs - Minimum but sufficient notes Do not include: - Large background explanations - Troubleshooting content unrelated to implementation ``` -------------------------------- ### Configuration Validation Error Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/01-ServerConfiguration.md This example shows the format of error messages outputted by the Fantasy Framework when configuration validation fails during server startup. ```text [ERROR] Fantasy.config 验证失败: - 场景 ID 1001 和 1002 的 innerPort 冲突:都使用了 11001 - 进程 ID 2 的 machineId=999 引用了不存在的机器 - 场景 ID 50 的 ID 过小,建议使用 1000 以上的ID ``` -------------------------------- ### Install Fantasy CLI Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/07-NetworkProtocol.md Install the Fantasy CLI tool globally using .NET CLI. Verify the installation by checking the version. ```bash dotnet tool install -g Fantasy.Cli fantasy --version ``` -------------------------------- ### Fantasy CLI Installation and Project Creation Source: https://github.com/qq362946/fantasy/blob/main/Fantasy.Packages/Fantasy.Net/README.md Commands to install the Fantasy CLI tool globally and create a new project named 'MyGame'. ```bash dotnet tool install -g Fantasy.Cli fantasy init -n MyGame ``` -------------------------------- ### Example Content Writing Rule: Short, Migratable Examples Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/REFERENCE_GUIDELINES.md Examples should be concise (20-40 lines), demonstrate a complete action, and closely follow Fantasy's naming conventions to avoid overwhelming the document. ```markdown Examples should be: - Within 20-40 lines - Demonstrating a complete action - As close as possible to Fantasy's actual naming style Avoid: - Overly long examples that overshadow the main document - 5 nearly identical examples for the same concept ``` -------------------------------- ### Full Runtime Connection with Callbacks Source: https://github.com/qq362946/fantasy/blob/main/Docs/02-Unity/02-FantasyRuntime.md A comprehensive example of connecting to a server, including handling connection success, failure, and disconnection events via callbacks. This example also shows sending a login request upon successful connection. ```csharp using Fantasy; using Fantasy.Async; using UnityEngine; public class NetworkController : MonoBehaviour { [SerializeField] private string serverIP = "127.0.0.1"; [SerializeField] private int serverPort = 20000; private void Start() { ConnectAsync().Coroutine(); } private async FTask ConnectAsync() { try { Log.Info("开始连接服务器..."); var session = await Runtime.Connect( remoteIP: serverIP, remotePort: serverPort, protocol: FantasyRuntime.NetworkProtocolType.TCP, isHttps: false, connectTimeout: 5000, enableHeartbeat: true, heartbeatInterval: 2000, heartbeatTimeOut: 30000, heartbeatTimeOutInterval: 5000, maxPingSamples: 4, onConnectComplete: OnConnected, onConnectFail: OnConnectionFailed, onConnectDisconnect: OnDisconnected ); Log.Info($"连接成功: {session.RemoteEndPoint}"); } catch (System.Exception ex) { Log.Error($"连接异常: {ex.Message}"); } } private void OnConnected() { Log.Info("回调: 连接成功"); // 发送登录消息 Runtime.Session.Send(new LoginRequest { Username = "Player123", Password = "password" }); } private void OnConnectionFailed() { Log.Error("回调: 连接失败"); // 显示重试 UI ShowRetryDialog(); } private void OnDisconnected() { Log.Warning("回调: 连接断开"); // 返回登录界面 ReturnToLoginScreen(); } private void ShowRetryDialog() { // TODO: 显示重试对话框 } private void ReturnToLoginScreen() { // TODO: 返回登录界面 UnityEngine.SceneManagement.SceneManager.LoadScene("LoginScene"); } private void OnDestroy() { Runtime.OnDestroy(); } } ``` -------------------------------- ### IRequest / IResponse - Login Example Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/protocol/define-outer.md Example of an RPC request and response for client login. ```protobuf /// 客户端请求登录 message C2G_LoginRequest // IRequest,G2C_LoginResponse { string Account = 1; string Password = 2; } message G2C_LoginResponse // IResponse { int64 PlayerId = 1; string Token = 2; } ``` -------------------------------- ### IRequest/IResponse Usage Examples Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/07-NetworkProtocol.md Examples of request and response message definitions for a login scenario. ```protobuf // 客户端请求登录 message C2G_LoginRequest // IRequest,G2C_LoginResponse { string Username = 1; string Password = 2; } // 服务器返回登录结果 message G2C_LoginResponse // IResponse { int64 PlayerId = 1; // 玩家ID string Token = 2; // 会话Token // int32 ErrorCode = 3; // 0=成功, 非0=错误码, 生成器自动添加该字段 } ``` -------------------------------- ### NLog Configuration Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/06-LogSystem.md Example NLog.config file demonstrating console output for development and file output for release. Includes rules for different log levels and target configurations. ```xml ``` -------------------------------- ### Scene Configuration Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/01-ServerConfiguration.md Defines parameters for a game scene, including its ID, runtime mode, type, and network settings. This example shows a Gate scene. ```xml ``` -------------------------------- ### Full Example: OnCreateSceneEvent with Component Lifecycle and SceneType Handling Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/04-OnCreateScene.md A comprehensive example demonstrating OnCreateScene event handling, including component lifecycle management (SubSceneTestComponent) and detailed logic for various SceneTypes (Addressable, Map, Chat, Gate). Includes commented-out stress test code. ```csharp using Fantasy.Assembly; using Fantasy.Async; using Fantasy.Entitas; using Fantasy.Entitas.Interface; using Fantasy.Event; namespace Fantasy; // 示例组件:在 SubScene 下测试组件生命周期 public sealed class SubSceneTestComponent : Entity { public override void Dispose() { Log.Debug("销毁SubScene下的SubSceneTestComponent"); base.Dispose(); } } // 示例组件的 Awake 系统 public sealed class SubSceneTestComponentAwakeSystem : AwakeSystem { protected override void Awake(SubSceneTestComponent self) { Log.Debug("SubSceneTestComponentAwakeSystem"); } } // OnCreateScene 事件处理器 public sealed class OnCreateSceneEvent : AsyncEventSystem { private static long _addressableSceneRunTimeId; /// /// Handles the OnCreateScene event. /// /// The OnCreateScene object. /// A task representing the asynchronous operation. protected override async FTask Handler(OnCreateScene self) { var scene = self.Scene; await FTask.CompletedTask; switch (scene.SceneType) { case 6666: { // 使用自定义 SceneType 值 break; } case SceneType.Addressable: { // 保存 Addressable 场景的 RuntimeId _addressableSceneRunTimeId = scene.RuntimeId; break; } case SceneType.Map: { // Map 场景初始化 Log.Debug($"Map Scene SceneRuntimeId:{scene.RuntimeId}"); break; } case SceneType.Chat: { // Chat 场景初始化 break; } case SceneType.Gate: { // Gate 场景初始化 // 下面是压力测试代码示例(已注释) // var tasks = new List(2000); // var session = scene.GetSession(_addressableSceneRunTimeId); // var sceneNetworkMessagingComponent = scene.NetworkMessagingComponent; // var g2ATestRequest = new G2A_TestRequest(); // // async FTask Call() // { // await sceneNetworkMessagingComponent.CallInnerRouteBySession(session,_addressableSceneRunTimeId,g2ATestRequest); // } // // for (int i = 0; i < 100000000000; i++) // { // tasks.Clear(); // for (int j = 0; j < tasks.Capacity; ++j) // { // tasks.Add(Call()); // } // await FTask.WaitAll(tasks); // } break; } } } } ``` -------------------------------- ### Basic Server Startup Code Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/02-WritingStartupCode.md This is the core code required to start a Fantasy server. It initializes assemblies and then starts the Fantasy framework. Ensure you have the necessary using directives and error handling. ```csharp using Fantasy; try { // 1. 初始化程序集(触发 Source Generator 生成的代码) AssemblyHelper.Initialize(); // 2. 启动 Fantasy 框架 await Fantasy.Platform.Net.Entry.Start(); } catch (Exception ex) { Console.Error.WriteLine($"服务器启动失败:{ex}"); Environment.Exit(1); } ``` -------------------------------- ### MachineConfig Usage Examples Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/05-ConfigUsage.md Provides practical examples of how to use MachineConfigData to retrieve and utilize machine-specific configuration information. Covers getting a specific machine's config, safely checking for existence, and iterating through all machines. ```csharp using Fantasy.Platform.Net; // 示例1:获取指定机器的配置 var machineConfig = MachineConfigData.Instance.Get(machineId: 1); Log.Info($"机器1的外网IP: {machineConfig.OuterIP}"); Log.Info($"机器1的内网绑定IP: {machineConfig.InnerBindIP}"); // 示例2:安全获取机器配置 if (MachineConfigData.Instance.TryGet(machineId: 999, out var config)) { Log.Info($"找到机器999: {config.OuterIP}"); } else { Log.Warning("机器999不存在"); } // 示例3:遍历所有机器 foreach (var machine in MachineConfigData.Instance.List) { Log.Info($"机器 {machine.Id}: 外网IP={machine.OuterIP}, 内网IP={machine.InnerBindIP}"); } // 示例4:根据进程配置查找机器 var processConfig = ProcessConfigData.Instance.Get(processId: 1); var machineOfProcess = MachineConfigData.Instance.Get(processConfig.MachineId); Log.Info($"进程1运行在机器{machineOfProcess.Id}上,IP地址: {machineOfProcess.OuterIP}"); ``` -------------------------------- ### Run Fantasy Server Source: https://github.com/qq362946/fantasy/blob/main/Fantasy.Packages/Fantasy.Cil/README.md Execute the Fantasy server application after building. This command starts the game server. ```bash dotnet run --project Server/Main/Main.csproj ``` -------------------------------- ### Multi-Configuration launchSettings.json Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/03-CommandLineArguments.md Illustrates a more complex `launchSettings.json` with multiple profiles for different server types and custom configurations, including environment variables and specific arguments. ```json { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { "开发模式 - 所有进程": { "commandName": "Project", "commandLineArgs": "--m Develop" }, "发布模式 - Gate服务器": { "commandName": "Project", "commandLineArgs": "--m Release --pid 1" }, "发布模式 - Map服务器": { "commandName": "Project", "commandLineArgs": "--m Release --pid 2" }, "发布模式 - 战斗服务器组": { "commandName": "Project", "commandLineArgs": "--m Release -g 1" }, "自定义配置": { "commandName": "Project", "commandLineArgs": "--m Release --pid 10 -a Game", "environmentVariables": { "LOG_LEVEL": "Debug" } } } } ``` -------------------------------- ### Example Entry Page Structure Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/REFERENCE_GUIDELINES.md An entry page should guide users to the correct sub-document based on their problem type and highlight essential rules. ```markdown # Xxx Entry One sentence explaining what this topic addresses. ## Workflow ```text Problem A -> a.md Problem B -> b.md Problem C -> c.md ``` ## Rules to Remember 1. ... 2. ... 3. ... ## Sub-documents - `a.md` - ... - `b.md` - ... ``` -------------------------------- ### Quick Start: Initialize Logging Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/06-LogSystem.md Demonstrates how to initialize the Fantasy Framework with different logging configurations. This includes using NLog, a custom logger, or the default console logger. ```csharp // 1. 使用 NLog(推荐生产环境) var logger = new Fantasy.NLog("Server"); await Fantasy.Platform.Net.Entry.Start(logger); // 2. 使用自定义日志 var logger = new MyCustomLog(); await Fantasy.Platform.Net.Entry.Start(logger); // 3. 使用默认控制台日志(开发测试) await Fantasy.Platform.Net.Entry.Start(); ``` -------------------------------- ### Build and Run Server in Release Mode Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/03-CommandLineArguments.md Demonstrates the command-line steps to build, publish, and run the server application in a release configuration. ```bash # 1. 构建项目 dotnet build --configuration Release # 2. 进入输出目录 cd bin/Release/net8.0/ # 3. 启动服务器 dotnet YourServer.dll --m Release --pid 1 ``` ```bash # 1. 发布项目 (用于生成自包含部署包) dotnet publish --configuration Release --output ./publish # 2. 进入发布目录 cd ./publish # 3. 启动服务器 dotnet YourServer.dll --m Release --pid 1 ``` -------------------------------- ### ProcessId Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/03-CommandLineArguments.md Specify the Process ID to start using the `--pid` argument in Release mode. This ID must correspond to a Process ID defined in Fantasy.config. ```bash # 启动 ProcessId = 1 的进程 (例如 Gate 服务器) dotnet YourServer.dll --m Release --pid 1 # 启动 ProcessId = 2 的进程 (例如 Game 服务器) dotnet YourServer.dll --m Release --pid 2 ``` -------------------------------- ### Starting Multiple Servers via Command Line Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/03-CommandLineArguments.md Shows how to launch individual server processes for different roles (e.g., Gate, Map) using separate command-line executions. ```bash # 启动 Gate 服务器 (ProcessId = 1) dotnet YourServer.dll --m Release --pid 1 # 启动 Map 服务器 (ProcessId = 2, 在另一个终端或后台运行) dotnet YourServer.dll --m Release --pid 2 ``` -------------------------------- ### Conditional Initialization based on Scene Type Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/ecs/scene.md Provides an example of initializing a Scene differently based on whether it's a Root Scene or a SubScene within the OnCreateScene event handler. This allows for scene-specific setup. ```csharp protected override async FTask Handler(OnCreateScene self) { var scene = self.Scene; if (scene.SceneRuntimeType == SceneRuntimeType.SubScene) { // SubScene 专属初始化 return; } // Root Scene 初始化 switch (scene.SceneType) { case SceneType.Map: scene.AddComponent(); break; } await FTask.CompletedTask; } ``` -------------------------------- ### Validate Configuration on Application Start Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/05-ConfigUsage.md Ensure configuration integrity by implementing validation checks during application startup. This example shows how to use a ConfigValidator to verify configurations and check for port conflicts, exiting the application if validation fails. ```csharp public class OnApplicationStartEvent : EventSystem { protected override void Handler(OnApplicationStart self) { // 验证配置 var validator = new ConfigValidator(); if (!validator.ValidateConfig()) { Log.Error("配置验证失败,服务器即将退出"); Environment.Exit(1); } if (!validator.CheckPortConflicts()) { Log.Error("端口冲突,服务器即将退出"); Environment.Exit(1); } Log.Info("配置验证通过"); } } ``` -------------------------------- ### Fantasy Server Configuration Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/00-GettingStarted/01-QuickStart-Server.md This XML configuration file defines various aspects of the Fantasy server, including network settings, session timeouts, machine and process definitions, world configurations with database connections, and scene setups with network protocols and ports. ```xml ``` -------------------------------- ### Project Structure Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/02-WritingStartupCode.md Illustrates a recommended project structure for server-side Fantasy framework applications, highlighting where to place the `AssemblyHelper.cs` file. ```plaintext YourSolution/ ├── Server/ # 入口项目 │ └── Program.cs # 调用 AssemblyHelper.Initialize() │ ├── Server.Entity/ # Entity 项目(直接引用 Fantasy) │ ├── AssemblyHelper.cs # ✅ 在这里定义 AssemblyHelper │ ├── Fantasy.config │ └── Components.cs │ └── Server.Hotfix/ # Hotfix 项目 └── MessageHandlers.cs ``` -------------------------------- ### 创建新项目 (交互模式) Source: https://github.com/qq362946/fantasy/blob/main/Docs/00-GettingStarted/01-QuickStart-Server.md 使用 `fantasy init` 命令以交互方式创建新的 Fantasy Framework 服务器项目。工具将引导你完成项目配置。 ```bash fantasy init ``` -------------------------------- ### Get Single Component Source: https://github.com/qq362946/fantasy/blob/main/Docs/04-Advanced/CoreSystems/01-ECS.md Shows how to retrieve a single component from an entity, with options to get it if it exists, get or add it, or check for its existence. ```csharp // 获取组件,不存在返回 null var health = player.GetComponent(); // 获取或添加组件(不存在则创建) var inventory = player.GetOrAddComponent(); // 检查组件是否存在 if (player.HasComponent()) { // 组件存在 } ``` -------------------------------- ### Minimal Runtime Connection Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/02-Unity/02-FantasyRuntime.md Demonstrates the simplest way to connect to a server using Runtime.Connect. Ensure to call Runtime.OnDestroy in OnDestroy to clean up resources. ```csharp using Fantasy; using Fantasy.Async; using UnityEngine; public class SimpleConnection : MonoBehaviour { private async void Start() { // 最简单的连接方式 await Runtime.Connect( remoteIP: "127.0.0.1", remotePort: 20000, protocol: FantasyRuntime.NetworkProtocolType.TCP, isHttps: false, connectTimeout: 5000, enableHeartbeat: true, heartbeatInterval: 2000, heartbeatTimeOut: 30000, heartbeatTimeOutInterval: 5000, maxPingSamples: 4 ); // 连接成功后,通过 Runtime 静态类访问 Runtime.Session.Send(new MyMessage()); Log.Info($"Ping: {Runtime.PingMilliseconds} ms"); } private void OnDestroy() { Runtime.OnDestroy(); } } ``` -------------------------------- ### Add All Framework Components Source: https://github.com/qq362946/fantasy/blob/main/Fantasy.Packages/Fantasy.Cil/README.md Add all available framework components and tools to your Fantasy project with a single command. Use this for a comprehensive setup. ```bash fantasy add -t all ``` -------------------------------- ### IMessage - Heartbeat Example Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/protocol/define-outer.md Example of an IMessage for a client sending a heartbeat to the server. ```protobuf /// 客户端发送心跳 message C2G_Heartbeat // IMessage { int64 Timestamp = 1; } ``` -------------------------------- ### IMessage - Server Kick Example Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/protocol/define-outer.md Example of an IMessage for a server kicking a client offline. ```protobuf /// 服务器主动踢下线 message G2C_Kick // IMessage { string Reason = 1; } ``` -------------------------------- ### IMessage Usage Examples Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/07-NetworkProtocol.md Examples of IMessage definitions for client-to-server heartbeat and server-to-client notifications. ```protobuf /// 客户端通知服务器心跳 message C2G_Heartbeat // IMessage { int64 Timestamp = 1; } /// 服务器推送消息给客户端 message G2C_NotifyMessage // IMessage { string Content = 1; int32 MessageType = 2; } ``` -------------------------------- ### Checking Server Logs for Success Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/03-CommandLineArguments.md Provides example log output indicating a successful server startup, including assembly loading, initialization completion, scene creation, and port listening. ```text [INFO] 加载程序集:Entity [INFO] 加载程序集:Hotfix [INFO] Fantasy.Net 初始化完成 [INFO] 场景创建:SceneId=1001, SceneType=Gate [INFO] Gate 场景监听:0.0.0.0:20000 (KCP) [INFO] 服务器启动完成 ``` -------------------------------- ### Enum Usage Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/07-NetworkProtocol.md An example of an enum definition for chat channels, illustrating discrete constants. ```protobuf enum ChatChannel { World = 0, Guild = 1, Private = 2, } ``` -------------------------------- ### Fantasy.config Scene Configuration Example Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/config.md Illustrates the relationship between Fantasy.config scene definitions and the creation of Root Scene instances at server startup. Each `` entry in the config corresponds to a running Scene instance. ```xml ``` -------------------------------- ### Install Fantasy CLI Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/08-NetworkProtocolExporter.md Installs the Fantasy CLI tool globally. This is the recommended method for managing Fantasy tools. ```bash dotnet tool install -g Fantasy.Cli ``` ```bash fantasy --version ``` -------------------------------- ### Manual Assembly Loading and Fantasy Initialization in Unity Source: https://github.com/qq362946/fantasy/blob/main/Docs/02-Unity/01-WritingStartupCode-Unity.md Demonstrates the complete process of manually loading custom assemblies and initializing the Fantasy framework. Ensure custom assemblies are loaded and registered before initializing the Fantasy framework and creating the scene. ```csharp using Fantasy; using Fantasy.Async; using System.IO; using UnityEngine; public class AssemblyLoaderExample : MonoBehaviour { private Scene _scene; private void Start() { StartAsync().Coroutine(); } private void OnDestroy() { _scene?.Dispose(); } private async FTask StartAsync() { // 1️⃣ 加载自定义程序集 (必须第一步) await LoadCustomAssemblies(); // 2️⃣ 初始化 Fantasy 框架 await Fantasy.Platform.Unity.Entry.Initialize(); // 3️⃣ 创建 Scene (最后一步) _scene = await Fantasy.Platform.Unity.Entry.CreateScene(); Log.Debug("程序集加载完成,Fantasy 初始化成功!"); } private async FTask LoadCustomAssemblies() { // 从资源加载 DLL 字节数据 // 可以从 AssetBundle、StreamingAssets、网络下载等方式加载 var dllPath = Path.Combine(Application.streamingAssetsPath, "MyHotfix.dll"); byte[] dllBytes = File.ReadAllBytes(dllPath); // 加载程序集 var assembly = System.Reflection.Assembly.Load(dllBytes); Log.Debug($"已加载程序集: {assembly.FullName}"); // ⚠️ 关键步骤: 手动触发 Fantasy 注册 // 调用 Assembly.EnsureLoaded() 来触发该程序集中的 Fantasy 框架注册 // 如果不调用这个方法,Fantasy 无法识别新程序集中的: // - 实体系统 (AwakeSystem, UpdateSystem 等) // - 消息处理器 (IMessageHandler) // - 事件处理器 (IEvent) // - 网络协议 (OpCode 注册) assembly.EnsureLoaded(); Log.Debug($"已触发 Fantasy 注册: {assembly.GetName().Name}"); await FTask.CompletedTask; } } ``` -------------------------------- ### Unity Client Scene Initialization and Connection Source: https://github.com/qq362946/fantasy/blob/main/Docs/04-Advanced/CoreSystems/03-Scene.md Example of initializing Fantasy, creating a client Scene, and connecting to a server within a Unity MonoBehaviour. Includes callbacks for connection status and adding a heartbeat component. ```csharp using Fantasy; using Fantasy.Async; using Fantasy.Network; using UnityEngine; public class GameManager : MonoBehaviour { private Scene _scene; private Session _session; private void Start() { InitializeAsync().Coroutine(); } private void OnDestroy() { // 销毁 Scene,释放所有资源 _scene?.Dispose(); } private async FTask InitializeAsync() { // 1. 初始化 Fantasy 框架 await Fantasy.Platform.Unity.Entry.Initialize(); // 2. 创建客户端 Scene _scene = await Scene.Create(SceneRuntimeMode.MainThread); // 3. 连接服务器 _session = _scene.Connect( "127.0.0.1:20000", // 服务器地址 NetworkProtocolType.KCP, // 协议类型 OnConnectComplete, // 连接成功回调 OnConnectFail, // 连接失败回调 OnConnectDisconnect, // 断开连接回调 false, // 是否 HTTPS (WebSocket) 5000); // 连接超时 (毫秒) } private void OnConnectComplete() { Log.Debug("连接成功"); // 添加心跳组件保持连接 _session.AddComponent().Start(2000); } private void OnConnectFail() { Log.Debug("连接失败"); } private void OnConnectDisconnect() { Log.Debug("连接断开"); } } ``` -------------------------------- ### Initialize New Fantasy Project in Specific Directory Source: https://github.com/qq362946/fantasy/blob/main/Fantasy.Packages/Fantasy.Cil/README.md Create a new Fantasy project and specify the target directory for its creation. This allows for flexible project placement. ```bash fantasy init -p /path/to/project ``` -------------------------------- ### Verify Fantasy CLI Installation Source: https://github.com/qq362946/fantasy/blob/main/Fantasy.Packages/Fantasy.Cil/README.md Check if the Fantasy CLI is installed correctly by verifying its version. This command confirms the tool is accessible. ```bash fantasy --version ``` -------------------------------- ### Common Usage Pattern: Server Startup Subscription Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/server/sphere-event/implement.md Subscribe to events during server startup, typically in OnCreateScene or initialization paths. ```APIDOC ## Common Usage Pattern: Server Startup Subscription Suitable for establishing subscriptions in `OnCreateScene` or initialization paths: ```csharp public sealed class OnCreateMapSceneHandler : AsyncEventSystem { protected override async FTask Handler(OnCreateScene self) { var gateConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Gate)[0]; await self.Scene.SphereEventComponent.Subscribe(gateConfig.Address); } } ``` ``` -------------------------------- ### Complete Struct Event Example Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/event/struct-event.md A comprehensive example demonstrating the definition of a PlayerDamageEvent, creation of synchronous and asynchronous listeners, and both synchronous and asynchronous publishing. ```csharp // 1. 定义事件 public struct PlayerDamageEvent { public long AttackerId; public long TargetId; public int Damage; // 可以在事件里传递一个已存在的实体 public Player Player; } // 2. 创建同步监听器 public class OnPlayerDamage : EventSystem { protected override void Handler(PlayerDamageEvent self) { var player = self.Player; player.Health -= self.Damage; if (player.Health <= 0) { self.Scene.EventComponent.Publish(new PlayerDeathEvent { PlayerId = self.TargetId, KillerId = self.AttackerId }); } } } // 3. 创建异步监听器 public class LogDamageAsync : AsyncEventSystem { protected override async FTask Handler(PlayerDamageEvent self) { // 异步记录伤害日志到数据库 await SaveDamageLogToDatabase(self.AttackerId, self.TargetId, self.Damage); } private async FTask SaveDamageLogToDatabase(long attackerId, long targetId, int damage) { await FTask.CompletedTask; } } // 4. 同步发布事件 scene.EventComponent.Publish(new PlayerDamageEvent { AttackerId = attacker.Id, TargetId = target.Id, Damage = 100, Player = player }); // 5. 异步发布事件(等待所有异步监听器完成) await scene.EventComponent.PublishAsync(new PlayerDamageEvent { AttackerId = attacker.Id, TargetId = target.Id, Damage = 100, Player = player }); ``` -------------------------------- ### Fantasy.config Workflow Overview Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/config.md Outlines the typical workflow for managing project configurations, including initial setup, machine/process modifications, world/database changes, and scene/network adjustments. ```text 新建项目或从零写配置 -> config-scenarios.md 新增 / 修改 machine 或 process -> templates/Fantasy.config 新增 / 修改 world 或 database -> config-scenarios.md 新增 / 删除 scene、改端口、改网络协议 -> config-scenarios.md 需要查节点属性和默认值 -> templates/Fantasy.config ``` -------------------------------- ### Start Server with Logger Instance Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/02-WritingStartupCode.md Ensure a logger instance, such as `ConsoleLog`, is passed to `Fantasy.Platform.Net.Entry.Start` to avoid silent startup failures. This is important for diagnosing issues when the server produces no output. ```csharp // 确保传递了日志实例 var logger = new ConsoleLog(); await Fantasy.Platform.Net.Entry.Start(logger); ``` -------------------------------- ### Buff System Example Source: https://github.com/qq362946/fantasy/blob/main/Docs/04-Advanced/CoreSystems/02-ISupportedMultiEntity.md A practical example of using ISupportedMultiEntity for a Buff System, including a BuffComponent definition and a system to handle buff expiration. ```csharp public class BuffComponent : Entity, ISupportedMultiEntity { public int BuffId { get; set; } public int BuffType { get; set; } public float Value { get; set; } public long ExpireTime { get; set; } } public class BuffSystem : UpdateSystem { protected override void Update(BuffComponent self) { // 检查 Buff 是否过期 if (TimeHelper.Now >= self.ExpireTime) { Log.Info($"Buff {self.BuffId} expired!"); self.Dispose(); } } } // 使用示例 public void AddBuff(Player player, int buffId, int buffType, float value, long duration) { var buff = player.AddComponent(); buff.BuffId = buffId; buff.BuffType = buffType; buff.Value = value; buff.ExpireTime = TimeHelper.Now + duration; } ``` -------------------------------- ### Create a New Fantasy Project Source: https://github.com/qq362946/fantasy/blob/main/README.md Initialize a new Fantasy project using the CLI. Supports interactive creation or quick creation with a specified project name. ```bash fantasy init # 交互式创建项目 fantasy init -n MyGameServer # 使用项目名快速创建 ``` -------------------------------- ### Map Server Example: Handling OnCreateTerminus Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/server/roaming/on-create-terminus.md Example implementation of an OnCreateTerminus event handler for a Map server, demonstrating creating an entity first and then linking it. ```APIDOC ## Map Server Example ### Handler Implementation This example shows how a Map server can implement the `OnCreateTerminus` event handler by first creating a `MapPlayer` entity and then associating it with the terminus. ```csharp // Map 服务器 Hotfix assembly public sealed class MapOnCreateTerminusHandler : AsyncEventSystem { protected override async FTask Handler(OnCreateTerminus self) { if (self.Terminus.RoamingType != RoamingType.MapRoamingType) { return; } var mapPlayer = Entity.Create(self.Scene); mapPlayer.PlayerId = self.Terminus.RuntimeId; await self.Terminus.LinkTerminusEntity(mapPlayer, autoDispose: true); self.Args?.Dispose(); await FTask.CompletedTask; } } ``` ``` -------------------------------- ### Example: Gate to Map Player Data Sync Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/protocol/define-inner.md An example of an IAddressMessage used by the Gate server to notify the Map server about player data synchronization. ```protobuf /// Gate 通知 Map 同步玩家数据 message G2M_SyncPlayerData // IAddressMessage { int64 PlayerId = 1; bytes Data = 2; } ``` -------------------------------- ### IRequest/IResponse Example: Query Player Info Source: https://github.com/qq362946/fantasy/blob/main/Docs/01-Server/07-NetworkProtocol.md Shows an example of using IRequest and IResponse for querying data, such as player inventory, where a structured response is expected. ```protobuf // 查询背包信息 message C2G_GetInventoryRequest // IRequest,G2C_GetInventoryResponse { int64 PlayerId = 1; } message G2C_GetInventoryResponse // IResponse { repeated Item Items = 1; // 使用 repeated 定义列表 int32 MaxSlots = 2; } message Item { int32 ItemId = 1; int32 Count = 2; int64 ExpireTime = 3; } ``` -------------------------------- ### Initialize and Start with Fantasy.NLog Source: https://github.com/qq362946/fantasy/blob/main/Skills/fantasy-net/references/logging.md Register a Fantasy.NLog instance with Entry.Start() in your Program.cs. The parameter is the NLog logger name, matching the 'name' in NLog.config. ```csharp // Program.cs(入口项目) AssemblyHelper.Initialize(); var logger = new Fantasy.NLog("Server"); // 参数为 NLog logger 名称,与 NLog.config 中的 name 对应 await Fantasy.Platform.Net.Entry.Start(logger); ```