From 0ae54973c53d93ec72f4ca9144ff0aec877f6aee Mon Sep 17 00:00:00 2001 From: Sakura_TA Date: Wed, 26 Feb 2025 15:26:20 +0800 Subject: [PATCH 1/3] Fix VFE issue syncing Command_SetItemsToSpawn Should fix VFE-Faction Machanoid_ FishTrapper being able to set products The issue is that Command_SetItemsToSpawn is actualing using a ThingComp as param instead of Thing, witch can't be synced as Thing. Added function for command with comp_building. --- Source/Mods/VanillaExpandedFramework.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/Mods/VanillaExpandedFramework.cs b/Source/Mods/VanillaExpandedFramework.cs index b147edc..c354f14 100644 --- a/Source/Mods/VanillaExpandedFramework.cs +++ b/Source/Mods/VanillaExpandedFramework.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Net.NetworkInformation; using System.Reflection; using System.Reflection.Emit; using System.Runtime.Serialization; @@ -93,6 +94,16 @@ private static void SyncCommandWithBuilding(SyncWorker sync, ref Command command else building.SetValue(sync.Read()); } + private static void SyncCommandWithCompBuilding(SyncWorker sync, ref Command command) + { + var traverse = Traverse.Create(command); + var building = traverse.Field("building"); + + if (sync.isWriting) + sync.Write(building.GetValue() as ThingComp); + else + building.SetValue(sync.Read()); + } #endregion @@ -634,7 +645,7 @@ private static void PatchVanillaFurnitureExpanded() var type = AccessTools.TypeByName("VanillaFurnitureExpanded.Command_SetItemsToSpawn"); MpCompat.RegisterLambdaDelegate(type, "ProcessInput", 1); - MP.RegisterSyncWorker(SyncCommandWithBuilding, type, shouldConstruct: true); + MP.RegisterSyncWorker(SyncCommandWithCompBuilding, type, shouldConstruct: true); MpCompat.RegisterLambdaMethod("VanillaFurnitureExpanded.CompRockSpawner", "CompGetGizmosExtra", 0); From b0e7c64adc21b36b77e7d7973f5e53b36510c5c8 Mon Sep 17 00:00:00 2001 From: Sakura_TA Date: Fri, 28 Feb 2025 15:51:08 +0800 Subject: [PATCH 2/3] Replace VFE CommandSetStoneType to use shared sync worker --- Source/Mods/VanillaExpandedFramework.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Source/Mods/VanillaExpandedFramework.cs b/Source/Mods/VanillaExpandedFramework.cs index c354f14..d5481e9 100644 --- a/Source/Mods/VanillaExpandedFramework.cs +++ b/Source/Mods/VanillaExpandedFramework.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Net.NetworkInformation; using System.Reflection; using System.Reflection.Emit; using System.Runtime.Serialization; @@ -630,7 +629,6 @@ private static void SyncHireable(SyncWorker sync, ref object obj) #region Vanilla Furniture Expanded // Vanilla Furniture Expanded - private static AccessTools.FieldRef setStoneBuildingField; private static Type randomBuildingGraphicCompType; private static FastInvokeHandler randomBuildingGraphicCompChangeGraphicMethod; @@ -650,9 +648,7 @@ private static void PatchVanillaFurnitureExpanded() MpCompat.RegisterLambdaMethod("VanillaFurnitureExpanded.CompRockSpawner", "CompGetGizmosExtra", 0); type = AccessTools.TypeByName("VanillaFurnitureExpanded.Command_SetStoneType"); - setStoneBuildingField = AccessTools.FieldRefAccess(type, "building"); - MpCompat.RegisterLambdaMethod(type, "ProcessInput", 0); - MP.RegisterSyncWorker(SyncSetStoneTypeCommand, type, shouldConstruct: true); + MP.RegisterSyncWorker(SyncCommandWithCompBuilding, type, shouldConstruct: true); MpCompat.RegisterLambdaDelegate(type, "ProcessInput", 1); type = randomBuildingGraphicCompType = AccessTools.TypeByName("VanillaFurnitureExpanded.CompRandomBuildingGraphic"); @@ -690,14 +686,6 @@ private static void PatchVanillaFurnitureExpanded() MP.RegisterSyncWorker(SyncCompGlower); } - private static void SyncSetStoneTypeCommand(SyncWorker sync, ref Command obj) - { - if (sync.isWriting) - sync.Write(setStoneBuildingField(obj)); - else - setStoneBuildingField(obj) = sync.Read(); - } - private static bool Dialog_ChooseGraphic_ReplacementButton(Rect butRect, bool doMouseoverSound, Thing thingToChange, int index, Window window) { var result = Widgets.ButtonInvisible(butRect, doMouseoverSound); From 27ef55d2277c54adb867540497449fe9a247d5d0 Mon Sep 17 00:00:00 2001 From: SokyranTheDragon Date: Tue, 13 May 2025 23:46:02 +0200 Subject: [PATCH 3/3] Update Vanilla Expanded Framework - Sync customizable graphics - Used by Vanilla Furniture Expanded 2.0 update, modular couch - Sync gizmo to attack a moving base - Used by Vanilla Factions Expanded - Medieval 2, - Temporary fix for missing moving base gizmo graphic - This doesn't fix the error, which needs MP update - only the missing graphic - All the logs in this specific compat had "MPCompat :: " - Some already had "MultiplayerCompat ::", but I went with "MP Compat ::" for consistency with a few other classes that did it --- Source/Mods/VanillaExpandedFramework.cs | 76 ++++++++++++++++++------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/Source/Mods/VanillaExpandedFramework.cs b/Source/Mods/VanillaExpandedFramework.cs index ad7d3e9..f4d03e6 100644 --- a/Source/Mods/VanillaExpandedFramework.cs +++ b/Source/Mods/VanillaExpandedFramework.cs @@ -56,6 +56,7 @@ public VanillaExpandedFramework(ModContentPack mod) (PatchDraftedAi, "Drafted AI", true), (PatchMapObjectGeneration, "Thing spawning on map generation (ObjectSpawnsDef)", false), (PatchQuestChainsDevMode, "Quest chain dev mode window", false), + (PatchMovingBases, "Moving bases", true), ]; foreach (var (patchMethod, componentName, latePatch) in patches) @@ -70,14 +71,14 @@ void ApplyPatch() try { #if DEBUG - Log.Message($"Patching Vanilla Expanded Framework - {componentName}"); + Log.Message($"MPCompat :: Patching Vanilla Expanded Framework - {componentName}"); #endif patchMethod(); } catch (Exception e) { - Log.Error($"Encountered an error patching {componentName} part of Vanilla Expanded Framework - this part of the mod may not work properly!"); + Log.Error($"MPCompat :: Encountered an error patching {componentName} part of Vanilla Expanded Framework - this part of the mod may not work properly!"); Log.Error(e.ToString()); } } @@ -611,7 +612,7 @@ private static void SyncProcess(SyncWorker sync, ref object process) if (process == null) { sync.Write(null); - Log.Error("Trying to sync a null process."); + Log.Error("MPCompat :: Trying to sync a null process."); return; } @@ -621,7 +622,7 @@ private static void SyncProcess(SyncWorker sync, ref object process) // Probably not needed unless some weird bugs going on if (parent == null) { - Log.Error($"Trying to sync a process with no parent. Process={process}"); + Log.Error($"MPCompat :: Trying to sync a process with no parent. Process={process}"); return; } @@ -636,7 +637,7 @@ private static void SyncProcess(SyncWorker sync, ref object process) var stack = advancedResourceProcessorProcessStackField(comp); if (stack == null) { - Log.Error($"The process has no stack. Comp={comp}"); + Log.Error($"MPCompat :: The process has no stack. Comp={comp}"); return; } @@ -644,14 +645,14 @@ private static void SyncProcess(SyncWorker sync, ref object process) // Shouldn't happen if (list == null) { - Log.Error($"The process has ProcessStack with no process list. Comp={comp}"); + Log.Error($"MPCompat :: The process has ProcessStack with no process list. Comp={comp}"); return; } var id = sync.Read(); process = GetProcessById(list, id); if (process == null) - Log.Error($"Could not find the correct process. Comp={comp}, id={id}"); + Log.Error($"MPCompat :: Could not find the correct process. Comp={comp}, id={id}"); } } @@ -828,12 +829,12 @@ private static void SyncVEFAbility(SyncWorker sync, ref ITargetingSource source) } else { - Log.Error("MultiplayerCompat :: SyncVEFAbility : Holder is missing or of unsupported type"); + Log.Error("MPCompat :: SyncVEFAbility : Holder is missing or of unsupported type"); } } else { - Log.Error("MultiplayerCompat :: SyncVEFAbility : Holder isn't a ThingWithComps"); + Log.Error("MPCompat :: SyncVEFAbility : Holder isn't a ThingWithComps"); } } } @@ -1051,6 +1052,16 @@ private static void PatchVanillaFurnitureExpanded() // Can't be synced with `isImplicit: true`, as it'll cause it to sync it with ThingComp // sync worker first before syncing it using this specific sync worker. MP.RegisterSyncWorker(SyncCompGlower); + + // Customizable graphic + type = AccessTools.TypeByName("VFECore.CompCustomizableGraphic"); + // Target static method with (List, int) arguments rather than + // instance type with (int?, bool, bool) types. Technically, the second one should also + // work, but all arguments (besides instance) would be repeated. On top of that, + // it would also sync the temporary changes to the graphic, which would cause us to sync + // unnecessarily frequently, rather than only when changing the graphic itself + // (and cause some issues on top of that). + MP.RegisterSyncMethod(type, "SelectGraphic", [typeof(List<>).MakeGenericType(type), typeof(int)]); } private static bool Dialog_ChooseGraphic_ReplacementButton(Rect butRect, bool doMouseoverSound, Thing thingToChange, int index, Window window) @@ -1186,7 +1197,7 @@ private static void PatchMVCF() // HashSet, using it as IEnumerable for a bit of extra safety in case it ever gets changed to a list or something. mvcfEnabledFeaturesSet = AccessTools.Field(type, "EnabledFeatures").GetValue(null) as IEnumerable; if (mvcfEnabledFeaturesSet == null) - Log.Warning("Cannot access the list of enabled MVCF features, this may cause issues"); + Log.Warning("MPCompat :: Cannot access the list of enabled MVCF features, this may cause issues"); type = AccessTools.TypeByName("MVCF.Utilities.PawnVerbUtility"); mvcfPawnVerbUtilityGetManager = AccessTools.MethodDelegate(AccessTools.Method(type, "Manager")); @@ -1398,7 +1409,7 @@ private static IEnumerable ReplaceTickWithConditionalTick(IEnum if (replacedCount != expected) { var name = (baseMethod.DeclaringType?.Namespace).NullOrEmpty() ? baseMethod.Name : $"{baseMethod.DeclaringType!.Name}:{baseMethod.Name}"; - Log.Warning($"Patched incorrect number of VerbManager.Tick calls (patched {replacedCount}, expected {expected}) for method {name}"); + Log.Warning($"MPCompat :: Patched incorrect number of VerbManager.Tick calls (patched {replacedCount}, expected {expected}) for method {name}"); } } @@ -1469,7 +1480,7 @@ private static void SyncPipeNet(SyncWorker sync, ref object pipeNet) var map = pipeNetMapField(pipeNet); if (map == null) { - Log.Error($"Trying to sync a PipeNet with a null map. PipeNet={pipeNet}"); + Log.Error($"MPCompat :: Trying to sync a PipeNet with a null map. PipeNet={pipeNet}"); sync.Write(-1); return; } @@ -1478,7 +1489,7 @@ private static void SyncPipeNet(SyncWorker sync, ref object pipeNet) var manager = map.GetComponent(pipeNetManagerType); if (manager == null) { - Log.Error($"Trying to sync a PipeNet with a map that doesn't have PipeNetManager. PipeNet={pipeNet}, Map={map}"); + Log.Error($"MPCompat :: Trying to sync a PipeNet with a map that doesn't have PipeNetManager. PipeNet={pipeNet}, Map={map}"); sync.Write(-1); return; } @@ -1504,7 +1515,7 @@ private static void SyncPipeNet(SyncWorker sync, ref object pipeNet) if (!found) { // We did not find the pipe net - log error and treat as null - Log.Error($"Trying to sync a PipeNet, but it's not held by the manager. PipeNet={pipeNet}, map={map}, manager={manager}"); + Log.Error($"MPCompat :: Trying to sync a PipeNet, but it's not held by the manager. PipeNet={pipeNet}, map={map}, manager={manager}"); sync.Write(-1); } else @@ -1670,7 +1681,7 @@ private static void SaferGetHashCode(ref object obj) return; } - Log.ErrorOnce($"Potentially unsupported type for HashCodeToMod call in Multiplayer, desyncs likely to happen. Object type: {obj.GetType()}", obj.GetHashCode()); + Log.ErrorOnce($"MPCompat :: Potentially unsupported type for HashCodeToMod call in Multiplayer, desyncs likely to happen. Object type: {obj.GetType()}", obj.GetHashCode()); } #endregion @@ -1694,7 +1705,7 @@ private static void PatchWeatherOverlayEffects() weatherOverlayEffectsNextDamageTickField = AccessTools.FieldRefAccess(nextDamageTickField); else { - Log.Error("VFECore.WeatherOverlay_Effects:nextDamageTick field, patch failed."); + Log.Error("MPCompat :: VFECore.WeatherOverlay_Effects:nextDamageTick field, patch failed."); return; } @@ -1744,7 +1755,7 @@ private static void PatchVanillaCookingExpanded() if (DefDatabase.AllDefsListForReading.Any(def => thoughtHediffType.IsAssignableFrom(def.thoughtClass))) PatchingUtilities.PatchTryGainMemory(TryGainThoughtHediff); } - else Log.Error("Trying to patch `VanillaCookingExpanded.Thought_Hediff`, but the type is null. Did it get moved, renamed, or removed?"); + else Log.Error("MPCompat :: Trying to patch `VanillaCookingExpanded.Thought_Hediff`, but the type is null. Did it get moved, renamed, or removed?"); } private static bool TryGainThoughtHediff(Thought_Memory thought) @@ -2241,7 +2252,7 @@ private static void SyncedQuestChainDevForceEnd(Quest quest, QuestEndOutcome out // Error log for person syncing the command. if (MP.IsExecutingSyncCommandIssuedBySelf) - Log.Error($"Trying to find a quest chain's QuestInfo for quest with ID {quest.id} and name {quest.name}, but there was no matching QuestInfo. This should not happen."); + Log.Error($"MPCompat :: Trying to find a quest chain's QuestInfo for quest with ID {quest.id} and name {quest.name}, but there was no matching QuestInfo. This should not happen."); } private static void SyncedQuestChainDevFireNow(QuestScriptDef def, int index) @@ -2250,7 +2261,7 @@ private static void SyncedQuestChainDevFireNow(QuestScriptDef def, int index) if (index < 0) { if (MP.IsExecutingSyncCommandIssuedBySelf) - Log.Error($"Failed syncing FutureQuestInfo, index too small: index={index}, def={def}"); + Log.Error($"MPCompat :: Failed syncing FutureQuestInfo, index too small: index={index}, def={def}"); return; } @@ -2259,7 +2270,7 @@ private static void SyncedQuestChainDevFireNow(QuestScriptDef def, int index) if (index >= list.Count) { if (MP.IsExecutingSyncCommandIssuedBySelf) - Log.Error($"Failed syncing FutureQuestInfo, index too high: index={index}, count={list.Count}, def={def}"); + Log.Error($"MPCompat :: Failed syncing FutureQuestInfo, index too high: index={index}, count={list.Count}, def={def}"); return; } @@ -2271,7 +2282,7 @@ private static void SyncedQuestChainDevFireNow(QuestScriptDef def, int index) if (localDef != def) { if (MP.IsExecutingSyncCommandIssuedBySelf) - Log.Error($"Failed syncing FutureQuestInfo, QuestScriptDef mismatch: index={index}, count={list.Count}, syncedDef={def}, localDef={localDef}"); + Log.Error($"MPCompat :: Failed syncing FutureQuestInfo, QuestScriptDef mismatch: index={index}, count={list.Count}, syncedDef={def}, localDef={localDef}"); return; } @@ -2284,5 +2295,28 @@ private static void SyncedQuestChainDevFireNow(QuestScriptDef def, int index) } #endregion + + #region Moving Bases + + private static void PatchMovingBases() + { + var type = AccessTools.TypeByName("VFECore.MovingBase"); + MP.RegisterSyncMethod(type, "Attack"); + + // Temp gizmo graphic fix. + var attackGraphicField = AccessTools.DeclaredField(type, "AttackCommand"); + if (attackGraphicField == null) + Log.Warning("MPCompat :: VFECore.MovingBase:AttackCommand does not exist, temporary patch needs to be updated or removed"); + else if (!attackGraphicField.IsStatic) + Log.Warning("MPCompat :: VFECore.MovingBase:AttackCommand is not static, temporary patch needs to be updated or removed"); + else if (attackGraphicField.FieldType != typeof(Texture2D)) + Log.Warning("MPCompat :: VFECore.MovingBase:AttackCommand is not Texture2D, temporary patch needs to be updated or removed"); + else if (attackGraphicField.GetValue(null) != null) + Log.Message("MPCompat :: VFECore.MovingBase:AttackCommand is not null, temporary patch is no longer needed - if the graphic issue fix is included in MP itself"); + else + attackGraphicField.SetValue(null, ContentFinder.Get("UI/Commands/AttackSettlement", true)); + } + + #endregion } } \ No newline at end of file