### Start Distributed Server Source: https://github.com/egametang/et/blob/master/README.md This command compiles and starts the ET framework server on a Linux environment. Ensure .NET Core is installed on the target machine. ```bash ./Run.sh Config/StartConfig/192.168.12.188.txt ``` -------------------------------- ### Subscribe to Player Start Event Source: https://github.com/egametang/et/blob/master/Book/3.4事件机制EventSystem.md Example of subscribing to the Start event of a Player component. This event is thrown before the component's UpdateSystem is called. ```csharp // 订阅Player的Start事件 public class PlayerStartSystem: StartSystem { public override void Start(Player self) { } } ``` -------------------------------- ### Adding Owner Key to BTEnv Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md Examples of how to add owner keys to the BTEnv for both bare variables and explicitly keyed variables. ```csharp env.AddEntity(ConditionExprEnvKeys.Unit, unit); env.AddEntity("Unit1", unit1); env.AddEntity("Unit2", unit2); ``` -------------------------------- ### Equip and Unequip Item Example Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.equipment/Scripts/Hotfix/Server/README.md Demonstrates how to equip an item to a specified slot and then unequip it. Ensure the ErrorCode enum and EquipmentSlotType are accessible. ```csharp int result = EquipmentHelper.EquipItem(unit, itemId, EquipmentSlotType.MainHand); if (result == ErrorCode.ERR_Success) { Log.Debug("equipment equipped successfully"); } result = EquipmentHelper.UnequipItem(unit, EquipmentSlotType.MainHand); if (result == ErrorCode.ERR_Success) { Log.Debug("equipment unequipped successfully"); } ``` -------------------------------- ### Expression Syntax: Recommended Parentheses Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md Example showing recommended explicit parentheses for complex conditions to improve readability. ```text (HP >= 10 : 10001 || MP >= 100 : 10002) && Speed > 0 : 10003 ``` -------------------------------- ### Multi-Owner Key Compilation and Execution Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md C# example demonstrating compilation of an expression with multiple owner keys and its subsequent execution with a properly configured BTEnv. ```csharp ConditionRoot root = ConditionExprCompiler.Compile( "Unit1.HP > 0 || Unit2.MP < 100", 9999); BTEnv env = BTEnv.Create(scene, unit1.Id); try { env.AddEntity("Unit1", unit1); env.AddEntity("Unit2", unit2); int errorCode = BTHelper.RunTree(root, env); } finally { env.Dispose(); } ``` -------------------------------- ### Dedicated Variable Node Registration Example Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md C# example of registering a custom condition node using ConditionVariableAttribute, including fields for parameters, operator, value, and error code. ```csharp [ConditionVariable("VipLevel")] public class BTVipLevelCompare : BTCondition { public string[] Params; public ConditionCompareOp Op; public long Value; public int ErrorCode; } ``` -------------------------------- ### Expression Syntax: Multi-Owner Key Usage Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md Example demonstrating how to specify which owner's data to read when multiple owners are present in the BTEnv. ```text Unit1.HP > 0 || Unit2.MP < 100 ``` -------------------------------- ### Install Halodi Unity Package Registry Manager Source: https://github.com/egametang/et/blob/master/Packages/com.halodi.halodi-unity-package-registry-manager/README.md Add the package to your Unity project by providing the Git repository URL in the Package Manager. ```bash https://github.com/Halodi/halodi-unity-package-registry-manager.git ``` -------------------------------- ### Task Operations in ET.Quest Client Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.quest/README.md Examples demonstrating how to accept and submit quests, and retrieve quest progress using the ClientQuestHelper and ClientQuestComponent. ```csharp // 接取任务 bool success = await ClientQuestHelper.AcceptQuest(scene, questId, npcId); // 提交任务 bool success = await ClientQuestHelper.SubmitQuest(scene, questId, npcId); // 获取任务进度 ClientQuestData questData = questComponent.GetQuestData(questId); ``` -------------------------------- ### Luban Field Example Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md Example of how to define and fill the ET.ConditionExpr field in Luban for defining conditions. ```text Expr = (HP >= 10 : 10001 || MP >= 100 : 10002) && Speed > 0 : 10003 ErrorCode = 9999 Desc = 进入副本条件 ``` -------------------------------- ### Variable Registration Example Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md Illustrates how ConditionVariableRegistry scans NumericType for public static int fields to register as variables. ```text HP -> BTNumericCompare, NumericType.HP MP -> BTNumericCompare, NumericType.MP Speed -> BTNumericCompare, NumericType.Speed ``` -------------------------------- ### Get help for a specific CLI tool Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.harness/skills/et-excel/references/et-excel-cli.md Use the 'help ' command to get detailed information about a specific tool, such as 'excel_range'. ```powershell dotnet ./Bin/ET.ExcelMcp.dll cli help excel_range ``` -------------------------------- ### InnerConfig Class with EndInit Implementation Source: https://github.com/egametang/et/blob/master/Book/3.2强大的MongoBson库.md An example of a configuration class that inherits from AConfigComponent and implements ISupportInitialize. The EndInit method converts a string address into an IPEndPoint after deserialization. ```csharp [BsonIgnoreExtraElements] public class InnerConfig: AConfigComponent { [BsonIgnore] public IPEndPoint IPEndPoint { get; private set; } public string Address { get; set; } public override void EndInit() { this.IPEndPoint = NetworkHelper.ToIPEndPoint(this.Address); } } ``` -------------------------------- ### Return Rule Example Interpretation Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md Interpreting the return values for a complex condition expression based on the behavior tree return rules. ```text (HP >= 10 : 10001 || MP >= 100 : 10002) && Speed > 0 : 10003 返回含义: HP 满足且 Speed 满足 -> 0 HP 不满足但 MP 满足且 Speed 满足 -> 0 HP 和 MP 都不满足 -> 10001 HP 或 MP 满足但 Speed 不满足 -> 10003 ``` -------------------------------- ### Bag Sorting Effect Example Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.item/README.md Shows the state of the bag before and after the sorting function is applied, illustrating item reordering and stacking. ```csharp // 整理前: // 槽位0:药水(10002)x5 // 槽位1:空 // 槽位2:装备(10001)x1 // 槽位3:药水(10002)x3 // 槽位4:材料(10003)x10 // 槽位5:空 // 槽位6:药水(10002)x7(最大堆叠10) // 整理后(按ConfigId排序且堆叠): // 槽位0:装备(10001)x1 // 槽位1:药水(10002)x10 // 槽位2:药水(10002)x5 // 槽位3:材料(10003)x10 // 槽位4:空 // 槽位5:空 // 槽位6:空 ``` -------------------------------- ### Coroutine Implementation with Async/Await Source: https://github.com/egametang/et/blob/master/Book/2.2更好的协程.md This example shows how to implement coroutines using C#'s async/await pattern with `TaskCompletionSource` to manage asynchronous operations and ensure callbacks are handled correctly, often on the main thread. ```csharp // example2_2 class Program { private static int loopCount = 0; static void Main(string[] args) { OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance; Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}"); Crontine(); while (true) { OneThreadSynchronizationContext.Instance.Update(); Thread.Sleep(1); ++loopCount; if (loopCount % 10000 == 0) { Console.WriteLine($"loop count: {loopCount}"); } } } private static async void Crontine() { await WaitTimeAsync(5000); Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); await WaitTimeAsync(4000); Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); await WaitTimeAsync(3000); Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); } private static Task WaitTimeAsync(int waitTime) { TaskCompletionSource tcs = new TaskCompletionSource(); Thread thread = new Thread(()=>WaitTime(waitTime, tcs)); thread.Start(); return tcs.Task; } /// /// 在另外的线程等待 /// private static void WaitTime(int waitTime, TaskCompletionSource tcs) { Thread.Sleep(waitTime); // 将tcs扔回主线程执行 OneThreadSynchronizationContext.Instance.Post(o=>tcs.SetResult(true), null); } } ``` -------------------------------- ### Passing Gate Session InstanceId to Map Process Source: https://github.com/egametang/et/blob/master/Book/5.4Actor模型.md This example illustrates how a Gate Session's InstanceId is passed to the map process during player login, enabling the map process to communicate with the gate session. ```csharp M2G_CreateUnit createUnit = (M2G_CreateUnit)await mapSession.Call(new G2M_CreateUnit() { PlayerId = player.Id, GateSessionId = session.InstanceId }); ``` -------------------------------- ### Server Notification for Equipping Item Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.equipment/README.md Server-side example of sending an M2C_UpdateEquipment message to notify clients about an item being equipped to a specific slot. ```csharp M2C_UpdateEquipment message = M2C_UpdateEquipment.Create(); message.SlotType = (int)EquipmentSlotType.MainHand; // 装备到主手 message.ItemId = item.Id; message.ConfigId = item.ConfigId; ``` -------------------------------- ### Single-Threaded Async/Await Timer Source: https://github.com/egametang/et/blob/master/Book/2.3单线程异步.md This example shows an asynchronous timer implemented using await and TaskCompletionSource, all within the main thread. It's an alternative to the callback-based approach for managing asynchronous operations without creating new threads. ```csharp // example2_3_2 class Program { private static int loopCount = 0; private static long time; private static TaskCompletionSource tcs; static void Main(string[] args) { Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}"); Crontine(); while (true) { Thread.Sleep(1); CheckTimerOut(); ++loopCount; if (loopCount % 10000 == 0) { Console.WriteLine($"loop count: {loopCount}"); } } } private static async void Crontine() { await WaitTimeAsync(5000); Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); await WaitTimeAsync(4000); Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); await WaitTimeAsync(3000); Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); } private static void CheckTimerOut() { if (time == 0) { return; } long nowTicks = DateTime.Now.Ticks / 10000; if (time > nowTicks) { return; } time = 0; tcs.SetResult(true); } private static Task WaitTimeAsync(int waitTime) { TaskCompletionSource t = new TaskCompletionSource(); time = DateTime.Now.Ticks / 10000 + waitTime; tcs = t; return t.Task; } } ``` -------------------------------- ### Automated Registration of Types for Serialization Source: https://github.com/egametang/et/blob/master/Book/3.2强大的MongoBson库.md Provides a C# code example demonstrating how to automatically register all subclass types within an assembly for MongoBson serialization, decoupling type registration from class definitions. ```csharp Type[] types = typeof(Game).Assembly.GetTypes(); foreach (Type type in types) { if (!type.IsSubclassOf(typeof(Component))) { continue; } BsonClassMap.LookupClassMap(type); } BsonSerializer.RegisterSerializer(new EnumSerializer(BsonType.String)); ``` -------------------------------- ### Protobuf Message Definitions Source: https://github.com/egametang/et/blob/master/Book/3.2强大的MongoBson库.md Example Protobuf message definitions for UnitInfo and G2C_EnterMap. G2C_EnterMap contains a repeated UnitInfo field, which is an array in Protobuf. ```protobuf message UnitInfo { int64 UnitId = 1; float X = 2; float Y = 3; float Z = 4; } // protobuf message G2C_EnterMap // IResponse { int32 RpcId = 90; int32 Error = 91; string Message = 92; // 自己的unit id int64 UnitId = 1; // 所有的unit repeated UnitInfo Units = 2; } ``` -------------------------------- ### Throw and Subscribe to Custom HP Change Event Source: https://github.com/egametang/et/blob/master/Book/3.4事件机制EventSystem.md Example of throwing a custom 'HpChange' event with two integer parameters and subscribing to it from different modules (UI and model head). ```csharp int oldhp = 10; int newhp = 5; // 抛出hp改变事件 Game.EventSystem.Run("HpChange", oldhp, newhp); // UI订阅hp改变事件 [Event("HpChange")] public class HpChange_ShowUI: AEvent { public override void Run(int a, int b) { throw new NotImplementedException(); } } // 模型头顶血条模块也订阅hp改变事件 [Event("HpChange")] public class HpChange_ModelHeadChange: AEvent { public override void Run(int a, int b) { throw new NotImplementedException(); } } ``` -------------------------------- ### Single-Threaded Timer with Callbacks Source: https://github.com/egametang/et/blob/master/Book/2.3单线程异步.md This example demonstrates a single-threaded asynchronous timer using callbacks. The main thread continuously checks for timer expirations. Use this for simple asynchronous tasks where thread overhead is a concern. ```csharp // example2_3 class Program { private static int loopCount = 0; private static long time; private static Action action; static void Main(string[] args) { Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}"); Crontine(); while (true) { Thread.Sleep(1); CheckTimerOut(); ++loopCount; if (loopCount % 10000 == 0) { Console.WriteLine($"loop count: {loopCount}"); } } } private static void Crontine() { WaitTimeAsync(5000, WaitTimeAsyncCallback1); } private static void WaitTimeAsyncCallback1() { Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); WaitTimeAsync(4000, WaitTimeAsyncCallback2); } private static void WaitTimeAsyncCallback2() { Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); WaitTimeAsync(3000, WaitTimeAsyncCallback3); } private static void WaitTimeAsyncCallback3() { Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); } private static void CheckTimerOut() { if (time == 0) { return; } long nowTicks = DateTime.Now.Ticks / 10000; if (time > nowTicks) { return; } time = 0; action.Invoke(); } private static void WaitTimeAsync(int waitTime, Action a) { time = DateTime.Now.Ticks / 10000 + waitTime; action = a; } } ``` -------------------------------- ### Subscribe to Player Load Event Source: https://github.com/egametang/et/blob/master/Book/3.4事件机制EventSystem.md Example of subscribing to the Load event of a Player component. This event is thrown when the EventSystem loads DLLs, useful for server hot updates. ```csharp // 订阅Player的Load事件 public class PlayerLoadSystem: LoadSystem { public override void Load(Player self) { } } ``` -------------------------------- ### Example Patrol Node Implementation Source: https://github.com/egametang/et/blob/master/Book/6.1AI框架.md Implements a patrol AI node. It checks if the unit is within the patrol area and not near enemies. The Run method includes asynchronous movement and timed waits, both supporting cancellation. ```csharp public class XunLuoNode: AINode { public virtual bool Check(Unit unit) { if (不在巡逻范围) { return false; } if (周围有敌人) { return false; } return true; } public virtual ETVoid Run(Unit unit, ETCancelToken cancelToken) { while (true) { Vector3 nextPoint = FindNextPoint(); bool ret = await MoveToAsync(nextPoint, cancelToken); // 移动到目标点, 返回false表示协程取消 if (!ret) { return; } // 停留两秒, 注意这里要能取消,任何协程都要能取消 bool ret = await TimeComponent.Instance.Wait(2000, cancelToken); if (!ret) { return; } } } } ``` -------------------------------- ### Get data from an Excel range (incorrect PowerShell quoting) Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.harness/skills/et-excel/references/et-excel-cli.md This example shows an incorrect way to pass JSON arguments in PowerShell, where double quotes are used incorrectly, leading to parsing issues. ```powershell dotnet ./Bin/ET.ExcelMcp.dll cli excel_range "{\"operation\":\"get\"}" ``` -------------------------------- ### Get data from an Excel range (correct PowerShell quoting) Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.harness/skills/et-excel/references/et-excel-cli.md Retrieve data from a specified range in an Excel file. This example demonstrates the correct way to pass JSON arguments within single quotes in PowerShell. ```powershell dotnet ./Bin/ET.ExcelMcp.dll cli excel_range '{"operation":"get","path":"C:\\Temp\\demo.xlsx","range":"A1:B2"}' ``` -------------------------------- ### Variable Usage in Expressions Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md Examples of how variables from NumericType are used in condition expressions, including multi-owner scenarios. ```text HP >= 10 MP >= 100 Speed > 0 Unit1.HP > 0 Unit2.MP < 100 ``` -------------------------------- ### Sort Bag Server and Client Calls Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.item/README.md Demonstrates how to initiate the bag sorting process on the server and request it from the client. ```csharp // 服务端调用 ItemComponent itemComponent = unit.GetComponent(); int errorCode = ItemHelper.SortBag(itemComponent); if (errorCode == ErrorCode.ERR_Success) { // 整理成功 } // 客户端请求 C2M_SortBag request = C2M_SortBag.Create(); var response = await fiber.Root.GetComponent().Call(request); ``` -------------------------------- ### Expression Syntax: Error Code Binding Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md Example of binding specific error codes to leaf conditions. ```text HP >= 10 : 10001 ``` -------------------------------- ### Compile Condition Expression from String Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md C# example of compiling a condition expression directly from a string using ConditionExprCompiler. ```csharp ConditionRoot root = ConditionExprCompiler.Compile( "(HP >= 10 : 10001 || MP >= 100 : 10002) && Speed > 0 : 10003", 9999); ``` -------------------------------- ### Item Movement Scenarios Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.item/README.md Illustrates different scenarios for moving items between slots, including stacking, partial stacking, swapping, and moving to empty slots. ```csharp // 情况1:堆叠相同物品 // 槽位0:药水x5(最大堆叠10) // 槽位1:药水x3(最大堆叠10) // 执行 MoveItem(0, 1) 后: // 槽位0:空 // 槽位1:药水x8 // 情况2:部分堆叠 // 槽位0:药水x7(最大堆叠10) // 槽位1:药水x8(最大堆叠10) // 执行 MoveItem(0, 1) 后: // 槽位0:药水x5 // 槽位1:药水x10(已满) // 情况3:交换不同物品 // 槽位0:药水x5 // 槽位1:装备x1 // 执行 MoveItem(0, 1) 后: // 槽位0:装备x1 // 槽位1:药水x5 // 情况4:移动到空槽位 // 槽位0:药水x5 // 槽位1:空 // 执行 MoveItem(0, 1) 后: // 槽位0:空 // 槽位1:药水x5 ``` -------------------------------- ### Adding Components to Player Source: https://github.com/egametang/et/blob/master/Book/4.1组件式设计.md Demonstrates how to attach various functional components to a Player entity. ```C# player.AddComponent(); player.AddComponent(); player.AddComponent(); player.AddComponent(); ``` -------------------------------- ### List available CLI commands Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.harness/skills/et-excel/references/et-excel-cli.md Use the 'list' command to see all available CLI tools. This is useful when the exact tool name is uncertain. ```powershell dotnet ./Bin/ET.ExcelMcp.dll cli list ``` -------------------------------- ### NPM.Publish Source: https://github.com/egametang/et/blob/master/Packages/com.halodi.halodi-unity-package-registry-manager/README.md Publishes a package from a specified folder to a given registry. It uses credentials stored in '~/.upmconfig.toml' and creates the package tarball using SharpZipLib. ```APIDOC ## NPM.Publish ### Description Publishes a local package to a remote NPM registry. This function is analogous to executing 'npm publish --registry [registry]' from within the specified package folder. It utilizes credentials configured in '~/.upmconfig.toml' and employs SharpZipLib for creating the package tarball to ensure compatibility. ### Method Signature `NPM.Publish(string packageFolder, string registry)` ### Parameters * **packageFolder** (string) - The path to the directory containing the package to be published. * **registry** (string) - The URL of the NPM registry to which the package will be published. ### Throws * **System.IO.IOException** - If an error occurs during the publish process. ``` -------------------------------- ### Subscribe to Player Deserialize Event Source: https://github.com/egametang/et/blob/master/Book/3.4事件机制EventSystem.md Example of subscribing to the Deserialize event of a Player component. This event is thrown after the component is deserialized. ```csharp // 订阅Player的Deserialize事件 public class PlayerDeserializeSystem: DeserializeSystem { public override void Deserialize(Player self) { } } // 这里player2会触发Deserialize事件 Player player2 = MongoHelper.FromBson(player.ToBson()); ``` -------------------------------- ### Subscribe to Player Update Event Source: https://github.com/egametang/et/blob/master/Book/3.4事件机制EventSystem.md Example of subscribing to the Update event of a Player component. This event is thrown every frame. ```csharp // 订阅Player的Update事件 public class PlayerUpdateSystem: UpdateSystem { public override void Update(Player self) { } } ``` -------------------------------- ### Compile Project Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.test/skills/et-test-run/SKILL.md Compiles the entire ET solution. This is often a prerequisite for running tests. ```powershell dotnet build ET.sln ``` -------------------------------- ### Publish NPM Package Source: https://github.com/egametang/et/blob/master/Packages/com.halodi.halodi-unity-package-registry-manager/README.md Publishes an NPM package from a specified folder to a given registry. It uses credentials from ~/.upmconfig.toml and creates the tarball using SharpZipLib. ```csharp public static void Publish(string packageFolder, string registry) ``` -------------------------------- ### AddItem Method Implementation Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.item/README.md Details the server-side logic for adding items, including parameter validation, configuration lookup, stacking, and client notification. ```csharp public static bool AddItem(ItemComponent self, int configId, int count, ItemChangeReason reason) { // 1. 参数验证 if (count <= 0) return false; // 2. 获取物品配置 ItemConfig itemConfig = ItemConfigCategory.Instance.Get(configId); if (itemConfig == null) return false; // 3. 堆叠逻辑:先尝试叠加到已有物品 if (itemConfig.MaxStack > 1) { // 遍历所有槽位,找到相同ConfigId且未满的物品 // 计算可以叠加的数量,更新物品Count } // 4. 创建新物品:剩余数量创建新物品堆 while (remainCount > 0) { // 查找空槽位 // 创建新Item实体 // 设置ConfigId和Count } // 5. 自动通知客户端:遍历所有更新的物品,发送M2C_UpdateItem foreach (long itemId in updatedItemIds) { NotifyItemUpdate(self, item); } } ``` -------------------------------- ### Configuring Network Components for Login Server Source: https://github.com/egametang/et/blob/master/Book/4.1组件式设计.md Attaches inner and outer network components to a scene for a login server, specifying host and port configurations. ```C# // 内网网络组件NetInnerComponent,处理对内网连接 Game.Scene.AddComponent(innerConfig.Host, innerConfig.Port); // 外网网络组件NetOuterComponent,处理与客户端连接 Game.Scene.AddComponent(outerConfig.Host, outerConfig.Port); ``` -------------------------------- ### Subscribe to Player Destroy Event and Trigger Source: https://github.com/egametang/et/blob/master/Book/3.4事件机制EventSystem.md Example of subscribing to the Destroy event of a Player component and triggering it. This event is thrown when the component is disposed. ```csharp // 订阅Player的Destroy事件 public class PlayerDestroySystem: DestroySystem { public override void Destroy(Player self) { } } Player player = ComponentFactory.Create(); // 这里会触发Destroy事件 player.Dispose(); ``` -------------------------------- ### Full-Stack Development with ET Harness Source: https://github.com/egametang/et/blob/master/README.md ET's full-stack capabilities enable convenient development of dual-end test frameworks, directly integrating front-end and back-end logic. This significantly reduces the workload for logic development, leaving only UI development as a separate task. ```markdown 2. game develop ai harness支持,ET10遥遥领先! 绝大部分框架要么客户端要么服务端,要想实现完善的vibe coding test框架是非常不舒服的,只有ET10这种前后端一起,才能极其方便开发双端test,直接打通前后端逻辑,联调都不用,test直接完成双端逻辑开发,完成联调工作。逻辑开发的工作量只剩下UI了。 3. 使用请参考 [Book/1.1Running Guide.md](Book/1.1Running%20Guide.md) ``` -------------------------------- ### Subscribe to Player Awake Event Source: https://github.com/egametang/et/blob/master/Book/3.4事件机制EventSystem.md Example of subscribing to the Awake event of a Player component. This event is thrown once after the component factory creates the component. ```csharp Player player = ComponentFactory.Create(); // 订阅Player的Awake事件 public class PlayerAwakeSystem: AwakeSystem { public override void Awake(Player self) { } } ``` -------------------------------- ### Creating a Hierarchical Entity Structure Source: https://github.com/egametang/et/blob/master/Book/3.3一切皆实体.md Demonstrates how to add nested components to an entity, creating a tree-like structure. Use `AddComponent` to attach components. ```csharp Head head = human.AddComponent(); head.AddComponent(); head.AddComponent(); head.AddComponent(); head.AddComponent(); human.AddComponent(); human.AddComponent(); human.AddComponent(); ``` -------------------------------- ### Server Notification for Unequipping Item Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.equipment/README.md Server-side example of sending an M2C_UpdateEquipment message to notify clients about an item being unequipped. SlotType is set to -1 to indicate unequipping. ```csharp M2C_UpdateEquipment message = M2C_UpdateEquipment.Create(); message.SlotType = -1; // -1表示卸下装备 message.ItemId = item.Id; message.ConfigId = item.ConfigId; ``` -------------------------------- ### Implementing Component Event Handlers Source: https://github.com/egametang/et/blob/master/Book/4.1组件式设计.md Provides an example of an ObjectEvent class that implements IAwake and IUpdate interfaces for a NetInnerComponent, enabling Awake and Update method calls. ```C# [ObjectEvent] public class NetInnerComponentEvent : ObjectEvent, IAwake, IUpdate { public void Awake() { this.Get().Awake(); } public void Update() { this.Get().Update(); } } ``` -------------------------------- ### Get NPM Login Token Source: https://github.com/egametang/et/blob/master/Packages/com.halodi.halodi-unity-package-registry-manager/README.md Retrieves a login token for a given NPM registry URL, username, and password. The NPMResponse struct indicates success or failure. ```csharp public struct NPMResponse { public bool ok; public string token; public string error; } public static NPMResponse GetLoginToken(string url, string user, string password) ``` -------------------------------- ### Equip Item using EquipmentHelper Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.equipment/README.md Use the EquipmentHelper to equip an item to a specific slot on a unit. ```csharp EquipmentHelper.EquipItem(unit, itemId, EquipmentSlotType.MainHand); ``` -------------------------------- ### SortBag Method Implementation Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.item/README.md Sorts the player's inventory by rearranging items. It groups items by ConfigId, stacks them, and places them contiguously starting from slot 0. ```csharp public static int SortBag(ItemComponent self) { // 第一步:收集所有物品信息,按ConfigId分组统计数量 Dictionary configIdToCount = new(); foreach (Item item in self.SlotItems) { configIdToCount[item.ConfigId] += item.Count; } // 第二步:通知客户端清空所有旧槽位(Count=0) foreach (Item item in self.SlotItems) { M2C_UpdateItem clearMessage = M2C_UpdateItem.Create(); clearMessage.Count = 0; // 清空标记 MapMessageHelper.NoticeClient(unit, clearMessage, NoticeType.Self); } // 第三步:销毁所有旧物品 foreach (Item item in self.SlotItems) { item?.Dispose(); } // 第四步:按ConfigId排序,重新创建物品堆 List sortedConfigIds = new(configIdToCount.Keys); sortedConfigIds.Sort(); // 升序排列 int currentSlot = 0; foreach (int configId in sortedConfigIds) { int maxStack = itemConfig.MaxStack; int remainCount = configIdToCount[configId]; // 创建物品堆,每堆最多maxStack个 while (remainCount > 0) { int stackCount = Math.Min(remainCount, maxStack); Item newItem = self.AddChild(); newItem.ConfigId = configId; newItem.Count = stackCount; self.SetSlotItem(currentSlot, newItem); remainCount -= stackCount; currentSlot++; } } // 第五步:通知客户端新物品 foreach (Item item in self.SlotItems) { NotifyItemUpdate(self, item); } } ``` -------------------------------- ### Serialize Player Object to JSON and BSON Source: https://github.com/egametang/et/blob/master/Book/3.2强大的MongoBson库.md Demonstrates serializing a Player object to both JSON and BSON formats. Note that MongoBson's default JSON output differs from standard JSON. ```csharp public sealed class Player { public long Id; public string Account { get; private set; } public long UnitId { get; set; } } Player player1 = new Player() { Id = 1 }; string json = player1.ToJson(); Console.WriteLine($"player1 to json: {json}"); Console.WriteLine($"player to bson: {player.ToBson().ToHex()}"); ``` -------------------------------- ### Runtime Invocation with Luban Object Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md C# code showing how to run a compiled condition expression from a Luban object using BTEnv and BTHelper. ```csharp ConditionExpr condition = mapConfig.EnterCondition; BTEnv env = BTEnv.Create(scene, unit.Id); try { env.AddEntity(ConditionExprEnvKeys.Unit, unit); int errorCode = BTHelper.RunTree(condition.Root, env); if (errorCode != ErrorCode.ERR_Success) { return errorCode; } } finally { env.Dispose(); } ``` -------------------------------- ### Declare a Message Handler for Gate Server Source: https://github.com/egametang/et/blob/master/Book/3.4事件机制EventSystem.md Example of declaring a message handler for a C2G_LoginGate RPC message, specifically for the Gate server type. This demonstrates how message events are handled. ```csharp [MessageHandler(AppType.Gate)] public class C2G_LoginGateHandler : AMRpcHandler { protected override void Run(Session session, C2G_LoginGate message, Action reply) { G2C_LoginGate response = new G2C_LoginGate(); reply(response); } } ``` -------------------------------- ### Runtime Data Reading for BTNumericCompare Source: https://github.com/egametang/et/blob/master/Packages/cn.etetet.conditionexpr/README.md C# code demonstrating how BTNumericCompare reads numeric values from a Unit's NumericComponent, requiring the owner key to be present in BTEnv. ```csharp Unit unit = env.GetEntity(node.OwnerKey); NumericComponent numericComponent = unit.GetComponent(); long value = numericComponent.GetAsLong(node.NumericType); ```