├── .github ├── FUNDING.yml └── workflows │ └── publish-release.yml ├── About ├── PublishedFileId.txt ├── Preview.png └── About.xml ├── References ├── Outposts.txt ├── VFEC.txt ├── VFED.txt ├── Vehicles.txt ├── AbilityUser.txt ├── CashRegister.txt ├── Gastronomy.txt ├── Inventory.txt ├── SmashTools.txt ├── Storefront.txt ├── TorannMagic.txt ├── VFEEmpire.txt ├── ZombieLand.txt ├── kentington_saveourship2_ShipsHaveInsides.txt.off ├── VFEC.dll ├── VFED.dll ├── Outposts.dll ├── Vehicles.dll ├── AbilityUser.dll ├── Gastronomy.dll ├── Inventory.dll ├── SmashTools.dll ├── Storefront.dll ├── TorannMagic.dll ├── VFEEmpire.dll ├── ZombieLand.dll ├── CashRegister.dll └── kentington_saveourship2_ShipsHaveInsides.dll ├── Languages ├── English │ └── Keyed │ │ ├── Shared.xml │ │ ├── VanillaFactionsInsectoid.xml │ │ └── VehicleFramework.xml └── Polish │ └── Keyed │ ├── Shared.xml │ └── VehicleFramework.xml ├── release_bundler.sh ├── Source ├── Mods │ ├── DubsPaintShop.cs │ ├── DubsMintMinimap.cs │ ├── MiscTraining.cs │ ├── AutoharvesterAutocycle.cs │ ├── RimEffectDrell.cs │ ├── MedicinesPlus.cs │ ├── HospitalityVendingMachines.cs │ ├── HugsLib.cs │ ├── EnhancedVatLearning.cs │ ├── HarvestOrgansPostMortem.cs │ ├── DeepStorage.cs │ ├── VanillaPlantsAutoPlowPatch.cs │ ├── AlphaMechs.cs │ ├── VanillaBrewingExpanded.cs │ ├── DeepStorageGUI.cs │ ├── CleanPathfinding2.cs │ ├── BetterGroundPenetratingScanner.cs │ ├── AlmostThere.cs │ ├── VanillaRacesSaurid.cs │ ├── VanillaPlantsMushrooms.cs │ ├── SparklingWorlds.cs │ ├── GrazingLands.cs │ ├── RimEffectExtendedCut.cs │ ├── PrisonCommons.cs │ ├── ToggleableShields.cs │ ├── VanillaFactionsMedieval.cs │ ├── VanillaRacesHussar.cs │ ├── Reunion.cs │ ├── PocketSand.cs │ ├── VanillaAnimalsEndangered.cs │ ├── Windows.cs │ ├── RunandGun.cs │ ├── VanillaRacesExpandedLycanthrope.cs │ ├── VanillaAnimalsWaste.cs │ ├── AnimaObelisk.cs │ ├── VPESkipdoorPathing.cs │ ├── CustomPrisonerInteractions.cs │ ├── VanillaHelixienGas.cs │ ├── SmallVehicleAddons.cs │ ├── VanillaFurnitureExpandedPower.cs │ ├── ClutterStructures.cs │ ├── StuffedFloors.cs │ ├── PerspectiveBuildings.cs │ ├── SimplyMoreBridges.cs │ ├── VanillaFactionsVikings.cs │ ├── PowerSwitch.cs │ ├── GeneRipper.cs │ ├── VanillaOutpostsExpanded.cs │ ├── VanillaPlantsMorePlants.cs │ ├── MechFramework.cs │ ├── BiomesCaverns.cs │ ├── VanillaNutrientPasteExpanded.cs │ ├── VanillaFurnitureExpandedFarming.cs │ ├── VanillaTemperatureExpanded.cs │ ├── Tacticowl.cs │ ├── AnimalLogic.cs │ ├── FertileFields.cs │ ├── Desynchronized.cs │ ├── VanillaIdeologyDryads.cs │ ├── VanillaRacesPhytokin.cs │ ├── VanillaSocialInteractions.cs │ ├── CleaningArea.cs │ ├── OneBedToSleepWithAll.cs │ ├── VanillaFactionsSettlers.cs │ ├── LightsOut.cs │ ├── PerspectiveOres.cs │ ├── CutPlantsBeforeBuilding.cs │ ├── HuntForMe.cs │ ├── VanillaAnimalsCaves.cs │ ├── RimFridge.cs │ ├── MinifyEverything.cs │ ├── ChoiceOfPsycasts.cs │ ├── BiotechMechStuffElongatedMechanoidUpgrades.cs │ ├── VanillaPsycastsExpandedPuppeteer.cs │ ├── LinkedPlanters.cs │ ├── VanillaFurnitureExpanded.cs │ ├── VanillaRecyclingExpanded.cs │ ├── BiomesPollutedLands.cs │ ├── AlphaBiomes.cs │ ├── PickupAndHaul.cs │ ├── VanillaSkillsExpanded.cs │ ├── VanillaPsycastsExpandedHemosage.cs │ ├── VanillaVehiclesExpanded.cs │ ├── DubsCentralHeating.cs │ ├── DubsMintMenu.cs │ ├── Immortals.cs │ ├── AutoExtractGenes.cs │ ├── QualityBuilder.cs │ ├── RimEffectCore.cs │ ├── VanillaRacesSanguophage.cs │ ├── EccentricTechFusionPower.cs │ ├── AutomaticParking.cs │ ├── HospitalityCasino.cs │ ├── ReinforcedMechanoids2.cs │ ├── VanillaEventsExpanded.cs │ ├── AncientMiningIndustry.cs │ ├── ExtendedBioengineeringForVFEInsectoids.cs │ ├── BiomesCore.cs │ ├── AvoidFriendlyFire.cs │ ├── VanillaTraitsExpanded.cs │ ├── RPGStyleInventory.cs │ ├── TimeOfDAySwitch.cs │ ├── MiscRobots.cs │ ├── VanillaFishingExpanded.cs │ ├── FollowMe.cs │ ├── BiotechExpansionMythic.cs │ ├── VanillaRacesFungoid.cs │ ├── BadHygiene.cs │ ├── NonUnoPinata.cs │ ├── VanillaFurnitureExpandedSecurity.cs │ ├── FortificationsIndustrial.cs │ ├── SmartFarming.cs │ ├── ReGrowthCore.cs │ ├── ExosuitFramework.cs │ ├── RimsentialSpaceports.cs │ ├── VanillaFactionsMechanoid.cs │ ├── SignsAndComments.cs │ ├── HealerMechSerumChoice.cs │ ├── Pharmacist.cs │ ├── MedicalTab.cs │ └── AlphaAnimals.cs ├── Properties │ └── AssemblyInfo.cs ├── Multiplayer_Compat.csproj ├── CodeFinder.cs └── Extensions.cs ├── LICENSE ├── Multiplayer_Compat.sln └── Source_Referenced ├── Multiplayer_Compat_Referenced.csproj └── HemogenExtractor.cs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: sokyran 2 | -------------------------------------------------------------------------------- /About/PublishedFileId.txt: -------------------------------------------------------------------------------- 1 | 1629973374 2 | -------------------------------------------------------------------------------- /References/Outposts.txt: -------------------------------------------------------------------------------- 1 | D62DCA10D455F19FF2520A038DCDC11A0585C7F8 -------------------------------------------------------------------------------- /References/VFEC.txt: -------------------------------------------------------------------------------- 1 | 1D3B8B299E2720EF1A58E0DC172CDBF46A90F78D -------------------------------------------------------------------------------- /References/VFED.txt: -------------------------------------------------------------------------------- 1 | D7209F463476A902E87925CDEC1A359D3681E1D1 -------------------------------------------------------------------------------- /References/Vehicles.txt: -------------------------------------------------------------------------------- 1 | C247E42DE0895065905D94E69604E1A5B42EBD04 -------------------------------------------------------------------------------- /References/AbilityUser.txt: -------------------------------------------------------------------------------- 1 | 018EB6AABB5223A1F76F03A0DD8E9F31E9FC7621 -------------------------------------------------------------------------------- /References/CashRegister.txt: -------------------------------------------------------------------------------- 1 | 51DBDB6DF2F049BA65531EA4E7EE3F89294733E0 -------------------------------------------------------------------------------- /References/Gastronomy.txt: -------------------------------------------------------------------------------- 1 | 4F26F754ABFCD66B172D5F9DB1F0A1BDE0D205FF -------------------------------------------------------------------------------- /References/Inventory.txt: -------------------------------------------------------------------------------- 1 | 2CAF9AB8B7DE9F0347A53E1F688395533DA5B163 -------------------------------------------------------------------------------- /References/SmashTools.txt: -------------------------------------------------------------------------------- 1 | 8CA63772CD5B47E61E32EA1CE55E91F7183ABB0E -------------------------------------------------------------------------------- /References/Storefront.txt: -------------------------------------------------------------------------------- 1 | 69142435ECCD6BFCB83714C642F80A55E0776791 -------------------------------------------------------------------------------- /References/TorannMagic.txt: -------------------------------------------------------------------------------- 1 | 25A6D01C84C64F02846D29BC4454F9B294B5E228 -------------------------------------------------------------------------------- /References/VFEEmpire.txt: -------------------------------------------------------------------------------- 1 | DBF938E6018DEFD18A5447945112687C556D3055 -------------------------------------------------------------------------------- /References/ZombieLand.txt: -------------------------------------------------------------------------------- 1 | 31E313C3571E960E6C803C6BFE0AA2ADB3E6E9CB -------------------------------------------------------------------------------- /About/Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/About/Preview.png -------------------------------------------------------------------------------- /References/kentington_saveourship2_ShipsHaveInsides.txt.off: -------------------------------------------------------------------------------- 1 | E6688F4A560D3EC53D39755DCA2A6E92E0FE970D -------------------------------------------------------------------------------- /References/VFEC.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/VFEC.dll -------------------------------------------------------------------------------- /References/VFED.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/VFED.dll -------------------------------------------------------------------------------- /References/Outposts.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/Outposts.dll -------------------------------------------------------------------------------- /References/Vehicles.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/Vehicles.dll -------------------------------------------------------------------------------- /References/AbilityUser.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/AbilityUser.dll -------------------------------------------------------------------------------- /References/Gastronomy.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/Gastronomy.dll -------------------------------------------------------------------------------- /References/Inventory.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/Inventory.dll -------------------------------------------------------------------------------- /References/SmashTools.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/SmashTools.dll -------------------------------------------------------------------------------- /References/Storefront.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/Storefront.dll -------------------------------------------------------------------------------- /References/TorannMagic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/TorannMagic.dll -------------------------------------------------------------------------------- /References/VFEEmpire.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/VFEEmpire.dll -------------------------------------------------------------------------------- /References/ZombieLand.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/ZombieLand.dll -------------------------------------------------------------------------------- /References/CashRegister.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/CashRegister.dll -------------------------------------------------------------------------------- /References/kentington_saveourship2_ShipsHaveInsides.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwmt/Multiplayer-Compatibility/HEAD/References/kentington_saveourship2_ShipsHaveInsides.dll -------------------------------------------------------------------------------- /Languages/English/Keyed/Shared.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Switch to map 6 | 7 | -------------------------------------------------------------------------------- /Languages/English/Keyed/VanillaFactionsInsectoid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Teleporter loading session 5 | 6 | -------------------------------------------------------------------------------- /Languages/Polish/Keyed/Shared.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Przełącz na mapę 6 | 7 | 8 | -------------------------------------------------------------------------------- /release_bundler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | dotnet build -c Release 6 | 7 | rm -rf Multiplayer-Compatibility/ 8 | mkdir -p Multiplayer-Compatibility 9 | 10 | cp -r About Assemblies Referenced Languages Multiplayer-Compatibility 11 | 12 | # Zip for Github releases 13 | rm -f Multiplayer-Compatibility.zip 14 | zip -r -q Multiplayer-Compatibility.zip Multiplayer-Compatibility 15 | 16 | echo "Ok, $PWD/Multiplayer-Compatibility.zip ready" 17 | -------------------------------------------------------------------------------- /Source/Mods/DubsPaintShop.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Dubs Paint Shop by Dubwise 6 | /// 7 | [MpCompatFor("dubwise.dubspaintshop")] 8 | internal class DubsPaintShop 9 | { 10 | public DubsPaintShop(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(() => DubsMintMenu.Patch("DubRoss.MP_Util")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Mods/DubsMintMinimap.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Dubs Mint Minimap by Dubwise 6 | /// 7 | [MpCompatFor("dubwise.dubsmintminimap")] 8 | internal class DubsMintMinimap 9 | { 10 | public DubsMintMinimap(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(() => DubsMintMenu.Patch("DubsMintMinimap.MP_Util")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Mods/MiscTraining.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Misc. Training by HaploX1 6 | /// 7 | [MpCompatFor("Haplo.Miscellaneous.Training")] 8 | class MiscTraining 9 | { 10 | public MiscTraining(ModContentPack mod) 11 | { 12 | PatchingUtilities.PatchPushPopRand("TrainingFacility.JobDriver_Archery:ShootArrow"); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Source/Mods/AutoharvesterAutocycle.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Autoharvester Auto-cycle by FluffyKittens 6 | /// 7 | [MpCompatFor("FluffyKittens.Autoharvester")] 8 | internal class AutoharvesterAutocycle 9 | { 10 | public AutoharvesterAutocycle(ModContentPack mod) 11 | => MpCompat.RegisterLambdaMethod("AutoHarvesterCycle.CycleComp", "CompGetGizmosExtra", 0, 1, 2, 3); 12 | } 13 | } -------------------------------------------------------------------------------- /Source/Mods/RimEffectDrell.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Rim-Effect: Drell by Vanilla Expanded Team and Co. 6 | /// 7 | /// 8 | [MpCompatFor("RimEffect.Drell")] 9 | public class RimEffectDrell 10 | { 11 | public RimEffectDrell(ModContentPack mod) 12 | => MpCompat.RegisterLambdaMethod("REDrell.Comp_Drell", "CompGetGizmosExtra", 0, 1); 13 | } 14 | } -------------------------------------------------------------------------------- /Languages/English/Keyed/VehicleFramework.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vehicle caravan forming session 5 | Vehicle cargo loading session 6 | Aerial vehicle landing session 7 | 8 | 9 | Vehicles waiting to land 10 | 11 | -------------------------------------------------------------------------------- /Source/Mods/MedicinesPlus.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Medicines+ by Atlas, TedDraws 6 | /// 7 | /// 8 | [MpCompatFor("TedDraws.MedicinesPlus.AT")] 9 | internal class MedicinesPlus 10 | { 11 | public MedicinesPlus(ModContentPack mod) 12 | => PatchingUtilities.PatchSystemRand("AT_MedicinesPlus.Hediff_HypnotolAddiction:PostRemoved", false); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/Mods/HospitalityVendingMachines.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// 6 | /// Hospitality: Vending machines by Adamas 7 | /// 8 | /// 9 | /// 10 | [MpCompatFor("Adamas.VendingMachines")] 11 | public class HospitalityVendingMachines 12 | { 13 | public HospitalityVendingMachines(ModContentPack mod) 14 | => HospitalityCasino.InitializeGizmos("VendingMachines"); 15 | } 16 | } -------------------------------------------------------------------------------- /Source/Mods/HugsLib.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat; 4 | 5 | /// HugsLib by UnlimitedHugs 6 | /// 7 | /// 8 | [MpCompatFor("UnlimitedHugs.HugsLib")] 9 | public class HugsLib 10 | { 11 | public HugsLib(ModContentPack mod) 12 | { 13 | // Stop the long event from running when it could break stuff. 14 | PatchingUtilities.CancelCallIfLongEventsAreUnsafe("HugsLib.HugsLibController:OnMapInitFinalized"); 15 | } 16 | } -------------------------------------------------------------------------------- /Source/Mods/EnhancedVatLearning.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Enhanced Vat Learning by SmArtKar, Elseud 6 | /// 7 | /// 8 | [MpCompatFor("smartkar.enhancedvatlearning")] 9 | public class EnhancedVatLearning 10 | { 11 | public EnhancedVatLearning(ModContentPack mod) 12 | => PatchingUtilities.PatchSystemRandCtor("EnhancedVatLearning.HediffComp_EnhancedLearning", false); 13 | } 14 | } -------------------------------------------------------------------------------- /Languages/Polish/Keyed/VehicleFramework.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sesja formowania karawany pojazdów 5 | Sesja ładowania ładunku pojazdu 6 | Sesja lądowania pojazdu powietrznego 7 | 8 | 9 | Pojazdy oczekujące na lądowanie 10 | 11 | 12 | -------------------------------------------------------------------------------- /Source/Mods/HarvestOrgansPostMortem.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// 6 | /// Harvest Organs Post Mortem by Smuffle 7 | /// 8 | /// 9 | /// 10 | [MpCompatFor("Smuffle.HarvestOrgansPostMortem")] 11 | public class HarvestOrgansPostMortem 12 | { 13 | public HarvestOrgansPostMortem(ModContentPack mod) 14 | => PatchingUtilities.PatchSystemRand("Autopsy.NewMedicalRecipesUtility:TraverseBody"); 15 | } 16 | } -------------------------------------------------------------------------------- /Source/Mods/DeepStorage.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Deep Storage by LWM 8 | /// 9 | /// 10 | [MpCompatFor("LWM.DeepStorage")] 11 | class DeepStorage 12 | { 13 | public DeepStorage(ModContentPack mod) 14 | => MP.RegisterSyncMethod(AccessTools.TypeByName("LWM.DeepStorage.ITab_DeepStorage_Inventory"), "EjectTarget").CancelIfAnyArgNull(); 15 | } 16 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaPlantsAutoPlowPatch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Vanilla Plants Expanded - Auto Plow Patch by Archoran 8 | /// 9 | [MpCompatFor("Archoran.Utils.VPEAutoPlow")] 10 | class VanillaPlantsAutoPlowPatch 11 | { 12 | public VanillaPlantsAutoPlowPatch(ModContentPack mod) 13 | => MP.RegisterSyncDelegateLambda(AccessTools.TypeByName("VPEAutoPlow.Patch_Zone_Growing_GetGizmos"), "Add_AllowAutoPlow_Gizmo", 1); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/Mods/AlphaMechs.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Alpha Mechs by Sarg Bjornson 8 | /// 9 | /// 10 | [MpCompatFor("sarg.alphamechs")] 11 | public class AlphaMechs 12 | { 13 | public AlphaMechs(ModContentPack mod) 14 | { 15 | // Gizmos 16 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("AlphaMechs.Pawn_HemogenVat:EjectContents")); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaBrewingExpanded.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Brewing Expanded by Oskar Potocki, Sarg Bjornson, Chowder 6 | /// 7 | /// 8 | [MpCompatFor("VanillaExpanded.VBrewE")] 9 | class VanillaBrewingExpanded 10 | { 11 | public VanillaBrewingExpanded(ModContentPack mod) 12 | { 13 | PatchingUtilities.PatchSystemRandCtor("VanillaBrewingExpanded.Hediff_ConsumedCocktail"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/Mods/DeepStorageGUI.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Netrve's DeepStorage GUI by Netrve 8 | /// 9 | /// 10 | [MpCompatFor("netrve.dsgui")] 11 | [MpCompatFor("Mlie.NetrvesDeepStorageGUI")] 12 | class DeepStorageGUI 13 | { 14 | public DeepStorageGUI(ModContentPack mod) 15 | => MP.RegisterSyncMethod(AccessTools.TypeByName("DSGUI.DSGUI_TabItem"), "EjectTarget").CancelIfAnyArgNull(); 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Mods/CleanPathfinding2.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Clean Pathfinding 2 by Owlchemist 8 | /// 9 | /// 10 | [MpCompatFor("Owlchemist.CleanPathfinding")] 11 | public class CleanPathfinding2 12 | { 13 | public CleanPathfinding2(ModContentPack mod) 14 | // Switch door type gizmo 15 | => MP.RegisterSyncMethod(AccessTools.DeclaredMethod("CleanPathfinding.MapComponent_DoorPathing:SwitchDoorType")); 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Mods/BetterGroundPenetratingScanner.cs: -------------------------------------------------------------------------------- 1 | using Multiplayer.API; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Better ground-penetrating scanner by Kikohi 7 | /// 8 | [MpCompatFor("kikohi.BetterGroundPenetratingScanner")] 9 | public class BetterGroundPenetratingScanner 10 | { 11 | public BetterGroundPenetratingScanner(ModContentPack mod) 12 | { 13 | // Select random outcome (2) or specific one (3) 14 | MpCompat.RegisterLambdaDelegate("BGPScanner.CompBetterDeepScanner", "PostSpawnSetup", 2, 3).SetContext(SyncContext.MapSelected); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Mods/AlmostThere.cs: -------------------------------------------------------------------------------- 1 | using RimWorld.Planet; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Almost There! by Roolo 7 | /// 8 | /// 9 | [MpCompatFor("roolo.AlmostThere")] 10 | [MpCompatFor("Chad.Almostthere1.5")] 11 | public class AlmostThere 12 | { 13 | public AlmostThere(ModContentPack mod) 14 | { 15 | // Toggle: almost there (1), never rest (3), force rest (5) 16 | MpCompat.RegisterLambdaMethod("AlmostThere.AlmostThereWorldObjectComp", nameof(WorldObjectComp.GetGizmos), 1, 3, 5); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaRacesSaurid.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// 6 | /// Vanilla Races Expanded - Saurid by Oskar Potocki, Neronix17 7 | /// 8 | /// 9 | /// 10 | [MpCompatFor("vanillaracesexpanded.saurid")] 11 | public class VanillaRacesSaurid 12 | { 13 | public VanillaRacesSaurid(ModContentPack mod) 14 | { 15 | // Hatch now (0), regenerate child (1) 16 | MpCompat.RegisterLambdaMethod("VRESaurids.Comp_HumanHatcher", "CompGetGizmosExtra", 0, 1).SetDebugOnly(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaPlantsMushrooms.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Plants Expanded - Mushrooms by Taranchuk, Sarg Bjornson, Oskar Potocki 6 | /// 7 | /// 8 | [MpCompatFor("VanillaExpanded.VPlantsEMushrooms")] 9 | public class VanillaPlantsMushrooms 10 | { 11 | public VanillaPlantsMushrooms(ModContentPack mod) 12 | { 13 | // Toggle allow cut (1), allow sow (3) 14 | MpCompat.RegisterLambdaMethod("VanillaPlantsExpandedMushrooms.Zone_GrowingMushroom", "GetGizmos", 1, 3); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Mods/SparklingWorlds.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Sparkling Worlds by Albion 8 | /// 9 | /// 10 | [MpCompatFor("Albion.SparklingWorlds.Full")] 11 | [MpCompatFor("Albion.SparklingWorlds.Core")] 12 | public class SparklingWorlds 13 | { 14 | public SparklingWorlds(ModContentPack mod) 15 | { 16 | LongEventHandler.ExecuteWhenFinished(() => MP.RegisterSyncMethod(AccessTools.TypeByName("SparklingWorlds.CompOrbitalLaunchSW"), "TryLaunch")); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Mods/GrazingLands.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Grazing Lands by avilmask 7 | /// 8 | /// 9 | [MpCompatFor("avilmask.GrazingLands")] 10 | public class GrazingLands 11 | { 12 | public GrazingLands(ModContentPack mod) 13 | { 14 | var type = AccessTools.TypeByName("GrazingLands.PlantPropertiesPatch"); 15 | type = AccessTools.Inner(type, "Plant_IngestedCalculateAmounts_GrazingLandsPatch"); 16 | PatchingUtilities.PatchSystemRand(AccessTools.Method(type, "Prefix"), false); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Source/Mods/RimEffectExtendedCut.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Rim-Effect: Extended Cut by Vanilla Expanded Team and Co. 6 | /// 7 | /// 8 | [MpCompatFor("RimEffect.ExtendedCut")] 9 | public class RimEffectExtendedCut 10 | { 11 | public RimEffectExtendedCut(ModContentPack mod) 12 | { 13 | MpCompat.RegisterLambdaDelegate("RimEffectExtendedCut.Building_WarzoneTable", "GetBattleSetOptions", 0); 14 | MpCompat.RegisterLambdaMethod("RimEffectExtendedCut.CompPowerOutDoorLamp", "CompGetGizmosExtra", 0, 1).SetDebugOnly(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Mods/PrisonCommons.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Prison Commons by Ben Lubar 6 | /// 7 | /// 8 | [MpCompatFor("me.lubar.PrisonCommons")] 9 | [MpCompatFor("me.lubar.PrisonCommons.temp")] 10 | [MpCompatFor("Mlie.PrisonCommons")] 11 | internal class PrisonCommons 12 | { 13 | public PrisonCommons(ModContentPack mod) 14 | => LongEventHandler.ExecuteWhenFinished(LatePatch); 15 | 16 | private static void LatePatch() 17 | => MpCompat.RegisterLambdaMethod("RimWorld.CompPrisonCommons", "CompGetGizmosExtra", 1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Mods/ToggleableShields.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Toggleable Shields by Owlchemist 8 | /// 9 | /// 10 | [MpCompatFor("Owlchemist.ToggleableShields")] 11 | internal class ToggleableShields 12 | { 13 | public ToggleableShields(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(LatePatch); 14 | 15 | private static void LatePatch() 16 | => MP.RegisterSyncMethod(AccessTools.DeclaredMethod("ToggleableShields.Patch_Gizmo_EnergyShieldStatus_GizmoOnGUI:ToggleShields")); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Mods/VanillaFactionsMedieval.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Factions Expanded - Medieval by Oskar Potocki, XeoNovaDan 6 | /// 7 | /// 8 | [MpCompatFor("OskarPotocki.VanillaFactionsExpanded.MedievalModule")] 9 | public class VanillaFactionsMedieval 10 | { 11 | // Archery target uses RNG - but only when the player is looking at it. Which may not be the case in MP for all players. 12 | public VanillaFactionsMedieval(ModContentPack mod) 13 | => PatchingUtilities.PatchPushPopRand("VFEMedieval.JobDriver_PlayArchery:ThrowObjectAt"); 14 | } 15 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaRacesHussar.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Races Expanded - Hussar by Oskar Potocki, xrushha, Taranchuk, Sarg 6 | /// 7 | /// 8 | [MpCompatFor("vanillaracesexpanded.hussar")] 9 | public class VanillaRacesHussar 10 | { 11 | public VanillaRacesHussar(ModContentPack mod) 12 | { 13 | // Trigger queued mental break when undrafted, which will happen right after clicking but before it's synced. 14 | PatchingUtilities.PatchCancelInInterface("VREHussars.Pawn_DraftController_Drafted_Patch:Postfix"); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Mods/Reunion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Reunion by Kyrun 7 | /// 8 | /// 9 | [MpCompatFor("Kyrun.Reunion")] 10 | public class Reunion 11 | { 12 | public Reunion(ModContentPack mod) 13 | { 14 | var methods = new[] 15 | { 16 | "Kyrun.Reunion.GameComponent:GetRandomAllyForSpawning", 17 | "Kyrun.Reunion.GameComponent:TryScheduleNextEvent", 18 | "Kyrun.Reunion.GameComponent:DecideAndDoEvent", 19 | }; 20 | PatchingUtilities.PatchUnityRand(methods); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Source/Mods/PocketSand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Pocket Sand by Reisen 9 | /// 10 | [MpCompatFor("usagirei.pocketsand")] 11 | public class PocketSandCompat 12 | { 13 | public PocketSandCompat(ModContentPack mod) 14 | { 15 | LongEventHandler.ExecuteWhenFinished(LateLoad); 16 | } 17 | 18 | static void LateLoad() 19 | { 20 | Type type = AccessTools.TypeByName("PocketSand.PawnExtensions"); 21 | 22 | MP.RegisterSyncMethod(type, "EquipFromInventory"); 23 | MP.RegisterSyncMethod(type, "DropFromInventory"); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaAnimalsEndangered.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Vanilla Animals Expanded - Endangered by Oskar Potocki, Sarg Bjornson, Erin, Taranchuk 8 | /// 9 | /// 10 | [MpCompatFor("VanillaExpanded.VAEEndAndExt")] 11 | class VanillaAnimalsEndangered 12 | { 13 | public VanillaAnimalsEndangered(ModContentPack mod) 14 | { 15 | var type = AccessTools.TypeByName("VanillaAnimalsExpandedEndangered.Pawn_GetGizmos_Patch"); 16 | 17 | MP.RegisterSyncDelegateLambda(type, "Postfix", 1); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Mods/Windows.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Windows by Owlchemist, jptrrs 7 | /// 8 | /// 9 | [MpCompatFor("Owlchemist.Windows")] 10 | internal class Windows 11 | { 12 | public Windows(ModContentPack mod) 13 | { 14 | var type = AccessTools.TypeByName("OpenTheWindows.Building_Window"); 15 | MpCompat.RegisterLambdaMethod(type, "GetGizmos", 3, 5); 16 | 17 | type = AccessTools.TypeByName("OpenTheWindows.CompWindow"); 18 | MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", 1); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Mods/RunandGun.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Verse; 3 | using HarmonyLib; 4 | using Multiplayer.API; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// RunAndGun by roolo 9 | /// 10 | /// 11 | [MpCompatFor("roolo.RunAndGun")] 12 | class RunandGun 13 | { 14 | public RunandGun(ModContentPack mod) 15 | { 16 | Type type = AccessTools.TypeByName("RunAndGun.Harmony.Pawn_DraftController_GetGizmos_Patch"); 17 | 18 | MP.RegisterSyncDelegateLambda(type, "Postfix", 1); 19 | 20 | PatchingUtilities.PatchSystemRand("RunAndGun.Harmony.MentalStateHandler_TryStartMentalState:shouldRunAndGun", false); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Mods/VanillaRacesExpandedLycanthrope.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Vanilla Races Expanded - Lycanthrope by Oskar Potocki, Sarg Bjornson 7 | /// 8 | /// 9 | [MpCompatFor("vanillaracesexpanded.lycanthrope")] 10 | public class VanillaRacesExpandedLycanthrope 11 | { 12 | public VanillaRacesExpandedLycanthrope(ModContentPack mod) 13 | { 14 | AccessTools.DeclaredField("VanillaRacesExpandedLycanthrope.VanillaRacesExpandedLycanthrope_InteractionWorker_Interacted_Patch:random") 15 | .SetValue(null, PatchingUtilities.RandRedirector.Instance); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaAnimalsWaste.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Animals Expanded — Waste Animals by Oskar Potocki, Sarg Bjornson, Sir Van 6 | /// 7 | /// 8 | [MpCompatFor("VanillaExpanded.VAEWaste")] 9 | public class VanillaAnimalsWaste 10 | { 11 | public VanillaAnimalsWaste(ModContentPack mod) 12 | => PatchingUtilities.PatchSystemRand(new[] 13 | { 14 | "VanillaAnimalsExpandedWaste.HediffComp_Toxflu:CompPostPostRemoved", 15 | "VanillaAnimalsExpandedWaste.VanillaAnimalsExpandedWaste_InteractionWorker_Interacted_Patch:Toxflu", 16 | }, false); 17 | } 18 | } -------------------------------------------------------------------------------- /Source/Mods/AnimaObelisk.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HarmonyLib; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Anima Obelisk by DimonSever000 8 | /// 9 | [MpCompatFor("DimonSever000.AnimaObelisk.Specific")] 10 | internal class AnimaObelisk 11 | { 12 | public AnimaObelisk(ModContentPack mod) 13 | { 14 | var type = AccessTools.TypeByName("PsyObelisk.Things.ThingComp_PsyObelisk"); 15 | PatchingUtilities.PatchUnityRand(AccessTools.Method(type, "GlowAround"), false); 16 | var syncMethods = MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", Enumerable.Range(0, 8).ToArray()); // 0 to 7 17 | syncMethods.Skip(2).SetDebugOnly(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Mods/VPESkipdoorPathing.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// VPE Skipdoor Pathing by V337 6 | /// You should probably use Better VPE Skipdoor Pathing instead, as it has less issues: 7 | /// 8 | /// 9 | [MpCompatFor("v337.VPESkipdoorPathing")] 10 | public class VPESkipdoorPathing 11 | { 12 | public VPESkipdoorPathing(ModContentPack mod) 13 | { 14 | // Toggle use while drafted gizmo 15 | MpCompat.RegisterLambdaMethod("VPESkipdoorPathing.CompSkipdoorPathing", nameof(ThingComp.CompGetGizmosExtra), 1); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Source/Mods/CustomPrisonerInteractions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HarmonyLib; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Custom Prisoner Interactions by Mlie 8 | /// 9 | /// 10 | [MpCompatFor("Mlie.CustomPrisonerInteractions")] 11 | public class CustomPrisonerInteractions 12 | { 13 | public CustomPrisonerInteractions(ModContentPack mod) 14 | { 15 | var type = AccessTools.TypeByName("CustomPrisonerInteractions.ITab_Pawn_Visitor_FillTab"); 16 | MpCompat.RegisterLambdaDelegate(type, "Prefix", Enumerable.Range(0, 11).ToArray()); // 0 to 10 17 | MpCompat.RegisterLambdaDelegate(type, "Postfix", 0); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaHelixienGas.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Helixien Gas Expanded by Oskar Potocki, Kikohi 6 | /// 7 | /// 8 | [MpCompatFor("VanillaExpanded.HelixienGas")] 9 | public class VanillaHelixienGasExpanded 10 | { 11 | public VanillaHelixienGasExpanded(ModContentPack mod) 12 | { 13 | // Fill gizmo 14 | MpCompat.RegisterLambdaMethod("VHelixienGasE.Building_GasGeyser", "GetGizmos", 0); 15 | // GenView.ShouldSpawnMotesAt call with RNG calls, need to push/pop 16 | PatchingUtilities.PatchPushPopRand("VHelixienGasE.IntermittentGasSprayer:ThrowGasPuffUp"); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | paths-ignore: 9 | - 'README.md' 10 | 11 | jobs: 12 | build: 13 | name: Build workshop 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout Repository 17 | uses: actions/checkout@v3 18 | 19 | - name: Setup Dotnet 20 | uses: actions/setup-dotnet@v2 21 | with: 22 | dotnet-version: 8.0.x 23 | 24 | - name: Run workshop bundler 25 | run: ./release_bundler.sh 26 | 27 | - uses: "marvinpinto/action-automatic-releases@latest" 28 | with: 29 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 30 | prerelease: false 31 | automatic_release_tag: "latest" 32 | title: "Multiplayer Compatibility" 33 | files: | 34 | Multiplayer-Compatibility.zip 35 | -------------------------------------------------------------------------------- /Source/Mods/SmallVehicleAddons.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat; 6 | 7 | /// Small Vehicle Add-ons by いのしし_3 8 | /// 9 | [MpCompatFor("Inoshishi3.SmallVehicleAddons")] 10 | public class SmallVehicleAddons 11 | { 12 | public SmallVehicleAddons(ModContentPack mod) 13 | { 14 | // Quickly add all nearby items to transferables 15 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("SmallVehicleAddons.CompFastCargo:AddItemsToTheVehicleTransferables")); 16 | // Honk the car horn 17 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("SmallVehicleAddons.CompHonk:DoHonkHonk")); 18 | // Toggle car lights on/off 19 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("SmallVehicleAddons.CompLights:ToggleDeployment")); 20 | } 21 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaFurnitureExpandedPower.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using UnityEngine; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Vanilla Furniture Expanded - Power by Oskar Potocki and Sarg Bjornson 10 | /// 11 | /// 12 | /// Contribution to Multiplayer Compatibility by Sokyran and Reshiram 13 | [MpCompatFor("VanillaExpanded.VFEPower")] 14 | class VanillaPowerExpanded 15 | { 16 | public VanillaPowerExpanded(ModContentPack mod) 17 | { 18 | // Violence generator 19 | var type = AccessTools.TypeByName("VanillaPowerExpanded.CompSoulsPowerPlant"); 20 | MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", 1); // Toggle on/off 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Source/Mods/ClutterStructures.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Clutter Structures by mrofa and neronix17 7 | /// 8 | /// 9 | [MpCompatFor("neronix17.clutterstructures")] 10 | class ClutterStructures 11 | { 12 | public ClutterStructures(ModContentPack mod) 13 | { 14 | MpCompat.harmony.Patch( 15 | AccessTools.Method("Clutter_StructureWall.StructureDefGenerator:StuffGeneratrs"), 16 | postfix: new HarmonyMethod(typeof(ClutterStructures), nameof(GetClutterStructureDefPostfix)) 17 | ); 18 | } 19 | 20 | static void GetClutterStructureDefPostfix(ThingDef Wallzie) 21 | { 22 | DefDatabase.Add(Wallzie); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Source/Mods/StuffedFloors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | using HarmonyLib; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | 10 | /// Stuffed Floors by Fluffy 11 | /// 12 | /// 13 | [MpCompatFor("fluffy.stuffedfloors")] 14 | class StuffedFloorsCompat 15 | { 16 | public StuffedFloorsCompat(ModContentPack mod) 17 | { 18 | MpCompat.harmony.Patch( 19 | AccessTools.Method("StuffedFloors.FloorTypeDef:GetStuffedTerrainDef"), 20 | postfix: new HarmonyMethod(typeof(StuffedFloorsCompat), nameof(GetStuffedTerrainDefPosfix)) 21 | ); 22 | } 23 | 24 | static void GetStuffedTerrainDefPosfix(TerrainDef __result) 25 | { 26 | DefDatabase.Add(__result); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Mods/PerspectiveBuildings.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Perspective: Buildings by Owlchemist 8 | /// 9 | /// 10 | [MpCompatFor("Owlchemist.PerspectiveBuildings")] 11 | [MpCompatFor("Mlie.PerspectiveBuildings")] 12 | internal class PerspectiveBuildings 13 | { 14 | public PerspectiveBuildings(ModContentPack mod) 15 | { 16 | LongEventHandler.ExecuteWhenFinished(LatePatch); 17 | } 18 | 19 | private static void LatePatch() 20 | { 21 | var type = AccessTools.TypeByName("Perspective.CompOffsetter"); 22 | MP.RegisterSyncMethod(type, "SetCurrentOffset"); 23 | MP.RegisterSyncMethod(type, "SetMirroredState"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/Mods/SimplyMoreBridges.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | using HarmonyLib; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Simply More Bridges by Lanilor 10 | /// 11 | /// 12 | [MpCompatFor("Mlie.SimplyMoreBridges")] 13 | class SimplyMoreBridgesCompat 14 | { 15 | public SimplyMoreBridgesCompat(ModContentPack mod) 16 | { 17 | MpCompat.harmony.Patch( 18 | AccessTools.Method("SimplyMoreBridges.GenerateBridges:GenerateBridgeDef"), 19 | postfix: new HarmonyMethod(typeof(SimplyMoreBridgesCompat), nameof(GenerateBridgeDefPostfix)) 20 | ); 21 | } 22 | 23 | static void GenerateBridgeDefPostfix(TerrainDef __result) 24 | { 25 | DefDatabase.Add(__result); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/Mods/VanillaFactionsVikings.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Vanilla Factions Expanded - Vikings by Oskar Potocki, Erin, Sarg Bjornson, erdelf, Kikohi, Taranchuk, Helixien, Chowder 7 | /// 8 | /// 9 | [MpCompatFor("OskarPotocki.VFE.Vikings")] 10 | class VanillaFactionsVikings 11 | { 12 | public VanillaFactionsVikings(ModContentPack mod) 13 | { 14 | // Debug stuff 15 | var type = AccessTools.TypeByName("VFEV.Apiary"); 16 | MpCompat.RegisterLambdaMethod(type, "GetGizmos", 0, 1).SetDebugOnly(); 17 | 18 | // This method seems unused... But I guess it's better to be safe than sorry. 19 | PatchingUtilities.PatchSystemRand(AccessTools.Method(type, "ResetTend"), false); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Source/Mods/PowerSwitch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | 8 | /// PowerSwitch by Haplo 9 | /// 10 | /// 11 | [MpCompatFor("Haplo.PowerSwitch")] 12 | public class PowerSwitch 13 | { 14 | public PowerSwitch(ModContentPack mod) 15 | { 16 | var type = AccessTools.TypeByName("PowerSwitch.Building_PowerSwitchMod"); 17 | 18 | MP.RegisterSyncMethod(type, "switchPowerOnOff"); 19 | MP.RegisterSyncMethod(type, "SwitchEnemyOnActiveOnOff"); 20 | MP.RegisterSyncMethod(type, "SwitchEnemyOffActiveOnOff"); 21 | MP.RegisterSyncMethod(type, "SwitchPawnActiveOnOff"); 22 | MP.RegisterSyncMethod(type, "TimerOffClicked"); 23 | MP.RegisterSyncMethod(type, "TimerOnClicked"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/Mods/GeneRipper.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Gene Ripper by Obi Vayne Kenobi 8 | /// 9 | [MpCompatFor("DanielWedemeyer.GeneRipper")] 10 | public class GeneRipper 11 | { 12 | public GeneRipper(ModContentPack mod) 13 | { 14 | // It's loading resources, must patch later. 15 | LongEventHandler.ExecuteWhenFinished(LatePatch); 16 | } 17 | 18 | static void LatePatch() 19 | { 20 | var type = AccessTools.TypeByName("GeneRipper.Building_GeneRipper"); 21 | // Dialog accept 22 | MpCompat.RegisterLambdaDelegate(type, "SelectPawn", 1); 23 | 24 | // Command finish, debug 25 | MP.RegisterSyncMethod(type, "Finish"); 26 | 27 | // Command cancel 28 | MP.RegisterSyncMethod(type, "Cancel"); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaOutpostsExpanded.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HarmonyLib; 7 | using Multiplayer.API; 8 | using Verse; 9 | 10 | namespace Multiplayer.Compat 11 | { 12 | /// Vanilla Outposts Expanded by legodude17, Oskar Potocki, Chowder, Kikohi 13 | /// 14 | /// 15 | [MpCompatFor("vanillaexpanded.outposts")] 16 | internal class VanillaOutpostsExpanded 17 | { 18 | public VanillaOutpostsExpanded(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(LatePatch); 19 | 20 | public void LatePatch() 21 | { 22 | MP.RegisterSyncMethod(AccessTools.TypeByName("VOE.Outpost_Artillery"), "Fire"); 23 | MpCompat.RegisterLambdaDelegate("VOE.Outpost_Defensive", "GetGizmos", 3); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/Mods/VanillaPlantsMorePlants.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Plants Expanded - More Plants by Sarg Bjornson, Oskar Potocki 6 | /// 7 | /// 8 | [MpCompatFor("VanillaExpanded.VPlantsEMore")] 9 | internal class VanillaPlantsMorePlants 10 | { 11 | public VanillaPlantsMorePlants(ModContentPack mod) 12 | { 13 | PatchingUtilities.PatchSystemRand("VanillaPlantsExpandedMorePlants.Plant_SowsAdjacent:SpawnSetup", false); 14 | PatchingUtilities.PatchSystemRand("VanillaPlantsExpandedMorePlants.Plant_TransformOnMaturity:TickLong", false); 15 | 16 | MpCompat.RegisterLambdaMethod("VanillaPlantsExpandedMorePlants.Zone_GrowingAquatic", "GetGizmos", 1, 3); 17 | MpCompat.RegisterLambdaMethod("VanillaPlantsExpandedMorePlants.Zone_GrowingSandy", "GetGizmos", 1, 3); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Mods/MechFramework.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Mech Framework by andery233xj 8 | /// 9 | /// 10 | [MpCompatFor("Aoba.AMP")] 11 | [MpCompatFor("Aoba.WalkerGears")] // 1.3, not updated yet 12 | // Couldn't find any more mods using it, but more may exist. 13 | public class MechFramework 14 | { 15 | // Just in case multiple mods using it are active 16 | private static bool patchOnce = false; 17 | 18 | public MechFramework(ModContentPack mod) 19 | { 20 | if (patchOnce) 21 | return; 22 | 23 | patchOnce = true; 24 | LongEventHandler.ExecuteWhenFinished(LatePatch); 25 | } 26 | 27 | private static void LatePatch() 28 | => MP.RegisterSyncMethod(AccessTools.DeclaredMethod("WalkerGear.WalkerGear_Core:GetOut")); 29 | } 30 | } -------------------------------------------------------------------------------- /Source/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("Multiplayer_Compat_DubsMintMenus")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("notfood")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.1.0.0")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Meru 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Source/Mods/BiomesCaverns.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Biomes! Caverns by The Biomes Mod Team 6 | /// 7 | /// 8 | [MpCompatFor("BiomesTeam.BiomesCaverns")] 9 | public class BiomesCaverns 10 | { 11 | public BiomesCaverns(ModContentPack mod) 12 | { 13 | // Gizmos 14 | { 15 | // Create drill pod blueprint 16 | MpCompat.RegisterLambdaDelegate("BiomesCaverns.Patches.Building_PodLauncher_GetGizmos_Patch", "DrillPodGizmo", 0); 17 | // (Dev) set progress to 100% 18 | MpCompat.RegisterLambdaMethod("Building_MushroomFermentingBarrel", nameof(Building.GetGizmos), 0).SetDebugOnly(); 19 | } 20 | 21 | // RNG + GenView.ShouldSpawnMotesAt/mote saturation check 22 | { 23 | PatchingUtilities.PatchPushPopRand("Caveworld_Flora_Unleashed.FruitingBody_Gleamcap:ThrowPoisonSmoke"); 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaNutrientPasteExpanded.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Nutrient Paste Expanded by Oskar Potocki, Kikohi 6 | /// 7 | /// 8 | [MpCompatFor("VanillaExpanded.VNutrientE")] 9 | public class VanillaNutrientPasteExpanded 10 | { 11 | public VanillaNutrientPasteExpanded(ModContentPack mod) 12 | { 13 | LongEventHandler.ExecuteWhenFinished(LatePatch); 14 | 15 | // Drop 5/10/20 meals 16 | MpCompat.RegisterLambdaDelegate("VNPE.Building_NutrientPasteDispenser_GetGizmos", "Postfix", 0, 1, 2); 17 | } 18 | 19 | private static void LatePatch() 20 | { 21 | // Drop 5/10/20 meals 22 | MpCompat.RegisterLambdaMethod("VNPE.Building_NutrientPasteTap", "GetGizmos", 0, 1, 2); 23 | 24 | // Drain 25 | MpCompat.RegisterLambdaMethod("VNPE.CompRegisterIngredients", "CompGetGizmosExtra", 0); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaFurnitureExpandedFarming.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Vanilla Furniture Expanded - Farming by Oskar Potocki and dninemfive 8 | /// 9 | /// 10 | /// contribution to Multiplayer Compatibility by Cody Spring 11 | [MpCompatFor("VanillaExpanded.VFEFarming")] 12 | class VFEF 13 | { 14 | public VFEF(ModContentPack mod) 15 | { 16 | //RNG Fix 17 | { 18 | var methods = new[] { 19 | "VFEF.MoteSprinkler:NewMote", 20 | "VFEF.MoteSprinkler:ThrowWaterSpray", 21 | }; 22 | 23 | PatchingUtilities.PatchPushPopRand(methods); 24 | } 25 | 26 | // Gizmo fix 27 | { 28 | // Toggle should scarecrow affect tamed/player animals 29 | MpCompat.RegisterLambdaMethod("VFEF.Building_Scarecrow", "GetGizmos", 1); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaTemperatureExpanded.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat; 6 | 7 | /// Vanilla Temperature Expanded by Oskar Potocki, xrushha, Arquebus, Taranchuk 8 | /// 9 | /// 10 | [MpCompatFor("VanillaExpanded.Temperature")] 11 | public class VanillaTemperatureExpanded 12 | { 13 | public VanillaTemperatureExpanded(ModContentPack mod) 14 | { 15 | LongEventHandler.ExecuteWhenFinished(LatePatch); 16 | 17 | // Unlink/relink 18 | MpCompat.RegisterLambdaDelegate("VanillaTemperatureExpanded.Comps.CompAcTempControl", nameof(ThingComp.CompGetGizmosExtra), 0); 19 | } 20 | 21 | private static void LatePatch() 22 | { 23 | var type = AccessTools.TypeByName("VanillaTemperatureExpanded.Buildings.Building_AcControlUnit"); 24 | // Change temperature by +/- 1/10 25 | MP.RegisterSyncMethod(type, "InterfaceChangeTargetNetworkTemperature"); 26 | // Reset temperature 27 | MP.RegisterSyncMethodLambda(type, nameof(Thing.GetGizmos), 2); 28 | } 29 | } -------------------------------------------------------------------------------- /Source/Mods/Tacticowl.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Tacticowl by Roolo, Owlchemist 6 | /// 7 | /// 8 | // The workshop link will need updating once it goes out of beta, as it's going to get a new page while this one will be taken down 9 | [MpCompatFor("Owlchemist.Tacticowl")] 10 | public class Tacticowl 11 | { 12 | public Tacticowl(ModContentPack mod) 13 | { 14 | // May be worth keeping an eye on those patches while the mod is still in the beta, as stuff may break easily 15 | // Search and Destroy 16 | MpCompat.RegisterLambdaDelegate("Tacticowl.Patch_GetGizmos", "CreateGizmo_SearchAndDestroy_Melee", 1); 17 | MpCompat.RegisterLambdaDelegate("Tacticowl.Patch_GetGizmos", "CreateGizmo_SearchAndDestroy_Ranged", 1); 18 | 19 | // Run and Gun 20 | MpCompat.RegisterLambdaDelegate("Tacticowl.Patch_PawnGetGizmos", "Postfix", 1); 21 | PatchingUtilities.PatchSystemRand("Tacticowl.Patch_TryStartMentalState:Postfix", false); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Source/Mods/AnimalLogic.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using RimWorld; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Animals logic by Oblitus 8 | /// 9 | /// 10 | /// Beds are already watched, they just lack ownership. Adding it in the button desyncs. This is a better place. 11 | [MpCompatFor("Oblitus.AnimalsLogic")] 12 | public class AnimalLogicCompat 13 | { 14 | public AnimalLogicCompat(ModContentPack mod) 15 | { 16 | MpCompat.harmony.Patch( 17 | AccessTools.Method(typeof(PawnComponentsUtility), nameof(PawnComponentsUtility.CreateInitialComponents)), 18 | postfix: new HarmonyMethod(typeof(AnimalLogicCompat), nameof(CreateInitialComponentsPostfix)) 19 | ); 20 | } 21 | 22 | static void CreateInitialComponentsPostfix(Pawn pawn) 23 | { 24 | if (pawn.RaceProps.Animal && pawn.ownership == null) { 25 | pawn.ownership = new Pawn_Ownership(pawn); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Mods/FertileFields.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Fertile Fields by Jamaican Castle 9 | /// 10 | [MpCompatFor("jamaicancastle.RF.fertilefields")] 11 | [MpCompatFor("greysuki.RF.fertilefields")] 12 | class FertileFieldsCompat 13 | { 14 | public FertileFieldsCompat(ModContentPack mod) 15 | { 16 | LongEventHandler.ExecuteWhenFinished(LatePatch); 17 | 18 | { 19 | // RNG 20 | PatchingUtilities.PatchSystemRand("RFF_Code.Building_CompostBin:PlaceProduct"); 21 | } 22 | 23 | Type type = AccessTools.TypeByName("RFF_Code.GrowZoneManager"); 24 | 25 | MP.RegisterSyncMethod(type, "ToggleReturnToSoil"); 26 | MP.RegisterSyncMethod(type, "ToggleDesignateReplacements"); 27 | } 28 | 29 | private static void LatePatch() 30 | { 31 | // Dev: set progress to 1 32 | MpCompat.RegisterLambdaMethod("RFF_Code.Building_CompostBarrel", nameof(Building.GetGizmos), 0).SetDebugOnly(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Mods/Desynchronized.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Desynchronized by Vectorial1024s and emipa606 7 | /// 8 | /// 9 | /// contribution to Multiplayer Compatibility by Ari 10 | [MpCompatFor("Mlie.Desynchronized")] 11 | public class Desynchronized 12 | { 13 | public Desynchronized(ModContentPack mod) 14 | { 15 | var rngFixMethods = new[] 16 | { 17 | "Desynchronized.TNDBS.Pawn_NewsKnowledgeTracker:ForgetRandom", 18 | "Desynchronized.TNDBS.Pawn_NewsKnowledgeTracker:ForgetRandomly", 19 | "Desynchronized.TNDBS.Utilities.NewsSpreadUtility:SelectNewsRandomly", 20 | "Desynchronized.TNDBS.Utilities.NewsSpreadUtility:SelectNewsDistinctly", 21 | "Desynchronized.Patches.NewsTransmit.PostFix_InteractionWorker:ExecuteNewsTarnsmission", 22 | "Desynchronized.TNDBS.TaleNewsPawnDied:CalculateNewsImportanceForPawn", 23 | }; 24 | PatchingUtilities.PatchPushPopRand(rngFixMethods); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaIdeologyDryads.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HarmonyLib; 7 | using Multiplayer.API; 8 | using Multiplayer.Compat; 9 | using Verse; 10 | 11 | namespace Multiplayer.Compat 12 | { 13 | /// Vanilla Ideology Expanded - Dryads by Oskar Potocki, Sarg Bjornson, Taranchuk, Reann Shepard 14 | /// 15 | /// 16 | [MpCompatFor("VanillaExpanded.Ideo.Dryads")] 17 | internal class VanillaIdeologyDryads 18 | { 19 | public VanillaIdeologyDryads(ModContentPack mod) 20 | { 21 | // RNG 22 | PatchingUtilities.PatchSystemRandCtor("VanillaIdeologyExpanded_Dryads.HediffComp_PeriodicWounds"); 23 | 24 | // Gizmos 25 | MP.RegisterSyncMethod(AccessTools.TypeByName("VanillaIdeologyExpanded_Dryads.CompPawnMerge"), "SetDryadAwakenPod"); 26 | MpCompat.RegisterLambdaMethod("VanillaIdeologyExpanded_Dryads.CompSpawnAwakened", "CompGetGizmosExtra", 0).SetDebugOnly(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Mods/VanillaRacesPhytokin.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// 8 | /// Vanilla Races Expanded - Phytokin by Oskar Potocki, Sarg Bjornson, Allie, Erin, Sir Van, Reann Shepard 9 | /// 10 | /// 11 | /// 12 | [MpCompatFor("vanillaracesexpanded.phytokin")] 13 | public class VanillaRacesPhytokin 14 | { 15 | public VanillaRacesPhytokin(ModContentPack mod) 16 | { 17 | // Dev mode gizmos 18 | // Reset dryad counter (in case of counter breaking) 19 | MpCompat.RegisterLambdaDelegate("VanillaRacesExpandedPhytokin.CompDryadCounter", "CompGetGizmosExtra", 0).SetDebugOnly(); 20 | 21 | var type = AccessTools.TypeByName("VanillaRacesExpandedPhytokin.CompVariablePollutionPump"); 22 | // Force pump now 23 | MP.RegisterSyncMethod(type, "Pump").SetDebugOnly(); // Also called while ticking 24 | // Set next pump time 25 | MpCompat.RegisterLambdaDelegate(type, "CompGetGizmosExtra", 1).SetDebugOnly(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaSocialInteractions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using HarmonyLib; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Vanilla Social Interactions Expanded by Oskar Potocki, Taranchuk 8 | /// 9 | /// 10 | [MpCompatFor("VanillaExpanded.VanillaSocialInteractionsExpanded")] 11 | public class VanillaSocialInteractions 12 | { 13 | private static AccessTools.FieldRef averageOpinionCacheField; 14 | 15 | public VanillaSocialInteractions(ModContentPack mod) 16 | { 17 | // Clear cache 18 | var field = AccessTools.DeclaredField("VanillaSocialInteractionsExpanded.VSIE_Utils:averageOpinionOfCache"); 19 | averageOpinionCacheField = AccessTools.StaticFieldRefAccess(field); 20 | 21 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod(typeof(GameComponentUtility), nameof(GameComponentUtility.FinalizeInit)), 22 | postfix: new HarmonyMethod(typeof(VanillaSocialInteractions), nameof(ClearCache))); 23 | } 24 | 25 | private static void ClearCache() => averageOpinionCacheField().Clear(); 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Mods/CleaningArea.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Cleaning Area by Hatti 9 | /// 10 | [MpCompatFor("hatti.cleaningarea")] 11 | [MpCompatFor("s1.cleaningareatemp")] 12 | public class CleaningArea 13 | { 14 | static Type CleaningAreaMapComponentType; 15 | 16 | public CleaningArea(ModContentPack mod) 17 | { 18 | Type type = CleaningAreaMapComponentType = AccessTools.TypeByName("CleaningArea.CleaningArea_MapComponent") ?? 19 | AccessTools.TypeByName("CleaningAreaTemp.CleaningArea_MapComponent"); 20 | 21 | MP.RegisterSyncMethod(AccessTools.PropertySetter(type, "cleaningArea")); 22 | MP.RegisterSyncWorker(SyncWorkerForCleaningArea, type); 23 | } 24 | 25 | static void SyncWorkerForCleaningArea(SyncWorker sw, ref MapComponent comp) 26 | { 27 | if (sw.isWriting) { 28 | sw.Write(comp.map); 29 | } else { 30 | comp = sw.Read().GetComponent(CleaningAreaMapComponentType); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Mods/OneBedToSleepWithAll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using HarmonyLib; 4 | using Multiplayer.API; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// One bed to sleep with all by Densevoid 10 | /// 11 | /// 12 | [MpCompatFor("densevoid.onebedtosleepwithall")] 13 | internal class OneBedToSleepWithAll 14 | { 15 | private static Type compType; 16 | 17 | public OneBedToSleepWithAll(ModContentPack mod) 18 | { 19 | compType = AccessTools.TypeByName("OneBedToSleepWithAll.CompPolygamyMode"); 20 | MpCompat.RegisterLambdaMethod(compType, "CompGetGizmosExtra", 1); 21 | // Comp has a null props.compClass, most likely it's dynamically created 22 | MP.RegisterSyncWorker(SyncCompPolygamyMode, compType); 23 | } 24 | 25 | private static void SyncCompPolygamyMode(SyncWorker sync, ref ThingComp comp) 26 | { 27 | if (sync.isWriting) sync.Write(comp.parent); 28 | else comp = sync.Read().AllComps.FirstOrDefault(x => x.GetType() == compType); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Mods/VanillaFactionsSettlers.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Fanilla Factions Expanded - Settlers by OskarPotocki.VanillaFactionsExpanded.SettlersModule 8 | /// 9 | /// 10 | [MpCompatFor("OskarPotocki.VanillaFactionsExpanded.SettlersModule")] 11 | class VanillaFactionsSettlers 12 | { 13 | public VanillaFactionsSettlers(ModContentPack mod) 14 | { 15 | var type = AccessTools.TypeByName("VFE_Settlers.Utilities.UtilityEvent"); 16 | 17 | // Protection fee event 18 | MP.RegisterSyncDialogNodeTree(type, "ProtectionFee"); 19 | // Caravan gizmo - turn in wanted criminal to settlement 20 | MpCompat.RegisterLambdaDelegate(type, "CommandTurnInWanted", 0); 21 | // Toggle mode 22 | MpCompat.RegisterLambdaMethod(AccessTools.TypeByName("Warmup.CompWarmUpReduction"), "CompGetGizmosExtra", 1); 23 | // Five fingers fillet table 24 | PatchingUtilities.PatchUnityRand("VFE_Settlers.JobGivers.JobDriver_PlayFiveFingerFillet:WatchTickAction", false); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Mods/LightsOut.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using HarmonyLib; 4 | using Multiplayer.API; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// LightsOut by juanlopez2008 10 | /// 11 | /// 12 | [MpCompatFor("juanlopez2008.LightsOut")] 13 | internal class LightsOut 14 | { 15 | public LightsOut(ModContentPack mod) 16 | { 17 | // Gizmos 18 | { 19 | MP.RegisterSyncMethod(AccessTools.PropertySetter("LightsOut.ThingComps.KeepOnComp:KeepOn")); 20 | } 21 | 22 | // Patched sync methods 23 | { 24 | PatchingUtilities.PatchCancelInInterface( 25 | // From the patches we care for, PatchTableOff affects gene extractor and subcore scanner 26 | "LightsOut.Common.TablesHelper:PatchTableOff", 27 | // PatchTableOn doesn't look like it needs a patch, as it's not used on any sync methods 28 | "LightsOut.Patches.ModCompatibility.Biotech.PatchGeneAssembly:OnStart", 29 | "LightsOut.Patches.ModCompatibility.Biotech.PatchWasteAtomiser:UpdateWorking"); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Multiplayer_Compat.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Multiplayer_Compat", "Source\Multiplayer_Compat.csproj", "{163F72FB-89D2-4FA7-B392-D31513C473BE}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Multiplayer_Compat_Referenced", "Source_Referenced\Multiplayer_Compat_Referenced.csproj", "{65FF869B-3A3E-418A-8734-4D70FE2D0B1F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {163F72FB-89D2-4FA7-B392-D31513C473BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {163F72FB-89D2-4FA7-B392-D31513C473BE}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {163F72FB-89D2-4FA7-B392-D31513C473BE}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {163F72FB-89D2-4FA7-B392-D31513C473BE}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {65FF869B-3A3E-418A-8734-4D70FE2D0B1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {65FF869B-3A3E-418A-8734-4D70FE2D0B1F}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {65FF869B-3A3E-418A-8734-4D70FE2D0B1F}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {65FF869B-3A3E-418A-8734-4D70FE2D0B1F}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | EndGlobal 24 | -------------------------------------------------------------------------------- /Source/Mods/PerspectiveOres.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Perspective: Ores by Owlchemist 8 | /// 9 | /// 10 | [MpCompatFor("Owlchemist.PerspectiveOres")] 11 | public class PerspectiveOres 12 | { 13 | public PerspectiveOres(ModContentPack mod) 14 | { 15 | PatchingUtilities.PatchLongEventMarkers(); 16 | 17 | // Stop the "ProcessMap" from running when it could break stuff. 18 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod("PerspectiveOres.PerspectiveOresSetup:ProcessMap"), 19 | prefix: new HarmonyMethod(typeof(PerspectiveOres), nameof(StopSecondHostCall))); 20 | } 21 | 22 | private static bool StopSecondHostCall(bool reset) 23 | { 24 | // Stop the host from running it for the second time. It messes stuff up due to the place it's called from. 25 | if (!PatchingUtilities.AllowedToRunLongEvents) 26 | return false; 27 | // Prevent it from being called due to settings change, could mess stuff up. 28 | if (reset && MP.IsInMultiplayer) 29 | return false; 30 | return true; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Source/Mods/CutPlantsBeforeBuilding.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Cut plants before building by tammybee 8 | /// 9 | [MpCompatFor("tammybee.cutplantsbeforebuilding")] 10 | [MpCompatFor("Mlie.CutPlantsBeforeBuilding")] 11 | class CutPlantsBeforeBuilding 12 | { 13 | private static ISyncField syncAutoCutPlants; 14 | 15 | public CutPlantsBeforeBuilding(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(LatePatch); 16 | 17 | private static void LatePatch() 18 | { 19 | syncAutoCutPlants = MP.RegisterSyncField( 20 | AccessTools.Field(AccessTools.TypeByName("CutPlantsBeforeBuilding.Main"), "AutoDesignatePlantsCutMode")); 21 | MpCompat.harmony.Patch(AccessTools.Method("CutPlantsBeforeBuilding.HarmonyPatches.PlaySettings_DoPlaySettingsGlobalControls:Prefix"), 22 | prefix: new HarmonyMethod(WatchAutoCutPlantsPrefix)); 23 | } 24 | 25 | // No need to begin and end watching, as this prefix is already called in a place where it's done by the MP mod 26 | // (The place where auto expanding home area and auto rebuilding destroyed things is synced) 27 | private static void WatchAutoCutPlantsPrefix() => syncAutoCutPlants.Watch(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Mods/HuntForMe.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Hunt for Me by aRandomKiwi 9 | /// 10 | /// 11 | [MpCompatFor("aRandomKiwi.HuntForMe")] 12 | internal class HuntForMe 13 | { 14 | public HuntForMe(ModContentPack mod) 15 | { 16 | var type = AccessTools.TypeByName("aRandomKiwi.HFM.Pawn_MindState_Patch"); 17 | type = AccessTools.Inner(type, "GetGizmos"); 18 | MpCompat.RegisterLambdaDelegate(type, "Listener", 1, 3, 5, 6, 7, 8) 19 | .Take(3).ToArray().SetDebugOnly(); 20 | 21 | var typeNames = new[] 22 | { 23 | "aRandomKiwi.HFM.PawnColumnWorker_Hunting", 24 | "aRandomKiwi.HFM.PawnColumnWorker_HuntingCanAssist", 25 | "aRandomKiwi.HFM.PawnColumnWorker_PreyModeHunting", 26 | "aRandomKiwi.HFM.PawnColumnWorker_SupervisedHunting", 27 | }; 28 | 29 | foreach (var typeName in typeNames) 30 | { 31 | type = AccessTools.TypeByName(typeName); 32 | MP.RegisterSyncMethod(type, "SetValue"); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/Mods/VanillaAnimalsCaves.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | 3 | namespace Multiplayer.Compat 4 | { 5 | /// Vanilla Animals Expanded - Caves by Oskar Potocki, Sarg Bjornson, Aquiles 6 | /// 7 | /// 8 | [MpCompatFor("VanillaExpanded.VAECaves")] 9 | internal class VanillaAnimalsCaves 10 | { 11 | public VanillaAnimalsCaves(ModContentPack mod) 12 | { 13 | // RNG 14 | { 15 | var methods = new[] 16 | { 17 | "VAECaves.Building_SpiderEggs:Tick", 18 | "VAECaves.DamageWorker_TwoArmSlam:Apply", 19 | "VAECaves.IncidentWorker_Hulk:CanFireNowSub", 20 | "VAECaves.JobDriver_CreateEggs:b__1_0", 21 | }; 22 | 23 | PatchingUtilities.PatchSystemRand(methods, false); 24 | PatchingUtilities.PatchSystemRandCtor("VAECaves.Hediff_WallBreaker", false); 25 | } 26 | 27 | // Gizmos 28 | { 29 | MpCompat.RegisterLambdaMethod("VAECaves.Building_Cocoon", "GetGizmos", 0); 30 | MpCompat.RegisterLambdaMethod("VAECaves.CompConditionalSpawner", "CompGetGizmosExtra", 0).SetDebugOnly(); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Mods/RimFridge.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// RimFridge by KiameV, maintained by "Just Harry" 8 | /// Fixes for gizmos 9 | /// 10 | /// 11 | [MpCompatFor("rimfridge.kv.rw")] 12 | public class RimFridgeCompat 13 | { 14 | public RimFridgeCompat(ModContentPack mod) 15 | { 16 | // Several Gizmos 17 | { 18 | var type = AccessTools.TypeByName("RimFridge.CompRefrigerator"); 19 | // Offset temperature by x degrees 20 | MP.RegisterSyncMethod(type, "InterfaceChangeTargetTemperature"); 21 | // Reset to default 22 | MpCompat.RegisterLambdaDelegate(type, "CompGetGizmosExtra", 2); 23 | 24 | // Toggle darklight 25 | MpCompat.RegisterLambdaMethod("RimFridge.CompToggleGlower", "CompGetGizmosExtra", 0); 26 | } 27 | 28 | // Current map usage 29 | // Not needed for the fork made by "Not Harry" (at the moment, the only 1.5 fork), but other forks may need this. 30 | { 31 | PatchingUtilities.ReplaceCurrentMapUsage("RimFridge.Patch_ReachabilityUtility_CanReach:Prefix", false, false); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Mods/MinifyEverything.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using HarmonyLib; 4 | using Multiplayer.API; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Minify Everything by Erdelf 10 | /// 11 | /// 12 | [MpCompatFor("erdelf.MinifyEverything")] 13 | public class MinifyEverything 14 | { 15 | public MinifyEverything(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(LatePatch); 16 | 17 | public static void LatePatch() 18 | { 19 | var type = AccessTools.TypeByName("MinifyEverything.MinifyEverything"); 20 | 21 | MpCompat.harmony.Patch(AccessTools.Method(type, "DoStuff"), 22 | prefix: new HarmonyMethod(typeof(MinifyEverything), nameof(PreDoStuff))); 23 | } 24 | 25 | // Cancel the coroutine in MP and redirect the action to LongEventHandler 26 | private static bool PreDoStuff(Action action, ref IEnumerator __result) 27 | { 28 | if (!MP.IsInMultiplayer) return true; 29 | // Return a fake, empty coroutine as a result, so we don't get any errors 30 | __result = FakeCoroutine(); 31 | LongEventHandler.ExecuteWhenFinished(action); 32 | return false; 33 | } 34 | 35 | private static IEnumerator FakeCoroutine() 36 | { 37 | yield return null; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/Multiplayer_Compat.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net48 5 | 12 6 | false 7 | false 8 | false 9 | ..\Assemblies\ 10 | false 11 | false 12 | None 13 | Multiplayer.Compat 14 | 15 | 16 | 17 | 18 | 2.3.6 19 | runtime 20 | 21 | 22 | 0.6.0 23 | runtime 24 | 25 | 26 | 1.6.* 27 | runtime 28 | 29 | 30 | 2.3.* 31 | all 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Source/Mods/ChoiceOfPsycasts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Choice Of Psycasts by Azuraal 9 | /// 10 | [MpCompatFor("azuraal.choiceofpsycasts")] 11 | public class ChoiceOfPsycastsCompat 12 | { 13 | static Type learnPsycastsType; 14 | static AccessTools.FieldRef levelField; 15 | static AccessTools.FieldRef parentField; 16 | 17 | public ChoiceOfPsycastsCompat(ModContentPack mod) 18 | { 19 | var type = learnPsycastsType = AccessTools.TypeByName("ChoiceOfPsycasts.LearnPsycasts"); 20 | 21 | MpCompat.RegisterLambdaDelegate(type, "Choice", 1); 22 | MpCompat.RegisterLambdaDelegate(type, "ChoiceCustom", 1); 23 | 24 | levelField = AccessTools.FieldRefAccess(type, "Level"); 25 | parentField = AccessTools.FieldRefAccess(type, "Parent"); 26 | MP.RegisterSyncWorker(SyncLearnPsycasts, type); 27 | } 28 | 29 | static void SyncLearnPsycasts(SyncWorker sync, ref Command_Action command) 30 | { 31 | if (sync.isWriting) { 32 | sync.Write(levelField(command)); 33 | sync.Write(parentField(command)); 34 | } else { 35 | command = (Command_Action) Activator.CreateInstance(learnPsycastsType, sync.Read(), sync.Read()); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Mods/BiotechMechStuffElongatedMechanoidUpgrades.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Biotech Mech Stuff Elongated - Mechanoid Upgrades V2 by MrKociak 7 | /// 8 | [MpCompatFor("MrKociak.BiotechMoreMechStuffMechUpgradePod")] 9 | public class BiotechMechStuffElongatedMechanoidUpgrades 10 | { 11 | public BiotechMechStuffElongatedMechanoidUpgrades(ModContentPack mod) 12 | { 13 | LongEventHandler.ExecuteWhenFinished(LatePatch); 14 | 15 | // Toggle: auto repeat (1), pick same mechanoid (3), pick random mechanoid (5), advanced filters (7) 16 | // Toggle (dis)allow filters: (light/heavy) mechs (9), (medium/ultra heavy) mechs (11), melee mechs (13), ranged mechs (15), worker mechs (17) 17 | // Ligh/medium and heavy/ultra heavy gizmos depend on the type of the building. 18 | MpCompat.RegisterLambdaMethod("RimWorld.CompRepeatCycle_BMU", "CompGetGizmosExtra", 1, 3, 5, 7, 9, 11, 13, 15, 17); 19 | // Change into another building (single building has multiple of those) 20 | MpCompat.RegisterLambdaMethod("RimWorld.CompSwapBuilding_BMU", "CompGetGizmosExtra", 0); 21 | } 22 | 23 | private static void LatePatch() 24 | { 25 | // Start procedure (2), cancel load/procedure, forget selected mech (6, 7), dev: enable/disable ingredients (0), complete (8) 26 | MpCompat.RegisterLambdaMethod("Building_MechUpgrader", "GetGizmos", 2, 6, 0, 8).TakeLast(2).SetDebugOnly(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaPsycastsExpandedPuppeteer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using HarmonyLib; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Vanilla Psycasts Expanded - Puppeteer by Oskar Potocki, Taranchuk, Reann Shepard 8 | /// Not on Github yet it seems 9 | /// 10 | [MpCompatFor("VanillaExpanded.VPE.Puppeteer")] 11 | public class VanillaPsycastsExpandedPuppeteer 12 | { 13 | private static AccessTools.FieldRef canDoRandomBreaksCache; 14 | 15 | public VanillaPsycastsExpandedPuppeteer(ModContentPack mod) 16 | { 17 | // RNG 18 | { 19 | PatchingUtilities.PatchPushPopRand("VPEPuppeteer.Hediff_BrainLeech:ThrowFleck"); 20 | } 21 | 22 | // Cache 23 | { 24 | // May not be necessary, but it's hard to test if it actually is or isn't. 25 | // Won't break anything or cause issues to have this, so may as well keep it. 26 | canDoRandomBreaksCache = AccessTools.StaticFieldRefAccess( 27 | AccessTools.DeclaredField("VPEPuppeteer.MentalBreaker_CanDoRandomMentalBreaks_Patch:cachedResults")); 28 | 29 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod(typeof(GameComponentUtility), nameof(GameComponentUtility.FinalizeInit)), 30 | postfix: new HarmonyMethod(typeof(VanillaPsycastsExpandedPuppeteer), nameof(ClearCache))); 31 | } 32 | } 33 | 34 | private static void ClearCache() => canDoRandomBreaksCache().Clear(); 35 | } 36 | } -------------------------------------------------------------------------------- /Source/Mods/LinkedPlanters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using RimWorld; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Linked Planters by rswallen 10 | /// 11 | [MpCompatFor("rswallen.linkedplanters")] 12 | public class LinkedPlanters 13 | { 14 | private static Type gizmoCacheType; 15 | private static AccessTools.FieldRef gizmoCacheRootField; 16 | 17 | public LinkedPlanters(ModContentPack mod) 18 | { 19 | var type = AccessTools.TypeByName("LinkedPlanters.GrowerGroup"); 20 | type = gizmoCacheType = AccessTools.Inner(type, "GizmoCache"); 21 | 22 | gizmoCacheRootField = AccessTools.FieldRefAccess(type, "root"); 23 | MP.RegisterSyncWorker(SyncGrowerGroupGizmoCache, type); 24 | 25 | // Can't use MpCompat.RegisterLambdaDelegate, need to pass constructor as parent 26 | // 0 - link, 2 - unlink 27 | var methods = MpMethodUtil.GetLambda(type, null, MethodType.Constructor, new[] { typeof(Building_PlantGrower) }, 0, 2); 28 | foreach (var method in methods) 29 | MP.RegisterSyncDelegate(type, method.DeclaringType!.Name, method.Name).SetContext(SyncContext.MapSelected); 30 | } 31 | 32 | private static void SyncGrowerGroupGizmoCache(SyncWorker sync, ref object obj) 33 | { 34 | if (sync.isWriting) 35 | sync.Write(gizmoCacheRootField(obj)); 36 | else 37 | obj = Activator.CreateInstance(gizmoCacheType, sync.Read()); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaFurnitureExpanded.cs: -------------------------------------------------------------------------------- 1 | using Multiplayer.API; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Vanilla Furniture Expanded by OskarPotocki, Atlas, Kikohi 7 | /// 8 | /// 9 | [MpCompatFor("VanillaExpanded.VFECore")] 10 | internal class VanillaFurnitureExpanded 11 | { 12 | [MpCompatSyncField("VanillaFurnitureEC.CompBinClean", "cleanupTarget")] 13 | private static ISyncField cleanupTargetField = null; 14 | 15 | public VanillaFurnitureExpanded(ModContentPack mod) 16 | { 17 | MpCompatPatchLoader.LoadPatch(this); 18 | 19 | // Uses ShouldSpawnMotesAt, so could potentially cause issues somewhere. Seems like no RNG though... Let's better be safe here. 20 | PatchingUtilities.PatchPushPopRand("VanillaFurnitureEC.JobDriver_PlayDarts:ThrowDart"); 21 | } 22 | 23 | [MpCompatPrefix("VanillaFurnitureEC.Command_CleanupTarget", "GizmoOnGUI")] 24 | private static void PreGizmoGui(ThingComp ___comp, out bool __state) 25 | { 26 | if (!MP.IsInMultiplayer || ___comp == null) 27 | { 28 | __state = false; 29 | return; 30 | } 31 | 32 | __state = true; 33 | MP.WatchBegin(); 34 | cleanupTargetField.Watch(___comp); 35 | } 36 | 37 | [MpCompatPostfix("VanillaFurnitureEC.Command_CleanupTarget", "GizmoOnGUI")] 38 | private static void PostGizmoGui(bool __state) 39 | { 40 | if (__state) 41 | MP.WatchEnd(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaRecyclingExpanded.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using RimWorld; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat; 7 | 8 | /// Vanilla Recycling Expanded by OskarPotocki, Sarg Bjornson, xrushha 9 | [MpCompatFor("VanillaExpanded.Recycling")] 10 | public class VanillaRecyclingExpanded 11 | { 12 | public VanillaRecyclingExpanded(ModContentPack mod) 13 | { 14 | MpCompatPatchLoader.LoadPatch(this); 15 | 16 | var type = AccessTools.TypeByName("VanillaRecyclingExpanded.CompBiopackDissolution"); 17 | // Dev: Single dissolution event (0), dissolution events until destroyed (1), +25% dissolution progress (2) 18 | MpCompat.RegisterLambdaMethod(type, nameof(ThingComp.CompGetGizmosExtra), 0, 1, 2).SetDebugOnly(); 19 | // Dev: Set next dissolve time 20 | MP.RegisterSyncDelegateLambda(type, nameof(ThingComp.CompGetGizmosExtra), 4).SetDebugOnly(); 21 | 22 | type = AccessTools.TypeByName("VanillaRecyclingExpanded.CompSuperSimpleProcessor"); 23 | MP.RegisterSyncMethod(type, "EjectContents"); 24 | // In interface only called as dev mode command 25 | MP.RegisterSyncMethod(type, "DoAtomize").SetDebugOnly(); 26 | // Toggle auto load 27 | MP.RegisterSyncMethodLambda(type, nameof(ThingComp.CompGetGizmosExtra), 1); 28 | // Dev: Set next processing time 29 | MP.RegisterSyncDelegateLambda(type, nameof(ThingComp.CompGetGizmosExtra), 3).SetDebugOnly(); 30 | } 31 | 32 | // Stop methods from running (most likely after syncing) when it would cause errors or other issues. 33 | [MpCompatPrefix("VanillaRecyclingExpanded.CompSuperSimpleProcessor", "DoAtomize")] 34 | private static bool PreDoAtomize(CompThingContainer __instance) => __instance.ContainedThing is { stackCount: > 0 }; 35 | } -------------------------------------------------------------------------------- /Source/Mods/BiomesPollutedLands.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat; 6 | 7 | /// Biomes! Polluted Lands by The Biomes Mod Team 8 | /// 9 | /// 10 | [MpCompatFor("BiomesTeam.BiomesPollutedLands")] 11 | public class BiomesPollutedLands 12 | { 13 | public BiomesPollutedLands(ModContentPack mod) 14 | { 15 | #region (Dev) gizmos 16 | 17 | { 18 | // DEV: chronic pains 19 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("BMT_PollutedLands.Gene_AddHediffWithInterval:Pain")).SetDebugOnly(); 20 | // Dev: add or remove hediff 21 | MpCompat.RegisterLambdaMethod("BMT_PollutedLands.Gene_AddOrRemoveHediff", nameof(Gene.GetGizmos), 0).SetDebugOnly(); 22 | // Dev: blood vomit 23 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("BMT_PollutedLands.Gene_BloodExplusion:Vomit")).SetDebugOnly(); 24 | // Dev: try hunt for food 25 | MpCompat.RegisterLambdaMethod("BMT_PollutedLands.Gene_CarrionMetabolism", nameof(Gene.GetGizmos), 0).SetDebugOnly(); 26 | // Dev: heal permanent wound 27 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("BMT_PollutedLands.Gene_MoltingRegeneration:TryHealWound")).SetDebugOnly(); 28 | // Dev: release gas 29 | MpCompat.RegisterLambdaMethod("BMT_PollutedLands.Gene_ToxspewingPores", nameof(Gene.GetGizmos), 0).SetDebugOnly(); 30 | } 31 | 32 | #endregion 33 | 34 | #region RNG 35 | 36 | { 37 | PatchingUtilities.PatchSystemRand("BMT_PollutedLands.Hediff_Mutapox:Tick"); 38 | } 39 | 40 | #endregion 41 | } 42 | } -------------------------------------------------------------------------------- /Source/Mods/AlphaBiomes.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Alpha Biomes by Sarg Bjornson 9 | /// 10 | /// 11 | [MpCompatFor("sarg.alphabiomes")] 12 | class AlphaBiomes 13 | { 14 | private static AccessTools.FieldRef buildingField; 15 | 16 | public AlphaBiomes(ModContentPack mod) 17 | { 18 | var type = AccessTools.TypeByName("AlphaBiomes.Command_SetStoneType"); 19 | 20 | buildingField = AccessTools.FieldRefAccess(type, "building"); 21 | // SyncWorker needed as the methods require syncing of the `building` field 22 | MP.RegisterSyncWorker(SyncSetStoneType, type, shouldConstruct: true); 23 | MpCompat.RegisterLambdaMethod(type, "ProcessInput", Enumerable.Range(0, 6).ToArray()); 24 | 25 | var rngFixMethods = new[] 26 | { 27 | "AlphaBiomes.CompGasProducer:CompTick", 28 | "AlphaBiomes.TarSprayer:SteamSprayerTick", 29 | // AlphaBiomes.TarSprayer:ThrowAirPuffUp - only contained by above method 30 | "AlphaBiomes.GameCondition_AcidRain:DoCellSteadyEffects", 31 | }; 32 | 33 | PatchingUtilities.PatchPushPopRand(rngFixMethods); 34 | } 35 | 36 | private static void SyncSetStoneType(SyncWorker sync, ref Command command) 37 | { 38 | if (sync.isWriting) 39 | sync.Write(buildingField(command)); 40 | else 41 | buildingField(command) = sync.Read(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Source/Mods/PickupAndHaul.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | using HarmonyLib; 6 | using RimWorld; 7 | using Verse; 8 | 9 | namespace Multiplayer.Compat 10 | { 11 | /// Pick Up And Haul by Mehni 12 | /// 13 | /// 14 | [MpCompatFor("Mehni.PickUpAndHaul")] 15 | public class PickupAndHaul 16 | { 17 | public PickupAndHaul(ModContentPack mod) 18 | { 19 | // Sorts the ListerHaulables list from UI, causes issues 20 | MpCompat.harmony.Patch(AccessTools.Method("PickUpAndHaul.WorkGiver_HaulToInventory:PotentialWorkThingsGlobal"), 21 | transpiler: new HarmonyMethod(typeof(PickupAndHaul), nameof(Transpiler))); 22 | } 23 | 24 | private static IEnumerable Transpiler(IEnumerable instr) 25 | { 26 | var target = AccessTools.Method(typeof(ListerHaulables), nameof(ListerHaulables.ThingsPotentiallyNeedingHauling)); 27 | var newListCtor = AccessTools.Constructor(typeof(List), new[] { typeof(IEnumerable) }); 28 | 29 | var patched = false; 30 | foreach (var ci in instr) 31 | { 32 | yield return ci; 33 | 34 | if (ci.opcode == OpCodes.Callvirt && ci.operand is MethodInfo method && method == target) 35 | { 36 | yield return new CodeInstruction(OpCodes.Newobj, newListCtor); 37 | patched = true; 38 | } 39 | } 40 | 41 | if (!patched) 42 | throw new Exception("Failed patching Pickup and Haul"); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaSkillsExpanded.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using RimWorld; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Vanilla Skills Expanded by legodude17, Oskar Potocki 10 | /// 11 | /// 12 | [MpCompatFor("vanillaexpanded.skills")] 13 | public class VanillaSkillsExpanded 14 | { 15 | private static IDictionary expertiseTrackers; 16 | 17 | public VanillaSkillsExpanded(ModContentPack mod) 18 | { 19 | expertiseTrackers = AccessTools.StaticFieldRefAccess("VSE.ExpertiseTrackers:trackers"); 20 | 21 | var type = AccessTools.TypeByName("VSE.ExpertiseTracker"); 22 | 23 | MP.RegisterSyncMethod(type, "AddExpertise"); 24 | MP.RegisterSyncMethod(type, "ClearExpertise").SetDebugOnly(); 25 | MP.RegisterSyncWorker(SyncExpertiseTracker, type); 26 | } 27 | 28 | public static void SyncExpertiseTracker(SyncWorker sync, ref object expertiseTracker) 29 | { 30 | if (sync.isWriting) 31 | { 32 | var found = false; 33 | 34 | foreach (DictionaryEntry entry in expertiseTrackers) 35 | { 36 | if (entry.Value != expertiseTracker) continue; 37 | 38 | sync.Write(((Pawn_SkillTracker)entry.Key).pawn); 39 | found = true; 40 | break; 41 | } 42 | 43 | if (!found) 44 | sync.Write(null); 45 | } 46 | else expertiseTracker = expertiseTrackers[sync.Read().skills]; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaPsycastsExpandedHemosage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using HarmonyLib; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Vanilla Psycasts Expanded - Hemosage by Oskar Potocki, Taranchuk, Reann Shepard, Sir Van 8 | /// 9 | /// 10 | [MpCompatFor("VanillaExpanded.VPE.Hemosage")] 11 | public class VanillaPsycastsExpandedHemosage 12 | { 13 | private static AccessTools.FieldRef unroofedCellsField; 14 | 15 | public VanillaPsycastsExpandedHemosage(ModContentPack mod) 16 | { 17 | // RNG 18 | { 19 | PatchingUtilities.PatchPushPopRand("VPEHemosage.Hediff_Bloodmist:ThrowFleck"); 20 | // Doesn't seem like it's needed (no ShouldSpawnMotesAt) - VPEHemosage.Ability_CorpseExplosion:ThrowBloodSmoke 21 | } 22 | 23 | // Gizmos 24 | { 25 | // Cancel bloodfocus/remove hediff 26 | MpCompat.RegisterLambdaDelegate("VPEHemosage.Ability_Bloodfocus", "GetGizmo", 0); 27 | } 28 | 29 | // Cache 30 | { 31 | unroofedCellsField = AccessTools.StaticFieldRefAccess( 32 | AccessTools.DeclaredField("VPEHemosage.WeatherOverlay_Bloodstorm:unroofedCells")); 33 | 34 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod(typeof(GameComponentUtility), nameof(GameComponentUtility.FinalizeInit)), 35 | postfix: new HarmonyMethod(typeof(VanillaPsycastsExpandedHemosage), nameof(ClearCache))); 36 | } 37 | } 38 | 39 | private static void ClearCache() => unroofedCellsField().Clear(); 40 | } 41 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaVehiclesExpanded.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Vanilla Vehicles Expanded by Oskar Potocki, xrushha, Smash Phil, Taranchuk, Reann Shepard 8 | /// 9 | [MpCompatFor("OskarPotocki.VanillaVehiclesExpanded")] 10 | public class VanillaVehiclesExpanded 11 | { 12 | public VanillaVehiclesExpanded(ModContentPack mod) 13 | { 14 | MpSyncWorkers.Requires(); 15 | 16 | var type = AccessTools.TypeByName("VanillaVehiclesExpanded.GarageDoor"); 17 | 18 | // Open (0), close (2), and cancel (1, 3) 19 | MpCompat.RegisterLambdaDelegate(type, "GetDoorGizmos", 0, 1, 2, 3); 20 | 21 | // Open/close gizmo from GarageAutoDoor 22 | MP.RegisterSyncMethod(type, "StartOpening"); 23 | MP.RegisterSyncMethod(type, "StartClosing"); 24 | 25 | // Toggle fridge on/off 26 | type = AccessTools.TypeByName("VanillaVehiclesExpanded.CompRefrigerator"); 27 | MpCompat.RegisterLambdaMethod(type, nameof(ThingComp.CompGetGizmosExtra), 1); 28 | 29 | // Reloadable verb 30 | type = AccessTools.TypeByName("VanillaVehiclesExpanded.CompReloadableWithVerbs"); 31 | MP.RegisterSyncMethod(type, "TryReload"); 32 | // (Dev) reload to full 33 | MpCompat.RegisterLambdaMethod(type, nameof(ThingComp.CompGetGizmosExtra), 0).SetDebugOnly(); 34 | 35 | // Restore wreck 36 | type = AccessTools.TypeByName("VanillaVehiclesExpanded.CompVehicleWreck"); 37 | // Restore (0)/cancel (1) 38 | MpCompat.RegisterLambdaDelegate(type, nameof(ThingComp.CompGetGizmosExtra), 0, 1); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Source/Mods/DubsCentralHeating.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Dubs Central Heating by Dubwise 8 | /// 9 | /// 10 | [MpCompatFor("Dubwise.DubsCentralHeating")] 11 | public class DubsCentralHeating 12 | { 13 | private static AccessTools.FieldRef removePlumbingRemovalModeField; 14 | 15 | public DubsCentralHeating(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(LatePatch); 16 | 17 | private static void LatePatch() 18 | { 19 | // Designators 20 | // Same patch as Bad Hygiene, different namespace. 21 | var type = AccessTools.TypeByName("DubsCentralHeating.Designator_RemovePlumbing"); 22 | removePlumbingRemovalModeField = AccessTools.FieldRefAccess(type, "RemovalMode"); 23 | MP.RegisterSyncWorker(SyncRemovePlumbingDesignator, type, shouldConstruct: true); 24 | 25 | // Patch all methods in the mod. 26 | // It looks like it has all the required methods marked with SyncMethod attribute. It just never registers it. 27 | // This is likely due to the mod source code being based on Bad Hygiene, just with unnecessary stuff stripped away. 28 | MP.RegisterAll(type.Assembly); 29 | } 30 | 31 | private static void SyncRemovePlumbingDesignator(SyncWorker sync, ref Designator designator) 32 | { 33 | if (sync.isWriting) 34 | sync.Write(removePlumbingRemovalModeField(designator)); 35 | else 36 | removePlumbingRemovalModeField(designator) = sync.Read(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Source/Mods/DubsMintMenu.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Dubs Mint Menus by Dubwise 8 | /// 9 | [MpCompatFor("Dubwise.DubsMintMenus")] 10 | internal class DubsMintMenu 11 | { 12 | public DubsMintMenu(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(() => Patch("DubsMintMenus.MP_Util")); 13 | 14 | public static void Patch(string name) 15 | { 16 | var modType = AccessTools.TypeByName(name); 17 | 18 | var mpSyncMethod = AccessTools.TypeByName("Multiplayer.Client.SyncMethod"); 19 | AccessTools.Field(modType, "RegisterRef").SetValue(null, AccessTools.Method(mpSyncMethod, "Register")); 20 | 21 | var mpSyncUtil = AccessTools.TypeByName("Multiplayer.Client.SyncFieldUtil"); 22 | AccessTools.Field(modType, "FieldWatchPrefixRef").SetValue(null, AccessTools.Method(mpSyncUtil, "FieldWatchPrefix")); 23 | AccessTools.Field(modType, "FieldWatchPostfixRef").SetValue(null, AccessTools.Method(mpSyncUtil, "FieldWatchPostfix")); 24 | 25 | var mpSyncField = AccessTools.TypeByName("Multiplayer.Client.SyncField"); 26 | AccessTools.Field(modType, "WatchRef").SetValue(null, AccessTools.Method(mpSyncField, "Watch")); 27 | AccessTools.Field(modType, "SetBufferChangesRef").SetValue(null, AccessTools.Method(mpSyncField, "SetBufferChanges")); 28 | 29 | var mpSync = AccessTools.TypeByName("Multiplayer.Client.Sync"); 30 | AccessTools.Field(modType, "FieldRef").SetValue(null, AccessTools.Method(mpSync, "Field", new[] { typeof(Type), typeof(string) })); 31 | 32 | AccessTools.Method(modType, "asshai").Invoke(null, Array.Empty()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Mods/Immortals.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using System; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Immortals by fridgeBaron 8 | /// 9 | /// contribution to Multiplayer Compatibility by Reshiram and Sokyran 10 | [MpCompatFor("fridgeBaron.Immortals")] 11 | class Immortals 12 | { 13 | private static Type immortalComponent; 14 | private static bool shouldCall = false; 15 | 16 | public Immortals(ModContentPack mod) 17 | { 18 | immortalComponent = AccessTools.TypeByName("Immortals.Immortal_Component"); 19 | MpCompat.harmony.Patch(AccessTools.Method("Immortals.Immortal_Component:GameComponentUpdate"), prefix: new HarmonyMethod(typeof(Immortals), nameof(GameComponentUpdatePrefix))); 20 | MpCompat.harmony.Patch(AccessTools.Method("Verse.GameComponent:GameComponentTick"), prefix: new HarmonyMethod(typeof(Immortals), nameof(GameComponentTickPrefix))); 21 | } 22 | 23 | //Patch GameComponentTick to Call GameComponentUpdate instead, shouldCall true to make it actually run 24 | private static void GameComponentTickPrefix(GameComponent __instance) 25 | { 26 | //Check if it's our component - ImmortalComponent does not override this method 27 | if (__instance.GetType() == immortalComponent) 28 | { 29 | shouldCall = true; 30 | __instance.GameComponentUpdate(); 31 | shouldCall = false; 32 | } 33 | } 34 | 35 | //Patch GameComponentUpdate to not be called by itself which desyncs, but only when we call it from GameComponentTick, who the hell uses that anyways besides debug??? 36 | private static bool GameComponentUpdatePrefix() 37 | { 38 | return shouldCall; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Source/Mods/AutoExtractGenes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using HarmonyLib; 4 | using Multiplayer.API; 5 | using RimWorld; 6 | using Verse; 7 | 8 | namespace Multiplayer.Compat 9 | { 10 | /// Auto-Extract Genes by Nibato 11 | /// 12 | /// 13 | [MpCompatFor("Nibato.AutoExtractGenes")] 14 | public class AutoExtractGenes 15 | { 16 | private static Type autoExtractGenesCompType; 17 | private static ISyncField autoExtractGenesField; 18 | 19 | public AutoExtractGenes(ModContentPack mod) 20 | { 21 | autoExtractGenesCompType = AccessTools.TypeByName("AutoExtractGenes.AutoExtractGenesComp"); 22 | autoExtractGenesField = MP.RegisterSyncField(autoExtractGenesCompType, "isEnabled"); 23 | 24 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod(typeof(HealthCardUtility), nameof(HealthCardUtility.DrawOverviewTab)), 25 | prefix: new HarmonyMethod(typeof(AutoExtractGenes), nameof(PreFillTab)), 26 | finalizer: new HarmonyMethod(typeof(AutoExtractGenes), nameof(PostFillTab))); 27 | } 28 | 29 | private static void PreFillTab(Pawn pawn) 30 | { 31 | if (!MP.IsInMultiplayer) 32 | { 33 | return; 34 | } 35 | MP.WatchBegin(); 36 | var comp = pawn.comps.SingleOrDefault(comp => comp.GetType() == autoExtractGenesCompType); 37 | if (comp != null) 38 | { 39 | autoExtractGenesField.Watch(comp); 40 | } 41 | } 42 | 43 | private static void PostFillTab() 44 | { 45 | if (!MP.IsInMultiplayer) 46 | { 47 | return; 48 | } 49 | MP.WatchEnd(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Source/Mods/QualityBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | using RimWorld; 6 | using System.Reflection; 7 | 8 | namespace Multiplayer.Compat 9 | { 10 | /// Quality Builder by Hatti 11 | /// 12 | [MpCompatFor("hatti.qualitybuilder")] 13 | class QualityBuilder 14 | { 15 | public QualityBuilder(ModContentPack mod) 16 | { 17 | Type builderCompType = AccessTools.TypeByName("QualityBuilder.CompQualityBuilder"); 18 | Type toggleCommandType = AccessTools.TypeByName("QualityBuilder.CompQualityBuilder+ToggleCommand+<>c"); 19 | Type designatorType = AccessTools.TypeByName("QualityBuilder._Designator_SkilledBuilder+<>c"); 20 | 21 | Type[] argTypes = { typeof(QualityCategory) }; 22 | 23 | MethodInfo toggleCommandMethod = MpMethodUtil.GetFirstMethodBySignature(toggleCommandType, argTypes); 24 | MethodInfo designatorMethod = MpMethodUtil.GetFirstMethodBySignature(designatorType, argTypes); 25 | 26 | //my decompiler shows the method name is b__3_0 27 | //while harmony saids it is b__2_0, weird..." 28 | 29 | MP.RegisterSyncWorker(SyncTypes, toggleCommandType); 30 | MP.RegisterSyncWorker(SyncTypes, designatorType); 31 | 32 | MP.RegisterSyncMethod(builderCompType, "ToggleSkilled"); 33 | MP.RegisterSyncMethod(toggleCommandMethod).SetContext(SyncContext.MapSelected); 34 | MP.RegisterSyncMethod(designatorMethod); 35 | 36 | } 37 | 38 | void SyncTypes(SyncWorker sync, ref object c) 39 | { 40 | if (sync.isWriting) 41 | { 42 | sync.Write(c.GetType()); 43 | } else 44 | { 45 | c = Activator.CreateInstance(sync.Read()); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/Mods/RimEffectCore.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Rim-Effect: Core by Vanilla Expanded Team and Co. 9 | /// 10 | /// 11 | [MpCompatFor("RimEffect.Core")] 12 | public class RimEffectCore 13 | { 14 | private static ConstructorInfo doorValueCtor; 15 | private static FieldInfo doorValueKeyField; 16 | 17 | public RimEffectCore(ModContentPack mod) 18 | { 19 | // May need patching: 20 | // AddHumanlikeOrders_Patch - free hostages 21 | // RimEffect.CompSpawnOnInteract - open 22 | 23 | MpCompat.RegisterLambdaMethod("RimEffect.AmmoBelt", "GetWornGizmos", 1); 24 | 25 | var autoDoorType = AccessTools.TypeByName("RimEffect.Building_AutoDoorLockable"); 26 | var method = MpMethodUtil.GetLambda(autoDoorType, "GetGizmos", lambdaOrdinal: 1); 27 | var field = AccessTools.Field(method.DeclaringType, "doorState"); 28 | var doorValueType = field.FieldType; 29 | var genericTypes = doorValueType.GetGenericArguments(); 30 | doorValueCtor = AccessTools.DeclaredConstructor(doorValueType, new[] { genericTypes[0], genericTypes[1] }); 31 | doorValueKeyField = AccessTools.Field(doorValueType, "key"); 32 | 33 | MP.RegisterSyncDelegate(autoDoorType, method.DeclaringType.Name, method.Name); 34 | MP.RegisterSyncWorker(SyncDoorValue, doorValueType); 35 | } 36 | 37 | private static void SyncDoorValue(SyncWorker sync, ref object doorValue) 38 | { 39 | if (sync.isWriting) 40 | sync.Write((int)doorValueKeyField.GetValue(doorValue)); 41 | else 42 | doorValue = doorValueCtor.Invoke(new object[] { sync.Read(), string.Empty }); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaRacesSanguophage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using HarmonyLib; 4 | using Multiplayer.API; 5 | using RimWorld; 6 | using Verse; 7 | 8 | namespace Multiplayer.Compat 9 | { 10 | /// Vanilla Races Expanded - Sanguophage by Oskar Potocki, Sarg Bjornson, Erin 11 | /// 12 | /// 13 | [MpCompatFor("vanillaracesexpanded.sanguophage")] 14 | public class VanillaRacesSanguophage 15 | { 16 | private static Type singleUseAbilitiesCommandType; 17 | private static AccessTools.FieldRef singleUseAbilitiesCommandBuildingField; 18 | 19 | public VanillaRacesSanguophage(ModContentPack mod) 20 | { 21 | var type = singleUseAbilitiesCommandType = AccessTools.TypeByName("VanillaRacesExpandedSanguophage.Command_SingleUseAbilities"); 22 | singleUseAbilitiesCommandBuildingField = AccessTools.FieldRefAccess(type, "building"); 23 | MP.RegisterSyncWorker(SyncSingleUseAbilitiesCommand, type); 24 | MpCompat.RegisterLambdaDelegate(type, "ProcessInput", 0); 25 | 26 | type = AccessTools.TypeByName("VanillaRacesExpandedSanguophage.CompDraincasket"); 27 | // Called from b__47_0 28 | MP.RegisterSyncMethod(type, "EjectContents"); 29 | // Creates a job (should be handled through MP), and operates on the comp (which we need to sync) 30 | MpCompat.RegisterLambdaDelegate(type, "AddCarryToBatteryJobs", 0); 31 | } 32 | 33 | private static void SyncSingleUseAbilitiesCommand(SyncWorker sync, ref Command command) 34 | { 35 | if (sync.isWriting) 36 | sync.Write(singleUseAbilitiesCommandBuildingField(command)); 37 | else 38 | command = (Command)Activator.CreateInstance(singleUseAbilitiesCommandType, sync.Read()); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Source_Referenced/Multiplayer_Compat_Referenced.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net48 5 | 12 6 | false 7 | false 8 | false 9 | ..\Referenced\ 10 | false 11 | false 12 | None 13 | Multiplayer.Compat 14 | 15 | None 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 2.3.6 23 | runtime 24 | 25 | 26 | 0.6.0 27 | runtime 28 | 29 | 30 | 1.6.* 31 | runtime 32 | 33 | 34 | 2.3.* 35 | all 36 | 37 | 38 | false 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Source/Mods/EccentricTechFusionPower.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Eccentric Tech - Fusion Power by Aelanna 9 | /// 10 | [MpCompatFor("Aelanna.EccentricTech.FusionPower")] 11 | internal class EccentricTechFusionPower 12 | { 13 | public EccentricTechFusionPower(ModContentPack mod) 14 | { 15 | // RNG 16 | { 17 | var type = AccessTools.TypeByName("EccentricPower.CompFusionGenerator"); 18 | PatchingUtilities.PatchSystemRand(AccessTools.Method(type, "generateNewFailurePoints"), false); 19 | PatchingUtilities.PatchSystemRand(AccessTools.Method(type, "doEmergencyVentFireDamage"), true); 20 | } 21 | 22 | // Gizmos 23 | { 24 | var type = AccessTools.TypeByName("EccentricPower.CompFusionGenerator"); 25 | MP.RegisterSyncMethod(type, "SetDesiredOutputLevel"); 26 | MP.RegisterSyncMethod(type, "setPowerMode"); 27 | MP.RegisterSyncMethod(type, "StartIgnitionCycle"); 28 | MP.RegisterSyncMethod(type, "StartShutdownCycle"); 29 | MP.RegisterSyncMethod(type, "StartEmergencyVent"); 30 | MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", Enumerable.Range(3, 5).ToArray()).SetDebugOnly(); // 3 to 7 31 | } 32 | 33 | LongEventHandler.ExecuteWhenFinished(LatePatch); 34 | } 35 | 36 | public static void LatePatch() 37 | { 38 | var type = AccessTools.TypeByName("EccentricPower.CompFusionCapacitor"); 39 | MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", 0, 1, 2).SetDebugOnly(); 40 | 41 | type = AccessTools.TypeByName("EccentricPower.CompFusionStorage"); 42 | MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", 1, 3, 4, 5, 6).Skip(2).SetDebugOnly(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/Mods/AutomaticParking.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat; 7 | 8 | /// Automatic Parking by rabiosus 9 | /// 10 | [MpCompatFor("rabiosus.vfautoparking")] 11 | public class AutomaticParking 12 | { 13 | // CompParkingController 14 | private static Type parkingControllerCompType; 15 | private static AccessTools.FieldRef parkingControllerCompControllerField; 16 | // ParkingController 17 | private static AccessTools.FieldRef parkingControllerVehicleField; 18 | 19 | public AutomaticParking(ModContentPack mod) 20 | { 21 | MpCompatPatchLoader.LoadPatch(this); 22 | 23 | // CompParkingController 24 | var type = parkingControllerCompType = AccessTools.TypeByName("VehicleAutoParking.Core.CompParkingController"); 25 | parkingControllerCompControllerField = AccessTools.FieldRefAccess(type, "parkingController"); 26 | // ParkingController 27 | type = AccessTools.TypeByName("VehicleAutoParking.Core.ParkingController"); 28 | parkingControllerVehicleField = AccessTools.FieldRefAccess(type, "vehicle"); 29 | MP.RegisterSyncMethod(type, "SaveParkingPosition"); 30 | MP.RegisterSyncMethod(type, "ResetParkingPosition"); 31 | MP.RegisterSyncMethod(type, "MoveToParkingPosition"); 32 | } 33 | 34 | [MpCompatSyncWorker("VehicleAutoParking.Core.ParkingController")] 35 | private static void SyncParkingController(SyncWorker sync, ref object controller) 36 | { 37 | if (sync.isWriting) 38 | { 39 | sync.Write(parkingControllerVehicleField(controller)); 40 | } 41 | else 42 | { 43 | var comp = sync.Read().AllComps.FirstOrDefault(c => parkingControllerCompType.IsInstanceOfType(c)); 44 | if (comp != null) 45 | controller = parkingControllerCompControllerField(comp); 46 | else 47 | Log.Error("A vehicle is missing CompParkingController."); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Source/Mods/HospitalityCasino.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// 9 | /// Hospitality: Casino by Adamas 10 | /// 11 | /// 12 | /// 13 | [MpCompatFor("Adamas.HospitalityCasino")] 14 | public class HospitalityCasino 15 | { 16 | public HospitalityCasino(ModContentPack mod) 17 | { 18 | InitializeGizmos("HospitalityCasino"); 19 | 20 | // The method is called from SlotMachineComp.CompTick and calls ThingMaker.MakeThing. 21 | // The method itself is only called once (sets initialized to true and skips execution 22 | // if true). Due to the method making a thing during ticking, but max only once per 23 | // game launch, it'll cause issues with MP when someone joins a game with already built 24 | // slot machines. Making the thing will use up thing IDs in Thing.PostMake (and may 25 | // potentially result in RNG calls, but doesn't seem like it's the case this time). 26 | // Anyway, just ensure the textures are initialized on game launch instead of when 27 | // playing the game to prevent issues (seems like making them during startup is safe). 28 | LongEventHandler.ExecuteWhenFinished( 29 | () => AccessTools.DeclaredMethod("HospitalityCasino.HospitalityCasinoMod:InitialiseTextures") 30 | .Invoke(null, Array.Empty())); 31 | } 32 | 33 | public static void InitializeGizmos(string targetNamespace) 34 | { 35 | var type = AccessTools.TypeByName($"{targetNamespace}.CompVendingMachine"); 36 | 37 | // Gizmos 38 | MP.RegisterSyncMethod(type, "ToggleActive"); 39 | MP.RegisterSyncMethod(type, "SetPricing"); 40 | // The setter is never used, but may as well sync it in case it's ever used in the future. 41 | MP.RegisterSyncMethod(type, "Pricing"); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Source/Mods/ReinforcedMechanoids2.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Reinforced Mechanoid 2 by Helixien, Taranchuk, OskarPotocki, Luizi, IcingWithCheeseCake, Sir Van, Reann_Shepard 9 | /// 10 | /// 11 | [MpCompatFor("hlx.ReinforcedMechanoids2")] 12 | public class ReinforcedMechanoids2 13 | { 14 | public ReinforcedMechanoids2(ModContentPack mod) 15 | { 16 | // Called from CompCauseGameCondition_ForceWeather and CommandAction_RightClickWeather 17 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("ReinforcedMechanoids.CompCauseGameCondition_ForceWeather:ChangeWeather")); 18 | // Called from CompCauseGameCondition_TemperatureOffset 19 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("ReinforcedMechanoids.CompCauseGameCondition_TemperatureOffset:SetTemperatureOffset")); 20 | // Called from Targeter called from gizmo 21 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("ReinforcedMechanoids.CompPawnJumper:DoJump", new []{ typeof(LocalTargetInfo) })); 22 | 23 | // Toggle overlay on/off 24 | MpCompat.RegisterLambdaDelegate("ReinforcedMechanoids.CompAllianceOverlayToggle", "CompGetGizmosExtra", 1); 25 | // Toggle invisibility on/off 26 | MpCompat.RegisterLambdaMethod("ReinforcedMechanoids.CompInvisibility", "CompGetGizmosExtra", 1); 27 | 28 | // ReinforcedMechanoids.Building_Container:GetMultiSelectFloatMenuOptions has a delegate that will need syncing, 29 | // but as of right now - it's broken in the RM2 mod itself and causes ArgumentOutOfRangeException 30 | // First it clears tmpAllowedPawns list, and then tries to access its values 31 | // https://github.com/Helixien/ReinforcedMechanoids-2/blob/4772309ab7329e94c8c4cc5d1d40f38946200308/1.4/Source/ReinforcedMechanoids/ReinforcedMechanoids/Comps/CompLootContainer.cs#L245 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaEventsExpanded.cs: -------------------------------------------------------------------------------- 1 | using RimWorld; 2 | using Verse; 3 | 4 | namespace Multiplayer.Compat 5 | { 6 | /// Vanilla Events Expanded by Oskar Potocki, Helixien, Kikohi 7 | /// 8 | /// 9 | /// Contribution to Multiplayer Compatibility by Sokyran and Reshiram 10 | [MpCompatFor("VanillaExpanded.VEE")] 11 | class VEE 12 | { 13 | public VEE(ModContentPack mod) 14 | { 15 | MpSyncWorkers.Requires(); 16 | 17 | var methodsForAll = new[] 18 | { 19 | "VEE.RegularEvents.ApparelPod:TryExecuteWorker", 20 | "VEE.RegularEvents.CaravanAnimalWI:GenerateGroup", 21 | "VEE.RegularEvents.MeteoriteShower:TryExecuteWorker", 22 | "VEE.RegularEvents.WeaponPod:TryExecuteWorker", 23 | "VEE.RegularEvents.EarthQuake:DamageInRadius", 24 | }; 25 | 26 | PatchingUtilities.PatchSystemRand(methodsForAll, false); 27 | 28 | // Unity RNG 29 | PatchingUtilities.PatchUnityRand("VEE.Shuttle:Tick", false); 30 | 31 | // Current map usage, picks between rain and snow based on current map temperature, instead of using map it affects 32 | PatchingUtilities.ReplaceCurrentMapUsage("VEE.PurpleEvents.PsychicRain:ForcedWeather"); 33 | 34 | // Reset game conditions - technically does not require debug mode, 35 | // but lets you end (almost?) any game condition at any time 36 | // so I'd consider it close enough to justify `SetDebugOnly` on it. 37 | MpCompat.RegisterLambdaDelegate("VEE.Settings.VEESettings", "ResetWorldCondButton", 0).SetDebugOnly(); 38 | MpCompat.RegisterLambdaDelegate("VEE.Settings.VEESettings", "ResetMapCondButton", 0).SetDebugOnly(); 39 | 40 | LongEventHandler.ExecuteWhenFinished(LatePatch); 41 | } 42 | 43 | public static void LatePatch() => PatchingUtilities.PatchSystemRand("VEE.RegularEvents.SpaceBattle:GameConditionTick", false); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/Mods/AncientMiningIndustry.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat; 6 | 7 | /// Ancient mining industry by MO 8 | /// 9 | [MpCompatFor("XMB.AncientMiningIndustry.MO")] 10 | public class AncientMiningIndustry 11 | { 12 | private static SyncType questScriptPairType; 13 | 14 | public AncientMiningIndustry(ModContentPack mod) 15 | { 16 | // Turn on/off (0), build walls on/off (1) 17 | MpCompat.RegisterLambdaMethod("AncientMining.Building_BoringMachine", nameof(Thing.GetGizmos), 0, 1); 18 | 19 | // Dev produce portion (0), shutdown (1) 20 | MpCompat.RegisterLambdaMethod("AncientMining.CompDeepDrillAutomated", nameof(ThingComp.CompGetGizmosExtra), 0, 1).SetDebugOnly(); 21 | 22 | // No quest (1), specific quest (3) 23 | MpCompat.RegisterLambdaDelegate("AncientMining.CompQuestScanner", nameof(ThingComp.CompGetGizmosExtra), 1, 3).SetContext(SyncContext.MapSelected); 24 | 25 | // A bit of a "hacky" solution. It'll sync the field by exposing it, instead of setting it 26 | // to a specific reference from the comp's props. However, due to the way it's handled, it 27 | // should work completely fine - the reference itself doesn't matter, only its contents. 28 | // An alternative would be to make a sync worker that searches all `ThingDef`s for quest 29 | // scanner props, checks if it contains that specific item in its list of possible quests, 30 | // and then syncs the def and position of that object on the list. 31 | // Side note: this could be done using ISyncDelegate.ExposeFields - however, 32 | // seems I may have left a bug behind in that code, so it'll need to be fixed first. 33 | questScriptPairType = AccessTools.TypeByName("AncientMining.QuestScriptTexPathPair"); 34 | questScriptPairType.expose = true; 35 | 36 | MP.RegisterSyncWorker(SyncQuestScriptTexPathPair, questScriptPairType.type); 37 | } 38 | 39 | private static void SyncQuestScriptTexPathPair(SyncWorker sync, ref object obj) => sync.Bind(ref obj, questScriptPairType); 40 | } -------------------------------------------------------------------------------- /Source/Mods/ExtendedBioengineeringForVFEInsectoids.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Extended Bioengineering for VFE Insectoids by Turnovus 8 | /// 9 | [MpCompatFor("turnovus.submod.extendedbioengineering")] 10 | public class ExtendedBioengineeringForVFEInsectoids 11 | { 12 | public ExtendedBioengineeringForVFEInsectoids(ModContentPack mod) 13 | { 14 | // Gizmos 15 | { 16 | var type = AccessTools.TypeByName("extendedbioengineering.Building_Inducer"); 17 | MP.RegisterSyncMethod(type, "TryFireInducerNow"); 18 | MP.RegisterSyncMethod(type, "SetCountdownToNearZero").SetDebugOnly(); 19 | 20 | // Unused - artificial hive is not included in the mod 21 | // MP.RegisterSyncMethod(AccessTools.Method("extendedbioengineering.Building_ArtificialHive:TryRequestNewCulture")); 22 | } 23 | 24 | // RNG 25 | { 26 | var type = AccessTools.TypeByName("extendedbioengineering.OutputWorker_Dissect"); 27 | type = AccessTools.FirstInner(type, _ => true); // There should be only 1 inner class 28 | PatchingUtilities.PatchSystemRand(AccessTools.Method(type, "MoveNext"), false); 29 | 30 | type = AccessTools.TypeByName("extendedbioengineering.OutputWorker_Genes"); 31 | type = AccessTools.FirstInner(type, _ => true); // There should be only 1 inner class 32 | PatchingUtilities.PatchSystemRand(AccessTools.Method(type, "MoveNext"), false); 33 | } 34 | 35 | LongEventHandler.ExecuteWhenFinished(LatePatch); 36 | } 37 | 38 | private static void LatePatch() 39 | { 40 | // Gizmos 41 | { 42 | var type = AccessTools.TypeByName("extendedbioengineering.ThingComp_JellySynthesizer"); 43 | 44 | MP.RegisterSyncMethod(type, "TrySetNewSetting"); 45 | MP.RegisterSyncMethod(AccessTools.PropertySetter(type, "TargetFuel")); 46 | MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", 1); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Source/Mods/BiomesCore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Biomes! Core by The Biomes Mod Team 9 | /// 10 | /// 11 | [MpCompatFor("BiomesTeam.BiomesCore")] 12 | public class BiomesCore 13 | { 14 | public BiomesCore(ModContentPack mod) 15 | { 16 | // Gizmos (only dev mode gizmos) 17 | { 18 | var typeNames = new[] 19 | { 20 | "BiomesCore.CompHarvestAnimalProduct", 21 | 22 | "BiomesCore.ThingComponents.CompAnimalsAroundThing", 23 | "BiomesCore.ThingComponents.CompAnimalThingSpawner", 24 | }; 25 | 26 | foreach (var type in typeNames) 27 | MpCompat.RegisterLambdaMethod(type, nameof(ThingComp.CompGetGizmosExtra), 0).SetDebugOnly(); 28 | 29 | var methodNames = new[] 30 | { 31 | "BiomesCore.CompCleanFilthAround:ClearFilth", 32 | "BiomesCore.CompPlantPolluteOverTime:Pollute", 33 | "BiomesCore.CompPlantProximityExplosive:Detonate", 34 | }; 35 | 36 | foreach (var method in methodNames) 37 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod(method)).SetDebugOnly(); 38 | } 39 | 40 | // Current map usage 41 | { 42 | PatchingUtilities.ReplaceCurrentMapUsage("BiomesCore.GameCondition_Earthquake:GameConditionTick"); 43 | PatchingUtilities.ReplaceCurrentMapUsage("BiomesCore.IncidentWorker_Earthquake:TryExecuteWorker"); 44 | } 45 | 46 | // RNG + GenView.ShouldSpawnMotesAt 47 | { 48 | PatchingUtilities.PatchSystemRand(new[] 49 | { 50 | // Two 'new Random()' calls, one is never used 51 | "BiomesCore.GenSteps.ValleyPatch:Postfix", 52 | }, false); 53 | 54 | PatchingUtilities.PatchPushPopRand("BiomesCore.CompPlantReleaseSpore:ThrowPoisonSmoke"); 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Source/Mods/AvoidFriendlyFire.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | using HarmonyLib; 5 | using Multiplayer.API; 6 | using Verse; 7 | 8 | namespace Multiplayer.Compat 9 | { 10 | 11 | /// Avoid Friendly Fire by Falconne 12 | /// 13 | /// 14 | [MpCompatFor("falconne.AFF")] 15 | public class AvoidFriendlyFire 16 | { 17 | static IDictionary extendedPawnDataDictionary; 18 | 19 | public AvoidFriendlyFire(ModContentPack mod) 20 | { 21 | { 22 | var type = AccessTools.TypeByName("AvoidFriendlyFire.ExtendedPawnData"); 23 | MP.RegisterSyncWorker(SyncWorkerFor, type); 24 | } 25 | { 26 | MpCompat.RegisterLambdaDelegate("AvoidFriendlyFire.Pawn_DraftController_GetGizmos_Patch", "Postfix", 1); 27 | } 28 | } 29 | 30 | static IDictionary ExtendedPawnDataDictionary { 31 | get { 32 | if (extendedPawnDataDictionary == null) { 33 | Type type = AccessTools.TypeByName("AvoidFriendlyFire.ExtendedDataStorage"); 34 | 35 | var comp = Find.World.GetComponent(type); 36 | 37 | extendedPawnDataDictionary = AccessTools.Field(type, "_store").GetValue(comp) as IDictionary; 38 | } 39 | 40 | return extendedPawnDataDictionary; 41 | } 42 | } 43 | 44 | static int GetIdFromExtendedPawnData(object extendedPawnData) { 45 | foreach(object key in ExtendedPawnDataDictionary.Keys) 46 | { 47 | if (ExtendedPawnDataDictionary[key] == extendedPawnData) { 48 | return (int) key; 49 | } 50 | } 51 | 52 | return 0; 53 | } 54 | 55 | static void SyncWorkerFor(SyncWorker sw, ref object extendedPawnData) 56 | { 57 | if (sw.isWriting) { 58 | sw.Write(GetIdFromExtendedPawnData(extendedPawnData)); 59 | } else { 60 | int id = sw.Read(); 61 | 62 | extendedPawnData = ExtendedPawnDataDictionary[id]; 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Source/Mods/VanillaTraitsExpanded.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Vanilla Traits Expanded by Oskar Potocki, Taranchuk, Chowder 9 | /// 10 | /// 11 | [MpCompatFor("VanillaExpanded.VanillaTraitsExpanded")] 12 | public class VanillaTraitsExpanded 13 | { 14 | private static AccessTools.FieldRef mapPawnsAnimalListField; 15 | 16 | public VanillaTraitsExpanded(ModContentPack mod) 17 | { 18 | LongEventHandler.ExecuteWhenFinished(LatePatch); 19 | 20 | // Clear cache 21 | { 22 | // List of animals on current map for menagerist trait 23 | var field = AccessTools.DeclaredField("VanillaTraitsExpanded.ThoughtWorker_AnimalsInColony:mapPawns"); 24 | mapPawnsAnimalListField = AccessTools.StaticFieldRefAccess(field); 25 | 26 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod(typeof(GameComponentUtility), nameof(GameComponentUtility.FinalizeInit)), 27 | postfix: new HarmonyMethod(typeof(VanillaTraitsExpanded), nameof(ClearCache))); 28 | } 29 | } 30 | 31 | private static void LatePatch() 32 | { 33 | // Stop doing stuff with absent minded/rebel/submissive pawns outside of synced context 34 | { 35 | // Basically, this method adds/removes slow work speed hediff to/from rebel/submissive pawns. 36 | // On top of that, it also adds the current job to the list of forced jobs for absent minded pawns. 37 | // This can cause issues when the mod operates and does stuff based on those, but they are in different state for all players. 38 | // The issue is that a sync method catches this call, but all prefixes/postfixes/etc. still run. 39 | PatchingUtilities.PatchCancelInInterface(AccessTools.DeclaredMethod("VanillaTraitsExpanded.TryTakeOrderedJob_Patch:Postfix")); 40 | } 41 | } 42 | 43 | private static void ClearCache() => mapPawnsAnimalListField().Clear(); 44 | } 45 | } -------------------------------------------------------------------------------- /Source/Mods/RPGStyleInventory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | using RimWorld; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// RPG Style Inventory by Sandy 10 | /// 11 | /// 12 | /// 13 | /// 14 | [MpCompatFor("Sandy.RPGStyleInventory")] 15 | [MpCompatFor("Nykot.RPGStyleInventory")] 16 | [MpCompatFor("Sandy.RPGStyleInventory.avilmask.Revamped")] 17 | class RPGStyleInventory 18 | { 19 | private static Type tabType; 20 | 21 | public RPGStyleInventory(ModContentPack mod) 22 | { 23 | tabType = AccessTools.TypeByName("Sandy_Detailed_RPG_Inventory.Sandy_Detailed_RPG_GearTab"); 24 | 25 | MP.RegisterSyncWorker(SyncITab, tabType); 26 | 27 | // Original re-implemented InterfaceDrop/InterfaceIngest, but some stopped doing that. 28 | // Sync those methods if they're declared, but skip if they aren't since we don't want 29 | // to sync vanilla methods (which are already synced). On top of that, vanilla marked 30 | // InterfaceIngest as obsolete (use FoodUtility.IngestFromInventoryNow instead), 31 | // so avoid HugsLib warnings about patching obsolete methods. 32 | foreach (var methodName in new[] { "InterfaceDrop", "InterfaceIngest" }) 33 | { 34 | var method = AccessTools.DeclaredMethod(tabType, methodName); 35 | if (method != null) 36 | MP.RegisterSyncMethod(method).SetContext(SyncContext.MapSelected); 37 | } 38 | 39 | // Remove/add forced apparel 40 | if (mod.PackageId == "Sandy.RPGStyleInventory.avilmask.Revamped".ToLower()) { 41 | MpCompat.RegisterLambdaDelegate(tabType, "PopupMenu", 0, 1); 42 | } 43 | } 44 | 45 | private static void SyncITab(SyncWorker sync, ref ITab_Pawn_Gear gearITab) 46 | { 47 | if (!sync.isWriting) 48 | gearITab = Activator.CreateInstance(tabType) as ITab_Pawn_Gear; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Source/Mods/TimeOfDAySwitch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using RimWorld; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Time of day switches by merthsoft 9 | /// 10 | /// 11 | [MpCompatFor("Merthsoft.TimeOfDaySwitch")] 12 | class TimeOfDAySwitch 13 | { 14 | // Temporary states. When pasting will temporarily store synced states, 15 | // when executing the paste method it will temporarily store current 16 | // player's copied state, and restore it after executing. 17 | private static bool[] tempState; 18 | private static AccessTools.FieldRef copiedStatesField; 19 | 20 | public TimeOfDAySwitch(ModContentPack mod) 21 | { 22 | var type = AccessTools.TypeByName("Merthsoft.TimerSwitches.TimeOfDaySwitch"); 23 | MP.RegisterSyncMethod(type, "SetState"); 24 | // Pasting requires us to also sync the copied state which will be pasted, 25 | // so we need to transform the target to sync it as well. 26 | MP.RegisterSyncMethod(type, "Paste") 27 | .SetPreInvoke(PrePaste) 28 | .SetPostInvoke(PostPaste) 29 | .TransformTarget(Serializer.New( 30 | (Building_PowerSwitch building) => (building, states: copiedStatesField()), 31 | tuple => 32 | { 33 | tempState = tuple.states; 34 | return tuple.building; 35 | }), true); 36 | 37 | // Field ref for accessing the copied state, we'll need to change those when copy/pasting stuff. 38 | copiedStatesField = AccessTools.StaticFieldRefAccess( 39 | AccessTools.DeclaredField("Merthsoft.TimerSwitches.ClipBoard:states")); 40 | } 41 | 42 | private static void PrePaste(object instance, object[] args) 43 | { 44 | ref var state = ref copiedStatesField(); 45 | (state, tempState) = (tempState, state); 46 | } 47 | 48 | private static void PostPaste(object instance, object[] args) 49 | { 50 | copiedStatesField() = tempState; 51 | tempState = null; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Source/Mods/MiscRobots.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using Verse; 7 | 8 | namespace Multiplayer.Compat 9 | { 10 | /// Misc. Robots by HaploX1 11 | /// 12 | [MpCompatFor("Haplo.Miscellaneous.Robots")] 13 | class MiscRobots 14 | { 15 | public MiscRobots(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(LatePatch); 16 | private static void LatePatch() 17 | { 18 | Type rechargestationType = AccessTools.TypeByName("AIRobot.X2_Building_AIRobotRechargeStation"); 19 | Type robotType = AccessTools.TypeByName("AIRobot.X2_AIRobot"); 20 | Type robothelperType = AccessTools.TypeByName("AIRobot.AIRobot_Helper"); 21 | 22 | MP.RegisterSyncMethod(rechargestationType, "Button_CallAllBotsForShutdown"); 23 | MP.RegisterSyncMethod(rechargestationType, "Button_CallBotForShutdown"); 24 | MP.RegisterSyncMethod(rechargestationType, "Button_RequestRepair4Robot"); 25 | MP.RegisterSyncMethod(rechargestationType, "Button_RepairDamagedRobot").SetDebugOnly(); 26 | MP.RegisterSyncMethod(rechargestationType, "Button_ResetDestroyedRobot").SetDebugOnly(); 27 | MP.RegisterSyncMethod(rechargestationType, "Button_SpawnAllAvailableBots"); 28 | MP.RegisterSyncMethod(rechargestationType, "Button_SpawnBot"); 29 | 30 | //Might as well sync the arrow gizmos in case some one actually click on it, because it is accessible by users,but not affecting gameplay 31 | MP.RegisterSyncMethod(robotType, "Debug_ForceGotoDistance"); 32 | MP.RegisterSyncMethod(robotType, "Debug_Info"); 33 | 34 | MP.RegisterSyncMethod(rechargestationType, "AddRobotToContainer").SetContext(SyncContext.CurrentMap); 35 | 36 | PatchingUtilities.PatchPushPopRand("AIRobot.MoteThrowHelper:ThrowBatteryXYZ"); 37 | PatchingUtilities.PatchPushPopRand("AIRobot.MoteThrowHelper:ThrowNoRobotSign"); 38 | 39 | //Rand.value access 40 | PatchingUtilities.PatchPushPopRand("AIRobot.X2_Building_AIRobotRechargeStation:TryHealDamagedBodyPartOfRobot"); 41 | 42 | MP.RegisterSyncMethod(robothelperType, "StartStationRepairJob"); 43 | 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/Mods/VanillaFishingExpanded.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.Serialization; 4 | using HarmonyLib; 5 | using Multiplayer.API; 6 | using Verse; 7 | 8 | namespace Multiplayer.Compat 9 | { 10 | /// Vanilla Fishing Expanded by Oskar Potocki, Sarg Bjornson 11 | /// 12 | /// 13 | /// contribution to Multiplayer Compatibility by Cody Spring 14 | [MpCompatFor("VanillaExpanded.VCEF")] 15 | class VanillaFishingExpanded 16 | { 17 | private static Type commandType; 18 | private static AccessTools.FieldRef mapField; 19 | private static AccessTools.FieldRef fishingZoneField; 20 | 21 | public VanillaFishingExpanded(ModContentPack mod) 22 | { 23 | // RNG fix 24 | { 25 | PatchingUtilities.PatchSystemRandCtor("VCE_Fishing.JobDriver_Fish", false); 26 | PatchingUtilities.PatchPushPopRand("VCE_Fishing.JobDriver_Fish:SelectFishToCatch"); 27 | } 28 | 29 | // Gizmo (select fish size to catch) 30 | { 31 | commandType = AccessTools.TypeByName("VCE_Fishing.Command_SetFishList"); 32 | mapField = AccessTools.FieldRefAccess(commandType, "map"); 33 | fishingZoneField = AccessTools.FieldRefAccess(commandType, "zone"); 34 | 35 | MpCompat.RegisterLambdaMethod(commandType, "ProcessInput", Enumerable.Range(0, 3).ToArray()); 36 | MP.RegisterSyncWorker(SyncFishingZoneChange, commandType, shouldConstruct: false); 37 | 38 | MpCompat.RegisterLambdaMethod(AccessTools.TypeByName("VCE_Fishing.Zone_Fishing"), "GetGizmos", 1); 39 | } 40 | } 41 | 42 | private static void SyncFishingZoneChange(SyncWorker sync, ref Command command) 43 | { 44 | if (sync.isWriting) 45 | { 46 | sync.Write(mapField(command)); 47 | sync.Write(fishingZoneField(command)); 48 | } 49 | else 50 | { 51 | command = (Command)FormatterServices.GetUninitializedObject(commandType); 52 | mapField(command) = sync.Read(); 53 | fishingZoneField(command) = sync.Read(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Source_Referenced/HemogenExtractor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using RimWorld; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Hemogen Extractor by Уверен? 10 | /// 11 | [MpCompatFor("Uveren.HemogenExtractor")] 12 | public class HemogenExtractor 13 | { 14 | private static FastInvokeHandler getStoreSettingsMethod; 15 | private static FastInvokeHandler getParentStoreSettingsMethod; 16 | 17 | private static Type nutritionRefuelableType; 18 | 19 | public HemogenExtractor(ModContentPack mod) 20 | { 21 | MpCompat.RegisterLambdaMethod("HemogenExtractor.CompSpawnerHemogen", "CompGetGizmosExtra", 0, 1).SetDebugOnly(); 22 | 23 | var type = nutritionRefuelableType = AccessTools.TypeByName("HemogenExtractor.CompNutritionRefuelable"); 24 | getStoreSettingsMethod = MethodInvoker.GetHandler(AccessTools.DeclaredMethod(type, "GetStoreSettings")); 25 | getParentStoreSettingsMethod = MethodInvoker.GetHandler(AccessTools.DeclaredMethod(type, "GetParentStoreSettings")); 26 | 27 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod("HemogenExtractor.ITab_CustomNutrition:FillTab"), 28 | prefix: new HarmonyMethod(typeof(HemogenExtractor), nameof(PreFillTab)), 29 | finalizer: new HarmonyMethod(typeof(HemogenExtractor), nameof(PostFillTab))); 30 | } 31 | 32 | private static void PreFillTab() 33 | { 34 | if (!MP.IsInMultiplayer || Find.Selector.SingleSelectedThing is not ThingWithComps thing) 35 | return; 36 | 37 | if (thing.comps.FirstOrDefault(c => c.GetType() == nutritionRefuelableType) is CompRefuelable comp) 38 | MP.SetThingFilterContext(new HemogenExtractorContext(comp)); 39 | } 40 | 41 | private static void PostFillTab() => MP.SetThingFilterContext(null); 42 | 43 | [MpCompatRequireMod("Uveren.HemogenExtractor")] 44 | public record HemogenExtractorContext(CompRefuelable Obj) : ThingFilterContext 45 | { 46 | public CompRefuelable Obj { get; } = Obj; 47 | 48 | public override ThingFilter Filter => ((StorageSettings)getStoreSettingsMethod(Obj)).filter; 49 | public override ThingFilter ParentFilter => ((StorageSettings)getParentStoreSettingsMethod(Obj))?.filter; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Source/Mods/FollowMe.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Reflection; 7 | using System.Reflection.Emit; 8 | using Verse; 9 | 10 | namespace Multiplayer.Compat 11 | { 12 | /// Follow Me by Fluffy 13 | /// 14 | /// 15 | [MpCompatFor("Fluffy.FollowMe")] 16 | class FollowMe 17 | { 18 | public FollowMe(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(LatePatch); 19 | 20 | public static Type cinematicCamera = AccessTools.TypeByName("FollowMe.CinematicCamera"); 21 | public static MethodInfo followNewSubject = AccessTools.Method("FollowMe.CinematicCamera:FollowNewSubject"); 22 | public static MethodInfo verseRandRange = AccessTools.Method("Verse.Rand:Range", new Type[] { typeof(int), typeof(int) }); 23 | 24 | private static uint state; 25 | 26 | private static uint myCheapRand() 27 | { 28 | uint x = state; 29 | x ^= (x << 13); 30 | x ^= (x >> 17); 31 | x ^= (x << 5); 32 | state = x; 33 | return x; 34 | } 35 | 36 | private static float myCheapRandFloat() 37 | { 38 | return (float)myCheapRand() / (float)uint.MaxValue; 39 | } 40 | private static int myCheapRange(int min, int max) 41 | { 42 | float r = myCheapRandFloat(); 43 | 44 | return (int)(min + ((max - min) * r)); 45 | } 46 | 47 | public static IEnumerable transpileCheapRand(IEnumerable instr) 48 | { 49 | foreach(var ci in instr) 50 | { 51 | if (ci.opcode == OpCodes.Call && ci.operand is MethodInfo callee && callee == verseRandRange) 52 | { 53 | ci.operand = AccessTools.Method(typeof(FollowMe), nameof(myCheapRange)); 54 | } 55 | yield return ci; 56 | } 57 | } 58 | 59 | private static void LatePatch() 60 | { 61 | state = (uint)(DateTime.UtcNow - new DateTime(1970, 01, 01)).TotalMilliseconds; 62 | var transpiler = new HarmonyMethod(typeof(FollowMe), nameof(transpileCheapRand)); 63 | 64 | MpCompat.harmony.Patch(followNewSubject, transpiler: transpiler); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Source/Mods/BiotechExpansionMythic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using RimWorld; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Biotech Expansion - Mythic by Lennoxicon 10 | /// 11 | /// 12 | [MpCompatFor("biotexpans.mythic")] 13 | public class BiotechExpansionMythic 14 | { 15 | private static Type geneAurumType; 16 | private static Type geneReverenceType; 17 | 18 | private static ISyncField syncAurumAllowedField; 19 | private static ISyncField syncReverenceAllowedField; 20 | 21 | public BiotechExpansionMythic(ModContentPack mod) 22 | { 23 | var type = AccessTools.TypeByName("BTE_MY.CompCreateReveredMote"); 24 | MP.RegisterSyncMethod(type, "GetMeditationSpots"); // Scan for meditation spots 25 | MP.RegisterSyncMethod(type, "AddProgress").SetDebugOnly(); // (Dev) add 100% progress (also called during ticking) 26 | 27 | // -/+ 10% resource 28 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("BTE_MY.AurumUtility:OffsetResource")).SetDebugOnly(); 29 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("BTE_MY.ReverenceUtility:OffsetResource")).SetDebugOnly(); 30 | 31 | geneAurumType = type = AccessTools.TypeByName("BTE_MY.Gene_Aurum"); 32 | syncAurumAllowedField = MP.RegisterSyncField(type, "aurumFuelAllowed"); 33 | 34 | geneReverenceType = type = AccessTools.TypeByName("BTE_MY.Gene_Reverence"); 35 | syncReverenceAllowedField = MP.RegisterSyncField(type, "ReverenceFuelAllowed"); 36 | 37 | LongEventHandler.ExecuteWhenFinished(LatePatch); 38 | } 39 | 40 | private static void LatePatch() 41 | { 42 | foreach (var typeName in new[] { "BTE_MY.GeneGizmo_ResourceAurum", "BTE_MY.GeneGizmo_ResourceReverence" }) 43 | { 44 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod($"{typeName}:{nameof(GeneGizmo_Resource.DrawHeader)}"), 45 | prefix: new HarmonyMethod(typeof(BiotechExpansionMythic), nameof(PreDrawLabel))); 46 | } 47 | } 48 | 49 | private static void PreDrawLabel(GeneGizmo_Resource __instance) 50 | { 51 | if (!MP.IsInMultiplayer) 52 | return; 53 | 54 | var type = __instance.gene.GetType(); 55 | if (type == geneAurumType) 56 | syncAurumAllowedField.Watch(__instance.gene); 57 | else if (type == geneReverenceType) 58 | syncReverenceAllowedField.Watch(__instance.gene); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Source/CodeFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | using HarmonyLib; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | public class CodeFinder 10 | { 11 | private MethodBase inMethod; 12 | private List list; 13 | 14 | public int Pos { get; private set; } 15 | 16 | public CodeFinder(MethodBase inMethod, List list) 17 | { 18 | this.inMethod = inMethod; 19 | this.list = list; 20 | } 21 | 22 | public CodeFinder Advance(int steps) 23 | { 24 | Pos += steps; 25 | return this; 26 | } 27 | 28 | public CodeFinder Forward(OpCode opcode, object operand = null) 29 | { 30 | Find(opcode, operand, 1); 31 | return this; 32 | } 33 | 34 | public CodeFinder Backward(OpCode opcode, object operand = null) 35 | { 36 | Find(opcode, operand, -1); 37 | return this; 38 | } 39 | 40 | public CodeFinder Find(OpCode opcode, object operand, int direction) 41 | { 42 | while (Pos < list.Count && Pos >= 0) { 43 | if (Matches(list[Pos], opcode, operand)) return this; 44 | Pos += direction; 45 | } 46 | 47 | throw new Exception($"Couldn't find instruction ({opcode}) with operand ({operand}) in {inMethod.FullDescription()}."); 48 | } 49 | 50 | public CodeFinder Find(Predicate predicate, int direction) 51 | { 52 | while (Pos < list.Count && Pos >= 0) { 53 | if (predicate(list[Pos])) return this; 54 | Pos += direction; 55 | } 56 | 57 | throw new Exception($"Couldn't find instruction using predicate ({predicate.Method}) in method {inMethod.FullDescription()}."); 58 | } 59 | 60 | public CodeFinder Start() 61 | { 62 | Pos = 0; 63 | return this; 64 | } 65 | 66 | public CodeFinder End() 67 | { 68 | Pos = list.Count - 1; 69 | return this; 70 | } 71 | 72 | private bool Matches(CodeInstruction inst, OpCode opcode, object operand) 73 | { 74 | if (inst.opcode != opcode) return false; 75 | if (operand == null) return true; 76 | 77 | if (opcode == OpCodes.Stloc_S) 78 | return (inst.operand as LocalBuilder).LocalIndex == (int) operand; 79 | 80 | return Equals(inst.operand, operand); 81 | } 82 | 83 | public static implicit operator int(CodeFinder finder) 84 | { 85 | return finder.Pos; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Source/Mods/VanillaRacesFungoid.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HarmonyLib; 3 | using RimWorld; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Vanilla Races Expanded - Fungoid by Oskar Potocki, Sarg Bjornson 9 | /// 10 | /// 11 | [MpCompatFor("vanillaracesexpanded.fungoid")] 12 | public class VanillaRacesFungoid 13 | { 14 | private static AccessTools.FieldRef> pawnsAndXenotypesDictionaryField; 15 | 16 | public VanillaRacesFungoid(ModContentPack mod) 17 | { 18 | // RNG 19 | { 20 | PatchingUtilities.PatchSystemRand(new[] 21 | { 22 | "VanillaRacesExpandedFungoid.DamageWorker_ExtraFungoidInfection:ApplySpecialEffectsToPart", 23 | // random.NextDouble() > 0... Feels a bit pointless considering extremely low chance? 24 | "VanillaRacesExpandedFungoid.DamageWorker_ExtraFungoidInfection_Bite:ApplySpecialEffectsToPart", 25 | }); 26 | 27 | PatchingUtilities.PatchUnityRand("VanillaRacesExpandedFungoid.Building_FungoidShip:PopUpFungoids"); 28 | } 29 | 30 | // Gizmos 31 | { 32 | // Dev advance by 10 days 33 | MpCompat.RegisterLambdaMethod("VanillaRacesExpandedFungoid.Hediff_GeneInfected", nameof(Hediff.GetGizmos), 0).SetDebugOnly(); 34 | } 35 | 36 | // Cache 37 | { 38 | var type = AccessTools.TypeByName("VanillaRacesExpandedFungoid.StaticCollectionsClass"); 39 | pawnsAndXenotypesDictionaryField = AccessTools.StaticFieldRefAccess>(AccessTools.DeclaredField(type, "pawns_and_xenotypes")); 40 | 41 | // Those 2 likely don't need clearing, and could possibly cause issues instead: 42 | // VanillaRacesExpandedFungoid.MapComponent_CoalescenceTracker:xenotypesAndMood_backup - used for exposing data only 43 | // VanillaRacesExpandedFungoid.StaticCollectionsClass:xenotypesAndMood - is exposed on load (copied from the previous one in FinalizeInit), so it should be safe 44 | 45 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod(typeof(GameComponentUtility), nameof(GameComponentUtility.FinalizeInit)), 46 | postfix: new HarmonyMethod(typeof(VanillaRacesFungoid), nameof(ClearCache))); 47 | } 48 | } 49 | 50 | private static void ClearCache() 51 | { 52 | pawnsAndXenotypesDictionaryField().Clear(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Source/Mods/BadHygiene.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Dubs Bad Hygiene and Dubs Bad Hygiene Lite by Dubwise 8 | /// 9 | /// 10 | /// 11 | /// 12 | [MpCompatFor("Dubwise.DubsBadHygiene")] 13 | [MpCompatFor("Dubwise.DubsBadHygiene.Lite")] 14 | public class BadHygiene 15 | { 16 | private static AccessTools.FieldRef removePlumbingRemovalModeField; 17 | private static AccessTools.FieldRef placeFertilizerAddingField; 18 | 19 | public BadHygiene(ModContentPack mod) 20 | { 21 | // RNG 22 | { 23 | var type = AccessTools.TypeByName("DubsBadHygiene.Comp_SaunaHeater"); 24 | var methods = new[] 25 | { 26 | AccessTools.Method(type, "SteamyNow"), 27 | AccessTools.Method(type, "CompTick"), 28 | }; 29 | 30 | PatchingUtilities.PatchPushPopRand(methods); 31 | } 32 | 33 | // Designators 34 | { 35 | var type = AccessTools.TypeByName("DubsBadHygiene.Designator_AreaPlaceFertilizer"); 36 | placeFertilizerAddingField = AccessTools.FieldRefAccess(type, "Adding"); 37 | MP.RegisterSyncWorker(SyncFertilizerAreaDesignator, type, shouldConstruct: true); 38 | 39 | type = AccessTools.TypeByName("DubsBadHygiene.Designator_RemovePlumbing"); 40 | removePlumbingRemovalModeField = AccessTools.FieldRefAccess(type, "RemovalMode"); 41 | MP.RegisterSyncWorker(SyncRemovePlumbingDesignator, type, shouldConstruct: true); 42 | } 43 | } 44 | 45 | private static void SyncRemovePlumbingDesignator(SyncWorker sync, ref Designator designator) 46 | { 47 | if (sync.isWriting) 48 | sync.Write(removePlumbingRemovalModeField(designator)); 49 | else 50 | removePlumbingRemovalModeField(designator) = sync.Read(); 51 | } 52 | 53 | private static void SyncFertilizerAreaDesignator(SyncWorker sync, ref Designator designator) 54 | { 55 | if (sync.isWriting) 56 | sync.Write(placeFertilizerAddingField(designator)); 57 | else 58 | placeFertilizerAddingField(designator) = sync.Read(); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Source/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Multiplayer.API; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | public static class Extensions 8 | { 9 | internal static string After(this string s, char c) 10 | { 11 | if (s.IndexOf(c) == -1) 12 | throw new Exception($"Char {c} not found in string {s}"); 13 | return s.Substring(s.IndexOf(c) + 1); 14 | } 15 | 16 | internal static string Until(this string s, char c) 17 | { 18 | if (s.IndexOf(c) == -1) 19 | throw new Exception($"Char {c} not found in string {s}"); 20 | return s.Substring(0, s.IndexOf(c)); 21 | } 22 | 23 | internal static int CharacterCount(this string s, char c) 24 | { 25 | int num = 0; 26 | for (int i = 0; i < s.Length; i++) 27 | if (s[i] == c) 28 | num++; 29 | return num; 30 | } 31 | 32 | public static void SetDebugOnly(this IEnumerable syncMethods) 33 | { 34 | foreach (var method in syncMethods) 35 | { 36 | method.SetDebugOnly(); 37 | } 38 | } 39 | 40 | public static void SetDebugOnly(this IEnumerable syncMethods) 41 | { 42 | foreach (var method in syncMethods) 43 | { 44 | method.SetDebugOnly(); 45 | } 46 | } 47 | 48 | public static void SetContext(this IEnumerable syncDelegates, SyncContext context) 49 | { 50 | foreach (var method in syncDelegates) 51 | { 52 | method.SetContext(context); 53 | } 54 | } 55 | 56 | public static void SetContext(this IEnumerable syncDelegates, SyncContext context) 57 | { 58 | foreach (var method in syncDelegates) 59 | { 60 | method.SetContext(context); 61 | } 62 | } 63 | 64 | private const string SteamSuffix = "_steam"; 65 | private const string CopySuffix = "_copy"; 66 | 67 | internal static string NoModIdSuffix(this string modId) 68 | { 69 | while (true) 70 | { 71 | if (modId.EndsWith(SteamSuffix)) 72 | { 73 | modId = modId.Substring(0, modId.Length - SteamSuffix.Length); 74 | continue; 75 | } 76 | 77 | if (modId.EndsWith(CopySuffix)) 78 | { 79 | modId = modId.Substring(0, modId.Length - CopySuffix.Length); 80 | continue; 81 | } 82 | 83 | return modId; 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Source/Mods/NonUnoPinata.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using HarmonyLib; 4 | using Multiplayer.API; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Non uno Pinata by Avil 10 | /// 11 | /// 12 | [MpCompatFor("avilmask.NonUnoPinata")] 13 | public class NonUnoPinata 14 | { 15 | 16 | private static Type compStripCheckerType; 17 | 18 | public NonUnoPinata(ModContentPack mod) 19 | { 20 | compStripCheckerType = AccessTools.TypeByName("NonUnoPinata.CompStripChecker"); 21 | 22 | // Register the worker that encodes/decodes the CompStripChecker 23 | MP.RegisterSyncWorker(SyncWorkerForCompStripChecker, compStripCheckerType); 24 | 25 | LongEventHandler.ExecuteWhenFinished(LatePatch); 26 | } 27 | 28 | private static void LatePatch() 29 | { 30 | // This method is called whenever the user clicks the strip button in the ITab 31 | MP.RegisterSyncMethod(AccessTools.Method("NonUnoPinata.NUPUtility:SetShouldStrip")); 32 | 33 | // Under Multiplayer we can't have the state altered without a SyncMethod. 34 | // CompStripChecker.GetChecker creates the comp if not found, which is disallowed. 35 | // To keep the logic being used in ITab_Pawn_GearPatch, it's required that the comp exists. 36 | // This workaround has no impact on performance 37 | InjectComp(); 38 | } 39 | 40 | // Sends the ThingWithComps parent for reference on write 41 | // Retrieves CompStripChecker from the parent on read 42 | static void SyncWorkerForCompStripChecker(SyncWorker sw, ref ThingComp comp) 43 | { 44 | if (sw.isWriting) 45 | { 46 | sw.Write(comp.parent); 47 | } 48 | else 49 | { 50 | comp = sw.Read().AllComps.First(c => c.GetType() == compStripCheckerType); 51 | } 52 | } 53 | 54 | // Searches for haulable items and adds the CompStripChecker 55 | static void InjectComp() 56 | { 57 | var compProperties = new CompProperties { compClass = compStripCheckerType }; 58 | var defs = DefDatabase.AllDefs.Where( 59 | def => typeof(ThingWithComps).IsAssignableFrom(def.thingClass) 60 | && def.EverHaulable 61 | && !def.HasComp(compStripCheckerType) 62 | ); 63 | foreach (var def in defs) 64 | { 65 | def.comps.Add(compProperties); 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaFurnitureExpandedSecurity.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Vanilla Furniture Expanded - Security by Oskar Potocki, Trunken, and XeoNovaDan 8 | /// 9 | /// 10 | /// Contribution to Multiplayer Compatibility by Sokyran and Reshiram 11 | [MpCompatFor("VanillaExpanded.VFESecurity")] 12 | class VFESecurity 13 | { 14 | public VFESecurity(ModContentPack mod) 15 | { 16 | LongEventHandler.ExecuteWhenFinished(LateSyncMethods); 17 | 18 | // 2 of the overloads of VFESecurity.TrenchUtility:FinalAdjustedRangeFromTerrain use `Find.CurrentMap` 19 | // which would normally cause issues, but they are unused by the mod at all. 20 | 21 | // RNG fix 22 | { 23 | // Motes 24 | PatchingUtilities.PatchPushPopRand("VFESecurity.ExtendedMoteMaker:SearchlightEffect"); 25 | } 26 | 27 | // Patched sync methods 28 | { 29 | // When picking a new target the old one is (supposed to be) cleared. 30 | // Could cause issues if the player is behind on ticks. 31 | var type = AccessTools.TypeByName("VFESecurity.Patch_Building_TurretGun"); 32 | type = AccessTools.Inner(type, "OrderAttack"); 33 | PatchingUtilities.PatchCancelInInterface(AccessTools.DeclaredMethod(type, "Postfix")); 34 | } 35 | } 36 | 37 | private static void LateSyncMethods() 38 | { 39 | // Artillery fix 40 | { 41 | var type = AccessTools.TypeByName("VFESecurity.CompLongRangeArtillery"); 42 | 43 | MP.RegisterSyncMethod(type, "ResetForcedTarget"); 44 | 45 | var method = AccessTools.DeclaredMethod(type, "SetTargetedTile"); 46 | MP.RegisterSyncMethod(method).SetContext(SyncContext.MapSelected); 47 | MpCompat.harmony.Patch(method, prefix: new HarmonyMethod(typeof(VFESecurity), nameof(PreSetTargetedTile))); 48 | } 49 | 50 | // RNG fix 51 | { 52 | PatchingUtilities.PatchPushPopRand("VFESecurity.Building_Shield:DrawAt"); 53 | } 54 | } 55 | 56 | // Will run before the original method gets synced 57 | private static void PreSetTargetedTile() 58 | { 59 | // Close now, as waiting for the method to be synced may take a while depending on ticks behind 60 | if (!MP.IsExecutingSyncCommand) 61 | CameraJumper.TryHideWorld(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Source/Mods/FortificationsIndustrial.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat; 6 | 7 | /// Fortifications - Industrial by AobaKuma 8 | /// 9 | [MpCompatFor("Aoba.Fortress.Industrial")] 10 | public class FortificationsIndustrial 11 | { 12 | public FortificationsIndustrial(ModContentPack mod) 13 | { 14 | LongEventHandler.ExecuteWhenFinished(LatePatch); 15 | 16 | #region AOBAUtilities 17 | 18 | // Seem like it may be used at some point in a different mod. I could not find another one using this, but 19 | // if that ever happens - this should be extracted into a separate patch (with additional check to apply once). 20 | 21 | // RNG 22 | { 23 | PatchingUtilities.PatchPushPopRand("AOBAUtilities.CompFlecker:CompTick"); 24 | } 25 | 26 | // Dev gizmos 27 | { 28 | MpCompat.RegisterLambdaMethod("AOBAUtilities.CompFueledSpawner", nameof(ThingComp.CompGetGizmosExtra), 0).SetDebugOnly(); 29 | } 30 | 31 | #endregion 32 | } 33 | 34 | private static void LatePatch() 35 | { 36 | #region Fortifications - Industrial 37 | 38 | // RNG 39 | { 40 | PatchingUtilities.PatchPushPopRand("Fortification.CompCastFlecker:BurstFleck"); 41 | PatchingUtilities.PatchUnityRand("Fortification.CompCastFlecker:DrawPos"); 42 | } 43 | 44 | // Gizmos 45 | { 46 | // Make all pawns leave a building (bunker) 47 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("Fortification.Building_TurretCapacity:GetOut")); 48 | // (Dev) trigger countdown 49 | MpCompat.RegisterLambdaMethod("Fortification.CompExplosiveWithComposite", nameof(ThingComp.CompGetGizmosExtra), 0).SetDebugOnly(); 50 | // Deploy minified thing, called from a 2 places 51 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("Fortification.MinifiedThingDeployable:Deploy")); 52 | } 53 | 54 | #endregion 55 | 56 | #region Combat Extended 57 | 58 | // May not be active so patches check for non-null method/type 59 | // Gizmos 60 | { 61 | // Make all pawns leave a building (bunker) 62 | var method = AccessTools.DeclaredMethod("Fortification.Building_TurretCapacityCE:GetOut"); 63 | if (method != null) 64 | MP.RegisterSyncMethod(method); 65 | 66 | var type = AccessTools.TypeByName("Fortification.CompExplosiveWithCompositeCE"); 67 | // (Dev) trigger countdown 68 | if (type != null) 69 | MP.RegisterSyncDelegateLambda(type, nameof(ThingComp.CompGetGizmosExtra), 0).SetDebugOnly(); 70 | } 71 | 72 | #endregion 73 | } 74 | } -------------------------------------------------------------------------------- /Source/Mods/SmartFarming.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Smart Farming by Owlchemist 9 | /// 10 | /// 11 | [MpCompatFor("Owlchemist.SmartFarming")] 12 | internal class SmartFarming 13 | { 14 | private static IDictionary compCache; 15 | private static AccessTools.FieldRef growZoneRegistryField; 16 | 17 | public SmartFarming(ModContentPack mod) 18 | { 19 | var type = AccessTools.TypeByName("SmartFarming.Mod_SmartFarming"); 20 | compCache = AccessTools.StaticFieldRefAccess(type, "compCache"); 21 | 22 | type = AccessTools.TypeByName("SmartFarming.MapComponent_SmartFarming"); 23 | growZoneRegistryField = AccessTools.FieldRefAccess(type, "growZoneRegistry"); 24 | 25 | type = AccessTools.TypeByName("SmartFarming.ZoneData"); 26 | // Toggle: no petty jobs, allow harvest, harvest now, orchard alignment 27 | MpCompat.RegisterLambdaDelegate(type, "Init", 3, 5, 6, 8); 28 | MP.RegisterSyncMethod(type, "SwitchSowMode"); // Called from two places 29 | MP.RegisterSyncMethod(type, "SwitchPriority"); // Called from two places 30 | MP.RegisterSyncMethod(type, "MergeZones"); 31 | MP.RegisterSyncWorker(SyncZoneData, type); 32 | } 33 | 34 | private static void SyncZoneData(SyncWorker sync, ref object zoneData) 35 | { 36 | if (sync.isWriting) 37 | { 38 | int? zoneId = null; 39 | var comp = compCache[Find.CurrentMap.uniqueID]; 40 | var zoneRegistry = growZoneRegistryField(comp); 41 | 42 | foreach (DictionaryEntry entry in zoneRegistry) 43 | { 44 | if (entry.Value == zoneData) 45 | { 46 | zoneId = (int)entry.Key; 47 | break; 48 | } 49 | } 50 | 51 | sync.Write(zoneId); 52 | if (zoneId != null) 53 | sync.Write(Find.CurrentMap.uniqueID); 54 | } 55 | else 56 | { 57 | var zoneId = sync.Read(); 58 | if (zoneId != null) 59 | { 60 | var comp = compCache[sync.Read()]; 61 | var zoneRegistry = growZoneRegistryField(comp); 62 | zoneData = zoneRegistry[zoneId.Value]; 63 | } 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /Source/Mods/ReGrowthCore.cs: -------------------------------------------------------------------------------- 1 | using Verse; 2 | using Multiplayer.API; 3 | using HarmonyLib; 4 | using RimWorld.Planet; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// ReGrowth: Core by Helixien, Taranchuk 9 | /// 10 | /// 11 | [MpCompatFor("ReGrowth.BOTR.Core")] 12 | public class ReGrowthCore 13 | { 14 | public ReGrowthCore(ModContentPack mod) 15 | { 16 | LongEventHandler.ExecuteWhenFinished(LatePatch); 17 | MpCompatPatchLoader.LoadPatch(this); 18 | 19 | // (Dev) spawn leaves 20 | MpCompat.RegisterLambdaMethod("ReGrowthCore.CompLeavesSpawnerBase", "CompGetGizmosExtra", 0).SetDebugOnly(); 21 | 22 | // RNG 23 | // Could be fixed by clearing the cache on join, but it affects a small graphical thing (motes). Not really worth bothering with. 24 | PatchingUtilities.PatchPushPopRand("ReGrowthCore.WeatherOverlay_FogMotes:TickOverlay"); 25 | 26 | // Register the MakeCamp method to be synchronized 27 | var type = AccessTools.TypeByName("ReGrowthCore.Caravan_GetGizmos_Patch"); 28 | MP.RegisterSyncMethod(type, "MakeCamp"); 29 | } 30 | 31 | private static void LatePatch() => PatchingUtilities.PatchPushPopRand("ReGrowthCore.DevilDust_Tornado:ThrowDevilDustPuff"); 32 | 33 | [MpCompatPostfix("ReGrowthCore.CaravanCamp", nameof(Site.Tick))] 34 | private static void PostCaravanCompTick(MapParent __instance) 35 | { 36 | // MP has a multifaction patch to prevent maps from being automatically 37 | // removed as long as they belong to any player facionts. This is not 38 | // that big of a deal MP + vanilla only, but with other mods it can cause 39 | // situations where a temporary map (like this one) is prevented from 40 | // being remove due to belonging to one of the player. 41 | // 42 | // The patch here is to basically remove the faction from the map once 43 | // the map should be removed naturally, allowing the map to be removed. 44 | // Also, by having no faction, the world object won't have associated 45 | // player faction's color. 46 | 47 | // Make sure there is owner and it's a player faction. 48 | if (__instance.Faction?.IsPlayer != true) 49 | return; 50 | 51 | // If there's no map there's no point in having owner faction. 52 | // If there's a map, check if the map should be removed. 53 | // If yes, set the faction to null to allow the map to 54 | // be removed, leaving "abandoned camp" object behind. 55 | if (!__instance.HasMap || __instance.ShouldRemoveMapNow(out _)) 56 | __instance.SetFaction(null); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Source/Mods/ExosuitFramework.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat; 7 | 8 | /// Exosuit Framework by AobaKuma 9 | /// 10 | /// 11 | [MpCompatFor("Aoba.Exosuit.Framework")] 12 | public class ExosuitFramework 13 | { 14 | public ExosuitFramework(ModContentPack mod) 15 | { 16 | LongEventHandler.ExecuteWhenFinished(LatePatch); 17 | 18 | #region Gizmos 19 | 20 | { 21 | // Eject to location (0), launch to map (3), release (8) 22 | MpCompat.RegisterLambdaMethod("WalkerGear.Building_EjectorBay", nameof(Building.GetGizmos), 0, 3, 8) 23 | // The first 2 gizmos use current map, the last one doesn't care 24 | .SkipLast(1).SetContext(SyncContext.CurrentMap); 25 | // Get in (0), toggle auto repair (2) 26 | MpCompat.RegisterLambdaMethod("WalkerGear.Building_MaintenanceBay", nameof(Building.GetGizmos), 0, 2); 27 | // Toggle safety (1), syncing it is not necessary, as it's only used to enable the eject 28 | // gizmo. Better to sync it anyway in case some mods end up using it in some other way. 29 | // Eject (2) is synced through WalkerGear_Core:Eject for more compatibility. 30 | MpCompat.RegisterLambdaMethod("WalkerGear.ModuleComp_EmergencyEject", nameof(ThingComp.CompGetWornGizmosExtra), 1); 31 | } 32 | 33 | #endregion 34 | 35 | #region Float Menus 36 | 37 | { 38 | var type = AccessTools.TypeByName("WalkerGear.Building_MaintenanceBay"); 39 | // Add/replace/remove methods, called from ITab_MechGear 40 | MP.RegisterSyncMethod(type, "AddOrReplaceModule"); 41 | MP.RegisterSyncMethod(type, "RemoveModules"); 42 | 43 | type = AccessTools.TypeByName("WalkerGear.FloatMenuMakerMap_MakeForFrame"); 44 | // Take to maintenance bay. Needs to be synced due to the method 45 | // modifying a field after starting a job - `job.count = 1`. 46 | MpCompat.RegisterLambdaDelegate(type, "AddHumanlikeOrders", 1); 47 | } 48 | 49 | #endregion 50 | 51 | // Once it's included in a mod/implemented, also patch Building_EjectorBay and WG_PawnFlyer. 52 | // The building (and thus the flyer) aren't used by this mod or any of its addons yet. 53 | } 54 | 55 | private static void LatePatch() 56 | { 57 | #region Gizmos 58 | 59 | { 60 | // Eject, only called from lambda in ModuleComp_EmergencyEject. Could sync it instead, 61 | // but could be called from other mods. Probably safer to call this method. 62 | MP.RegisterSyncMethod(AccessTools.DeclaredMethod("WalkerGear.WalkerGear_Core:Eject")); 63 | } 64 | 65 | #endregion 66 | } 67 | } -------------------------------------------------------------------------------- /Source/Mods/RimsentialSpaceports.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Rimsential - Spaceports by SomewhereOutInSpace 9 | /// 10 | /// 11 | [MpCompatFor("SomewhereOutInSpace.Spaceports")] 12 | [MpCompatFor("zal.spaceports")] 13 | internal class RimsentialSpaceports 14 | { 15 | public RimsentialSpaceports(ModContentPack mod) 16 | { 17 | // Gizmos 18 | { 19 | var type = AccessTools.TypeByName("Spaceports.Buildings.Building_Beacon"); 20 | // Toggle forced lockdown, dismiss all, recall all 21 | MpCompat.RegisterLambdaMethod(type, "GetGizmos", 1, 2, 3); 22 | MP.RegisterSyncMethod(type, "ConfirmAction"); 23 | 24 | type = AccessTools.TypeByName("Spaceports.Buildings.Building_Shuttle"); 25 | // Force immediate departure, recall party 26 | MpCompat.RegisterLambdaMethod(type, "GetGizmos", 0, 1); 27 | 28 | type = AccessTools.TypeByName("Spaceports.Buildings.Building_ShuttlePad"); 29 | MP.RegisterSyncMethod(type, "SetAccessState"); 30 | 31 | type = AccessTools.TypeByName("Spaceports.Buildings.Building_ShuttleSpot"); 32 | MP.RegisterSyncMethod(type, "SetAccessState"); 33 | } 34 | 35 | // Choice letters 36 | { 37 | var type = AccessTools.TypeByName("Spaceports.Letters.PrisonerTransferLetter"); 38 | var methods = MpMethodUtil.GetLambda(type, "Choices", MethodType.Getter, null, 0, 1, 2).ToArray(); 39 | MP.RegisterSyncMethod(methods[0]); 40 | MP.RegisterSyncMethod(methods[1]); 41 | MP.RegisterSyncMethod(methods[2]); 42 | MP.RegisterDefaultLetterChoice(methods[2], type); 43 | 44 | var typeNames = new[] 45 | { 46 | "Spaceports.Letters.InterstellarDerelictLetter", 47 | "Spaceports.Letters.MedevacLetter", 48 | "Spaceports.Letters.MysteryCargoLetter", 49 | "Spaceports.Letters.SpicyPawnLendingLetter", 50 | }; 51 | 52 | foreach (var typeName in typeNames) 53 | { 54 | type = AccessTools.TypeByName(typeName); 55 | methods = MpMethodUtil.GetLambda(type, "Choices", MethodType.Getter, null, 0, 1).ToArray(); 56 | MP.RegisterSyncMethod(methods[0]); 57 | MP.RegisterSyncMethod(methods[1]); 58 | MP.RegisterDefaultLetterChoice(methods[1], type); 59 | } 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Source/Mods/VanillaFactionsMechanoid.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using RimWorld; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Vanilla Factions Expanded - Mechanoids by Oskar Potocki, ISOREX, Sarg Bjornson, erdelf, Kikohi, Taranchuk, Kentington, Chowder 10 | /// 11 | /// 12 | [MpCompatFor("OskarPotocki.VFE.Mechanoid")] 13 | public class VanillaFactionsMechanoid 14 | { 15 | public VanillaFactionsMechanoid(ModContentPack mod) => LongEventHandler.ExecuteWhenFinished(LatePatch); 16 | 17 | private static void LatePatch() 18 | { 19 | // Missile silo 20 | var type = AccessTools.TypeByName("VFEMech.MissileSilo"); 21 | MP.RegisterSyncMethod(type, "StartFire"); 22 | var configureNewTargetMethod = AccessTools.Method(type, "ConfigureNewTarget"); 23 | MP.RegisterSyncMethod(configureNewTargetMethod); 24 | foreach (var method in MpCompat.RegisterLambdaMethod(type, "GetGizmos", 2, 3)) method.SetDebugOnly(); 25 | 26 | MpCompat.harmony.Patch(configureNewTargetMethod, 27 | postfix: new HarmonyMethod(typeof(VanillaFactionsMechanoid), nameof(CloseWorldTargetter))); 28 | 29 | // Auto plant machines (planter/harvester) 30 | type = AccessTools.TypeByName("VFE.Mechanoids.Buildings.Building_AutoPlant"); 31 | MpCompat.RegisterLambdaMethod(type, "GetGizmos", 0, 2, 3); 32 | 33 | // Indoctrination Pod 34 | type = AccessTools.TypeByName("VFEMech.Building_IndoctrinationPod"); 35 | MP.RegisterSyncMethod(type, nameof(Building_Casket.EjectContents)); // Overrides the method from vanilla 36 | MpCompat.RegisterLambdaDelegate(type, "GetGizmos", 1, 3); // Set target ideo (both of them are identical) 37 | // TODO: Test float menus 38 | 39 | // Industrial apiary 40 | type = AccessTools.TypeByName("VFEMech.Building_IndustrialApiary"); 41 | MpCompat.RegisterLambdaMethod(type, "GetGizmos", 0, 1).SetDebugOnly(); // Finish/add progress 42 | 43 | // Propaganda comp 44 | type = AccessTools.TypeByName("VFEMech.CompPropaganda"); 45 | MpCompat.RegisterLambdaDelegate(type, "GetGizmos", 1, 3); // Set propaganda mode/set target ideo 46 | } 47 | 48 | private static void CloseWorldTargetter(bool __result) 49 | { 50 | // Force close the targetter to give the players a visual cue that it was successful 51 | // Otherwise, it would successfully mark the target but not give any indication 52 | if (MP.IsInMultiplayer && __result && Find.WorldTargeter.IsTargeting && Find.WorldTargeter.closeWorldTabWhenFinished) 53 | Find.WorldTargeter.StopTargeting(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /About/About.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | rwmt.MultiplayerCompatibility 4 | Multiplayer Compatibility 5 | RimWorld Multiplayer Team 6 | 7 |
  • 1.6
  • 8 |
    9 | Miscellaneous patches to make Multiplayer work 10 | Mod Support: 11 | - [KV] RimFridge 12 | - [KV] Path Avoid 13 | - [O21] Clutter Structures 14 | - Alpha Animals 15 | - Alpha Biomes 16 | - Animals Logic 17 | - Avoid Friendly Fire 18 | - Choice of Psycasts 19 | - Cleaning Area 20 | - Common Sense 21 | - Corruption Core 22 | - Corruption Psykers 23 | - Corruption - Worship 24 | - Cut Plants Before Building 25 | - Desynchronized: Tales and News (Continued) 26 | - Dragon's Descent 27 | - Fertile Fields 28 | - Gastronomy 29 | - Genetic Rim 30 | - Immortals 31 | - LWM's Deep Storage 32 | - MinifyEverything 33 | - Misc. Robots 34 | - Misc. Training 35 | - Netrve's DeepStorage GUI 36 | - Pocket Sand 37 | - PowerSwitch 38 | - Prison Labor 39 | - QualityBuilder 40 | - Reunion 41 | - RunAndGun 42 | - RPG Style Inventory Revamped 43 | - Sandy's RPG Style Inventory 44 | - Simple Sidearms 45 | - Simply More Bridges (Continued) 46 | - Smarter Construction 47 | - Sparkling Worlds - Full Mod 48 | - Sparkling Worlds - Modular Core - Reduced features 49 | - SRTS Expanded 50 | - Stuffed Floors 51 | - Time-of-Day Switches 52 | - Vanilla Achievements Expanded 53 | - Vanilla Animals Expanded — Endangered 54 | - Vanilla Brewing Expanded 55 | - Vanilla Cooking Expanded 56 | - Vanilla Events Expanded 57 | - Vanilla Expanded Framework 58 | - Vanilla Factions Expanded - Insectoids 59 | - Vanilla Factions Expanded - Mechanoids 60 | - Vanilla Factions Expanded - Medieval 61 | - Vanilla Factions Expanded - Settlers 62 | - Vanilla Factions Expanded - Vikings 63 | - Vanilla Fishing Expanded 64 | - Vanilla Furniture Expanded - Farming 65 | - Vanilla Furniture Expanded - Power 66 | - Vanilla Furniture Expanded - Security 67 | - Vanilla Hair Expanded 68 | - Vanilla Plants Expanded - Auto Plow Patch 69 | - Vanilla Social Interactions Expanded 70 | - Vanilla Traits Expanded (Partial Support) 71 | - Vanilla Weapons Expanded - Laser 72 | - What the hack?! 73 | 74 | 75 |
  • 76 | rwmt.Multiplayer 77 | Multiplayer 78 | https://github.com/rwmt/Multiplayer/releases 79 | steam://url/CommunityFilePage/1752864297 80 |
  • 81 |
    82 | 83 |
  • rwmt.Multiplayer
  • 84 |
    85 | 86 |
  • UnlimitedHugs.HugsLib
  • 87 |
  • OskarPotocki.VanillaFactionsExpanded.Core
  • 88 |
    89 |
    90 | -------------------------------------------------------------------------------- /Source/Mods/SignsAndComments.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using UnityEngine; 5 | using Verse; 6 | 7 | namespace Multiplayer.Compat 8 | { 9 | /// Signs and Comments by DarkFlame7 10 | /// 11 | /// 12 | [MpCompatFor("Dark.Signs")] 13 | public class SignsAndComments 14 | { 15 | private static ConstructorInfo renameSignDialogCtor; 16 | private static AccessTools.FieldRef dialogColorField; 17 | private static AccessTools.FieldRef dialogSignCompField; 18 | 19 | public SignsAndComments(ModContentPack mod) 20 | { 21 | var clipboardType = AccessTools.TypeByName("Dark.Signs.CommentContentClipboard"); 22 | MpCompat.RegisterLambdaDelegate(clipboardType, "CopyPasteGizmosFor", 0, 1); // Paste/copy 23 | 24 | var compType = AccessTools.TypeByName("Dark.Signs.Comp_Sign"); 25 | // Some of those could have been skipped, but they will just end up being synced with host once a player rejoins... 26 | MpCompat.RegisterLambdaMethod(compType, nameof(ThingComp.CompGetGizmosExtra), 1, 2, 4); // Toggle hide, change size, apply color 27 | MpCompat.harmony.Patch(AccessTools.PropertyGetter(compType, "editOnPlacement"), 28 | postfix: new HarmonyMethod(typeof(SignsAndComments), nameof(CancelAutoOpenNotTargetedAtMe))); 29 | 30 | var renameDialogType = AccessTools.TypeByName("Dark.Signs.Dialog_RenameSign"); 31 | 32 | renameSignDialogCtor = AccessTools.DeclaredConstructor(renameDialogType, new[] { compType }); 33 | dialogColorField = AccessTools.FieldRefAccess(renameDialogType, "curColor"); 34 | dialogSignCompField = AccessTools.FieldRefAccess(renameDialogType, "signComp"); 35 | 36 | MP.RegisterSyncMethod(renameDialogType, nameof(IRenameable.RenamableLabel)); 37 | MP.RegisterSyncWorker(SyncRenameSignDialog, renameDialogType); 38 | } 39 | 40 | private static void SyncRenameSignDialog(SyncWorker sync, ref object dialog) 41 | { 42 | if (sync.isWriting) 43 | { 44 | sync.Write(dialogSignCompField(dialog)); 45 | sync.Write(dialogColorField(dialog)); 46 | } 47 | else 48 | { 49 | var comp = sync.Read(); 50 | var color = sync.Read(); 51 | 52 | dialog = renameSignDialogCtor.Invoke(new object[] { comp }); 53 | dialogColorField(dialog) = color; 54 | } 55 | } 56 | 57 | private static void CancelAutoOpenNotTargetedAtMe(ref bool __result) 58 | { 59 | if (__result && MP.IsInMultiplayer && !MP.IsExecutingSyncCommandIssuedBySelf) 60 | __result = false; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Source/Mods/HealerMechSerumChoice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using HarmonyLib; 5 | using Multiplayer.API; 6 | using Verse; 7 | 8 | namespace Multiplayer.Compat 9 | { 10 | /// Healer Mech Serum Choice by Syrus 11 | /// 12 | /// 13 | [MpCompatFor("Syrus.HMSChoice")] 14 | internal class HealerMechSerumChoice 15 | { 16 | private static Type hediffSelectionDialogType; 17 | private static AccessTools.FieldRef dialogSelectedHediffField; 18 | 19 | public HealerMechSerumChoice(ModContentPack mod) 20 | { 21 | MP.RegisterSyncMethod(typeof(HealerMechSerumChoice), nameof(SyncedSetHediff)); 22 | 23 | // Dialog 24 | hediffSelectionDialogType = AccessTools.TypeByName("HMSChoice.Dialog_HediffSelection"); 25 | 26 | dialogSelectedHediffField = AccessTools.FieldRefAccess(hediffSelectionDialogType, "SelectedHediff"); 27 | MP.RegisterSyncMethod(AccessTools.Method(hediffSelectionDialogType, "Close", new[] { typeof(Hediff) })); 28 | MP.RegisterSyncWorker(SyncHediffDialog, hediffSelectionDialogType); 29 | MpCompat.harmony.Patch(AccessTools.Method(hediffSelectionDialogType, nameof(Window.DoWindowContents)), 30 | prefix: new HarmonyMethod(typeof(HealerMechSerumChoice), nameof(PreDoWindowContents)), 31 | postfix: new HarmonyMethod(typeof(HealerMechSerumChoice), nameof(PostDoWindowContents))); 32 | 33 | DialogUtilities.RegisterDialogCloseSync(hediffSelectionDialogType, true); 34 | } 35 | 36 | private static void PreDoWindowContents(Hediff ___SelectedHediff, ref Hediff __state) 37 | { 38 | if (!MP.IsInMultiplayer) return; 39 | 40 | __state = ___SelectedHediff; 41 | } 42 | 43 | private static void PostDoWindowContents(ref Hediff ___SelectedHediff, Hediff __state) 44 | { 45 | if (!MP.IsInMultiplayer) return; 46 | 47 | if (___SelectedHediff != __state) 48 | { 49 | SyncedSetHediff(___SelectedHediff); 50 | ___SelectedHediff = __state; 51 | } 52 | } 53 | 54 | private static void SyncedSetHediff(Hediff hediff) 55 | { 56 | var window = Find.WindowStack.Windows.FirstOrDefault(x => x.GetType() == hediffSelectionDialogType); 57 | 58 | if (window != null) 59 | dialogSelectedHediffField(window) = hediff; 60 | } 61 | 62 | private static void SyncHediffDialog(SyncWorker sync, ref Window window) 63 | { 64 | if (!sync.isWriting) 65 | window = Find.WindowStack.Windows.FirstOrDefault(x => x.GetType() == hediffSelectionDialogType); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Source/Mods/Pharmacist.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using Verse; 4 | 5 | namespace Multiplayer.Compat 6 | { 7 | /// Pharmacist by Fluffy 8 | /// 9 | /// 10 | [MpCompatFor("Fluffy.Pharmacist")] 11 | internal class Pharmacist 12 | { 13 | private delegate void SetDefaults(); 14 | 15 | private static SetDefaults setDefaultsMethod; 16 | private static AccessTools.FieldRef medicalCareField; 17 | private static ISyncField diseaseMarginField; 18 | private static ISyncField minorWoundsThresholdField; 19 | private static ISyncField diseaseThresholdField; 20 | 21 | public Pharmacist(ModContentPack mod) 22 | { 23 | var type = AccessTools.TypeByName("Pharmacist.PharmacistSettings"); 24 | var outer = type; 25 | setDefaultsMethod = AccessTools.MethodDelegate(AccessTools.Method(type, "SetDefaults")); 26 | medicalCareField = AccessTools.StaticFieldRefAccess(AccessTools.Field(type, "medicalCare")); 27 | 28 | type = AccessTools.Inner(outer, "MedicalCare"); 29 | diseaseMarginField = MP.RegisterSyncField(type, "_diseaseMargin"); 30 | minorWoundsThresholdField = MP.RegisterSyncField(type, "_minorWoundsThreshold"); 31 | diseaseThresholdField = MP.RegisterSyncField(type, "_diseaseThreshold"); 32 | MP.RegisterSyncWorker(SyncMedicalCare, type); 33 | 34 | type = AccessTools.TypeByName("Pharmacist.MainTabWindow_Pharmacist"); 35 | MpCompat.RegisterLambdaDelegate(type, "DrawCareSelectors", 0, 1, 2); 36 | MpCompat.harmony.Patch(AccessTools.Method(type, "DrawOptions"), 37 | prefix: new HarmonyMethod(typeof(Pharmacist), nameof(PreDrawOptions)), 38 | postfix: new HarmonyMethod(typeof(Pharmacist), nameof(PostDrawOptions))); 39 | } 40 | 41 | private static void PreDrawOptions() 42 | { 43 | if (!MP.IsInMultiplayer) 44 | return; 45 | 46 | MP.WatchBegin(); 47 | var target = medicalCareField(); 48 | diseaseMarginField.Watch(target); 49 | minorWoundsThresholdField.Watch(target); 50 | diseaseThresholdField.Watch(target); 51 | } 52 | 53 | private static void PostDrawOptions() 54 | { 55 | if (MP.IsInMultiplayer) 56 | MP.WatchEnd(); 57 | } 58 | 59 | private static void SyncMedicalCare(SyncWorker sync, ref object obj) 60 | { 61 | if (sync.isWriting) return; 62 | 63 | obj = medicalCareField(); 64 | if (obj != null) return; 65 | 66 | setDefaultsMethod(); 67 | obj = medicalCareField(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Source/Mods/MedicalTab.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Multiplayer.API; 3 | using RimWorld; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Medical Tab by Fluffy 9 | /// 10 | /// 11 | /// 12 | [MpCompatFor("Fluffy.MedicalTab")] 13 | [MpCompatFor("tofudriver.MedicalTabForked")] 14 | internal class MedicalTab 15 | { 16 | private static ISyncField syncMedCare; 17 | private static ISyncField[] syncDefaultCare; 18 | 19 | private delegate MainTabWindow_PawnTable GetMainTab(); 20 | private static GetMainTab mainTabGetter; 21 | 22 | public MedicalTab(ModContentPack mod) 23 | { 24 | var type = AccessTools.TypeByName("Multiplayer.Client.SyncFields"); 25 | syncMedCare = (ISyncField)AccessTools.Field(type, "SyncMedCare").GetValue(null); 26 | syncDefaultCare = (ISyncField[])AccessTools.Field(type, "SyncDefaultCare").GetValue(null); 27 | 28 | type = AccessTools.TypeByName("Fluffy.MainTabWindow_Medical"); 29 | mainTabGetter = AccessTools.MethodDelegate(AccessTools.PropertyGetter(type, "Instance")); 30 | 31 | type = AccessTools.TypeByName("Fluffy.PawnColumnWorker_MedicalCare"); 32 | MpCompat.harmony.Patch(AccessTools.Method(type, nameof(PawnColumnWorker.DoHeader)), 33 | prefix: new HarmonyMethod(typeof(MedicalTab), nameof(PreDoHeader)), 34 | postfix: new HarmonyMethod(typeof(MedicalTab), nameof(StopWatch))); 35 | MpCompat.harmony.Patch(AccessTools.Method(type, nameof(PawnColumnWorker.DoCell)), 36 | prefix: new HarmonyMethod(typeof(MedicalTab), nameof(PreDoCell)), 37 | postfix: new HarmonyMethod(typeof(MedicalTab), nameof(StopWatch))); 38 | 39 | type = AccessTools.TypeByName("Fluffy.PawnColumnWorker_SelfTend"); 40 | // Last parameter (PawnTable) does not need sync, but whatever - no "SkipParameter" method 41 | MP.RegisterSyncMethod(type, "SetValue"); 42 | } 43 | 44 | private static void PreDoHeader() 45 | { 46 | if (!MP.IsInMultiplayer) 47 | return; 48 | 49 | MP.WatchBegin(); 50 | foreach (var syncField in syncDefaultCare) 51 | syncField.Watch(); 52 | foreach (var pawn in mainTabGetter().table.PawnsListForReading) 53 | syncMedCare.Watch(pawn); 54 | } 55 | 56 | private static void PreDoCell(Pawn pawn) 57 | { 58 | if (!MP.IsInMultiplayer) 59 | return; 60 | 61 | MP.WatchBegin(); 62 | syncMedCare.Watch(pawn); 63 | } 64 | 65 | private static void StopWatch() 66 | { 67 | if (MP.IsInMultiplayer) 68 | MP.WatchEnd(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Source/Mods/AlphaAnimals.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using HarmonyLib; 3 | using Multiplayer.API; 4 | using Verse; 5 | 6 | namespace Multiplayer.Compat 7 | { 8 | /// Alpha Animals by Sarg Bjornson 9 | /// 10 | /// 11 | /// contribution to Multiplayer Compatibility by Reshiram and Sokyran 12 | [MpCompatFor("sarg.alphaanimals")] 13 | class AlphaAnimals 14 | { 15 | #region Fields 16 | 17 | private static MethodBase unsafeMethod; 18 | 19 | #endregion 20 | 21 | #region Main patch 22 | 23 | public AlphaAnimals(ModContentPack mod) 24 | { 25 | LongEventHandler.ExecuteWhenFinished(LatePatch); 26 | 27 | #region MP unsafe method patching 28 | 29 | // Only apply if VFE-I2 is inactive. 30 | // Need to check for _steam due to RW bug when 31 | // running a workshop version while a local 32 | // copy of a mod is active. 33 | if (!ModsConfig.IsActive("OskarPotocki.VFE.Insectoid2") && !ModsConfig.IsActive("OskarPotocki.VFE.Insectoid2_steam")) 34 | { 35 | unsafeMethod = AccessTools.DeclaredMethod("AlphaBehavioursAndEvents.BlackCocoon:Tick"); 36 | 37 | // Make sure the method actually exists 38 | if (unsafeMethod != null) 39 | MpCompat.harmony.Patch(AccessTools.DeclaredMethod("Multiplayer.Client.Extensions:PatchMeasure"), 40 | prefix: new HarmonyMethod(DontPatchUnsafeMethods)); 41 | } 42 | 43 | #endregion 44 | } 45 | 46 | private static void LatePatch() 47 | { 48 | #region Gizmos 49 | 50 | { 51 | // Detonate. 52 | // Unused in 1.4 (code moved to Alpha Memes), so it could potentially 53 | // be removed in the future. Include a null method check. 54 | var method = AccessTools.DeclaredMethod($"AlphaBehavioursAndEvents.Pawn_Detonator:{nameof(Pawn.GetGizmos)}"); 55 | if (method != null) 56 | MP.RegisterSyncMethodLambda(method.DeclaringType, method.Name, 0); 57 | } 58 | 59 | #endregion 60 | } 61 | 62 | #endregion 63 | 64 | #region MP unsafe method patching 65 | 66 | private static bool DontPatchUnsafeMethods(MethodBase original) 67 | { 68 | // Multiplayer patches all ticking methods for any Thing 69 | // subtype. Alpha Animals uses methods from Vanilla 70 | // Factions Expanded - Insectoids 2, which (if that mod 71 | // is not loaded) will cause an exception when attempting 72 | // to patch that mod. We cannot patch VFE-I2 method 73 | // directly, as that is specifically the issue MP itself 74 | // is encountering, so we have to prevent MP from patching 75 | // that method to make sure that it can load correctly. 76 | return original != unsafeMethod; 77 | } 78 | 79 | #endregion 80 | } 81 | } --------------------------------------------------------------------------------