diff --git a/Assemblies/RJW.dll b/Assemblies/RJW.dll
index 4723803b72bd4c4cc1db380250d39911473895cf..77d366e50e950ecb091bfa35c8eec3659d63f04e 100644
Binary files a/Assemblies/RJW.dll and b/Assemblies/RJW.dll differ
diff --git a/Multiplayer.txt b/Multiplayer.txt
index 9627f4810abe768896334e83cbc44e5f2f9531aa..4e8b6e7607480e36a168bb4346d7d33fe836515f 100644
--- a/Multiplayer.txt
+++ b/Multiplayer.txt
@@ -1,23 +1,10 @@
 Multiplayer:
-Zetrith's Multiplayer https://ludeon.com/forums/index.php?topic=47445.0
-UnofficialMultiplayerAPI https://github.com/Pecius/UnofficialMultiplayerAPI
+https://github.com/Parexy/Multiplayer
 =====================================================
-working:
-sex
-rape
-records
-operations
-bondage
-gender change effects
 
 not working:
-nymph incidents
-after sex log and social stuff is not added
-(rape) mood effects
-pregnancy?
+-?
 
 desync:
-spawn things with dev mode
-changing designators after host started hosing
-added traits (not sure if they get added after sex)
+-?
 =====================================================
\ No newline at end of file
diff --git a/RimJobWorld.Main.csproj b/RimJobWorld.Main.csproj
index 40517a6809f19e673eeb4ce6db114c6115601fca..5f54510e7f57f963a826fdba8a4a875d705b39f0 100644
--- a/RimJobWorld.Main.csproj
+++ b/RimJobWorld.Main.csproj
@@ -44,6 +44,7 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Source\Harmony\patch_races.cs" />
+    <Compile Include="Source\JobDrivers\JobDriver_Sex.cs" />
     <Compile Include="Source\Modules\Bondage\bondage_gear.cs" />
     <Compile Include="Source\Common\Breeder_Helper.cs" />
     <Compile Include="Source\Modules\Multiplayer\Multiplayer.cs" />
@@ -219,6 +220,7 @@
     <Content Include="About\Preview.png" />
     <Content Include="Assemblies\%24HugsLibChecker.dll" />
     <Content Include="Assemblies\0Harmony.dll" />
+    <Content Include="Assemblies\0MultiplayerAPI.dll" />
     <Content Include="Assemblies\RJW.dll" />
     <Compile Include="Source\Settings\Settings.cs" />
     <Content Include="Defs\BodyPartDefs\BodyParts_Humanlike.xml">
diff --git a/Source/Common/Breeder_Helper.cs b/Source/Common/Breeder_Helper.cs
index a1930561c57bd6bfeed5e24616c9fe44eca981b9..216973ceb4d6a96157b0463c922004015f66454d 100644
--- a/Source/Common/Breeder_Helper.cs
+++ b/Source/Common/Breeder_Helper.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
 using System.Linq;
 using RimWorld;
 using System.Diagnostics;
+using Multiplayer.API;
 
 namespace rjw
 { 
@@ -23,6 +24,7 @@ namespace rjw
 		}
 
 		//animal try to find best designated pawn to breed
+		[SyncMethod]
 		public static Pawn find_designated_breeder(Pawn pawn, Map m)
 		{
 			DebugText("BreederHelper::find_designated_breeder( " + xxx.get_pawnname(pawn) + " ) called");
@@ -57,6 +59,7 @@ namespace rjw
 
 		//animal or human try to find animals to breed
 		//public static Pawn find_breeder_animal(Pawn pawn, Map m, bool SameRace = true)
+		[SyncMethod]
 		public static Pawn find_breeder_animal(Pawn pawn, Map m)
 		{
 			DebugText("BreederHelper::find_breeder_animal( " + xxx.get_pawnname(pawn) + " ) called");
@@ -136,6 +139,8 @@ namespace rjw
 			}
 
 			DebugText(valid_targets.Count() + " valid targets found on map.");
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return valid_targets.Any() ? valid_targets.RandomElement() : null;
 		}
 	}
diff --git a/Source/Common/CORE_EXPOSED/PawnGenerator.cs b/Source/Common/CORE_EXPOSED/PawnGenerator.cs
index bd43aab20ba034b2e58dbb874e0481ffde83bdfb..3041ba834b30b15325960fb608b8a257e9bafe8c 100644
--- a/Source/Common/CORE_EXPOSED/PawnGenerator.cs
+++ b/Source/Common/CORE_EXPOSED/PawnGenerator.cs
@@ -16,6 +16,7 @@ using System.Linq;
 using System.Runtime.InteropServices;
 using UnityEngine;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -158,9 +159,12 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		private static Pawn GenerateOrRedressPawnInternal(PawnGenerationRequest request)
 		{
 			Pawn result = null;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (!request.Newborn && !request.ForceGenerateNewPawn)
 			{
 				if (request.ForceRedressWorldPawnIfFormerColonist)
@@ -364,11 +368,14 @@ namespace rjw
 			return pawn;
 		}
 
+		[SyncMethod]
 		private static Pawn TryGenerateNewPawnInternal(ref PawnGenerationRequest request, out string error, bool ignoreScenarioRequirements, bool ignoreValidator)
 		{
 			error = null;
 			Pawn pawn = (Pawn)ThingMaker.MakeThing(request.KindDef.race);
 			pawnsBeingGenerated.Add(new PawnGenerationStatus(pawn, null));
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			try
 			{
 				pawn.kindDef = request.KindDef;
@@ -551,6 +558,7 @@ namespace rjw
 			PawnInventoryGenerator.GenerateInventoryFor(pawn, request);
 		}
 
+		[SyncMethod]
 		private static void GenerateInitialHediffs(Pawn pawn, PawnGenerationRequest request)
 		{
 			int num = 0;
@@ -591,8 +599,11 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		private static void GenerateRandomAge(Pawn pawn, PawnGenerationRequest request)
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (request.FixedBiologicalAge.HasValue && request.FixedChronologicalAge.HasValue)
 			{
 				float? fixedBiologicalAge = request.FixedBiologicalAge;
@@ -671,15 +682,19 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		public static int RandomTraitDegree(TraitDef traitDef)
 		{
 			if (traitDef.degreeDatas.Count == 1)
 			{
 				return traitDef.degreeDatas[0].degree;
 			}
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return traitDef.degreeDatas.RandomElementByWeight((TraitDegreeData dd) => dd.commonality).degree;
 		}
 
+		[SyncMethod]
 		private static void GenerateTraits(Pawn pawn, PawnGenerationRequest request)
 		{
 			if (pawn.story != null)
@@ -716,6 +731,8 @@ namespace rjw
 						}
 					}
 				}
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 				int num = Rand.RangeInclusive(2, 3);
 				if (request.AllowGay && (LovePartnerRelationUtility.HasAnyLovePartnerOfTheSameGender(pawn) || LovePartnerRelationUtility.HasAnyExLovePartnerOfTheSameGender(pawn)))
 				{
@@ -748,8 +765,11 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		private static void GenerateBodyType(Pawn pawn)
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (pawn.story.adulthood != null)
 			{
 				pawn.story.bodyType = pawn.story.adulthood.BodyTypeFor(pawn.gender);
@@ -764,6 +784,7 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		private static void GenerateSkills(Pawn pawn)
 		{
 			List<SkillDef> allDefsListForReading = DefDatabase<SkillDef>.AllDefsListForReading;
@@ -775,6 +796,8 @@ namespace rjw
 				skill.Level = num;
 				if (!skill.TotallyDisabled)
 				{
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					float num2 = (float)num * 0.11f;
 					float value = Rand.Value;
 					if (value < num2)
@@ -793,8 +816,11 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		private static int FinalLevelOfSkill(Pawn pawn, SkillDef sk)
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			float num = (!sk.usuallyDefinedInBackstories) ? Rand.ByCurve(LevelRandomCurve) : ((float)Rand.RangeInclusive(0, 4));
 			foreach (Backstory item in from bs in pawn.story.AllBackstories
 			where bs != null
@@ -836,10 +862,13 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		private static void GeneratePawnRelations(Pawn pawn, ref PawnGenerationRequest request)
 		{
 			if (pawn.RaceProps.Humanlike)
 			{
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 				Pawn[] array = (from x in PawnsFinder.AllMapsWorldAndTemporary_AliveOrDead
 				where x.def == pawn.def
 				select x).ToArray();
@@ -875,9 +904,12 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		private static Pair<Pawn, PawnRelationDef>[] GenerateSamples(Pawn[] pawns, PawnRelationDef[] relations, int count)
 		{
 			Pair<Pawn, PawnRelationDef>[] array = new Pair<Pawn, PawnRelationDef>[count];
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			for (int i = 0; i < count; i++)
 			{
 				array[i] = new Pair<Pawn, PawnRelationDef>(pawns[Rand.Range(0, pawns.Length)], relations[Rand.Range(0, relations.Length)]);
diff --git a/Source/Common/Genital_Helper.cs b/Source/Common/Genital_Helper.cs
index 9f79959076976c076892528e6ecd073fac03a5d6..3ad743bca85c37d150576efd59049f055a0fcba6 100644
--- a/Source/Common/Genital_Helper.cs
+++ b/Source/Common/Genital_Helper.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using Verse;
 using System;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -340,6 +341,7 @@ namespace rjw
 			return (has_penis(pawn) && has_vagina(pawn));
 		}
 
+		[SyncMethod]
 		public static double GenderTechLevelCheck(Pawn pawn)
 		{
 			if (pawn == null)
@@ -348,6 +350,8 @@ namespace rjw
 
 			bool lowtechlevel = true;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			double value = Rand.Value;
 
 			if (pawn.Faction != null)
@@ -368,6 +372,7 @@ namespace rjw
 			return (pawn.gender == Gender.Male) ? (gender != Gender.Female) : (gender == Gender.Male);
 		}
 
+		[SyncMethod]
 		public static void add_genitals(Pawn pawn, Pawn parent = null, Gender gender = Gender.None)
 		{
 			//--Log.Message("Genital_Helper::add_genitals( " + xxx.get_pawnname(pawn) + " ) called");
@@ -713,6 +718,8 @@ namespace rjw
 			else if (racename.Contains("impmother") || racename.Contains("demon"))
 			{
 				pawn.health.AddHediff((privates_gender(pawn, gender)) ? demon_penis : demon_vagina, Part);
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 				if (Rand.Value < 0.25f)
 					pawn.health.AddHediff((privates_gender(pawn, gender)) ? demon_penis : demonT_penis, Part);
 				return;
@@ -720,6 +727,8 @@ namespace rjw
 			//animal demons - MoreMonstergirls
 			else if (racename.Contains("baphomet"))
 			{
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 				if (Rand.Value < 0.50f)
 					pawn.health.AddHediff((privates_gender(pawn, gender)) ? demon_penis : demon_vagina, Part);
 				else
@@ -1201,12 +1210,15 @@ namespace rjw
 			pawn.health.AddHediff(asshole, Part);
 		}
 
+		[SyncMethod]
 		public static void sexualize_pawn(Pawn pawn)
 		{
 			//Log.Message("[RJW] sexualize_pawn( " + xxx.get_pawnname(pawn) + " ) called");
 			if (pawn == null)
 				return;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (!pawn.RaceProps.hasGenders)
 			{
 				if (Current.ProgramState == ProgramState.Playing) // DO NOT run at world generation, throws error
diff --git a/Source/Common/PawnData.cs b/Source/Common/PawnData.cs
index 22a72cc9d1fd46d398c9808d74d0c240e861bb85..55a1c2a34489dde418b76f8593610276b614a04f 100644
--- a/Source/Common/PawnData.cs
+++ b/Source/Common/PawnData.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
 using System.Linq;
 using Verse;
 using RimWorld;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -37,6 +38,7 @@ namespace rjw
 		public bool Breeding = false;
 		public bool Milking = false;
 		public bool Hero = false;
+		public string HeroOwner = "";
 		public bool BreedingAnimal = false;
 		public Pawn Pawn = null;
 
@@ -56,69 +58,132 @@ namespace rjw
 			Scribe_Values.Look<bool>(ref Breeding, "Breeding", false, true);
 			Scribe_Values.Look<bool>(ref Milking, "Milking", false, true);
 			Scribe_Values.Look<bool>(ref Hero, "Hero", false, true);
+			Scribe_Values.Look<String>(ref HeroOwner, "HeroOwner", "", true);
 			Scribe_Values.Look<bool>(ref BreedingAnimal, "BreedingAnimal", false, true);
 			Scribe_References.Look<Pawn>(ref this.Pawn, "Pawn");
 		}
 
 		public bool IsValid { get { return Pawn != null; } }
 	}
-	//Shitcode ahead
 	public static class PawnDesignations
 	{
-		public static bool IsDesignatedComfort(this Pawn pawn)
+		public static bool CanChangeDesignationColonist(this Pawn pawn)
 		{
-			//return comfort_prisoners.is_designated(pawn);
-			if (!CanDesignateComfort(pawn))
-				UnDesignateComfort(pawn);
+			//check if pawn is a hero of other player
+			//limit widget access in mp
 
-			return SaveStorage.DataStore.GetPawnData(pawn).Comfort;
-		}
-		public static bool IsDesignatedService(this Pawn pawn)
-		{
-			if (!CanDesignateService(pawn))
-				UnDesignateService(pawn);
+			//Log.Warning("CanChangeDesignationColonist:: IsHeroOwner" + pawn.Name + " IsDesignatedHero" + pawn.IsDesignatedHero() + " IsHeroOwner" + pawn.IsHeroOwner());
+			if ((pawn.IsDesignatedHero() && !pawn.IsHeroOwner()))
+				return false;
 
-			return SaveStorage.DataStore.GetPawnData(pawn).Service;
+			return true;
 		}
-		public static bool IsDesignatedBreeding(this Pawn pawn)
+		public static bool CanChangeDesignationPrisoner(this Pawn pawn)
 		{
-			if (!CanDesignateBreed(pawn))
-				UnDesignateBreeding(pawn);
+			//check if player hero slave/prisoner
+			//limit widget access in mp
 
-			return SaveStorage.DataStore.GetPawnData(pawn).Breeding;
+			foreach (Pawn item in Find.AnyPlayerHomeMap.mapPawns.AllPawnsSpawned.ToList().Where(x => x.IsDesignatedHero()))
+			//foreach (Pawn item in AllDesignationsOn(pawn.Map).Where(data => (data.Hero)).Select(data => data.Pawn))
+			{
+				//Log.Warning(item.IsHeroOwner() + " IsHeroOwner" + item.Name );
+				if (item.IsHeroOwner())
+				{
+					if (item.IsPrisonerOfColony || xxx.is_slave(item))
+					{
+						//Log.Warning("Hero of "+MP.PlayerName+" is prisoner");
+						return false;
+					}
+				}
+			}
+
+			return true;
 		}
-		public static bool IsDesignatedMilking(this Pawn pawn)
+		public static bool CanDesignateComfort(this Pawn pawn)
 		{
-			if (!CanDesignateMilk(pawn))
-				UnDesignateMilking(pawn);
+			//no permission to change designation for NON prisoner hero/ other player
+			if (!pawn.CanChangeDesignationPrisoner() && !pawn.CanChangeDesignationColonist())
+				return false;
 
-			return SaveStorage.DataStore.GetPawnData(pawn).Milking;
+			//no permission to change designation for prisoner hero/ self
+			if (!pawn.CanChangeDesignationPrisoner())
+				return false;
+
+			//cant sex
+			if (!(xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn)))
+				return false;
+
+			if (!pawn.IsDesignatedHero())
+			{
+				if (xxx.is_masochist(pawn) && pawn.IsColonist)
+					return true;
+			}
+			else if (pawn.IsHeroOwner())
+				return true;
+
+			if (pawn.IsPrisonerOfColony || xxx.is_slave(pawn))
+				return true;
+
+			return false;
 		}
-		public static bool IsDesignatedHero(this Pawn pawn)
+		public static bool CanDesignateService(this Pawn pawn)
 		{
-			return SaveStorage.DataStore.GetPawnData(pawn).Hero;
+			//no permission to change designation for NON prisoner hero/ other player
+			if (!pawn.CanChangeDesignationPrisoner() && !pawn.CanChangeDesignationColonist())
+				return false;
+
+			//no permission to change designation for prisoner hero/ self
+			if (!pawn.CanChangeDesignationPrisoner())
+				return false;
+
+			//cant sex
+			if (!(xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn)))
+				return false;
+
+			if (!pawn.IsDesignatedHero())
+			{
+				if (pawn.IsColonist)
+					return true;
+			}
+			else if (pawn.IsHeroOwner())
+				return true;
+
+			if (pawn.IsPrisonerOfColony || xxx.is_slave(pawn))
+				return true;
+
+			return false;
 		}
-		public static bool IsDesignatedBreedingAnimal(this Pawn pawn)
+		public static bool CanDesignateBreeding(this Pawn pawn)
 		{
-			if (!CanDesignateBreedAnimal(pawn))
-				UnDesignateBreedingAnimal(pawn);
+			//no permission to change designation for NON prisoner hero/ other player
+			if (!pawn.CanChangeDesignationPrisoner() && !pawn.CanChangeDesignationColonist())
+				return false;
 
-			return SaveStorage.DataStore.GetPawnData(pawn).BreedingAnimal;
-		}
+			//no permission to change designation for prisoner hero/ self
+			if (!pawn.CanChangeDesignationPrisoner())
+				return false;
 
-		public static bool CanDesignateComfort(this Pawn pawn) { return (RJWSettings.WildMode || (pawn.IsPrisonerOfColony || xxx.is_slave(pawn) || (xxx.is_masochist(pawn) && pawn.IsColonist) || pawn.IsDesignatedHero()) && (xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn))); }
-		public static bool CanDesignateService(this Pawn pawn) { return (RJWSettings.WildMode || (pawn.IsColonist || pawn.IsPrisonerOfColony || pawn.IsDesignatedHero()) && (xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn))); }
-		public static bool CanDesignateBreed(this Pawn pawn)
-		{
-			if (RJWSettings.bestiality_enabled
-				&& xxx.is_human(pawn)
-				&& xxx.can_be_fucked(pawn)
-				&& (RJWSettings.WildMode || (pawn.IsPrisonerOfColony || xxx.is_slave(pawn) || (xxx.is_zoophile(pawn) && pawn.IsColonist)) || pawn.IsDesignatedHero()))
-				return true;
+			//cant sex
+			if (!xxx.can_be_fucked(pawn))
+				return false;
+
+
+			if (RJWSettings.bestiality_enabled && xxx.is_human(pawn))
+			{
+				if (!pawn.IsDesignatedHero())
+				{
+					if (xxx.is_zoophile(pawn) && pawn.IsColonist)
+						return true;
+				}
+				else if (pawn.IsHeroOwner())
+					return true;
+
+				if (pawn.IsPrisonerOfColony || xxx.is_slave(pawn))
+					return true;
+			}
 
 			if (RJWSettings.animal_on_animal_enabled
 				&& xxx.is_animal(pawn)
-				&& xxx.can_be_fucked(pawn)
 				&& pawn.Faction == Faction.OfPlayer)
 				return true;
 
@@ -126,6 +191,14 @@ namespace rjw
 		}
 		public static bool CanDesignateBreedAnimal(this Pawn pawn)
 		{
+			//no permission to change designation for NON prisoner hero/ other player
+			if (!pawn.CanChangeDesignationPrisoner() && !pawn.CanChangeDesignationColonist())
+				return false;
+
+			//no permission to change designation for prisoner hero/ self
+			if (!pawn.CanChangeDesignationPrisoner())
+				return false;
+
 			//Log.Message("CanDesignateAnimal for " + xxx.get_pawnname(pawn) + " " + SaveStorage.bestiality_enabled);
 			//Log.Message("checking animal props " + (pawn.Faction?.IsPlayer.ToString()?? "tynanfag") + xxx.is_animal(pawn) + xxx.can_rape(pawn));
 			if ((RJWSettings.bestiality_enabled || RJWSettings.animal_on_animal_enabled)
@@ -137,7 +210,7 @@ namespace rjw
 			return false;
 
 		}
-		public static bool CanDesignateMilk(this Pawn pawn) { return false; }//todo
+		public static bool CanDesignateMilking(this Pawn pawn) { return false; }//todo
 		public static bool CanDesignateHero(this Pawn pawn)
 		{
 			if ((RJWSettings.RPG_hero_control)
@@ -145,12 +218,18 @@ namespace rjw
 				&& pawn.IsColonist
 				&& pawn.Map.IsPlayerHome)
 			{
-				foreach (Pawn item in Find.ColonistBar.GetColonistsInOrder())
-				//foreach (Pawn item in Find.AnyPlayerHomeMap.mapPawns.AllPawnsSpawned.ToList())
+				//foreach (Pawn item in (Find.ColonistBar.GetColonistsInOrder()))
+				foreach (Pawn item in Find.AnyPlayerHomeMap.mapPawns.AllPawnsSpawned.ToList())
 				{
 					if (item.IsDesignatedHero())
 					{
-						return false;
+						if (item.IsHeroOwner())
+						{
+							//Log.Warning("CanDesignateHero:: "  + MP.PlayerName + " already has hero - " + item.Name);
+							return false;
+						}
+						else
+							continue;
 					}
 				}
 				return true;
@@ -158,59 +237,208 @@ namespace rjw
 			return false;
 		}
 
-		public static void DesignateComfort(this Pawn pawn)
+
+
+
+		public static void ToggleComfort(this Pawn pawn)
 		{
 			if (pawn.CanDesignateComfort())
 			{
-				SaveStorage.DataStore.GetPawnData(pawn).Comfort = true;
+				if (!pawn.IsDesignatedComfort())
+					DesignateComfort(pawn);
+				else
+					UnDesignateComfort(pawn);
 			}
 		}
-		public static void DesignateService(this Pawn pawn)
+		public static bool IsDesignatedComfort(this Pawn pawn)
+		{
+			//return comfort_prisoners.is_designated(pawn);
+
+			//fix for prisoners becoming colonists, breaks mp permissions, fuck it
+
+			//if (!pawn.IsDesignatedHero() && pawn.CanChangeDesignationColonist() && pawn.CanChangeDesignationPrisoner())
+			//	if (!CanDesignateComfort(pawn))
+			//		UnDesignateComfort(pawn);
+
+			return SaveStorage.DataStore.GetPawnData(pawn).Comfort;
+		}
+		[SyncMethod]
+		public static void DesignateComfort(this Pawn pawn)
+		{
+			SaveStorage.DataStore.GetPawnData(pawn).Comfort = true;
+		}
+		[SyncMethod]
+		public static void UnDesignateComfort(this Pawn pawn)
+		{
+			SaveStorage.DataStore.GetPawnData(pawn).Comfort = false;
+		}
+
+		public static void ToggleService(this Pawn pawn)
 		{
 			if (pawn.CanDesignateService())
 			{
-				SaveStorage.DataStore.GetPawnData(pawn).Service = true;
+				if (!pawn.IsDesignatedService())
+					DesignateService(pawn);
+				else
+					UnDesignateService(pawn);
 			}
 		}
+		public static bool IsDesignatedService(this Pawn pawn)
+		{
+			/*
+			if (pawn.CanChangeDesignationColonist() && pawn.CanChangeDesignationPrisoner())
+				if (!CanDesignateService(pawn))
+					UnDesignateService(pawn);
+			*/
+
+			return SaveStorage.DataStore.GetPawnData(pawn).Service;
+		}
+		[SyncMethod]
+		public static void DesignateService(this Pawn pawn)
+		{
+			SaveStorage.DataStore.GetPawnData(pawn).Service = true;
+		}
+		[SyncMethod]
+		public static void UnDesignateService(this Pawn pawn)
+		{
+			SaveStorage.DataStore.GetPawnData(pawn).Service = false;
+		}
+
+		public static void ToggleBreeding(this Pawn pawn)
+		{
+			if (pawn.CanDesignateBreeding())
+			{
+				if (!pawn.IsDesignatedBreeding())
+					DesignateBreeding(pawn);
+				else
+					UnDesignateBreeding(pawn);
+			}
+		}
+		public static bool IsDesignatedBreeding(this Pawn pawn)
+		{
+			/*
+			if (pawn.CanChangeDesignationColonist() && pawn.CanChangeDesignationPrisoner())
+				if (!CanDesignateBreed(pawn))
+					UnDesignateBreeding(pawn);
+			*/
+
+			return SaveStorage.DataStore.GetPawnData(pawn).Breeding;
+		}
+		[SyncMethod]
 		public static void DesignateBreeding(this Pawn pawn)
 		{
-			if (pawn.CanDesignateBreed())
+			SaveStorage.DataStore.GetPawnData(pawn).Breeding = true;
+		}
+		[SyncMethod]
+		public static void UnDesignateBreeding(this Pawn pawn)
+		{
+			SaveStorage.DataStore.GetPawnData(pawn).Breeding = false;
+		}
+
+		public static void ToggleBreedingAnimal(this Pawn pawn)
+		{
+			if (pawn.CanDesignateBreeding())
 			{
-				SaveStorage.DataStore.GetPawnData(pawn).Breeding = true;
+				if (!pawn.IsDesignatedBreedingAnimal())
+					DesignateBreedingAnimal(pawn);
+				else
+					UnDesignateBreedingAnimal(pawn);
 			}
 		}
+		public static bool IsDesignatedBreedingAnimal(this Pawn pawn)
+		{
+			/*
+			if (pawn.CanChangeDesignationColonist() && pawn.CanChangeDesignationPrisoner())
+				if (!CanDesignateBreedAnimal(pawn))
+					UnDesignateBreedingAnimal(pawn);
+			*/
 
+			return SaveStorage.DataStore.GetPawnData(pawn).BreedingAnimal;
+		}
+		[SyncMethod]
 		public static void DesignateBreedingAnimal(this Pawn pawn)
 		{
-			if (pawn.CanDesignateBreedAnimal())
+			SaveStorage.DataStore.GetPawnData(pawn).BreedingAnimal = true;
+		}
+		[SyncMethod]
+		public static void UnDesignateBreedingAnimal(this Pawn pawn)
+		{
+			SaveStorage.DataStore.GetPawnData(pawn).BreedingAnimal = false;
+		}
+
+		public static void ToggleMilking(this Pawn pawn)
+		{
+			if (pawn.CanDesignateMilking())
 			{
-				SaveStorage.DataStore.GetPawnData(pawn).BreedingAnimal = true;
+				if (!pawn.IsDesignatedMilking())
+					DesignateMilking(pawn);
+				else
+					UnDesignateMilking(pawn);
 			}
 		}
+		public static bool IsDesignatedMilking(this Pawn pawn)
+		{
+			/*
+			if (pawn.CanChangeDesignationColonist() && pawn.CanChangeDesignationPrisoner())
+				if (!CanDesignateMilk(pawn))
+					UnDesignateMilking(pawn);
+			*/
 
+			return SaveStorage.DataStore.GetPawnData(pawn).Milking;
+		}
+		[SyncMethod]
 		public static void DesignateMilking(this Pawn pawn)
 		{
-			if (pawn.CanDesignateMilk())
-			{
-				SaveStorage.DataStore.GetPawnData(pawn).Milking = true;
-			}
+			SaveStorage.DataStore.GetPawnData(pawn).Milking = true;
+		}
+		[SyncMethod]
+		public static void UnDesignateMilking(this Pawn pawn)
+		{
+			SaveStorage.DataStore.GetPawnData(pawn).Milking = false;
 		}
 
-		public static void DesignateHero(this Pawn pawn)
+		public static void ToggleHero(this Pawn pawn)
 		{
 			if (pawn.CanDesignateHero())
 			{
-				SaveStorage.DataStore.GetPawnData(pawn).Hero = true;
-				Log.Message(pawn.Name + " is set to hero:" + SaveStorage.DataStore.GetPawnData(pawn).Hero);
+				if (!pawn.IsDesignatedHero())
+					DesignateHero(pawn);
+				else
+					UnDesignateHero(pawn);
 			}
 		}
+		public static void DesignateHero(this Pawn pawn)
+		{
+			MyMethod(pawn, MP.PlayerName);
+		}
+		public static bool IsDesignatedHero(this Pawn pawn)
+		{
+			return SaveStorage.DataStore.GetPawnData(pawn).Hero;
+		}
+		public static bool IsHeroOwner(this Pawn pawn)
+		{
+			if (!MP.enabled)
+				return SaveStorage.DataStore.GetPawnData(pawn).HeroOwner == "";
+			else
+				return SaveStorage.DataStore.GetPawnData(pawn).HeroOwner == MP.PlayerName;
+		}
+		[SyncMethod]
+		static void MyMethod(Pawn pawn, string theName)
+		{
+			SaveStorage.DataStore.GetPawnData(pawn).Hero = true;
+			SaveStorage.DataStore.GetPawnData(pawn).HeroOwner = theName;
+			string text = pawn.Name + " is now hero of " + theName;
+			Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
+			//Log.Message(MP.PlayerName + "  set " + pawn.Name + " to hero:" + SaveStorage.DataStore.GetPawnData(pawn).Hero);
+		}
+		[SyncMethod]
+		public static void UnDesignateHero(this Pawn pawn)
+		{
+		}
+
+
+
 
-		public static void UnDesignateComfort(this Pawn pawn) { SaveStorage.DataStore.GetPawnData(pawn).Comfort = false; }
-		public static void UnDesignateService(this Pawn pawn) { SaveStorage.DataStore.GetPawnData(pawn).Service = false; }
-		public static void UnDesignateBreeding(this Pawn pawn) { SaveStorage.DataStore.GetPawnData(pawn).Breeding = false; }
-		public static void UnDesignateBreedingAnimal(this Pawn pawn) { SaveStorage.DataStore.GetPawnData(pawn).BreedingAnimal = false; }
-		public static void UnDesignateMilking(this Pawn pawn) { SaveStorage.DataStore.GetPawnData(pawn).Milking = false; }
-		public static void UnDesignateHero(this Pawn pawn) {}
 
 		private static IEnumerable<PawnData> AllDesignationsOn(Map map)
 		{
diff --git a/Source/Common/SexUtility.cs b/Source/Common/SexUtility.cs
index e8327f18ea852c8177e09aca285c93763f96782a..2b0b779112c04fb7504421308d70d0f270c86f51 100644
--- a/Source/Common/SexUtility.cs
+++ b/Source/Common/SexUtility.cs
@@ -8,6 +8,7 @@ using Verse.AI;
 using Verse.Sound;
 using JobDriver_Lovin = rjw_CORE_EXPOSED.JobDriver_Lovin;
 using Harmony;
+using Multiplayer.API;
 
 
 namespace rjw
@@ -229,6 +230,7 @@ namespace rjw
 		}
 
 		// The pawn may or may not clean up the mess after fapping.
+		[SyncMethod]
 		public static bool ConsiderCleaning(Pawn fapper)
 		{
 			if (!RJWSettings.cum_filth) return false;
@@ -276,10 +278,13 @@ namespace rjw
 			if (xxx.is_ascetic(fapper))
 				do_cleaning += 0.2f;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return Rand.Chance(do_cleaning);
 		}
 
 		// <summary>Handles after-sex trait and thought gain, and fluid creation. Initiator of the act (whore, rapist, female zoophile, etc) should be first.</summary>
+		[SyncMethod]
 		private static void Aftersex(Pawn pawn, Pawn partner, bool rape = false, bool isCoreLovin = false, xxx.rjwSextype sextype = xxx.rjwSextype.Vaginal)
 		{
 			bool bothInMap = false;
@@ -292,6 +297,8 @@ namespace rjw
 			pawn.rotationTracker.Face(partner.DrawPos);
 			pawn.rotationTracker.FaceCell(partner.Position);
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (bothInMap)
 			{
 				if (!partner.Dead)
@@ -576,6 +583,7 @@ namespace rjw
 
 		}
 
+		[SyncMethod]
 		private static void CheckTraitGain(Pawn pawn)
 		{
 			if (!xxx.has_traits(pawn) || pawn.records.GetValue(xxx.CountOfSex) <= 10) return;
@@ -595,6 +603,8 @@ namespace rjw
 				if (xxx.is_bloodlust(pawn)) chance += 0.2f;
 				if (xxx.is_psychopath(pawn)) chance += 0.25f;
 
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 				if (Rand.Chance(chance))
 				{
 					pawn.story.traits.GainTrait(new Trait(xxx.rapist));
@@ -628,9 +638,12 @@ namespace rjw
 				pawn.mindState.canLovinTick = currentTime + GenerateMinTicksToNextLovin(pawn);
 		}
 
+		[SyncMethod]
 		public static int GenerateMinTicksToNextLovin(Pawn pawn)
 		{
 			if (DebugSettings.alwaysDoLovin) return 100;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 
 			float interval = JobDriver_Lovin.LovinIntervalHoursFromAgeCurve.Evaluate(ScaleToHumanAge(pawn));
 			float rinterval = Math.Max(0.5f, Rand.Gaussian(interval, 0.3f));
@@ -691,6 +704,7 @@ namespace rjw
 			pawn.Drawer.Notify_MeleeAttackOn(partner);
 		}
 
+		[SyncMethod]
 		private static xxx.rjwSextype DetermineSextype(Pawn pawn, Pawn partner, bool rape, bool whoring, ref Pawn receiving)
 		{
 			//--Log.Message("[RJW]SexUtility::processSex is called for pawn" + xxx.get_pawnname(pawn) + " and partner " + xxx.get_pawnname(partner));
@@ -710,6 +724,9 @@ namespace rjw
 			bool pawnHasHands = pawn.health.hediffSet.GetNotMissingParts().Any(part => part.IsInGroup(BodyPartGroupDefOf.RightHand) || part.IsInGroup(BodyPartGroupDefOf.LeftHand));
 			bool partnerHasHands = partner.health.hediffSet.GetNotMissingParts().Any(part => part.IsInGroup(BodyPartGroupDefOf.RightHand) || part.IsInGroup(BodyPartGroupDefOf.LeftHand));
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
+
 			/*Things to keep in mind:
 			 - Both the initiator and the partner can be female, male, or futa.
 			 - Can be rape or consensual.
diff --git a/Source/Common/xxx.cs b/Source/Common/xxx.cs
index 52eb425f169cf679c44f88fa2209676b6665124a..68dc0f6c7de42f03c2c6144896af1e5ea83968ce 100644
--- a/Source/Common/xxx.cs
+++ b/Source/Common/xxx.cs
@@ -10,6 +10,7 @@ using Verse;
 using Verse.AI;
 using Verse.AI.Group;
 using Verse.Sound;
+using Multiplayer.API;
 //using static RimWorld.Planet.CaravanInventoryUtility;
 //using RimWorldChildren;
 
@@ -243,8 +244,11 @@ namespace rjw
 			return pawn != null && is_human(pawn) && CompRJW.Comp(pawn).quirks.ToString().Contains(quirk);
 		}
 
+		[SyncMethod]
 		public static string random_pick_a_trait(this Pawn pawn)
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return has_traits(pawn) ? pawn.story.traits.allTraits.RandomElement().def.defName : null;
 		}
 
@@ -771,6 +775,7 @@ namespace rjw
 		///	Returns boolean, no more messing around with floats.
 		/// Just a simple 'Would rape? True/false'.
 		/// </summary>
+		[SyncMethod]
 		public static bool would_rape(Pawn rapist, Pawn rapee)
 		{
 			float rape_factor = 0.3f; // start at 30%
@@ -834,6 +839,8 @@ namespace rjw
 			if (rapist.needs.joy != null && rapist.needs.joy.CurLevel < 0.1f) // The rapist is really bored...
 				rape_factor *= 1.2f;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (rapist.relations == null || is_animal(rapist)) return Rand.Chance(rape_factor);
 			int opinion = rapist.relations.OpinionOf(rapee);
 
diff --git a/Source/Comps/CompRJW.cs b/Source/Comps/CompRJW.cs
index 6be8ab1790e0c0b808e5beddac9bad854b24af65..c8a3b444e9cfef665e2dc92346427e31407f8a8b 100644
--- a/Source/Comps/CompRJW.cs
+++ b/Source/Comps/CompRJW.cs
@@ -3,6 +3,7 @@ using SyrTraits;
 using System.Text;
 using Verse;
 using RimWorld;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -144,12 +145,15 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		public void GenerateQuirks(Pawn pawn)
 		{
 			// No quirks for droids, at least not for now.
 			if (pawn.kindDef.race.defName.ToLower().Contains("droid") || xxx.is_mechanoid(pawn))
-				return; 
+				return;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (Rand.Chance(0.025f))
 				quirks.AppendWithComma("Messy"); // Increased cum generation.
 
@@ -311,6 +315,7 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		public static bool CheckPreference(Pawn pawn, Pawn partner)
 		{
 			if (xxx.RomanceDiversifiedIsActive || (xxx.has_traits(pawn) && pawn.story.traits.HasTrait(TraitDefOf.Gay)))
@@ -342,6 +347,8 @@ namespace rjw
 			bool isHomo = (Genital_Helper.has_vagina(pawn) && Genital_Helper.has_vagina(partner)) ||
 						   ((Genital_Helper.has_penis(partner) || Genital_Helper.has_penis_infertile(partner)) && (Genital_Helper.has_penis(pawn) || Genital_Helper.has_penis_infertile(pawn)));
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			switch (ori)
 			{
 				case Orientation.Heterosexual:
@@ -363,9 +370,12 @@ namespace rjw
 		}
 
 		// Just an example for now. Not used anywhere yet.
+		[SyncMethod]
 		public Orientation RollOrientation()
 		{
-			float random = UnityEngine.Random.Range(0f, 1f);
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
+			float random = Rand.Range(0f, 1f);
 			float checkpoint = RJWPreferenceSettings.asexual_ratio / RJWPreferenceSettings.GetTotal();
 
 			float checkpoint_pan = checkpoint + (RJWPreferenceSettings.pansexual_ratio / RJWPreferenceSettings.GetTotal());
@@ -395,9 +405,12 @@ namespace rjw
 
 		// Simpler system for animals, with most of them being heterosexual.
 		// Don't want to disturb player breeding projects by adding too many gay animals.
+		[SyncMethod]
 		public Orientation RollAnimalOrientation()
 		{
-			var random = UnityEngine.Random.Range(0f, 1f);
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
+			float random = Rand.Range(0f, 1f);
 
 			if (random < 0.03f)
 				return Orientation.Asexual;
diff --git a/Source/Designators/RJWdesignations.cs b/Source/Designators/RJWdesignations.cs
index 315934ccae57f9226d6879efa6f5e765c6526ec2..b8bde8abc62bc7f4bdfc00d2b8eb424b883b9ce8 100644
--- a/Source/Designators/RJWdesignations.cs
+++ b/Source/Designators/RJWdesignations.cs
@@ -108,18 +108,20 @@ namespace rjw
 			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/ComfortPrisoner_off");
 			static readonly Texture2D iconRefuse = ContentFinder<Texture2D>.Get("UI/Commands/ComfortPrisoner_Refuse");
 			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/ComfortPrisoner_on");
-			public override Texture2D texture(Pawn pawn) => pawn.CanDesignateComfort() && xxx.is_human(pawn) ? iconAccept : iconRefuse;
+			public override Texture2D texture(Pawn pawn) => (pawn.CanDesignateComfort() || pawn.IsDesignatedComfort()) && xxx.is_human(pawn) ? iconAccept : iconRefuse;
 			public override Texture2D cancel { get; } = iconCancel;
 
 			static readonly Vector2 posComf = new Vector2(IconGap + IconSize, 0);
 			public override Vector2 offset => posComf;
 
-			public override string desc(Pawn pawn) => pawn.CanDesignateComfort() ? "ForComfortDesc" : "ForComfortRefuseDesc";
+			public override string desc(Pawn pawn) => pawn.CanDesignateComfort() ? "ForComfortDesc" : 
+														!pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : 
+														!pawn.CanChangeDesignationColonist() ? "ForHeroRefuse1Desc" : "ForComfortRefuseDesc";
 
 			public override bool applicable(Pawn pawn) => xxx.is_human(pawn);
 			public override bool applied(Pawn pawn) => pawn.IsDesignatedComfort();
-			public override void apply(Pawn pawn) => pawn.DesignateComfort();
-			public override void unapply(Pawn pawn) => pawn.UnDesignateComfort();
+			public override void apply(Pawn pawn) => pawn.ToggleComfort();
+			public override void unapply(Pawn pawn) => pawn.ToggleComfort();
 		}
 		[StaticConstructorOnStartup]
 		public class Service : SubIcon
@@ -128,17 +130,19 @@ namespace rjw
 			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/Service_off");
 			static readonly Texture2D iconRefuse = ContentFinder<Texture2D>.Get("UI/Commands/Service_Refuse");
 			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/Service_on");
-			public override Texture2D texture(Pawn pawn) => pawn.CanDesignateService() && xxx.is_human(pawn) ? iconAccept : iconRefuse;
+			public override Texture2D texture(Pawn pawn) => (pawn.CanDesignateService() || pawn.IsDesignatedService()) && xxx.is_human(pawn) ? iconAccept : iconRefuse;
 			public override Texture2D cancel { get; } = iconCancel;
 
 			public override Vector2 offset { get; } = new Vector2(0, 0);
 
-			public override string desc(Pawn pawn) => pawn.CanDesignateService() ? "ForServiceDesc" : "ForServiceRefuseDesc";
+			public override string desc(Pawn pawn) => pawn.CanDesignateService() ? "ForServiceDesc" :
+														!pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : 
+														!pawn.CanChangeDesignationColonist() ? "ForHeroRefuse1Desc" : "ForServiceRefuseDesc";
 
 			public override bool applicable(Pawn pawn) => xxx.is_human(pawn);
 			public override bool applied(Pawn pawn) => pawn.IsDesignatedService() && xxx.is_human(pawn);
-			public override void apply(Pawn pawn) => pawn.DesignateService();
-			public override void unapply(Pawn pawn) => pawn.UnDesignateService();
+			public override void apply(Pawn pawn) => pawn.ToggleService();
+			public override void unapply(Pawn pawn) => pawn.ToggleService();
 		}
 		[StaticConstructorOnStartup]
 		public class BreedingHuman : SubIcon
@@ -147,17 +151,19 @@ namespace rjw
 			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/Breeding_Pawn_off");
 			static readonly Texture2D iconRefuse = ContentFinder<Texture2D>.Get("UI/Commands/Breeding_Pawn_Refuse");
 			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/Breeding_Pawn_on");
-			public override Texture2D texture(Pawn pawn) => (pawn.CanDesignateBreed() || pawn.IsDesignatedBreeding()) && xxx.is_human(pawn) ? iconAccept : iconRefuse;
+			public override Texture2D texture(Pawn pawn) => (pawn.CanDesignateBreeding() || pawn.IsDesignatedBreeding()) && xxx.is_human(pawn) ? iconAccept : iconRefuse;
 			public override Texture2D cancel { get; } = iconCancel;
 
 			public override Vector2 offset { get; } = new Vector2(0, IconSize + IconGap);
 
-			public override string desc(Pawn pawn) => (pawn.CanDesignateBreed() || pawn.IsDesignatedBreeding()) && xxx.is_human(pawn) ? "ForBreedingDesc" : "ForBreedingRefuseDesc";
+			public override string desc(Pawn pawn) => pawn.CanDesignateBreeding() && xxx.is_human(pawn) ? "ForBreedingDesc" :
+														!pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : 
+														!pawn.CanChangeDesignationColonist() ? "ForHeroRefuse1Desc" : "ForBreedingRefuseDesc";
 
 			public override bool applicable(Pawn pawn) => xxx.is_human(pawn);
 			public override bool applied(Pawn pawn) => pawn.IsDesignatedBreeding() && xxx.is_human(pawn);
-			public override void apply(Pawn pawn) => pawn.DesignateBreeding();
-			public override void unapply(Pawn pawn) => pawn.UnDesignateBreeding();
+			public override void apply(Pawn pawn) => pawn.ToggleBreeding();
+			public override void unapply(Pawn pawn) => pawn.ToggleBreeding();
 		}
 
 		[StaticConstructorOnStartup]
@@ -171,12 +177,12 @@ namespace rjw
 
 			public override Vector2 offset { get; } = new Vector2(0, IconSize + IconGap);
 
-			public override string desc(Pawn pawn) => "ForBreedingDesc";
+			public override string desc(Pawn pawn) => !pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : "ForBreedingDesc";
 
-			public override bool applicable(Pawn pawn) => (pawn.CanDesignateBreed() || pawn.IsDesignatedBreeding()) && xxx.is_animal(pawn);
+			public override bool applicable(Pawn pawn) => (pawn.CanDesignateBreeding() || pawn.IsDesignatedBreeding()) && xxx.is_animal(pawn);
 			public override bool applied(Pawn pawn) => pawn.IsDesignatedBreeding() && xxx.is_animal(pawn);
-			public override void apply(Pawn pawn) => pawn.DesignateBreeding();
-			public override void unapply(Pawn pawn) => pawn.UnDesignateBreeding();
+			public override void apply(Pawn pawn) => pawn.ToggleBreeding();
+			public override void unapply(Pawn pawn) => pawn.ToggleBreeding();
 		}
 		[StaticConstructorOnStartup]
 		public class Breeder : SubIcon
@@ -189,12 +195,12 @@ namespace rjw
 
 			public override Vector2 offset { get; } = new Vector2(0, 0);
 
-			public override string desc(Pawn pawn) => "ForBreedingAnimalDesc";
+			public override string desc(Pawn pawn) => !pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : "ForBreedingAnimalDesc";
 
 			public override bool applicable(Pawn pawn) => pawn.CanDesignateBreedAnimal() || pawn.IsDesignatedBreedingAnimal();
 			public override bool applied(Pawn pawn) => pawn.IsDesignatedBreedingAnimal();
-			public override void apply(Pawn pawn) => pawn.DesignateBreedingAnimal();
-			public override void unapply(Pawn pawn) => pawn.UnDesignateBreedingAnimal();
+			public override void apply(Pawn pawn) => pawn.ToggleBreedingAnimal();
+			public override void unapply(Pawn pawn) => pawn.ToggleBreedingAnimal();
 		}
 		[StaticConstructorOnStartup]
 		public class Milking : SubIcon
@@ -206,12 +212,12 @@ namespace rjw
 
 			public override Vector2 offset { get; } = new Vector2(IconGap + IconSize, IconSize + IconGap);
 
-			public override string desc(Pawn pawn) => "ForMilkingDesc";
+			public override string desc(Pawn pawn) => !pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : "ForMilkingDesc";
 
-			public override bool applicable(Pawn pawn) => pawn.CanDesignateMilk() || pawn.IsDesignatedMilking();
+			public override bool applicable(Pawn pawn) => pawn.CanDesignateMilking() || pawn.IsDesignatedMilking();
 			public override bool applied(Pawn pawn) => pawn.IsDesignatedMilking();
-			public override void apply(Pawn pawn) => pawn.DesignateMilking();
-			public override void unapply(Pawn pawn) => pawn.UnDesignateMilking();
+			public override void apply(Pawn pawn) => pawn.ToggleMilking();
+			public override void unapply(Pawn pawn) => pawn.ToggleMilking();
 		}
 		[StaticConstructorOnStartup]
 		public class Hero : SubIcon
@@ -223,12 +229,14 @@ namespace rjw
 
 			public override Vector2 offset { get; } = new Vector2(IconGap + IconSize, IconSize + IconGap);
 
-			public override string desc(Pawn pawn) => "ForHeroDesc";
+			//public override string desc(Pawn pawn) => pawn.CanDesignateHero() || (pawn.IsDesignatedHero() && pawn.IsHeroOwner()) ? "ForHeroDesc" : "Hero of " + SaveStorage.DataStore.GetPawnData(pawn).HeroOwner;
+			public override string desc(Pawn pawn) => pawn.CanDesignateHero() ? "ForHeroDesc" :
+														pawn.IsDesignatedHero() ? "Hero of " + SaveStorage.DataStore.GetPawnData(pawn).HeroOwner : "ForHeroDesc";
 
 			public override bool applicable(Pawn pawn) => pawn.CanDesignateHero() || pawn.IsDesignatedHero();
 			public override bool applied(Pawn pawn) => pawn.IsDesignatedHero();
-			public override void apply(Pawn pawn) => pawn.DesignateHero();
-			public override void unapply(Pawn pawn) => pawn.UnDesignateHero();
+			public override void apply(Pawn pawn) => pawn.ToggleHero();
+			public override void unapply(Pawn pawn) => pawn.ToggleHero();
 		}
 	}
 }
diff --git a/Source/Harmony/patch_pregnancy.cs b/Source/Harmony/patch_pregnancy.cs
index e896417f125d72308b9ae8681723553edde9db6d..4b8e1b6ba501427608a0c434ac89ca2e88675da4 100644
--- a/Source/Harmony/patch_pregnancy.cs
+++ b/Source/Harmony/patch_pregnancy.cs
@@ -4,6 +4,7 @@ using RimWorld;
 using RimWorld.Planet;
 using UnityEngine;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -21,6 +22,7 @@ namespace rjw
 		/// <param name="father"></param>
 		/// <returns></returns>
 		[HarmonyPrefix]
+		[SyncMethod]
 		private static bool on_begin_DoBirthSpawn(ref Pawn mother, ref Pawn father)
 		{
 			//--Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn() called");
@@ -69,6 +71,8 @@ namespace rjw
 				Log.Error("Hediff_Pregnant::DoBirthSpawn() - mother is male -> exit");
 				return false;
 			}
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 
 			// get a reference to the hediff we are applying
 			//do birth for vanilla pregnancy, if using rjw - add RJW pregnancy and birth it instead
diff --git a/Source/Harmony/patch_semenOverlay.cs b/Source/Harmony/patch_semenOverlay.cs
index f750227309dc4ed9059a5273d6c5399f72fa7067..441629e65b26ffea95a8cf0906e09b77988e4858 100644
--- a/Source/Harmony/patch_semenOverlay.cs
+++ b/Source/Harmony/patch_semenOverlay.cs
@@ -4,6 +4,7 @@ using Harmony;
 using UnityEngine;
 using System;
 using RimWorld;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -70,32 +71,7 @@ namespace rjw
 				addSemen.defaultLabel = "AddSemen";
 				addSemen.action = delegate ()
 				{
-					Pawn pawn = __instance;
-
-					if (!pawn.Dead && pawn.records != null)
-					{
-						//get all acceptable body parts:
-						IEnumerable<BodyPartRecord> filteredParts = SemenHelper.getAvailableBodyParts(pawn);
-
-						//select random part:
-						BodyPartRecord randomPart;
-						//filteredParts.TryRandomElement<BodyPartRecord>(out randomPart);
-						//for testing - choose either genitals or anus:
-						if (Rand.Value > 0.5f)
-						{
-							randomPart = pawn.RaceProps.body.AllParts.Find(x => x.def == xxx.anus.def);
-						}
-						else
-						{
-							randomPart = pawn.RaceProps.body.AllParts.Find(x => x.def == xxx.genitals.def);
-						}
-
-						if (randomPart != null)
-						{
-							SemenHelper.cumOn(pawn, randomPart, 0.2f, null, SemenHelper.CUM_NORMAL);
-						}
-
-					};
+					Addsemen(__instance);
 				};
 
 				NewList.Add(addSemen);
@@ -107,5 +83,37 @@ namespace rjw
 			__result = output;
 
 		}
+
+		[SyncMethod]
+		static void Addsemen(Pawn pawn)
+		{
+			//Log.Message("add semen button is pressed for " + pawn);
+
+			if (!pawn.Dead && pawn.records != null)
+			{
+				//get all acceptable body parts:
+				IEnumerable<BodyPartRecord> filteredParts = SemenHelper.getAvailableBodyParts(pawn);
+
+				//select random part:
+				BodyPartRecord randomPart;
+				//filteredParts.TryRandomElement<BodyPartRecord>(out randomPart);
+				//for testing - choose either genitals or anus:
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
+				if (Rand.Value > 0.5f)
+				{
+					randomPart = pawn.RaceProps.body.AllParts.Find(x => x.def == xxx.anus.def);
+				}
+				else
+				{
+					randomPart = pawn.RaceProps.body.AllParts.Find(x => x.def == xxx.genitals.def);
+				}
+
+				if (randomPart != null)
+				{
+					SemenHelper.cumOn(pawn, randomPart, 0.2f, null, SemenHelper.CUM_NORMAL);
+				}
+			};
+		}
 	}
 }
diff --git a/Source/Harmony/submit_button.cs b/Source/Harmony/submit_button.cs
index 827480191806bf2f097ac573d80d08f957c74963..f939769c5cfb054765f8c7a922ed31d8fab18ba9 100644
--- a/Source/Harmony/submit_button.cs
+++ b/Source/Harmony/submit_button.cs
@@ -4,7 +4,7 @@ using Harmony;
 using RimWorld;
 using Verse;
 using UnityEngine;
-
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -21,30 +21,32 @@ namespace rjw
 			var gizmos = __result.ToList();
 			var enabled = RJWSettings.submit_button_enabled;
 
-			if (enabled && pawn.IsColonistPlayerControlled &&  pawn.Drafted)
-			{
-				gizmos.Add(new Command_Action
+			if (pawn.CanChangeDesignationColonist())
+				if (enabled && pawn.IsColonistPlayerControlled && pawn.Drafted)
 				{
-					defaultLabel = "CommandSubmit".Translate(),
-					icon = submit_icon,
-					defaultDesc = "CommandSubmitDesc".Translate(),
-					action = delegate
+					gizmos.Add(new Command_Action
 					{
-						pawn.health.AddHediff(submit_hediff);
-					},
-					hotKey = KeyBindingDefOf.Misc3
-				});
-			}
+						defaultLabel = "CommandSubmit".Translate(),
+						icon = submit_icon,
+						defaultDesc = "CommandSubmitDesc".Translate(),
+						action = delegate
+						{
+							LayDownAndAccept(pawn);
+						},
+						hotKey = KeyBindingDefOf.Misc3
+					});
+				}
 			__result = gizmos.AsEnumerable();
 		}
 
 		static Texture2D submit_icon = ContentFinder<Texture2D>.Get("UI/Commands/Submit", true);
 		static HediffDef submit_hediff= HediffDef.Named("Hediff_Submitting");
 
-		//static void LayDownAndAccept(Pawn pawn)
-		//{
-		//	//Log.Message("Submit button is pressed for " + pawn);
-		//	pawn.health.AddHediff(submit_hediff);
-		//}
+		[SyncMethod]
+		static void LayDownAndAccept(Pawn pawn)
+		{
+			//Log.Message("Submit button is pressed for " + pawn);
+			pawn.health.AddHediff(submit_hediff);
+		}
 	}
 }
diff --git a/Source/JobDrivers/JobDriver_BestialityForFemale.cs b/Source/JobDrivers/JobDriver_BestialityForFemale.cs
index 4b3f5e400be63e974d98baf2a9315e4d6414fdd8..74ee30e5e76a8afc6df2dca2c7f0ccb23e864dbb 100644
--- a/Source/JobDrivers/JobDriver_BestialityForFemale.cs
+++ b/Source/JobDrivers/JobDriver_BestialityForFemale.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -40,6 +41,7 @@ namespace rjw
 			return pawn.Reserve(Partner, job, 1, 0, null, errorOnFailed);
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
 			this.FailOnDespawnedOrNull(PartnerInd);
@@ -75,6 +77,8 @@ namespace rjw
 			{
 				initAction = delegate
 				{
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					ticksLeftThisToil = 5000;
 					ticks_left = (int)(2000.0f * Rand.Range(0.30f, 1.30f));
 				},
diff --git a/Source/JobDrivers/JobDriver_BestialityForMale.cs b/Source/JobDrivers/JobDriver_BestialityForMale.cs
index 168d9fd2fb0ac7010a9c1796a55b5de5dea96bb2..e7cd8f13061a5bc0bee19c6bf4d89bba1a617935 100644
--- a/Source/JobDrivers/JobDriver_BestialityForMale.cs
+++ b/Source/JobDrivers/JobDriver_BestialityForMale.cs
@@ -1,9 +1,9 @@
-using System;
 using System.Collections.Generic;
 using System.Linq;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -22,9 +22,12 @@ namespace rjw
 			return pawn.Reserve(animal, job, 1, 0, null, errorOnFailed);
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
 			//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() called");
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			duration = (int)(2500.0f * Rand.Range(0.50f, 0.90f));
 			ticks_between_hearts = Rand.RangeInclusive(70, 130);
 			ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
@@ -158,6 +161,7 @@ namespace rjw
 			};
 		}
 
+		[SyncMethod]
 		private Toil TalkToAnimal(Pawn pawn, Pawn animal)
 		{
 			Toil toil = new Toil();
@@ -165,6 +169,8 @@ namespace rjw
 			{
 				pawn.interactions.TryInteractWith(animal, SexUtility.AnimalSexChat);
 			};
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			toil.defaultCompleteMode = ToilCompleteMode.Delay;
 			toil.defaultDuration = Rand.Range(120, 220);
 			return toil;
diff --git a/Source/JobDrivers/JobDriver_Breeding.cs b/Source/JobDrivers/JobDriver_Breeding.cs
index 94ef0fc0120a3c5d439d4465b80408b18c49b317..3a901c732c76daa42fbd87fc027144d06c479677 100644
--- a/Source/JobDrivers/JobDriver_Breeding.cs
+++ b/Source/JobDrivers/JobDriver_Breeding.cs
@@ -1,8 +1,8 @@
-using System;
 using System.Collections.Generic;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -20,11 +20,14 @@ namespace rjw
 			return pawn.Reserve(Target, job, BreederHelper.max_animals_at_once, 0);
 		}
 
+		[SyncMethod]
 		public virtual void roll_to_hit(Pawn pawn, Pawn partner)
 		{
 			if (!RJWSettings.rape_beating || !xxx.is_human(pawn))
 				return;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			float rand_value = Rand.Value;
 			float victim_pain = partner.health.hediffSet.PainTotal;
 			float chance_to_hit = xxx.config.base_chance_to_hit_prisoner/5;
@@ -37,8 +40,11 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			int duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
 			int ticks_between_hearts = Rand.RangeInclusive(70, 130);
 			int ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
diff --git a/Source/JobDrivers/JobDriver_Fappin.cs b/Source/JobDrivers/JobDriver_Fappin.cs
index b55db38f02a84925c9f7a16480911c142f6ad241..b6181618e7865ea3254d7a290607cc324c01be85 100644
--- a/Source/JobDrivers/JobDriver_Fappin.cs
+++ b/Source/JobDrivers/JobDriver_Fappin.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -19,9 +20,12 @@ namespace rjw
 			return pawn.Reserve(Bed, job, Bed.SleepingSlotsCount, 0, null, errorOnFailed);
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
 			// Faster fapping when frustrated.
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			ticks_left = (int)(xxx.need_some_sex(pawn) > 2f ? 2500.0f * Rand.Range(0.2f, 0.7f) : 2500.0f * Rand.Range(0.2f, 0.4f));
 
 			this.FailOnDespawnedOrNull(ibed);
diff --git a/Source/JobDrivers/JobDriver_GettinRaped.cs b/Source/JobDrivers/JobDriver_GettinRaped.cs
index ca10580108cbf6a770bea7bcb14b2ca3836bd47b..e3263d343e1557eb7c3d684a4d902b65c65dab96 100644
--- a/Source/JobDrivers/JobDriver_GettinRaped.cs
+++ b/Source/JobDrivers/JobDriver_GettinRaped.cs
@@ -53,6 +53,8 @@ namespace rjw
 
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			ticks_between_hearts = Rand.RangeInclusive(70, 130);
 			//was_laying_down = pawn.GetPosture() != PawnPosture.Standing;
 			//Log.Message(xxx.get_pawnname(Initiator) + ": was_laying_down:" + was_laying_down + " LayingInBed:" + Initiator.GetPosture());
diff --git a/Source/JobDrivers/JobDriver_JoinInBed.cs b/Source/JobDrivers/JobDriver_JoinInBed.cs
index 624b26993d9b4154a76a49b7c749c951e366fb0a..2bde244490d12705caa130f85aac2ea04158da04 100644
--- a/Source/JobDrivers/JobDriver_JoinInBed.cs
+++ b/Source/JobDrivers/JobDriver_JoinInBed.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -24,6 +25,7 @@ namespace rjw
 			return Top.Reserve(Partner, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
 			//--Log.Message("JobDriver_JoinInBed::MakeNewToils() called");
@@ -39,6 +41,8 @@ namespace rjw
 				initAction = delegate
 				{
 					//--Log.Message("JobDriver_JoinInBed::MakeNewToils() - setting initAction");
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					ticks_left = (int)(2500.0f * Rand.Range(0.30f, 1.30f));
 					Job gettin_loved = new Job(xxx.gettin_loved, Top, Bed);
 					Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced);
diff --git a/Source/JobDrivers/JobDriver_PrisonerComfortRapin.cs b/Source/JobDrivers/JobDriver_PrisonerComfortRapin.cs
index 87df4f76740d89f9ce2d9c63b1de3daa889a743c..16921eb099a9ea7358f2959062ca257199d08752 100644
--- a/Source/JobDrivers/JobDriver_PrisonerComfortRapin.cs
+++ b/Source/JobDrivers/JobDriver_PrisonerComfortRapin.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -24,6 +25,7 @@ namespace rjw
 			return pawn.Reserve(Target, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
 		}
 
+		[SyncMethod]
 		public virtual void roll_to_hit(Pawn rapist, Pawn p)
 		{
 			if (!RJWSettings.rape_beating)
@@ -31,6 +33,8 @@ namespace rjw
 				return;
 			}
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			float rand_value = Rand.Value;
 			float victim_pain = p.health.hediffSet.PainTotal;
 			// bloodlust makes the aggressor more likely to hit the prisoner
@@ -62,8 +66,11 @@ namespace rjw
 			*/
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
 			ticks_between_hearts = Rand.RangeInclusive(70, 130);
 			ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
diff --git a/Source/JobDrivers/JobDriver_QuickFap.cs b/Source/JobDrivers/JobDriver_QuickFap.cs
index 10647b7657117fd7619a3ccafc5d0d570e94a1e0..dc56f0c333ad688aeb9ad111b5dc7c3f16ffa5bc 100644
--- a/Source/JobDrivers/JobDriver_QuickFap.cs
+++ b/Source/JobDrivers/JobDriver_QuickFap.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -17,6 +18,7 @@ namespace rjw
 			return true; // No reservations needed.
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
 			//this.FailOn(() => PawnUtility.PlayerForcedJobNowOrSoon(pawn));
@@ -25,6 +27,8 @@ namespace rjw
 			this.FailOn(() => pawn.IsFighting());
 			this.FailOn(() => pawn.Drafted);
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			// Faster fapping when frustrated.
 			ticks_left = (int)(xxx.need_some_sex(pawn) > 2f ? 2500.0f * Rand.Range(0.2f, 0.7f) : 2500.0f * Rand.Range(0.2f, 0.4f));
 
diff --git a/Source/JobDrivers/JobDriver_Rape.cs b/Source/JobDrivers/JobDriver_Rape.cs
index 0a67181091234133d39e1c1f32103cca7e736543..3fac0d21ee0c208556a104ea63e0a875c58a1881 100644
--- a/Source/JobDrivers/JobDriver_Rape.cs
+++ b/Source/JobDrivers/JobDriver_Rape.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -22,11 +23,14 @@ namespace rjw
 			return pawn.Reserve(Target, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
 		}
 
+		[SyncMethod]
 		public static void roll_to_hit(Pawn rapist, Pawn p)
 		{
 			if (!RJWSettings.rape_beating)
 				return;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			float rand_value = Rand.Value;
 			float victim_pain = p.health.hediffSet.PainTotal;
 			// bloodlust makes the aggressor more likely to hit the prisoner
@@ -56,8 +60,11 @@ namespace rjw
 			*/
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			//Log.Message("[RJW]" + this.GetType().ToString() + "::MakeNewToils() called");
 			duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
 			ticks_between_hearts = Rand.RangeInclusive(70, 130);
diff --git a/Source/JobDrivers/JobDriver_Sex.cs b/Source/JobDrivers/JobDriver_Sex.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3ca939c0e0004aba3873f3791abf36f129ebb59c
--- /dev/null
+++ b/Source/JobDrivers/JobDriver_Sex.cs
@@ -0,0 +1,46 @@
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace rjw
+{
+	public abstract class JobDriver_Sex : JobDriver
+	{
+		protected float satisfaction = 1.0f;
+
+		public bool shouldreserve = true;
+
+		public int maxPawns = 1;
+		public int stackCount = 0;
+
+		public int ticks_between_hearts;
+		public int ticks_between_hits = 50;
+		public int ticks_between_thrusts;
+
+		public Thing Target = null;
+
+		//private Building_Bed Bed;
+
+		public override bool TryMakePreToilReservations(bool errorOnFailed)
+		{
+			Log.Message("shouldreserve " + shouldreserve);
+			if (shouldreserve)
+				return pawn.Reserve(Target, job, maxPawns, stackCount, null, errorOnFailed);
+			else
+				return true; // No reservations needed.
+
+			//return this.pawn.Reserve(this.Partner, this.job, 1, 0, null) && this.pawn.Reserve(this.Bed, this.job, 1, 0, null);
+		}
+
+		public void CalculateSatisfactionPerTick()
+		{
+				satisfaction = 1.0f;
+		}
+
+		protected override IEnumerable<Toil> MakeNewToils()
+		{
+			return null;
+		}
+	}
+}
\ No newline at end of file
diff --git a/Source/JobDrivers/JobDriver_ViolateCorpse.cs b/Source/JobDrivers/JobDriver_ViolateCorpse.cs
index 2239cd57949948a46723a284886346ced328b0c6..52d11437602728ae87a2bb4e8279eb3ff28a6d72 100644
--- a/Source/JobDrivers/JobDriver_ViolateCorpse.cs
+++ b/Source/JobDrivers/JobDriver_ViolateCorpse.cs
@@ -3,6 +3,7 @@ using RimWorld;
 using Verse;
 using Verse.AI;
 using Verse.Sound;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -14,9 +15,9 @@ namespace rjw
 		private int ticks_between_thrusts;
 
 		protected TargetIndex icorpse = TargetIndex.A;
-		protected Corpse corpse => (Corpse)(job.GetTarget(icorpse));
+		protected Corpse Target => (Corpse)(job.GetTarget(icorpse));
 
-		public static void sexTick(Pawn pawn, Thing corpse)
+		public static void sexTick(Pawn pawn, Thing Target)
 		{
 			if (!xxx.has_quirk(pawn, "Endytophile"))
 			{
@@ -26,18 +27,21 @@ namespace rjw
 			if (RJWSettings.sounds_enabled)
 				SoundDef.Named("Sex").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
 
-			pawn.Drawer.Notify_MeleeAttackOn(corpse);
-			pawn.rotationTracker.FaceCell(corpse.Position);
+			pawn.Drawer.Notify_MeleeAttackOn(Target);
+			pawn.rotationTracker.FaceCell(Target.Position);
 		}
 
 		public override bool TryMakePreToilReservations(bool errorOnFailed)
 		{
-			return pawn.Reserve(corpse, job, 1, -1, null, errorOnFailed);
+			return pawn.Reserve(Target, job, 1, -1, null, errorOnFailed);
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
 			//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() called");
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
 			ticks_between_hearts = Rand.RangeInclusive(70, 130);
 			ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
@@ -49,33 +53,33 @@ namespace rjw
 				ticks_between_hits = (int)(ticks_between_hits * 0.90);
 
 			this.FailOnDespawnedNullOrForbidden(icorpse);
-			this.FailOn(() => !pawn.CanReserve(corpse, 1, 0));  // Fail if someone else reserves the prisoner before the pawn arrives
+			this.FailOn(() => !pawn.CanReserve(Target, 1, 0));  // Fail if someone else reserves the prisoner before the pawn arrives
 			this.FailOn(() => pawn.IsFighting());
 			this.FailOn(() => pawn.Drafted);
-			this.FailOn(corpse.IsBurning);
+			this.FailOn(Target.IsBurning);
 
-			//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - moving towards corpse");
+			//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - moving towards Target");
 			yield return Toils_Goto.GotoThing(icorpse, PathEndMode.OnCell);
 
 			var alert = RJWPreferenceSettings.rape_alert_sound == RJWPreferenceSettings.RapeAlert.Disabled ? 
 				MessageTypeDefOf.SilentInput : MessageTypeDefOf.NeutralEvent;
-			Messages.Message(pawn.Name + " is trying to rape a corpse.", pawn, alert);
+			Messages.Message(pawn.Name + " is trying to rape a Target.", pawn, alert);
 
 			var rape = new Toil();
 			rape.initAction = delegate
 			{
-				//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - reserving corpse");
-				//pawn.Reserve(corpse, 1, 0); // corpse rapin seems like a solitary activity
+				//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - reserving Target");
+				//pawn.Reserve(Target, 1, 0); // Target rapin seems like a solitary activity
 
-				//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - stripping corpse");
-				corpse.Strip();
+				//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - stripping Target");
+				Target.Strip();
 			};
 			rape.tickAction = delegate
 			{
 				if (pawn.IsHashIntervalTick(ticks_between_hearts))
 					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
 				if (pawn.IsHashIntervalTick(ticks_between_thrusts))
-					sexTick(pawn, corpse);
+					sexTick(pawn, Target);
 				/*
 				if (pawn.IsHashIntervalTick (ticks_between_hits))
 					roll_to_hit (pawn, Corpse);
@@ -96,10 +100,10 @@ namespace rjw
 				initAction = delegate
 				{
 					//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - creating aftersex toil");
-					//Addded by nizhuan-jjr: Try to apply an aftersex process for the pawn and the corpse
-					if (corpse.InnerPawn != null)
+					//Addded by nizhuan-jjr: Try to apply an aftersex process for the pawn and the Target
+					if (Target.InnerPawn != null)
 					{
-						SexUtility.ProcessSex(pawn, corpse.InnerPawn, true);
+						SexUtility.ProcessSex(pawn, Target.InnerPawn, true);
 					}
 				},
 				defaultCompleteMode = ToilCompleteMode.Instant
diff --git a/Source/JobGivers/JobGiver_AIRapePrisoner.cs b/Source/JobGivers/JobGiver_AIRapePrisoner.cs
index 749e0107d0ff42cf529f0dbe6fded8352862ed08..5d95e4a1cf8fbaa5407fca249dfc271d61cc399d 100644
--- a/Source/JobGivers/JobGiver_AIRapePrisoner.cs
+++ b/Source/JobGivers/JobGiver_AIRapePrisoner.cs
@@ -3,6 +3,7 @@ using RimWorld;
 using Verse;
 using Verse.AI;
 using System.Collections.Generic;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -11,6 +12,7 @@ namespace rjw
 	{
 		private const float min_fuckability = 0.10f; // Don't rape prisoners with <10% fuckability
 
+		[SyncMethod]
 		public static Pawn find_victim(Pawn rapist, Map m)
 		{
 			IEnumerable<Pawn> possible_targets = m.mapPawns.AllPawns.Where(x
@@ -33,6 +35,8 @@ namespace rjw
 				}
 			}
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return valid_targets.Any() ? valid_targets.RandomElement() : null;
 		}
 
diff --git a/Source/JobGivers/JobGiver_Breed.cs b/Source/JobGivers/JobGiver_Breed.cs
index a274af5ccdc810bac158a69565d42a10eb8575e6..9789ca50f33a8d5864f40f4f6d9d85a1863d577a 100644
--- a/Source/JobGivers/JobGiver_Breed.cs
+++ b/Source/JobGivers/JobGiver_Breed.cs
@@ -1,6 +1,7 @@
 using Verse;
 using Verse.AI;
 using System.Collections.Generic;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -9,6 +10,7 @@ namespace rjw
 	/// </summary>
 	public class JobGiver_Breed : ThinkNode_JobGiver
 	{
+		[SyncMethod]
 		protected override Job TryGiveJob(Pawn animal)
 		{
 			//Log.Message("[RJW] JobGiver_Breed::TryGiveJob( " + xxx.get_pawnname(animal) + " ) called0" + (SexUtility.ReadyForLovin(animal)));
@@ -64,6 +66,8 @@ namespace rjw
 
 				if (valid_targets != null && valid_targets.Any())
 				{
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					var target = valid_targets.RandomElement();
 					//Log.Message("Target: " + xxx.get_pawnname(target));
 					return new Job(DefDatabase<JobDef>.GetNamed("Breed"), target, animal);
diff --git a/Source/JobGivers/JobGiver_DoFappin.cs b/Source/JobGivers/JobGiver_DoFappin.cs
index 047fa17d0e09d478ec7bfe50d2dd85601acefca9..995bde9755da73cf7c91842e894d987d11ad39f8 100644
--- a/Source/JobGivers/JobGiver_DoFappin.cs
+++ b/Source/JobGivers/JobGiver_DoFappin.cs
@@ -3,11 +3,13 @@ using Verse;
 using Verse.AI;
 using System.Collections.Generic;
 using System.Linq;
+using Multiplayer.API;
 
 namespace rjw
 {
 	public class JobGiver_DoFappin : ThinkNode_JobGiver
 	{
+		[SyncMethod]
 		public virtual IntVec3 FindFapLocation(Pawn p)
 		{
 			IntVec3 position = p.Position;
@@ -26,6 +28,8 @@ namespace rjw
 
 			//Log.Message("[RJW] Pawn is " + xxx.get_pawnname(p) + ", current cell is " + cell);
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			List<IntVec3> random_cells = new List<IntVec3>();
 			for (int loop = 0; loop < 50; ++loop)
 			{
diff --git a/Source/JobGivers/JobGiver_JoinInBed.cs b/Source/JobGivers/JobGiver_JoinInBed.cs
index 64db0361605e282de7334780e7953738bc6e649b..b5a022069908ed6cdd76f3f40a057d2cbb6ee205 100644
--- a/Source/JobGivers/JobGiver_JoinInBed.cs
+++ b/Source/JobGivers/JobGiver_JoinInBed.cs
@@ -3,6 +3,7 @@ using System.Linq;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -13,19 +14,25 @@ namespace rjw
 			return xxx.is_healthy(target) && (xxx.can_fuck(target) || xxx.can_be_fucked(target));
 		}
 
+		[SyncMethod]
 		private static bool roll_to_skip(Pawn pawn, Pawn target)
 		{
 			float fuckability = xxx.would_fuck(pawn, target); // 0.0 to 1.0
 			if (fuckability < 0.1f) return false;
 
 			float chance_to_skip = 0.9f - 0.7f * fuckability;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return Rand.Value < chance_to_skip;
 		}
 
+		[SyncMethod]
 		public static Pawn find_pawn_to_fuck(Pawn pawn, Map map)
 		{
 			Pawn best_fuckee = null;
 			float best_distance = 1.0e6f;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 
 			List<Pawn> targets = map.mapPawns.FreeColonists.Where(x 
 				=> x.InBed()
diff --git a/Source/Modules/Bondage/Comps/CompHoloCryptoStamped.cs b/Source/Modules/Bondage/Comps/CompHoloCryptoStamped.cs
index 78fdbcc6eb9d7241d3afb5f111dcc4baa3eb12d7..8a35a48f606e8b092165d2be927ba94a192c02ea 100644
--- a/Source/Modules/Bondage/Comps/CompHoloCryptoStamped.cs
+++ b/Source/Modules/Bondage/Comps/CompHoloCryptoStamped.cs
@@ -1,6 +1,7 @@
 using System;
 using RimWorld;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -9,8 +10,11 @@ namespace rjw
 		public string name;
 		public string key;
 
+		[SyncMethod]
 		public string random_hex_byte()
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			var rv = Rand.RangeInclusive(0x00, 0xFF);
 			var padding = (rv < 0x10) ? "0" : "";
 			return padding + rv.ToString("X");
diff --git a/Source/Modules/Bondage/Recipes/Recipe_ForceOffGear.cs b/Source/Modules/Bondage/Recipes/Recipe_ForceOffGear.cs
index f897d96725627a0445b035d6521b0ca78f63d28d..10f02816890dc2ccc1584bf83c1ef5b905f2bf88 100644
--- a/Source/Modules/Bondage/Recipes/Recipe_ForceOffGear.cs
+++ b/Source/Modules/Bondage/Recipes/Recipe_ForceOffGear.cs
@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using RimWorld;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -29,6 +30,7 @@ namespace rjw
 				yield return null;
 		}
 
+		[SyncMethod]
 		public static void apply_burns(Pawn p, List<BodyPartDef> parts, float min_severity, float max_severity)
 		{
 			foreach (var part in parts)
@@ -36,6 +38,8 @@ namespace rjw
 				var rec = find_part_record(part, p);
 				if (rec != null)
 				{
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					var to_deal = Rand.Range(min_severity, max_severity) * part.GetMaxHealth(p);
 					var dealt = 0.0f;
 					var counter = 0;
@@ -50,6 +54,7 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		public override void ApplyOnPawn(Pawn p, BodyPartRecord null_part, Pawn surgeon, List<Thing> ingredients,Bill bill)
 		{
 			var r = (force_off_gear_def)recipe;
@@ -66,6 +71,8 @@ namespace rjw
 					}
 
 				// Destroy parts
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 				var def_to_destroy = r.destroys_one_of.RandomElement<BodyPartDef>();
 				if (def_to_destroy != null)
 				{
diff --git a/Source/Modules/Multiplayer/Multiplayer.cs b/Source/Modules/Multiplayer/Multiplayer.cs
index 8e16f001f55f14635981dd56706917dc8d768b93..429fd2f1882a823359b27d99aaa2b3dceb3e2369 100644
--- a/Source/Modules/Multiplayer/Multiplayer.cs
+++ b/Source/Modules/Multiplayer/Multiplayer.cs
@@ -1,6 +1,6 @@
-using Multiplayer.API;
 using Verse;
-using Harmony;
+using RimWorld;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -15,18 +15,23 @@ namespace rjw
 			// auto register, similar to Harmony's PatchAll.
 			MP.RegisterAll();
 
-			var type = AccessTools.TypeByName("rjw.RJWdesignations");
+				/*
+				Log.Message("RJW MP compat testing");
+				var type = AccessTools.TypeByName("rjw.RJWdesignations");
+				//Log.Message("rjw MP compat " + type.Name);
+				Log.Message("is host " + MP.IsHosting);
+				Log.Message("PlayerName " + MP.PlayerName);
+				Log.Message("IsInMultiplayer " + MP.IsInMultiplayer);
 
-			Log.Message("rjw MP compat " + type.Name);
-			//MP.RegisterSyncMethod(type, "Comfort");
-			/*
-			MP.RegisterSyncMethod(type, "<GetGizmos>Service");
-			MP.RegisterSyncMethod(type, "<GetGizmos>BreedingHuman");
-			MP.RegisterSyncMethod(type, "<GetGizmos>BreedingAnimal");
-			MP.RegisterSyncMethod(type, "<GetGizmos>Breeder");
-			MP.RegisterSyncMethod(type, "<GetGizmos>Milking");
-			MP.RegisterSyncMethod(type, "<GetGizmos>Hero");
-			*/
+				//MP.RegisterSyncMethod(type, "Comfort");
+				/*
+				MP.RegisterSyncMethod(type, "<GetGizmos>Service");
+				MP.RegisterSyncMethod(type, "<GetGizmos>BreedingHuman");
+				MP.RegisterSyncMethod(type, "<GetGizmos>BreedingAnimal");
+				MP.RegisterSyncMethod(type, "<GetGizmos>Breeder");
+				MP.RegisterSyncMethod(type, "<GetGizmos>Milking");
+				MP.RegisterSyncMethod(type, "<GetGizmos>Hero");
+				*/
 
 
 			// You can choose to not auto register and do it manually
@@ -35,5 +40,26 @@ namespace rjw
 			// Use MP.IsInMultiplayer to act upon it in other places
 			// user can have it enabled and not be in session
 		}
+
+		//generate PredictableSeed for Verse.Rand
+		public static int PredictableSeed()
+		{
+			int seed = 0;
+			try
+			{
+				Map map = Find.CurrentMap;
+				//int seedHourOfDay = GenLocalDate.HourOfDay(map);
+				//int seedDayOfYear = GenLocalDate.DayOfYear(map);
+				//int seedYear = GenLocalDate.Year(map);
+				seed = (GenLocalDate.HourOfDay(map) + GenLocalDate.DayOfYear(map)) * GenLocalDate.Year(map);
+				//int seed = (seedHourOfDay + seedDayOfYear) * seedYear;
+				//Log.Warning("seedHourOfDay: " + seedHourOfDay + "\nseedDayOfYear: " + seedDayOfYear + "\nseedYear: " + seedYear + "\n" + seed);
+			}
+			catch
+			{
+				seed = Rand.Int;
+			}
+			return seed;
+		}
 	}
 }
\ No newline at end of file
diff --git a/Source/Modules/Nymphs/Incidents/IncidentWorker_NymphJoins.cs b/Source/Modules/Nymphs/Incidents/IncidentWorker_NymphJoins.cs
index fd18818550a5588e692d7142a5462fab3a7e5cce..ffb776fa24cede71d2e7573a0997ce28023d7b50 100644
--- a/Source/Modules/Nymphs/Incidents/IncidentWorker_NymphJoins.cs
+++ b/Source/Modules/Nymphs/Incidents/IncidentWorker_NymphJoins.cs
@@ -1,6 +1,7 @@
 using System.Linq;
 using RimWorld;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -17,6 +18,7 @@ namespace rjw
 			return colonist_count >= 1 && (nymph_fraction < xxx.config.max_nymph_fraction);
 		}
 
+		[SyncMethod]
 		protected override bool TryExecuteWorker(IncidentParms parms)
 		{
 			//--Log.Message("IncidentWorker_NymphJoins::TryExecute() called");
@@ -34,6 +36,8 @@ namespace rjw
 				//--Log.Message("IncidentWorker_NymphJoins::TryExecute() - map is ok");
 			}
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (!RCellFinder.TryFindRandomPawnEntryCell(out IntVec3 loc, map, CellFinder.EdgeRoadChance_Friendly + 0.2f))
 			{
 				//--Log.Message("IncidentWorker_NymphJoins::TryExecute() - no entry, abort!");
diff --git a/Source/Modules/Nymphs/Incidents/IncidentWorker_NymphVisitorGroup.cs b/Source/Modules/Nymphs/Incidents/IncidentWorker_NymphVisitorGroup.cs
index b9a6c2573172609cdcec01ab1a16697825316025..37944c316c9dd8d23739fc88b107f581201f89aa 100644
--- a/Source/Modules/Nymphs/Incidents/IncidentWorker_NymphVisitorGroup.cs
+++ b/Source/Modules/Nymphs/Incidents/IncidentWorker_NymphVisitorGroup.cs
@@ -1,5 +1,6 @@
 using RimWorld;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -24,6 +25,7 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		protected override bool TryExecuteWorker(IncidentParms parms)
 		{
 			//--Log.Message("IncidentWorker_NymphVisitorGroup::TryExecute() called");
@@ -45,6 +47,8 @@ namespace rjw
 				//--Log.Message("IncidentWorker_NymphJoins::TryExecute() - map is ok");
 			}
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (!RCellFinder.TryFindRandomPawnEntryCell(out IntVec3 loc, map, CellFinder.EdgeRoadChance_Friendly + 0.2f))
 			{
 				//--Log.Message("IncidentWorker_NymphJoins::TryExecute() - no entry, abort!");
diff --git a/Source/Modules/Nymphs/Pawns/Nymph_Backstories.cs b/Source/Modules/Nymphs/Pawns/Nymph_Backstories.cs
index 44fed8777c5283095186fe1b15b1c3945e54c75d..e05aad119d7cf871ad8dac5a8e730e942e5adcb6 100644
--- a/Source/Modules/Nymphs/Pawns/Nymph_Backstories.cs
+++ b/Source/Modules/Nymphs/Pawns/Nymph_Backstories.cs
@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using RimWorld;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -325,6 +326,7 @@ namespace rjw
 		}
 
 		// Randomly chooses backstories and traits for a nymph
+		[SyncMethod]
 		public static nymph_story generate()
 		{
 			var tr = new nymph_story();
@@ -334,6 +336,8 @@ namespace rjw
 			tr.traits = new List<Trait>();
 			tr.traits.Add(new Trait(xxx.nymphomaniac, 0, true));
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			var beauty = 0;
 			var rv = Rand.Value;
 			var rv2 = Rand.Value;
diff --git a/Source/Modules/Nymphs/Pawns/Nymph_Generator.cs b/Source/Modules/Nymphs/Pawns/Nymph_Generator.cs
index 240613815039ce609a66aa80963fa3b914b6333f..23d669d59a4f269e76591ab65d5b30916dd89245 100644
--- a/Source/Modules/Nymphs/Pawns/Nymph_Generator.cs
+++ b/Source/Modules/Nymphs/Pawns/Nymph_Generator.cs
@@ -3,6 +3,7 @@ using RimWorld;
 using UnityEngine;
 using Verse;
 using System;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -16,10 +17,13 @@ namespace rjw
 			return false;
 		}
 
+		[SyncMethod]
 		public static Gender setnymphgender()
 		{
 			//with males 100% its still 99%, coz im  to lazy to fix it
 			//float rnd = Rand.Value;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			float chance = RJWSettings.male_nymph_chance;
 
 			Gender Pawngender = Rand.Chance(chance) ? Gender.Male: Gender.Female;
@@ -29,6 +33,7 @@ namespace rjw
 		}
 
 		// Replaces a pawn's backstory and traits to turn it into a nymph
+		[SyncMethod]
 		public static void set_story(Pawn pawn)
 		{
 			var gen_sto = nymph_backstories.generate();
@@ -41,6 +46,8 @@ namespace rjw
 			{
 				BodyPartRecord torso = pawn.RaceProps.body.AllParts.Find((bpr) => String.Equals(bpr.def.defName, "Torso"));
 				pawn.health.AddHediff(xxx.feelingBroken, torso);
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 				(pawn.health.hediffSet.GetFirstHediffOfDef(xxx.feelingBroken)).Severity = Rand.Range(0.4f, 1.0f);
 			}
 
@@ -73,6 +80,7 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		private static int sum_previous_gains(SkillDef def, Pawn_StoryTracker sto, Pawn_AgeTracker age)
 		{
 			int total_gain = 0;
@@ -90,6 +98,8 @@ namespace rjw
 					total_gain += gain;
 
 			// Gains from age
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			var rgain = Rand.Value * (float)total_gain * 0.35f;
 			var age_factor = Mathf.Clamp01((age.AgeBiologicalYearsFloat - 17.0f) / 10.0f); // Assume nymphs are 17~27
 			total_gain += (int)(age_factor * rgain);
@@ -98,6 +108,7 @@ namespace rjw
 		}
 
 		// Set a nymph's initial skills & passions from backstory, traits, and age
+		[SyncMethod]
 		public static void set_skills(Pawn pawn)
 		{
 			foreach (var skill_def in DefDatabase<SkillDef>.AllDefsListForReading)
@@ -105,6 +116,8 @@ namespace rjw
 				var rec = pawn.skills.GetSkill(skill_def);
 				if (!rec.TotallyDisabled)
 				{
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					rec.Level = sum_previous_gains(skill_def, pawn.story, pawn.ageTracker);
 					rec.xpSinceLastLevel = rec.XpRequiredForLevelUp * Rand.Range(0.10f, 0.90f);
 
@@ -126,6 +139,7 @@ namespace rjw
 			set_skills(pawn);
 		}
 
+		[SyncMethod]
 		public static Pawn spawn_nymph(IntVec3 around_loc, ref Map map, Faction faction = null)
 		{
 			PawnKindDef pkd;
@@ -162,6 +176,8 @@ namespace rjw
 												 null); // Fixed last name
 
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			IntVec3 spawn_loc = CellFinder.RandomClosewalkCellNear(around_loc, map, 6);//RandomSpawnCellForPawnNear could be an alternative
 			
 			//Log.Message("[RJW] spawn_nymph1: " + request.FixedGender);
diff --git a/Source/Modules/Pregnancy/Hediffs/HeDiff_MicroComputer.cs b/Source/Modules/Pregnancy/Hediffs/HeDiff_MicroComputer.cs
index 885721dd35813d85e4bfbe23904e6a49591b11a8..a5781fd090ae38dc19ff53024f58a79504ddf0c9 100644
--- a/Source/Modules/Pregnancy/Hediffs/HeDiff_MicroComputer.cs
+++ b/Source/Modules/Pregnancy/Hediffs/HeDiff_MicroComputer.cs
@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -14,9 +15,12 @@ namespace rjw
 			Scribe_Values.Look<int>(ref this.nextEventTick, "nextEventTick", 60000, false);
 		}
 
+		[SyncMethod]
 		public override void PostMake()
 		{
 			base.PostMake();
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			nextEventTick = Rand.Range(mcDef.minEventInterval, mcDef.maxEventInterval);
 		}
 
@@ -43,17 +47,28 @@ namespace rjw
 
 		protected HediffDef_MechImplants mcDef
 		{
-			get { return ((HediffDef_MechImplants)def); }
+			get
+			{
+				return ((HediffDef_MechImplants)def);
+			}
 		}
 
 		protected List<string> randomEffects
 		{
-			get { return mcDef.randomHediffDefs; }
+			get
+			{
+				return mcDef.randomHediffDefs;
+			}
 		}
 
 		protected string randomEffect
 		{
-			get { return randomEffects.RandomElement<string>(); }
+			get
+			{
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
+				return randomEffects.RandomElement<string>();
+			}
 		}
 	}
 }
\ No newline at end of file
diff --git a/Source/Modules/Pregnancy/Hediffs/Hediff_BasePregnancy.cs b/Source/Modules/Pregnancy/Hediffs/Hediff_BasePregnancy.cs
index b09f3b3ae497d088c62d3118a5ab010775ef76be..3be16448256090eb3c3e2d56506a05ce58bc1b8a 100644
--- a/Source/Modules/Pregnancy/Hediffs/Hediff_BasePregnancy.cs
+++ b/Source/Modules/Pregnancy/Hediffs/Hediff_BasePregnancy.cs
@@ -4,6 +4,7 @@ using RimWorld;
 using Verse;
 using UnityEngine;
 using System.Text;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -146,11 +147,14 @@ namespace rjw
 			pawn.health?.RemoveHediff(this);
 		}
 
+		[SyncMethod]
 		public Pawn partstospawn(Pawn baby, Pawn mother, Pawn dad)
 		{
 			//decide what parts to inherit
 			//default use own parts
 			Pawn partstospawn = baby;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			//spawn with mother parts
 			if (mother != null && Rand.Range(0, 100) <= 10)
 				partstospawn = mother;
@@ -162,6 +166,7 @@ namespace rjw
 			return partstospawn;
 		}
 
+		[SyncMethod]
 		public bool spawnfutachild(Pawn baby, Pawn mother, Pawn dad)
 		{
 			int futachance = 0;
@@ -171,9 +176,12 @@ namespace rjw
 				futachance = futachance + 25;
 
 			//Log.Message("[RJW] Pregnancy spawnfutachild " + futachance);
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return (Rand.Range(0, 100) <= futachance);
 		}
 
+		[SyncMethod]
 		public override void Tick()
 		{
 			ageTicks++;
@@ -276,6 +284,7 @@ namespace rjw
 		}
 
 		//This should generate pawns to be born in due time. Should take into account all settings and parent races
+		[SyncMethod]
 		protected virtual void GenerateBabies()
 		{
 			Pawn mother = pawn;
@@ -286,6 +295,8 @@ namespace rjw
 				return;
 			}
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (father == null)
 			{
 				Log.Warning("Hediff_BasePregnancy::GenerateBabies() - no father defined");
@@ -320,6 +331,8 @@ namespace rjw
 			//Babies will have average number of traits of their parents, this way it will hopefully be compatible with various mods that change number of allowed traits
 			int trait_count = 0;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			List<Trait> traitpool = new List<Trait>();
 			float skin_whiteness = Rand.Range(0, 1);
 
@@ -613,6 +626,7 @@ namespace rjw
 			contractions = 0;
 			//Log.Message("[RJW]Base pregnancy generating babies before: " + this.babies.Count);
 			GenerateBabies();
+			//progress_per_tick = babies.NullOrEmpty() ? 1f : (1.0f) / (babies[0].RaceProps.gestationPeriodDays * TicksPerDay/50);
 			progress_per_tick = babies.NullOrEmpty() ? 1f : (1.0f) / (babies[0].RaceProps.gestationPeriodDays * TicksPerDay);
 			//Log.Message("[RJW]Base pregnancy generating babies after: " + this.babies.Count);
 		}
@@ -622,6 +636,7 @@ namespace rjw
 			StringBuilder stringBuilder = new StringBuilder();
 			stringBuilder.Append(base.DebugString());
 			stringBuilder.AppendLine("Gestation progress: " + GestationProgress.ToStringPercent());
+			//stringBuilder.AppendLine("Time left: " + ((int)((1f - GestationProgress) * babies[0].RaceProps.gestationPeriodDays * TicksPerDay/50)).ToStringTicksToPeriod());
 			stringBuilder.AppendLine("Time left: " + ((int)((1f - GestationProgress) * babies[0].RaceProps.gestationPeriodDays * TicksPerDay)).ToStringTicksToPeriod());
 			stringBuilder.AppendLine(" Father: " + xxx.get_pawnname(father));
 			return stringBuilder.ToString();
diff --git a/Source/Modules/Pregnancy/Hediffs/Hediff_InsectEggPregnancy.cs b/Source/Modules/Pregnancy/Hediffs/Hediff_InsectEggPregnancy.cs
index 98569e84a91e79c408aab81cee5c95e5a03cbad4..719c66953779e6af1df8f706cff7283445f09553 100644
--- a/Source/Modules/Pregnancy/Hediffs/Hediff_InsectEggPregnancy.cs
+++ b/Source/Modules/Pregnancy/Hediffs/Hediff_InsectEggPregnancy.cs
@@ -4,6 +4,7 @@ using RimWorld.Planet;
 using Verse;
 using System.Text;
 using Verse.AI.Group;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -126,10 +127,13 @@ namespace rjw
 		}
 
 		//should someday remake into birth eggs and then within few ticks hatch them
+		[SyncMethod]
 		public void GiveBirth()
 		{
 			Pawn mother = pawn;
 			Pawn baby = null;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (fertilized)
 			{
 				//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - Egg of " + parentDef + " in " + mother.ToString() + " birth!");
diff --git a/Source/Modules/Pregnancy/Hediffs/Hediff_ParasitePregnancy.cs b/Source/Modules/Pregnancy/Hediffs/Hediff_ParasitePregnancy.cs
index 1ab68a6a641abde7c575d756152756be16ebc3e0..cea435a64856122a35ea834884e115dc61c5760a 100644
--- a/Source/Modules/Pregnancy/Hediffs/Hediff_ParasitePregnancy.cs
+++ b/Source/Modules/Pregnancy/Hediffs/Hediff_ParasitePregnancy.cs
@@ -2,13 +2,17 @@
 using RimWorld.Planet;
 using UnityEngine;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
 	internal class Hediff_Parasite : Hediff_Pregnant
 	{
+		[SyncMethod]
 		new public static void DoBirthSpawn(Pawn mother, Pawn father)
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			int num = (mother.RaceProps.litterSizeCurve == null) ? 1 : Mathf.RoundToInt(Rand.ByCurve(mother.RaceProps.litterSizeCurve));
 			if (num < 1)
 			{
diff --git a/Source/Modules/Pregnancy/Hediffs/Hediff_SimpleBaby.cs b/Source/Modules/Pregnancy/Hediffs/Hediff_SimpleBaby.cs
index 4532a15e0a75d7c9f38621adddff37f0f3f3cc07..4125363611519f0d224fce2140a5b942459ad1c6 100644
--- a/Source/Modules/Pregnancy/Hediffs/Hediff_SimpleBaby.cs
+++ b/Source/Modules/Pregnancy/Hediffs/Hediff_SimpleBaby.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Text;
 using RimWorld;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -59,8 +60,11 @@ namespace rjw
 			GrowUpTo(stage, true);
 		}
 
+		[SyncMethod]
 		internal void GrowUpTo(int stage, bool generated)
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			grown_to = stage;
 
 			// Update the Colonist Bar
@@ -180,6 +184,8 @@ namespace rjw
 					}
 
 					// People(male or female) can turn gay during puberty
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					if (Rand.Value <= 0.03f && pawn.story.traits.allTraits.Count <= 3)
 					{
 						pawn.story.traits.GainTrait(new Trait(TraitDefOf.Gay, 0, true));
diff --git a/Source/Modules/Pregnancy/Pregnancy_Helper.cs b/Source/Modules/Pregnancy/Pregnancy_Helper.cs
index 1224f361890fca849cd0448e527117d32bbf4bda..1e9e314060c6dcecaadcba13dd55181c7c16d89f 100644
--- a/Source/Modules/Pregnancy/Pregnancy_Helper.cs
+++ b/Source/Modules/Pregnancy/Pregnancy_Helper.cs
@@ -1,9 +1,9 @@
 using RimWorld;
 using Verse;
-using System.Reflection;
 using System;
 using System.Linq;
 using System.Collections.Generic;
+using Multiplayer.API;
 
 ///RimWorldChildren pregnancy:
 //using RimWorldChildren;
@@ -53,6 +53,8 @@ namespace rjw
 					// Not an actual pregnancy. This implants mechanoid tech into the target.
 					//may lead to pregnancy
 					//old  "chip pregnancies", maybe integrate them somehow?
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					HediffDef_MechImplants egg = (from x in DefDatabase<HediffDef_MechImplants>.AllDefs	select x).RandomElement();
 					if (egg != null)
 					{
@@ -122,6 +124,7 @@ namespace rjw
 
 		///<summary>Can get preg with above conditions, do impregnation.</summary>
 
+		[SyncMethod]
 		public static void DoEgg(Pawn pawn, Pawn partner)
 		{
 			if (RJWPregnancySettings.insect_pregnancy_enabled)
@@ -144,6 +147,8 @@ namespace rjw
 						float eggssize = ((xxx.is_human(pawn) || !xxx.is_insect(pawn)) && !EggRace_filter.Contains(pawn.kindDef.race.defName)) ? 0.04f : pawn.RaceProps.baseBodySize / 5;
 						//TODO move "Megascarab" to InsectEgg and rework heddif, so it defaults to Megascarab if not insect
 						//once there is some sort of genetic inheritance form parts/ovis
+						//Rand.PopState();
+						//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 						HediffDef_InsectEgg egg = (from x in DefDatabase<HediffDef_InsectEgg>.AllDefs where x.IsParent(defname) select x).RandomElement();
 						if (egg != null)
 						{
@@ -196,6 +201,7 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		public static void Doimpregnate(Pawn pawn, Pawn partner)
 		{
 			if (RJWSettings.DevMode) Log.Message("[RJW] Doimpregnate " + xxx.get_pawnname(pawn) + " is a father, " + xxx.get_pawnname(partner) + " is a mother");
@@ -213,6 +219,8 @@ namespace rjw
 					fertility *= RJWPregnancySettings.interspecies_impregnation_modifier;
 			}
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			float ReproductionFactor = Math.Min(pawn.health.capacities.GetLevel(xxx.reproduction), partner.health.capacities.GetLevel(xxx.reproduction));
 			float pregnancy_threshold = fertility * ReproductionFactor;
 			float non_pregnancy_chance = Rand.Value;
diff --git a/Source/Modules/STD/Thoughts/ThoughtWorker_ItchyCrotch.cs b/Source/Modules/STD/Thoughts/ThoughtWorker_ItchyCrotch.cs
index 8202312c7f1262da88511454716663f51c730420..7a0c4703d64691232f20f62e45fdf0d6d8098cca 100644
--- a/Source/Modules/STD/Thoughts/ThoughtWorker_ItchyCrotch.cs
+++ b/Source/Modules/STD/Thoughts/ThoughtWorker_ItchyCrotch.cs
@@ -7,7 +7,7 @@ namespace rjw
 	{
 		protected override ThoughtState CurrentStateInternal(Pawn p)
 		{
-			var sev = std.genital_rash_severity(p);
+			int sev = std.genital_rash_severity(p);
 			if (sev <= 0)
 				return ThoughtState.Inactive;
 			else if (sev == 1)
diff --git a/Source/Modules/STD/std.cs b/Source/Modules/STD/std.cs
index 2d0c51e3164be617253bddf900ddc68cc988fb0b..52f805bb80b92b809b3ed173063d9cad078d20d9 100644
--- a/Source/Modules/STD/std.cs
+++ b/Source/Modules/STD/std.cs
@@ -3,6 +3,7 @@ using System.Text;
 using RimWorld;
 using UnityEngine;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -123,6 +124,7 @@ namespace rjw
 			return id != null && id.CurStageIndex > 0;
 		}
 
+		[SyncMethod]
 		public static void update_immunodeficiency(Pawn p)
 		{
 			float min_bf_for_id = 1.0f - immunodeficiency.minSeverity;
@@ -162,6 +164,8 @@ namespace rjw
 			// The new equation should be .1 = (1-x)^(24000/kj)
 			// log(.1) = (24000/kj) log(1-x),  so log(1-x)= (kj/24000) log(.1), 1-x = .1^(kj/24000), x= 1-.1^(kj/24000)
 			// Since k=10,j=1, so kj=10, new x is 1-.1^(10/24000)=0.0009589504, let it be 959/1000000
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (Rand.RangeInclusive(1, 1000000) <= 959 && Rand.Value < bf / min_bf_for_id)
 			{
 				BodyPartRecord part;
@@ -194,9 +198,12 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		public static void roll_to_catch(Pawn catcher, Pawn pitcher)
 		{
 			if (!RJWSettings.stds_enabled) return;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 
 			// Archotech genitalia automagically purge any known STDs.
 			if (catcher.health.hediffSet.HasHediff(Genital_Helper.archotech_vagina) || pitcher.health.hediffSet.HasHediff(Genital_Helper.archotech_vagina) ||
@@ -280,6 +287,7 @@ namespace rjw
 				}
 		}
 
+		[SyncMethod]
 		public static void generate_on(Pawn p)
 		{
 			if (p == null) return;
@@ -289,6 +297,8 @@ namespace rjw
 			if (!xxx.is_human(p))
 				return;
 			float nymph_mul = !xxx.is_nympho(p) ? 1.0f : xxx.config.nymph_spawn_with_std_mul;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			foreach (std_def sd in all)
 				if (Rand.Value < sd.spawn_chance * nymph_mul)
 				{
@@ -302,6 +312,7 @@ namespace rjw
 				}
 		}
 
+		[SyncMethod]
 		public static void roll_for_syphilis_damage(Pawn p)
 		{
 			Hediff syp = p.health.hediffSet.GetFirstHediffOfDef(syphilis.hediff_def);
@@ -315,6 +326,8 @@ namespace rjw
 			// The new equation should be .7 = (1-x)^(400/kj)
 			// 1-x = .7^(kj/400), x =1-.7^(kj/400)
 			// Since k=10,j=1, so kj=10, new x is 1-.7^(10/400)=0.0088772362, let it be 888/100000
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (Rand.RangeInclusive(1, 100000) <= 888)
 			{
 				BodyPartRecord part;
diff --git a/Source/Modules/SemenOverlay/BukkakeContent.cs b/Source/Modules/SemenOverlay/BukkakeContent.cs
index 5601e9be615ec105239b30d142cf25e1522ed2ca..dc3e488c7072aab87409cbee0c955d29fa9a52cd 100644
--- a/Source/Modules/SemenOverlay/BukkakeContent.cs
+++ b/Source/Modules/SemenOverlay/BukkakeContent.cs
@@ -1,6 +1,6 @@
 using Verse;
 using UnityEngine;
-
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -25,8 +25,11 @@ namespace rjw
 		public static readonly Material semenSplatch9 = MaterialPool.MatFrom("Bukkake/splatch_9", ShaderDatabase.Cutout);
 
 
+		[SyncMethod]
 		public static Material pickRandomSplatch()
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			int rand = Rand.Range(0, 8);
 			switch (rand)
 			{
diff --git a/Source/Modules/SemenOverlay/Hediffs/Hediff_Bukkake.cs b/Source/Modules/SemenOverlay/Hediffs/Hediff_Bukkake.cs
index 4b6e53fb9074938c9ed42317a1b0eca6436cf0e8..b36d055e4b8aec23418c7744eea79c95ed37814a 100644
--- a/Source/Modules/SemenOverlay/Hediffs/Hediff_Bukkake.cs
+++ b/Source/Modules/SemenOverlay/Hediffs/Hediff_Bukkake.cs
@@ -138,6 +138,8 @@ namespace rjw
 				semenMaterial = new Material(BukkakeContent.pickRandomSplatch());//needs to create new material in order to allow for different colors
 				semenMaterial.SetTextureScale("_MainTex", new Vector2(-1, 1));
 				this.bodyPart = bodyPart;
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 
 				//set color:
 				switch (semenType)
diff --git a/Source/Modules/SemenOverlay/Hediffs/Hediff_Semen.cs b/Source/Modules/SemenOverlay/Hediffs/Hediff_Semen.cs
index 509e9e1668881a03d7f40501084a57693da5afc6..2880c4616ff63f12d508fca6de70b260796a8b10 100644
--- a/Source/Modules/SemenOverlay/Hediffs/Hediff_Semen.cs
+++ b/Source/Modules/SemenOverlay/Hediffs/Hediff_Semen.cs
@@ -3,6 +3,7 @@ using System.Linq;
 using System.Text;
 using Verse;
 using UnityEngine;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -56,6 +57,7 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		public override bool TryMergeWith(Hediff other)
 		{
 			//if a new Semen hediff is added to the same body part, they are combined. if severity reaches more than 1, spillover to other body parts occurs
@@ -71,6 +73,8 @@ namespace rjw
 					BodyPartDef spillOverTo = SemenHelper.spillover(this.Part.def);//SemenHelper saves valid other body parts for spillover
 					if (spillOverTo != null)
 					{
+						//Rand.PopState();
+						//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 						IEnumerable<BodyPartRecord> availableParts = SemenHelper.getAvailableBodyParts(pawn);//gets all non missing, valid body parts
 						IEnumerable<BodyPartRecord> filteredParts = availableParts.Where(x => x.def == spillOverTo);//filters again for valid spill target
 						BodyPartRecord spillPart = filteredParts.RandomElement<BodyPartRecord>();//then pick one
diff --git a/Source/Modules/SemenOverlay/SemenHelper.cs b/Source/Modules/SemenOverlay/SemenHelper.cs
index ec6f971d1e4d46c5d3f455c69d859a1c8add14a0..0c509741c49790f98a4fdbfac3a2a15eb26bba97 100644
--- a/Source/Modules/SemenOverlay/SemenHelper.cs
+++ b/Source/Modules/SemenOverlay/SemenHelper.cs
@@ -5,6 +5,7 @@ using RimWorld;
 using Verse;
 using Harmony;
 using UnityEngine;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -265,11 +266,14 @@ namespace rjw
 		}
 
 		//if spunk on one body part reaches a certain level, it can spill over to others, this function returns from where to where
+		[SyncMethod]
 		public static BodyPartDef spillover(BodyPartDef sourcePart)
 		{
 			//caution: danger of infinite loop if circular spillover between 2 full parts -> don't define possible circles
 			BodyPartDef newPart = null;
 			int sel;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			if (sourcePart == BodyPartDefOf.Torso)
 			{
 				sel = Rand.Range(0, 4);
@@ -354,11 +358,14 @@ namespace rjw
 		}
 
 		//determines who is the active male (or equivalent) in the exchange and the amount of semen dispensed and where to
+		[SyncMethod]
 		public static void calculateAndApplySemen(Pawn pawn, Pawn partner, xxx.rjwSextype sextype)
 		{
 			if (!RJWSettings.cum_on_body) return;
 
 			Pawn giver, receiver;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 
 			//dispenser of the seed
 			if (Genital_Helper.has_penis(pawn) || xxx.is_mechanoid(pawn) || xxx.is_insect(pawn))
diff --git a/Source/Modules/SemenOverlay/WorkGivers/WorkGiver_CleanSelf.cs b/Source/Modules/SemenOverlay/WorkGivers/WorkGiver_CleanSelf.cs
index 0f1d69fad7419969b92bc64f13a4d48133ad86a9..a6a87a7587bb2c512c4fa3d8d077bb3ba7ac28fc 100644
--- a/Source/Modules/SemenOverlay/WorkGivers/WorkGiver_CleanSelf.cs
+++ b/Source/Modules/SemenOverlay/WorkGivers/WorkGiver_CleanSelf.cs
@@ -30,13 +30,40 @@ namespace rjw
 		//conditions for self-cleaning job to be available
 		public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
 		{
+			if (!pawn.CanReserve(t, 1, -1, null, forced))
+				return false;
+
 			Hediff hediff = pawn.health.hediffSet.hediffs.Find(x => (x.def == RJW_SemenoOverlayHediffDefOf.Hediff_Bukkake));
-			int minAge = 3 * 2500;//3 hours in-game must have passed
-			if (pawn == t && hediff != null && hediff.ageTicks > minAge && pawn.CanReserve(t, 1, -1, null, forced) && !xxx.DubsBadHygieneIsActive)
+			if (pawn != t || hediff == null )
+				return false;
+
+			if (xxx.DubsBadHygieneIsActive)
+				return false;
+
+			if (pawn.IsDesignatedHero())
 			{
-				return true;
+				if (!forced)
+				{
+					//Log.Message("[RJW]WorkGiver_CleanSelf::not player interaction for hero, exit");
+					return false;
+				}
+				if (!pawn.IsHeroOwner())
+				{
+					//Log.Message("[RJW]WorkGiver_CleanSelf::player interaction for not owned hero, exit");
+					return false;
+				}
 			}
-			return false;
+			else
+			{
+				int minAge = 3 * 2500;//3 hours in-game must have passed
+				if (!(hediff.ageTicks > minAge))
+				{
+					//Log.Message("[RJW]WorkGiver_CleanSelf:: 3 hours in-game must pass to self-clean, exit");
+					return false;
+				}
+			}
+
+			return true;
 		}
 
 		public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
diff --git a/Source/Modules/Whoring/JobDrivers/JobDriver_WhoreIsServingVisitors.cs b/Source/Modules/Whoring/JobDrivers/JobDriver_WhoreIsServingVisitors.cs
index a44e95fb8f108e986ea74b6b504916af1fb1afbd..d87684a57f5e5851563e815c4f743d7832133277 100644
--- a/Source/Modules/Whoring/JobDrivers/JobDriver_WhoreIsServingVisitors.cs
+++ b/Source/Modules/Whoring/JobDrivers/JobDriver_WhoreIsServingVisitors.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -43,6 +44,7 @@ namespace rjw
 			return pawn.Reserve(Partner, job, 1, 0, null, errorOnFailed);
 		}
 
+		[SyncMethod]
 		protected override IEnumerable<Toil> MakeNewToils()
 		{
 			//Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() - making toils");
@@ -82,6 +84,8 @@ namespace rjw
 			{
 				initAction = delegate
 				{
+					//Rand.PopState();
+					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 					//Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - waitInBed, initAction is called");
 					ticksLeftThisToil = 5000;
 					ticks_left = (int)(2000.0f * Rand.Range(0.30f, 1.30f));
@@ -130,6 +134,8 @@ namespace rjw
 						if (xxx.HasNonPolyPartner(Partner))
 						{
 							Pawn lover = LovePartnerRelationUtility.ExistingLovePartner(Partner);
+							//Rand.PopState();
+							//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 							if ((Actor != lover && !lover.Dead) && (lover.Map == Partner.Map || Rand.Value < 0.25))
 							{
 								lover.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.CheatedOnMe, Partner);
diff --git a/Source/Modules/Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs b/Source/Modules/Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs
index 7e14aaf7a1d8d4decf09a4b9a2142c2b7e48e55a..03e549a813201d6483b89759eac4fbc67d2723c2 100644
--- a/Source/Modules/Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs
+++ b/Source/Modules/Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs
@@ -3,6 +3,7 @@ using System.Linq;
 using RimWorld;
 using Verse;
 using Verse.AI;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -18,8 +19,11 @@ namespace rjw
 			return val == null ? false : true;
 		}
 
+		[SyncMethod]
 		private static bool Roll_to_skip(Pawn client, Pawn whore)
 		{
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			float fuckability = xxx.would_fuck(client, whore); // 0.0 to 1.
 
 			// More likely to skip other whores, because they're supposed to be working.
@@ -81,6 +85,8 @@ namespace rjw
 			//Use this check when client is in the same faction as the whore
 			internal bool RelationCheckPass(Pawn client)
 			{
+				//Rand.PopState();
+				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 				if (xxx.isSingleOrPartnerNotHere(client) || xxx.is_lecher(client) || Rand.Value < 0.9f)
 				{
 					if (client != LovePartnerRelationUtility.ExistingLovePartner(whore))
@@ -92,6 +98,7 @@ namespace rjw
 			}
 		}
 
+		[SyncMethod]
 		public static Pawn FindAttractivePawn(Pawn whore, out int price)
 		{
 			price = 0;
@@ -99,6 +106,9 @@ namespace rjw
 			{
 				return null;
 			}
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
+
 			FindAttractivePawnHelper client = new FindAttractivePawnHelper
 			{
 				whore = whore
@@ -163,6 +173,7 @@ namespace rjw
 			// Most things are now checked in the ThinkNode_ConditionalWhore.
 
 			if (pawn.Drafted) return null;
+			if (MP.IsInMultiplayer) return null; //fix error someday, maybe
 
 			//Log.Message("[RJW] JobGiver_WhoreInvitingVisitors::TryGiveJob( " + xxx.get_pawnname(pawn) + " ) called");
 			if (!SexUtility.ReadyForLovin(pawn))
diff --git a/Source/Modules/Whoring/Whoring_Helper.cs b/Source/Modules/Whoring/Whoring_Helper.cs
index a86c4dd7bc725acdc4bdb7f2a332a7b14cc25950..1e294e567b7378a07f4b92bc0333fbffdc8708aa 100644
--- a/Source/Modules/Whoring/Whoring_Helper.cs
+++ b/Source/Modules/Whoring/Whoring_Helper.cs
@@ -9,6 +9,7 @@ using System.Diagnostics;
 using System;
 using UnityEngine;
 using Verse.AI.Group;
+using Multiplayer.API;
 
 namespace rjw
 {
@@ -152,10 +153,13 @@ namespace rjw
 			return room_multiplier;
 		}
 
+		[SyncMethod]
 		public static int PriceOfWhore(Pawn whore)
 		{
 			float NeedSexFactor = xxx.need_some_sex(whore) > 1 ? 1 - xxx.need_some_sex(whore) / 8 : 1f;
 
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			float price = Rand.Range(WhoreMinPrice(whore), WhoreMaxPrice(whore));
 
 			price *= NeedSexFactor;
@@ -268,6 +272,7 @@ namespace rjw
 			return xxx.is_healthy(pawn) && !pawn.Downed && !pawn.Suspended;
 		}
 
+		[SyncMethod]
 		public static bool IsHookupAppealing(Pawn target, Pawn whore)
 		{
 			if (PawnUtility.WillSoonHaveBasicNeed(target))
@@ -307,11 +312,14 @@ namespace rjw
 			num *= 0.8f + ((float)whore.skills.GetSkill(SkillDefOf.Social).Level / 40); // 0.8 to 1.3
 			num *= Mathf.InverseLerp(-100f, 0f, target.relations.OpinionOf(whore));
 			//Log.Message("IsHookupAppealing - score: " + num);
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return Rand.Range(0.05f, 1f) < num;
 		}
 
 		// Summary:
 		//   Check if the pawn is willing to hook up. Checked for both target and the whore.
+		[SyncMethod]
 		public static bool WillPawnTryHookup(Pawn target)
 		{
 			if (xxx.RomanceDiversifiedIsActive && target.story.traits.HasTrait(xxx.asexual))
@@ -351,6 +359,8 @@ namespace rjw
 				num2 *= 1.4f;
 			}
 			num2 /= 1.5f;
+			//Rand.PopState();
+			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			return Rand.Range(0f, 1f) < num2;
 		}
 	}
diff --git a/Source/Settings/RJWSettingsController.cs b/Source/Settings/RJWSettingsController.cs
index aa181bea78290639adbb25d8e5fd3342b6d991ac..e126076c93759a63f0db6379a9ed3069f3ca3c73 100644
--- a/Source/Settings/RJWSettingsController.cs
+++ b/Source/Settings/RJWSettingsController.cs
@@ -1,5 +1,6 @@
 using UnityEngine;
 using Verse;
+using Multiplayer.API;
 
 namespace rjw.Settings
 {
@@ -17,6 +18,8 @@ namespace rjw.Settings
 
 		public override void DoSettingsWindowContents(Rect inRect)
 		{
+			if (MP.IsInMultiplayer)
+				return;
 			RJWSettings.DoWindowContents(inRect);
 		}
 	}
@@ -35,6 +38,8 @@ namespace rjw.Settings
 
 		public override void DoSettingsWindowContents(Rect inRect)
 		{
+			if (MP.IsInMultiplayer)
+				return;
 			RJWDebugSettings.DoWindowContents(inRect);
 		}
 	}
@@ -53,6 +58,8 @@ namespace rjw.Settings
 
 		public override void DoSettingsWindowContents(Rect inRect)
 		{
+			if (MP.IsInMultiplayer)
+				return;
 			RJWPregnancySettings.DoWindowContents(inRect);
 		}
 	}
@@ -71,6 +78,8 @@ namespace rjw.Settings
 
 		public override void DoSettingsWindowContents(Rect inRect)
 		{
+			if (MP.IsInMultiplayer)
+				return;
 			RJWPreferenceSettings.DoWindowContents(inRect);
 		}
 	}
diff --git a/Source/Settings/Settings.cs b/Source/Settings/Settings.cs
index 762fc776362ba331e51e47281e809f89b680df0a..adbd39535d6b8cb230abb68611279e6a08fd59f5 100644
--- a/Source/Settings/Settings.cs
+++ b/Source/Settings/Settings.cs
@@ -1,5 +1,4 @@
-using System;
-using HugsLib.Settings;
+using HugsLib.Settings;
 using UnityEngine;
 using Verse;
 
diff --git a/Source/WorkGivers/WorkGiver_Sexchecks.cs b/Source/WorkGivers/WorkGiver_Sexchecks.cs
index 99264a5be5c03aaea0331e4759b86582190f1ece..a7a2e73707485ef3a9b5383b787282ee9dcb0c05 100644
--- a/Source/WorkGivers/WorkGiver_Sexchecks.cs
+++ b/Source/WorkGivers/WorkGiver_Sexchecks.cs
@@ -81,6 +81,7 @@ namespace rjw
 			}
 			//Log.Message("7");
 			if (!pawn.IsDesignatedHero())
+			{
 				if (!RJWSettings.WildMode)
 				{
 					if (pawn.IsDesignatedComfort() || pawn.IsDesignatedBreeding())
@@ -100,6 +101,16 @@ namespace rjw
 					}
 				}
 
+			}
+			else
+			{
+				if (!pawn.IsHeroOwner())
+				{
+					//Log.Message("[RJW]WorkGiver_Sexchecks::player interaction for not owned hero, exit");
+					return false;
+				}
+			}
+
 			if (!MoreChecks(pawn, t, forced))
 				return false;
 
diff --git a/todo.txt b/todo.txt
index a356edf6b11ab29a446bb1a2dc685d94bccfe47b..2ea6de43ea8c8ab82668cb84659331068e80be7d 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,4 +1,5 @@
 CORE:
+
 fix naked pawn drawing when job driver is interrupted for any reason and pawn doesnt get redressed/changes back to dressed art
 split processsex(again) in 2 functions:
 1st - at beginning of jobgiver to determine sex type
@@ -47,9 +48,7 @@ BDSM:
 -vibrators?
 
 Multiplayer:
--synchronising for designators, so it doesnt disconnect, pawn interraction log, who knows  what else
--bind hero to player id
--fixing all unknown bugs?(lol)
+-figure out predictable seeds instead of syncmethod's
 
 maybe:
 add support for other pregnancy mods