From db172b0b0f209d1245538a1cd457478944b0bdd2 Mon Sep 17 00:00:00 2001 From: Somebody Date: Thu, 31 Jul 2025 17:48:01 +0800 Subject: [PATCH] feat: cache the handlers that are loaded from reflection --- Common/Configuration/ConfigContainer.cs | 16 +- DanhengKcpSharp/DanhengConnection.cs | 37 ++- DanhengKcpSharp/DanhengListener.cs | 7 +- DanhengKcpSharp/PacketLogHelper.cs | 63 +++++ .../Game/ChessRogue/ChessRogueInstance.cs | 13 +- .../ChessRogueDiceModifierInstance.cs | 10 +- GameServer/Game/Mission/MissionManager.cs | 34 +-- GameServer/Game/Mission/StoryLineManager.cs | 9 +- .../Game/Rogue/Event/RogueEventManager.cs | 38 +-- .../Game/Task/AvatarTask/AbilityLevelTask.cs | 260 ++++++++++-------- .../Task/AvatarTask/SummonUnitLevelTask.cs | 80 ++++-- GameServer/Game/Task/LevelTask.cs | 186 ++++++++----- GameServer/Server/ServerUtils.cs | 69 ++++- Program/Program/EntryPoint.cs | 5 +- 14 files changed, 512 insertions(+), 315 deletions(-) create mode 100644 DanhengKcpSharp/PacketLogHelper.cs diff --git a/Common/Configuration/ConfigContainer.cs b/Common/Configuration/ConfigContainer.cs index 6dfc6b45..c0432a47 100644 --- a/Common/Configuration/ConfigContainer.cs +++ b/Common/Configuration/ConfigContainer.cs @@ -83,9 +83,9 @@ public class ServerOption public ServerAnnounce ServerAnnounce { get; set; } = new(); public ServerProfile ServerProfile { get; set; } = new(); public bool AutoCreateUser { get; set; } = true; - public bool SavePersonalDebugFile { get; set; } = false; + public LogOption LogOption { get; set; } = new(); public int FarmingDropRate { get; set; } = 1; - public bool UseCache { get; set; } = true; + public bool UseCache { get; set; } = false; // didnt recommend public int ValidFarmingDropRate() { @@ -93,6 +93,18 @@ public class ServerOption } } +public class LogOption +{ +#if DEBUG + public bool EnableGamePacketLog { get; set; } = true; +#else + public bool EnableGamePacketLog { get; set; } = false; +#endif + public bool LogPacketToConsole { get; set; } = true; + public bool DisableLogDetailPacket { get; set; } = false; + public bool SavePersonalDebugFile { get; set; } = false; +} + public class ServerAnnounce { public bool EnableAnnounce { get; set; } = true; diff --git a/DanhengKcpSharp/DanhengConnection.cs b/DanhengKcpSharp/DanhengConnection.cs index 4008179a..bb13c147 100644 --- a/DanhengKcpSharp/DanhengConnection.cs +++ b/DanhengKcpSharp/DanhengConnection.cs @@ -1,10 +1,7 @@ using System.Collections.Concurrent; using System.Net; -using System.Reflection; using EggLink.DanhengServer.Kcp.KcpSharp; using EggLink.DanhengServer.Util; -using Google.Protobuf; -using Google.Protobuf.Reflection; namespace EggLink.DanhengServer.Kcp; @@ -73,24 +70,21 @@ public class DanhengConnection public void LogPacket(string sendOrRecv, ushort opcode, byte[] payload) { + if (!ConfigManager.Config.ServerOption.LogOption.EnableGamePacketLog) return; + try { - //Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}"); if (IgnoreLog.Contains(opcode)) return; - var typ = AppDomain.CurrentDomain.GetAssemblies() - .SingleOrDefault(assembly => assembly.GetName().Name == "DanhengProto")!.GetTypes() - .First(t => t.Name == $"{LogMap[opcode]}"); //get the type using the packet name - var descriptor = - typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static)?.GetValue( - null, null) as MessageDescriptor; // get the static property Descriptor - var packet = descriptor?.Parser.ParseFrom(payload); - var formatter = JsonFormatter.Default; - var asJson = formatter.Format(packet); + + if (ConfigManager.Config.ServerOption.LogOption.DisableLogDetailPacket) throw new Exception(); + + var asJson = PacketLogHelper.ConvertPacketToJson(opcode, payload); var output = $"{sendOrRecv}: {LogMap[opcode]}({opcode})\r\n{asJson}"; -#if DEBUG - Logger.Debug(output); -#endif - if (DebugFile == "" || !ConfigManager.Config.ServerOption.SavePersonalDebugFile) return; + + if (ConfigManager.Config.ServerOption.LogOption.LogPacketToConsole) + Logger.Debug(output); + + if (DebugFile == "" || !ConfigManager.Config.ServerOption.LogOption.SavePersonalDebugFile) return; var sw = GetWriter(); sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output); sw.Flush(); @@ -98,10 +92,11 @@ public class DanhengConnection catch { var output = $"{sendOrRecv}: {LogMap.GetValueOrDefault(opcode, "UnknownPacket")}({opcode})"; -#if DEBUG - Logger.Debug(output); -#endif - if (DebugFile != "" && ConfigManager.Config.ServerOption.SavePersonalDebugFile) + + if (ConfigManager.Config.ServerOption.LogOption.LogPacketToConsole) + Logger.Debug(output); + + if (DebugFile != "" && ConfigManager.Config.ServerOption.LogOption.SavePersonalDebugFile) { var sw = GetWriter(); sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output); diff --git a/DanhengKcpSharp/DanhengListener.cs b/DanhengKcpSharp/DanhengListener.cs index 8ae00b55..c6c856a3 100644 --- a/DanhengKcpSharp/DanhengListener.cs +++ b/DanhengKcpSharp/DanhengListener.cs @@ -25,7 +25,8 @@ public class DanhengListener KeepAliveOptions = new KcpKeepAliveOptions(1000, 30000) }; - public static Type BaseConnection { get; set; } = typeof(DanhengConnection); + public delegate DanhengConnection ConnectionCreatedHandler(KcpConversation conversation, IPEndPoint remote); + public static ConnectionCreatedHandler? CreateConnection { get; set; } = null; private static Socket? UDPListener => UDPClient?.Client; private static IKcpMultiplexConnection? Multiplex => KCPTransport?.Connection; @@ -120,8 +121,8 @@ public class DanhengListener { var convId = Connections.GetNextAvailableIndex(); var convo = Multiplex?.CreateConversation(convId, rcv.RemoteEndPoint, ConvOpt); - if (convo == null) return; - var con = (DanhengConnection)Activator.CreateInstance(BaseConnection, convo, rcv.RemoteEndPoint)!; + if (convo == null || CreateConnection == null) return; + var con = CreateConnection(convo, rcv.RemoteEndPoint); RegisterConnection(con); await SendHandshakeResponse(con, enet); } diff --git a/DanhengKcpSharp/PacketLogHelper.cs b/DanhengKcpSharp/PacketLogHelper.cs new file mode 100644 index 00000000..61db2a6e --- /dev/null +++ b/DanhengKcpSharp/PacketLogHelper.cs @@ -0,0 +1,63 @@ +using System.Collections.Concurrent; +using System.Reflection; +using EggLink.DanhengServer.Proto; +using Google.Protobuf; +using Google.Protobuf.Reflection; + +namespace EggLink.DanhengServer.Kcp; + +public static class PacketLogHelper +{ + private delegate IMessage ParseIMessage(byte[] data); + private static ConcurrentDictionary CachedParsers { get; } = []; + + public static string ConvertPacketToJson(ushort opcode, byte[] payload) + { + var descriptor = GetParser(opcode); + if (descriptor == null) + { + throw new Exception(); + } + + var message = descriptor(payload); + var formatter = JsonFormatter.Default; + var asJson = formatter.Format(message); + return asJson ?? throw new Exception(); + } + + private static ParseIMessage? GetParser(ushort opcode) + { + if (CachedParsers.TryGetValue(opcode, out var parser)) + { + return parser; + } + + lock (CachedParsers) + { + // try to find the descriptor by opcode + var asbly = Assembly.GetAssembly(typeof(PlayerGetTokenCsReq)); + if (asbly == null) return null; + + var typ = asbly.GetType($"EggLink.DanhengServer.Proto.{DanhengConnection.LogMap[opcode]}"); + if (typ == null) return null; + var desc = typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static); + if (desc?.GetMethod == null) return null; + + // get parser + if (desc.GetValue(null) is not MessageDescriptor parserProperty) return null; + + var parserMethod = parserProperty.Parser.GetType().GetMethod("ParseFrom", [typeof(byte[])]); + if (parserMethod == null) return null; + + parser = (ParseIMessage)Delegate.CreateDelegate( + typeof(ParseIMessage), + parserProperty.Parser, + parserMethod + ); + + CachedParsers[opcode] = parser; + + return parser; + } + } +} \ No newline at end of file diff --git a/GameServer/Game/ChessRogue/ChessRogueInstance.cs b/GameServer/Game/ChessRogue/ChessRogueInstance.cs index c1279b1b..7e1561af 100644 --- a/GameServer/Game/ChessRogue/ChessRogueInstance.cs +++ b/GameServer/Game/ChessRogue/ChessRogueInstance.cs @@ -35,17 +35,6 @@ public class ChessRogueInstance : BaseRogueInstance EventManager = new RogueEventManager(player, this); RogueType = rogueSubMode == RogueSubModeEnum.ChessRogueNous ? 160 : 130; - - var types = Assembly.GetExecutingAssembly().GetTypes(); - foreach (var type in types) - { - var attr = type.GetCustomAttribute(); - if (attr == null) continue; - - var handler = (ModifierEffectHandler)Activator.CreateInstance(type, null)!; - ModifierEffectHandlers.Add(attr.EffectType, handler); - } - foreach (var difficulty in areaExcel.DifficultyID) if (GameData.RogueDLCDifficultyData.TryGetValue(difficulty, out var diff)) DifficultyExcel.Add(diff); @@ -69,7 +58,7 @@ public class ChessRogueInstance : BaseRogueInstance public int BossAeonId { get; set; } public List DifficultyExcel { get; set; } = []; public ChessRogueDiceInstance DiceInstance { get; set; } - public Dictionary ModifierEffectHandlers { get; set; } = []; + public static Dictionary ModifierEffectHandlers { get; set; } = []; public Dictionary RogueCells { get; set; } = []; public ChessRogueCellInstance? CurCell { get; set; } diff --git a/GameServer/Game/ChessRogue/Modifier/ChessRogueDiceModifierInstance.cs b/GameServer/Game/ChessRogue/Modifier/ChessRogueDiceModifierInstance.cs index c5b80c00..14d6d8c2 100644 --- a/GameServer/Game/ChessRogue/Modifier/ChessRogueDiceModifierInstance.cs +++ b/GameServer/Game/ChessRogue/Modifier/ChessRogueDiceModifierInstance.cs @@ -39,7 +39,7 @@ public class ChessRogueDiceModifierInstance(int modifierId, ChessRogueDiceSurfac { var effect = EffectConfig.EffectType; - instance.ModifierEffectHandlers.TryGetValue(effect, out var handler); + ChessRogueInstance.ModifierEffectHandlers.TryGetValue(effect, out var handler); if (handler != null) await handler.SelectCell(this, instance, selectCellId); @@ -51,7 +51,7 @@ public class ChessRogueDiceModifierInstance(int modifierId, ChessRogueDiceSurfac { var effect = EffectConfig.EffectType; - instance.ModifierEffectHandlers.TryGetValue(effect, out var handler); + ChessRogueInstance.ModifierEffectHandlers.TryGetValue(effect, out var handler); if (handler != null) await handler.SelectModifierCell(this, instance, selectCellId); @@ -63,7 +63,7 @@ public class ChessRogueDiceModifierInstance(int modifierId, ChessRogueDiceSurfac { var effect = EffectConfig.EffectType; - instance.ModifierEffectHandlers.TryGetValue(effect, out var handler); + ChessRogueInstance.ModifierEffectHandlers.TryGetValue(effect, out var handler); if (handler != null) await handler.OnConfirmed(this, instance); @@ -75,7 +75,7 @@ public class ChessRogueDiceModifierInstance(int modifierId, ChessRogueDiceSurfac { var effect = EffectConfig.EffectType; - instance.ModifierEffectHandlers.TryGetValue(effect, out var handler); + ChessRogueInstance.ModifierEffectHandlers.TryGetValue(effect, out var handler); if (handler != null) handler.BeforeBattle(this, battle, instance); @@ -87,7 +87,7 @@ public class ChessRogueDiceModifierInstance(int modifierId, ChessRogueDiceSurfac { var effect = EffectConfig.EffectType; - instance.ModifierEffectHandlers.TryGetValue(effect, out var handler); + ChessRogueInstance.ModifierEffectHandlers.TryGetValue(effect, out var handler); if (handler != null) await handler.AfterBattle(this, battle); diff --git a/GameServer/Game/Mission/MissionManager.cs b/GameServer/Game/Mission/MissionManager.cs index c420abac..fe217aeb 100644 --- a/GameServer/Game/Mission/MissionManager.cs +++ b/GameServer/Game/Mission/MissionManager.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Config; using EggLink.DanhengServer.Database; using EggLink.DanhengServer.Database.Inventory; @@ -19,39 +18,16 @@ using MissionData = EggLink.DanhengServer.Database.Quests.MissionData; namespace EggLink.DanhengServer.GameServer.Game.Mission; -public class MissionManager : BasePlayerManager +public class MissionManager(PlayerInstance player) : BasePlayerManager(player) { #region Initializer & Properties - public MissionData Data { get; set; } - public Dictionary ActionHandlers = []; - public Dictionary FinishTypeHandlers = []; + public MissionData Data { get; set; } = DatabaseHelper.Instance!.GetInstanceOrCreateNew(player.Uid); + public static readonly Dictionary ActionHandlers = []; + public static readonly Dictionary FinishTypeHandlers = []; public readonly List SkipSubMissionList = []; // bug - public MissionManager(PlayerInstance player) : base(player) - { - Data = DatabaseHelper.Instance!.GetInstanceOrCreateNew(player.Uid); - - var types = Assembly.GetExecutingAssembly().GetTypes(); - foreach (var type in types) - { - var attr = type.GetCustomAttribute(); - if (attr != null) - { - var handler = (MissionFinishActionHandler)Activator.CreateInstance(type, null)!; - ActionHandlers.Add(attr.FinishAction, handler); - } - - var attr2 = type.GetCustomAttribute(); - if (attr2 != null) - { - var handler = (MissionFinishTypeHandler)Activator.CreateInstance(type, null)!; - FinishTypeHandlers.Add(attr2.FinishType, handler); - } - } - } - #endregion #region Mission Actions diff --git a/GameServer/Game/Mission/StoryLineManager.cs b/GameServer/Game/Mission/StoryLineManager.cs index 6ba72b0f..6ec55a33 100644 --- a/GameServer/Game/Mission/StoryLineManager.cs +++ b/GameServer/Game/Mission/StoryLineManager.cs @@ -10,14 +10,9 @@ using EggLink.DanhengServer.Util; namespace EggLink.DanhengServer.GameServer.Game.Mission; -public class StoryLineManager : BasePlayerManager +public class StoryLineManager(PlayerInstance player) : BasePlayerManager(player) { - public StoryLineManager(PlayerInstance player) : base(player) - { - StoryLineData = DatabaseHelper.Instance!.GetInstanceOrCreateNew(player.Uid); - } - - public StoryLineData StoryLineData { get; set; } + public StoryLineData StoryLineData { get; set; } = DatabaseHelper.Instance!.GetInstanceOrCreateNew(player.Uid); public async ValueTask CheckIfEnterStoryLine() { diff --git a/GameServer/Game/Rogue/Event/RogueEventManager.cs b/GameServer/Game/Rogue/Event/RogueEventManager.cs index 6205e2b5..c50d74d0 100644 --- a/GameServer/Game/Rogue/Event/RogueEventManager.cs +++ b/GameServer/Game/Rogue/Event/RogueEventManager.cs @@ -1,44 +1,18 @@ -using System.Reflection; -using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Enums.Rogue; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Server.Packet.Send.RogueCommon; namespace EggLink.DanhengServer.GameServer.Game.Rogue.Event; -public class RogueEventManager +public class RogueEventManager(PlayerInstance player, BaseRogueInstance rogueInstance) { - public Dictionary CostHandler = []; - public Dictionary EffectHandler = []; - public PlayerInstance Player; - public BaseRogueInstance Rogue; + public static Dictionary CostHandler = []; + public static Dictionary EffectHandler = []; + public PlayerInstance Player = player; + public BaseRogueInstance Rogue = rogueInstance; public List RunningEvent = []; - public RogueEventManager(PlayerInstance player, BaseRogueInstance rogueInstance) - { - Player = player; - Rogue = rogueInstance; - - var types = Assembly.GetExecutingAssembly().GetTypes(); - foreach (var type in types) - { - var attr = type.GetCustomAttribute(); - if (attr == null) continue; - if (attr.EffectType != DialogueEventTypeEnum.None) - { - // Effect - var effect = (RogueEventEffectHandler)Activator.CreateInstance(type, null)!; - EffectHandler.Add(attr.EffectType, effect); - } - else - { - // Cost - var cost = (RogueEventCostHandler)Activator.CreateInstance(type, null)!; - CostHandler.Add(attr.CostType, cost); - } - } - } - public void OnNextRoom() { RunningEvent.Clear(); // Clear all running events diff --git a/GameServer/Game/Task/AvatarTask/AbilityLevelTask.cs b/GameServer/Game/Task/AvatarTask/AbilityLevelTask.cs index cc623501..1159b49c 100644 --- a/GameServer/Game/Task/AvatarTask/AbilityLevelTask.cs +++ b/GameServer/Game/Task/AvatarTask/AbilityLevelTask.cs @@ -1,4 +1,5 @@ -using EggLink.DanhengServer.Data; +using System.Collections.Concurrent; +using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Config; using EggLink.DanhengServer.Data.Config.Task; using EggLink.DanhengServer.Enums.Avatar; @@ -22,9 +23,13 @@ public class AbilityLevelTask(PlayerInstance player) #region Selector - public List TargetAlias(TargetEvaluator selector, BaseGameEntity casterEntity, - List targetEntities) + public async ValueTask TargetAlias(AbilityLevelParam param) { + await ValueTask.CompletedTask; + var selector = param.TargetEvaluator!; + var casterEntity = param.CasterEntity; + var targetEntities = param.TargetEntities; + if (selector is TargetAlias target) return target.Alias switch { @@ -33,7 +38,7 @@ public class AbilityLevelTask(PlayerInstance player) _ => targetEntities }; - return []; + return new List(); } #endregion @@ -69,14 +74,13 @@ public class AbilityLevelTask(PlayerInstance player) { var methodName = param.Act.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) - { - var res = method.Invoke(this, [param]); - if (res is AbilityLevelResult result) return result; + // try to get from cache + var method = GetOrCreateExecuteTask(methodName); + if (method == null) return new AbilityLevelResult(); - if (res is ValueTask valueTask) return await valueTask; - } + var res = method(param); + var re = await res; + if (re is AbilityLevelResult result) return result; } catch (Exception e) { @@ -86,11 +90,28 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(); } + private ExecuteTask? GetOrCreateExecuteTask(string methodName) + { + // try to get from cache + if (_cachedTasks.TryGetValue(methodName, out var method)) return method; + var methodProp = GetType().GetMethod(methodName); + if (methodProp == null) return null; + + method = (ExecuteTask)Delegate.CreateDelegate(typeof(ExecuteTask), this, methodProp); + _cachedTasks[methodName] = method; // cached + + return method; + } + + private delegate ValueTask ExecuteTask(AbilityLevelParam param); + + private readonly ConcurrentDictionary _cachedTasks = []; + #endregion #region Task - public async ValueTask PredicateTaskList(AbilityLevelParam param) + public async ValueTask PredicateTaskList(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; @@ -99,32 +120,23 @@ public class AbilityLevelTask(PlayerInstance player) { // handle predicateCondition var methodName = predicateTaskList.Predicate.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); + + var method = GetOrCreateExecuteTask(methodName); + var res = true; if (method != null) { - var resp = method.Invoke(this, [param with { Act = predicateTaskList.Predicate }]); - if (resp is not bool res) return new AbilityLevelResult(instance, battleInfos); - - res = predicateTaskList.Predicate.Inverse ? !res : res; - if (res) - foreach (var task in predicateTaskList.SuccessTaskList) - { - var result = await TriggerTask(param with { Act = task }); - if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); - - if (result.Instance != null) instance = result.Instance; - } + var resp = await method(param with { Act = predicateTaskList.Predicate }); + if (resp is not bool r) + { + res = false; + } else - foreach (var task in predicateTaskList.FailedTaskList) - { - var result = await TriggerTask(param with { Act = task }); - if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); - - if (result.Instance != null) instance = result.Instance; - } + { + res = predicateTaskList.Predicate.Inverse ? !r : r; + } } - else - { + + if (res) foreach (var task in predicateTaskList.SuccessTaskList) { var result = await TriggerTask(param with { Act = task }); @@ -132,13 +144,20 @@ public class AbilityLevelTask(PlayerInstance player) if (result.Instance != null) instance = result.Instance; } - } + else + foreach (var task in predicateTaskList.FailedTaskList) + { + var result = await TriggerTask(param with { Act = task }); + if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); + + if (result.Instance != null) instance = result.Instance; + } } return new AbilityLevelResult(instance, battleInfos); } - public async ValueTask AdventureTriggerAttack(AbilityLevelParam param) + public async ValueTask AdventureTriggerAttack(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; @@ -146,39 +165,37 @@ public class AbilityLevelTask(PlayerInstance player) if (param.Act is AdventureTriggerAttack adventureTriggerAttack) { var methodName = adventureTriggerAttack.AttackTargetType.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) + var method = GetOrCreateExecuteTask(methodName); + if (method == null) return new AbilityLevelResult(); + var resp = await method(param with { TargetEvaluator = adventureTriggerAttack.AttackTargetType }); + + if (resp is List target) { - var resp = method.Invoke(this, - [adventureTriggerAttack.AttackTargetType, param.CasterEntity, param.TargetEntities]); - if (resp is List target) + foreach (var task in adventureTriggerAttack.OnAttack) { - foreach (var task in adventureTriggerAttack.OnAttack) + var result = await TriggerTask(param with { Act = task }); + if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); + } + + if (target.Count > 0 && adventureTriggerAttack.TriggerBattle) + { + foreach (var task in adventureTriggerAttack.OnBattle) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); } - if (target.Count > 0 && adventureTriggerAttack.TriggerBattle) + foreach (var entity in param.TargetEntities) { - foreach (var task in adventureTriggerAttack.OnBattle) - { - var result = await TriggerTask(param with { Act = task }); - if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); - } + var type = MonsterBattleType.TriggerBattle; + if (entity is EntityMonster { IsAlive: false }) + type = MonsterBattleType.DirectDieSkipBattle; - foreach (var entity in param.TargetEntities) - { - var type = MonsterBattleType.TriggerBattle; - if (entity is EntityMonster { IsAlive: false }) - type = MonsterBattleType.DirectDieSkipBattle; - - battleInfos.Add(new HitMonsterInstance(entity.EntityId, type)); - } - - instance = await Player.BattleManager!.StartBattle(param.CasterEntity, param.TargetEntities, - param.Request.SkillIndex == 1); + battleInfos.Add(new HitMonsterInstance(entity.EntityId, type)); } + + instance = await Player.BattleManager!.StartBattle(param.CasterEntity, param.TargetEntities, + param.Request.SkillIndex == 1); } } } @@ -186,7 +203,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(instance, battleInfos); } - public async ValueTask AddMazeBuff(AbilityLevelParam param) + public async ValueTask AddMazeBuff(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; @@ -194,32 +211,29 @@ public class AbilityLevelTask(PlayerInstance player) if (param.Act is AddMazeBuff addMazeBuff) { var methodName = addMazeBuff.TargetType.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) - { - var resp = method.Invoke(this, - [addMazeBuff.TargetType, param.CasterEntity, param.TargetEntities]); + var method = GetOrCreateExecuteTask(methodName); + if (method == null) return new AbilityLevelResult(); + var resp = await method(param with { TargetEvaluator = addMazeBuff.TargetType }); - Dictionary dynamic = []; - foreach (var dynamicValue in addMazeBuff.DynamicValues) - dynamic.Add(dynamicValue.Key, dynamicValue.Value.GetValue()); + Dictionary dynamic = []; + foreach (var dynamicValue in addMazeBuff.DynamicValues) + dynamic.Add(dynamicValue.Key, dynamicValue.Value.GetValue()); - if (resp is not List target) return new AbilityLevelResult(instance, battleInfos); + if (resp is not List target) return new AbilityLevelResult(instance, battleInfos); - foreach (var entity in target) - await entity.AddBuff(new SceneBuff(addMazeBuff.ID, 1, - (param.CasterEntity as AvatarSceneInfo)?.AvatarInfo.BaseAvatarId ?? 0, - addMazeBuff.LifeTime.FixedValue.Value < -1 ? 20 : -1) - { - DynamicValues = dynamic - }); - } + foreach (var entity in target) + await entity.AddBuff(new SceneBuff(addMazeBuff.ID, 1, + (param.CasterEntity as AvatarSceneInfo)?.AvatarInfo.BaseAvatarId ?? 0, + addMazeBuff.LifeTime.FixedValue.Value < -1 ? 20 : -1) + { + DynamicValues = dynamic + }); } return new AbilityLevelResult(instance, battleInfos); } - public async ValueTask AdventureFireProjectile(AbilityLevelParam param) + public async ValueTask AdventureFireProjectile(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; @@ -248,7 +262,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(instance, battleInfos); } - public async ValueTask NewAdventureFireProjectile(AbilityLevelParam param) + public async ValueTask NewAdventureFireProjectile(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; @@ -277,7 +291,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(instance, battleInfos); } - public async ValueTask CreateSummonUnit(AbilityLevelParam param) + public async ValueTask CreateSummonUnit(AbilityLevelParam param) { if (param.Act is CreateSummonUnit createSummonUnit) { @@ -304,7 +318,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(); } - public async ValueTask DestroySummonUnit(AbilityLevelParam param) + public async ValueTask DestroySummonUnit(AbilityLevelParam param) { if (param.Act is DestroySummonUnit destroySummonUnit) await Player.SceneInstance!.RemoveSummonUnitById(destroySummonUnit.SummonUnit.SummonUnitID); // TODO @@ -312,7 +326,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(); } - public async ValueTask AddAdventureModifier(AbilityLevelParam param) + public async ValueTask AddAdventureModifier(AbilityLevelParam param) { if (param.Act is AddAdventureModifier addAdventureModifier) { @@ -325,7 +339,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(); } - public async ValueTask RemoveAdventureModifier(AbilityLevelParam param) + public async ValueTask RemoveAdventureModifier(AbilityLevelParam param) { if (param.Act is RemoveAdventureModifier removeAdventureModifier) { @@ -338,7 +352,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(); } - public async ValueTask RemoveSelfModifier(AbilityLevelParam param) + public async ValueTask RemoveSelfModifier(AbilityLevelParam param) { if (param.ModifierName != null) if (param.CasterEntity is IGameModifier mod) @@ -347,7 +361,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(); } - public async ValueTask RefreshMazeBuffTime(AbilityLevelParam param) + public async ValueTask RefreshMazeBuffTime(AbilityLevelParam param) { if (param.Act is RefreshMazeBuffTime refreshMazeBuffTime) { @@ -361,7 +375,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(); } - public async ValueTask AdvModifyMaxMazeMP(AbilityLevelParam param) + public async ValueTask AdvModifyMaxMazeMP(AbilityLevelParam param) { await ValueTask.CompletedTask; @@ -379,7 +393,7 @@ public class AbilityLevelTask(PlayerInstance player) return new AbilityLevelResult(); } - public async ValueTask AdventureSetAttackTargetMonsterDie(AbilityLevelParam param) + public async ValueTask AdventureSetAttackTargetMonsterDie(AbilityLevelParam param) { var avatar = param.CasterEntity as AvatarSceneInfo; if (GameData.AvatarConfigData.TryGetValue(avatar?.AvatarInfo.AvatarId ?? 0, out var excel)) @@ -418,14 +432,18 @@ public class AbilityLevelTask(PlayerInstance player) await instance.GainMoney(Random.Shared.Next(20, 60)); } } + + return new AbilityLevelResult(); } #endregion #region Predicate - public bool ByAllowInstantKill(AbilityLevelParam param) + public async ValueTask ByAllowInstantKill(AbilityLevelParam param) { + await ValueTask.CompletedTask; + foreach (var targetEntity in param.TargetEntities) if (targetEntity is EntityMonster monster) if (monster.MonsterData.Rank < MonsterRankEnum.Elite) @@ -434,30 +452,31 @@ public class AbilityLevelTask(PlayerInstance player) return false; } - public bool ByIsContainAdventureModifier(AbilityLevelParam param) + public async ValueTask ByIsContainAdventureModifier(AbilityLevelParam param) { + await ValueTask.CompletedTask; + if (param.Act is ByIsContainAdventureModifier byIsContain) { // get target var result = false; var methodName = byIsContain.TargetType.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) - { - var resp = method.Invoke(this, - [byIsContain.TargetType, param.CasterEntity, param.TargetEntities]); - if (resp is List target) - foreach (var entity in target) + var method = GetOrCreateExecuteTask(methodName); + if (method == null) return false; + + var resp = await method(param with { TargetEvaluator = byIsContain.TargetType }); + + if (resp is List target) + foreach (var entity in target) + { + if (entity is not IGameModifier modifier) continue; + if (modifier.Modifiers.Contains(byIsContain.ModifierName)) { - if (entity is not IGameModifier modifier) continue; - if (modifier.Modifiers.Contains(byIsContain.ModifierName)) - { - result = true; - break; - } + result = true; + break; } - } + } return result; } @@ -465,42 +484,50 @@ public class AbilityLevelTask(PlayerInstance player) return false; } - public bool AdventureByInMotionState(AbilityLevelParam param) + public async ValueTask AdventureByInMotionState(AbilityLevelParam param) { + await ValueTask.CompletedTask; + return true; } - public bool AdventureByPlayerCurrentSkillType(AbilityLevelParam param) + public async ValueTask AdventureByPlayerCurrentSkillType(AbilityLevelParam param) { + await ValueTask.CompletedTask; + if (param.Act is AdventureByPlayerCurrentSkillType byPlayerCurrentSkillType) return param.Request.SkillIndex == (uint)byPlayerCurrentSkillType.SkillType; return false; } - public bool ByCompareCarryMazebuff(AbilityLevelParam param) + public async ValueTask ByCompareCarryMazebuff(AbilityLevelParam param) { + await ValueTask.CompletedTask; + if (param.Act is ByCompareCarryMazebuff byCompareCarryMazebuff) return param.CasterEntity.BuffList.Any(x => x.BuffId == byCompareCarryMazebuff.BuffID); return false; } - public bool ByAnd(AbilityLevelParam param) + public async ValueTask ByAnd(AbilityLevelParam param) { + await ValueTask.CompletedTask; + if (param.Act is ByAnd byAnd) { foreach (var task in byAnd.PredicateList) { var methodName = task.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) - { - var resp = method.Invoke(this, [param with { Act = task }]); - if (resp is not bool res) return false; - res = task.Inverse ? !res : res; - if (!res) return false; - } + + var method = GetOrCreateExecuteTask(methodName); + if (method == null) return false; + + var resp = await method(param with { Act = task }); + if (resp is not bool res) return false; + res = task.Inverse ? !res : res; + if (!res) return false; } return true; @@ -520,4 +547,5 @@ public record AbilityLevelParam( BaseGameEntity CasterEntity, List TargetEntities, SceneCastSkillCsReq Request, - string? ModifierName); \ No newline at end of file + string? ModifierName, + TargetEvaluator? TargetEvaluator = null); \ No newline at end of file diff --git a/GameServer/Game/Task/AvatarTask/SummonUnitLevelTask.cs b/GameServer/Game/Task/AvatarTask/SummonUnitLevelTask.cs index 3d990250..f1e464ed 100644 --- a/GameServer/Game/Task/AvatarTask/SummonUnitLevelTask.cs +++ b/GameServer/Game/Task/AvatarTask/SummonUnitLevelTask.cs @@ -3,6 +3,8 @@ using EggLink.DanhengServer.GameServer.Game.Scene; using EggLink.DanhengServer.GameServer.Game.Scene.Entity; using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lineup; using EggLink.DanhengServer.Proto; +using System.Collections.Concurrent; +using EggLink.DanhengServer.Util; namespace EggLink.DanhengServer.GameServer.Game.Task.AvatarTask; @@ -10,9 +12,11 @@ public class SummonUnitLevelTask { #region Task Condition - public bool ByIsContainAdventureModifier(TaskConfigInfo act, List targetEntities, + public async ValueTask ByIsContainAdventureModifier(TaskConfigInfo act, List targetEntities, EntitySummonUnit? summonUnit) { + await ValueTask.CompletedTask; + return true; } @@ -31,19 +35,40 @@ public class SummonUnitLevelTask { var methodName = act.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) _ = method.Invoke(this, [act, targetEntities, summonUnit]); + // try to get from cache + var method = GetOrCreateExecuteTask(methodName); + if (method == null) return; + + method(act, targetEntities, summonUnit); } - catch + catch (Exception e) { + Logger.GetByClassName().Error("An error occured, ", e); } } + private ExecuteTask? GetOrCreateExecuteTask(string methodName) + { + // try to get from cache + if (_cachedTasks.TryGetValue(methodName, out var method)) return method; + var methodProp = GetType().GetMethod(methodName); + if (methodProp == null) return null; + + method = (ExecuteTask)Delegate.CreateDelegate(typeof(ExecuteTask), this, methodProp); + _cachedTasks[methodName] = method; // cached + + return method; + } + + private delegate ValueTask ExecuteTask(TaskConfigInfo act, List targetEntities, EntitySummonUnit? summonUnit); + + private readonly ConcurrentDictionary _cachedTasks = []; + #endregion #region Task - public async ValueTask PredicateTaskList(TaskConfigInfo act, List targetEntities, + public async ValueTask PredicateTaskList(TaskConfigInfo act, List targetEntities, EntitySummonUnit? summonUnit) { if (act is PredicateTaskList predicateTaskList) @@ -51,26 +76,25 @@ public class SummonUnitLevelTask // handle predicateCondition var methodName = predicateTaskList.Predicate.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) - { - var resp = method.Invoke(this, [predicateTaskList.Predicate, targetEntities, summonUnit]); - if (resp is bool res && res) - foreach (var task in predicateTaskList.SuccessTaskList) - TriggerTask(task, targetEntities, summonUnit); - else - foreach (var task in predicateTaskList.FailedTaskList) - TriggerTask(task, targetEntities, summonUnit); - } + var method = GetOrCreateExecuteTask(methodName); + if (method == null) return null; + + var resp = await method(predicateTaskList.Predicate, targetEntities, summonUnit); + if (resp is true) + foreach (var task in predicateTaskList.SuccessTaskList) + TriggerTask(task, targetEntities, summonUnit); + else + foreach (var task in predicateTaskList.FailedTaskList) + TriggerTask(task, targetEntities, summonUnit); } - await ValueTask.CompletedTask; + return null; } - public async ValueTask AddMazeBuff(TaskConfigInfo act, List targetEntities, + public async ValueTask AddMazeBuff(TaskConfigInfo act, List targetEntities, EntitySummonUnit? summonUnit) { - if (act is not AddMazeBuff addMazeBuff) return; + if (act is not AddMazeBuff addMazeBuff) return null; var buff = new SceneBuff(addMazeBuff.ID, 1, summonUnit?.CreateAvatarId ?? 0) { @@ -86,12 +110,14 @@ public class SummonUnitLevelTask await monster.AddBuff(buff); } + + return null; } - public async ValueTask RemoveMazeBuff(TaskConfigInfo act, List targetEntities, + public async ValueTask RemoveMazeBuff(TaskConfigInfo act, List targetEntities, EntitySummonUnit? summonUnit) { - if (act is not RemoveMazeBuff removeMazeBuff) return; + if (act is not RemoveMazeBuff removeMazeBuff) return null; foreach (var targetEntity in targetEntities) { @@ -99,12 +125,14 @@ public class SummonUnitLevelTask await monster.RemoveBuff(removeMazeBuff.ID); } + + return null; } - public async ValueTask RefreshMazeBuffTime(TaskConfigInfo act, List targetEntities, + public async ValueTask RefreshMazeBuffTime(TaskConfigInfo act, List targetEntities, EntitySummonUnit? summonUnit) { - if (act is not RefreshMazeBuffTime refreshMazeBuffTime) return; + if (act is not RefreshMazeBuffTime refreshMazeBuffTime) return null; var buff = new SceneBuff(refreshMazeBuffTime.ID, 1, summonUnit?.CreateAvatarId ?? 0) { @@ -118,9 +146,11 @@ public class SummonUnitLevelTask await monster.AddBuff(buff); } + + return null; } - public async ValueTask TriggerHitProp(TaskConfigInfo act, List targetEntities, + public async ValueTask TriggerHitProp(TaskConfigInfo act, List targetEntities, EntitySummonUnit? summonUnit) { foreach (var targetEntity in targetEntities) @@ -145,6 +175,8 @@ public class SummonUnitLevelTask prop.Scene.Player.RogueManager!.GetRogueInstance()?.OnPropDestruct(prop); } + + return null; } #endregion diff --git a/GameServer/Game/Task/LevelTask.cs b/GameServer/Game/Task/LevelTask.cs index 282db85c..6f7d1a11 100644 --- a/GameServer/Game/Task/LevelTask.cs +++ b/GameServer/Game/Task/LevelTask.cs @@ -7,7 +7,9 @@ using EggLink.DanhengServer.Enums.Scene; using EggLink.DanhengServer.Enums.Task; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Game.Scene.Entity; +using EggLink.DanhengServer.GameServer.Game.Task.AvatarTask; using EggLink.DanhengServer.Proto; +using System.Collections.Concurrent; namespace EggLink.DanhengServer.GameServer.Game.Task; @@ -17,8 +19,10 @@ public class LevelTask(PlayerInstance player) #region Prop Target - public EntityProp? TargetFetchAdvPropEx(TargetEvaluator act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask TargetFetchAdvPropEx(TargetEvaluator act, SubMissionData subMission, GroupInfo? group = null) { + await ValueTask.CompletedTask; + if (act is TargetFetchAdvPropEx fetch) { if (fetch.FetchType != TargetFetchAdvPropFetchTypeEnum.SinglePropByPropID) return null; @@ -51,33 +55,54 @@ public class LevelTask(PlayerInstance player) { var methodName = act.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) _ = method.Invoke(this, [act, subMission, group]); + var method = GetOrCreateExecuteTask(methodName); + if (method != null) _ = method(act, subMission, group); } catch { } } + private ExecuteTask? GetOrCreateExecuteTask(string methodName) + { + // try to get from cache + if (_cachedTasks.TryGetValue(methodName, out var method)) return method; + var methodProp = GetType().GetMethod(methodName); + if (methodProp == null) return null; + + method = (ExecuteTask)Delegate.CreateDelegate(typeof(ExecuteTask), this, methodProp); + _cachedTasks[methodName] = method; // cached + + return method; + } + + private delegate ValueTask ExecuteTask(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null); + + private readonly ConcurrentDictionary _cachedTasks = []; + #endregion #region Task - public async ValueTask PlayMessage(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask PlayMessage(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is PlayMessage message) await Player.MessageManager!.AddMessageSection(message.MessageSectionID); + + return null; } - public async ValueTask DestroyProp(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask DestroyProp(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is DestroyProp destroyProp) foreach (var entity in Player.SceneInstance!.Entities.Values) if (entity is EntityProp prop && prop.GroupId == destroyProp.GroupID.GetValue() && prop.InstId == destroyProp.ID.GetValue()) await Player.SceneInstance.RemoveEntity(entity); + + return null; } - public async ValueTask TriggerCustomString(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask TriggerCustomString(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is TriggerCustomString triggerCustomString) { @@ -91,21 +116,27 @@ public class LevelTask(PlayerInstance player) await Player.MissionManager!.HandleFinishType(MissionFinishTypeEnum.PropState); } + + return null; } - public async ValueTask EnterMap(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask EnterMap(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is EnterMap enterMap) await Player.EnterSceneByEntranceId(enterMap.EntranceID, enterMap.GroupID, enterMap.AnchorID, true); + + return null; } - public async ValueTask EnterMapByCondition(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask EnterMapByCondition(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is EnterMapByCondition enterMapByCondition) await Player.EnterSceneByEntranceId(enterMapByCondition.EntranceID.GetValue(), 0, 0, true); + + return null; } - public async ValueTask TriggerPerformance(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask TriggerPerformance(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is TriggerPerformance triggerPerformance) { @@ -117,63 +148,68 @@ public class LevelTask(PlayerInstance player) subMission); } - await System.Threading.Tasks.Task.CompletedTask; + await ValueTask.CompletedTask; + + return null; } - public async ValueTask PredicateTaskList(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask PredicateTaskList(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is PredicateTaskList predicateTaskList) { // handle predicateCondition var methodName = predicateTaskList.Predicate.Type.Replace("RPG.GameCore.", ""); - var method = GetType().GetMethod(methodName); - if (method != null) - { - var resp = method.Invoke(this, [predicateTaskList.Predicate, subMission, group]); - if (resp is bool res && res) - foreach (var task in predicateTaskList.SuccessTaskList) - TriggerTask(task, subMission, group); - else - foreach (var task in predicateTaskList.FailedTaskList) - TriggerTask(task, subMission, group); - } + var method = GetOrCreateExecuteTask(methodName); + if (method == null) return null; + + var resp = await method(predicateTaskList.Predicate, subMission, group); + if (resp is true) + foreach (var task in predicateTaskList.SuccessTaskList) + TriggerTask(task, subMission, group); + else + foreach (var task in predicateTaskList.FailedTaskList) + TriggerTask(task, subMission, group); } - await System.Threading.Tasks.Task.CompletedTask; + return null; } - public async ValueTask ChangePropState(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask ChangePropState(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { - if (subMission.SubMissionInfo?.FinishType == MissionFinishTypeEnum.PropState) - foreach (var entity in Player.SceneInstance!.Entities.Values) - if (entity is EntityProp prop && prop.GroupId == subMission.SubMissionInfo.ParamInt1 && - prop.InstId == subMission.SubMissionInfo.ParamInt2) - try - { - if (prop.Excel.PropStateList.Contains(PropStateEnum.Closed)) - { - await prop.SetState(PropStateEnum.Closed); - } - else - { - await prop.SetState( - prop.Excel.PropStateList[prop.Excel.PropStateList.IndexOf(prop.State) + 1]); + if (subMission.SubMissionInfo?.FinishType != MissionFinishTypeEnum.PropState) return null; - // Elevator - foreach (var id in prop.PropInfo.UnlockControllerID) - foreach (var entity2 in Player.SceneInstance!.Entities.Values) - if (entity2 is EntityProp prop2 && prop2.GroupId == id.Key && - id.Value.Contains(prop2.InstId)) - await prop2.SetState(PropStateEnum.Closed); - } - } - catch + foreach (var entity in Player.SceneInstance!.Entities.Values) + if (entity is EntityProp prop && prop.GroupId == subMission.SubMissionInfo.ParamInt1 && + prop.InstId == subMission.SubMissionInfo.ParamInt2) + try + { + if (prop.Excel.PropStateList.Contains(PropStateEnum.Closed)) { + await prop.SetState(PropStateEnum.Closed); } + else + { + await prop.SetState( + prop.Excel.PropStateList[prop.Excel.PropStateList.IndexOf(prop.State) + 1]); + + // Elevator + foreach (var id in prop.PropInfo.UnlockControllerID) + foreach (var entity2 in Player.SceneInstance!.Entities.Values) + if (entity2 is EntityProp prop2 && prop2.GroupId == id.Key && + id.Value.Contains(prop2.InstId)) + await prop2.SetState(PropStateEnum.Closed); + } + } + catch + { + // ignored + } + + return null; } - public async ValueTask CreateTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask CreateTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (subMission.SubMissionInfo?.FinishType == MissionFinishTypeEnum.GetTrialAvatar) await Player.LineupManager!.AddAvatarToCurTeam(subMission.SubMissionInfo.ParamInt1); @@ -181,9 +217,11 @@ public class LevelTask(PlayerInstance player) if (subMission.SubMissionInfo?.FinishType == MissionFinishTypeEnum.GetTrialAvatarList) subMission.SubMissionInfo.ParamIntList?.ForEach( async x => await Player.LineupManager!.AddAvatarToCurTeam(x)); + + return null; } - public async ValueTask ReplaceTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask ReplaceTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (subMission.SubMissionInfo?.FinishType == MissionFinishTypeEnum.GetTrialAvatar) { @@ -199,9 +237,11 @@ public class LevelTask(PlayerInstance player) subMission.SubMissionInfo.ParamIntList?.ForEach( async x => await Player.LineupManager!.AddAvatarToCurTeam(x)); } + + return null; } - public async ValueTask StoryLineReplaceTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask StoryLineReplaceTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (subMission.SubMissionInfo?.FinishType == MissionFinishTypeEnum.StoryLineAddTrialAvatar) { @@ -209,11 +249,13 @@ public class LevelTask(PlayerInstance player) ids.ForEach(async void (x) => await Player.LineupManager!.RemoveAvatarFromCurTeam(x.BaseAvatarId, false)); await Player.LineupManager!.AddAvatarToCurTeam(subMission.SubMissionInfo.ParamInt1); } + + return null; } - public async ValueTask ReplaceVirtualTeam(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask ReplaceVirtualTeam(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { - if (Player.LineupManager!.GetCurLineup()?.IsExtraLineup() != true) return; + if (Player.LineupManager!.GetCurLineup()?.IsExtraLineup() != true) return null; if (subMission.SubMissionInfo?.FinishType == MissionFinishTypeEnum.GetTrialAvatar) { @@ -230,9 +272,11 @@ public class LevelTask(PlayerInstance player) subMission.SubMissionInfo.ParamIntList?.ForEach( async x => await Player.LineupManager!.AddAvatarToCurTeam(x)); } + + return null; } - public async ValueTask CreateHeroTrialPlayer(TaskConfigInfo act, SubMissionData subMission, + public async ValueTask CreateHeroTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (subMission.SubMissionInfo?.FinishType == MissionFinishTypeEnum.GetTrialAvatar) @@ -266,24 +310,30 @@ public class LevelTask(PlayerInstance player) list.ForEach(async x => await Player.LineupManager!.AddAvatarToCurTeam(x)); } + + return null; } - public async ValueTask DestroyTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask DestroyTrialPlayer(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (subMission.SubMissionInfo?.FinishType == MissionFinishTypeEnum.DelTrialAvatar) await Player.LineupManager!.RemoveAvatarFromCurTeam(subMission.SubMissionInfo.ParamInt1); + + return null; } - public async ValueTask ChangeGroupState(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask ChangeGroupState(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (group != null) foreach (var entity in Player.SceneInstance?.Entities.Values.ToList() ?? []) if (entity is EntityProp prop && prop.GroupId == group.Id) if (prop.Excel.PropStateList.Contains(PropStateEnum.Open)) await prop.SetState(PropStateEnum.Open); + + return null; } - public async ValueTask TriggerEntityServerEvent(TaskConfigInfo act, SubMissionData subMission, + public async ValueTask TriggerEntityServerEvent(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (group != null) @@ -292,9 +342,11 @@ public class LevelTask(PlayerInstance player) if (prop.Excel.PropStateList.Contains(PropStateEnum.Open) && (prop.State == PropStateEnum.Closed || prop.State == PropStateEnum.Locked)) await prop.SetState(PropStateEnum.Open); + + return null; } - public async ValueTask TriggerEntityEvent(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask TriggerEntityEvent(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is TriggerEntityEvent triggerEntityEvent) if (group != null) @@ -303,18 +355,22 @@ public class LevelTask(PlayerInstance player) prop.InstId == triggerEntityEvent.InstanceID.GetValue()) if (prop.Excel.PropStateList.Contains(PropStateEnum.Closed)) await prop.SetState(PropStateEnum.Closed); + + return null; } - public async ValueTask PropSetupUITrigger(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask PropSetupUITrigger(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is PropSetupUITrigger propSetupUiTrigger) foreach (var task in propSetupUiTrigger.ButtonCallback) TriggerTask(task, subMission, group); - await System.Threading.Tasks.Task.CompletedTask; + await ValueTask.CompletedTask; + + return null; } - public async ValueTask PropStateExecute(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask PropStateExecute(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { if (act is PropStateExecute propStateExecute) { @@ -328,14 +384,18 @@ public class LevelTask(PlayerInstance player) if (resp is EntityProp result) await result.SetState(propStateExecute.State); } } + + return null; } #endregion #region Task Condition - public bool ByCompareSubMissionState(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask ByCompareSubMissionState(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { + await ValueTask.CompletedTask; + if (act is ByCompareSubMissionState compare) { var mission = Player.MissionManager!.GetSubMissionStatus(compare.SubMissionID); @@ -345,8 +405,10 @@ public class LevelTask(PlayerInstance player) return false; } - public bool ByCompareFloorSavedValue(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) + public async ValueTask ByCompareFloorSavedValue(TaskConfigInfo act, SubMissionData subMission, GroupInfo? group = null) { + await ValueTask.CompletedTask; + if (act is ByCompareFloorSavedValue compare) { var value = Player.SceneData!.FloorSavedData.GetValueOrDefault(Player.Data.FloorId, []); diff --git a/GameServer/Server/ServerUtils.cs b/GameServer/Server/ServerUtils.cs index 5292b8de..ee3e37a2 100644 --- a/GameServer/Server/ServerUtils.cs +++ b/GameServer/Server/ServerUtils.cs @@ -1,5 +1,13 @@ -using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.Enums.Rogue; +using EggLink.DanhengServer.GameServer.Game.ChessRogue.Modifier.ModifierEffect; +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.GameServer.Game.Mission; +using EggLink.DanhengServer.GameServer.Game.Mission.FinishAction; +using EggLink.DanhengServer.GameServer.Game.Mission.FinishType; using EggLink.DanhengServer.GameServer.Game.MultiPlayer; +using EggLink.DanhengServer.GameServer.Game.Rogue.Event; +using System.Reflection; +using EggLink.DanhengServer.GameServer.Game.ChessRogue; namespace EggLink.DanhengServer.GameServer.Server; @@ -7,4 +15,63 @@ public static class ServerUtils { public static LobbyServerManager LobbyServerManager { get; set; } = new(); public static MultiPlayerGameServerManager MultiPlayerGameServerManager { get; set; } = new(); + + public static void InitializeHandlers() + { + // mission handlers + { + var types = Assembly.GetExecutingAssembly().GetTypes(); + foreach (var type in types) + { + var attr = type.GetCustomAttribute(); + if (attr != null) + { + var handler = (MissionFinishActionHandler)Activator.CreateInstance(type, null)!; + MissionManager.ActionHandlers.Add(attr.FinishAction, handler); + } + + var attr2 = type.GetCustomAttribute(); + if (attr2 != null) + { + var handler = (MissionFinishTypeHandler)Activator.CreateInstance(type, null)!; + MissionManager.FinishTypeHandlers.Add(attr2.FinishType, handler); + } + } + } + + // rogue event handlers + { + var types = Assembly.GetExecutingAssembly().GetTypes(); + foreach (var type in types) + { + var attr = type.GetCustomAttribute(); + if (attr == null) continue; + if (attr.EffectType != DialogueEventTypeEnum.None) + { + // Effect + var effect = (RogueEventEffectHandler)Activator.CreateInstance(type, null)!; + RogueEventManager.EffectHandler.Add(attr.EffectType, effect); + } + else + { + // Cost + var cost = (RogueEventCostHandler)Activator.CreateInstance(type, null)!; + RogueEventManager.CostHandler.Add(attr.CostType, cost); + } + } + } + + // chess rogue modifier handlers + { + var types = Assembly.GetExecutingAssembly().GetTypes(); + foreach (var type in types) + { + var attr = type.GetCustomAttribute(); + if (attr == null) continue; + + var handler = (ModifierEffectHandler)Activator.CreateInstance(type, null)!; + ChessRogueInstance.ModifierEffectHandlers.Add(attr.EffectType, handler); + } + } + } } \ No newline at end of file diff --git a/Program/Program/EntryPoint.cs b/Program/Program/EntryPoint.cs index 92c80b84..12dad28a 100644 --- a/Program/Program/EntryPoint.cs +++ b/Program/Program/EntryPoint.cs @@ -166,7 +166,8 @@ public class EntryPoint Logger.Info(I18NManager.Translate("Server.ServerInfo.ServerRunning", I18NManager.Translate("Word.Dispatch"), GetConfig().HttpServer.GetDisplayAddress())); - DanhengListener.BaseConnection = typeof(Connection); + var handler = new DanhengListener.ConnectionCreatedHandler((conversation, remote) => new Connection(conversation, remote)); + DanhengListener.CreateConnection = handler; DanhengListener.StartListener(); GenerateLogMap(); @@ -326,6 +327,8 @@ public class EntryPoint Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItem", I18NManager.Translate("Word.Database"))); } + ServerUtils.InitializeHandlers(); + // check if the database is up to date var updated = false; foreach (var avatarData in DatabaseHelper.GetAllInstanceFromMap()!)