From cd4b8914bc32cb898bf2854d3bd40132671141a8 Mon Sep 17 00:00:00 2001 From: EgardA Date: Fri, 27 Sep 2024 18:18:07 +0200 Subject: [PATCH] Building Fields & Properties --- .../Services/Buildings/BuildingSyncTests.cs | 109 ++++++++++++++++++ source/E2E.Tests/Util/GameObjectCreator.cs | 2 + .../Util/ObjectBuilders/BuildingBuilder.cs | 14 +++ .../Services/Buildings/BuildingSync.cs | 31 +++++ 4 files changed, 156 insertions(+) create mode 100644 source/E2E.Tests/Services/Buildings/BuildingSyncTests.cs create mode 100644 source/E2E.Tests/Util/ObjectBuilders/BuildingBuilder.cs create mode 100644 source/GameInterface/Services/Buildings/BuildingSync.cs diff --git a/source/E2E.Tests/Services/Buildings/BuildingSyncTests.cs b/source/E2E.Tests/Services/Buildings/BuildingSyncTests.cs new file mode 100644 index 000000000..63dc50c08 --- /dev/null +++ b/source/E2E.Tests/Services/Buildings/BuildingSyncTests.cs @@ -0,0 +1,109 @@ +using Common.Util; +using E2E.Tests.Environment; +using E2E.Tests.Environment.Instance; +using E2E.Tests.Util; +using HarmonyLib; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TaleWorlds.CampaignSystem.Party.PartyComponents; +using TaleWorlds.CampaignSystem.Settlements; +using TaleWorlds.CampaignSystem.Settlements.Buildings; +using Xunit.Abstractions; + +namespace E2E.Tests.Services.Buildings +{ + public class BuildingSyncTests : IDisposable + { + E2ETestEnvironment TestEnvironment { get; } + + EnvironmentInstance Server => TestEnvironment.Server; + + IEnumerable Clients => TestEnvironment.Clients; + + private readonly string BuildingId; + private readonly string TownId; + private int newInt = 50; + private float newFloat = 25f; + + public BuildingSyncTests(ITestOutputHelper output) + { + TestEnvironment = new E2ETestEnvironment(output); + + var building = GameObjectCreator.CreateInitializedObject(); + var town = GameObjectCreator.CreateInitializedObject(); + + // Create objects on the server + Assert.True(Server.ObjectManager.AddNewObject(building, out BuildingId)); + Assert.True(Server.ObjectManager.AddNewObject(town, out TownId)); + + // Create objects on all clients + foreach (var client in Clients) + { + Assert.True(client.ObjectManager.AddExisting(BuildingId, building)); + Assert.True(client.ObjectManager.AddExisting(TownId, town)); + } + } + + public void Dispose() + { + TestEnvironment.Dispose(); + } + + [Fact] + public void Server_Building_Sync() + { + // Arrange + var server = TestEnvironment.Server; + + var hitpointsField = AccessTools.Field(typeof(Building), nameof(Building._hitpoints)); + var currentLevelField = AccessTools.Field(typeof(Building), nameof(Building._currentLevel)); + var isCurrentlyDefaultField = AccessTools.Field(typeof(Building), nameof(Building.IsCurrentlyDefault)); + var buildingProgressField = AccessTools.Field(typeof(Building), nameof(Building.BuildingProgress)); + + // Get field intercept to use on the server to simulate the field changing + var hitpointsIntercept = TestEnvironment.GetIntercept(hitpointsField); + var currentLevelIntercept = TestEnvironment.GetIntercept(currentLevelField); + var isCurrentlyDefaultIntercept = TestEnvironment.GetIntercept(isCurrentlyDefaultField); + var buildingProgressIntercept = TestEnvironment.GetIntercept(buildingProgressField); + + // Act + server.Call(() => + { + Assert.True(server.ObjectManager.TryGetObject(BuildingId, out var serverBuilding)); + Assert.True(server.ObjectManager.TryGetObject(TownId, out var serverTown)); + + // Simulate the field changing + hitpointsIntercept.Invoke(null, new object[] { serverBuilding, newFloat }); + currentLevelIntercept.Invoke(null, new object[] { serverBuilding, newInt }); + isCurrentlyDefaultIntercept.Invoke(null, new object[] { serverBuilding, true }); + buildingProgressIntercept.Invoke(null, new object[] { serverBuilding, newFloat }); + + serverBuilding.Town = serverTown; + + Assert.Equal(newFloat, serverBuilding._hitpoints); + Assert.Equal(newInt, serverBuilding._currentLevel); + Assert.True(serverBuilding.IsCurrentlyDefault); + Assert.Equal(newFloat, serverBuilding.BuildingProgress); + + Assert.Equal(serverTown, serverBuilding.Town); + }); + + // Assert + foreach (var client in Clients) + { + Assert.True(client.ObjectManager.TryGetObject(BuildingId, out var clientBuilding)); + Assert.True(client.ObjectManager.TryGetObject(TownId, out var clientTown)); + + Assert.Equal(newFloat, clientBuilding._hitpoints); + Assert.Equal(newInt, clientBuilding._currentLevel); + Assert.True(clientBuilding.IsCurrentlyDefault); + Assert.Equal(newFloat, clientBuilding.BuildingProgress); + + Assert.Equal(clientTown, clientBuilding.Town); + } + } + } +} diff --git a/source/E2E.Tests/Util/GameObjectCreator.cs b/source/E2E.Tests/Util/GameObjectCreator.cs index 656b21da7..be9165802 100644 --- a/source/E2E.Tests/Util/GameObjectCreator.cs +++ b/source/E2E.Tests/Util/GameObjectCreator.cs @@ -5,6 +5,7 @@ using TaleWorlds.CampaignSystem.Party; using TaleWorlds.CampaignSystem.Party.PartyComponents; using TaleWorlds.CampaignSystem.Settlements; +using TaleWorlds.CampaignSystem.Settlements.Buildings; using TaleWorlds.CampaignSystem.Settlements.Workshops; using TaleWorlds.CampaignSystem.Siege; @@ -33,6 +34,7 @@ internal class GameObjectCreator { typeof(SiegeEvent), new SiegeEventBuilder() }, { typeof(Workshop), new WorkshopBuilder() }, { typeof(WorkshopType), new WorkshopTypeBuilder() }, + { typeof(Building), new BuildingBuilder() }, }; public static T CreateInitializedObject() diff --git a/source/E2E.Tests/Util/ObjectBuilders/BuildingBuilder.cs b/source/E2E.Tests/Util/ObjectBuilders/BuildingBuilder.cs new file mode 100644 index 000000000..de4a45c23 --- /dev/null +++ b/source/E2E.Tests/Util/ObjectBuilders/BuildingBuilder.cs @@ -0,0 +1,14 @@ +using TaleWorlds.CampaignSystem.Settlements; +using TaleWorlds.CampaignSystem.Settlements.Buildings; + +namespace E2E.Tests.Util.ObjectBuilders; +internal class BuildingBuilder : IObjectBuilder +{ + public object Build() + { + var town = GameObjectCreator.CreateInitializedObject(); + var buildingType = new BuildingType("testBuildingType"); + + return new Building(buildingType, town); + } +} diff --git a/source/GameInterface/Services/Buildings/BuildingSync.cs b/source/GameInterface/Services/Buildings/BuildingSync.cs new file mode 100644 index 000000000..31184a958 --- /dev/null +++ b/source/GameInterface/Services/Buildings/BuildingSync.cs @@ -0,0 +1,31 @@ +using GameInterface.AutoSync; +using HarmonyLib; +using Helpers; +using System; +using System.Collections.Generic; +using System.Text; +using TaleWorlds.CampaignSystem.CampaignBehaviors; +using TaleWorlds.CampaignSystem.Settlements; +using TaleWorlds.CampaignSystem.Settlements.Buildings; + +namespace GameInterface.Services.Buildings +{ + internal class BuildingSync : IAutoSync + { + public BuildingSync(IAutoSyncBuilder autoSyncBuilder) + { + autoSyncBuilder.AddField(AccessTools.Field(typeof(Building), nameof(Building._hitpoints))); + + autoSyncBuilder.AddField(AccessTools.Field(typeof(Building), nameof(Building._currentLevel))); + + autoSyncBuilder.AddField(AccessTools.Field(typeof(Building), nameof(Building.IsCurrentlyDefault))); + autoSyncBuilder.AddFieldChangeMethod(AccessTools.Method(typeof(BuildingHelper), nameof(BuildingHelper.ChangeDefaultBuilding))); + autoSyncBuilder.AddFieldChangeMethod(AccessTools.Method(typeof(BuildingsCampaignBehavior), nameof(BuildingsCampaignBehavior.BuildDevelopmentsAtGameStart))); + + autoSyncBuilder.AddField(AccessTools.Field(typeof(Building), nameof(Building.BuildingProgress))); + autoSyncBuilder.AddFieldChangeMethod(AccessTools.Method(typeof(Town), nameof(Town.TickCurrentBuilding))); + + autoSyncBuilder.AddProperty(AccessTools.Property(typeof(Building), nameof(Building.Town))); + } + } +}