static public string PawnGCDebugResults(WorldPawnGC __instance) { Dictionary <Pawn, string> dictionary = new Dictionary <Pawn, string>(); AccumulatePawnGCData(__instance, dictionary).ExecuteEnumerable(); Dictionary <string, int> countReasons = new Dictionary <string, int>(); foreach (Pawn current in Find.WorldPawns.AllPawnsAlive) { string reason; if (!dictionary.TryGetValue(current, out reason)) { reason = "Discarded"; } int counter; if (!countReasons.TryGetValue(reason, out counter)) { counter = 0; } countReasons[reason] = ++counter; } return(GenText.ToLineList(from kvp in countReasons orderby kvp.Value descending select string.Format("{0}: {1}", kvp.Value, kvp.Key))); }
static public void RunGC(WorldPawnGC __instance) { __instance.CancelGCPass(); PerfLogger.Reset(); PawnGCPass(__instance).ExecuteEnumerable(); float num = PerfLogger.Duration() * 1000f; PerfLogger.Flush(); Log.Message(string.Format("World pawn GC run complete in {0} ms", num)); }
static public void LogDotgraph(WorldPawnGC __instance) { logDotgraph = new StringBuilder(); logDotgraphUniqueLinks = new HashSet <string>(); GUIUtility.systemCopyBuffer = "building log dot graph..."; logDotgraph.AppendLine("digraph { rankdir=LR;"); AccumulatePawnGCData(__instance, new Dictionary <Pawn, string>()).ExecuteEnumerable(); logDotgraph.AppendLine("}"); GUIUtility.systemCopyBuffer = logDotgraph.ToString(); Log.Message("Dotgraph copied to clipboard"); logDotgraph = null; logDotgraphUniqueLinks = null; }
static private IEnumerable AccumulatePawnGCData(WorldPawnGC __instance, Dictionary <Pawn, string> keptPawns) { GetCriticalPawnReasonType GetCriticalPawnReason = (GetCriticalPawnReasonType)Delegate.CreateDelegate(typeof(GetCriticalPawnReasonType), __instance, typeof(WorldPawnGC).GetMethod("GetCriticalPawnReason", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); foreach (Pawn current in PawnsFinder.AllMapsWorldAndTemporary_AliveOrDead) { string criticalPawnReason = GetCriticalPawnReason(current); if (!criticalPawnReason.NullOrEmpty()) { keptPawns[current] = criticalPawnReason; if (logDotgraph != null) { logDotgraph.AppendLine(string.Format("{0} [label=<{0}<br/><font point-size=\"10\">{1}</font>> color=\"{2}\" shape=\"{3}\"];", new object[] { WorldPawnGC.DotgraphIdentifier(current), criticalPawnReason, (current.relations == null || !current.relations.everSeenByPlayer) ? "grey" : "black", (!current.RaceProps.Humanlike) ? "box" : "oval" })); } } else if (logDotgraph != null) { logDotgraph.AppendLine(string.Format("{0} [color=\"{1}\" shape=\"{2}\"];", WorldPawnGC.DotgraphIdentifier(current), (current.relations == null || !current.relations.everSeenByPlayer) ? "grey" : "black", (!current.RaceProps.Humanlike) ? "box" : "oval")); } } //This is for finding non spawned but alive raiders/visitors not tremendously usefull foreach (Pawn current2 in (from pawn in PawnsFinder.AllMapsWorldAndTemporary_Alive where pawn.RaceProps.Humanlike && !keptPawns.ContainsKey(pawn) orderby pawn.records.StoryRelevance descending select pawn).Take(20)) { keptPawns[current2] = "StoryRelevant"; } //We now have lets say 1000 keepers. Over the generations family trees grow wider and wider. //Not only do the trees grow wider over generations, male animals will breed with multiple females and as such multiple family trees get merged amplyfiing the problem. Pawn[] storyRelevantPawns = new Pawn[keptPawns.Count]; keptPawns.Keys.CopyTo(storyRelevantPawns, 0); foreach (Pawn pawn in storyRelevantPawns) { AddAllRelationships(__instance, pawn, keptPawns); yield return(null); } foreach (Pawn pawn in storyRelevantPawns) { AddAllMemories(pawn, keptPawns); } }
public static bool WorldPawnGCTickPrefix(WorldPawnGC __instance) { if (lastSuccessfulGCTickGet(__instance) < Find.TickManager.TicksGame / 15000 * 15000) { if (activeGCProcessGet(__instance) == null) { activeGCProcessSet(__instance, PawnGCPass(__instance).GetEnumerator()); //since we are faster we could amp up the speed, reducing the chance of an interrupt and thus overhead //currentGCRateSet(__instance,8); if (DebugViewSettings.logWorldPawnGC) { Log.Message(string.Format("World pawn GC started at rate {0}", currentGCRateGet(__instance))); } } } return(true); }
public static bool WorldPawnGCTick(WorldPawnGC __instance) { if (lastSuccessfulGCTickFieldRef(__instance) >= Find.TickManager.TicksGame / 15000 * 15000) { return(false); } //if (activeGCProcess == null) //{ //activeGCProcess = PawnGCPass().GetEnumerator(); if (DebugViewSettings.logWorldPawnGC) { Log.Message($"World pawn GC started at rate {currentGCRateFieldRef(__instance)}"); } //} //if (activeGCProcess == null) //{ //return false; //} bool flag = false; for (int i = 0; i < currentGCRateFieldRef(__instance); i++) { if (flag) { break; } //flag = !activeGCProcess.MoveNext(); } if (flag) { lastSuccessfulGCTickFieldRef(__instance) = Find.TickManager.TicksGame; currentGCRateFieldRef(__instance) = 1; //activeGCProcess = null; if (DebugViewSettings.logWorldPawnGC) { Log.Message("World pawn GC complete"); } } return(false); }
static IEnumerable PawnGCPass(WorldPawnGC __instance) { Dictionary <Pawn, string> keptPawns = new Dictionary <Pawn, string>(); foreach (object _ in AccumulatePawnGCData(__instance, keptPawns)) { yield return(null); } //since resets happen if pawns are added/removed we do not need to create the copy before we accumulate the data Pawn[] AllPawnsAliveOrDeadBuffered = Find.WorldPawns.AllPawnsAliveOrDead.ToArray <Pawn>(); foreach (Pawn pawn in AllPawnsAliveOrDeadBuffered) { if (!keptPawns.ContainsKey(pawn)) { Find.WorldPawns.RemoveAndDiscardPawnViaGC(pawn); } } }
static bool Prefix(WorldPawnGC __instance, out string __result) { __result = WorldPawnGCOptimized.PawnGCDebugResults(__instance); return(false); }
static bool Prefix(WorldPawnGC __instance) { WorldPawnGCOptimized.LogDotgraph(__instance); return(false); }
static bool Prefix(WorldPawnGC __instance) { WorldPawnGCOptimized.RunGC(__instance); return(false); }
static bool Prefix(WorldPawnGC __instance) { return(WorldPawnGCOptimized.WorldPawnGCTickPrefix(__instance)); }
static private void AddAllMemories(Pawn pawn, Dictionary <Pawn, string> keptPawns) { if (pawn.needs == null || pawn.needs.mood == null || pawn.needs.mood.thoughts == null || pawn.needs.mood.thoughts.memories == null) { return; } foreach (Thought_Memory current in pawn.needs.mood.thoughts.memories.Memories) { if (current.otherPawn != null) { if (logDotgraph != null) { string text = string.Format("{0}->{1} [label=<{2}> color=\"orange\"];", WorldPawnGC.DotgraphIdentifier(pawn), WorldPawnGC.DotgraphIdentifier(current.otherPawn), current.def); if (!logDotgraphUniqueLinks.Contains(text)) { logDotgraphUniqueLinks.Add(text); logDotgraph.AppendLine(text); } } if (!keptPawns.ContainsKey(current.otherPawn)) { keptPawns[current.otherPawn] = "Memory"; } } } }
static private void AddAllRelationships(WorldPawnGC __instance, Pawn pawn, Dictionary <Pawn, string> keptPawns) { if (pawn.relations == null || !pawn.relations.RelatedToAnyoneOrAnyoneRelatedToMe || !pawn.RaceProps.IsFlesh) { return; } Stack <Pawn> stack = null; HashSet <Pawn> visited = null; try { stack = SimplePool <Stack <Pawn> > .Get(); visited = SimplePool <HashSet <Pawn> > .Get(); stack.Push(pawn); visited.Add(pawn); while (stack.Count > 0) { Pawn p = stack.Pop(); foreach (Pawn otherPawn in p.relations.DirectRelations.Select(r => r.otherPawn).Concat <Pawn>( (HashSet <Pawn>) typeof(Pawn_RelationsTracker).GetField("pawnsWithDirectRelationsWithMe", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).GetValue(p.relations))) { if (!visited.Contains(otherPawn)) { if (otherPawn.RaceProps.IsFlesh) { PawnRelationDef relation = AllAllowedRelationDefs.FirstOrDefault(def => def.Worker.InRelation(pawn, otherPawn)); if (relation != default(PawnRelationDef)) { if (logDotgraph != null) { string text = string.Format("{0}->{1} [label=<{2}> color=\"purple\"];", WorldPawnGC.DotgraphIdentifier(pawn), WorldPawnGC.DotgraphIdentifier(otherPawn), relation.ToString()); if (!logDotgraphUniqueLinks.Contains(text)) { logDotgraphUniqueLinks.Add(text); logDotgraph.AppendLine(text); } } if (!keptPawns.ContainsKey(otherPawn)) { keptPawns[otherPawn] = "Relationship"; //Not correct to have it here if the pairing between two pawns is filtered but if we allow all relations this is correct. //The pursuit of animal family trees if a human is bonded with an animal is pointless. if (relation != PawnRelationDefOf.Bond) { stack.Push(otherPawn); } } } } visited.Add(otherPawn); } } } } finally { if (stack != null) { stack.Clear(); stack.TrimExcess(); SimplePool <Stack <Pawn> > .Return(stack); } if (visited != null) { visited.Clear(); stack.TrimExcess(); SimplePool <HashSet <Pawn> > .Return(visited); } } }