From 904e907a6be57ec30ddbaafbf89315a7171e5e6d Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sun, 10 Nov 2024 15:13:20 -0600 Subject: [PATCH 01/22] fixed loading bug --- source/Common/LogicStates/ILogic.cs | 2 ++ source/Coop.Core/Client/ClientLogic.cs | 9 +++++++++ .../Coop.Core/Common/Network/CoopNetworkBase.cs | 7 ++++--- .../Coop.Core/CoopartiveMultiplayerExperience.cs | 15 ++++++++++++++- source/Coop.Core/Server/ServerLogic.cs | 4 ++++ .../Coop.Core/Server/States/InitialServerState.cs | 2 +- source/Coop/CoopMod.cs | 7 ++++++- .../MobilePartyAIs/Patches/PartyBehaviorPatch.cs | 5 ++++- .../Services/Time/Handlers/TimeControlHandler.cs | 6 +++--- .../Services/Time/Patches/TimePatches.cs | 6 +----- .../Services/Towns/Patches/TownPatches.cs | 6 ------ .../Services/Villages/Patches/VillagePatches.cs | 5 ----- .../Services/Workshops/Patches/WorkshopDisable.cs | 12 ------------ 13 files changed, 48 insertions(+), 38 deletions(-) delete mode 100644 source/GameInterface/Services/Workshops/Patches/WorkshopDisable.cs diff --git a/source/Common/LogicStates/ILogic.cs b/source/Common/LogicStates/ILogic.cs index 7633abd80..8216bc17f 100644 --- a/source/Common/LogicStates/ILogic.cs +++ b/source/Common/LogicStates/ILogic.cs @@ -4,5 +4,7 @@ public interface ILogic { void Start(); void Stop(); + + bool RunningState { get; } } } diff --git a/source/Coop.Core/Client/ClientLogic.cs b/source/Coop.Core/Client/ClientLogic.cs index 933692511..d17a583de 100644 --- a/source/Coop.Core/Client/ClientLogic.cs +++ b/source/Coop.Core/Client/ClientLogic.cs @@ -2,6 +2,8 @@ using Common.LogicStates; using Coop.Core.Client.States; using Serilog; +using System; +using System.Collections.Generic; namespace Coop.Core.Client; @@ -27,6 +29,11 @@ public class ClientLogic : IClientLogic public IStateFactory StateFactory { get; } public string ControlledHeroId { get; set; } private IClientState InitialState => StateFactory.CreateClientState(this); + private readonly HashSet RunningStates = new HashSet + { + typeof(MissionState), + typeof(CampaignState), + }; public IClientState State { get @@ -43,6 +50,8 @@ public IClientState State } } + public bool RunningState => RunningStates.Contains(_state.GetType()); + private IClientState _state; public ClientLogic(IStateFactory stateFactory) diff --git a/source/Coop.Core/Common/Network/CoopNetworkBase.cs b/source/Coop.Core/Common/Network/CoopNetworkBase.cs index 6c3a5d0a6..ed69d94ad 100644 --- a/source/Coop.Core/Common/Network/CoopNetworkBase.cs +++ b/source/Coop.Core/Common/Network/CoopNetworkBase.cs @@ -50,12 +50,13 @@ protected CoopNetworkBase(INetworkConfiguration configuration, ICommonSerializer public virtual void Dispose() { + netManager.Stop(); + + if (CancellationTokenSource.IsCancellationRequested) return; + CancellationTokenSource?.Cancel(); CancellationTokenSource?.Dispose(); UpdateThread?.Join(Configuration.ObjectCreationTimeout); - - netManager.DisconnectAll(); - netManager.Stop(); } private void UpdateThreadMethod() diff --git a/source/Coop.Core/CoopartiveMultiplayerExperience.cs b/source/Coop.Core/CoopartiveMultiplayerExperience.cs index 98c6c40a1..0d2f58f8a 100644 --- a/source/Coop.Core/CoopartiveMultiplayerExperience.cs +++ b/source/Coop.Core/CoopartiveMultiplayerExperience.cs @@ -9,10 +9,11 @@ using GameInterface; using GameInterface.Services.GameDebug.Messages; using GameInterface.Services.UI.Messages; +using System; namespace Coop.Core { - public class CoopartiveMultiplayerExperience + public class CoopartiveMultiplayerExperience : IDisposable { private readonly IMessageBroker messageBroker; private INetworkConfiguration configuration; @@ -30,6 +31,18 @@ public CoopartiveMultiplayerExperience() messageBroker.Subscribe(Handle); } + public bool Running { get + { + if (container == null) return false; + + var logic = container.Resolve(); + + return logic.RunningState; + } + } + + public void Dispose() => DestroyContainer(); + private void Handle(MessagePayload obj) { var connectMessage = obj.What; diff --git a/source/Coop.Core/Server/ServerLogic.cs b/source/Coop.Core/Server/ServerLogic.cs index f9576efd9..8e066785e 100644 --- a/source/Coop.Core/Server/ServerLogic.cs +++ b/source/Coop.Core/Server/ServerLogic.cs @@ -2,6 +2,7 @@ using Common.LogicStates; using Common.Messaging; using Common.Network; +using Coop.Core.Client.States; using Coop.Core.Server.Connections; using Coop.Core.Server.States; using Serilog; @@ -37,6 +38,9 @@ public IServerState State _state = value; } } + + public bool RunningState => _state is not InitialServerState; + private IServerState _state; public ServerLogic(IStateFactory stateFactory) diff --git a/source/Coop.Core/Server/States/InitialServerState.cs b/source/Coop.Core/Server/States/InitialServerState.cs index 7d64b73f5..a07b9d7f5 100644 --- a/source/Coop.Core/Server/States/InitialServerState.cs +++ b/source/Coop.Core/Server/States/InitialServerState.cs @@ -32,7 +32,7 @@ internal void Handle_GameLoaded(MessagePayload payload) network.Start(); // Remove server party - messageBroker.Publish(this, new RemoveMainParty()); + //messageBroker.Publish(this, new RemoveMainParty()); // Change to server running state Logic.SetState(); diff --git a/source/Coop/CoopMod.cs b/source/Coop/CoopMod.cs index 2474ce658..35e6db948 100644 --- a/source/Coop/CoopMod.cs +++ b/source/Coop/CoopMod.cs @@ -88,7 +88,7 @@ private void SetupLogging() public override void NoHarmonyLoad() { - Coop = new CoopartiveMultiplayerExperience(); + Coop = new CoopartiveMultiplayerExperience(); Updateables.Add(GameLoopRunner.Instance); @@ -162,6 +162,11 @@ protected override void OnBeforeInitialModuleScreenSetAsRoot() public override void OnGameEnd(Game game) { base.OnGameEnd(game); + + if (Coop.Running) + { + Coop.Dispose(); + } } private bool m_IsFirstTick = true; diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/PartyBehaviorPatch.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/PartyBehaviorPatch.cs index 080441cab..9f0a062f3 100644 --- a/source/GameInterface/Services/MobilePartyAIs/Patches/PartyBehaviorPatch.cs +++ b/source/GameInterface/Services/MobilePartyAIs/Patches/PartyBehaviorPatch.cs @@ -1,6 +1,7 @@ using Common.Logging; using Common.Messaging; using GameInterface.Services.MobileParties.Data; +using GameInterface.Services.MobileParties.Extensions; using GameInterface.Services.MobileParties.Handlers; using GameInterface.Services.MobileParties.Messages.Behavior; using HarmonyLib; @@ -52,6 +53,8 @@ private static bool SetAiBehaviorPrefix( { if (BehaviorIsSame(ref __instance, ref newAiBehavior, ref targetPartyFigure, ref bestTargetPoint)) return false; + if (__instance._mobileParty.IsPartyControlled() == false) return false; + MobileParty party = __instance._mobileParty; bool hasTargetEntity = false; @@ -83,7 +86,7 @@ private static bool BehaviorIsSame( if (targetPartyFigure != null) { - targetEntity = targetPartyFigure.IsSettlement ? targetPartyFigure.MobileParty : targetPartyFigure.Settlement; + targetEntity = targetPartyFigure.IsSettlement ? targetPartyFigure.Settlement : targetPartyFigure.MobileParty; } return __instance.AiBehaviorMapEntity == targetEntity && diff --git a/source/GameInterface/Services/Time/Handlers/TimeControlHandler.cs b/source/GameInterface/Services/Time/Handlers/TimeControlHandler.cs index 67cddeab2..45924ca2b 100644 --- a/source/GameInterface/Services/Time/Handlers/TimeControlHandler.cs +++ b/source/GameInterface/Services/Time/Handlers/TimeControlHandler.cs @@ -1,4 +1,5 @@ using Common.Messaging; +using Common.Network; using GameInterface.Services.Heroes.Interaces; using GameInterface.Services.Heroes.Messages; using System; @@ -13,7 +14,8 @@ internal class TimeControlHandler : IHandler public TimeControlHandler( ITimeControlInterface timeControlInterface, - IMessageBroker messageBroker) + IMessageBroker messageBroker, + INetwork network) { this.timeControlInterface = timeControlInterface; this.messageBroker = messageBroker; @@ -40,7 +42,5 @@ private void Handle(MessagePayload obj) var payload = obj.What; timeControlInterface.SetTimeControl(payload.NewTimeMode); - - messageBroker.Respond(obj.Who, new TimeControlModeSet(payload.NewTimeMode)); } } diff --git a/source/GameInterface/Services/Time/Patches/TimePatches.cs b/source/GameInterface/Services/Time/Patches/TimePatches.cs index ba5c0e1ba..52567e91b 100644 --- a/source/GameInterface/Services/Time/Patches/TimePatches.cs +++ b/source/GameInterface/Services/Time/Patches/TimePatches.cs @@ -1,14 +1,10 @@ -using Common.Extensions; -using Common.Messaging; +using Common.Messaging; using Common.Util; using GameInterface.Services.Heroes.Messages; using GameInterface.Services.Time; using HarmonyLib; using SandBox.View.Map; -using System; using System.Collections.Generic; -using System.Linq; -using System.Reflection; using System.Reflection.Emit; using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Encounters; diff --git a/source/GameInterface/Services/Towns/Patches/TownPatches.cs b/source/GameInterface/Services/Towns/Patches/TownPatches.cs index 36ce75ccf..a5d102f64 100644 --- a/source/GameInterface/Services/Towns/Patches/TownPatches.cs +++ b/source/GameInterface/Services/Towns/Patches/TownPatches.cs @@ -25,7 +25,6 @@ public class TownPatches [HarmonyPrefix] private static bool TownGovernorPrefix(ref Town __instance, ref Hero value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -53,7 +52,6 @@ public static void ChangeTownGovernor(Town town, Hero governor) [HarmonyPrefix] private static bool TownProsperityPrefix(ref Town __instance, ref float value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -80,7 +78,6 @@ public static void ChangeTownProsperity(Town town, float prosperity) [HarmonyPrefix] private static bool TownLoyaltyPrefix(ref Town __instance, ref float value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -132,7 +129,6 @@ public static void ChangeTownSecurity(Town town, float security) [HarmonyPrefix] private static bool TownLastCapturedByPrefix(ref Town __instance, ref Clan value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -175,7 +171,6 @@ public static void ChangeTownGarrisonAutoRecruitmentIsEnabled(Town town, bool ga [HarmonyPrefix] private static bool TownTradeTaxAccumulatedPrefix(ref Town __instance, ref int value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -201,7 +196,6 @@ public static void ChangeTradeTaxAccumulated(Town town, int tradeTaxAccumulated) [HarmonyPrefix] private static bool SetSoldItemsPrefix(Town __instance, IEnumerable logList) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; diff --git a/source/GameInterface/Services/Villages/Patches/VillagePatches.cs b/source/GameInterface/Services/Villages/Patches/VillagePatches.cs index 02d125f7f..b453c6a13 100644 --- a/source/GameInterface/Services/Villages/Patches/VillagePatches.cs +++ b/source/GameInterface/Services/Villages/Patches/VillagePatches.cs @@ -28,7 +28,6 @@ internal class VillagePatches [HarmonyPrefix] private static bool VillageStatePrefix(ref Village __instance, ref VillageStates value) { - if(AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -63,7 +62,6 @@ public static void RunVillageStateChange(Village village, VillageStates state) [HarmonyPrefix] private static bool HearthPrefix(ref Village __instance, ref float value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -88,7 +86,6 @@ public static void ChangeHearth(Village village, float Hearth) [HarmonyPrefix] private static bool TradeBoundPrefix(ref Village __instance, ref Settlement value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -117,7 +114,6 @@ internal static void RunTradeBoundChange(Village village, Settlement tradebound) [HarmonyPrefix] private static bool TradeTaxAccumulatedPrefix(ref Village __instance, ref int value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -142,7 +138,6 @@ internal static void RunTradeTaxChange(Village village, int tradeTaxAccumulated) [HarmonyPrefix] private static bool LastDemandSatisifiedTimePrefix(ref Village __instance, ref float value) { - if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; diff --git a/source/GameInterface/Services/Workshops/Patches/WorkshopDisable.cs b/source/GameInterface/Services/Workshops/Patches/WorkshopDisable.cs deleted file mode 100644 index 53192fd00..000000000 --- a/source/GameInterface/Services/Workshops/Patches/WorkshopDisable.cs +++ /dev/null @@ -1,12 +0,0 @@ -using HarmonyLib; -using TaleWorlds.CampaignSystem.Settlements.Workshops; - -namespace GameInterface.Services.Workshops.Patches -{ - [HarmonyPatch(typeof(Workshop))] - internal class WorkshopDisable - { - [HarmonyPatch(nameof(Workshop.AfterLoad))] - static bool Prefix() => ModInformation.IsServer; - } -} From 684de091b4917fa93798448637e633e27c3b2e5a Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sun, 10 Nov 2024 17:48:35 -0600 Subject: [PATCH 02/22] loadin fix --- source/Coop.Core/Server/States/InitialServerState.cs | 2 +- .../Services/Workshops/WorkshopTypeRegistry.cs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/source/Coop.Core/Server/States/InitialServerState.cs b/source/Coop.Core/Server/States/InitialServerState.cs index a07b9d7f5..7d64b73f5 100644 --- a/source/Coop.Core/Server/States/InitialServerState.cs +++ b/source/Coop.Core/Server/States/InitialServerState.cs @@ -32,7 +32,7 @@ internal void Handle_GameLoaded(MessagePayload payload) network.Start(); // Remove server party - //messageBroker.Publish(this, new RemoveMainParty()); + messageBroker.Publish(this, new RemoveMainParty()); // Change to server running state Logic.SetState(); diff --git a/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs b/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs index 25935605f..f0bad3dc7 100644 --- a/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs +++ b/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs @@ -19,10 +19,11 @@ public WorkshopTypeRegistry(IRegistryCollection collection) : base(collection) { public override void RegisterAll() { - foreach(WorkshopType workshopType in WorkshopType.All) - { - RegisterNewObject(workshopType, out var _); - } + // THIS BREAKS SAVING AND LOADING FOR SOME REASON, DO NOT UNCOMMENT UNTIL WE FIGURE OUT WHY AND HOW TO FIX + //foreach(WorkshopType workshopType in WorkshopType.All) + //{ + // RegisterNewObject(workshopType, out var _); + //} } protected override string GetNewId(WorkshopType party) From c4ef8c2518b42688bfd84c82c17e588cf5478530 Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Fri, 15 Nov 2024 18:23:50 +0100 Subject: [PATCH 03/22] Start --- .../Services/Heroes/HeroPropertyTests.cs | 61 +++++++++++++++++++ .../GameInterface/Services/Heroes/HeroSync.cs | 19 ++++++ 2 files changed, 80 insertions(+) create mode 100644 source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs create mode 100644 source/GameInterface/Services/Heroes/HeroSync.cs diff --git a/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs b/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs new file mode 100644 index 000000000..45c39c6fe --- /dev/null +++ b/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs @@ -0,0 +1,61 @@ +using E2E.Tests.Environment; +using E2E.Tests.Environment.Instance; +using E2E.Tests.Util; +using System.Runtime.InteropServices; +using TaleWorlds.CampaignSystem; +using TaleWorlds.CampaignSystem.Settlements.Buildings; +using TaleWorlds.CampaignSystem.Settlements; +using Xunit.Abstractions; +using TaleWorlds.Core; +using Autofac.Features.OwnedInstances; + +namespace E2E.Tests.Services.Heroes +{ + public class HeroPropertyTests : IDisposable + { + E2ETestEnvironment TestEnvironment { get; } + + EnvironmentInstance Server => TestEnvironment.Server; + + IEnumerable Clients => TestEnvironment.Clients; + + private string HeroId; + + StaticBodyProperties body = new StaticBodyProperties(1, 2, 1, 2, 1, 3, 1, 2); + + public HeroPropertyTests(ITestOutputHelper output) + { + TestEnvironment = new E2ETestEnvironment(output); + } + + public void Dispose() + { + TestEnvironment.Dispose(); + } + + [Fact] + public void Server_Sync_Hero() + { + var server = TestEnvironment.Server; + Hero hero = null; + + server.Call(() => + { + hero = GameObjectCreator.CreateInitializedObject(); + + hero.StaticBodyProperties = body; + + Assert.Equal(body, hero.StaticBodyProperties); + + Assert.True(server.ObjectManager.TryGetId(hero, out HeroId)); + }); + + foreach (var client in Clients) + { + Assert.True(client.ObjectManager.TryGetObject(HeroId, out var clientHero)); + + Assert.Equal(body, clientHero.StaticBodyProperties); + } + } + } +} diff --git a/source/GameInterface/Services/Heroes/HeroSync.cs b/source/GameInterface/Services/Heroes/HeroSync.cs new file mode 100644 index 000000000..b510eb240 --- /dev/null +++ b/source/GameInterface/Services/Heroes/HeroSync.cs @@ -0,0 +1,19 @@ +using GameInterface.AutoSync; +using HarmonyLib; +using TaleWorlds.CampaignSystem; + +namespace GameInterface.Services.Heroes +{ + internal class HeroSync : IAutoSync + { + public HeroSync(IAutoSyncBuilder autoSyncBuilder) + { + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.StaticBodyProperties))); + + + + + + } + } +} From 2e1da35e65c0c27bd9f4a20f43e9888bd812b8b7 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 23 Nov 2024 16:06:07 -0600 Subject: [PATCH 04/22] Updated saving --- .../Server/Services/Save/CoopSaveManager.cs | 3 +- .../Server/Services/Save/Data/CoopSession.cs | 33 ++++++++++++---- .../Services/Save/Handlers/SaveGameHandler.cs | 38 ++++++++----------- .../Coop.Tests/Autofac/ContainerBuildTests.cs | 6 ++- .../Server/Services/Save/JSONSessionTests.cs | 14 ++++--- .../Services/Save/SaveLoadCoopSessionTests.cs | 35 +++++++++++------ source/Coop.Tests/ServerTestComponent.cs | 3 ++ .../GameInterface/Properties/AssemblyInfo.cs | 3 +- .../Entity/ControlledEntityRegistry.cs | 37 +++++++++++++++--- .../Services/Entity/Data/ControlledEntity.cs | 23 ++++++++++- .../Save/Messages/LoadExistingObjectGuids.cs | 20 ---------- .../Surrogates/SurrogateCollection.cs | 16 ++++++-- 12 files changed, 150 insertions(+), 81 deletions(-) delete mode 100644 source/GameInterface/Services/Save/Messages/LoadExistingObjectGuids.cs diff --git a/source/Coop.Core/Server/Services/Save/CoopSaveManager.cs b/source/Coop.Core/Server/Services/Save/CoopSaveManager.cs index 6d5d4c79a..16d1469ae 100644 --- a/source/Coop.Core/Server/Services/Save/CoopSaveManager.cs +++ b/source/Coop.Core/Server/Services/Save/CoopSaveManager.cs @@ -2,6 +2,7 @@ using Common.Serialization; using Coop.Core.Server.Services.Save.Data; using System; +using System.Collections; using System.IO; namespace Coop.Core.Server.Services.Save @@ -17,7 +18,7 @@ internal interface ICoopSaveManager internal class CoopSaveManager : ICoopSaveManager { - public string DefaultPath { get; } = "./Saves/"; + public string DefaultPath { get; } = "./saves/"; public string FileType { get; } = ".json"; /// diff --git a/source/Coop.Core/Server/Services/Save/Data/CoopSession.cs b/source/Coop.Core/Server/Services/Save/Data/CoopSession.cs index c9c4351a9..ac99d6ac3 100644 --- a/source/Coop.Core/Server/Services/Save/Data/CoopSession.cs +++ b/source/Coop.Core/Server/Services/Save/Data/CoopSession.cs @@ -1,5 +1,10 @@ -using GameInterface.Services.Heroes.Data; +using Autofac.Features.OwnedInstances; +using Common; +using GameInterface.Services.Entity.Data; +using GameInterface.Services.Heroes.Data; using ProtoBuf; +using System.Collections.Generic; +using System.Linq; namespace Coop.Core.Server.Services.Save.Data; @@ -9,8 +14,8 @@ namespace Coop.Core.Server.Services.Save.Data; /// public interface ICoopSession { - string UniqueGameId { get; set; } - GameObjectGuids GameObjectGuids { get; set; } + string UniqueGameId { get; } + Dictionary> ControlledEntityMap { get; } } /// @@ -18,9 +23,15 @@ public interface ICoopSession public class CoopSession : ICoopSession { [ProtoMember(1)] - public string UniqueGameId { get; set; } + public string UniqueGameId { get; } [ProtoMember(2)] - public GameObjectGuids GameObjectGuids { get; set; } + public Dictionary> ControlledEntityMap { get; } + + public CoopSession(string uniqueGameId, Dictionary> controlledEntityMap) + { + UniqueGameId = uniqueGameId; + ControlledEntityMap = controlledEntityMap; + } public override bool Equals(object obj) { @@ -28,13 +39,21 @@ public override bool Equals(object obj) if (UniqueGameId != session.UniqueGameId) return false; - if (GameObjectGuids.Equals(session.GameObjectGuids) == false) return false; + if (ControlledEntityMap.Count != session.ControlledEntityMap.Count) return false; + + if (ControlledEntityMap.Zip(session.ControlledEntityMap, (l, r) => + { + return l.Key == r.Key && l.Value.SetEquals(r.Value); + }).All(x => x) == false) return false; return true; } public override int GetHashCode() { - return base.GetHashCode(); + int hash = 1236898; + hash = hash * 31 + UniqueGameId.GetHashCode(); + hash = hash * 31 + ControlledEntityMap.GetHashCode(); + return hash; } } diff --git a/source/Coop.Core/Server/Services/Save/Handlers/SaveGameHandler.cs b/source/Coop.Core/Server/Services/Save/Handlers/SaveGameHandler.cs index 22d83bf39..4ab5e66f1 100644 --- a/source/Coop.Core/Server/Services/Save/Handlers/SaveGameHandler.cs +++ b/source/Coop.Core/Server/Services/Save/Handlers/SaveGameHandler.cs @@ -16,19 +16,21 @@ internal class SaveGameHandler : IHandler private readonly ICoopSaveManager saveManager; private readonly ICoopServer coopServer; private readonly IControllerIdProvider controllerIdProvider; + private readonly IControlledEntityRegistry controlledEntityRegistry; public SaveGameHandler( IMessageBroker messageBroker, ICoopSaveManager saveManager, ICoopServer coopServer, - IControllerIdProvider controllerIdProvider) + IControllerIdProvider controllerIdProvider, + IControlledEntityRegistry controlledEntityRegistry) { this.messageBroker = messageBroker; this.saveManager = saveManager; this.coopServer = coopServer; this.controllerIdProvider = controllerIdProvider; + this.controlledEntityRegistry = controlledEntityRegistry; messageBroker.Subscribe(Handle_GameSaved); - messageBroker.Subscribe(Handle_ObjectGuidsPackaged); messageBroker.Subscribe(Handle_GameLoaded); messageBroker.Subscribe(Handle_CampaignLoaded); @@ -38,28 +40,20 @@ public SaveGameHandler( public void Dispose() { messageBroker.Unsubscribe(Handle_GameSaved); - messageBroker.Unsubscribe(Handle_ObjectGuidsPackaged); messageBroker.Unsubscribe(Handle_GameLoaded); messageBroker.Unsubscribe(Handle_CampaignLoaded); messageBroker.Unsubscribe(Handle_AllGameObjectsRegistered); } - private string saveName; private void Handle_GameSaved(MessagePayload obj) { - saveName = obj.What.SaveName; + var saveName = obj.What.SaveName; messageBroker.Publish(this, new PackageObjectGuids()); - } - private void Handle_ObjectGuidsPackaged(MessagePayload obj) - { - var payload = obj.What; - CoopSession session = new CoopSession() - { - UniqueGameId = payload.UniqueGameId, - GameObjectGuids = payload.GameObjectGuids, - }; + var controlledEntities = controlledEntityRegistry.PackageControlledEntities(); + + CoopSession session = new CoopSession(saveName, controlledEntities); saveManager.SaveCoopSession(saveName, session); } @@ -71,20 +65,20 @@ private void Handle_GameLoaded(MessagePayload obj) } private void Handle_CampaignLoaded(MessagePayload obj) + { + // Register all game objects with our object manager + messageBroker.Publish(this, new RegisterAllGameObjects()); + } + + private void Handle_AllGameObjectsRegistered(MessagePayload obj) { if (savedSession == null) { - messageBroker.Publish(this, new RegisterAllGameObjects()); + messageBroker.Publish(this, new RegisterAllPartiesAsControlled(controllerIdProvider.ControllerId)); } else { - var message = new LoadExistingObjectGuids(savedSession.GameObjectGuids); - messageBroker.Publish(this, message); + controlledEntityRegistry.LoadControlledEntities(savedSession.ControlledEntityMap); } } - - private void Handle_AllGameObjectsRegistered(MessagePayload obj) - { - messageBroker.Publish(this, new RegisterAllPartiesAsControlled(controllerIdProvider.ControllerId)); - } } diff --git a/source/Coop.Tests/Autofac/ContainerBuildTests.cs b/source/Coop.Tests/Autofac/ContainerBuildTests.cs index 386d47994..eee66aa24 100644 --- a/source/Coop.Tests/Autofac/ContainerBuildTests.cs +++ b/source/Coop.Tests/Autofac/ContainerBuildTests.cs @@ -4,6 +4,7 @@ using Coop.Core; using Coop.Core.Client; using Coop.Core.Server; +using GameInterface; using Xunit; namespace Coop.Tests.Autofac @@ -13,7 +14,7 @@ public class ContainerBuildTests [Fact] public void Client_Container_Build() { - var containerProvider = new ContainerProvider(); + var containerProvider = new Core.ContainerProvider(); ContainerBuilder builder = new ContainerBuilder(); builder.RegisterModule(); @@ -34,10 +35,11 @@ public void Client_Container_Build() [Fact] public void Server_Container_Build() { - var containerProvider = new ContainerProvider(); + var containerProvider = new Core.ContainerProvider(); ContainerBuilder builder = new ContainerBuilder(); builder.RegisterModule(); + builder.RegisterModule(); builder.RegisterInstance(containerProvider).As(); var container = builder.Build(); diff --git a/source/Coop.Tests/Server/Services/Save/JSONSessionTests.cs b/source/Coop.Tests/Server/Services/Save/JSONSessionTests.cs index 9dd8e8afc..41e3cf7ae 100644 --- a/source/Coop.Tests/Server/Services/Save/JSONSessionTests.cs +++ b/source/Coop.Tests/Server/Services/Save/JSONSessionTests.cs @@ -1,5 +1,6 @@ using Common.Serialization; using Coop.Core.Server.Services.Save.Data; +using GameInterface.Services.Entity; using GameInterface.Services.Heroes.Data; using System; using System.Collections.Generic; @@ -26,11 +27,14 @@ public void SaveLoadSessions() { var gameObjectGuids = new GameObjectGuids(new string[] { "Random STR" }); - ICoopSession sessionData = new CoopSession() - { - UniqueGameId = "TestId", - GameObjectGuids = gameObjectGuids, - }; + var entityRegistry = new ControlledEntityRegistry(); + + const string controllerId = "testController"; + const string entityId = "testEntity1"; + + Assert.True(entityRegistry.RegisterAsControlled(controllerId, entityId)); + + var sessionData = new CoopSession("TestId", entityRegistry.PackageControlledEntities()); string saveFile = SAVE_PATH + sessionData.UniqueGameId + ".json"; diff --git a/source/Coop.Tests/Server/Services/Save/SaveLoadCoopSessionTests.cs b/source/Coop.Tests/Server/Services/Save/SaveLoadCoopSessionTests.cs index a7761ab33..eeba5e3eb 100644 --- a/source/Coop.Tests/Server/Services/Save/SaveLoadCoopSessionTests.cs +++ b/source/Coop.Tests/Server/Services/Save/SaveLoadCoopSessionTests.cs @@ -3,6 +3,7 @@ using Coop.Core.Server; using Coop.Core.Server.Services.Save; using Coop.Core.Server.Services.Save.Data; +using GameInterface.Services.Entity; using GameInterface.Services.Heroes.Data; using System; using System.Collections.Generic; @@ -34,14 +35,19 @@ public void SaveSession() { // Setup var saveManager = container.Resolve(); + var entityRegistry = container.Resolve(); - var gameObjectGuids = new GameObjectGuids(new string[] { "Random STR" }); + const string controllerId = "testController"; + const string controller2Id = "testController2"; + const string entityId = "testEntity1"; + const string entity2Id = "testEntity2"; - ICoopSession sessionData = new CoopSession() - { - UniqueGameId = "SaveManagerTest", - GameObjectGuids = gameObjectGuids - }; + Assert.True(entityRegistry.RegisterAsControlled(controllerId, entityId)); + Assert.True(entityRegistry.RegisterAsControlled(controller2Id, entity2Id)); + + var entityMap = entityRegistry.PackageControlledEntities(); + + ICoopSession sessionData = new CoopSession("SaveManagerTest", entityMap); string saveFile = sessionData.UniqueGameId; @@ -67,14 +73,19 @@ public void SaveLoadSession() { // Setup var saveManager = container.Resolve(); + var entityRegistry = container.Resolve(); - var gameObjectGuids = new GameObjectGuids(new string[] { "Random STR" }); + const string controllerId = "testController"; + const string controller2Id = "testController2"; + const string entityId = "testEntity1"; + const string entity2Id = "testEntity2"; - ICoopSession sessionData = new CoopSession() - { - UniqueGameId = "SaveLoadManagerTest", - GameObjectGuids = gameObjectGuids, - }; + Assert.True(entityRegistry.RegisterAsControlled(controllerId, entityId)); + Assert.True(entityRegistry.RegisterAsControlled(controller2Id, entity2Id)); + + var entityMap = entityRegistry.PackageControlledEntities(); + + ICoopSession sessionData = new CoopSession("SaveManagerTest", entityMap); string saveFile = SAVE_PATH + sessionData.UniqueGameId; diff --git a/source/Coop.Tests/ServerTestComponent.cs b/source/Coop.Tests/ServerTestComponent.cs index cf82a1fba..beff4c2ce 100644 --- a/source/Coop.Tests/ServerTestComponent.cs +++ b/source/Coop.Tests/ServerTestComponent.cs @@ -1,5 +1,7 @@ using Autofac; using Coop.Core.Server; +using GameInterface; +using GameInterface.Services.Entity; using Xunit.Abstractions; namespace Coop.Tests; @@ -10,6 +12,7 @@ public ServerTestComponent(ITestOutputHelper output) : base(output) { var builder = new ContainerBuilder(); builder.RegisterModule(); + builder.RegisterType().As().InstancePerLifetimeScope(); Container = BuildContainer(builder); } } diff --git a/source/GameInterface/Properties/AssemblyInfo.cs b/source/GameInterface/Properties/AssemblyInfo.cs index b99533946..64fbece1c 100644 --- a/source/GameInterface/Properties/AssemblyInfo.cs +++ b/source/GameInterface/Properties/AssemblyInfo.cs @@ -29,4 +29,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: InternalsVisibleTo("GameInterface.Tests")] +[assembly: InternalsVisibleTo("Coop.Tests")] +[assembly: InternalsVisibleTo("GameInterface.Tests")] \ No newline at end of file diff --git a/source/GameInterface/Services/Entity/ControlledEntityRegistry.cs b/source/GameInterface/Services/Entity/ControlledEntityRegistry.cs index b09c8fe8f..c64ac8c52 100644 --- a/source/GameInterface/Services/Entity/ControlledEntityRegistry.cs +++ b/source/GameInterface/Services/Entity/ControlledEntityRegistry.cs @@ -9,6 +9,7 @@ using System.Collections.ObjectModel; using System.Linq; using TaleWorlds.Core; +using TaleWorlds.Library; namespace GameInterface.Services.Entity; @@ -22,7 +23,15 @@ public interface IControlledEntityRegistry /// Packages an immutable dictionary of controlled entities /// /// Immutable dictionary of controlled entities - IReadOnlyDictionary> PackageControlledEntities(); + Dictionary> PackageControlledEntities(); + + /// + /// Attempts to register all provided entities. + /// + /// Is meant to be used to load from save file + /// + /// Entities to load + void LoadControlledEntities(Dictionary> controlledEntityMap); /// /// Registers an Enumerable of entities with the registry @@ -89,12 +98,30 @@ internal class ControlledEntityRegistry : IControlledEntityRegistry [ProtoMember(2)] private readonly ConcurrentDictionary controllerIdLookup = new ConcurrentDictionary(); - public IReadOnlyDictionary> PackageControlledEntities() + public Dictionary> PackageControlledEntities() => controlledEntities.ToDictionary(k => k.Key, v => v.Value); + + public void LoadControlledEntities(Dictionary> controlledEntityMap) { - // Make dictionary immutable - var readonlyListDict = controlledEntities.ToDictionary(k => k.Key, k => k.Value.AsReadOnly() as IReadOnlySet); + if (controlledEntities.Count != 0) + { + Logger.Error($"Attempted to over-write existing controlled entity collection: ${nameof(controlledEntities)}"); + return; + } - return new ReadOnlyDictionary>(readonlyListDict); + if (controllerIdLookup.Count != 0) + { + Logger.Error($"Attempted to over-write existing controlled entity collection: ${nameof(controllerIdLookup)}"); + return; + } + + foreach(var entitySet in controlledEntityMap) + { + var controllerId = entitySet.Key; + foreach (var entity in entitySet.Value) + { + RegisterAsControlled(controllerId, entity.EntityId); + } + } } public void RegisterExistingEntities(IEnumerable entityIds) diff --git a/source/GameInterface/Services/Entity/Data/ControlledEntity.cs b/source/GameInterface/Services/Entity/Data/ControlledEntity.cs index 0684447de..0212e4c29 100644 --- a/source/GameInterface/Services/Entity/Data/ControlledEntity.cs +++ b/source/GameInterface/Services/Entity/Data/ControlledEntity.cs @@ -1,6 +1,6 @@ using Common.Logging; +using ProtoBuf; using Serilog; -using System; using TaleWorlds.ObjectSystem; namespace GameInterface.Services.Entity.Data; @@ -8,6 +8,7 @@ namespace GameInterface.Services.Entity.Data; /// /// Controllable entity and owner /// +[ProtoContract(SkipConstructor = true)] public class ControlledEntity { private static readonly ILogger Logger = LogManager.GetLogger(); @@ -16,6 +17,7 @@ public class ControlledEntity /// Id of owner of the controlled entity. /// This can be either a client or server. /// + [ProtoMember(1)] public string OwnerId { get; } /// @@ -24,6 +26,7 @@ public class ControlledEntity /// /// This will normally be the StringId from the class /// + [ProtoMember(2)] public string EntityId { get; } public ControlledEntity(string ownerId, string entityId) @@ -50,5 +53,21 @@ public override bool Equals(object obj) return OwnerId == controlledEntity.OwnerId && EntityId == controlledEntity.EntityId; } - public override int GetHashCode() => base.GetHashCode(); + public static bool operator ==(ControlledEntity obj1, ControlledEntity obj2) + { + return obj1.Equals(obj2); + } + + public static bool operator !=(ControlledEntity obj1, ControlledEntity obj2) + { + return !obj1.Equals(obj2); + } + + public override int GetHashCode() + { + int hash = 552523; + hash = hash * 31 + OwnerId.GetHashCode(); + hash = hash * 31 + EntityId.GetHashCode(); + return hash; + } } diff --git a/source/GameInterface/Services/Save/Messages/LoadExistingObjectGuids.cs b/source/GameInterface/Services/Save/Messages/LoadExistingObjectGuids.cs deleted file mode 100644 index 3d71a4598..000000000 --- a/source/GameInterface/Services/Save/Messages/LoadExistingObjectGuids.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Common.Messaging; -using GameInterface.Services.Heroes.Data; -using System; - -namespace GameInterface.Services.Heroes.Messages; - -public record LoadExistingObjectGuids : ICommand -{ - public GameObjectGuids GameObjectGuids { get; } - - public LoadExistingObjectGuids( - GameObjectGuids gameObjectGuids) - { - GameObjectGuids = gameObjectGuids; - } -} - -public record ExistingObjectGuidsLoaded : IResponse -{ -} diff --git a/source/GameInterface/Surrogates/SurrogateCollection.cs b/source/GameInterface/Surrogates/SurrogateCollection.cs index 38cfd5046..2f13570c7 100644 --- a/source/GameInterface/Surrogates/SurrogateCollection.cs +++ b/source/GameInterface/Surrogates/SurrogateCollection.cs @@ -1,4 +1,5 @@ using ProtoBuf.Meta; +using System; using TaleWorlds.Library; using TaleWorlds.Localization; @@ -10,10 +11,17 @@ internal class SurrogateCollection : ISurrogateCollection { public SurrogateCollection() { - if (RuntimeTypeModel.Default.CanSerialize(typeof(Vec2)) == false) - RuntimeTypeModel.Default.SetSurrogate(); + AddSurrogate(); + AddSurrogate(); + } - if (RuntimeTypeModel.Default.CanSerialize(typeof(TextObject)) == false) - RuntimeTypeModel.Default.SetSurrogate(); + private void AddSurrogate() + { + try + { + RuntimeTypeModel.Default.SetSurrogate(); + } + catch (InvalidOperationException) { } + catch (ArgumentException) { } } } From 35dabea1590f20f4cf3bbdfbcab9941597c24244 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 23 Nov 2024 16:16:23 -0600 Subject: [PATCH 05/22] test fix --- .../Environment/Mock/MockControlledEntityRegistry.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/E2E.Tests/Environment/Mock/MockControlledEntityRegistry.cs b/source/E2E.Tests/Environment/Mock/MockControlledEntityRegistry.cs index 420bb9bd6..fffad240b 100644 --- a/source/E2E.Tests/Environment/Mock/MockControlledEntityRegistry.cs +++ b/source/E2E.Tests/Environment/Mock/MockControlledEntityRegistry.cs @@ -10,7 +10,12 @@ public bool IsControlledBy(string controllerId, string entityId) throw new NotImplementedException(); } - public IReadOnlyDictionary> PackageControlledEntities() + public void LoadControlledEntities(Dictionary> controlledEntityMap) + { + throw new NotImplementedException(); + } + + public Dictionary> PackageControlledEntities() { throw new NotImplementedException(); } From 25ac939a75a895085927f28e9a731b49b4d7c428 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 23 Nov 2024 17:15:07 -0600 Subject: [PATCH 06/22] Saving disabled for client and minor party cleanup --- .../Interfaces/GameStateInterface.cs | 2 +- .../Handlers/PartyComponentHandler.cs | 41 --------------- .../NetworkChangePartyComponentMobileParty.cs | 18 ------- .../PartyComponentMobilePartyChanged.cs | 10 ---- .../PartyComponents/PartyComponentSync.cs | 12 +++++ .../Patches/PartyComponentPatches.cs | 50 ------------------- .../Services/Save/Patches/SavePatches.cs | 6 ++- 7 files changed, 18 insertions(+), 121 deletions(-) delete mode 100644 source/GameInterface/Services/PartyComponents/Messages/NetworkChangePartyComponentMobileParty.cs delete mode 100644 source/GameInterface/Services/PartyComponents/Messages/PartyComponentMobilePartyChanged.cs create mode 100644 source/GameInterface/Services/PartyComponents/PartyComponentSync.cs delete mode 100644 source/GameInterface/Services/PartyComponents/Patches/PartyComponentPatches.cs diff --git a/source/GameInterface/Services/GameState/Interfaces/GameStateInterface.cs b/source/GameInterface/Services/GameState/Interfaces/GameStateInterface.cs index 55b5fff6f..9bf643080 100644 --- a/source/GameInterface/Services/GameState/Interfaces/GameStateInterface.cs +++ b/source/GameInterface/Services/GameState/Interfaces/GameStateInterface.cs @@ -30,7 +30,7 @@ public void EnterMainMenu() public void LoadSaveGame(byte[] saveData) { - GameLoopRunner.RunOnMainThread(() => InteralLoadSaveGame(saveData)); + GameLoopRunner.RunOnMainThread(() => InteralLoadSaveGame(saveData), blocking: true); } private void InteralLoadSaveGame(byte[] saveData) diff --git a/source/GameInterface/Services/PartyComponents/Handlers/PartyComponentHandler.cs b/source/GameInterface/Services/PartyComponents/Handlers/PartyComponentHandler.cs index c167658a7..6332f87ed 100644 --- a/source/GameInterface/Services/PartyComponents/Handlers/PartyComponentHandler.cs +++ b/source/GameInterface/Services/PartyComponents/Handlers/PartyComponentHandler.cs @@ -39,53 +39,12 @@ public PartyComponentHandler(IMessageBroker messageBroker, INetwork network, IOb this.objectManager = objectManager; messageBroker.Subscribe(Handle); messageBroker.Subscribe(Handle); - - messageBroker.Subscribe(Handle); - messageBroker.Subscribe(Handle); } public void Dispose() { messageBroker.Unsubscribe(Handle); messageBroker.Unsubscribe(Handle); - - messageBroker.Unsubscribe(Handle); - messageBroker.Unsubscribe(Handle); - } - - private void Handle(MessagePayload payload) - { - var componentId = payload.What.ComponentId; - var partyId = payload.What.PartyId; - - if(objectManager.TryGetObject(componentId, out var component) == false) - { - Logger.Error("Could not find PartyComponent with id {componentId}", componentId); - return; - } - - if (objectManager.TryGetObject(partyId, out var party) == false) - { - Logger.Error("Could not find MobileParty with id {componentId}", partyId); - return; - } - - PartyComponentPatches.OverrideSetParty(component, party); - } - - private void Handle(MessagePayload payload) - { - var component = payload.What.Component; - var party = payload.What.Party; - - if(objectManager.TryGetId(component, out var componentId) == false) - { - Logger.Error("PartyComponent was not registered with party PartyComponentRegistry"); - return; - } - - var message = new NetworkChangePartyComponentMobileParty(componentId, party.StringId); - network.SendAll(message); } private void Handle(MessagePayload payload) diff --git a/source/GameInterface/Services/PartyComponents/Messages/NetworkChangePartyComponentMobileParty.cs b/source/GameInterface/Services/PartyComponents/Messages/NetworkChangePartyComponentMobileParty.cs deleted file mode 100644 index 51179dd6d..000000000 --- a/source/GameInterface/Services/PartyComponents/Messages/NetworkChangePartyComponentMobileParty.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Common.Messaging; -using ProtoBuf; - -namespace GameInterface.Services.PartyComponents.Messages; -[ProtoContract(SkipConstructor = true)] -internal class NetworkChangePartyComponentMobileParty : ICommand -{ - public NetworkChangePartyComponentMobileParty(string componentId, string partyId) - { - ComponentId = componentId; - PartyId = partyId; - } - - [ProtoMember(1)] - public string ComponentId { get; } - [ProtoMember(2)] - public string PartyId { get; } -} diff --git a/source/GameInterface/Services/PartyComponents/Messages/PartyComponentMobilePartyChanged.cs b/source/GameInterface/Services/PartyComponents/Messages/PartyComponentMobilePartyChanged.cs deleted file mode 100644 index c49de0514..000000000 --- a/source/GameInterface/Services/PartyComponents/Messages/PartyComponentMobilePartyChanged.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Common.Messaging; -using TaleWorlds.CampaignSystem.Party; -using TaleWorlds.CampaignSystem.Party.PartyComponents; - -namespace GameInterface.Services.PartyComponents.Messages; -internal record PartyComponentMobilePartyChanged(PartyComponent Component, MobileParty Party) : IEvent -{ - public PartyComponent Component { get; } = Component; - public MobileParty Party { get; } = Party; -} diff --git a/source/GameInterface/Services/PartyComponents/PartyComponentSync.cs b/source/GameInterface/Services/PartyComponents/PartyComponentSync.cs new file mode 100644 index 000000000..889b9c019 --- /dev/null +++ b/source/GameInterface/Services/PartyComponents/PartyComponentSync.cs @@ -0,0 +1,12 @@ +using GameInterface.AutoSync; +using HarmonyLib; +using TaleWorlds.CampaignSystem.Party.PartyComponents; + +namespace GameInterface.Services.PartyComponents; +internal class PartyComponentSync : IAutoSync +{ + public PartyComponentSync(IAutoSyncBuilder autoSyncBuilder) + { + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(PartyComponent), nameof(PartyComponent.MobileParty))); + } +} diff --git a/source/GameInterface/Services/PartyComponents/Patches/PartyComponentPatches.cs b/source/GameInterface/Services/PartyComponents/Patches/PartyComponentPatches.cs deleted file mode 100644 index b52589b4e..000000000 --- a/source/GameInterface/Services/PartyComponents/Patches/PartyComponentPatches.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Common; -using Common.Logging; -using Common.Messaging; -using Common.Util; -using GameInterface.Policies; -using GameInterface.Services.PartyComponents.Messages; -using HarmonyLib; -using Serilog; -using System; -using TaleWorlds.CampaignSystem.Party; -using TaleWorlds.CampaignSystem.Party.PartyComponents; - -namespace GameInterface.Services.PartyComponents.Patches; - -[HarmonyPatch(typeof(PartyComponent))] -internal class PartyComponentPatches -{ - private static readonly ILogger Logger = LogManager.GetLogger(); - - [HarmonyPatch(nameof(PartyComponent.MobileParty), MethodType.Setter)] - private static bool Prefix(PartyComponent __instance, MobileParty value) - { - // Call original if we call this function - if (CallOriginalPolicy.IsOriginalAllowed()) return true; - - if (ModInformation.IsClient) - { - Logger.Error("Client created unmanaged {name}\n" - + "Callstack: {callstack}", typeof(PartyComponent), Environment.StackTrace); - return false; - } - - var message = new PartyComponentMobilePartyChanged(__instance, value); - - MessageBroker.Instance.Publish(__instance, message); - - return true; - } - - public static void OverrideSetParty(PartyComponent component, MobileParty party) - { - GameLoopRunner.RunOnMainThread(() => - { - using (new AllowedThread()) - { - component.MobileParty = party; - } - }); - } -} diff --git a/source/GameInterface/Services/Save/Patches/SavePatches.cs b/source/GameInterface/Services/Save/Patches/SavePatches.cs index 473143e16..521bdebfc 100644 --- a/source/GameInterface/Services/Save/Patches/SavePatches.cs +++ b/source/GameInterface/Services/Save/Patches/SavePatches.cs @@ -8,8 +8,12 @@ namespace GameInterface.Services.Heroes.Patches; [HarmonyPatch(typeof(Game), "Save")] class SavePatches { - static void Prefix(Game __instance, ref string saveName) + static bool Prefix(Game __instance, ref string saveName) { + // Disable saving for the client so we don't have to worry about pausing to save + if (ModInformation.IsClient) return false; + MessageBroker.Instance.Publish(__instance, new GameSaved(saveName)); + return true; } } From 4620f749e68fa1791aa7bd8e2587eea25a560ad9 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 23 Nov 2024 17:28:35 -0600 Subject: [PATCH 07/22] fixed tests --- .../Environment/Mock/MockControlledEntityRegistry.cs | 7 ++++++- .../Coop.IntegrationTests/Environment/TestEnvironment.cs | 2 ++ source/E2E.Tests/Environment/TestEnvironment.cs | 2 ++ source/GameInterface/Properties/AssemblyInfo.cs | 1 + source/GameInterface/Surrogates/SurrogateCollection.cs | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/source/Coop.IntegrationTests/Environment/Mock/MockControlledEntityRegistry.cs b/source/Coop.IntegrationTests/Environment/Mock/MockControlledEntityRegistry.cs index f274c36a9..7644d5c36 100644 --- a/source/Coop.IntegrationTests/Environment/Mock/MockControlledEntityRegistry.cs +++ b/source/Coop.IntegrationTests/Environment/Mock/MockControlledEntityRegistry.cs @@ -10,7 +10,12 @@ public bool IsControlledBy(string controllerId, string entityId) throw new NotImplementedException(); } - public IReadOnlyDictionary> PackageControlledEntities() + public void LoadControlledEntities(Dictionary> controlledEntityMap) + { + throw new NotImplementedException(); + } + + public Dictionary> PackageControlledEntities() { throw new NotImplementedException(); } diff --git a/source/Coop.IntegrationTests/Environment/TestEnvironment.cs b/source/Coop.IntegrationTests/Environment/TestEnvironment.cs index e864c9f26..cb4b21422 100644 --- a/source/Coop.IntegrationTests/Environment/TestEnvironment.cs +++ b/source/Coop.IntegrationTests/Environment/TestEnvironment.cs @@ -10,6 +10,7 @@ using Coop.IntegrationTests.Environment.Mock; using GameInterface; using GameInterface.Policies; +using GameInterface.Services.Entity; namespace Coop.IntegrationTests.Environment; @@ -87,6 +88,7 @@ private EnvironmentInstance CreateServer() builder.RegisterModule(); builder.RegisterType().AsSelf().As().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().AsSelf(); builder.RegisterInstance(containerProvider).As().SingleInstance(); diff --git a/source/E2E.Tests/Environment/TestEnvironment.cs b/source/E2E.Tests/Environment/TestEnvironment.cs index a00adb0b0..392d22295 100644 --- a/source/E2E.Tests/Environment/TestEnvironment.cs +++ b/source/E2E.Tests/Environment/TestEnvironment.cs @@ -10,6 +10,7 @@ using E2E.Tests.Environment.Mock; using GameInterface; using GameInterface.Policies; +using GameInterface.Surrogates; using Xunit.Abstractions; using ContainerProvider = Coop.Core.ContainerProvider; @@ -117,6 +118,7 @@ private ContainerBuilder AddSharedDependencies(ContainerBuilder builder) builder.RegisterType().AsSelf().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().InstancePerLifetimeScope().AutoActivate(); return builder; } diff --git a/source/GameInterface/Properties/AssemblyInfo.cs b/source/GameInterface/Properties/AssemblyInfo.cs index 64fbece1c..7778567b5 100644 --- a/source/GameInterface/Properties/AssemblyInfo.cs +++ b/source/GameInterface/Properties/AssemblyInfo.cs @@ -30,4 +30,5 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: InternalsVisibleTo("Coop.Tests")] +[assembly: InternalsVisibleTo("Coop.IntegrationTests")] [assembly: InternalsVisibleTo("GameInterface.Tests")] \ No newline at end of file diff --git a/source/GameInterface/Surrogates/SurrogateCollection.cs b/source/GameInterface/Surrogates/SurrogateCollection.cs index d06922c88..324a07647 100644 --- a/source/GameInterface/Surrogates/SurrogateCollection.cs +++ b/source/GameInterface/Surrogates/SurrogateCollection.cs @@ -14,6 +14,7 @@ internal class SurrogateCollection : ISurrogateCollection public SurrogateCollection() { AddSurrogate(); + AddSurrogate(); AddSurrogate(); AddSurrogate(); } From 054f6cc7a31389101637ed55b12efc15ab2647b6 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 23 Nov 2024 17:54:02 -0600 Subject: [PATCH 08/22] debug logging --- .../Handlers/ClientMobilePartyBehaviorHandler.cs | 10 ++++++++-- .../PacketHandlers/PartyAIBehaviorPacketHandler.cs | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/source/Coop.Core/Client/Services/MobileParties/Handlers/ClientMobilePartyBehaviorHandler.cs b/source/Coop.Core/Client/Services/MobileParties/Handlers/ClientMobilePartyBehaviorHandler.cs index bd55e1234..241b65eb1 100644 --- a/source/Coop.Core/Client/Services/MobileParties/Handlers/ClientMobilePartyBehaviorHandler.cs +++ b/source/Coop.Core/Client/Services/MobileParties/Handlers/ClientMobilePartyBehaviorHandler.cs @@ -1,10 +1,12 @@ -using Common.Messaging; +using Common.Logging; +using Common.Messaging; using Common.Network; using Coop.Core.Client.Services.MobileParties.Messages; using Coop.Core.Server.Services.MobileParties.Messages; using Coop.Core.Server.Services.MobileParties.Packets; using GameInterface.Services.MobileParties.Messages; using GameInterface.Services.MobileParties.Messages.Behavior; +using Serilog; namespace Coop.Core.Client.Services.MobileParties.Handlers { @@ -15,10 +17,12 @@ namespace Coop.Core.Client.Services.MobileParties.Handlers /// Game Interface's Handler public class ClientMobilePartyBehaviorHandler : IHandler { + private static readonly ILogger Logger = LogManager.GetLogger(); + private readonly IMessageBroker messageBroker; private readonly INetwork network; - public ClientMobilePartyBehaviorHandler(IMessageBroker messageBroker, INetwork network) + public ClientMobilePartyBehaviorHandler(IMessageBroker messageBroker, INetwork network, ILogger logger) { this.messageBroker = messageBroker; this.network = network; @@ -42,6 +46,8 @@ private void HandleChangeWageOtherClients(MessagePayload obj) { + Logger.Debug($"Attempting to update party movement X:{obj.What.BehaviorUpdateData.TargetPointX} Y:{obj.What.BehaviorUpdateData.TargetPointY}"); + network.SendAll(new RequestMobilePartyBehaviorPacket(obj.What.BehaviorUpdateData)); } diff --git a/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs b/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs index 293cab0aa..be974656a 100644 --- a/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs +++ b/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs @@ -1,10 +1,12 @@ -using Common.Messaging; +using Common.Logging; +using Common.Messaging; using Common.Network; using Common.PacketHandlers; using Coop.Core.Client.Services.MobileParties.Packets; using Coop.Core.Server.Services.MobileParties.Packets; using GameInterface.Services.MobileParties.Messages.Behavior; using LiteNetLib; +using Serilog; namespace Coop.Core.Server.Services.MobileParties.PacketHandlers; @@ -13,11 +15,13 @@ namespace Coop.Core.Server.Services.MobileParties.PacketHandlers; /// internal class RequestMobilePartyBehaviorPacketHandler : IPacketHandler { + private static readonly ILogger Logger = LogManager.GetLogger(); public PacketType PacketType => PacketType.RequestUpdatePartyBehavior; private readonly IPacketManager packetManager; private readonly INetwork network; private readonly IMessageBroker messageBroker; + private readonly ILogger logger; public RequestMobilePartyBehaviorPacketHandler( IPacketManager packetManager, @@ -52,6 +56,8 @@ private void Handle_PartyBehaviorUpdated(MessagePayload pa { var data = payload.What.BehaviorUpdateData; + Logger.Debug($"Party Behavior updated"); + network.SendAll(new UpdatePartyBehaviorPacket(ref data)); } } \ No newline at end of file From 5b66c4c2f1e3e67ff4ff485d1feae3c30f19aef8 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 23 Nov 2024 18:09:54 -0600 Subject: [PATCH 09/22] fixed deploy script --- deploy.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deploy.ps1 b/deploy.ps1 index e0999f0e5..f6026f6f2 100644 --- a/deploy.ps1 +++ b/deploy.ps1 @@ -42,7 +42,8 @@ if(Test-Path (${BaseDir} + $config.modsDir)) New-Item -Force -ItemType Directory -Path "${ModDir}\bin" | Out-Null New-Item -Force -ItemType Directory -Path "${ModDir}\bin\Win64_Shipping_Client" | Out-Null $ModSourceDir = ${SolutionDir} + "\" + $config.name - Get-ChildItem -Path "${ModSourceDir}" -Filter "*.dll" -Recurse -ErrorAction Ignore | Where { $_.PSIsContainer -eq $false } | Copy-Item -Destination "${ModDir}\bin\Win64_Shipping_Client" + Get-ChildItem -Path "${ModSourceDir}\bin\Debug" -Filter "*.dll" -Recurse -ErrorAction Ignore | Where { $_.PSIsContainer -eq $false } | Copy-Item -Destination "${ModDir}\bin\Win64_Shipping_Client" + Get-ChildItem -Path "${ModSourceDir}\bin\Release" -Filter "*.dll" -Recurse -ErrorAction Ignore | Where { $_.PSIsContainer -eq $false } | Copy-Item -Destination "${ModDir}\bin\Win64_Shipping_Client" Copy-Item -Force "${DeployDir}\SubModule.xml" -Destination "${ModDir}\" } From f89372188594f6bca9c2a20837800edf3889ec9e Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Sun, 24 Nov 2024 20:35:46 +0100 Subject: [PATCH 10/22] Update HeroPropertyTests.cs --- .../Services/Heroes/HeroPropertyTests.cs | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs b/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs index 45c39c6fe..d69eb705d 100644 --- a/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs +++ b/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs @@ -8,6 +8,11 @@ using Xunit.Abstractions; using TaleWorlds.Core; using Autofac.Features.OwnedInstances; +using TaleWorlds.Localization; +using TaleWorlds.CampaignSystem.Issues; +using static TaleWorlds.CampaignSystem.Issues.BettingFraudIssueBehavior; +using TaleWorlds.CampaignSystem.Actions; +using TaleWorlds.CampaignSystem.Party; namespace E2E.Tests.Services.Heroes { @@ -22,6 +27,16 @@ public class HeroPropertyTests : IDisposable private string HeroId; StaticBodyProperties body = new StaticBodyProperties(1, 2, 1, 2, 1, 3, 1, 2); + float newFloat = 5f; + int newInt = 9; + long newLong = 99; + TextObject newText = new TextObject("testText"); + CampaignTime newCampaignTime = new CampaignTime(999); + FormationClass newFormation = new FormationClass(); + Hero.CharacterStates newCharState = Hero.CharacterStates.Released; + Occupation newOccupation = Occupation.Mercenary; + KillCharacterAction.KillCharacterActionDetail newKillAction = KillCharacterAction.KillCharacterActionDetail.Murdered; + EquipmentElement newEquipmentElement = new EquipmentElement(); public HeroPropertyTests(ITestOutputHelper output) { @@ -37,13 +52,60 @@ public void Dispose() public void Server_Sync_Hero() { var server = TestEnvironment.Server; - Hero hero = null; + Hero hero = null; + Hero newHero = null; server.Call(() => { hero = GameObjectCreator.CreateInitializedObject(); + newHero = GameObjectCreator.CreateInitializedObject(); + Clan newClan = GameObjectCreator.CreateInitializedObject(); + Settlement newSettlement = GameObjectCreator.CreateInitializedObject(); + Town newTown = GameObjectCreator.CreateInitializedObject(); + MobileParty newMobileParty = GameObjectCreator.CreateInitializedObject(); + Equipment newBattleEquipment = new Equipment(false); + Equipment newCivEquipment = new Equipment(true); + BettingFraudIssue newIssue = new BettingFraudIssue(hero); + + hero.StaticBodyProperties = body; + hero.Weight = newFloat; + hero.Build = newFloat; + hero.PassedTimeAtHomeSettlement = newFloat; + hero.EncyclopediaText = newText; + hero.IsFemale = true; + hero._battleEquipment = newBattleEquipment; + hero._civilianEquipment = newCivEquipment; + hero.CaptivityStartTime = newCampaignTime; + hero.PreferredUpgradeFormation = newFormation; + hero.HeroState = newCharState; + hero.IsMinorFactionHero = true; + hero.Issue = newIssue; + hero.CompanionOf = newClan; + hero.Occupation = newOccupation; + hero.DeathMark = newKillAction; + hero.DeathMarkKillerHero = newHero; + hero.LastKnownClosestSettlement = newSettlement; + hero.HitPoints = newInt; + hero.DeathDay = newCampaignTime; + hero.LastExaminedLogEntryID = newLong; + hero.Clan = newClan; + hero.SupporterOf = newClan; + hero.GovernorOf = newTown; + hero.PartyBelongedTo = newMobileParty; + hero.PartyBelongedToAsPrisoner = newMobileParty.Party; + hero.StayingInSettlement = newSettlement; + hero.IsKnownToPlayer = true; + hero.HasMet = true; + hero.LastMeetingTimeWithPlayer = newCampaignTime; + hero.BornSettlement = newSettlement; + hero.Gold = newInt; + hero.RandomValue = newInt; + hero.BannerItem = newEquipmentElement; + hero.Father = newHero; + hero.Mother = newHero; + hero.Spouse = newHero; Assert.Equal(body, hero.StaticBodyProperties); From c1575388205883d6896a951d70878dd2fc19ef72 Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Mon, 25 Nov 2024 23:12:54 +0100 Subject: [PATCH 11/22] upd --- .../Services/Heroes/HeroPropertyTests.cs | 1 + .../Services/Clans/ClanRegistry.cs | 4 +- .../Services/Heroes/HeroRegistry.cs | 4 +- .../GameInterface/Services/Heroes/HeroSync.cs | 41 ++++++++++++++++--- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs b/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs index d69eb705d..bb2f0c5af 100644 --- a/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs +++ b/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs @@ -63,6 +63,7 @@ public void Server_Sync_Hero() Settlement newSettlement = GameObjectCreator.CreateInitializedObject(); Town newTown = GameObjectCreator.CreateInitializedObject(); MobileParty newMobileParty = GameObjectCreator.CreateInitializedObject(); + Equipment newBattleEquipment = new Equipment(false); Equipment newCivEquipment = new Equipment(true); BettingFraudIssue newIssue = new BettingFraudIssue(hero); diff --git a/source/GameInterface/Services/Clans/ClanRegistry.cs b/source/GameInterface/Services/Clans/ClanRegistry.cs index 1fb3dc6da..f8c530b9d 100644 --- a/source/GameInterface/Services/Clans/ClanRegistry.cs +++ b/source/GameInterface/Services/Clans/ClanRegistry.cs @@ -1,5 +1,6 @@ using Common; using GameInterface.Services.Registry; +using System.Threading; using TaleWorlds.CampaignSystem; namespace GameInterface.Services.Clans; @@ -10,6 +11,7 @@ namespace GameInterface.Services.Clans; internal class ClanRegistry : RegistryBase { private const string ClanStringIdPrefix = "CoopClan"; + private int InstanceCounter = 0; public ClanRegistry(IRegistryCollection collection) : base(collection) { } @@ -31,7 +33,7 @@ public override void RegisterAll() protected override string GetNewId(Clan party) { - party.StringId = Campaign.Current.CampaignObjectManager.FindNextUniqueStringId(ClanStringIdPrefix); + party.StringId = $"{ClanStringIdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; return party.StringId; } diff --git a/source/GameInterface/Services/Heroes/HeroRegistry.cs b/source/GameInterface/Services/Heroes/HeroRegistry.cs index 292754745..57ac34498 100644 --- a/source/GameInterface/Services/Heroes/HeroRegistry.cs +++ b/source/GameInterface/Services/Heroes/HeroRegistry.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Reflection; +using System.Threading; using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Party; @@ -14,6 +15,7 @@ namespace GameInterface.Services.Registry; internal class HeroRegistry : RegistryBase { public static readonly string HeroStringIdPrefix = "CoopHero"; + private int InstanceCounter = 0; public HeroRegistry(IRegistryCollection collection) : base(collection) { } @@ -45,7 +47,7 @@ public override bool RegisterExistingObject(string id, object obj) protected override string GetNewId(Hero hero) { - hero.StringId = Campaign.Current.CampaignObjectManager.FindNextUniqueStringId(HeroStringIdPrefix); + hero.StringId = $"{HeroStringIdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; return hero.StringId; } diff --git a/source/GameInterface/Services/Heroes/HeroSync.cs b/source/GameInterface/Services/Heroes/HeroSync.cs index b510eb240..68e9412f6 100644 --- a/source/GameInterface/Services/Heroes/HeroSync.cs +++ b/source/GameInterface/Services/Heroes/HeroSync.cs @@ -9,11 +9,42 @@ internal class HeroSync : IAutoSync public HeroSync(IAutoSyncBuilder autoSyncBuilder) { autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.StaticBodyProperties))); - - - - - + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Weight))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Build))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.PassedTimeAtHomeSettlement))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.EncyclopediaText))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.IsFemale))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero._battleEquipment))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero._civilianEquipment))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.CaptivityStartTime))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.PreferredUpgradeFormation))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.HeroState))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.IsMinorFactionHero))); + //autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Issue))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.CompanionOf))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Occupation))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathMark))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathMarkKillerHero))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.LastKnownClosestSettlement))); + //autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.HitPoints))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathDay))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.LastExaminedLogEntryID))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Clan))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.SupporterOf))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.GovernorOf))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.PartyBelongedTo))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.PartyBelongedToAsPrisoner))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.StayingInSettlement))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.IsKnownToPlayer))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.HasMet))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.LastMeetingTimeWithPlayer))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.BornSettlement))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Gold))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.RandomValue))); + //autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.BannerItem))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Father))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Mother))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Spouse))); } } } From 019e549d87968b072efe4f6ce98cde283ea02746 Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Mon, 25 Nov 2024 23:36:42 +0100 Subject: [PATCH 12/22] Fixed tests --- .../E2E.Tests/Services/Clans/ClanSyncTests.cs | 45 +++++++------------ .../ObjectBuilders/CharacterObjectBuilder.cs | 2 +- .../Util/ObjectBuilders/CultureBuilder.cs | 2 - .../CultureObjects/CultureObjectRegistry.cs | 3 +- .../Patches/CultureObjectLifetimePatches.cs | 2 +- 5 files changed, 21 insertions(+), 33 deletions(-) diff --git a/source/E2E.Tests/Services/Clans/ClanSyncTests.cs b/source/E2E.Tests/Services/Clans/ClanSyncTests.cs index 95f0a8149..aee4c059f 100644 --- a/source/E2E.Tests/Services/Clans/ClanSyncTests.cs +++ b/source/E2E.Tests/Services/Clans/ClanSyncTests.cs @@ -27,29 +27,6 @@ public class ClanSyncTests : IDisposable public ClanSyncTests(ITestOutputHelper output) { TestEnvironment = new E2ETestEnvironment(output); - - var kingdom = GameObjectCreator.CreateInitializedObject(); - var settlement = GameObjectCreator.CreateInitializedObject(); - var characterObject = GameObjectCreator.CreateInitializedObject(); - var hero = GameObjectCreator.CreateInitializedObject(); - var culture = GameObjectCreator.CreateInitializedObject(); - - // Create objects on the server - Assert.True(Server.ObjectManager.AddNewObject(kingdom, out KingdomId)); - Assert.True(Server.ObjectManager.AddNewObject(settlement, out SettlementId)); - Assert.True(Server.ObjectManager.AddNewObject(characterObject, out CharacterObjectId)); - Assert.True(Server.ObjectManager.AddNewObject(hero, out HeroId)); - Assert.True(Server.ObjectManager.AddNewObject(culture, out CultureId)); - - // Create objects on all clients - foreach (var client in Clients) - { - Assert.True(client.ObjectManager.AddExisting(KingdomId, kingdom)); - Assert.True(client.ObjectManager.AddExisting(SettlementId, settlement)); - Assert.True(client.ObjectManager.AddExisting(CharacterObjectId, characterObject)); - Assert.True(client.ObjectManager.AddExisting(HeroId, hero)); - Assert.True(client.ObjectManager.AddExisting(CultureId, culture)); - } } public void Dispose() @@ -67,6 +44,18 @@ public void ServerUpdateClan_SyncAllClients() string? clanId = null; server.Call(() => { + var kingdom = GameObjectCreator.CreateInitializedObject(); + var settlement = GameObjectCreator.CreateInitializedObject(); + var characterObject = GameObjectCreator.CreateInitializedObject(); + var hero = GameObjectCreator.CreateInitializedObject(); + var culture = GameObjectCreator.CreateInitializedObject(); + + server.ObjectManager.TryGetId(kingdom, out KingdomId); + server.ObjectManager.TryGetId(settlement, out SettlementId); + server.ObjectManager.TryGetId(characterObject, out CharacterObjectId); + server.ObjectManager.TryGetId(hero, out HeroId); + server.ObjectManager.TryGetId(culture, out CultureId); + var clan = Clan.CreateClan(""); clanId = clan.StringId; clan.Name = new TextObject("testName"); @@ -170,17 +159,17 @@ public void ServerUpdateClan_SyncAllClients() Assert.Equal(serverClan.NotAttackableByPlayerUntilTime, clientClan.NotAttackableByPlayerUntilTime); Assert.Equal(serverClan._isEliminated, clientClan._isEliminated); - Assert.Equal(serverClan._kingdom, clientClan._kingdom); + Assert.Equal(serverClan._kingdom.StringId, clientClan._kingdom.StringId); Assert.Equal(serverClan._influence, clientClan._influence); - Assert.Equal(serverClan._clanMidSettlement, clientClan._clanMidSettlement); - Assert.Equal(serverClan._basicTroop, clientClan._basicTroop); - Assert.Equal(serverClan._leader, clientClan._leader); + Assert.Equal(serverClan._clanMidSettlement.StringId, clientClan._clanMidSettlement.StringId); + Assert.Equal(serverClan._basicTroop.StringId, clientClan._basicTroop.StringId); + Assert.Equal(serverClan._leader.StringId, clientClan._leader.StringId); Assert.Equal(serverClan._banner._bannerVisual, clientClan._banner._bannerVisual); Assert.Equal(serverClan._banner._bannerDataList, clientClan._banner._bannerDataList); Assert.Equal(serverClan._tier, clientClan._tier); Assert.Equal(serverClan._aggressiveness, clientClan._aggressiveness); Assert.Equal(serverClan._tributeWallet, clientClan._tributeWallet); - Assert.Equal(serverClan._home, clientClan._home); + Assert.Equal(serverClan._home.StringId, clientClan._home.StringId); Assert.Equal(serverClan._clanDebtToKingdom, clientClan._clanDebtToKingdom); } } diff --git a/source/E2E.Tests/Util/ObjectBuilders/CharacterObjectBuilder.cs b/source/E2E.Tests/Util/ObjectBuilders/CharacterObjectBuilder.cs index a309a24b2..48e037795 100644 --- a/source/E2E.Tests/Util/ObjectBuilders/CharacterObjectBuilder.cs +++ b/source/E2E.Tests/Util/ObjectBuilders/CharacterObjectBuilder.cs @@ -16,7 +16,7 @@ public object Build() { var characterObject = new CharacterObject(); characterObject.Culture = new CultureObject(); - characterObject.StringId = ""; + //characterObject.StringId = ""; characterObject._basicName = new TextObject("Test Character"); characterObject.DefaultCharacterSkills = new MBCharacterSkills(); diff --git a/source/E2E.Tests/Util/ObjectBuilders/CultureBuilder.cs b/source/E2E.Tests/Util/ObjectBuilders/CultureBuilder.cs index a38021f83..8ca635430 100644 --- a/source/E2E.Tests/Util/ObjectBuilders/CultureBuilder.cs +++ b/source/E2E.Tests/Util/ObjectBuilders/CultureBuilder.cs @@ -13,8 +13,6 @@ public object Build() { var cultureObject = new CultureObject(); - cultureObject.StringId = Guid.NewGuid().ToString(); - cultureObject._defaultPolicyList = new MBList(); cultureObject._maleNameList = new MBList(); cultureObject._femaleNameList = new MBList(); diff --git a/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs b/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs index 08955bcef..da963fffd 100644 --- a/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs +++ b/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs @@ -30,7 +30,8 @@ public override void RegisterAll() protected override string GetNewId(CultureObject culture) { - return $"{CultureStringIdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; + culture.StringId = $"{CultureStringIdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; + return culture.StringId; } } } diff --git a/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs b/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs index d312cb61e..a947830ff 100644 --- a/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs +++ b/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs @@ -26,7 +26,7 @@ private static bool ctorPrefix(ref CultureObject __instance) Logger.Error("Client created unmanaged {name}\n" + "Callstack: {callstack}", typeof(CultureObject), Environment.StackTrace); - return true; + return false; } var message = new CultureObjectCreated(__instance); From 850c958ccf01302a99ed08175b9c6b44d53314a0 Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Mon, 25 Nov 2024 23:48:28 +0100 Subject: [PATCH 13/22] Update HeroSetNameTests.cs --- .../Services/Heroes/HeroSetNameTests.cs | 95 ++++++++----------- 1 file changed, 42 insertions(+), 53 deletions(-) diff --git a/source/E2E.Tests/Services/Heroes/HeroSetNameTests.cs b/source/E2E.Tests/Services/Heroes/HeroSetNameTests.cs index 141e24a48..bde589a81 100644 --- a/source/E2E.Tests/Services/Heroes/HeroSetNameTests.cs +++ b/source/E2E.Tests/Services/Heroes/HeroSetNameTests.cs @@ -1,4 +1,6 @@ using E2E.Tests.Environment; +using E2E.Tests.Environment.Instance; +using E2E.Tests.Util; using TaleWorlds.CampaignSystem; using TaleWorlds.Localization; using Xunit.Abstractions; @@ -8,6 +10,9 @@ namespace E2E.Tests.Services.Heroes; public class HeroSetNameTests : IDisposable { E2ETestEnvironment TestEnvironment { get; } + + IEnumerable Clients => TestEnvironment.Clients; + public HeroSetNameTests(ITestOutputHelper output) { TestEnvironment = new E2ETestEnvironment(output); @@ -24,37 +29,31 @@ public void ServerSetName_SyncAllClients() // Arrange var server = TestEnvironment.Server; - var networkId = "CoopHero_1"; - // Creates a new hero and registers it with the objectManager - // using the networkId as an identifier - var serverHero = TestEnvironment.Server.CreateRegisteredObject(networkId); - - // Creates and stores heroes on the clients with same id as server - var clientHeroes = new List(); - foreach(var client in TestEnvironment.Clients) - { - clientHeroes.Add(client.CreateRegisteredObject(networkId)); - } + string HeroId = null; - // Create new text objects for name fields - var fullName = new TextObject("Test Name"); - var firstName = new TextObject("Name"); + // Create new text objects for name fields for server to set + var originalFullName = new TextObject("Test Name"); + var originalFirstName = new TextObject("Name"); // Act server.Call(() => { - serverHero.SetName(fullName, firstName); - }); + Hero hero = GameObjectCreator.CreateInitializedObject(); + hero.SetName(originalFullName, originalFirstName); - // Assert - Assert.Equal(fullName.Value, serverHero.Name.Value); - Assert.Equal(firstName.Value, serverHero.FirstName.Value); + server.ObjectManager.TryGetId(hero, out HeroId); + // Assert + Assert.Equal(originalFullName.Value, hero.Name.Value); + Assert.Equal(originalFirstName.Value, hero.FirstName.Value); + }); - foreach (var clientHero in clientHeroes) + foreach(var client in Clients) { - Assert.Equal(fullName.Value, clientHero.Name.Value); - Assert.Equal(firstName.Value, clientHero.FirstName.Value); + client.ObjectManager.TryGetObject(HeroId, out Hero hero); + + Assert.Equal(originalFullName.Value, hero.Name.Value); + Assert.Equal(originalFirstName.Value, hero.FirstName.Value); } } @@ -65,53 +64,43 @@ public void ClientSetName_DoesNothing() var server = TestEnvironment.Server; var client1 = TestEnvironment.Clients.First(); - var networkId = "CoopHero_1"; - - // Creates a new hero and registers it with the objectManager - // using the networkId as an identifier - var serverHero = TestEnvironment.Server.CreateRegisteredObject(networkId); - - // Creates and stores heroes on the clients with same id as server - var clientHeroes = new List(); - foreach (var client in TestEnvironment.Clients) - { - clientHeroes.Add(client.CreateRegisteredObject(networkId)); - } + string HeroId = null; // Create new text objects for name fields for server to set var originalFullName = new TextObject("Test Name"); var originalFirstName = new TextObject("Name"); - server.Call(() => - { - serverHero.SetName(originalFullName, originalFirstName); - }); - // Create new text objects for name fields for client to attempt to set // expected that it does not change var differentFullName = new TextObject("Dont set me"); var differentFirstName = new TextObject("Dont set me"); // Act - client1.Call(() => + server.Call(() => { - clientHeroes.First().SetName(differentFullName, differentFirstName); - }); + Hero hero = GameObjectCreator.CreateInitializedObject(); + hero.SetName(originalFullName, originalFirstName); - // Assert - Assert.Equal(originalFullName.Value, serverHero.Name.Value); - Assert.Equal(originalFirstName.Value, serverHero.FirstName.Value); + server.ObjectManager.TryGetId(hero, out HeroId); - Assert.NotEqual(differentFullName.Value, serverHero.Name.Value); - Assert.NotEqual(differentFirstName.Value, serverHero.FirstName.Value); + // Assert + Assert.Equal(originalFullName.Value, hero.Name.Value); + Assert.Equal(originalFirstName.Value, hero.FirstName.Value); - foreach (var clientHero in clientHeroes) + Assert.NotEqual(differentFullName.Value, hero.Name.Value); + Assert.NotEqual(differentFirstName.Value, hero.FirstName.Value); + }); + + client1.Call(() => { - Assert.Equal(originalFullName.Value, clientHero.Name.Value); - Assert.Equal(originalFirstName.Value, clientHero.FirstName.Value); + client1.ObjectManager.TryGetObject(HeroId, out Hero hero); + hero.SetName(differentFullName, differentFirstName); - Assert.NotEqual(differentFullName.Value, clientHero.Name.Value); - Assert.NotEqual(differentFirstName.Value, clientHero.FirstName.Value); - } + Assert.Equal(originalFullName.Value, hero.Name.Value); + Assert.Equal(originalFirstName.Value, hero.FirstName.Value); + + Assert.NotEqual(differentFullName.Value, hero.Name.Value); + Assert.NotEqual(differentFirstName.Value, hero.FirstName.Value); + }); } } \ No newline at end of file From e4a075bbe3eed9a74bb5496a8ed4da02dbeba84f Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Mon, 25 Nov 2024 23:50:48 +0100 Subject: [PATCH 14/22] Update HeroSync.cs --- source/GameInterface/Services/Heroes/HeroSync.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/GameInterface/Services/Heroes/HeroSync.cs b/source/GameInterface/Services/Heroes/HeroSync.cs index 68e9412f6..797688503 100644 --- a/source/GameInterface/Services/Heroes/HeroSync.cs +++ b/source/GameInterface/Services/Heroes/HeroSync.cs @@ -41,7 +41,7 @@ public HeroSync(IAutoSyncBuilder autoSyncBuilder) autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.BornSettlement))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Gold))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.RandomValue))); - //autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.BannerItem))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.BannerItem))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Father))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Mother))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Spouse))); From 41b370acad6a814f00ee2ff7097f6b4b773d6783 Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Mon, 25 Nov 2024 23:52:37 +0100 Subject: [PATCH 15/22] Update HeroSync.cs --- source/GameInterface/Services/Heroes/HeroSync.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/GameInterface/Services/Heroes/HeroSync.cs b/source/GameInterface/Services/Heroes/HeroSync.cs index 797688503..3f229d13f 100644 --- a/source/GameInterface/Services/Heroes/HeroSync.cs +++ b/source/GameInterface/Services/Heroes/HeroSync.cs @@ -26,7 +26,7 @@ public HeroSync(IAutoSyncBuilder autoSyncBuilder) autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathMark))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathMarkKillerHero))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.LastKnownClosestSettlement))); - //autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.HitPoints))); + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.HitPoints))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathDay))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.LastExaminedLogEntryID))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Clan))); @@ -41,7 +41,7 @@ public HeroSync(IAutoSyncBuilder autoSyncBuilder) autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.BornSettlement))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Gold))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.RandomValue))); - autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.BannerItem))); + //autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.BannerItem))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Father))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Mother))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Spouse))); From 9f479b371c48fb5b1b6e9bda4ea5c00433d4db43 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Thu, 5 Dec 2024 21:23:22 -0600 Subject: [PATCH 16/22] stability fix 1 --- source/Common/GameLoopRunner.cs | 2 +- source/Common/Logging/BatchLogger.cs | 2 +- source/Common/Logging/LogManager.cs | 16 +++++++-- source/Common/Util/ObjectHelper.cs | 2 +- .../PartyAIBehaviorPacketHandler.cs | 2 -- .../Environment/E2ETestEnvironment.cs | 22 ++++++++++++- .../AutoSync/Fields/FieldTranspilerCreator.cs | 33 +++++++++++++++++++ source/GameInterface/ContainerProvider.cs | 13 +------- .../Armies/Patches/ArmyDisablePatches.cs | 13 ++++++++ .../Services/Clans/ClanRegistry.cs | 5 ++- .../Services/Heroes/HeroRegistry.cs | 1 + .../Disable/IssueManagerDisablePatches.cs | 16 +++++++++ .../Handlers/MobilePartyFieldsHandler.cs | 19 ++++++++--- .../MobileParties/MobilePartyRegistry.cs | 13 ++------ .../Patches/MobilePartyFieldPatches.cs | 2 +- .../Patches/ParallelRobustnessPatches.cs | 1 + .../MobilePartyAIs/MobilePartyAiSync.cs | 2 ++ .../Patches/MobilePartyAIDisablePatches.cs | 14 ++++++++ .../Patches/MobilePartyAIRobustnessPatches.cs | 13 ++++++++ .../Patches/PartiesThinkPatch.cs | 5 +-- .../CustomPartyComponentPatches.cs | 5 ++- .../Handlers/PartyVisualLifetimeHandler.cs | 8 +++-- .../Services/Registry/RegistryBase.cs | 3 ++ .../Settlements/SettlementRegistry.cs | 6 +++- 24 files changed, 174 insertions(+), 44 deletions(-) create mode 100644 source/GameInterface/Services/Armies/Patches/ArmyDisablePatches.cs create mode 100644 source/GameInterface/Services/IssuesService/Patches/Disable/IssueManagerDisablePatches.cs create mode 100644 source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIDisablePatches.cs create mode 100644 source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIRobustnessPatches.cs diff --git a/source/Common/GameLoopRunner.cs b/source/Common/GameLoopRunner.cs index a51f661bd..60df28dd0 100644 --- a/source/Common/GameLoopRunner.cs +++ b/source/Common/GameLoopRunner.cs @@ -38,7 +38,7 @@ public void Update(TimeSpan frameTime) List<(Action, EventWaitHandle)> toBeRun = new List<(Action, EventWaitHandle)>(); - lock (m_Queue) + lock (Instance.m_QueueLock) { while (m_Queue.Count > 0) { diff --git a/source/Common/Logging/BatchLogger.cs b/source/Common/Logging/BatchLogger.cs index b280f299f..5ac62644e 100644 --- a/source/Common/Logging/BatchLogger.cs +++ b/source/Common/Logging/BatchLogger.cs @@ -30,7 +30,7 @@ public BatchLogger(TimeSpan pollInterval) { this.pollInterval = pollInterval; poller = new Poller(Poll, pollInterval); - poller.Start(); + //poller.Start(); } /// diff --git a/source/Common/Logging/LogManager.cs b/source/Common/Logging/LogManager.cs index 08de6d2f3..62832f49e 100644 --- a/source/Common/Logging/LogManager.cs +++ b/source/Common/Logging/LogManager.cs @@ -1,14 +1,26 @@ using System; +using System.Collections.Generic; using Serilog; +using Serilog.Core; namespace Common.Logging; public static class LogManager { public static LoggerConfiguration Configuration { get; set; } = new LoggerConfiguration(); - + + public static List Sinks { get; } = new List(); + + // If this is called before the Configuration is setup, logging does not work - private static Lazy _logger = new Lazy(() => Configuration.CreateLogger()); + private static Lazy _logger = new Lazy(() => { + + foreach (var sink in Sinks) + { + Configuration = Configuration.WriteTo.Sink(sink); + } + return Configuration.CreateLogger(); + }); public static ILogger GetLogger() => _logger.Value .ForContext(); diff --git a/source/Common/Util/ObjectHelper.cs b/source/Common/Util/ObjectHelper.cs index c4f0fc0bc..a2b51ee1e 100644 --- a/source/Common/Util/ObjectHelper.cs +++ b/source/Common/Util/ObjectHelper.cs @@ -20,7 +20,7 @@ public class ObjectHelper /// New instance of public static T SkipConstructor() { - Logger.Verbose("{pid}: Creating {type}", Thread.CurrentThread.ManagedThreadId, typeof(T)); + //Logger.Verbose("{pid}: Creating {type}", Thread.CurrentThread.ManagedThreadId, typeof(T)); return (T)FormatterServices.GetUninitializedObject(typeof(T)); } diff --git a/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs b/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs index be974656a..fc847c329 100644 --- a/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs +++ b/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs @@ -56,8 +56,6 @@ private void Handle_PartyBehaviorUpdated(MessagePayload pa { var data = payload.What.BehaviorUpdateData; - Logger.Debug($"Party Behavior updated"); - network.SendAll(new UpdatePartyBehaviorPacket(ref data)); } } \ No newline at end of file diff --git a/source/E2E.Tests/Environment/E2ETestEnvironment.cs b/source/E2E.Tests/Environment/E2ETestEnvironment.cs index 3133366e3..8ac8bb331 100644 --- a/source/E2E.Tests/Environment/E2ETestEnvironment.cs +++ b/source/E2E.Tests/Environment/E2ETestEnvironment.cs @@ -7,6 +7,9 @@ using GameInterface.AutoSync; using GameInterface.Tests.Bootstrap; using Serilog; +using Serilog.Core; +using Serilog.Events; +using Serilog.Sinks.XUnit; using System.Reflection; using TaleWorlds.CampaignSystem; using TaleWorlds.Core; @@ -27,9 +30,26 @@ internal class E2ETestEnvironment : IDisposable private TestEnvironment IntegrationEnvironment { get; } + private class TestOutputSink : ILogEventSink + { + private readonly ITestOutputHelper output; + + public TestOutputSink(ITestOutputHelper output) + { + this.output = output; + } + + public void Emit(LogEvent logEvent) + { + TextWriter textWriter = new StringWriter(); + logEvent.RenderMessage(textWriter); + output.WriteLine(textWriter.ToString()); + } + } + public E2ETestEnvironment(ITestOutputHelper output, int numClients = 2) { - LogManager.Configuration = new LoggerConfiguration().WriteTo.TestOutput(output); + LogManager.Sinks.Add(new TestOutputSink(output)); GameLoopRunner.Instance.SetGameLoopThread(); diff --git a/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs b/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs index ff3180a53..3ae17b135 100644 --- a/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs +++ b/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs @@ -435,6 +435,23 @@ private MethodBuilder CreateInterceptByValue(int typeId, int propId, FieldInfo f var il = methodBuilder.GetILGenerator(); + var neqLabel = il.DefineLabel(); + + // if (this.currentValue == newValue) return; + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, field); + il.Emit(OpCodes.Ldarg_1); + + + il.Emit(OpCodes.Ceq); + il.Emit(OpCodes.Brfalse, neqLabel); + + il.Emit(OpCodes.Ret); + + + il.MarkLabel(neqLabel); + + IsClientCheck(il, field); var networkLocal = TryResolve(il); @@ -474,6 +491,22 @@ private MethodBuilder CreateInterceptByRef(int typeId, int propId, FieldInfo fie var il = methodBuilder.GetILGenerator(); + var neqLabel = il.DefineLabel(); + + // if (this.currentValue == newValue) return; + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, field); + il.Emit(OpCodes.Ldarg_1); + + + il.Emit(OpCodes.Ceq); + il.Emit(OpCodes.Brfalse, neqLabel); + + il.Emit(OpCodes.Ret); + + + il.MarkLabel(neqLabel); + IsClientCheck(il, field); var networkLocal = TryResolve(il); diff --git a/source/GameInterface/ContainerProvider.cs b/source/GameInterface/ContainerProvider.cs index ca34f2a6c..307d5b1fb 100644 --- a/source/GameInterface/ContainerProvider.cs +++ b/source/GameInterface/ContainerProvider.cs @@ -26,18 +26,7 @@ public static bool TryGetContainer(out ILifetimeScope lifetimeScope) { lifetimeScope = _lifetimeScope; - if (lifetimeScope == null) - { - var callStack = Environment.StackTrace; - Logger.Error("{name} was not setup properly, try using {setupFnName}\n" + - "CallStack: {callStack}", - nameof(ContainerProvider), - nameof(SetContainer), - callStack); - return false; - } - - return true; + return lifetimeScope != null; } public static bool TryResolve(out T instance) where T : class diff --git a/source/GameInterface/Services/Armies/Patches/ArmyDisablePatches.cs b/source/GameInterface/Services/Armies/Patches/ArmyDisablePatches.cs new file mode 100644 index 000000000..e63d26cd9 --- /dev/null +++ b/source/GameInterface/Services/Armies/Patches/ArmyDisablePatches.cs @@ -0,0 +1,13 @@ +using HarmonyLib; +using TaleWorlds.CampaignSystem; + +namespace GameInterface.Services.Armies.Patches +{ + [HarmonyPatch(typeof(Army))] + class ArmyDisablePatches + { + [HarmonyPatch(nameof(Army.Tick))] + [HarmonyPrefix] + private static bool DisableArmyTick() => ModInformation.IsServer; + } +} diff --git a/source/GameInterface/Services/Clans/ClanRegistry.cs b/source/GameInterface/Services/Clans/ClanRegistry.cs index 1fb3dc6da..fd7c0606b 100644 --- a/source/GameInterface/Services/Clans/ClanRegistry.cs +++ b/source/GameInterface/Services/Clans/ClanRegistry.cs @@ -1,5 +1,6 @@ using Common; using GameInterface.Services.Registry; +using System.Threading; using TaleWorlds.CampaignSystem; namespace GameInterface.Services.Clans; @@ -10,6 +11,7 @@ namespace GameInterface.Services.Clans; internal class ClanRegistry : RegistryBase { private const string ClanStringIdPrefix = "CoopClan"; + private static int InstanceCount = 0; public ClanRegistry(IRegistryCollection collection) : base(collection) { } @@ -26,12 +28,13 @@ public override void RegisterAll() foreach (var clan in objectManager.Clans) { RegisterExistingObject(clan.StringId, clan); + Interlocked.Increment(ref InstanceCount); } } protected override string GetNewId(Clan party) { - party.StringId = Campaign.Current.CampaignObjectManager.FindNextUniqueStringId(ClanStringIdPrefix); + party.StringId = $"{ClanStringIdPrefix}_{Interlocked.Increment(ref InstanceCount)}"; return party.StringId; } diff --git a/source/GameInterface/Services/Heroes/HeroRegistry.cs b/source/GameInterface/Services/Heroes/HeroRegistry.cs index 292754745..cccc44f67 100644 --- a/source/GameInterface/Services/Heroes/HeroRegistry.cs +++ b/source/GameInterface/Services/Heroes/HeroRegistry.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Reflection; +using System.Threading; using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Party; diff --git a/source/GameInterface/Services/IssuesService/Patches/Disable/IssueManagerDisablePatches.cs b/source/GameInterface/Services/IssuesService/Patches/Disable/IssueManagerDisablePatches.cs new file mode 100644 index 000000000..da4ed4a3b --- /dev/null +++ b/source/GameInterface/Services/IssuesService/Patches/Disable/IssueManagerDisablePatches.cs @@ -0,0 +1,16 @@ +using HarmonyLib; +using TaleWorlds.CampaignSystem.Issues; + +namespace GameInterface.Services.IssuesService.Patches.Disable; + +[HarmonyPatch(typeof(IssueManager))] +internal class IssueManagerDisablePatches +{ + [HarmonyPatch(nameof(IssueManager.DailyTick))] + [HarmonyPrefix] + private static bool DisableIssueDailyTick() => false; + + [HarmonyPatch(nameof(IssueManager.HourlyTick))] + [HarmonyPrefix] + private static bool DisableIssueHourlyTick() => false; +} diff --git a/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs b/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs index 8000f415b..e4c5cbed6 100644 --- a/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs +++ b/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs @@ -1,16 +1,11 @@ -using System.Collections.Generic; using Common.Logging; using Common.Messaging; using GameInterface.Services.MobileParties.Messages.Fields.Commands; using GameInterface.Services.ObjectManager; using Serilog; using TaleWorlds.CampaignSystem; -using TaleWorlds.CampaignSystem.ComponentInterfaces; using TaleWorlds.CampaignSystem.Party; -using TaleWorlds.CampaignSystem.Party.PartyComponents; using TaleWorlds.CampaignSystem.Settlements; -using TaleWorlds.Library; -using TaleWorlds.Localization; namespace GameInterface.Services.MobileParties.Handlers; @@ -52,11 +47,19 @@ private void Handle(MessagePayload payload) Logger.Error("Unable to find {type} with id: {id}", typeof(MobileParty), data.MobilePartyId); return; } + + if (data.AttachedToId == null) + { + instance._attachedTo = null; + return; + } + if (objectManager.TryGetObject(data.AttachedToId, out var attachedToMobileParty) == false) { Logger.Error("Unable to find {type} with id: {id}", typeof(Settlement), data.AttachedToId); return; } + instance._attachedTo = attachedToMobileParty; } @@ -152,6 +155,12 @@ private void Handle(MessagePayload payload) Logger.Error("Unable to find {type} with id: {id}", typeof(MobileParty), data.MobilePartyId); return; } + + if (data.CustomHomeSettlementId == null) + { + instance._customHomeSettlement = null; + return; + } if (objectManager.TryGetObject(data.CustomHomeSettlementId, out var settlement) == false) { diff --git a/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs b/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs index c5ce5962f..db164d046 100644 --- a/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs +++ b/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs @@ -14,7 +14,7 @@ namespace GameInterface.Services.MobileParties; internal class MobilePartyRegistry : RegistryBase { private const string PartyStringIdPrefix = "CoopParty"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; private readonly IMessageBroker messageBroker; public MobilePartyRegistry(IRegistryCollection collection, IMessageBroker messageBroker) : base(collection) @@ -24,17 +24,10 @@ public MobilePartyRegistry(IRegistryCollection collection, IMessageBroker messag public override void RegisterAll() { - var objectManager = Campaign.Current?.CampaignObjectManager; - - if (objectManager == null) - { - Logger.Error("Unable to register objects when CampaignObjectManager is null"); - return; - } - - foreach (var party in objectManager.MobileParties) + foreach (var party in MobileParty.All) { base.RegisterExistingObject(party.StringId, party); + Interlocked.Increment(ref InstanceCounter); } } diff --git a/source/GameInterface/Services/MobileParties/Patches/MobilePartyFieldPatches.cs b/source/GameInterface/Services/MobileParties/Patches/MobilePartyFieldPatches.cs index 4c679e20a..a692d8a8a 100644 --- a/source/GameInterface/Services/MobileParties/Patches/MobilePartyFieldPatches.cs +++ b/source/GameInterface/Services/MobileParties/Patches/MobilePartyFieldPatches.cs @@ -63,7 +63,7 @@ public static void AttachedToIntercept(MobileParty instance, MobileParty newAtta return; } - MessageBroker.Instance.Publish(instance, new AttachedToChanged(newAttachedTo.StringId, instance.StringId)); + MessageBroker.Instance.Publish(instance, new AttachedToChanged(newAttachedTo?.StringId, instance.StringId)); instance._attachedTo = newAttachedTo; } diff --git a/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs b/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs index 4a40c8cae..982ab52b0 100644 --- a/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs +++ b/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs @@ -99,6 +99,7 @@ static bool ParallelTickArmies(CampaignTickCacheDataStore __instance, int startI MobileParty mobileParty = tickCachePerParty.MobileParty; if (mobileParty.Party == null) continue; + if (mobileParty.AttachedTo == null) continue; MobileParty.CachedPartyVariables localVariables = tickCachePerParty.LocalVariables; mobileParty.TickForMovingArmyLeader(ref localVariables, __instance._currentDt, __instance._currentRealDt); diff --git a/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiSync.cs b/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiSync.cs index 927170685..d10a21e2e 100644 --- a/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiSync.cs +++ b/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiSync.cs @@ -7,6 +7,8 @@ internal class MobilePartyAiSync : IAutoSync { public MobilePartyAiSync(IAutoSyncBuilder autoSyncBuilder) { + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(MobilePartyAi), nameof(MobilePartyAi.AiBehaviorPartyBase))); + autoSyncBuilder.AddField(AccessTools.Field(typeof(MobilePartyAi), nameof(MobilePartyAi._mobileParty))); } } diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIDisablePatches.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIDisablePatches.cs new file mode 100644 index 000000000..285071794 --- /dev/null +++ b/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIDisablePatches.cs @@ -0,0 +1,14 @@ +using GameInterface.Services.MobileParties.Extensions; +using HarmonyLib; +using TaleWorlds.CampaignSystem.Party; + +namespace GameInterface.Services.MobilePartyAIs.Patches; + +[HarmonyPatch(typeof(MobilePartyAi))] +internal class MobilePartyAIDisablePatches +{ + + [HarmonyPatch(nameof(MobilePartyAi.Tick))] + [HarmonyPrefix] + private static bool ClientDisableTickPrefix(MobilePartyAi __instance) => ModInformation.IsServer || __instance._mobileParty.IsPartyControlled(); +} diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIRobustnessPatches.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIRobustnessPatches.cs new file mode 100644 index 000000000..8d2ab18d3 --- /dev/null +++ b/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIRobustnessPatches.cs @@ -0,0 +1,13 @@ +using HarmonyLib; +using TaleWorlds.CampaignSystem.Party; + +namespace GameInterface.Services.MobilePartyAIs.Patches; + +//[HarmonyPatch(typeof(MobilePartyAi))] +class MobilePartyAIRobustnessPatches +{ + [HarmonyPatch(nameof(MobilePartyAi.GetNearbyPartyToFlee))] + [HarmonyPrefix] + // Skipe if partyToFleeFrom is null + private static bool RobustnessPrefix_GetNearbyPartyToFlee(MobileParty partyToFleeFrom) => partyToFleeFrom != null; +} diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/PartiesThinkPatch.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/PartiesThinkPatch.cs index 032cb56d4..a4e3adc0a 100644 --- a/source/GameInterface/Services/MobilePartyAIs/Patches/PartiesThinkPatch.cs +++ b/source/GameInterface/Services/MobilePartyAIs/Patches/PartiesThinkPatch.cs @@ -1,4 +1,5 @@ -using HarmonyLib; +using GameInterface.Services.MobileParties.Extensions; +using HarmonyLib; using System.Threading.Tasks; using TaleWorlds.CampaignSystem; @@ -27,7 +28,7 @@ private static bool PartiesThinkPrefix(Campaign __instance, ref float dt) { var currentIdx = (CurrentStartIdx + i) % __instance.MobileParties.Count; - __instance.MobileParties[currentIdx].Ai.Tick(dt); + __instance.MobileParties[currentIdx].Ai?.Tick(dt); } CurrentStartIdx = (CurrentStartIdx + UPDATES_PER_TICK) % __instance.MobileParties.Count; diff --git a/source/GameInterface/Services/PartyComponents/Patches/CustomPartyComponents/CustomPartyComponentPatches.cs b/source/GameInterface/Services/PartyComponents/Patches/CustomPartyComponents/CustomPartyComponentPatches.cs index 1184b671a..a91b4537b 100644 --- a/source/GameInterface/Services/PartyComponents/Patches/CustomPartyComponents/CustomPartyComponentPatches.cs +++ b/source/GameInterface/Services/PartyComponents/Patches/CustomPartyComponents/CustomPartyComponentPatches.cs @@ -88,14 +88,17 @@ private static IEnumerable HomeSettlementTranspiler(IEnumerable public static void HomeSettlementIntercept(CustomPartyComponent instance, Settlement newSettlement) { + if (instance._homeSettlement == newSettlement) return; + if (CallOriginalPolicy.IsOriginalAllowed()) { instance._homeSettlement = newSettlement; return; } + if (ModInformation.IsClient) { - Logger.Error("Client added unmanaged item: {callstack}", Environment.StackTrace); + //Logger.Error("Client added unmanaged item: {callstack}", Environment.StackTrace); return; } diff --git a/source/GameInterface/Services/PartyVisuals/Handlers/PartyVisualLifetimeHandler.cs b/source/GameInterface/Services/PartyVisuals/Handlers/PartyVisualLifetimeHandler.cs index 1ab21d621..f8a5c6d43 100644 --- a/source/GameInterface/Services/PartyVisuals/Handlers/PartyVisualLifetimeHandler.cs +++ b/source/GameInterface/Services/PartyVisuals/Handlers/PartyVisualLifetimeHandler.cs @@ -48,9 +48,11 @@ private void Handle(MessagePayload payload) { objectManager.TryGetObject(payload.What.PartyBaseId, out var partyBase); - PartyVisual newVisual = new PartyVisual(partyBase); - - objectManager.AddExisting(payload.What.PartyVisualId, newVisual); + using(new AllowedThread()) + { + PartyVisual newVisual = new PartyVisual(partyBase); + objectManager.AddExisting(payload.What.PartyVisualId, newVisual); + } } private void Handle(MessagePayload payload) diff --git a/source/GameInterface/Services/Registry/RegistryBase.cs b/source/GameInterface/Services/Registry/RegistryBase.cs index 1a4df1e4e..9f9ec8506 100644 --- a/source/GameInterface/Services/Registry/RegistryBase.cs +++ b/source/GameInterface/Services/Registry/RegistryBase.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; +using TaleWorlds.CampaignSystem; using TaleWorlds.ObjectSystem; namespace GameInterface.Services.Registry; @@ -84,6 +85,8 @@ public virtual bool RegisterNewObject(object obj, out string id) if (obj is MBObjectBase mbObject) { mbObject.StringId = newId; + + MBObjectManager.Instance?.RegisterObject(mbObject); } objIds.Add(newId, castedObj); diff --git a/source/GameInterface/Services/Settlements/SettlementRegistry.cs b/source/GameInterface/Services/Settlements/SettlementRegistry.cs index c0834dd24..da0580ae2 100644 --- a/source/GameInterface/Services/Settlements/SettlementRegistry.cs +++ b/source/GameInterface/Services/Settlements/SettlementRegistry.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading; using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Settlements; using TaleWorlds.ObjectSystem; @@ -10,6 +11,7 @@ namespace GameInterface.Services.Settlements; internal class SettlementRegistry : RegistryBase { public static readonly string SettlementStringIdPrefix = "CoopSettlement"; + private static int InstanceCounter = 0; public SettlementRegistry(IRegistryCollection collection) : base(collection) { } @@ -25,7 +27,9 @@ public override void RegisterAll() foreach (var settlement in campaignObjectManager.Settlements) { + base.RegisterExistingObject(settlement.StringId, settlement); + Interlocked.Increment(ref InstanceCounter); } } @@ -40,7 +44,7 @@ public override bool RegisterExistingObject(string id, object obj) protected override string GetNewId(Settlement settlement) { - settlement.StringId = Campaign.Current.CampaignObjectManager.FindNextUniqueStringId(SettlementStringIdPrefix); + settlement.StringId = $"{SettlementStringIdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; return settlement.StringId; } From cf316fc8e8072a9b1e5c28b28d8df3e95629f1b8 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 7 Dec 2024 19:11:24 -0600 Subject: [PATCH 17/22] Autosync fixes and registry fixes --- .../Environment/E2ETestEnvironment.cs | 1 - .../AutoSync/Fields/FieldSwitchCreator.cs | 48 +++++++++++----- .../AutoSync/Fields/FieldTranspilerCreator.cs | 55 +++++++++++++----- .../Properties/PropertySwitchCreator.cs | 43 +++++++++----- .../BasicCharacterObjectRegistry.cs | 33 ----------- .../Handlers/BasicCharacterLifetimeHandler.cs | 56 ------------------- .../Messages/BasicCharacterCreated.cs | 18 ------ .../Messages/NetworkCreateBasicCharacter.cs | 17 ------ .../BasicCharacterObjectLifetimePatches.cs | 43 -------------- .../BasicCultureObjectRegistry.cs | 39 ------------- .../BasicCultureObjectLifetimeHandler.cs | 56 ------------------- .../Messages/BasicCultureCreated.cs | 15 ----- .../Messages/NetworkCreateBasicCulture.cs | 17 ------ .../BasicCultureObjectLifetimePatches.cs | 41 -------------- .../BesiegerCamps/BesiegerCampRegistry.cs | 2 +- .../Services/Buildings/BuildingRegistry.cs | 2 +- .../CharacterObjectRegistry.cs | 11 +++- .../Patches/CharacterObjectLifetimePatches.cs | 33 +++++++++++ .../Services/Clans/ClanRegistry.cs | 4 +- .../CraftingService/CraftingRegistry.cs | 2 +- .../CultureObjects/CultureObjectRegistry.cs | 10 +++- .../Patches/CultureObjectLifetimePatches.cs | 32 +++++++++++ .../Services/Equipments/EquipmentRegistry.cs | 2 +- .../Equipments/Handler/EquipmentHandler.cs | 26 +++------ .../Patches/EquipmentLifetimePatches.cs | 15 +++-- .../Heroes/Handlers/HeroFieldsHandler.cs | 7 +++ .../ItemObjects/ItemObjectRegistry.cs | 2 +- .../MapEventSides/MapEventSideRegistry.cs | 2 +- .../Services/MapEvents/MapEventRegistry.cs | 2 +- .../Handlers/MobilePartyFieldsHandler.cs | 2 +- .../MobileParties/MobilePartyRegistry.cs | 2 +- .../MobilePartyAIs/MobilePartyAiRegistry.cs | 2 +- .../Services/PartyBases/PartyBaseRegistry.cs | 2 +- .../PartyComponents/PartyComponentRegistry.cs | 2 +- .../PartyVisuals/PartyVisualRegistry.cs | 2 +- .../SettlementComponentRegistry.cs | 2 +- .../Settlements/SettlementRegistry.cs | 2 +- ...SiegeEngineConstructionProgressRegistry.cs | 2 +- .../SiegeEnginesContainerRegistry.cs | 2 +- .../SiegeEvents/SiegeEventRegistry.cs | 2 +- .../WeaponDesigns/WeaponDesignRegistry.cs | 2 +- .../Services/Workshops/WorkshopRegistry.cs | 2 +- .../Workshops/WorkshopTypeRegistry.cs | 8 +-- .../Surrogates/SurrogateCollection.cs | 4 +- 44 files changed, 235 insertions(+), 437 deletions(-) delete mode 100644 source/GameInterface/Services/BasicCharacterObjects/BasicCharacterObjectRegistry.cs delete mode 100644 source/GameInterface/Services/BasicCharacterObjects/Handlers/BasicCharacterLifetimeHandler.cs delete mode 100644 source/GameInterface/Services/BasicCharacterObjects/Messages/BasicCharacterCreated.cs delete mode 100644 source/GameInterface/Services/BasicCharacterObjects/Messages/NetworkCreateBasicCharacter.cs delete mode 100644 source/GameInterface/Services/BasicCharacterObjects/Patches/BasicCharacterObjectLifetimePatches.cs delete mode 100644 source/GameInterface/Services/BasicCultureObjects/BasicCultureObjectRegistry.cs delete mode 100644 source/GameInterface/Services/BasicCultureObjects/Handlers/BasicCultureObjectLifetimeHandler.cs delete mode 100644 source/GameInterface/Services/BasicCultureObjects/Messages/BasicCultureCreated.cs delete mode 100644 source/GameInterface/Services/BasicCultureObjects/Messages/NetworkCreateBasicCulture.cs delete mode 100644 source/GameInterface/Services/BasicCultureObjects/Patches/BasicCultureObjectLifetimePatches.cs diff --git a/source/E2E.Tests/Environment/E2ETestEnvironment.cs b/source/E2E.Tests/Environment/E2ETestEnvironment.cs index 8ac8bb331..1c519d97f 100644 --- a/source/E2E.Tests/Environment/E2ETestEnvironment.cs +++ b/source/E2E.Tests/Environment/E2ETestEnvironment.cs @@ -89,7 +89,6 @@ private void SetupMainHero() Server.Call(() => { var characterObject = GameObjectCreator.CreateInitializedObject(); - MBObjectManager.Instance.RegisterObject(characterObject); var mainHero = HeroCreator.CreateSpecialHero(characterObject); characterObject.HeroObject = mainHero; Game.Current.PlayerTroop = characterObject; diff --git a/source/GameInterface/AutoSync/Fields/FieldSwitchCreator.cs b/source/GameInterface/AutoSync/Fields/FieldSwitchCreator.cs index 1bac68b61..1289e114a 100644 --- a/source/GameInterface/AutoSync/Fields/FieldSwitchCreator.cs +++ b/source/GameInterface/AutoSync/Fields/FieldSwitchCreator.cs @@ -157,44 +157,64 @@ private void CreateByValue(ILGenerator il, FieldInfo field, LocalBuilder instanc private void CreateByRef(ILGenerator il, FieldInfo field, LocalBuilder instanceLocal) { - var errorString = $"Unable to find instance of type {instanceType.Name} with id "; + var errorString = $"Unable to find instance of type {field.Name} with id "; var stringConcatMethod = AccessTools.Method(typeof(FieldSwitchCreator), nameof(Concat)); + var valueId = il.DeclareLocal(typeof(string)); var valueLocal = il.DeclareLocal(field.FieldType); - il.Emit(OpCodes.Ldloc, instanceLocal); + var setValue = il.DefineLabel(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(FieldAutoSyncPacket), nameof(FieldAutoSyncPacket.value))); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(FieldSwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); + + il.Emit(OpCodes.Stloc, valueId); + + // If id is null set null + if (field.FieldType.IsClass) // structs cannot be null + { + var notNull = il.DefineLabel(); + + il.Emit(OpCodes.Ldloc, valueId); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(string), nameof(string.IsNullOrEmpty))); + il.Emit(OpCodes.Brfalse, notNull); + + il.Emit(OpCodes.Ldloc, instanceLocal); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Stfld, field); + il.Emit(OpCodes.Ret); + + il.MarkLabel(notNull); + } + + + // Get instance from id // Load objectmanager il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, objectManagerField); - var getObjectSuccess = il.DefineLabel(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(FieldAutoSyncPacket), nameof(FieldAutoSyncPacket.value))); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(FieldSwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); - + il.Emit(OpCodes.Ldloc, valueId); il.Emit(OpCodes.Ldloca, valueLocal); - il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(IObjectManager), nameof(IObjectManager.TryGetObject)).MakeGenericMethod(field.FieldType)); - il.Emit(OpCodes.Brtrue, getObjectSuccess); + il.Emit(OpCodes.Brtrue, setValue); // if TryGetObject failes log error il.Emit(OpCodes.Ldsfld, loggerField); il.Emit(OpCodes.Ldstr, errorString); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(FieldAutoSyncPacket), nameof(FieldAutoSyncPacket.value))); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(FieldSwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); + il.Emit(OpCodes.Ldloc, valueId); il.Emit(OpCodes.Call, stringConcatMethod); il.Emit(OpCodes.Call, AccessTools.Method(typeof(ILogger), nameof(ILogger.Error), new Type[] { typeof(string) })); - il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ret); - il.MarkLabel(getObjectSuccess); + // Set value + il.MarkLabel(setValue); + il.Emit(OpCodes.Ldloc, instanceLocal); il.Emit(OpCodes.Ldloc, valueLocal); il.Emit(OpCodes.Stfld, field); il.Emit(OpCodes.Ret); diff --git a/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs b/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs index 3ae17b135..62facbeec 100644 --- a/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs +++ b/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs @@ -13,6 +13,8 @@ using System.Linq; using ProtoBuf; using ProtoBuf.Meta; +using System.Diagnostics; +using Common.Util; namespace GameInterface.AutoSync.Fields; public class FieldTranspilerCreator @@ -20,6 +22,7 @@ public class FieldTranspilerCreator private readonly TypeBuilder typeBuilder; private readonly FieldBuilder loggerField; + private readonly MethodInfo logErrorFn; private readonly IObjectManager objectManager; private readonly Dictionary interceptMap; @@ -41,6 +44,14 @@ public FieldTranspilerCreator(IObjectManager objectManager, ModuleBuilder module loggerField = typeBuilder.DefineField("logger", typeof(ILogger), FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static); + const int parameterCount = 2; + + logErrorFn = typeof(ILogger).GetMethods(BindingFlags.Instance | BindingFlags.Public) + .Where( + m => m.Name == nameof(ILogger.Error) && + m.IsGenericMethodDefinition && + m.GetParameters().Count() == parameterCount) + .Single(); CreateStaticCtor(); @@ -61,6 +72,17 @@ public FieldTranspilerCreator(IObjectManager objectManager, ModuleBuilder module il.Emit(OpCodes.Ret); } + public static string GetStackTrace() + { + var stackTrace = new StackTrace(); + + var frames = stackTrace.GetFrames(); + + var result = string.Join("\n", frames.Take(frames.Count() - 1).Select(frame => frame.ToString())); + + return result; + } + private void CreateStaticCtor() { var cctorBuilder = typeBuilder.DefineConstructor( @@ -437,20 +459,12 @@ private MethodBuilder CreateInterceptByValue(int typeId, int propId, FieldInfo f var neqLabel = il.DefineLabel(); - // if (this.currentValue == newValue) return; - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, field); - il.Emit(OpCodes.Ldarg_1); + var allowedLabel = il.DefineLabel(); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(AllowedThread), nameof(AllowedThread.IsThisThreadAllowed))); + il.Emit(OpCodes.Brtrue, allowedLabel); - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Brfalse, neqLabel); - - il.Emit(OpCodes.Ret); - - - il.MarkLabel(neqLabel); - + // TODO add same value checking, has to be more complex than just ==, needs .Equals function to properly work with surrogates IsClientCheck(il, field); @@ -472,6 +486,8 @@ private MethodBuilder CreateInterceptByValue(int typeId, int propId, FieldInfo f il.Emit(OpCodes.Box, typeof(FieldAutoSyncPacket)); il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(INetwork), nameof(INetwork.SendAll), new Type[] { typeof(IPacket) })); + il.MarkLabel(allowedLabel); + il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, field); @@ -507,6 +523,11 @@ private MethodBuilder CreateInterceptByRef(int typeId, int propId, FieldInfo fie il.MarkLabel(neqLabel); + var allowedLabel = il.DefineLabel(); + + il.Emit(OpCodes.Call, AccessTools.Method(typeof(AllowedThread), nameof(AllowedThread.IsThisThreadAllowed))); + il.Emit(OpCodes.Brtrue, allowedLabel); + IsClientCheck(il, field); var networkLocal = TryResolve(il); @@ -528,6 +549,8 @@ private MethodBuilder CreateInterceptByRef(int typeId, int propId, FieldInfo fie il.Emit(OpCodes.Box, typeof(FieldAutoSyncPacket)); il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(INetwork), nameof(INetwork.SendAll), new Type[] { typeof(IPacket) })); + il.MarkLabel(allowedLabel); + il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, field); @@ -596,8 +619,12 @@ private void IsClientCheck(ILGenerator il, MemberInfo field) // Log error il.Emit(OpCodes.Ldsfld, loggerField); - il.Emit(OpCodes.Ldstr, $"Client attempted to change {field.Name}"); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(ILogger), nameof(ILogger.Error), new Type[] { typeof(string) })); + + il.Emit(OpCodes.Ldstr, $"Client attempted to change {field.Name} {{trace}}"); + + il.Emit(OpCodes.Call, AccessTools.PropertyGetter(typeof(Environment), nameof(Environment.StackTrace))); + + il.Emit(OpCodes.Callvirt, logErrorFn.MakeGenericMethod(typeof(string))); // Return il.Emit(OpCodes.Ret); diff --git a/source/GameInterface/AutoSync/Properties/PropertySwitchCreator.cs b/source/GameInterface/AutoSync/Properties/PropertySwitchCreator.cs index 298c6a210..b59bcf1fc 100644 --- a/source/GameInterface/AutoSync/Properties/PropertySwitchCreator.cs +++ b/source/GameInterface/AutoSync/Properties/PropertySwitchCreator.cs @@ -166,41 +166,58 @@ private void CreateByRef(ILGenerator il, PropertyInfo property, LocalBuilder ins var errorString = $"Unable to find instance of type {instanceType.Name} with id "; var stringConcatMethod = AccessTools.Method(typeof(PropertySwitchCreator), nameof(Concat)); + var valueId = il.DeclareLocal(typeof(string)); var valueLocal = il.DeclareLocal(property.PropertyType); - il.Emit(OpCodes.Ldloc, instanceLocal); - - // Load objectmanager - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, objectManagerField); + - var getObjectSuccess = il.DefineLabel(); + var setValue = il.DefineLabel(); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(PropertyAutoSyncPacket), nameof(PropertyAutoSyncPacket.value))); il.Emit(OpCodes.Call, AccessTools.Method(typeof(PropertySwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); + il.Emit(OpCodes.Stloc, valueId); - il.Emit(OpCodes.Ldloca, valueLocal); + // If id is null set null + if (property.PropertyType.IsClass) // structs cannot be null + { + var notNull = il.DefineLabel(); + + il.Emit(OpCodes.Ldloc, valueId); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(string), nameof(string.IsNullOrEmpty))); + il.Emit(OpCodes.Brfalse, notNull); + + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Stloc, valueLocal); + il.Emit(OpCodes.Br, setValue); + + il.MarkLabel(notNull); + } + + // Try get value from id + // Load objectmanager + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, objectManagerField); + il.Emit(OpCodes.Ldloc, valueId); + il.Emit(OpCodes.Ldloca, valueLocal); il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(IObjectManager), nameof(IObjectManager.TryGetObject)).MakeGenericMethod(property.PropertyType)); - il.Emit(OpCodes.Brtrue, getObjectSuccess); + il.Emit(OpCodes.Brtrue, setValue); // if TryGetObject failes log error il.Emit(OpCodes.Ldsfld, loggerField); il.Emit(OpCodes.Ldstr, errorString); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(PropertyAutoSyncPacket), nameof(PropertyAutoSyncPacket.value))); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(PropertySwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); + il.Emit(OpCodes.Ldloc, valueId); il.Emit(OpCodes.Call, stringConcatMethod); il.Emit(OpCodes.Call, AccessTools.Method(typeof(ILogger), nameof(ILogger.Error), new Type[] { typeof(string) })); - il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ret); - il.MarkLabel(getObjectSuccess); + il.MarkLabel(setValue); + il.Emit(OpCodes.Ldloc, instanceLocal); il.Emit(OpCodes.Ldloc, valueLocal); il.Emit(OpCodes.Call, AccessTools.Method(typeof(AllowedThread), nameof(AllowedThread.AllowThisThread))); diff --git a/source/GameInterface/Services/BasicCharacterObjects/BasicCharacterObjectRegistry.cs b/source/GameInterface/Services/BasicCharacterObjects/BasicCharacterObjectRegistry.cs deleted file mode 100644 index 219a3bc1b..000000000 --- a/source/GameInterface/Services/BasicCharacterObjects/BasicCharacterObjectRegistry.cs +++ /dev/null @@ -1,33 +0,0 @@ -using GameInterface.Services.Registry; -using System; -using System.Threading; -using TaleWorlds.CampaignSystem; -using TaleWorlds.Core; - -namespace GameInterface.Services.BasicCharacterObjects -{ - internal class BasicCharacterObjectRegistry : RegistryBase - { - private const string IdPrefix = "CoopBasicCharacter"; - private static int InstanceCounter = 0; - - public BasicCharacterObjectRegistry(IRegistryCollection collection) : base(collection) - { - } - - public override void RegisterAll() - { - foreach (BasicCharacterObject character in Campaign.Current.Characters) - { - if (TryGetId(character, out _)) continue; - - RegisterExistingObject(character.StringId, character); - } - } - - protected override string GetNewId(BasicCharacterObject obj) - { - return $"{IdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; - } - } -} diff --git a/source/GameInterface/Services/BasicCharacterObjects/Handlers/BasicCharacterLifetimeHandler.cs b/source/GameInterface/Services/BasicCharacterObjects/Handlers/BasicCharacterLifetimeHandler.cs deleted file mode 100644 index cf51b3f88..000000000 --- a/source/GameInterface/Services/BasicCharacterObjects/Handlers/BasicCharacterLifetimeHandler.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Common.Logging; -using Common.Messaging; -using Common.Network; -using Common.Util; -using GameInterface.Services.BasicCharacterObjects.Messages; -using GameInterface.Services.ObjectManager; -using Serilog; -using TaleWorlds.Core; - -namespace GameInterface.Services.BasicCharacterObjects.Handlers -{ - internal class BasicCharacterLifetimeHandler : IHandler - { - private static readonly ILogger Logger = LogManager.GetLogger(); - private readonly IMessageBroker messageBroker; - private readonly IObjectManager objectManager; - private readonly INetwork network; - - public BasicCharacterLifetimeHandler(IMessageBroker messageBroker, IObjectManager objectManager, INetwork network) - { - this.messageBroker = messageBroker; - this.objectManager = objectManager; - this.network = network; - messageBroker.Subscribe(Handle); - messageBroker.Subscribe(Handle); - } - - public void Dispose() - { - messageBroker.Unsubscribe(Handle); - messageBroker.Unsubscribe(Handle); - } - - private void Handle(MessagePayload obj) - { - var payload = obj.What; - - if (objectManager.AddNewObject(payload.CharacterObject, out string basicCharacterId) == false) return; - - var message = new NetworkCreateBasicCharacter(basicCharacterId); - network.SendAll(message); - } - - private void Handle(MessagePayload obj) - { - var payload = obj.What; - - var basicCharacter = ObjectHelper.SkipConstructor(); - if (objectManager.AddExisting(payload.BasicCharacterId, basicCharacter) == false) - { - Logger.Error("Failed to add existing Building, {id}", payload.BasicCharacterId); - return; - } - } - } -} diff --git a/source/GameInterface/Services/BasicCharacterObjects/Messages/BasicCharacterCreated.cs b/source/GameInterface/Services/BasicCharacterObjects/Messages/BasicCharacterCreated.cs deleted file mode 100644 index f0b7a6593..000000000 --- a/source/GameInterface/Services/BasicCharacterObjects/Messages/BasicCharacterCreated.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Common.Messaging; -using System; -using System.Collections.Generic; -using System.Text; -using TaleWorlds.Core; - -namespace GameInterface.Services.BasicCharacterObjects.Messages -{ - internal class BasicCharacterCreated : IEvent - { - public BasicCharacterObject CharacterObject { get; } - - public BasicCharacterCreated(BasicCharacterObject characterObject) - { - CharacterObject = characterObject; - } - } -} diff --git a/source/GameInterface/Services/BasicCharacterObjects/Messages/NetworkCreateBasicCharacter.cs b/source/GameInterface/Services/BasicCharacterObjects/Messages/NetworkCreateBasicCharacter.cs deleted file mode 100644 index 1dfd98fa3..000000000 --- a/source/GameInterface/Services/BasicCharacterObjects/Messages/NetworkCreateBasicCharacter.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Common.Messaging; -using ProtoBuf; - -namespace GameInterface.Services.BasicCharacterObjects.Messages -{ - [ProtoContract(SkipConstructor = true)] - internal class NetworkCreateBasicCharacter : ICommand - { - [ProtoMember(1)] - public string BasicCharacterId { get; } - - public NetworkCreateBasicCharacter(string basicCharacterId) - { - BasicCharacterId = basicCharacterId; - } - } -} diff --git a/source/GameInterface/Services/BasicCharacterObjects/Patches/BasicCharacterObjectLifetimePatches.cs b/source/GameInterface/Services/BasicCharacterObjects/Patches/BasicCharacterObjectLifetimePatches.cs deleted file mode 100644 index 3e5b20db1..000000000 --- a/source/GameInterface/Services/BasicCharacterObjects/Patches/BasicCharacterObjectLifetimePatches.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Common.Logging; -using Common.Messaging; -using GameInterface.Policies; -using GameInterface.Services.BasicCharacterObjects.Messages; -using GameInterface.Services.CharacterObjects.Messages; -using HarmonyLib; -using Serilog; -using System; -using TaleWorlds.CampaignSystem; -using TaleWorlds.Core; - -namespace GameInterface.Services.CharacterObjects.Patches -{ - /// - /// Lifetime Patches for BasicCharacterObjects - /// - [HarmonyPatch] - internal class BasicCharacterObjectLifetimePatches - { - private static ILogger Logger = LogManager.GetLogger(); - - [HarmonyPatch(typeof(BasicCharacterObject), MethodType.Constructor)] - [HarmonyPrefix] - private static bool CreateCharacterObjectPrefix(ref BasicCharacterObject __instance) - { - // Call original if we call this function - if (CallOriginalPolicy.IsOriginalAllowed()) return true; - - if (ModInformation.IsClient) - { - Logger.Error("Client created unmanaged {name}\n" - + "Callstack: {callstack}", typeof(BasicCharacterObject), Environment.StackTrace); - return false; - } - - var message = new BasicCharacterCreated(__instance); - - MessageBroker.Instance.Publish(__instance, message); - - return true; - } - } -} diff --git a/source/GameInterface/Services/BasicCultureObjects/BasicCultureObjectRegistry.cs b/source/GameInterface/Services/BasicCultureObjects/BasicCultureObjectRegistry.cs deleted file mode 100644 index 674f0b4a4..000000000 --- a/source/GameInterface/Services/BasicCultureObjects/BasicCultureObjectRegistry.cs +++ /dev/null @@ -1,39 +0,0 @@ -using GameInterface.Services.Registry; -using System.Threading; -using TaleWorlds.CampaignSystem; -using TaleWorlds.Core; -using TaleWorlds.ObjectSystem; - -namespace GameInterface.Services.BasicCultureObjects -{ - internal class BasicCultureObjectRegistry : RegistryBase - { - private const string IdPrefix = "CoopBasicCulture"; - private static int InstanceCounter = 0; - - public BasicCultureObjectRegistry(IRegistryCollection collection) : base(collection) - { - } - - public override void RegisterAll() - { - var objectManager = MBObjectManager.Instance; - - if (objectManager == null) - { - Logger.Error("Unable to register objects when CampaignObjectManager is null"); - return; - } - - foreach (var culture in objectManager.GetObjectTypeList()) - { - RegisterExistingObject(culture.StringId, culture); - } - } - - protected override string GetNewId(BasicCultureObject obj) - { - return $"{IdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; - } - } -} diff --git a/source/GameInterface/Services/BasicCultureObjects/Handlers/BasicCultureObjectLifetimeHandler.cs b/source/GameInterface/Services/BasicCultureObjects/Handlers/BasicCultureObjectLifetimeHandler.cs deleted file mode 100644 index 4fd8a4395..000000000 --- a/source/GameInterface/Services/BasicCultureObjects/Handlers/BasicCultureObjectLifetimeHandler.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Common.Logging; -using Common.Messaging; -using Common.Network; -using Common.Util; -using GameInterface.Services.BasicCultureObjects.Messages; -using GameInterface.Services.ObjectManager; -using Serilog; -using TaleWorlds.Core; - -namespace GameInterface.Services.BasicCultureObjects.Handlers -{ - internal class BasicCultureObjectLifetimeHandler : IHandler - { - private static readonly ILogger Logger = LogManager.GetLogger(); - private readonly IMessageBroker messageBroker; - private readonly IObjectManager objectManager; - private readonly INetwork network; - - public BasicCultureObjectLifetimeHandler(IMessageBroker messageBroker, IObjectManager objectManager, INetwork network) - { - this.messageBroker = messageBroker; - this.objectManager = objectManager; - this.network = network; - messageBroker.Subscribe(Handle); - messageBroker.Subscribe(Handle); - } - - public void Dispose() - { - messageBroker.Unsubscribe(Handle); - messageBroker.Unsubscribe(Handle); - } - - private void Handle(MessagePayload obj) - { - var payload = obj.What; - - if (objectManager.AddNewObject(payload.CultureObject, out string BasicCultureId) == false) return; - - var message = new NetworkCreateBasicCulture(BasicCultureId); - network.SendAll(message); - } - - private void Handle(MessagePayload obj) - { - var payload = obj.What; - - var BasicCulture = ObjectHelper.SkipConstructor(); - if (objectManager.AddExisting(payload.CultureId, BasicCulture) == false) - { - Logger.Error("Failed to add existing Building, {id}", payload.CultureId); - return; - } - } - } -} \ No newline at end of file diff --git a/source/GameInterface/Services/BasicCultureObjects/Messages/BasicCultureCreated.cs b/source/GameInterface/Services/BasicCultureObjects/Messages/BasicCultureCreated.cs deleted file mode 100644 index e63cd3eba..000000000 --- a/source/GameInterface/Services/BasicCultureObjects/Messages/BasicCultureCreated.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Common.Messaging; -using TaleWorlds.Core; - -namespace GameInterface.Services.BasicCultureObjects.Messages -{ - internal class BasicCultureCreated : IEvent - { - public BasicCultureObject CultureObject { get; } - - public BasicCultureCreated(BasicCultureObject cultureObject) - { - CultureObject = cultureObject; - } - } -} diff --git a/source/GameInterface/Services/BasicCultureObjects/Messages/NetworkCreateBasicCulture.cs b/source/GameInterface/Services/BasicCultureObjects/Messages/NetworkCreateBasicCulture.cs deleted file mode 100644 index 999e0dc69..000000000 --- a/source/GameInterface/Services/BasicCultureObjects/Messages/NetworkCreateBasicCulture.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Common.Messaging; -using ProtoBuf; - -namespace GameInterface.Services.BasicCultureObjects.Messages -{ - [ProtoContract(SkipConstructor = true)] - internal class NetworkCreateBasicCulture : ICommand - { - [ProtoMember(1)] - public string CultureId { get; } - - public NetworkCreateBasicCulture(string cultureId) - { - CultureId = cultureId; - } - } -} diff --git a/source/GameInterface/Services/BasicCultureObjects/Patches/BasicCultureObjectLifetimePatches.cs b/source/GameInterface/Services/BasicCultureObjects/Patches/BasicCultureObjectLifetimePatches.cs deleted file mode 100644 index 11354f653..000000000 --- a/source/GameInterface/Services/BasicCultureObjects/Patches/BasicCultureObjectLifetimePatches.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Common.Logging; -using Common.Messaging; -using GameInterface.Policies; -using GameInterface.Services.BasicCultureObjects.Messages; -using HarmonyLib; -using Serilog; -using System; -using TaleWorlds.Core; - -namespace GameInterface.Services.BasicCultureObjects.Patches -{ - /// - /// Lifetime Patches for BasicCultureObjects - /// - [HarmonyPatch] - internal class BasicCultureObjectLifetimePatches - { - private static ILogger Logger = LogManager.GetLogger(); - - [HarmonyPatch(typeof(BasicCultureObject), MethodType.Constructor)] - [HarmonyPrefix] - private static bool CreateBasicCultureObjectPrefix(ref BasicCultureObject __instance) - { - // Call original if we call this function - if (CallOriginalPolicy.IsOriginalAllowed()) return true; - - if (ModInformation.IsClient) - { - Logger.Error("Client created unmanaged {name}\n" - + "Callstack: {callstack}", typeof(BasicCultureObject), Environment.StackTrace); - return false; - } - - var message = new BasicCultureCreated(__instance); - - MessageBroker.Instance.Publish(__instance, message); - - return true; - } - } -} diff --git a/source/GameInterface/Services/BesiegerCamps/BesiegerCampRegistry.cs b/source/GameInterface/Services/BesiegerCamps/BesiegerCampRegistry.cs index 3f78aa2b3..97cf7614f 100644 --- a/source/GameInterface/Services/BesiegerCamps/BesiegerCampRegistry.cs +++ b/source/GameInterface/Services/BesiegerCamps/BesiegerCampRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.BesiegerCamps; internal class BeseigerCampRegistry : RegistryBase { private const string BeseigerCampIdPrefix = "CoopBeseigerCamp"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public BeseigerCampRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/Buildings/BuildingRegistry.cs b/source/GameInterface/Services/Buildings/BuildingRegistry.cs index 96b9e8bd3..899fafa27 100644 --- a/source/GameInterface/Services/Buildings/BuildingRegistry.cs +++ b/source/GameInterface/Services/Buildings/BuildingRegistry.cs @@ -13,7 +13,7 @@ namespace GameInterface.Services.Armies; internal class BuildingRegistry : RegistryBase { private const string BuildingIdPrefix = "CoopBuilding"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public BuildingRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/CharacterObjects/CharacterObjectRegistry.cs b/source/GameInterface/Services/CharacterObjects/CharacterObjectRegistry.cs index 72fdc0115..3a0bda0c9 100644 --- a/source/GameInterface/Services/CharacterObjects/CharacterObjectRegistry.cs +++ b/source/GameInterface/Services/CharacterObjects/CharacterObjectRegistry.cs @@ -1,6 +1,9 @@ using GameInterface.Services.Registry; +using System; +using System.Collections.Generic; using System.Threading; using TaleWorlds.CampaignSystem; +using TaleWorlds.Core; namespace GameInterface.Services.Armies; @@ -9,8 +12,14 @@ namespace GameInterface.Services.Armies; /// internal class CharacterObjectRegistry : RegistryBase { + public override IEnumerable ManagedTypes => new Type[] + { + typeof(BasicCharacterObject), + typeof(CharacterObject) + }; + private const string CharacterObjectPrefix = "CoopCharacterObject"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public CharacterObjectRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs b/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs index 00a8da106..d856b142e 100644 --- a/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs +++ b/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs @@ -5,7 +5,11 @@ using HarmonyLib; using Serilog; using System; +using System.Collections.Generic; +using System.Reflection.Emit; +using System.Reflection; using TaleWorlds.CampaignSystem; +using TaleWorlds.ObjectSystem; namespace GameInterface.Services.CharacterObjects.Patches { @@ -38,4 +42,33 @@ private static bool CreateCharacterObjectPrefix(ref CharacterObject __instance) return true; } } + + + [HarmonyPatch] + internal class MBObjectManagerLifetimePatches + { + private static ILogger Logger = LogManager.GetLogger(); + + private static IEnumerable TargetMethods() + { + yield return AccessTools.Method(typeof(MBObjectManager), nameof(MBObjectManager.CreateObject), new Type[] { typeof(string) }).MakeGenericMethod(typeof(CharacterObject)); + } + + [HarmonyTranspiler] + private static IEnumerable CreateFromTranspiler(IEnumerable instructions) + { + foreach (var instr in instructions) + { + if (instr.Calls(AccessTools.PropertySetter(typeof(MBObjectBase), nameof(MBObjectBase.StringId)))) + { + yield return new CodeInstruction(OpCodes.Pop); + yield return new CodeInstruction(OpCodes.Pop); + } + else + { + yield return instr; + } + } + } + } } diff --git a/source/GameInterface/Services/Clans/ClanRegistry.cs b/source/GameInterface/Services/Clans/ClanRegistry.cs index ec6363d24..1bea25ade 100644 --- a/source/GameInterface/Services/Clans/ClanRegistry.cs +++ b/source/GameInterface/Services/Clans/ClanRegistry.cs @@ -11,7 +11,7 @@ namespace GameInterface.Services.Clans; internal class ClanRegistry : RegistryBase { private const string ClanStringIdPrefix = "CoopClan"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public ClanRegistry(IRegistryCollection collection) : base(collection) { } @@ -28,7 +28,7 @@ public override void RegisterAll() foreach (var clan in objectManager.Clans) { RegisterExistingObject(clan.StringId, clan); - Interlocked.Increment(ref InstanceCount); + Interlocked.Increment(ref InstanceCounter); } } diff --git a/source/GameInterface/Services/CraftingService/CraftingRegistry.cs b/source/GameInterface/Services/CraftingService/CraftingRegistry.cs index ed1df164f..42b8563c3 100644 --- a/source/GameInterface/Services/CraftingService/CraftingRegistry.cs +++ b/source/GameInterface/Services/CraftingService/CraftingRegistry.cs @@ -7,7 +7,7 @@ namespace GameInterface.Services.CraftingService internal class CraftingRegistry : RegistryBase { private const string CraftingIdPrefix = "CoopCrafting"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public CraftingRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs b/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs index da963fffd..00a95ed3e 100644 --- a/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs +++ b/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs @@ -1,14 +1,22 @@ using GameInterface.Services.Registry; +using System; +using System.Collections.Generic; using System.Threading; using TaleWorlds.CampaignSystem; +using TaleWorlds.Core; using TaleWorlds.ObjectSystem; namespace GameInterface.Services.CultureObjects { internal class CultureObjectRegistry : RegistryBase { + public override IEnumerable ManagedTypes => new Type[] { + typeof(BasicCultureObject), + typeof(CultureObject), + }; + private const string CultureStringIdPrefix = "CoopCulture"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public CultureObjectRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs b/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs index d312cb61e..f8d97d3b3 100644 --- a/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs +++ b/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs @@ -5,7 +5,11 @@ using HarmonyLib; using Serilog; using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; using TaleWorlds.CampaignSystem; +using TaleWorlds.ObjectSystem; namespace GameInterface.Services.CultureObjects.Patches { @@ -36,4 +40,32 @@ private static bool ctorPrefix(ref CultureObject __instance) return true; } } + + [HarmonyPatch] + internal class MBObjectManagerLifetimePatches + { + private static ILogger Logger = LogManager.GetLogger(); + + private static IEnumerable TargetMethods() + { + yield return AccessTools.Method(typeof(MBObjectManager), nameof(MBObjectManager.CreateObject), new Type[] { typeof(string) }).MakeGenericMethod(typeof(CultureObject)); + } + + [HarmonyTranspiler] + private static IEnumerable CreateFromTranspiler(IEnumerable instructions) + { + foreach (var instr in instructions) + { + if (instr.Calls(AccessTools.PropertySetter(typeof(MBObjectBase), nameof(MBObjectBase.StringId)))) + { + yield return new CodeInstruction(OpCodes.Pop); + yield return new CodeInstruction(OpCodes.Pop); + } + else + { + yield return instr; + } + } + } + } } diff --git a/source/GameInterface/Services/Equipments/EquipmentRegistry.cs b/source/GameInterface/Services/Equipments/EquipmentRegistry.cs index cae6c59a8..f4fc239e1 100644 --- a/source/GameInterface/Services/Equipments/EquipmentRegistry.cs +++ b/source/GameInterface/Services/Equipments/EquipmentRegistry.cs @@ -15,7 +15,7 @@ namespace GameInterface.Services.Equipments; internal class EquipmentRegistry : RegistryBase { private const string EquipmentPrefix = $"Coop{nameof(Equipment)}"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public EquipmentRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/Equipments/Handler/EquipmentHandler.cs b/source/GameInterface/Services/Equipments/Handler/EquipmentHandler.cs index 718d8387a..3d5c61057 100644 --- a/source/GameInterface/Services/Equipments/Handler/EquipmentHandler.cs +++ b/source/GameInterface/Services/Equipments/Handler/EquipmentHandler.cs @@ -96,36 +96,26 @@ private void Handle(MessagePayload obj) { var payload = obj.What.Data; - Equipment newEquipment = ObjectHelper.SkipConstructor(); GameLoopRunner.RunOnMainThread(() => { - using (new AllowedThread()) - { - Equipment_ctor.Invoke(newEquipment, Array.Empty()); - } - objectManager.AddExisting(payload.EquipmentId, newEquipment); - + using (new AllowedThread()) + { + var newEquipment = new Equipment(); + objectManager.AddExisting(payload.EquipmentId, newEquipment); + } }); } private void Handle(MessagePayload obj) { var payload = obj.What.Data; - Equipment propertyEquipment = null; - if (payload.EquipmentPropertyId != null) - { - if (objectManager.TryGetObject(payload.EquipmentPropertyId, out propertyEquipment) == false) - { - Logger.Error("Equipment param not found in object manager. Breaking field sync inside ctor."); - return; - } - } - Equipment newEquipment = ObjectHelper.SkipConstructor(); + objectManager.TryGetObject(payload.EquipmentPropertyId, out var propertyEquipment); + GameLoopRunner.RunOnMainThread(() => { using (new AllowedThread()) { - EquipmentParam_ctor.Invoke(newEquipment, new object[] { propertyEquipment }); + var newEquipment = new Equipment(propertyEquipment); objectManager.AddExisting(payload.EquipmentId, newEquipment); } diff --git a/source/GameInterface/Services/Equipments/Patches/EquipmentLifetimePatches.cs b/source/GameInterface/Services/Equipments/Patches/EquipmentLifetimePatches.cs index 1568c9e17..6ffd14aae 100644 --- a/source/GameInterface/Services/Equipments/Patches/EquipmentLifetimePatches.cs +++ b/source/GameInterface/Services/Equipments/Patches/EquipmentLifetimePatches.cs @@ -32,10 +32,10 @@ private static bool CreateEquipmentPrefix(Equipment __instance) if (ModInformation.IsClient) - { /* + { Logger.Error("Client created unmanaged {name}\n" + "Callstack: {callstack}", typeof(Equipment), Environment.StackTrace); - */ + return true; } @@ -53,10 +53,10 @@ private static bool CreateEquipmentParamPrefix(Equipment __instance, Equipment e if (ModInformation.IsClient) { - /* Logger.Error("Client created unmanaged {name}\n" + Logger.Error("Client created unmanaged {name}\n" + "Callstack: {callstack}", typeof(Equipment), Environment.StackTrace); - */ + return true; } @@ -67,21 +67,20 @@ private static bool CreateEquipmentParamPrefix(Equipment __instance, Equipment e [HarmonyPatch(typeof(Hero), nameof(Hero.OnDeath))] [HarmonyPrefix] - private static bool OnDeathPrefix(ref Hero __instance) + private static void OnDeathPrefix(ref Hero __instance) { - if (CallOriginalPolicy.IsOriginalAllowed()) return true; + if (CallOriginalPolicy.IsOriginalAllowed()) return; if (ModInformation.IsClient) { Logger.Error("Client created unmanaged {name}\n" + "Callstack: {callstack}", typeof(Hero), Environment.StackTrace); - return true; + return; } var message = new EquipmentRemoved(__instance.BattleEquipment, __instance.CivilianEquipment); MessageBroker.Instance.Publish(__instance, message); - return true; } diff --git a/source/GameInterface/Services/Heroes/Handlers/HeroFieldsHandler.cs b/source/GameInterface/Services/Heroes/Handlers/HeroFieldsHandler.cs index 7da3ea7b7..3b4d342bb 100644 --- a/source/GameInterface/Services/Heroes/Handlers/HeroFieldsHandler.cs +++ b/source/GameInterface/Services/Heroes/Handlers/HeroFieldsHandler.cs @@ -82,6 +82,13 @@ private void Handle(MessagePayload payload) Logger.Error("Unable to find {type} with id: {id}", typeof(Hero), data.HeroId); return; } + + if (data.SettlementStringId == null) + { + instance._homeSettlement = null; + return; + } + if (objectManager.TryGetObject(data.SettlementStringId, out var settlement) == false) { Logger.Error("Unable to find {type} with id: {id}", typeof(Settlement), data.SettlementStringId); diff --git a/source/GameInterface/Services/ItemObjects/ItemObjectRegistry.cs b/source/GameInterface/Services/ItemObjects/ItemObjectRegistry.cs index b2f7d122a..f16319421 100644 --- a/source/GameInterface/Services/ItemObjects/ItemObjectRegistry.cs +++ b/source/GameInterface/Services/ItemObjects/ItemObjectRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.ItemObjects internal class ItemObjectRegistry : RegistryBase { private const string ItemObjectIdPrefix = "CoopItemObject"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public ItemObjectRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/MapEventSides/MapEventSideRegistry.cs b/source/GameInterface/Services/MapEventSides/MapEventSideRegistry.cs index 4db009fe1..0d8f83ee5 100644 --- a/source/GameInterface/Services/MapEventSides/MapEventSideRegistry.cs +++ b/source/GameInterface/Services/MapEventSides/MapEventSideRegistry.cs @@ -12,7 +12,7 @@ namespace GameInterface.Services.MapEvents; internal class MapEventSideRegistry : RegistryBase { private const string MapEventSideIdPrefix = "CoopMapEventSide"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public MapEventSideRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/MapEvents/MapEventRegistry.cs b/source/GameInterface/Services/MapEvents/MapEventRegistry.cs index 2c7f635d3..ff6d0bc11 100644 --- a/source/GameInterface/Services/MapEvents/MapEventRegistry.cs +++ b/source/GameInterface/Services/MapEvents/MapEventRegistry.cs @@ -11,7 +11,7 @@ namespace GameInterface.Services.MapEvents; internal class MapEventRegistry : RegistryBase { private const string MapEventIdPrefix = "CoopMapEvent"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public MapEventRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs b/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs index e4c5cbed6..f3edb9b1a 100644 --- a/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs +++ b/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs @@ -56,7 +56,7 @@ private void Handle(MessagePayload payload) if (objectManager.TryGetObject(data.AttachedToId, out var attachedToMobileParty) == false) { - Logger.Error("Unable to find {type} with id: {id}", typeof(Settlement), data.AttachedToId); + Logger.Error("Unable to find {type} with id: {id}", typeof(MobileParty), data.AttachedToId); return; } diff --git a/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs b/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs index db164d046..3db0c6f35 100644 --- a/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs +++ b/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs @@ -14,7 +14,7 @@ namespace GameInterface.Services.MobileParties; internal class MobilePartyRegistry : RegistryBase { private const string PartyStringIdPrefix = "CoopParty"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; private readonly IMessageBroker messageBroker; public MobilePartyRegistry(IRegistryCollection collection, IMessageBroker messageBroker) : base(collection) diff --git a/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiRegistry.cs b/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiRegistry.cs index 618cd6901..37f39be96 100644 --- a/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiRegistry.cs +++ b/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiRegistry.cs @@ -8,7 +8,7 @@ namespace GameInterface.Services.MobilePartyAIs; internal class MobilePartyAiRegistry : RegistryBase { private const string MobilePartyAiIdPrefix = "CoopMobilePartyAi"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public MobilePartyAiRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/PartyBases/PartyBaseRegistry.cs b/source/GameInterface/Services/PartyBases/PartyBaseRegistry.cs index b7c73eabf..c4d12374e 100644 --- a/source/GameInterface/Services/PartyBases/PartyBaseRegistry.cs +++ b/source/GameInterface/Services/PartyBases/PartyBaseRegistry.cs @@ -6,7 +6,7 @@ namespace GameInterface.Services.PartyBases; internal class PartyBaseRegistry : RegistryBase { private const string PartyBaseIdPrefix = "CoopPartyBase"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public PartyBaseRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/PartyComponents/PartyComponentRegistry.cs b/source/GameInterface/Services/PartyComponents/PartyComponentRegistry.cs index 60b06de30..47572163b 100644 --- a/source/GameInterface/Services/PartyComponents/PartyComponentRegistry.cs +++ b/source/GameInterface/Services/PartyComponents/PartyComponentRegistry.cs @@ -14,7 +14,7 @@ namespace GameInterface.Services.PartyComponents; internal class PartyComponentRegistry : RegistryBase { private const string PartyComponentIdPrefix = "CoopPartyComponent"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public PartyComponentRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/PartyVisuals/PartyVisualRegistry.cs b/source/GameInterface/Services/PartyVisuals/PartyVisualRegistry.cs index e400c20c5..5e2c11159 100644 --- a/source/GameInterface/Services/PartyVisuals/PartyVisualRegistry.cs +++ b/source/GameInterface/Services/PartyVisuals/PartyVisualRegistry.cs @@ -10,7 +10,7 @@ namespace GameInterface.Services.PartyVisuals internal class PartyVisualRegistry : RegistryBase { private const string PartyVisualIdPrefix = $"Coop{nameof(PartyVisual)}"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public PartyVisualRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/SettlementComponents/SettlementComponentRegistry.cs b/source/GameInterface/Services/SettlementComponents/SettlementComponentRegistry.cs index 6509cc952..32884a3ff 100644 --- a/source/GameInterface/Services/SettlementComponents/SettlementComponentRegistry.cs +++ b/source/GameInterface/Services/SettlementComponents/SettlementComponentRegistry.cs @@ -8,7 +8,7 @@ namespace GameInterface.Services.SettlementComponents; internal class SettlementComponentRegistry : RegistryBase { private const string SettlementComponentIdPrefix = "CoopSettlementComponent"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public override IEnumerable ManagedTypes { get; } = new Type[] { diff --git a/source/GameInterface/Services/Settlements/SettlementRegistry.cs b/source/GameInterface/Services/Settlements/SettlementRegistry.cs index da0580ae2..cd887e707 100644 --- a/source/GameInterface/Services/Settlements/SettlementRegistry.cs +++ b/source/GameInterface/Services/Settlements/SettlementRegistry.cs @@ -11,7 +11,7 @@ namespace GameInterface.Services.Settlements; internal class SettlementRegistry : RegistryBase { public static readonly string SettlementStringIdPrefix = "CoopSettlement"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public SettlementRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/SiegeEngineConstructionProgress/SiegeEngineConstructionProgressRegistry.cs b/source/GameInterface/Services/SiegeEngineConstructionProgress/SiegeEngineConstructionProgressRegistry.cs index b71e1ab71..b730052ea 100644 --- a/source/GameInterface/Services/SiegeEngineConstructionProgress/SiegeEngineConstructionProgressRegistry.cs +++ b/source/GameInterface/Services/SiegeEngineConstructionProgress/SiegeEngineConstructionProgressRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.SiegeEngines internal class SiegeEngineConstructionProgressRegistry : RegistryBase { private const string SiegeEngineConstructionProgressIdPrefix = "CoopSiegeEngineConstructionProgress"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public SiegeEngineConstructionProgressRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/SiegeEngines/SiegeEnginesContainerRegistry.cs b/source/GameInterface/Services/SiegeEngines/SiegeEnginesContainerRegistry.cs index a6dabb7c5..e732bb6eb 100644 --- a/source/GameInterface/Services/SiegeEngines/SiegeEnginesContainerRegistry.cs +++ b/source/GameInterface/Services/SiegeEngines/SiegeEnginesContainerRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.SiegeEngines internal class SiegeEnginesContainerRegistry : RegistryBase { private const string SiegeEnginesContainerIdPrefix = "CoopSiegeEnginesContainer"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public SiegeEnginesContainerRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/SiegeEvents/SiegeEventRegistry.cs b/source/GameInterface/Services/SiegeEvents/SiegeEventRegistry.cs index fdc59cf91..b73983e01 100644 --- a/source/GameInterface/Services/SiegeEvents/SiegeEventRegistry.cs +++ b/source/GameInterface/Services/SiegeEvents/SiegeEventRegistry.cs @@ -13,7 +13,7 @@ namespace GameInterface.Services.SiegeEvents; internal class SiegeEventRegistry : RegistryBase { private const string SiegeEventIdPrefix = "CoopSiegeEvent"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public SiegeEventRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/WeaponDesigns/WeaponDesignRegistry.cs b/source/GameInterface/Services/WeaponDesigns/WeaponDesignRegistry.cs index fc25b2213..868836079 100644 --- a/source/GameInterface/Services/WeaponDesigns/WeaponDesignRegistry.cs +++ b/source/GameInterface/Services/WeaponDesigns/WeaponDesignRegistry.cs @@ -11,7 +11,7 @@ namespace GameInterface.Services.ItemObjects internal class WeaponDesignRegistry : RegistryBase { private const string ItemObjectIdPrefix = "CoopWeaponDesign"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public WeaponDesignRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/Workshops/WorkshopRegistry.cs b/source/GameInterface/Services/Workshops/WorkshopRegistry.cs index d86604817..33cfab4f8 100644 --- a/source/GameInterface/Services/Workshops/WorkshopRegistry.cs +++ b/source/GameInterface/Services/Workshops/WorkshopRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.Workshops internal class WorkshopRegistry : RegistryBase { private const string WorkshopIdPrefix = $"Coop{nameof(Workshop)}"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public WorkshopRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs b/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs index f0bad3dc7..f8e17a3d9 100644 --- a/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs +++ b/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs @@ -1,11 +1,5 @@ using GameInterface.Services.Registry; -using ProtoBuf.Meta; -using System; -using System.Collections.Generic; -using System.Text; using System.Threading; -using TaleWorlds.CampaignSystem.MapEvents; -using TaleWorlds.CampaignSystem.Settlements; using TaleWorlds.CampaignSystem.Settlements.Workshops; namespace GameInterface.Services.Workshops @@ -13,7 +7,7 @@ namespace GameInterface.Services.Workshops internal class WorkshopTypeRegistry : RegistryBase { private const string WorkshopTypePrefix = $"Coop{nameof(WorkshopType)}"; - private static int InstanceCounter = 0; + private int InstanceCounter = 0; public WorkshopTypeRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Surrogates/SurrogateCollection.cs b/source/GameInterface/Surrogates/SurrogateCollection.cs index df2907802..cb4d9582a 100644 --- a/source/GameInterface/Surrogates/SurrogateCollection.cs +++ b/source/GameInterface/Surrogates/SurrogateCollection.cs @@ -25,11 +25,9 @@ public SurrogateCollection() private void AddSurrogate() { - try + if (RuntimeTypeModel.Default.CanSerialize(typeof(T)) == false) { RuntimeTypeModel.Default.SetSurrogate(); } - catch (InvalidOperationException) { } - catch (ArgumentException) { } } } From 7794f7c52961c6715210318cec70fa2ee977b183 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 7 Dec 2024 19:23:36 -0600 Subject: [PATCH 18/22] minor updates --- source/Common/Logging/BatchLogger.cs | 2 +- source/GameInterface/ContainerProvider.cs | 13 ++++++++++++- .../GameInterface/Services/Heroes/HeroRegistry.cs | 8 +------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/source/Common/Logging/BatchLogger.cs b/source/Common/Logging/BatchLogger.cs index 5ac62644e..b280f299f 100644 --- a/source/Common/Logging/BatchLogger.cs +++ b/source/Common/Logging/BatchLogger.cs @@ -30,7 +30,7 @@ public BatchLogger(TimeSpan pollInterval) { this.pollInterval = pollInterval; poller = new Poller(Poll, pollInterval); - //poller.Start(); + poller.Start(); } /// diff --git a/source/GameInterface/ContainerProvider.cs b/source/GameInterface/ContainerProvider.cs index 307d5b1fb..ca34f2a6c 100644 --- a/source/GameInterface/ContainerProvider.cs +++ b/source/GameInterface/ContainerProvider.cs @@ -26,7 +26,18 @@ public static bool TryGetContainer(out ILifetimeScope lifetimeScope) { lifetimeScope = _lifetimeScope; - return lifetimeScope != null; + if (lifetimeScope == null) + { + var callStack = Environment.StackTrace; + Logger.Error("{name} was not setup properly, try using {setupFnName}\n" + + "CallStack: {callStack}", + nameof(ContainerProvider), + nameof(SetContainer), + callStack); + return false; + } + + return true; } public static bool TryResolve(out T instance) where T : class diff --git a/source/GameInterface/Services/Heroes/HeroRegistry.cs b/source/GameInterface/Services/Heroes/HeroRegistry.cs index cccc44f67..d51eb77d8 100644 --- a/source/GameInterface/Services/Heroes/HeroRegistry.cs +++ b/source/GameInterface/Services/Heroes/HeroRegistry.cs @@ -1,11 +1,5 @@ -using Common; -using Common.Extensions; -using System; -using System.Linq; -using System.Reflection; -using System.Threading; +using System.Linq; using TaleWorlds.CampaignSystem; -using TaleWorlds.CampaignSystem.Party; namespace GameInterface.Services.Registry; From 0b8ebc602b649725074118daa5200e45be23a095 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 7 Dec 2024 19:24:36 -0600 Subject: [PATCH 19/22] Update PartyAIBehaviorPacketHandler.cs --- .../MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs b/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs index fc847c329..3a1dadf52 100644 --- a/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs +++ b/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs @@ -21,7 +21,6 @@ internal class RequestMobilePartyBehaviorPacketHandler : IPacketHandler private readonly IPacketManager packetManager; private readonly INetwork network; private readonly IMessageBroker messageBroker; - private readonly ILogger logger; public RequestMobilePartyBehaviorPacketHandler( IPacketManager packetManager, From d1adca17ce868edf3e5e795b606d3db095f97bd8 Mon Sep 17 00:00:00 2001 From: Garrett Luskey Date: Sat, 7 Dec 2024 20:09:48 -0600 Subject: [PATCH 20/22] partial fix for objectmanager setting string id --- .../Environment/Mock/MockServer.cs | 5 -- .../Environment/Instance/GameInstance.cs | 4 +- source/GameInterface/ContainerProvider.cs | 12 ++--- .../Patches/CharacterObjectLifetimePatches.cs | 50 +++++++++---------- .../Patches/CultureObjectLifetimePatches.cs | 50 +++++++++---------- 5 files changed, 58 insertions(+), 63 deletions(-) diff --git a/source/Coop.IntegrationTests/Environment/Mock/MockServer.cs b/source/Coop.IntegrationTests/Environment/Mock/MockServer.cs index 3fe7f2b92..c14a0ab6c 100644 --- a/source/Coop.IntegrationTests/Environment/Mock/MockServer.cs +++ b/source/Coop.IntegrationTests/Environment/Mock/MockServer.cs @@ -26,11 +26,6 @@ public void RemovePeer(NetPeer peer) peers.Remove(peer); } - public void Dispose() - { - throw new NotImplementedException(); - } - public void OnConnectionRequest(ConnectionRequest request) { throw new NotImplementedException(); diff --git a/source/E2E.Tests/Environment/Instance/GameInstance.cs b/source/E2E.Tests/Environment/Instance/GameInstance.cs index 47b9ebc8b..c5ef49773 100644 --- a/source/E2E.Tests/Environment/Instance/GameInstance.cs +++ b/source/E2E.Tests/Environment/Instance/GameInstance.cs @@ -33,8 +33,8 @@ public GameInstance() Module = (Module)AccessTools.Constructor(typeof(Module)).Invoke(null); GameManager = new SandBoxGameManager(); Campaign = new Campaign(CampaignGameMode.Campaign); - MBObjectManager = MBObjectManager.Instance; Game = Game.CreateGame(Campaign, GameManager); + MBObjectManager = MBObjectManager.Instance; RegisterType(MBObjectManager); @@ -72,6 +72,6 @@ public void SetStatics() MBObjectManager.Instance = MBObjectManager; Campaign.Current = Campaign; Game.Current = Game; - AccessTools.Property(typeof(Module), nameof(Module.CurrentModule)).SetValue(null, Module); + Module.CurrentModule = Module; } } diff --git a/source/GameInterface/ContainerProvider.cs b/source/GameInterface/ContainerProvider.cs index ca34f2a6c..572d1c3be 100644 --- a/source/GameInterface/ContainerProvider.cs +++ b/source/GameInterface/ContainerProvider.cs @@ -28,12 +28,12 @@ public static bool TryGetContainer(out ILifetimeScope lifetimeScope) if (lifetimeScope == null) { - var callStack = Environment.StackTrace; - Logger.Error("{name} was not setup properly, try using {setupFnName}\n" + - "CallStack: {callStack}", - nameof(ContainerProvider), - nameof(SetContainer), - callStack); + //var callStack = Environment.StackTrace; + //Logger.Error("{name} was not setup properly, try using {setupFnName}\n" + + // "CallStack: {callStack}", + // nameof(ContainerProvider), + // nameof(SetContainer), + // callStack); return false; } diff --git a/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs b/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs index d856b142e..cf880e46b 100644 --- a/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs +++ b/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs @@ -44,31 +44,31 @@ private static bool CreateCharacterObjectPrefix(ref CharacterObject __instance) } - [HarmonyPatch] - internal class MBObjectManagerLifetimePatches - { - private static ILogger Logger = LogManager.GetLogger(); + //[HarmonyPatch] + //internal class MBObjectManagerLifetimePatches + //{ + // private static ILogger Logger = LogManager.GetLogger(); - private static IEnumerable TargetMethods() - { - yield return AccessTools.Method(typeof(MBObjectManager), nameof(MBObjectManager.CreateObject), new Type[] { typeof(string) }).MakeGenericMethod(typeof(CharacterObject)); - } + // private static IEnumerable TargetMethods() + // { + // yield return AccessTools.Method(typeof(MBObjectManager), nameof(MBObjectManager.CreateObject), new Type[] { typeof(string) }).MakeGenericMethod(typeof(CharacterObject)); + // } - [HarmonyTranspiler] - private static IEnumerable CreateFromTranspiler(IEnumerable instructions) - { - foreach (var instr in instructions) - { - if (instr.Calls(AccessTools.PropertySetter(typeof(MBObjectBase), nameof(MBObjectBase.StringId)))) - { - yield return new CodeInstruction(OpCodes.Pop); - yield return new CodeInstruction(OpCodes.Pop); - } - else - { - yield return instr; - } - } - } - } + // [HarmonyTranspiler] + // private static IEnumerable CreateFromTranspiler(IEnumerable instructions) + // { + // foreach (var instr in instructions) + // { + // if (instr.Calls(AccessTools.PropertySetter(typeof(MBObjectBase), nameof(MBObjectBase.StringId)))) + // { + // yield return new CodeInstruction(OpCodes.Pop); + // yield return new CodeInstruction(OpCodes.Pop); + // } + // else + // { + // yield return instr; + // } + // } + // } + //} } diff --git a/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs b/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs index f8d97d3b3..6a9f0a9dd 100644 --- a/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs +++ b/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs @@ -41,31 +41,31 @@ private static bool ctorPrefix(ref CultureObject __instance) } } - [HarmonyPatch] - internal class MBObjectManagerLifetimePatches - { - private static ILogger Logger = LogManager.GetLogger(); + //[HarmonyPatch] + //internal class MBObjectManagerLifetimePatches + //{ + // private static ILogger Logger = LogManager.GetLogger(); - private static IEnumerable TargetMethods() - { - yield return AccessTools.Method(typeof(MBObjectManager), nameof(MBObjectManager.CreateObject), new Type[] { typeof(string) }).MakeGenericMethod(typeof(CultureObject)); - } + // private static IEnumerable TargetMethods() + // { + // yield return AccessTools.Method(typeof(MBObjectManager), nameof(MBObjectManager.CreateObject), new Type[] { typeof(string) }).MakeGenericMethod(typeof(CultureObject)); + // } - [HarmonyTranspiler] - private static IEnumerable CreateFromTranspiler(IEnumerable instructions) - { - foreach (var instr in instructions) - { - if (instr.Calls(AccessTools.PropertySetter(typeof(MBObjectBase), nameof(MBObjectBase.StringId)))) - { - yield return new CodeInstruction(OpCodes.Pop); - yield return new CodeInstruction(OpCodes.Pop); - } - else - { - yield return instr; - } - } - } - } + // [HarmonyTranspiler] + // private static IEnumerable CreateFromTranspiler(IEnumerable instructions) + // { + // foreach (var instr in instructions) + // { + // if (instr.Calls(AccessTools.PropertySetter(typeof(MBObjectBase), nameof(MBObjectBase.StringId)))) + // { + // yield return new CodeInstruction(OpCodes.Pop); + // yield return new CodeInstruction(OpCodes.Pop); + // } + // else + // { + // yield return instr; + // } + // } + // } + //} } From 2019d7ce56fb5c360a21f8accf614b186b3a71a6 Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Sun, 15 Dec 2024 14:37:32 +0100 Subject: [PATCH 21/22] tests pass, some remaining --- .../Services/Heroes/HeroPropertyTests.cs | 65 ++++++++++++++++--- .../GameInterface/Services/Heroes/HeroSync.cs | 4 +- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs b/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs index bb2f0c5af..f2450ac33 100644 --- a/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs +++ b/source/E2E.Tests/Services/Heroes/HeroPropertyTests.cs @@ -25,6 +25,13 @@ public class HeroPropertyTests : IDisposable IEnumerable Clients => TestEnvironment.Clients; private string HeroId; + private string OtherHeroId; + private string ClanId; + private string SettlementId; + private string TownId; + private string MobilePartyId; + private string CivEquipmentId; + private string BattleEquipmentId; StaticBodyProperties body = new StaticBodyProperties(1, 2, 1, 2, 1, 3, 1, 2); float newFloat = 5f; @@ -64,12 +71,10 @@ public void Server_Sync_Hero() Town newTown = GameObjectCreator.CreateInitializedObject(); MobileParty newMobileParty = GameObjectCreator.CreateInitializedObject(); - Equipment newBattleEquipment = new Equipment(false); - Equipment newCivEquipment = new Equipment(true); + Equipment newBattleEquipment = GameObjectCreator.CreateInitializedObject(); + Equipment newCivEquipment = GameObjectCreator.CreateInitializedObject(); BettingFraudIssue newIssue = new BettingFraudIssue(hero); - - hero.StaticBodyProperties = body; hero.Weight = newFloat; hero.Build = newFloat; @@ -82,13 +87,11 @@ public void Server_Sync_Hero() hero.PreferredUpgradeFormation = newFormation; hero.HeroState = newCharState; hero.IsMinorFactionHero = true; - hero.Issue = newIssue; hero.CompanionOf = newClan; hero.Occupation = newOccupation; hero.DeathMark = newKillAction; hero.DeathMarkKillerHero = newHero; hero.LastKnownClosestSettlement = newSettlement; - hero.HitPoints = newInt; hero.DeathDay = newCampaignTime; hero.LastExaminedLogEntryID = newLong; hero.Clan = newClan; @@ -103,7 +106,6 @@ public void Server_Sync_Hero() hero.BornSettlement = newSettlement; hero.Gold = newInt; hero.RandomValue = newInt; - hero.BannerItem = newEquipmentElement; hero.Father = newHero; hero.Mother = newHero; hero.Spouse = newHero; @@ -111,13 +113,60 @@ public void Server_Sync_Hero() Assert.Equal(body, hero.StaticBodyProperties); Assert.True(server.ObjectManager.TryGetId(hero, out HeroId)); + Assert.True(server.ObjectManager.TryGetId(newHero, out OtherHeroId)); + Assert.True(server.ObjectManager.TryGetId(newSettlement, out SettlementId)); + Assert.True(server.ObjectManager.TryGetId(newClan, out ClanId)); + Assert.True(server.ObjectManager.TryGetId(newTown, out TownId)); + Assert.True(server.ObjectManager.TryGetId(newMobileParty, out MobilePartyId)); + Assert.True(server.ObjectManager.TryGetId(newCivEquipment, out CivEquipmentId)); + Assert.True(server.ObjectManager.TryGetId(newBattleEquipment, out BattleEquipmentId)); }); foreach (var client in Clients) { Assert.True(client.ObjectManager.TryGetObject(HeroId, out var clientHero)); - + Assert.True(client.ObjectManager.TryGetObject(OtherHeroId, out var clientOtherHero)); + Assert.True(client.ObjectManager.TryGetObject(SettlementId, out var clientSettlement)); + Assert.True(client.ObjectManager.TryGetObject(ClanId, out var clientClan)); + Assert.True(client.ObjectManager.TryGetObject(TownId, out var clientTown)); + Assert.True(client.ObjectManager.TryGetObject(MobilePartyId, out var clientMobileParty)); + Assert.True(client.ObjectManager.TryGetObject(CivEquipmentId, out var clientCivEquipment)); + Assert.True(client.ObjectManager.TryGetObject(BattleEquipmentId, out var clientBattleEquipment)); + + Assert.Equal(clientCivEquipment, clientHero._civilianEquipment); + Assert.Equal(clientBattleEquipment, clientHero._battleEquipment); + Assert.Equal(clientClan, clientHero.Clan); + Assert.Equal(clientOtherHero, clientHero.DeathMarkKillerHero); + Assert.Equal(clientSettlement, clientHero.LastKnownClosestSettlement); + Assert.Equal(clientClan, clientHero.CompanionOf); + Assert.Equal(clientClan, clientHero.SupporterOf); + Assert.Equal(clientTown, clientHero.GovernorOf); + Assert.Equal(clientMobileParty, clientHero.PartyBelongedTo); + Assert.Equal(clientMobileParty.Party, clientHero.PartyBelongedToAsPrisoner); + Assert.Equal(clientSettlement, clientHero.StayingInSettlement); + Assert.Equal(clientSettlement, clientHero.BornSettlement); + Assert.Equal(clientOtherHero, clientHero.Mother); + Assert.Equal(clientOtherHero, clientHero.Father); + Assert.Equal(clientOtherHero, clientHero.Spouse); Assert.Equal(body, clientHero.StaticBodyProperties); + Assert.Equal(newFloat, clientHero.Weight); + Assert.Equal(newFloat, clientHero.Build); + Assert.Equal(newFloat, clientHero.PassedTimeAtHomeSettlement); + Assert.Equal(newText.Value, clientHero.EncyclopediaText.Value); + Assert.True(clientHero.IsFemale); + Assert.Equal(newCampaignTime, clientHero.CaptivityStartTime); + Assert.Equal(newFormation, clientHero.PreferredUpgradeFormation); + Assert.Equal(newCharState, clientHero.HeroState); + Assert.True(clientHero.IsMinorFactionHero); + Assert.Equal(newOccupation, clientHero.Occupation); + Assert.Equal(newKillAction, clientHero.DeathMark); + Assert.Equal(newCampaignTime, clientHero.DeathDay); + Assert.Equal(newLong, clientHero.LastExaminedLogEntryID); + Assert.True(clientHero.IsKnownToPlayer); + Assert.True(clientHero.HasMet); + Assert.Equal(newCampaignTime, clientHero.LastMeetingTimeWithPlayer); + Assert.Equal(newInt, clientHero.Gold); + Assert.Equal(newInt, clientHero.RandomValue); } } } diff --git a/source/GameInterface/Services/Heroes/HeroSync.cs b/source/GameInterface/Services/Heroes/HeroSync.cs index 3f229d13f..9f36e33b0 100644 --- a/source/GameInterface/Services/Heroes/HeroSync.cs +++ b/source/GameInterface/Services/Heroes/HeroSync.cs @@ -26,7 +26,7 @@ public HeroSync(IAutoSyncBuilder autoSyncBuilder) autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathMark))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathMarkKillerHero))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.LastKnownClosestSettlement))); - autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.HitPoints))); + //autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.HitPoints))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.DeathDay))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.LastExaminedLogEntryID))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Clan))); @@ -45,6 +45,8 @@ public HeroSync(IAutoSyncBuilder autoSyncBuilder) autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Father))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Mother))); autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Hero), nameof(Hero.Spouse))); + + autoSyncBuilder.AddField(AccessTools.Field(typeof(Hero), nameof(Hero._health))); } } } From dad918d62e719a39afe5a664f588657c20ae8e4e Mon Sep 17 00:00:00 2001 From: Alexander Egard Date: Sun, 15 Dec 2024 14:41:02 +0100 Subject: [PATCH 22/22] Reverted branch merge --- deploy.ps1 | 3 +- source/Common/GameLoopRunner.cs | 2 +- source/Common/Logging/LogManager.cs | 16 +---- source/Common/LogicStates/ILogic.cs | 2 - source/Common/Util/ObjectHelper.cs | 2 +- source/Coop.Core/Client/ClientLogic.cs | 9 --- .../ClientMobilePartyBehaviorHandler.cs | 10 +-- .../Common/Network/CoopNetworkBase.cs | 7 +- .../CoopartiveMultiplayerExperience.cs | 15 +---- source/Coop.Core/Server/ServerLogic.cs | 4 -- .../PartyAIBehaviorPacketHandler.cs | 5 +- .../Server/Services/Save/CoopSaveManager.cs | 3 +- .../Server/Services/Save/Data/CoopSession.cs | 33 ++-------- .../Services/Save/Handlers/SaveGameHandler.cs | 38 ++++++----- .../Mock/MockControlledEntityRegistry.cs | 7 +- .../Environment/Mock/MockServer.cs | 5 ++ .../Environment/TestEnvironment.cs | 2 - .../Coop.Tests/Autofac/ContainerBuildTests.cs | 6 +- .../Server/Services/Save/JSONSessionTests.cs | 14 ++-- .../Services/Save/SaveLoadCoopSessionTests.cs | 35 ++++------ source/Coop.Tests/ServerTestComponent.cs | 3 - source/Coop/CoopMod.cs | 7 +- .../Environment/E2ETestEnvironment.cs | 23 +------ .../Environment/Instance/GameInstance.cs | 4 +- .../Mock/MockControlledEntityRegistry.cs | 7 +- .../E2E.Tests/Environment/TestEnvironment.cs | 2 - .../AutoSync/Fields/FieldSwitchCreator.cs | 48 ++++---------- .../AutoSync/Fields/FieldTranspilerCreator.cs | 64 +------------------ .../Properties/PropertySwitchCreator.cs | 43 ++++--------- source/GameInterface/ContainerProvider.cs | 12 ++-- .../GameInterface/Properties/AssemblyInfo.cs | 4 +- .../Armies/Patches/ArmyDisablePatches.cs | 13 ---- .../BasicCharacterObjectRegistry.cs | 33 ++++++++++ .../Handlers/BasicCharacterLifetimeHandler.cs | 56 ++++++++++++++++ .../Messages/BasicCharacterCreated.cs | 18 ++++++ .../Messages/NetworkCreateBasicCharacter.cs | 17 +++++ .../BasicCharacterObjectLifetimePatches.cs | 43 +++++++++++++ .../BasicCultureObjectRegistry.cs | 39 +++++++++++ .../BasicCultureObjectLifetimeHandler.cs | 56 ++++++++++++++++ .../Messages/BasicCultureCreated.cs | 15 +++++ .../Messages/NetworkCreateBasicCulture.cs | 17 +++++ .../BasicCultureObjectLifetimePatches.cs | 41 ++++++++++++ .../BesiegerCamps/BesiegerCampRegistry.cs | 2 +- .../Services/Buildings/BuildingRegistry.cs | 2 +- .../CharacterObjectRegistry.cs | 11 +--- .../Patches/CharacterObjectLifetimePatches.cs | 33 ---------- .../Services/Clans/ClanRegistry.cs | 3 +- .../CraftingService/CraftingRegistry.cs | 2 +- .../CultureObjects/CultureObjectRegistry.cs | 10 +-- .../Patches/CultureObjectLifetimePatches.cs | 32 ---------- .../Entity/ControlledEntityRegistry.cs | 37 ++--------- .../Services/Entity/Data/ControlledEntity.cs | 23 +------ .../Services/Equipments/EquipmentRegistry.cs | 2 +- .../Patches/EquipmentLifetimePatches.cs | 15 +++-- .../Interfaces/GameStateInterface.cs | 2 +- .../Heroes/Handlers/HeroFieldsHandler.cs | 7 -- .../Services/Heroes/HeroRegistry.cs | 11 +++- .../Disable/IssueManagerDisablePatches.cs | 16 ----- .../ItemObjects/ItemObjectRegistry.cs | 2 +- .../MapEventSides/MapEventSideRegistry.cs | 2 +- .../Services/MapEvents/MapEventRegistry.cs | 2 +- .../Handlers/MobilePartyFieldsHandler.cs | 21 ++---- .../MobileParties/MobilePartyRegistry.cs | 11 +++- .../Patches/MobilePartyFieldPatches.cs | 2 +- .../Patches/ParallelRobustnessPatches.cs | 1 - .../MobilePartyAIs/MobilePartyAiRegistry.cs | 2 +- .../MobilePartyAIs/MobilePartyAiSync.cs | 2 - .../Patches/MobilePartyAIDisablePatches.cs | 14 ---- .../Patches/MobilePartyAIRobustnessPatches.cs | 13 ---- .../Patches/PartiesThinkPatch.cs | 5 +- .../Patches/PartyBehaviorPatch.cs | 5 +- .../Services/PartyBases/PartyBaseRegistry.cs | 2 +- .../Handlers/PartyComponentHandler.cs | 41 ++++++++++++ .../NetworkChangePartyComponentMobileParty.cs | 18 ++++++ .../PartyComponentMobilePartyChanged.cs | 10 +++ .../PartyComponents/PartyComponentRegistry.cs | 2 +- .../PartyComponents/PartyComponentSync.cs | 12 ---- .../CustomPartyComponentPatches.cs | 5 +- .../Patches/PartyComponentPatches.cs | 50 +++++++++++++++ .../Handlers/PartyVisualLifetimeHandler.cs | 8 +-- .../PartyVisuals/PartyVisualRegistry.cs | 2 +- .../Services/Registry/RegistryBase.cs | 3 - .../Save/Messages/LoadExistingObjectGuids.cs | 20 ++++++ .../Services/Save/Patches/SavePatches.cs | 6 +- .../SettlementComponentRegistry.cs | 2 +- ...SiegeEngineConstructionProgressRegistry.cs | 2 +- .../SiegeEnginesContainerRegistry.cs | 2 +- .../SiegeEvents/SiegeEventRegistry.cs | 2 +- .../Time/Handlers/TimeControlHandler.cs | 6 +- .../Services/Time/Patches/TimePatches.cs | 6 +- .../Services/Towns/Patches/TownPatches.cs | 6 ++ .../Villages/Patches/VillagePatches.cs | 5 ++ .../WeaponDesigns/WeaponDesignRegistry.cs | 2 +- .../Workshops/Patches/WorkshopDisable.cs | 12 ++++ .../Services/Workshops/WorkshopRegistry.cs | 2 +- .../Workshops/WorkshopTypeRegistry.cs | 17 +++-- 96 files changed, 694 insertions(+), 622 deletions(-) delete mode 100644 source/GameInterface/Services/Armies/Patches/ArmyDisablePatches.cs create mode 100644 source/GameInterface/Services/BasicCharacterObjects/BasicCharacterObjectRegistry.cs create mode 100644 source/GameInterface/Services/BasicCharacterObjects/Handlers/BasicCharacterLifetimeHandler.cs create mode 100644 source/GameInterface/Services/BasicCharacterObjects/Messages/BasicCharacterCreated.cs create mode 100644 source/GameInterface/Services/BasicCharacterObjects/Messages/NetworkCreateBasicCharacter.cs create mode 100644 source/GameInterface/Services/BasicCharacterObjects/Patches/BasicCharacterObjectLifetimePatches.cs create mode 100644 source/GameInterface/Services/BasicCultureObjects/BasicCultureObjectRegistry.cs create mode 100644 source/GameInterface/Services/BasicCultureObjects/Handlers/BasicCultureObjectLifetimeHandler.cs create mode 100644 source/GameInterface/Services/BasicCultureObjects/Messages/BasicCultureCreated.cs create mode 100644 source/GameInterface/Services/BasicCultureObjects/Messages/NetworkCreateBasicCulture.cs create mode 100644 source/GameInterface/Services/BasicCultureObjects/Patches/BasicCultureObjectLifetimePatches.cs delete mode 100644 source/GameInterface/Services/IssuesService/Patches/Disable/IssueManagerDisablePatches.cs delete mode 100644 source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIDisablePatches.cs delete mode 100644 source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIRobustnessPatches.cs create mode 100644 source/GameInterface/Services/PartyComponents/Messages/NetworkChangePartyComponentMobileParty.cs create mode 100644 source/GameInterface/Services/PartyComponents/Messages/PartyComponentMobilePartyChanged.cs delete mode 100644 source/GameInterface/Services/PartyComponents/PartyComponentSync.cs create mode 100644 source/GameInterface/Services/PartyComponents/Patches/PartyComponentPatches.cs create mode 100644 source/GameInterface/Services/Save/Messages/LoadExistingObjectGuids.cs create mode 100644 source/GameInterface/Services/Workshops/Patches/WorkshopDisable.cs diff --git a/deploy.ps1 b/deploy.ps1 index f6026f6f2..e0999f0e5 100644 --- a/deploy.ps1 +++ b/deploy.ps1 @@ -42,8 +42,7 @@ if(Test-Path (${BaseDir} + $config.modsDir)) New-Item -Force -ItemType Directory -Path "${ModDir}\bin" | Out-Null New-Item -Force -ItemType Directory -Path "${ModDir}\bin\Win64_Shipping_Client" | Out-Null $ModSourceDir = ${SolutionDir} + "\" + $config.name - Get-ChildItem -Path "${ModSourceDir}\bin\Debug" -Filter "*.dll" -Recurse -ErrorAction Ignore | Where { $_.PSIsContainer -eq $false } | Copy-Item -Destination "${ModDir}\bin\Win64_Shipping_Client" - Get-ChildItem -Path "${ModSourceDir}\bin\Release" -Filter "*.dll" -Recurse -ErrorAction Ignore | Where { $_.PSIsContainer -eq $false } | Copy-Item -Destination "${ModDir}\bin\Win64_Shipping_Client" + Get-ChildItem -Path "${ModSourceDir}" -Filter "*.dll" -Recurse -ErrorAction Ignore | Where { $_.PSIsContainer -eq $false } | Copy-Item -Destination "${ModDir}\bin\Win64_Shipping_Client" Copy-Item -Force "${DeployDir}\SubModule.xml" -Destination "${ModDir}\" } diff --git a/source/Common/GameLoopRunner.cs b/source/Common/GameLoopRunner.cs index 60df28dd0..a51f661bd 100644 --- a/source/Common/GameLoopRunner.cs +++ b/source/Common/GameLoopRunner.cs @@ -38,7 +38,7 @@ public void Update(TimeSpan frameTime) List<(Action, EventWaitHandle)> toBeRun = new List<(Action, EventWaitHandle)>(); - lock (Instance.m_QueueLock) + lock (m_Queue) { while (m_Queue.Count > 0) { diff --git a/source/Common/Logging/LogManager.cs b/source/Common/Logging/LogManager.cs index 62832f49e..08de6d2f3 100644 --- a/source/Common/Logging/LogManager.cs +++ b/source/Common/Logging/LogManager.cs @@ -1,26 +1,14 @@ using System; -using System.Collections.Generic; using Serilog; -using Serilog.Core; namespace Common.Logging; public static class LogManager { public static LoggerConfiguration Configuration { get; set; } = new LoggerConfiguration(); - - public static List Sinks { get; } = new List(); - - + // If this is called before the Configuration is setup, logging does not work - private static Lazy _logger = new Lazy(() => { - - foreach (var sink in Sinks) - { - Configuration = Configuration.WriteTo.Sink(sink); - } - return Configuration.CreateLogger(); - }); + private static Lazy _logger = new Lazy(() => Configuration.CreateLogger()); public static ILogger GetLogger() => _logger.Value .ForContext(); diff --git a/source/Common/LogicStates/ILogic.cs b/source/Common/LogicStates/ILogic.cs index 8216bc17f..7633abd80 100644 --- a/source/Common/LogicStates/ILogic.cs +++ b/source/Common/LogicStates/ILogic.cs @@ -4,7 +4,5 @@ public interface ILogic { void Start(); void Stop(); - - bool RunningState { get; } } } diff --git a/source/Common/Util/ObjectHelper.cs b/source/Common/Util/ObjectHelper.cs index a2b51ee1e..c4f0fc0bc 100644 --- a/source/Common/Util/ObjectHelper.cs +++ b/source/Common/Util/ObjectHelper.cs @@ -20,7 +20,7 @@ public class ObjectHelper /// New instance of public static T SkipConstructor() { - //Logger.Verbose("{pid}: Creating {type}", Thread.CurrentThread.ManagedThreadId, typeof(T)); + Logger.Verbose("{pid}: Creating {type}", Thread.CurrentThread.ManagedThreadId, typeof(T)); return (T)FormatterServices.GetUninitializedObject(typeof(T)); } diff --git a/source/Coop.Core/Client/ClientLogic.cs b/source/Coop.Core/Client/ClientLogic.cs index d17a583de..933692511 100644 --- a/source/Coop.Core/Client/ClientLogic.cs +++ b/source/Coop.Core/Client/ClientLogic.cs @@ -2,8 +2,6 @@ using Common.LogicStates; using Coop.Core.Client.States; using Serilog; -using System; -using System.Collections.Generic; namespace Coop.Core.Client; @@ -29,11 +27,6 @@ public class ClientLogic : IClientLogic public IStateFactory StateFactory { get; } public string ControlledHeroId { get; set; } private IClientState InitialState => StateFactory.CreateClientState(this); - private readonly HashSet RunningStates = new HashSet - { - typeof(MissionState), - typeof(CampaignState), - }; public IClientState State { get @@ -50,8 +43,6 @@ public IClientState State } } - public bool RunningState => RunningStates.Contains(_state.GetType()); - private IClientState _state; public ClientLogic(IStateFactory stateFactory) diff --git a/source/Coop.Core/Client/Services/MobileParties/Handlers/ClientMobilePartyBehaviorHandler.cs b/source/Coop.Core/Client/Services/MobileParties/Handlers/ClientMobilePartyBehaviorHandler.cs index 241b65eb1..bd55e1234 100644 --- a/source/Coop.Core/Client/Services/MobileParties/Handlers/ClientMobilePartyBehaviorHandler.cs +++ b/source/Coop.Core/Client/Services/MobileParties/Handlers/ClientMobilePartyBehaviorHandler.cs @@ -1,12 +1,10 @@ -using Common.Logging; -using Common.Messaging; +using Common.Messaging; using Common.Network; using Coop.Core.Client.Services.MobileParties.Messages; using Coop.Core.Server.Services.MobileParties.Messages; using Coop.Core.Server.Services.MobileParties.Packets; using GameInterface.Services.MobileParties.Messages; using GameInterface.Services.MobileParties.Messages.Behavior; -using Serilog; namespace Coop.Core.Client.Services.MobileParties.Handlers { @@ -17,12 +15,10 @@ namespace Coop.Core.Client.Services.MobileParties.Handlers /// Game Interface's Handler public class ClientMobilePartyBehaviorHandler : IHandler { - private static readonly ILogger Logger = LogManager.GetLogger(); - private readonly IMessageBroker messageBroker; private readonly INetwork network; - public ClientMobilePartyBehaviorHandler(IMessageBroker messageBroker, INetwork network, ILogger logger) + public ClientMobilePartyBehaviorHandler(IMessageBroker messageBroker, INetwork network) { this.messageBroker = messageBroker; this.network = network; @@ -46,8 +42,6 @@ private void HandleChangeWageOtherClients(MessagePayload obj) { - Logger.Debug($"Attempting to update party movement X:{obj.What.BehaviorUpdateData.TargetPointX} Y:{obj.What.BehaviorUpdateData.TargetPointY}"); - network.SendAll(new RequestMobilePartyBehaviorPacket(obj.What.BehaviorUpdateData)); } diff --git a/source/Coop.Core/Common/Network/CoopNetworkBase.cs b/source/Coop.Core/Common/Network/CoopNetworkBase.cs index ed69d94ad..6c3a5d0a6 100644 --- a/source/Coop.Core/Common/Network/CoopNetworkBase.cs +++ b/source/Coop.Core/Common/Network/CoopNetworkBase.cs @@ -50,13 +50,12 @@ protected CoopNetworkBase(INetworkConfiguration configuration, ICommonSerializer public virtual void Dispose() { - netManager.Stop(); - - if (CancellationTokenSource.IsCancellationRequested) return; - CancellationTokenSource?.Cancel(); CancellationTokenSource?.Dispose(); UpdateThread?.Join(Configuration.ObjectCreationTimeout); + + netManager.DisconnectAll(); + netManager.Stop(); } private void UpdateThreadMethod() diff --git a/source/Coop.Core/CoopartiveMultiplayerExperience.cs b/source/Coop.Core/CoopartiveMultiplayerExperience.cs index 0d2f58f8a..98c6c40a1 100644 --- a/source/Coop.Core/CoopartiveMultiplayerExperience.cs +++ b/source/Coop.Core/CoopartiveMultiplayerExperience.cs @@ -9,11 +9,10 @@ using GameInterface; using GameInterface.Services.GameDebug.Messages; using GameInterface.Services.UI.Messages; -using System; namespace Coop.Core { - public class CoopartiveMultiplayerExperience : IDisposable + public class CoopartiveMultiplayerExperience { private readonly IMessageBroker messageBroker; private INetworkConfiguration configuration; @@ -31,18 +30,6 @@ public CoopartiveMultiplayerExperience() messageBroker.Subscribe(Handle); } - public bool Running { get - { - if (container == null) return false; - - var logic = container.Resolve(); - - return logic.RunningState; - } - } - - public void Dispose() => DestroyContainer(); - private void Handle(MessagePayload obj) { var connectMessage = obj.What; diff --git a/source/Coop.Core/Server/ServerLogic.cs b/source/Coop.Core/Server/ServerLogic.cs index 8e066785e..f9576efd9 100644 --- a/source/Coop.Core/Server/ServerLogic.cs +++ b/source/Coop.Core/Server/ServerLogic.cs @@ -2,7 +2,6 @@ using Common.LogicStates; using Common.Messaging; using Common.Network; -using Coop.Core.Client.States; using Coop.Core.Server.Connections; using Coop.Core.Server.States; using Serilog; @@ -38,9 +37,6 @@ public IServerState State _state = value; } } - - public bool RunningState => _state is not InitialServerState; - private IServerState _state; public ServerLogic(IStateFactory stateFactory) diff --git a/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs b/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs index 3a1dadf52..293cab0aa 100644 --- a/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs +++ b/source/Coop.Core/Server/Services/MobileParties/PacketHandlers/PartyAIBehaviorPacketHandler.cs @@ -1,12 +1,10 @@ -using Common.Logging; -using Common.Messaging; +using Common.Messaging; using Common.Network; using Common.PacketHandlers; using Coop.Core.Client.Services.MobileParties.Packets; using Coop.Core.Server.Services.MobileParties.Packets; using GameInterface.Services.MobileParties.Messages.Behavior; using LiteNetLib; -using Serilog; namespace Coop.Core.Server.Services.MobileParties.PacketHandlers; @@ -15,7 +13,6 @@ namespace Coop.Core.Server.Services.MobileParties.PacketHandlers; /// internal class RequestMobilePartyBehaviorPacketHandler : IPacketHandler { - private static readonly ILogger Logger = LogManager.GetLogger(); public PacketType PacketType => PacketType.RequestUpdatePartyBehavior; private readonly IPacketManager packetManager; diff --git a/source/Coop.Core/Server/Services/Save/CoopSaveManager.cs b/source/Coop.Core/Server/Services/Save/CoopSaveManager.cs index 16d1469ae..6d5d4c79a 100644 --- a/source/Coop.Core/Server/Services/Save/CoopSaveManager.cs +++ b/source/Coop.Core/Server/Services/Save/CoopSaveManager.cs @@ -2,7 +2,6 @@ using Common.Serialization; using Coop.Core.Server.Services.Save.Data; using System; -using System.Collections; using System.IO; namespace Coop.Core.Server.Services.Save @@ -18,7 +17,7 @@ internal interface ICoopSaveManager internal class CoopSaveManager : ICoopSaveManager { - public string DefaultPath { get; } = "./saves/"; + public string DefaultPath { get; } = "./Saves/"; public string FileType { get; } = ".json"; /// diff --git a/source/Coop.Core/Server/Services/Save/Data/CoopSession.cs b/source/Coop.Core/Server/Services/Save/Data/CoopSession.cs index ac99d6ac3..c9c4351a9 100644 --- a/source/Coop.Core/Server/Services/Save/Data/CoopSession.cs +++ b/source/Coop.Core/Server/Services/Save/Data/CoopSession.cs @@ -1,10 +1,5 @@ -using Autofac.Features.OwnedInstances; -using Common; -using GameInterface.Services.Entity.Data; -using GameInterface.Services.Heroes.Data; +using GameInterface.Services.Heroes.Data; using ProtoBuf; -using System.Collections.Generic; -using System.Linq; namespace Coop.Core.Server.Services.Save.Data; @@ -14,8 +9,8 @@ namespace Coop.Core.Server.Services.Save.Data; /// public interface ICoopSession { - string UniqueGameId { get; } - Dictionary> ControlledEntityMap { get; } + string UniqueGameId { get; set; } + GameObjectGuids GameObjectGuids { get; set; } } /// @@ -23,15 +18,9 @@ public interface ICoopSession public class CoopSession : ICoopSession { [ProtoMember(1)] - public string UniqueGameId { get; } + public string UniqueGameId { get; set; } [ProtoMember(2)] - public Dictionary> ControlledEntityMap { get; } - - public CoopSession(string uniqueGameId, Dictionary> controlledEntityMap) - { - UniqueGameId = uniqueGameId; - ControlledEntityMap = controlledEntityMap; - } + public GameObjectGuids GameObjectGuids { get; set; } public override bool Equals(object obj) { @@ -39,21 +28,13 @@ public override bool Equals(object obj) if (UniqueGameId != session.UniqueGameId) return false; - if (ControlledEntityMap.Count != session.ControlledEntityMap.Count) return false; - - if (ControlledEntityMap.Zip(session.ControlledEntityMap, (l, r) => - { - return l.Key == r.Key && l.Value.SetEquals(r.Value); - }).All(x => x) == false) return false; + if (GameObjectGuids.Equals(session.GameObjectGuids) == false) return false; return true; } public override int GetHashCode() { - int hash = 1236898; - hash = hash * 31 + UniqueGameId.GetHashCode(); - hash = hash * 31 + ControlledEntityMap.GetHashCode(); - return hash; + return base.GetHashCode(); } } diff --git a/source/Coop.Core/Server/Services/Save/Handlers/SaveGameHandler.cs b/source/Coop.Core/Server/Services/Save/Handlers/SaveGameHandler.cs index 4ab5e66f1..22d83bf39 100644 --- a/source/Coop.Core/Server/Services/Save/Handlers/SaveGameHandler.cs +++ b/source/Coop.Core/Server/Services/Save/Handlers/SaveGameHandler.cs @@ -16,21 +16,19 @@ internal class SaveGameHandler : IHandler private readonly ICoopSaveManager saveManager; private readonly ICoopServer coopServer; private readonly IControllerIdProvider controllerIdProvider; - private readonly IControlledEntityRegistry controlledEntityRegistry; public SaveGameHandler( IMessageBroker messageBroker, ICoopSaveManager saveManager, ICoopServer coopServer, - IControllerIdProvider controllerIdProvider, - IControlledEntityRegistry controlledEntityRegistry) + IControllerIdProvider controllerIdProvider) { this.messageBroker = messageBroker; this.saveManager = saveManager; this.coopServer = coopServer; this.controllerIdProvider = controllerIdProvider; - this.controlledEntityRegistry = controlledEntityRegistry; messageBroker.Subscribe(Handle_GameSaved); + messageBroker.Subscribe(Handle_ObjectGuidsPackaged); messageBroker.Subscribe(Handle_GameLoaded); messageBroker.Subscribe(Handle_CampaignLoaded); @@ -40,20 +38,28 @@ public SaveGameHandler( public void Dispose() { messageBroker.Unsubscribe(Handle_GameSaved); + messageBroker.Unsubscribe(Handle_ObjectGuidsPackaged); messageBroker.Unsubscribe(Handle_GameLoaded); messageBroker.Unsubscribe(Handle_CampaignLoaded); messageBroker.Unsubscribe(Handle_AllGameObjectsRegistered); } + private string saveName; private void Handle_GameSaved(MessagePayload obj) { - var saveName = obj.What.SaveName; + saveName = obj.What.SaveName; messageBroker.Publish(this, new PackageObjectGuids()); + } - var controlledEntities = controlledEntityRegistry.PackageControlledEntities(); - - CoopSession session = new CoopSession(saveName, controlledEntities); + private void Handle_ObjectGuidsPackaged(MessagePayload obj) + { + var payload = obj.What; + CoopSession session = new CoopSession() + { + UniqueGameId = payload.UniqueGameId, + GameObjectGuids = payload.GameObjectGuids, + }; saveManager.SaveCoopSession(saveName, session); } @@ -65,20 +71,20 @@ private void Handle_GameLoaded(MessagePayload obj) } private void Handle_CampaignLoaded(MessagePayload obj) - { - // Register all game objects with our object manager - messageBroker.Publish(this, new RegisterAllGameObjects()); - } - - private void Handle_AllGameObjectsRegistered(MessagePayload obj) { if (savedSession == null) { - messageBroker.Publish(this, new RegisterAllPartiesAsControlled(controllerIdProvider.ControllerId)); + messageBroker.Publish(this, new RegisterAllGameObjects()); } else { - controlledEntityRegistry.LoadControlledEntities(savedSession.ControlledEntityMap); + var message = new LoadExistingObjectGuids(savedSession.GameObjectGuids); + messageBroker.Publish(this, message); } } + + private void Handle_AllGameObjectsRegistered(MessagePayload obj) + { + messageBroker.Publish(this, new RegisterAllPartiesAsControlled(controllerIdProvider.ControllerId)); + } } diff --git a/source/Coop.IntegrationTests/Environment/Mock/MockControlledEntityRegistry.cs b/source/Coop.IntegrationTests/Environment/Mock/MockControlledEntityRegistry.cs index 7644d5c36..f274c36a9 100644 --- a/source/Coop.IntegrationTests/Environment/Mock/MockControlledEntityRegistry.cs +++ b/source/Coop.IntegrationTests/Environment/Mock/MockControlledEntityRegistry.cs @@ -10,12 +10,7 @@ public bool IsControlledBy(string controllerId, string entityId) throw new NotImplementedException(); } - public void LoadControlledEntities(Dictionary> controlledEntityMap) - { - throw new NotImplementedException(); - } - - public Dictionary> PackageControlledEntities() + public IReadOnlyDictionary> PackageControlledEntities() { throw new NotImplementedException(); } diff --git a/source/Coop.IntegrationTests/Environment/Mock/MockServer.cs b/source/Coop.IntegrationTests/Environment/Mock/MockServer.cs index c14a0ab6c..3fe7f2b92 100644 --- a/source/Coop.IntegrationTests/Environment/Mock/MockServer.cs +++ b/source/Coop.IntegrationTests/Environment/Mock/MockServer.cs @@ -26,6 +26,11 @@ public void RemovePeer(NetPeer peer) peers.Remove(peer); } + public void Dispose() + { + throw new NotImplementedException(); + } + public void OnConnectionRequest(ConnectionRequest request) { throw new NotImplementedException(); diff --git a/source/Coop.IntegrationTests/Environment/TestEnvironment.cs b/source/Coop.IntegrationTests/Environment/TestEnvironment.cs index cb4b21422..e864c9f26 100644 --- a/source/Coop.IntegrationTests/Environment/TestEnvironment.cs +++ b/source/Coop.IntegrationTests/Environment/TestEnvironment.cs @@ -10,7 +10,6 @@ using Coop.IntegrationTests.Environment.Mock; using GameInterface; using GameInterface.Policies; -using GameInterface.Services.Entity; namespace Coop.IntegrationTests.Environment; @@ -88,7 +87,6 @@ private EnvironmentInstance CreateServer() builder.RegisterModule(); builder.RegisterType().AsSelf().As().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().AsSelf(); builder.RegisterInstance(containerProvider).As().SingleInstance(); diff --git a/source/Coop.Tests/Autofac/ContainerBuildTests.cs b/source/Coop.Tests/Autofac/ContainerBuildTests.cs index eee66aa24..386d47994 100644 --- a/source/Coop.Tests/Autofac/ContainerBuildTests.cs +++ b/source/Coop.Tests/Autofac/ContainerBuildTests.cs @@ -4,7 +4,6 @@ using Coop.Core; using Coop.Core.Client; using Coop.Core.Server; -using GameInterface; using Xunit; namespace Coop.Tests.Autofac @@ -14,7 +13,7 @@ public class ContainerBuildTests [Fact] public void Client_Container_Build() { - var containerProvider = new Core.ContainerProvider(); + var containerProvider = new ContainerProvider(); ContainerBuilder builder = new ContainerBuilder(); builder.RegisterModule(); @@ -35,11 +34,10 @@ public void Client_Container_Build() [Fact] public void Server_Container_Build() { - var containerProvider = new Core.ContainerProvider(); + var containerProvider = new ContainerProvider(); ContainerBuilder builder = new ContainerBuilder(); builder.RegisterModule(); - builder.RegisterModule(); builder.RegisterInstance(containerProvider).As(); var container = builder.Build(); diff --git a/source/Coop.Tests/Server/Services/Save/JSONSessionTests.cs b/source/Coop.Tests/Server/Services/Save/JSONSessionTests.cs index 41e3cf7ae..9dd8e8afc 100644 --- a/source/Coop.Tests/Server/Services/Save/JSONSessionTests.cs +++ b/source/Coop.Tests/Server/Services/Save/JSONSessionTests.cs @@ -1,6 +1,5 @@ using Common.Serialization; using Coop.Core.Server.Services.Save.Data; -using GameInterface.Services.Entity; using GameInterface.Services.Heroes.Data; using System; using System.Collections.Generic; @@ -27,14 +26,11 @@ public void SaveLoadSessions() { var gameObjectGuids = new GameObjectGuids(new string[] { "Random STR" }); - var entityRegistry = new ControlledEntityRegistry(); - - const string controllerId = "testController"; - const string entityId = "testEntity1"; - - Assert.True(entityRegistry.RegisterAsControlled(controllerId, entityId)); - - var sessionData = new CoopSession("TestId", entityRegistry.PackageControlledEntities()); + ICoopSession sessionData = new CoopSession() + { + UniqueGameId = "TestId", + GameObjectGuids = gameObjectGuids, + }; string saveFile = SAVE_PATH + sessionData.UniqueGameId + ".json"; diff --git a/source/Coop.Tests/Server/Services/Save/SaveLoadCoopSessionTests.cs b/source/Coop.Tests/Server/Services/Save/SaveLoadCoopSessionTests.cs index eeba5e3eb..a7761ab33 100644 --- a/source/Coop.Tests/Server/Services/Save/SaveLoadCoopSessionTests.cs +++ b/source/Coop.Tests/Server/Services/Save/SaveLoadCoopSessionTests.cs @@ -3,7 +3,6 @@ using Coop.Core.Server; using Coop.Core.Server.Services.Save; using Coop.Core.Server.Services.Save.Data; -using GameInterface.Services.Entity; using GameInterface.Services.Heroes.Data; using System; using System.Collections.Generic; @@ -35,19 +34,14 @@ public void SaveSession() { // Setup var saveManager = container.Resolve(); - var entityRegistry = container.Resolve(); - const string controllerId = "testController"; - const string controller2Id = "testController2"; - const string entityId = "testEntity1"; - const string entity2Id = "testEntity2"; + var gameObjectGuids = new GameObjectGuids(new string[] { "Random STR" }); - Assert.True(entityRegistry.RegisterAsControlled(controllerId, entityId)); - Assert.True(entityRegistry.RegisterAsControlled(controller2Id, entity2Id)); - - var entityMap = entityRegistry.PackageControlledEntities(); - - ICoopSession sessionData = new CoopSession("SaveManagerTest", entityMap); + ICoopSession sessionData = new CoopSession() + { + UniqueGameId = "SaveManagerTest", + GameObjectGuids = gameObjectGuids + }; string saveFile = sessionData.UniqueGameId; @@ -73,19 +67,14 @@ public void SaveLoadSession() { // Setup var saveManager = container.Resolve(); - var entityRegistry = container.Resolve(); - const string controllerId = "testController"; - const string controller2Id = "testController2"; - const string entityId = "testEntity1"; - const string entity2Id = "testEntity2"; + var gameObjectGuids = new GameObjectGuids(new string[] { "Random STR" }); - Assert.True(entityRegistry.RegisterAsControlled(controllerId, entityId)); - Assert.True(entityRegistry.RegisterAsControlled(controller2Id, entity2Id)); - - var entityMap = entityRegistry.PackageControlledEntities(); - - ICoopSession sessionData = new CoopSession("SaveManagerTest", entityMap); + ICoopSession sessionData = new CoopSession() + { + UniqueGameId = "SaveLoadManagerTest", + GameObjectGuids = gameObjectGuids, + }; string saveFile = SAVE_PATH + sessionData.UniqueGameId; diff --git a/source/Coop.Tests/ServerTestComponent.cs b/source/Coop.Tests/ServerTestComponent.cs index beff4c2ce..cf82a1fba 100644 --- a/source/Coop.Tests/ServerTestComponent.cs +++ b/source/Coop.Tests/ServerTestComponent.cs @@ -1,7 +1,5 @@ using Autofac; using Coop.Core.Server; -using GameInterface; -using GameInterface.Services.Entity; using Xunit.Abstractions; namespace Coop.Tests; @@ -12,7 +10,6 @@ public ServerTestComponent(ITestOutputHelper output) : base(output) { var builder = new ContainerBuilder(); builder.RegisterModule(); - builder.RegisterType().As().InstancePerLifetimeScope(); Container = BuildContainer(builder); } } diff --git a/source/Coop/CoopMod.cs b/source/Coop/CoopMod.cs index 35e6db948..2474ce658 100644 --- a/source/Coop/CoopMod.cs +++ b/source/Coop/CoopMod.cs @@ -88,7 +88,7 @@ private void SetupLogging() public override void NoHarmonyLoad() { - Coop = new CoopartiveMultiplayerExperience(); + Coop = new CoopartiveMultiplayerExperience(); Updateables.Add(GameLoopRunner.Instance); @@ -162,11 +162,6 @@ protected override void OnBeforeInitialModuleScreenSetAsRoot() public override void OnGameEnd(Game game) { base.OnGameEnd(game); - - if (Coop.Running) - { - Coop.Dispose(); - } } private bool m_IsFirstTick = true; diff --git a/source/E2E.Tests/Environment/E2ETestEnvironment.cs b/source/E2E.Tests/Environment/E2ETestEnvironment.cs index 1c519d97f..3133366e3 100644 --- a/source/E2E.Tests/Environment/E2ETestEnvironment.cs +++ b/source/E2E.Tests/Environment/E2ETestEnvironment.cs @@ -7,9 +7,6 @@ using GameInterface.AutoSync; using GameInterface.Tests.Bootstrap; using Serilog; -using Serilog.Core; -using Serilog.Events; -using Serilog.Sinks.XUnit; using System.Reflection; using TaleWorlds.CampaignSystem; using TaleWorlds.Core; @@ -30,26 +27,9 @@ internal class E2ETestEnvironment : IDisposable private TestEnvironment IntegrationEnvironment { get; } - private class TestOutputSink : ILogEventSink - { - private readonly ITestOutputHelper output; - - public TestOutputSink(ITestOutputHelper output) - { - this.output = output; - } - - public void Emit(LogEvent logEvent) - { - TextWriter textWriter = new StringWriter(); - logEvent.RenderMessage(textWriter); - output.WriteLine(textWriter.ToString()); - } - } - public E2ETestEnvironment(ITestOutputHelper output, int numClients = 2) { - LogManager.Sinks.Add(new TestOutputSink(output)); + LogManager.Configuration = new LoggerConfiguration().WriteTo.TestOutput(output); GameLoopRunner.Instance.SetGameLoopThread(); @@ -89,6 +69,7 @@ private void SetupMainHero() Server.Call(() => { var characterObject = GameObjectCreator.CreateInitializedObject(); + MBObjectManager.Instance.RegisterObject(characterObject); var mainHero = HeroCreator.CreateSpecialHero(characterObject); characterObject.HeroObject = mainHero; Game.Current.PlayerTroop = characterObject; diff --git a/source/E2E.Tests/Environment/Instance/GameInstance.cs b/source/E2E.Tests/Environment/Instance/GameInstance.cs index c5ef49773..47b9ebc8b 100644 --- a/source/E2E.Tests/Environment/Instance/GameInstance.cs +++ b/source/E2E.Tests/Environment/Instance/GameInstance.cs @@ -33,8 +33,8 @@ public GameInstance() Module = (Module)AccessTools.Constructor(typeof(Module)).Invoke(null); GameManager = new SandBoxGameManager(); Campaign = new Campaign(CampaignGameMode.Campaign); - Game = Game.CreateGame(Campaign, GameManager); MBObjectManager = MBObjectManager.Instance; + Game = Game.CreateGame(Campaign, GameManager); RegisterType(MBObjectManager); @@ -72,6 +72,6 @@ public void SetStatics() MBObjectManager.Instance = MBObjectManager; Campaign.Current = Campaign; Game.Current = Game; - Module.CurrentModule = Module; + AccessTools.Property(typeof(Module), nameof(Module.CurrentModule)).SetValue(null, Module); } } diff --git a/source/E2E.Tests/Environment/Mock/MockControlledEntityRegistry.cs b/source/E2E.Tests/Environment/Mock/MockControlledEntityRegistry.cs index fffad240b..420bb9bd6 100644 --- a/source/E2E.Tests/Environment/Mock/MockControlledEntityRegistry.cs +++ b/source/E2E.Tests/Environment/Mock/MockControlledEntityRegistry.cs @@ -10,12 +10,7 @@ public bool IsControlledBy(string controllerId, string entityId) throw new NotImplementedException(); } - public void LoadControlledEntities(Dictionary> controlledEntityMap) - { - throw new NotImplementedException(); - } - - public Dictionary> PackageControlledEntities() + public IReadOnlyDictionary> PackageControlledEntities() { throw new NotImplementedException(); } diff --git a/source/E2E.Tests/Environment/TestEnvironment.cs b/source/E2E.Tests/Environment/TestEnvironment.cs index 392d22295..a00adb0b0 100644 --- a/source/E2E.Tests/Environment/TestEnvironment.cs +++ b/source/E2E.Tests/Environment/TestEnvironment.cs @@ -10,7 +10,6 @@ using E2E.Tests.Environment.Mock; using GameInterface; using GameInterface.Policies; -using GameInterface.Surrogates; using Xunit.Abstractions; using ContainerProvider = Coop.Core.ContainerProvider; @@ -118,7 +117,6 @@ private ContainerBuilder AddSharedDependencies(ContainerBuilder builder) builder.RegisterType().AsSelf().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().InstancePerLifetimeScope().AutoActivate(); return builder; } diff --git a/source/GameInterface/AutoSync/Fields/FieldSwitchCreator.cs b/source/GameInterface/AutoSync/Fields/FieldSwitchCreator.cs index 1289e114a..1bac68b61 100644 --- a/source/GameInterface/AutoSync/Fields/FieldSwitchCreator.cs +++ b/source/GameInterface/AutoSync/Fields/FieldSwitchCreator.cs @@ -157,64 +157,44 @@ private void CreateByValue(ILGenerator il, FieldInfo field, LocalBuilder instanc private void CreateByRef(ILGenerator il, FieldInfo field, LocalBuilder instanceLocal) { - var errorString = $"Unable to find instance of type {field.Name} with id "; + var errorString = $"Unable to find instance of type {instanceType.Name} with id "; var stringConcatMethod = AccessTools.Method(typeof(FieldSwitchCreator), nameof(Concat)); - var valueId = il.DeclareLocal(typeof(string)); var valueLocal = il.DeclareLocal(field.FieldType); - var setValue = il.DefineLabel(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(FieldAutoSyncPacket), nameof(FieldAutoSyncPacket.value))); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(FieldSwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); - - il.Emit(OpCodes.Stloc, valueId); - - - // If id is null set null - if (field.FieldType.IsClass) // structs cannot be null - { - var notNull = il.DefineLabel(); - - il.Emit(OpCodes.Ldloc, valueId); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(string), nameof(string.IsNullOrEmpty))); - il.Emit(OpCodes.Brfalse, notNull); - - il.Emit(OpCodes.Ldloc, instanceLocal); - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Stfld, field); - il.Emit(OpCodes.Ret); - - il.MarkLabel(notNull); - } - + il.Emit(OpCodes.Ldloc, instanceLocal); - // Get instance from id // Load objectmanager il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, objectManagerField); - il.Emit(OpCodes.Ldloc, valueId); + var getObjectSuccess = il.DefineLabel(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(FieldAutoSyncPacket), nameof(FieldAutoSyncPacket.value))); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(FieldSwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); + il.Emit(OpCodes.Ldloca, valueLocal); + il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(IObjectManager), nameof(IObjectManager.TryGetObject)).MakeGenericMethod(field.FieldType)); - il.Emit(OpCodes.Brtrue, setValue); + il.Emit(OpCodes.Brtrue, getObjectSuccess); // if TryGetObject failes log error il.Emit(OpCodes.Ldsfld, loggerField); il.Emit(OpCodes.Ldstr, errorString); - il.Emit(OpCodes.Ldloc, valueId); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(FieldAutoSyncPacket), nameof(FieldAutoSyncPacket.value))); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(FieldSwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); il.Emit(OpCodes.Call, stringConcatMethod); il.Emit(OpCodes.Call, AccessTools.Method(typeof(ILogger), nameof(ILogger.Error), new Type[] { typeof(string) })); + il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ret); - // Set value - il.MarkLabel(setValue); + il.MarkLabel(getObjectSuccess); - il.Emit(OpCodes.Ldloc, instanceLocal); il.Emit(OpCodes.Ldloc, valueLocal); il.Emit(OpCodes.Stfld, field); il.Emit(OpCodes.Ret); diff --git a/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs b/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs index 62facbeec..ff3180a53 100644 --- a/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs +++ b/source/GameInterface/AutoSync/Fields/FieldTranspilerCreator.cs @@ -13,8 +13,6 @@ using System.Linq; using ProtoBuf; using ProtoBuf.Meta; -using System.Diagnostics; -using Common.Util; namespace GameInterface.AutoSync.Fields; public class FieldTranspilerCreator @@ -22,7 +20,6 @@ public class FieldTranspilerCreator private readonly TypeBuilder typeBuilder; private readonly FieldBuilder loggerField; - private readonly MethodInfo logErrorFn; private readonly IObjectManager objectManager; private readonly Dictionary interceptMap; @@ -44,14 +41,6 @@ public FieldTranspilerCreator(IObjectManager objectManager, ModuleBuilder module loggerField = typeBuilder.DefineField("logger", typeof(ILogger), FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static); - const int parameterCount = 2; - - logErrorFn = typeof(ILogger).GetMethods(BindingFlags.Instance | BindingFlags.Public) - .Where( - m => m.Name == nameof(ILogger.Error) && - m.IsGenericMethodDefinition && - m.GetParameters().Count() == parameterCount) - .Single(); CreateStaticCtor(); @@ -72,17 +61,6 @@ public FieldTranspilerCreator(IObjectManager objectManager, ModuleBuilder module il.Emit(OpCodes.Ret); } - public static string GetStackTrace() - { - var stackTrace = new StackTrace(); - - var frames = stackTrace.GetFrames(); - - var result = string.Join("\n", frames.Take(frames.Count() - 1).Select(frame => frame.ToString())); - - return result; - } - private void CreateStaticCtor() { var cctorBuilder = typeBuilder.DefineConstructor( @@ -457,15 +435,6 @@ private MethodBuilder CreateInterceptByValue(int typeId, int propId, FieldInfo f var il = methodBuilder.GetILGenerator(); - var neqLabel = il.DefineLabel(); - - var allowedLabel = il.DefineLabel(); - - il.Emit(OpCodes.Call, AccessTools.Method(typeof(AllowedThread), nameof(AllowedThread.IsThisThreadAllowed))); - il.Emit(OpCodes.Brtrue, allowedLabel); - - // TODO add same value checking, has to be more complex than just ==, needs .Equals function to properly work with surrogates - IsClientCheck(il, field); var networkLocal = TryResolve(il); @@ -486,8 +455,6 @@ private MethodBuilder CreateInterceptByValue(int typeId, int propId, FieldInfo f il.Emit(OpCodes.Box, typeof(FieldAutoSyncPacket)); il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(INetwork), nameof(INetwork.SendAll), new Type[] { typeof(IPacket) })); - il.MarkLabel(allowedLabel); - il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, field); @@ -507,27 +474,6 @@ private MethodBuilder CreateInterceptByRef(int typeId, int propId, FieldInfo fie var il = methodBuilder.GetILGenerator(); - var neqLabel = il.DefineLabel(); - - // if (this.currentValue == newValue) return; - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, field); - il.Emit(OpCodes.Ldarg_1); - - - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Brfalse, neqLabel); - - il.Emit(OpCodes.Ret); - - - il.MarkLabel(neqLabel); - - var allowedLabel = il.DefineLabel(); - - il.Emit(OpCodes.Call, AccessTools.Method(typeof(AllowedThread), nameof(AllowedThread.IsThisThreadAllowed))); - il.Emit(OpCodes.Brtrue, allowedLabel); - IsClientCheck(il, field); var networkLocal = TryResolve(il); @@ -549,8 +495,6 @@ private MethodBuilder CreateInterceptByRef(int typeId, int propId, FieldInfo fie il.Emit(OpCodes.Box, typeof(FieldAutoSyncPacket)); il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(INetwork), nameof(INetwork.SendAll), new Type[] { typeof(IPacket) })); - il.MarkLabel(allowedLabel); - il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, field); @@ -619,12 +563,8 @@ private void IsClientCheck(ILGenerator il, MemberInfo field) // Log error il.Emit(OpCodes.Ldsfld, loggerField); - - il.Emit(OpCodes.Ldstr, $"Client attempted to change {field.Name} {{trace}}"); - - il.Emit(OpCodes.Call, AccessTools.PropertyGetter(typeof(Environment), nameof(Environment.StackTrace))); - - il.Emit(OpCodes.Callvirt, logErrorFn.MakeGenericMethod(typeof(string))); + il.Emit(OpCodes.Ldstr, $"Client attempted to change {field.Name}"); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(ILogger), nameof(ILogger.Error), new Type[] { typeof(string) })); // Return il.Emit(OpCodes.Ret); diff --git a/source/GameInterface/AutoSync/Properties/PropertySwitchCreator.cs b/source/GameInterface/AutoSync/Properties/PropertySwitchCreator.cs index b59bcf1fc..298c6a210 100644 --- a/source/GameInterface/AutoSync/Properties/PropertySwitchCreator.cs +++ b/source/GameInterface/AutoSync/Properties/PropertySwitchCreator.cs @@ -166,58 +166,41 @@ private void CreateByRef(ILGenerator il, PropertyInfo property, LocalBuilder ins var errorString = $"Unable to find instance of type {instanceType.Name} with id "; var stringConcatMethod = AccessTools.Method(typeof(PropertySwitchCreator), nameof(Concat)); - var valueId = il.DeclareLocal(typeof(string)); var valueLocal = il.DeclareLocal(property.PropertyType); - - - var setValue = il.DefineLabel(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(PropertyAutoSyncPacket), nameof(PropertyAutoSyncPacket.value))); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(PropertySwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); - il.Emit(OpCodes.Stloc, valueId); - - // If id is null set null - if (property.PropertyType.IsClass) // structs cannot be null - { - var notNull = il.DefineLabel(); - - il.Emit(OpCodes.Ldloc, valueId); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(string), nameof(string.IsNullOrEmpty))); - il.Emit(OpCodes.Brfalse, notNull); - - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Stloc, valueLocal); - il.Emit(OpCodes.Br, setValue); - - il.MarkLabel(notNull); - } + il.Emit(OpCodes.Ldloc, instanceLocal); - // Try get value from id // Load objectmanager il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, objectManagerField); - il.Emit(OpCodes.Ldloc, valueId); + var getObjectSuccess = il.DefineLabel(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(PropertyAutoSyncPacket), nameof(PropertyAutoSyncPacket.value))); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(PropertySwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); + il.Emit(OpCodes.Ldloca, valueLocal); + il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(IObjectManager), nameof(IObjectManager.TryGetObject)).MakeGenericMethod(property.PropertyType)); - il.Emit(OpCodes.Brtrue, setValue); + il.Emit(OpCodes.Brtrue, getObjectSuccess); // if TryGetObject failes log error il.Emit(OpCodes.Ldsfld, loggerField); il.Emit(OpCodes.Ldstr, errorString); - il.Emit(OpCodes.Ldloc, valueId); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(PropertyAutoSyncPacket), nameof(PropertyAutoSyncPacket.value))); + il.Emit(OpCodes.Call, AccessTools.Method(typeof(PropertySwitchCreator), nameof(Deserialize)).MakeGenericMethod(typeof(string))); il.Emit(OpCodes.Call, stringConcatMethod); il.Emit(OpCodes.Call, AccessTools.Method(typeof(ILogger), nameof(ILogger.Error), new Type[] { typeof(string) })); + il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ret); - il.MarkLabel(setValue); + il.MarkLabel(getObjectSuccess); - il.Emit(OpCodes.Ldloc, instanceLocal); il.Emit(OpCodes.Ldloc, valueLocal); il.Emit(OpCodes.Call, AccessTools.Method(typeof(AllowedThread), nameof(AllowedThread.AllowThisThread))); diff --git a/source/GameInterface/ContainerProvider.cs b/source/GameInterface/ContainerProvider.cs index 572d1c3be..ca34f2a6c 100644 --- a/source/GameInterface/ContainerProvider.cs +++ b/source/GameInterface/ContainerProvider.cs @@ -28,12 +28,12 @@ public static bool TryGetContainer(out ILifetimeScope lifetimeScope) if (lifetimeScope == null) { - //var callStack = Environment.StackTrace; - //Logger.Error("{name} was not setup properly, try using {setupFnName}\n" + - // "CallStack: {callStack}", - // nameof(ContainerProvider), - // nameof(SetContainer), - // callStack); + var callStack = Environment.StackTrace; + Logger.Error("{name} was not setup properly, try using {setupFnName}\n" + + "CallStack: {callStack}", + nameof(ContainerProvider), + nameof(SetContainer), + callStack); return false; } diff --git a/source/GameInterface/Properties/AssemblyInfo.cs b/source/GameInterface/Properties/AssemblyInfo.cs index 7778567b5..b99533946 100644 --- a/source/GameInterface/Properties/AssemblyInfo.cs +++ b/source/GameInterface/Properties/AssemblyInfo.cs @@ -29,6 +29,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: InternalsVisibleTo("Coop.Tests")] -[assembly: InternalsVisibleTo("Coop.IntegrationTests")] -[assembly: InternalsVisibleTo("GameInterface.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("GameInterface.Tests")] diff --git a/source/GameInterface/Services/Armies/Patches/ArmyDisablePatches.cs b/source/GameInterface/Services/Armies/Patches/ArmyDisablePatches.cs deleted file mode 100644 index e63d26cd9..000000000 --- a/source/GameInterface/Services/Armies/Patches/ArmyDisablePatches.cs +++ /dev/null @@ -1,13 +0,0 @@ -using HarmonyLib; -using TaleWorlds.CampaignSystem; - -namespace GameInterface.Services.Armies.Patches -{ - [HarmonyPatch(typeof(Army))] - class ArmyDisablePatches - { - [HarmonyPatch(nameof(Army.Tick))] - [HarmonyPrefix] - private static bool DisableArmyTick() => ModInformation.IsServer; - } -} diff --git a/source/GameInterface/Services/BasicCharacterObjects/BasicCharacterObjectRegistry.cs b/source/GameInterface/Services/BasicCharacterObjects/BasicCharacterObjectRegistry.cs new file mode 100644 index 000000000..219a3bc1b --- /dev/null +++ b/source/GameInterface/Services/BasicCharacterObjects/BasicCharacterObjectRegistry.cs @@ -0,0 +1,33 @@ +using GameInterface.Services.Registry; +using System; +using System.Threading; +using TaleWorlds.CampaignSystem; +using TaleWorlds.Core; + +namespace GameInterface.Services.BasicCharacterObjects +{ + internal class BasicCharacterObjectRegistry : RegistryBase + { + private const string IdPrefix = "CoopBasicCharacter"; + private static int InstanceCounter = 0; + + public BasicCharacterObjectRegistry(IRegistryCollection collection) : base(collection) + { + } + + public override void RegisterAll() + { + foreach (BasicCharacterObject character in Campaign.Current.Characters) + { + if (TryGetId(character, out _)) continue; + + RegisterExistingObject(character.StringId, character); + } + } + + protected override string GetNewId(BasicCharacterObject obj) + { + return $"{IdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; + } + } +} diff --git a/source/GameInterface/Services/BasicCharacterObjects/Handlers/BasicCharacterLifetimeHandler.cs b/source/GameInterface/Services/BasicCharacterObjects/Handlers/BasicCharacterLifetimeHandler.cs new file mode 100644 index 000000000..cf51b3f88 --- /dev/null +++ b/source/GameInterface/Services/BasicCharacterObjects/Handlers/BasicCharacterLifetimeHandler.cs @@ -0,0 +1,56 @@ +using Common.Logging; +using Common.Messaging; +using Common.Network; +using Common.Util; +using GameInterface.Services.BasicCharacterObjects.Messages; +using GameInterface.Services.ObjectManager; +using Serilog; +using TaleWorlds.Core; + +namespace GameInterface.Services.BasicCharacterObjects.Handlers +{ + internal class BasicCharacterLifetimeHandler : IHandler + { + private static readonly ILogger Logger = LogManager.GetLogger(); + private readonly IMessageBroker messageBroker; + private readonly IObjectManager objectManager; + private readonly INetwork network; + + public BasicCharacterLifetimeHandler(IMessageBroker messageBroker, IObjectManager objectManager, INetwork network) + { + this.messageBroker = messageBroker; + this.objectManager = objectManager; + this.network = network; + messageBroker.Subscribe(Handle); + messageBroker.Subscribe(Handle); + } + + public void Dispose() + { + messageBroker.Unsubscribe(Handle); + messageBroker.Unsubscribe(Handle); + } + + private void Handle(MessagePayload obj) + { + var payload = obj.What; + + if (objectManager.AddNewObject(payload.CharacterObject, out string basicCharacterId) == false) return; + + var message = new NetworkCreateBasicCharacter(basicCharacterId); + network.SendAll(message); + } + + private void Handle(MessagePayload obj) + { + var payload = obj.What; + + var basicCharacter = ObjectHelper.SkipConstructor(); + if (objectManager.AddExisting(payload.BasicCharacterId, basicCharacter) == false) + { + Logger.Error("Failed to add existing Building, {id}", payload.BasicCharacterId); + return; + } + } + } +} diff --git a/source/GameInterface/Services/BasicCharacterObjects/Messages/BasicCharacterCreated.cs b/source/GameInterface/Services/BasicCharacterObjects/Messages/BasicCharacterCreated.cs new file mode 100644 index 000000000..f0b7a6593 --- /dev/null +++ b/source/GameInterface/Services/BasicCharacterObjects/Messages/BasicCharacterCreated.cs @@ -0,0 +1,18 @@ +using Common.Messaging; +using System; +using System.Collections.Generic; +using System.Text; +using TaleWorlds.Core; + +namespace GameInterface.Services.BasicCharacterObjects.Messages +{ + internal class BasicCharacterCreated : IEvent + { + public BasicCharacterObject CharacterObject { get; } + + public BasicCharacterCreated(BasicCharacterObject characterObject) + { + CharacterObject = characterObject; + } + } +} diff --git a/source/GameInterface/Services/BasicCharacterObjects/Messages/NetworkCreateBasicCharacter.cs b/source/GameInterface/Services/BasicCharacterObjects/Messages/NetworkCreateBasicCharacter.cs new file mode 100644 index 000000000..1dfd98fa3 --- /dev/null +++ b/source/GameInterface/Services/BasicCharacterObjects/Messages/NetworkCreateBasicCharacter.cs @@ -0,0 +1,17 @@ +using Common.Messaging; +using ProtoBuf; + +namespace GameInterface.Services.BasicCharacterObjects.Messages +{ + [ProtoContract(SkipConstructor = true)] + internal class NetworkCreateBasicCharacter : ICommand + { + [ProtoMember(1)] + public string BasicCharacterId { get; } + + public NetworkCreateBasicCharacter(string basicCharacterId) + { + BasicCharacterId = basicCharacterId; + } + } +} diff --git a/source/GameInterface/Services/BasicCharacterObjects/Patches/BasicCharacterObjectLifetimePatches.cs b/source/GameInterface/Services/BasicCharacterObjects/Patches/BasicCharacterObjectLifetimePatches.cs new file mode 100644 index 000000000..3e5b20db1 --- /dev/null +++ b/source/GameInterface/Services/BasicCharacterObjects/Patches/BasicCharacterObjectLifetimePatches.cs @@ -0,0 +1,43 @@ +using Common.Logging; +using Common.Messaging; +using GameInterface.Policies; +using GameInterface.Services.BasicCharacterObjects.Messages; +using GameInterface.Services.CharacterObjects.Messages; +using HarmonyLib; +using Serilog; +using System; +using TaleWorlds.CampaignSystem; +using TaleWorlds.Core; + +namespace GameInterface.Services.CharacterObjects.Patches +{ + /// + /// Lifetime Patches for BasicCharacterObjects + /// + [HarmonyPatch] + internal class BasicCharacterObjectLifetimePatches + { + private static ILogger Logger = LogManager.GetLogger(); + + [HarmonyPatch(typeof(BasicCharacterObject), MethodType.Constructor)] + [HarmonyPrefix] + private static bool CreateCharacterObjectPrefix(ref BasicCharacterObject __instance) + { + // Call original if we call this function + if (CallOriginalPolicy.IsOriginalAllowed()) return true; + + if (ModInformation.IsClient) + { + Logger.Error("Client created unmanaged {name}\n" + + "Callstack: {callstack}", typeof(BasicCharacterObject), Environment.StackTrace); + return false; + } + + var message = new BasicCharacterCreated(__instance); + + MessageBroker.Instance.Publish(__instance, message); + + return true; + } + } +} diff --git a/source/GameInterface/Services/BasicCultureObjects/BasicCultureObjectRegistry.cs b/source/GameInterface/Services/BasicCultureObjects/BasicCultureObjectRegistry.cs new file mode 100644 index 000000000..674f0b4a4 --- /dev/null +++ b/source/GameInterface/Services/BasicCultureObjects/BasicCultureObjectRegistry.cs @@ -0,0 +1,39 @@ +using GameInterface.Services.Registry; +using System.Threading; +using TaleWorlds.CampaignSystem; +using TaleWorlds.Core; +using TaleWorlds.ObjectSystem; + +namespace GameInterface.Services.BasicCultureObjects +{ + internal class BasicCultureObjectRegistry : RegistryBase + { + private const string IdPrefix = "CoopBasicCulture"; + private static int InstanceCounter = 0; + + public BasicCultureObjectRegistry(IRegistryCollection collection) : base(collection) + { + } + + public override void RegisterAll() + { + var objectManager = MBObjectManager.Instance; + + if (objectManager == null) + { + Logger.Error("Unable to register objects when CampaignObjectManager is null"); + return; + } + + foreach (var culture in objectManager.GetObjectTypeList()) + { + RegisterExistingObject(culture.StringId, culture); + } + } + + protected override string GetNewId(BasicCultureObject obj) + { + return $"{IdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; + } + } +} diff --git a/source/GameInterface/Services/BasicCultureObjects/Handlers/BasicCultureObjectLifetimeHandler.cs b/source/GameInterface/Services/BasicCultureObjects/Handlers/BasicCultureObjectLifetimeHandler.cs new file mode 100644 index 000000000..4fd8a4395 --- /dev/null +++ b/source/GameInterface/Services/BasicCultureObjects/Handlers/BasicCultureObjectLifetimeHandler.cs @@ -0,0 +1,56 @@ +using Common.Logging; +using Common.Messaging; +using Common.Network; +using Common.Util; +using GameInterface.Services.BasicCultureObjects.Messages; +using GameInterface.Services.ObjectManager; +using Serilog; +using TaleWorlds.Core; + +namespace GameInterface.Services.BasicCultureObjects.Handlers +{ + internal class BasicCultureObjectLifetimeHandler : IHandler + { + private static readonly ILogger Logger = LogManager.GetLogger(); + private readonly IMessageBroker messageBroker; + private readonly IObjectManager objectManager; + private readonly INetwork network; + + public BasicCultureObjectLifetimeHandler(IMessageBroker messageBroker, IObjectManager objectManager, INetwork network) + { + this.messageBroker = messageBroker; + this.objectManager = objectManager; + this.network = network; + messageBroker.Subscribe(Handle); + messageBroker.Subscribe(Handle); + } + + public void Dispose() + { + messageBroker.Unsubscribe(Handle); + messageBroker.Unsubscribe(Handle); + } + + private void Handle(MessagePayload obj) + { + var payload = obj.What; + + if (objectManager.AddNewObject(payload.CultureObject, out string BasicCultureId) == false) return; + + var message = new NetworkCreateBasicCulture(BasicCultureId); + network.SendAll(message); + } + + private void Handle(MessagePayload obj) + { + var payload = obj.What; + + var BasicCulture = ObjectHelper.SkipConstructor(); + if (objectManager.AddExisting(payload.CultureId, BasicCulture) == false) + { + Logger.Error("Failed to add existing Building, {id}", payload.CultureId); + return; + } + } + } +} \ No newline at end of file diff --git a/source/GameInterface/Services/BasicCultureObjects/Messages/BasicCultureCreated.cs b/source/GameInterface/Services/BasicCultureObjects/Messages/BasicCultureCreated.cs new file mode 100644 index 000000000..e63cd3eba --- /dev/null +++ b/source/GameInterface/Services/BasicCultureObjects/Messages/BasicCultureCreated.cs @@ -0,0 +1,15 @@ +using Common.Messaging; +using TaleWorlds.Core; + +namespace GameInterface.Services.BasicCultureObjects.Messages +{ + internal class BasicCultureCreated : IEvent + { + public BasicCultureObject CultureObject { get; } + + public BasicCultureCreated(BasicCultureObject cultureObject) + { + CultureObject = cultureObject; + } + } +} diff --git a/source/GameInterface/Services/BasicCultureObjects/Messages/NetworkCreateBasicCulture.cs b/source/GameInterface/Services/BasicCultureObjects/Messages/NetworkCreateBasicCulture.cs new file mode 100644 index 000000000..999e0dc69 --- /dev/null +++ b/source/GameInterface/Services/BasicCultureObjects/Messages/NetworkCreateBasicCulture.cs @@ -0,0 +1,17 @@ +using Common.Messaging; +using ProtoBuf; + +namespace GameInterface.Services.BasicCultureObjects.Messages +{ + [ProtoContract(SkipConstructor = true)] + internal class NetworkCreateBasicCulture : ICommand + { + [ProtoMember(1)] + public string CultureId { get; } + + public NetworkCreateBasicCulture(string cultureId) + { + CultureId = cultureId; + } + } +} diff --git a/source/GameInterface/Services/BasicCultureObjects/Patches/BasicCultureObjectLifetimePatches.cs b/source/GameInterface/Services/BasicCultureObjects/Patches/BasicCultureObjectLifetimePatches.cs new file mode 100644 index 000000000..11354f653 --- /dev/null +++ b/source/GameInterface/Services/BasicCultureObjects/Patches/BasicCultureObjectLifetimePatches.cs @@ -0,0 +1,41 @@ +using Common.Logging; +using Common.Messaging; +using GameInterface.Policies; +using GameInterface.Services.BasicCultureObjects.Messages; +using HarmonyLib; +using Serilog; +using System; +using TaleWorlds.Core; + +namespace GameInterface.Services.BasicCultureObjects.Patches +{ + /// + /// Lifetime Patches for BasicCultureObjects + /// + [HarmonyPatch] + internal class BasicCultureObjectLifetimePatches + { + private static ILogger Logger = LogManager.GetLogger(); + + [HarmonyPatch(typeof(BasicCultureObject), MethodType.Constructor)] + [HarmonyPrefix] + private static bool CreateBasicCultureObjectPrefix(ref BasicCultureObject __instance) + { + // Call original if we call this function + if (CallOriginalPolicy.IsOriginalAllowed()) return true; + + if (ModInformation.IsClient) + { + Logger.Error("Client created unmanaged {name}\n" + + "Callstack: {callstack}", typeof(BasicCultureObject), Environment.StackTrace); + return false; + } + + var message = new BasicCultureCreated(__instance); + + MessageBroker.Instance.Publish(__instance, message); + + return true; + } + } +} diff --git a/source/GameInterface/Services/BesiegerCamps/BesiegerCampRegistry.cs b/source/GameInterface/Services/BesiegerCamps/BesiegerCampRegistry.cs index 97cf7614f..3f78aa2b3 100644 --- a/source/GameInterface/Services/BesiegerCamps/BesiegerCampRegistry.cs +++ b/source/GameInterface/Services/BesiegerCamps/BesiegerCampRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.BesiegerCamps; internal class BeseigerCampRegistry : RegistryBase { private const string BeseigerCampIdPrefix = "CoopBeseigerCamp"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public BeseigerCampRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/Buildings/BuildingRegistry.cs b/source/GameInterface/Services/Buildings/BuildingRegistry.cs index 899fafa27..96b9e8bd3 100644 --- a/source/GameInterface/Services/Buildings/BuildingRegistry.cs +++ b/source/GameInterface/Services/Buildings/BuildingRegistry.cs @@ -13,7 +13,7 @@ namespace GameInterface.Services.Armies; internal class BuildingRegistry : RegistryBase { private const string BuildingIdPrefix = "CoopBuilding"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public BuildingRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/CharacterObjects/CharacterObjectRegistry.cs b/source/GameInterface/Services/CharacterObjects/CharacterObjectRegistry.cs index 3a0bda0c9..72fdc0115 100644 --- a/source/GameInterface/Services/CharacterObjects/CharacterObjectRegistry.cs +++ b/source/GameInterface/Services/CharacterObjects/CharacterObjectRegistry.cs @@ -1,9 +1,6 @@ using GameInterface.Services.Registry; -using System; -using System.Collections.Generic; using System.Threading; using TaleWorlds.CampaignSystem; -using TaleWorlds.Core; namespace GameInterface.Services.Armies; @@ -12,14 +9,8 @@ namespace GameInterface.Services.Armies; /// internal class CharacterObjectRegistry : RegistryBase { - public override IEnumerable ManagedTypes => new Type[] - { - typeof(BasicCharacterObject), - typeof(CharacterObject) - }; - private const string CharacterObjectPrefix = "CoopCharacterObject"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public CharacterObjectRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs b/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs index cf880e46b..00a8da106 100644 --- a/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs +++ b/source/GameInterface/Services/CharacterObjects/Patches/CharacterObjectLifetimePatches.cs @@ -5,11 +5,7 @@ using HarmonyLib; using Serilog; using System; -using System.Collections.Generic; -using System.Reflection.Emit; -using System.Reflection; using TaleWorlds.CampaignSystem; -using TaleWorlds.ObjectSystem; namespace GameInterface.Services.CharacterObjects.Patches { @@ -42,33 +38,4 @@ private static bool CreateCharacterObjectPrefix(ref CharacterObject __instance) return true; } } - - - //[HarmonyPatch] - //internal class MBObjectManagerLifetimePatches - //{ - // private static ILogger Logger = LogManager.GetLogger(); - - // private static IEnumerable TargetMethods() - // { - // yield return AccessTools.Method(typeof(MBObjectManager), nameof(MBObjectManager.CreateObject), new Type[] { typeof(string) }).MakeGenericMethod(typeof(CharacterObject)); - // } - - // [HarmonyTranspiler] - // private static IEnumerable CreateFromTranspiler(IEnumerable instructions) - // { - // foreach (var instr in instructions) - // { - // if (instr.Calls(AccessTools.PropertySetter(typeof(MBObjectBase), nameof(MBObjectBase.StringId)))) - // { - // yield return new CodeInstruction(OpCodes.Pop); - // yield return new CodeInstruction(OpCodes.Pop); - // } - // else - // { - // yield return instr; - // } - // } - // } - //} } diff --git a/source/GameInterface/Services/Clans/ClanRegistry.cs b/source/GameInterface/Services/Clans/ClanRegistry.cs index 1bea25ade..a0335d786 100644 --- a/source/GameInterface/Services/Clans/ClanRegistry.cs +++ b/source/GameInterface/Services/Clans/ClanRegistry.cs @@ -11,7 +11,7 @@ namespace GameInterface.Services.Clans; internal class ClanRegistry : RegistryBase { private const string ClanStringIdPrefix = "CoopClan"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public ClanRegistry(IRegistryCollection collection) : base(collection) { } @@ -28,7 +28,6 @@ public override void RegisterAll() foreach (var clan in objectManager.Clans) { RegisterExistingObject(clan.StringId, clan); - Interlocked.Increment(ref InstanceCounter); } } diff --git a/source/GameInterface/Services/CraftingService/CraftingRegistry.cs b/source/GameInterface/Services/CraftingService/CraftingRegistry.cs index 42b8563c3..ed1df164f 100644 --- a/source/GameInterface/Services/CraftingService/CraftingRegistry.cs +++ b/source/GameInterface/Services/CraftingService/CraftingRegistry.cs @@ -7,7 +7,7 @@ namespace GameInterface.Services.CraftingService internal class CraftingRegistry : RegistryBase { private const string CraftingIdPrefix = "CoopCrafting"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public CraftingRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs b/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs index 00a95ed3e..da963fffd 100644 --- a/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs +++ b/source/GameInterface/Services/CultureObjects/CultureObjectRegistry.cs @@ -1,22 +1,14 @@ using GameInterface.Services.Registry; -using System; -using System.Collections.Generic; using System.Threading; using TaleWorlds.CampaignSystem; -using TaleWorlds.Core; using TaleWorlds.ObjectSystem; namespace GameInterface.Services.CultureObjects { internal class CultureObjectRegistry : RegistryBase { - public override IEnumerable ManagedTypes => new Type[] { - typeof(BasicCultureObject), - typeof(CultureObject), - }; - private const string CultureStringIdPrefix = "CoopCulture"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public CultureObjectRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs b/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs index e9989eb28..a947830ff 100644 --- a/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs +++ b/source/GameInterface/Services/CultureObjects/Patches/CultureObjectLifetimePatches.cs @@ -5,11 +5,7 @@ using HarmonyLib; using Serilog; using System; -using System.Collections.Generic; -using System.Reflection; -using System.Reflection.Emit; using TaleWorlds.CampaignSystem; -using TaleWorlds.ObjectSystem; namespace GameInterface.Services.CultureObjects.Patches { @@ -40,32 +36,4 @@ private static bool ctorPrefix(ref CultureObject __instance) return true; } } - - //[HarmonyPatch] - //internal class MBObjectManagerLifetimePatches - //{ - // private static ILogger Logger = LogManager.GetLogger(); - - // private static IEnumerable TargetMethods() - // { - // yield return AccessTools.Method(typeof(MBObjectManager), nameof(MBObjectManager.CreateObject), new Type[] { typeof(string) }).MakeGenericMethod(typeof(CultureObject)); - // } - - // [HarmonyTranspiler] - // private static IEnumerable CreateFromTranspiler(IEnumerable instructions) - // { - // foreach (var instr in instructions) - // { - // if (instr.Calls(AccessTools.PropertySetter(typeof(MBObjectBase), nameof(MBObjectBase.StringId)))) - // { - // yield return new CodeInstruction(OpCodes.Pop); - // yield return new CodeInstruction(OpCodes.Pop); - // } - // else - // { - // yield return instr; - // } - // } - // } - //} } diff --git a/source/GameInterface/Services/Entity/ControlledEntityRegistry.cs b/source/GameInterface/Services/Entity/ControlledEntityRegistry.cs index c64ac8c52..b09c8fe8f 100644 --- a/source/GameInterface/Services/Entity/ControlledEntityRegistry.cs +++ b/source/GameInterface/Services/Entity/ControlledEntityRegistry.cs @@ -9,7 +9,6 @@ using System.Collections.ObjectModel; using System.Linq; using TaleWorlds.Core; -using TaleWorlds.Library; namespace GameInterface.Services.Entity; @@ -23,15 +22,7 @@ public interface IControlledEntityRegistry /// Packages an immutable dictionary of controlled entities /// /// Immutable dictionary of controlled entities - Dictionary> PackageControlledEntities(); - - /// - /// Attempts to register all provided entities. - /// - /// Is meant to be used to load from save file - /// - /// Entities to load - void LoadControlledEntities(Dictionary> controlledEntityMap); + IReadOnlyDictionary> PackageControlledEntities(); /// /// Registers an Enumerable of entities with the registry @@ -98,30 +89,12 @@ internal class ControlledEntityRegistry : IControlledEntityRegistry [ProtoMember(2)] private readonly ConcurrentDictionary controllerIdLookup = new ConcurrentDictionary(); - public Dictionary> PackageControlledEntities() => controlledEntities.ToDictionary(k => k.Key, v => v.Value); - - public void LoadControlledEntities(Dictionary> controlledEntityMap) + public IReadOnlyDictionary> PackageControlledEntities() { - if (controlledEntities.Count != 0) - { - Logger.Error($"Attempted to over-write existing controlled entity collection: ${nameof(controlledEntities)}"); - return; - } + // Make dictionary immutable + var readonlyListDict = controlledEntities.ToDictionary(k => k.Key, k => k.Value.AsReadOnly() as IReadOnlySet); - if (controllerIdLookup.Count != 0) - { - Logger.Error($"Attempted to over-write existing controlled entity collection: ${nameof(controllerIdLookup)}"); - return; - } - - foreach(var entitySet in controlledEntityMap) - { - var controllerId = entitySet.Key; - foreach (var entity in entitySet.Value) - { - RegisterAsControlled(controllerId, entity.EntityId); - } - } + return new ReadOnlyDictionary>(readonlyListDict); } public void RegisterExistingEntities(IEnumerable entityIds) diff --git a/source/GameInterface/Services/Entity/Data/ControlledEntity.cs b/source/GameInterface/Services/Entity/Data/ControlledEntity.cs index 0212e4c29..0684447de 100644 --- a/source/GameInterface/Services/Entity/Data/ControlledEntity.cs +++ b/source/GameInterface/Services/Entity/Data/ControlledEntity.cs @@ -1,6 +1,6 @@ using Common.Logging; -using ProtoBuf; using Serilog; +using System; using TaleWorlds.ObjectSystem; namespace GameInterface.Services.Entity.Data; @@ -8,7 +8,6 @@ namespace GameInterface.Services.Entity.Data; /// /// Controllable entity and owner /// -[ProtoContract(SkipConstructor = true)] public class ControlledEntity { private static readonly ILogger Logger = LogManager.GetLogger(); @@ -17,7 +16,6 @@ public class ControlledEntity /// Id of owner of the controlled entity. /// This can be either a client or server. /// - [ProtoMember(1)] public string OwnerId { get; } /// @@ -26,7 +24,6 @@ public class ControlledEntity /// /// This will normally be the StringId from the class /// - [ProtoMember(2)] public string EntityId { get; } public ControlledEntity(string ownerId, string entityId) @@ -53,21 +50,5 @@ public override bool Equals(object obj) return OwnerId == controlledEntity.OwnerId && EntityId == controlledEntity.EntityId; } - public static bool operator ==(ControlledEntity obj1, ControlledEntity obj2) - { - return obj1.Equals(obj2); - } - - public static bool operator !=(ControlledEntity obj1, ControlledEntity obj2) - { - return !obj1.Equals(obj2); - } - - public override int GetHashCode() - { - int hash = 552523; - hash = hash * 31 + OwnerId.GetHashCode(); - hash = hash * 31 + EntityId.GetHashCode(); - return hash; - } + public override int GetHashCode() => base.GetHashCode(); } diff --git a/source/GameInterface/Services/Equipments/EquipmentRegistry.cs b/source/GameInterface/Services/Equipments/EquipmentRegistry.cs index f4fc239e1..cae6c59a8 100644 --- a/source/GameInterface/Services/Equipments/EquipmentRegistry.cs +++ b/source/GameInterface/Services/Equipments/EquipmentRegistry.cs @@ -15,7 +15,7 @@ namespace GameInterface.Services.Equipments; internal class EquipmentRegistry : RegistryBase { private const string EquipmentPrefix = $"Coop{nameof(Equipment)}"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public EquipmentRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/Equipments/Patches/EquipmentLifetimePatches.cs b/source/GameInterface/Services/Equipments/Patches/EquipmentLifetimePatches.cs index 0231a481c..b2eacc0ea 100644 --- a/source/GameInterface/Services/Equipments/Patches/EquipmentLifetimePatches.cs +++ b/source/GameInterface/Services/Equipments/Patches/EquipmentLifetimePatches.cs @@ -32,10 +32,10 @@ private static bool CreateEquipmentPrefix(Equipment __instance) if (ModInformation.IsClient) - { + { /* Logger.Error("Client created unmanaged {name}\n" + "Callstack: {callstack}", typeof(Equipment), Environment.StackTrace); - + */ return true; } @@ -53,10 +53,10 @@ private static bool CreateEquipmentParamPrefix(Equipment __instance, Equipment e if (ModInformation.IsClient) { - Logger.Error("Client created unmanaged {name}\n" + /* Logger.Error("Client created unmanaged {name}\n" + "Callstack: {callstack}", typeof(Equipment), Environment.StackTrace); - + */ return true; } @@ -67,20 +67,21 @@ private static bool CreateEquipmentParamPrefix(Equipment __instance, Equipment e [HarmonyPatch(typeof(Hero), nameof(Hero.OnDeath))] [HarmonyPrefix] - private static void OnDeathPrefix(ref Hero __instance) + private static bool OnDeathPrefix(ref Hero __instance) { - if (CallOriginalPolicy.IsOriginalAllowed()) return; + if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) { Logger.Error("Client created unmanaged {name}\n" + "Callstack: {callstack}", typeof(Hero), Environment.StackTrace); - return; + return true; } var message = new EquipmentRemoved(__instance.BattleEquipment, __instance.CivilianEquipment); MessageBroker.Instance.Publish(__instance, message); + return true; } diff --git a/source/GameInterface/Services/GameState/Interfaces/GameStateInterface.cs b/source/GameInterface/Services/GameState/Interfaces/GameStateInterface.cs index 9bf643080..55b5fff6f 100644 --- a/source/GameInterface/Services/GameState/Interfaces/GameStateInterface.cs +++ b/source/GameInterface/Services/GameState/Interfaces/GameStateInterface.cs @@ -30,7 +30,7 @@ public void EnterMainMenu() public void LoadSaveGame(byte[] saveData) { - GameLoopRunner.RunOnMainThread(() => InteralLoadSaveGame(saveData), blocking: true); + GameLoopRunner.RunOnMainThread(() => InteralLoadSaveGame(saveData)); } private void InteralLoadSaveGame(byte[] saveData) diff --git a/source/GameInterface/Services/Heroes/Handlers/HeroFieldsHandler.cs b/source/GameInterface/Services/Heroes/Handlers/HeroFieldsHandler.cs index 3b4d342bb..7da3ea7b7 100644 --- a/source/GameInterface/Services/Heroes/Handlers/HeroFieldsHandler.cs +++ b/source/GameInterface/Services/Heroes/Handlers/HeroFieldsHandler.cs @@ -82,13 +82,6 @@ private void Handle(MessagePayload payload) Logger.Error("Unable to find {type} with id: {id}", typeof(Hero), data.HeroId); return; } - - if (data.SettlementStringId == null) - { - instance._homeSettlement = null; - return; - } - if (objectManager.TryGetObject(data.SettlementStringId, out var settlement) == false) { Logger.Error("Unable to find {type} with id: {id}", typeof(Settlement), data.SettlementStringId); diff --git a/source/GameInterface/Services/Heroes/HeroRegistry.cs b/source/GameInterface/Services/Heroes/HeroRegistry.cs index d51eb77d8..57ac34498 100644 --- a/source/GameInterface/Services/Heroes/HeroRegistry.cs +++ b/source/GameInterface/Services/Heroes/HeroRegistry.cs @@ -1,5 +1,11 @@ -using System.Linq; +using Common; +using Common.Extensions; +using System; +using System.Linq; +using System.Reflection; +using System.Threading; using TaleWorlds.CampaignSystem; +using TaleWorlds.CampaignSystem.Party; namespace GameInterface.Services.Registry; @@ -9,6 +15,7 @@ namespace GameInterface.Services.Registry; internal class HeroRegistry : RegistryBase { public static readonly string HeroStringIdPrefix = "CoopHero"; + private int InstanceCounter = 0; public HeroRegistry(IRegistryCollection collection) : base(collection) { } @@ -40,7 +47,7 @@ public override bool RegisterExistingObject(string id, object obj) protected override string GetNewId(Hero hero) { - hero.StringId = Campaign.Current.CampaignObjectManager.FindNextUniqueStringId(HeroStringIdPrefix); + hero.StringId = $"{HeroStringIdPrefix}_{Interlocked.Increment(ref InstanceCounter)}"; return hero.StringId; } diff --git a/source/GameInterface/Services/IssuesService/Patches/Disable/IssueManagerDisablePatches.cs b/source/GameInterface/Services/IssuesService/Patches/Disable/IssueManagerDisablePatches.cs deleted file mode 100644 index da4ed4a3b..000000000 --- a/source/GameInterface/Services/IssuesService/Patches/Disable/IssueManagerDisablePatches.cs +++ /dev/null @@ -1,16 +0,0 @@ -using HarmonyLib; -using TaleWorlds.CampaignSystem.Issues; - -namespace GameInterface.Services.IssuesService.Patches.Disable; - -[HarmonyPatch(typeof(IssueManager))] -internal class IssueManagerDisablePatches -{ - [HarmonyPatch(nameof(IssueManager.DailyTick))] - [HarmonyPrefix] - private static bool DisableIssueDailyTick() => false; - - [HarmonyPatch(nameof(IssueManager.HourlyTick))] - [HarmonyPrefix] - private static bool DisableIssueHourlyTick() => false; -} diff --git a/source/GameInterface/Services/ItemObjects/ItemObjectRegistry.cs b/source/GameInterface/Services/ItemObjects/ItemObjectRegistry.cs index fd83b02ff..9d054a1b8 100644 --- a/source/GameInterface/Services/ItemObjects/ItemObjectRegistry.cs +++ b/source/GameInterface/Services/ItemObjects/ItemObjectRegistry.cs @@ -12,7 +12,7 @@ namespace GameInterface.Services.ItemObjects internal class ItemObjectRegistry : RegistryBase { private const string ItemObjectIdPrefix = "CoopItemObject"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public ItemObjectRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/MapEventSides/MapEventSideRegistry.cs b/source/GameInterface/Services/MapEventSides/MapEventSideRegistry.cs index 0d8f83ee5..4db009fe1 100644 --- a/source/GameInterface/Services/MapEventSides/MapEventSideRegistry.cs +++ b/source/GameInterface/Services/MapEventSides/MapEventSideRegistry.cs @@ -12,7 +12,7 @@ namespace GameInterface.Services.MapEvents; internal class MapEventSideRegistry : RegistryBase { private const string MapEventSideIdPrefix = "CoopMapEventSide"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public MapEventSideRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/MapEvents/MapEventRegistry.cs b/source/GameInterface/Services/MapEvents/MapEventRegistry.cs index ff6d0bc11..2c7f635d3 100644 --- a/source/GameInterface/Services/MapEvents/MapEventRegistry.cs +++ b/source/GameInterface/Services/MapEvents/MapEventRegistry.cs @@ -11,7 +11,7 @@ namespace GameInterface.Services.MapEvents; internal class MapEventRegistry : RegistryBase { private const string MapEventIdPrefix = "CoopMapEvent"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public MapEventRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs b/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs index f3edb9b1a..8000f415b 100644 --- a/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs +++ b/source/GameInterface/Services/MobileParties/Handlers/MobilePartyFieldsHandler.cs @@ -1,11 +1,16 @@ +using System.Collections.Generic; using Common.Logging; using Common.Messaging; using GameInterface.Services.MobileParties.Messages.Fields.Commands; using GameInterface.Services.ObjectManager; using Serilog; using TaleWorlds.CampaignSystem; +using TaleWorlds.CampaignSystem.ComponentInterfaces; using TaleWorlds.CampaignSystem.Party; +using TaleWorlds.CampaignSystem.Party.PartyComponents; using TaleWorlds.CampaignSystem.Settlements; +using TaleWorlds.Library; +using TaleWorlds.Localization; namespace GameInterface.Services.MobileParties.Handlers; @@ -47,19 +52,11 @@ private void Handle(MessagePayload payload) Logger.Error("Unable to find {type} with id: {id}", typeof(MobileParty), data.MobilePartyId); return; } - - if (data.AttachedToId == null) - { - instance._attachedTo = null; - return; - } - if (objectManager.TryGetObject(data.AttachedToId, out var attachedToMobileParty) == false) { - Logger.Error("Unable to find {type} with id: {id}", typeof(MobileParty), data.AttachedToId); + Logger.Error("Unable to find {type} with id: {id}", typeof(Settlement), data.AttachedToId); return; } - instance._attachedTo = attachedToMobileParty; } @@ -155,12 +152,6 @@ private void Handle(MessagePayload payload) Logger.Error("Unable to find {type} with id: {id}", typeof(MobileParty), data.MobilePartyId); return; } - - if (data.CustomHomeSettlementId == null) - { - instance._customHomeSettlement = null; - return; - } if (objectManager.TryGetObject(data.CustomHomeSettlementId, out var settlement) == false) { diff --git a/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs b/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs index 3db0c6f35..c5ce5962f 100644 --- a/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs +++ b/source/GameInterface/Services/MobileParties/MobilePartyRegistry.cs @@ -24,10 +24,17 @@ public MobilePartyRegistry(IRegistryCollection collection, IMessageBroker messag public override void RegisterAll() { - foreach (var party in MobileParty.All) + var objectManager = Campaign.Current?.CampaignObjectManager; + + if (objectManager == null) + { + Logger.Error("Unable to register objects when CampaignObjectManager is null"); + return; + } + + foreach (var party in objectManager.MobileParties) { base.RegisterExistingObject(party.StringId, party); - Interlocked.Increment(ref InstanceCounter); } } diff --git a/source/GameInterface/Services/MobileParties/Patches/MobilePartyFieldPatches.cs b/source/GameInterface/Services/MobileParties/Patches/MobilePartyFieldPatches.cs index a692d8a8a..4c679e20a 100644 --- a/source/GameInterface/Services/MobileParties/Patches/MobilePartyFieldPatches.cs +++ b/source/GameInterface/Services/MobileParties/Patches/MobilePartyFieldPatches.cs @@ -63,7 +63,7 @@ public static void AttachedToIntercept(MobileParty instance, MobileParty newAtta return; } - MessageBroker.Instance.Publish(instance, new AttachedToChanged(newAttachedTo?.StringId, instance.StringId)); + MessageBroker.Instance.Publish(instance, new AttachedToChanged(newAttachedTo.StringId, instance.StringId)); instance._attachedTo = newAttachedTo; } diff --git a/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs b/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs index 982ab52b0..4a40c8cae 100644 --- a/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs +++ b/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs @@ -99,7 +99,6 @@ static bool ParallelTickArmies(CampaignTickCacheDataStore __instance, int startI MobileParty mobileParty = tickCachePerParty.MobileParty; if (mobileParty.Party == null) continue; - if (mobileParty.AttachedTo == null) continue; MobileParty.CachedPartyVariables localVariables = tickCachePerParty.LocalVariables; mobileParty.TickForMovingArmyLeader(ref localVariables, __instance._currentDt, __instance._currentRealDt); diff --git a/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiRegistry.cs b/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiRegistry.cs index 37f39be96..618cd6901 100644 --- a/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiRegistry.cs +++ b/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiRegistry.cs @@ -8,7 +8,7 @@ namespace GameInterface.Services.MobilePartyAIs; internal class MobilePartyAiRegistry : RegistryBase { private const string MobilePartyAiIdPrefix = "CoopMobilePartyAi"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public MobilePartyAiRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiSync.cs b/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiSync.cs index d10a21e2e..927170685 100644 --- a/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiSync.cs +++ b/source/GameInterface/Services/MobilePartyAIs/MobilePartyAiSync.cs @@ -7,8 +7,6 @@ internal class MobilePartyAiSync : IAutoSync { public MobilePartyAiSync(IAutoSyncBuilder autoSyncBuilder) { - autoSyncBuilder.AddProperty(AccessTools.Property(typeof(MobilePartyAi), nameof(MobilePartyAi.AiBehaviorPartyBase))); - autoSyncBuilder.AddField(AccessTools.Field(typeof(MobilePartyAi), nameof(MobilePartyAi._mobileParty))); } } diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIDisablePatches.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIDisablePatches.cs deleted file mode 100644 index 285071794..000000000 --- a/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIDisablePatches.cs +++ /dev/null @@ -1,14 +0,0 @@ -using GameInterface.Services.MobileParties.Extensions; -using HarmonyLib; -using TaleWorlds.CampaignSystem.Party; - -namespace GameInterface.Services.MobilePartyAIs.Patches; - -[HarmonyPatch(typeof(MobilePartyAi))] -internal class MobilePartyAIDisablePatches -{ - - [HarmonyPatch(nameof(MobilePartyAi.Tick))] - [HarmonyPrefix] - private static bool ClientDisableTickPrefix(MobilePartyAi __instance) => ModInformation.IsServer || __instance._mobileParty.IsPartyControlled(); -} diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIRobustnessPatches.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIRobustnessPatches.cs deleted file mode 100644 index 8d2ab18d3..000000000 --- a/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIRobustnessPatches.cs +++ /dev/null @@ -1,13 +0,0 @@ -using HarmonyLib; -using TaleWorlds.CampaignSystem.Party; - -namespace GameInterface.Services.MobilePartyAIs.Patches; - -//[HarmonyPatch(typeof(MobilePartyAi))] -class MobilePartyAIRobustnessPatches -{ - [HarmonyPatch(nameof(MobilePartyAi.GetNearbyPartyToFlee))] - [HarmonyPrefix] - // Skipe if partyToFleeFrom is null - private static bool RobustnessPrefix_GetNearbyPartyToFlee(MobileParty partyToFleeFrom) => partyToFleeFrom != null; -} diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/PartiesThinkPatch.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/PartiesThinkPatch.cs index a4e3adc0a..032cb56d4 100644 --- a/source/GameInterface/Services/MobilePartyAIs/Patches/PartiesThinkPatch.cs +++ b/source/GameInterface/Services/MobilePartyAIs/Patches/PartiesThinkPatch.cs @@ -1,5 +1,4 @@ -using GameInterface.Services.MobileParties.Extensions; -using HarmonyLib; +using HarmonyLib; using System.Threading.Tasks; using TaleWorlds.CampaignSystem; @@ -28,7 +27,7 @@ private static bool PartiesThinkPrefix(Campaign __instance, ref float dt) { var currentIdx = (CurrentStartIdx + i) % __instance.MobileParties.Count; - __instance.MobileParties[currentIdx].Ai?.Tick(dt); + __instance.MobileParties[currentIdx].Ai.Tick(dt); } CurrentStartIdx = (CurrentStartIdx + UPDATES_PER_TICK) % __instance.MobileParties.Count; diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/PartyBehaviorPatch.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/PartyBehaviorPatch.cs index 9f0a062f3..080441cab 100644 --- a/source/GameInterface/Services/MobilePartyAIs/Patches/PartyBehaviorPatch.cs +++ b/source/GameInterface/Services/MobilePartyAIs/Patches/PartyBehaviorPatch.cs @@ -1,7 +1,6 @@ using Common.Logging; using Common.Messaging; using GameInterface.Services.MobileParties.Data; -using GameInterface.Services.MobileParties.Extensions; using GameInterface.Services.MobileParties.Handlers; using GameInterface.Services.MobileParties.Messages.Behavior; using HarmonyLib; @@ -53,8 +52,6 @@ private static bool SetAiBehaviorPrefix( { if (BehaviorIsSame(ref __instance, ref newAiBehavior, ref targetPartyFigure, ref bestTargetPoint)) return false; - if (__instance._mobileParty.IsPartyControlled() == false) return false; - MobileParty party = __instance._mobileParty; bool hasTargetEntity = false; @@ -86,7 +83,7 @@ private static bool BehaviorIsSame( if (targetPartyFigure != null) { - targetEntity = targetPartyFigure.IsSettlement ? targetPartyFigure.Settlement : targetPartyFigure.MobileParty; + targetEntity = targetPartyFigure.IsSettlement ? targetPartyFigure.MobileParty : targetPartyFigure.Settlement; } return __instance.AiBehaviorMapEntity == targetEntity && diff --git a/source/GameInterface/Services/PartyBases/PartyBaseRegistry.cs b/source/GameInterface/Services/PartyBases/PartyBaseRegistry.cs index c4d12374e..b7c73eabf 100644 --- a/source/GameInterface/Services/PartyBases/PartyBaseRegistry.cs +++ b/source/GameInterface/Services/PartyBases/PartyBaseRegistry.cs @@ -6,7 +6,7 @@ namespace GameInterface.Services.PartyBases; internal class PartyBaseRegistry : RegistryBase { private const string PartyBaseIdPrefix = "CoopPartyBase"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public PartyBaseRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/PartyComponents/Handlers/PartyComponentHandler.cs b/source/GameInterface/Services/PartyComponents/Handlers/PartyComponentHandler.cs index 6332f87ed..c167658a7 100644 --- a/source/GameInterface/Services/PartyComponents/Handlers/PartyComponentHandler.cs +++ b/source/GameInterface/Services/PartyComponents/Handlers/PartyComponentHandler.cs @@ -39,12 +39,53 @@ public PartyComponentHandler(IMessageBroker messageBroker, INetwork network, IOb this.objectManager = objectManager; messageBroker.Subscribe(Handle); messageBroker.Subscribe(Handle); + + messageBroker.Subscribe(Handle); + messageBroker.Subscribe(Handle); } public void Dispose() { messageBroker.Unsubscribe(Handle); messageBroker.Unsubscribe(Handle); + + messageBroker.Unsubscribe(Handle); + messageBroker.Unsubscribe(Handle); + } + + private void Handle(MessagePayload payload) + { + var componentId = payload.What.ComponentId; + var partyId = payload.What.PartyId; + + if(objectManager.TryGetObject(componentId, out var component) == false) + { + Logger.Error("Could not find PartyComponent with id {componentId}", componentId); + return; + } + + if (objectManager.TryGetObject(partyId, out var party) == false) + { + Logger.Error("Could not find MobileParty with id {componentId}", partyId); + return; + } + + PartyComponentPatches.OverrideSetParty(component, party); + } + + private void Handle(MessagePayload payload) + { + var component = payload.What.Component; + var party = payload.What.Party; + + if(objectManager.TryGetId(component, out var componentId) == false) + { + Logger.Error("PartyComponent was not registered with party PartyComponentRegistry"); + return; + } + + var message = new NetworkChangePartyComponentMobileParty(componentId, party.StringId); + network.SendAll(message); } private void Handle(MessagePayload payload) diff --git a/source/GameInterface/Services/PartyComponents/Messages/NetworkChangePartyComponentMobileParty.cs b/source/GameInterface/Services/PartyComponents/Messages/NetworkChangePartyComponentMobileParty.cs new file mode 100644 index 000000000..51179dd6d --- /dev/null +++ b/source/GameInterface/Services/PartyComponents/Messages/NetworkChangePartyComponentMobileParty.cs @@ -0,0 +1,18 @@ +using Common.Messaging; +using ProtoBuf; + +namespace GameInterface.Services.PartyComponents.Messages; +[ProtoContract(SkipConstructor = true)] +internal class NetworkChangePartyComponentMobileParty : ICommand +{ + public NetworkChangePartyComponentMobileParty(string componentId, string partyId) + { + ComponentId = componentId; + PartyId = partyId; + } + + [ProtoMember(1)] + public string ComponentId { get; } + [ProtoMember(2)] + public string PartyId { get; } +} diff --git a/source/GameInterface/Services/PartyComponents/Messages/PartyComponentMobilePartyChanged.cs b/source/GameInterface/Services/PartyComponents/Messages/PartyComponentMobilePartyChanged.cs new file mode 100644 index 000000000..c49de0514 --- /dev/null +++ b/source/GameInterface/Services/PartyComponents/Messages/PartyComponentMobilePartyChanged.cs @@ -0,0 +1,10 @@ +using Common.Messaging; +using TaleWorlds.CampaignSystem.Party; +using TaleWorlds.CampaignSystem.Party.PartyComponents; + +namespace GameInterface.Services.PartyComponents.Messages; +internal record PartyComponentMobilePartyChanged(PartyComponent Component, MobileParty Party) : IEvent +{ + public PartyComponent Component { get; } = Component; + public MobileParty Party { get; } = Party; +} diff --git a/source/GameInterface/Services/PartyComponents/PartyComponentRegistry.cs b/source/GameInterface/Services/PartyComponents/PartyComponentRegistry.cs index 47572163b..60b06de30 100644 --- a/source/GameInterface/Services/PartyComponents/PartyComponentRegistry.cs +++ b/source/GameInterface/Services/PartyComponents/PartyComponentRegistry.cs @@ -14,7 +14,7 @@ namespace GameInterface.Services.PartyComponents; internal class PartyComponentRegistry : RegistryBase { private const string PartyComponentIdPrefix = "CoopPartyComponent"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public PartyComponentRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/PartyComponents/PartyComponentSync.cs b/source/GameInterface/Services/PartyComponents/PartyComponentSync.cs deleted file mode 100644 index 889b9c019..000000000 --- a/source/GameInterface/Services/PartyComponents/PartyComponentSync.cs +++ /dev/null @@ -1,12 +0,0 @@ -using GameInterface.AutoSync; -using HarmonyLib; -using TaleWorlds.CampaignSystem.Party.PartyComponents; - -namespace GameInterface.Services.PartyComponents; -internal class PartyComponentSync : IAutoSync -{ - public PartyComponentSync(IAutoSyncBuilder autoSyncBuilder) - { - autoSyncBuilder.AddProperty(AccessTools.Property(typeof(PartyComponent), nameof(PartyComponent.MobileParty))); - } -} diff --git a/source/GameInterface/Services/PartyComponents/Patches/CustomPartyComponents/CustomPartyComponentPatches.cs b/source/GameInterface/Services/PartyComponents/Patches/CustomPartyComponents/CustomPartyComponentPatches.cs index a91b4537b..1184b671a 100644 --- a/source/GameInterface/Services/PartyComponents/Patches/CustomPartyComponents/CustomPartyComponentPatches.cs +++ b/source/GameInterface/Services/PartyComponents/Patches/CustomPartyComponents/CustomPartyComponentPatches.cs @@ -88,17 +88,14 @@ private static IEnumerable HomeSettlementTranspiler(IEnumerable public static void HomeSettlementIntercept(CustomPartyComponent instance, Settlement newSettlement) { - if (instance._homeSettlement == newSettlement) return; - if (CallOriginalPolicy.IsOriginalAllowed()) { instance._homeSettlement = newSettlement; return; } - if (ModInformation.IsClient) { - //Logger.Error("Client added unmanaged item: {callstack}", Environment.StackTrace); + Logger.Error("Client added unmanaged item: {callstack}", Environment.StackTrace); return; } diff --git a/source/GameInterface/Services/PartyComponents/Patches/PartyComponentPatches.cs b/source/GameInterface/Services/PartyComponents/Patches/PartyComponentPatches.cs new file mode 100644 index 000000000..b52589b4e --- /dev/null +++ b/source/GameInterface/Services/PartyComponents/Patches/PartyComponentPatches.cs @@ -0,0 +1,50 @@ +using Common; +using Common.Logging; +using Common.Messaging; +using Common.Util; +using GameInterface.Policies; +using GameInterface.Services.PartyComponents.Messages; +using HarmonyLib; +using Serilog; +using System; +using TaleWorlds.CampaignSystem.Party; +using TaleWorlds.CampaignSystem.Party.PartyComponents; + +namespace GameInterface.Services.PartyComponents.Patches; + +[HarmonyPatch(typeof(PartyComponent))] +internal class PartyComponentPatches +{ + private static readonly ILogger Logger = LogManager.GetLogger(); + + [HarmonyPatch(nameof(PartyComponent.MobileParty), MethodType.Setter)] + private static bool Prefix(PartyComponent __instance, MobileParty value) + { + // Call original if we call this function + if (CallOriginalPolicy.IsOriginalAllowed()) return true; + + if (ModInformation.IsClient) + { + Logger.Error("Client created unmanaged {name}\n" + + "Callstack: {callstack}", typeof(PartyComponent), Environment.StackTrace); + return false; + } + + var message = new PartyComponentMobilePartyChanged(__instance, value); + + MessageBroker.Instance.Publish(__instance, message); + + return true; + } + + public static void OverrideSetParty(PartyComponent component, MobileParty party) + { + GameLoopRunner.RunOnMainThread(() => + { + using (new AllowedThread()) + { + component.MobileParty = party; + } + }); + } +} diff --git a/source/GameInterface/Services/PartyVisuals/Handlers/PartyVisualLifetimeHandler.cs b/source/GameInterface/Services/PartyVisuals/Handlers/PartyVisualLifetimeHandler.cs index f8a5c6d43..1ab21d621 100644 --- a/source/GameInterface/Services/PartyVisuals/Handlers/PartyVisualLifetimeHandler.cs +++ b/source/GameInterface/Services/PartyVisuals/Handlers/PartyVisualLifetimeHandler.cs @@ -48,11 +48,9 @@ private void Handle(MessagePayload payload) { objectManager.TryGetObject(payload.What.PartyBaseId, out var partyBase); - using(new AllowedThread()) - { - PartyVisual newVisual = new PartyVisual(partyBase); - objectManager.AddExisting(payload.What.PartyVisualId, newVisual); - } + PartyVisual newVisual = new PartyVisual(partyBase); + + objectManager.AddExisting(payload.What.PartyVisualId, newVisual); } private void Handle(MessagePayload payload) diff --git a/source/GameInterface/Services/PartyVisuals/PartyVisualRegistry.cs b/source/GameInterface/Services/PartyVisuals/PartyVisualRegistry.cs index 5e2c11159..e400c20c5 100644 --- a/source/GameInterface/Services/PartyVisuals/PartyVisualRegistry.cs +++ b/source/GameInterface/Services/PartyVisuals/PartyVisualRegistry.cs @@ -10,7 +10,7 @@ namespace GameInterface.Services.PartyVisuals internal class PartyVisualRegistry : RegistryBase { private const string PartyVisualIdPrefix = $"Coop{nameof(PartyVisual)}"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public PartyVisualRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/Registry/RegistryBase.cs b/source/GameInterface/Services/Registry/RegistryBase.cs index 9f9ec8506..1a4df1e4e 100644 --- a/source/GameInterface/Services/Registry/RegistryBase.cs +++ b/source/GameInterface/Services/Registry/RegistryBase.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -using TaleWorlds.CampaignSystem; using TaleWorlds.ObjectSystem; namespace GameInterface.Services.Registry; @@ -85,8 +84,6 @@ public virtual bool RegisterNewObject(object obj, out string id) if (obj is MBObjectBase mbObject) { mbObject.StringId = newId; - - MBObjectManager.Instance?.RegisterObject(mbObject); } objIds.Add(newId, castedObj); diff --git a/source/GameInterface/Services/Save/Messages/LoadExistingObjectGuids.cs b/source/GameInterface/Services/Save/Messages/LoadExistingObjectGuids.cs new file mode 100644 index 000000000..3d71a4598 --- /dev/null +++ b/source/GameInterface/Services/Save/Messages/LoadExistingObjectGuids.cs @@ -0,0 +1,20 @@ +using Common.Messaging; +using GameInterface.Services.Heroes.Data; +using System; + +namespace GameInterface.Services.Heroes.Messages; + +public record LoadExistingObjectGuids : ICommand +{ + public GameObjectGuids GameObjectGuids { get; } + + public LoadExistingObjectGuids( + GameObjectGuids gameObjectGuids) + { + GameObjectGuids = gameObjectGuids; + } +} + +public record ExistingObjectGuidsLoaded : IResponse +{ +} diff --git a/source/GameInterface/Services/Save/Patches/SavePatches.cs b/source/GameInterface/Services/Save/Patches/SavePatches.cs index 521bdebfc..473143e16 100644 --- a/source/GameInterface/Services/Save/Patches/SavePatches.cs +++ b/source/GameInterface/Services/Save/Patches/SavePatches.cs @@ -8,12 +8,8 @@ namespace GameInterface.Services.Heroes.Patches; [HarmonyPatch(typeof(Game), "Save")] class SavePatches { - static bool Prefix(Game __instance, ref string saveName) + static void Prefix(Game __instance, ref string saveName) { - // Disable saving for the client so we don't have to worry about pausing to save - if (ModInformation.IsClient) return false; - MessageBroker.Instance.Publish(__instance, new GameSaved(saveName)); - return true; } } diff --git a/source/GameInterface/Services/SettlementComponents/SettlementComponentRegistry.cs b/source/GameInterface/Services/SettlementComponents/SettlementComponentRegistry.cs index 32884a3ff..6509cc952 100644 --- a/source/GameInterface/Services/SettlementComponents/SettlementComponentRegistry.cs +++ b/source/GameInterface/Services/SettlementComponents/SettlementComponentRegistry.cs @@ -8,7 +8,7 @@ namespace GameInterface.Services.SettlementComponents; internal class SettlementComponentRegistry : RegistryBase { private const string SettlementComponentIdPrefix = "CoopSettlementComponent"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public override IEnumerable ManagedTypes { get; } = new Type[] { diff --git a/source/GameInterface/Services/SiegeEngineConstructionProgress/SiegeEngineConstructionProgressRegistry.cs b/source/GameInterface/Services/SiegeEngineConstructionProgress/SiegeEngineConstructionProgressRegistry.cs index b730052ea..b71e1ab71 100644 --- a/source/GameInterface/Services/SiegeEngineConstructionProgress/SiegeEngineConstructionProgressRegistry.cs +++ b/source/GameInterface/Services/SiegeEngineConstructionProgress/SiegeEngineConstructionProgressRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.SiegeEngines internal class SiegeEngineConstructionProgressRegistry : RegistryBase { private const string SiegeEngineConstructionProgressIdPrefix = "CoopSiegeEngineConstructionProgress"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public SiegeEngineConstructionProgressRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/SiegeEngines/SiegeEnginesContainerRegistry.cs b/source/GameInterface/Services/SiegeEngines/SiegeEnginesContainerRegistry.cs index e732bb6eb..a6dabb7c5 100644 --- a/source/GameInterface/Services/SiegeEngines/SiegeEnginesContainerRegistry.cs +++ b/source/GameInterface/Services/SiegeEngines/SiegeEnginesContainerRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.SiegeEngines internal class SiegeEnginesContainerRegistry : RegistryBase { private const string SiegeEnginesContainerIdPrefix = "CoopSiegeEnginesContainer"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public SiegeEnginesContainerRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/SiegeEvents/SiegeEventRegistry.cs b/source/GameInterface/Services/SiegeEvents/SiegeEventRegistry.cs index b73983e01..fdc59cf91 100644 --- a/source/GameInterface/Services/SiegeEvents/SiegeEventRegistry.cs +++ b/source/GameInterface/Services/SiegeEvents/SiegeEventRegistry.cs @@ -13,7 +13,7 @@ namespace GameInterface.Services.SiegeEvents; internal class SiegeEventRegistry : RegistryBase { private const string SiegeEventIdPrefix = "CoopSiegeEvent"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public SiegeEventRegistry(IRegistryCollection collection) : base(collection) { diff --git a/source/GameInterface/Services/Time/Handlers/TimeControlHandler.cs b/source/GameInterface/Services/Time/Handlers/TimeControlHandler.cs index 45924ca2b..67cddeab2 100644 --- a/source/GameInterface/Services/Time/Handlers/TimeControlHandler.cs +++ b/source/GameInterface/Services/Time/Handlers/TimeControlHandler.cs @@ -1,5 +1,4 @@ using Common.Messaging; -using Common.Network; using GameInterface.Services.Heroes.Interaces; using GameInterface.Services.Heroes.Messages; using System; @@ -14,8 +13,7 @@ internal class TimeControlHandler : IHandler public TimeControlHandler( ITimeControlInterface timeControlInterface, - IMessageBroker messageBroker, - INetwork network) + IMessageBroker messageBroker) { this.timeControlInterface = timeControlInterface; this.messageBroker = messageBroker; @@ -42,5 +40,7 @@ private void Handle(MessagePayload obj) var payload = obj.What; timeControlInterface.SetTimeControl(payload.NewTimeMode); + + messageBroker.Respond(obj.Who, new TimeControlModeSet(payload.NewTimeMode)); } } diff --git a/source/GameInterface/Services/Time/Patches/TimePatches.cs b/source/GameInterface/Services/Time/Patches/TimePatches.cs index 52567e91b..ba5c0e1ba 100644 --- a/source/GameInterface/Services/Time/Patches/TimePatches.cs +++ b/source/GameInterface/Services/Time/Patches/TimePatches.cs @@ -1,10 +1,14 @@ -using Common.Messaging; +using Common.Extensions; +using Common.Messaging; using Common.Util; using GameInterface.Services.Heroes.Messages; using GameInterface.Services.Time; using HarmonyLib; using SandBox.View.Map; +using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Reflection.Emit; using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Encounters; diff --git a/source/GameInterface/Services/Towns/Patches/TownPatches.cs b/source/GameInterface/Services/Towns/Patches/TownPatches.cs index a5d102f64..36ce75ccf 100644 --- a/source/GameInterface/Services/Towns/Patches/TownPatches.cs +++ b/source/GameInterface/Services/Towns/Patches/TownPatches.cs @@ -25,6 +25,7 @@ public class TownPatches [HarmonyPrefix] private static bool TownGovernorPrefix(ref Town __instance, ref Hero value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -52,6 +53,7 @@ public static void ChangeTownGovernor(Town town, Hero governor) [HarmonyPrefix] private static bool TownProsperityPrefix(ref Town __instance, ref float value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -78,6 +80,7 @@ public static void ChangeTownProsperity(Town town, float prosperity) [HarmonyPrefix] private static bool TownLoyaltyPrefix(ref Town __instance, ref float value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -129,6 +132,7 @@ public static void ChangeTownSecurity(Town town, float security) [HarmonyPrefix] private static bool TownLastCapturedByPrefix(ref Town __instance, ref Clan value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -171,6 +175,7 @@ public static void ChangeTownGarrisonAutoRecruitmentIsEnabled(Town town, bool ga [HarmonyPrefix] private static bool TownTradeTaxAccumulatedPrefix(ref Town __instance, ref int value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -196,6 +201,7 @@ public static void ChangeTradeTaxAccumulated(Town town, int tradeTaxAccumulated) [HarmonyPrefix] private static bool SetSoldItemsPrefix(Town __instance, IEnumerable logList) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; diff --git a/source/GameInterface/Services/Villages/Patches/VillagePatches.cs b/source/GameInterface/Services/Villages/Patches/VillagePatches.cs index b453c6a13..02d125f7f 100644 --- a/source/GameInterface/Services/Villages/Patches/VillagePatches.cs +++ b/source/GameInterface/Services/Villages/Patches/VillagePatches.cs @@ -28,6 +28,7 @@ internal class VillagePatches [HarmonyPrefix] private static bool VillageStatePrefix(ref Village __instance, ref VillageStates value) { + if(AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -62,6 +63,7 @@ public static void RunVillageStateChange(Village village, VillageStates state) [HarmonyPrefix] private static bool HearthPrefix(ref Village __instance, ref float value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -86,6 +88,7 @@ public static void ChangeHearth(Village village, float Hearth) [HarmonyPrefix] private static bool TradeBoundPrefix(ref Village __instance, ref Settlement value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -114,6 +117,7 @@ internal static void RunTradeBoundChange(Village village, Settlement tradebound) [HarmonyPrefix] private static bool TradeTaxAccumulatedPrefix(ref Village __instance, ref int value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; @@ -138,6 +142,7 @@ internal static void RunTradeTaxChange(Village village, int tradeTaxAccumulated) [HarmonyPrefix] private static bool LastDemandSatisifiedTimePrefix(ref Village __instance, ref float value) { + if (AllowedThread.IsThisThreadAllowed()) return true; if (CallOriginalPolicy.IsOriginalAllowed()) return true; if (ModInformation.IsClient) return false; diff --git a/source/GameInterface/Services/WeaponDesigns/WeaponDesignRegistry.cs b/source/GameInterface/Services/WeaponDesigns/WeaponDesignRegistry.cs index 868836079..fc25b2213 100644 --- a/source/GameInterface/Services/WeaponDesigns/WeaponDesignRegistry.cs +++ b/source/GameInterface/Services/WeaponDesigns/WeaponDesignRegistry.cs @@ -11,7 +11,7 @@ namespace GameInterface.Services.ItemObjects internal class WeaponDesignRegistry : RegistryBase { private const string ItemObjectIdPrefix = "CoopWeaponDesign"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public WeaponDesignRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/Workshops/Patches/WorkshopDisable.cs b/source/GameInterface/Services/Workshops/Patches/WorkshopDisable.cs new file mode 100644 index 000000000..53192fd00 --- /dev/null +++ b/source/GameInterface/Services/Workshops/Patches/WorkshopDisable.cs @@ -0,0 +1,12 @@ +using HarmonyLib; +using TaleWorlds.CampaignSystem.Settlements.Workshops; + +namespace GameInterface.Services.Workshops.Patches +{ + [HarmonyPatch(typeof(Workshop))] + internal class WorkshopDisable + { + [HarmonyPatch(nameof(Workshop.AfterLoad))] + static bool Prefix() => ModInformation.IsServer; + } +} diff --git a/source/GameInterface/Services/Workshops/WorkshopRegistry.cs b/source/GameInterface/Services/Workshops/WorkshopRegistry.cs index 33cfab4f8..d86604817 100644 --- a/source/GameInterface/Services/Workshops/WorkshopRegistry.cs +++ b/source/GameInterface/Services/Workshops/WorkshopRegistry.cs @@ -9,7 +9,7 @@ namespace GameInterface.Services.Workshops internal class WorkshopRegistry : RegistryBase { private const string WorkshopIdPrefix = $"Coop{nameof(Workshop)}"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public WorkshopRegistry(IRegistryCollection collection) : base(collection) { } diff --git a/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs b/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs index f8e17a3d9..25935605f 100644 --- a/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs +++ b/source/GameInterface/Services/Workshops/WorkshopTypeRegistry.cs @@ -1,5 +1,11 @@ using GameInterface.Services.Registry; +using ProtoBuf.Meta; +using System; +using System.Collections.Generic; +using System.Text; using System.Threading; +using TaleWorlds.CampaignSystem.MapEvents; +using TaleWorlds.CampaignSystem.Settlements; using TaleWorlds.CampaignSystem.Settlements.Workshops; namespace GameInterface.Services.Workshops @@ -7,17 +13,16 @@ namespace GameInterface.Services.Workshops internal class WorkshopTypeRegistry : RegistryBase { private const string WorkshopTypePrefix = $"Coop{nameof(WorkshopType)}"; - private int InstanceCounter = 0; + private static int InstanceCounter = 0; public WorkshopTypeRegistry(IRegistryCollection collection) : base(collection) { } public override void RegisterAll() { - // THIS BREAKS SAVING AND LOADING FOR SOME REASON, DO NOT UNCOMMENT UNTIL WE FIGURE OUT WHY AND HOW TO FIX - //foreach(WorkshopType workshopType in WorkshopType.All) - //{ - // RegisterNewObject(workshopType, out var _); - //} + foreach(WorkshopType workshopType in WorkshopType.All) + { + RegisterNewObject(workshopType, out var _); + } } protected override string GetNewId(WorkshopType party)