Skip to content

Commit

Permalink
Merge pull request #31 from lbmaian/master
Browse files Browse the repository at this point in the history
Fix crash in RW 1.3.3116+ and some minor bugs
  • Loading branch information
jecrell authored Sep 10, 2021
2 parents dc40019 + 1f9ecb3 commit b1d26eb
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 45 deletions.
Binary file modified 1.3/Assemblies/DoorsExpanded.dll
Binary file not shown.
9 changes: 8 additions & 1 deletion About/About.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<downloadUrl>https://github.com/pardeike/HarmonyRimWorld/releases/latest</downloadUrl>
</li>
</modDependencies>
<description>1.4.0.0 (08-08-2021)
<description>1.4.0.1 (09-10-2021)

Adds new types and sizes of doors to RimWorld and an extensible framework for other mods to add such doors.

Expand All @@ -42,6 +42,13 @@ Cade Perkinson, Jay Sacane, John Pahl, Tankok1998 also known as the Shermanlover
========================
Changelog
========================
1.4.0.1 (09-10-2021)
========================
Fix crash due to infinite recursion in RW 1.3.3116+
Fix blueprints and frames of our doors not having proper labels
Fix CompProperties_PostProcessText not being removed after processing if not in dev mode (minor optimization)
Update PlaceWorker_OnTopOfWalls to consider smoothed buildings as walls (note: if JecsTools is loaded before Doors Expanded, this fix won't apply for the time being)

1.4.0.0 (08-08-2021)
========================
Note: Starting from this version, changes only apply to RimWorld 1.3+.
Expand Down
7 changes: 7 additions & 0 deletions About/Changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
1.4.0.1 (09-10-2021)
========================
Fix crash due to infinite recursion in RW 1.3.3116+
Fix blueprints and frames of our doors not having proper labels
Fix CompProperties_PostProcessText not being removed after processing if not in dev mode (minor optimization)
Update PlaceWorker_OnTopOfWalls to consider smoothed buildings as walls (note: if JecsTools is loaded before Doors Expanded, this fix won't apply for the time being)

1.4.0.0 (08-08-2021)
========================
Note: Starting from this version, changes only apply to RimWorld 1.3+.
Expand Down
2 changes: 1 addition & 1 deletion About/Manifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Manifest>
<identifier>DoorsExpanded</identifier>
<version>1.4.0.0</version>
<version>1.4.0.1</version>
<dependencies />
<incompatibleWith />
<manifestUri>https://raw.githubusercontent.com/jecrell/DoorsExpanded/master/About/Manifest.xml</manifestUri>
Expand Down
2 changes: 1 addition & 1 deletion About/Version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.4.0.0
1.4.0.1
25 changes: 4 additions & 21 deletions Source/Building_DoorRegionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,10 @@ public Building_DoorExpanded ParentDoor
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
TLog.Log(this);
// Building_Door.SpawnSetup calls BlockedOpenMomentary, which will be delegating to Building_DoorExpanded.
// Since that Building_DoorExpanded may not be spawned yet, we want to avoid this.
// Building_Door.SpawnSetup also calls ClearReachabilityCache, which is redundant with Building_DoorExpanded
// (although not harmful).
// So we skip calling Building_Door.SpawnSetup (via base.SpawnSetup) and instead call Building.SpawnSetup.
var Building_SpawnSetup = (Action<Map, bool>)Activator.CreateInstance(typeof(Action<Map, bool>), this,
methodof_Building_SpawnSetup.MethodHandle.GetFunctionPointer());
Building_SpawnSetup(map, respawningAfterLoad);
// See HarmonyPatches.InvisDoorSpawnSetupTranspiler.
base.SpawnSetup(map, respawningAfterLoad);
}

private static readonly MethodInfo methodof_Building_SpawnSetup =
AccessTools.Method(typeof(Building), nameof(Building.SpawnSetup));

public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
{
TLog.Log(this);
Expand Down Expand Up @@ -159,18 +150,10 @@ public override void Tick()
ticksSinceOpen = parentDoor.TicksSinceOpen;
ticksUntilClose = parentDoor.TicksUntilClose;

// We're delegating all the Tick logic to Building_DoorExpanded, which syncs its fields with its invis doors as needed.
// So we skip calling Building_Door.Tick (via base.Tick()) and instead call Building.Tick (actually ThingWithComps.Tick).
// Not replicating the logic in ThingWithComps.Tick, in case the logic changes or another mod patches that method.
Building_Tick ??= (Action)Activator.CreateInstance(typeof(Action), this,
methodof_Building_Tick.MethodHandle.GetFunctionPointer());
Building_Tick();
// See HarmonyPatches.InvisDoorTickTranspiler.
base.Tick();
}

private static readonly MethodInfo methodof_Building_Tick =
AccessTools.Method(typeof(Building), nameof(Building.Tick));
private Action Building_Tick;

public override void PostApplyDamage(DamageInfo dinfo, float totalDamageDealt)
{
// Note: The invis door def has useHitPoints=true, so invis doors never take damage.
Expand Down
1 change: 1 addition & 0 deletions Source/Building_Door_Reference/RW1.3_migration_notes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ Room members that are new:
public bool ProperRoom
private int OpenRoofCountStopAt(int threshold)
Room.OpenRoofCountStopAt already exists, but its code is moved to District.OpenRoofCountStopAt
public IEnumerable<Thing> ContainedThingsList(IEnumerable<ThingDef> thingDefs)
private string DebugRolesString()
public override string ToString()
Room.ToString already exists, but its code is moved to District.ToString
Expand Down
85 changes: 70 additions & 15 deletions Source/CompProperties_PostProcessText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,16 @@ public class CompProperties_PostProcessText : CompProperties
public ThingDef defaultLabelAndDescriptionFrom;
public bool appendSizeToLabel;

[Unsaved(false)]
private string origLabel;
[Unsaved]
public string origLabel;

[Unsaved]
public string baseLabel;

[Unsaved]
private bool finalized = false;

public static readonly Dictionary<ThingDef, CompProperties_PostProcessText> defToComp = new();

public CompProperties_PostProcessText()
{
Expand All @@ -24,30 +32,77 @@ public CompProperties_PostProcessText()

public override void ResolveReferences(ThingDef parentDef)
{
// Note: ResolveReferences can run more than once for a single def, so ensure it's idempotent.
// This method can be called multiple times per instance, so ensure idempotency.
if (finalized)
return;

defToComp[parentDef] = this;

origLabel = parentDef.label;
// This logic can't be done in PostLoadSpecial due to needing defaultLabelAndDescriptionFrom needing to be resolved.
if (defaultLabelAndDescriptionFrom is not null)
{
var otherPostProcessText = defaultLabelAndDescriptionFrom.GetCompProperties<CompProperties_PostProcessText>();
otherPostProcessText?.ResolveReferences(defaultLabelAndDescriptionFrom);
if (parentDef.label.NullOrEmpty())
parentDef.label =
defaultLabelAndDescriptionFrom.GetCompProperties<CompProperties_PostProcessText>()?.origLabel ??
defaultLabelAndDescriptionFrom.label;
{
var newLabel = otherPostProcessText?.baseLabel ?? defaultLabelAndDescriptionFrom.label;
if (TLog.Enabled)
TLog.Log(this, $"{parentDef}.label: {QuoteString(parentDef.label)} => {QuoteString(newLabel)}");
parentDef.label = newLabel;
}
if (parentDef.description.NullOrEmpty())
parentDef.description = defaultLabelAndDescriptionFrom.description;
{
var newDescription = defaultLabelAndDescriptionFrom.description;
if (TLog.Enabled)
TLog.Log(this, $"{parentDef}.description: {QuoteString(parentDef.description)} => {QuoteString(newDescription)}");
parentDef.description = newDescription;
}
}
if (appendSizeToLabel && origLabel is null)
baseLabel = parentDef.label;
if (appendSizeToLabel)
{
origLabel = parentDef.label;
var size = parentDef.Size;
parentDef.label = $"{parentDef.label} ({size.x}x{size.z})";
var newLabel = $"{parentDef.label} ({size.x}x{size.z})";
if (TLog.Enabled)
TLog.Log(this, $"{parentDef}.label: {QuoteString(parentDef.label)} => {QuoteString(newLabel)}");
parentDef.label = newLabel;
}
finalized = true;
}

// This serves as a useful hook for cleaning up after ourselves.
public override IEnumerable<string> ConfigErrors(ThingDef parentDef)
internal static string QuoteString(string str) => str is null ? "null" : $"\"{str.Replace("\\", "\\\\").Replace("\"", "\\\"")}\"";
}

[StaticConstructorOnStartup]
public static class PostProcessTextOnStartup
{
static PostProcessTextOnStartup()
{
// Comps add overhead, so since we're done, remove ourselves.
parentDef.comps.Remove(this);
yield break;
// Update frames and blueprints that were implicitly generated for defs with CompProperties_PostProcessText.
// These are generated before ResolveReferences are ever called.
foreach (var def in DefDatabase<ThingDef>.AllDefsListForReading)
{
if (def.entityDefToBuild is ThingDef entityDef &&
CompProperties_PostProcessText.defToComp.TryGetValue(entityDef, out var postProcessTextComp))
{
var newLabel = entityDef.label +
(postProcessTextComp.origLabel.NullOrEmpty() ? def.label : def.label.Substring(postProcessTextComp.origLabel.Length));
if (TLog.Enabled)
TLog.Log(typeof(PostProcessTextOnStartup), string.Format("{0}.label: {1} => {2}",
def, CompProperties_PostProcessText.QuoteString(def.label), CompProperties_PostProcessText.QuoteString(newLabel)));
def.label = newLabel;
}
}

// Comps add overhead, so since we're done, remove the comps now that they're no longer needed.
foreach (var (def, postProcessTextComp) in CompProperties_PostProcessText.defToComp)
{
if (TLog.Enabled)
TLog.Log(typeof(PostProcessTextOnStartup), $"Removing {postProcessTextComp} from {def}");
def.comps.Remove(postProcessTextComp);
}
CompProperties_PostProcessText.defToComp.Clear();
}
}
}
54 changes: 54 additions & 0 deletions Source/HarmonyPatches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ public static void Patches()
Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.StartManualCloseBy)),
prefix: nameof(InvisDoorStartManualCloseByPrefix),
priority: Priority.First);
Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.SpawnSetup)),
transpiler: nameof(InvisDoorSpawnSetupTranspiler));
Patch(original: AccessTools.Method(typeof(Building_Door), nameof(Building_Door.Tick)),
transpiler: nameof(InvisDoorTickTranspiler));

// Patches to redirect access from invis door def to its parent door def.
Patch(original: AccessTools.Method(typeof(GenStep_Terrain), nameof(GenStep_Terrain.Generate)),
Expand Down Expand Up @@ -745,6 +749,56 @@ public static bool InvisDoorStartManualCloseByPrefix(Building_Door __instance, P
return true;
}

// Building_Door.SpawnSetup
public static IEnumerable<CodeInstruction> InvisDoorSpawnSetupTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilGen)
{
// Building_Door.SpawnSetup calls BlockedOpenMomentary, which will be delegating to Building_DoorExpanded.
// If this is a Building_DoorRegionHandler, that Building_DoorExpanded may not be spawned yet, so we want to avoid this.
// (It also calls ClearReachabilityCache, which is redundant with Building_DoorExpanded, although not harmful).
// So if this is a Building_DoorRegionHandler, exit early after the call to Building.SpawnSetup.
// Historical: this used to be done in Building_DoorRegionHandler.SpawnSetup override, but the way it was done
// no longer seems to work in RW 1.3.3116+ (due to the Unity update?); hence, this Harmony patch.
return InvisDoorBaseCallTranspiler(instructions, ilGen, AccessTools.Method(typeof(Building), nameof(Building.SpawnSetup)));
}

// Building_Door.Tick
public static IEnumerable<CodeInstruction> InvisDoorTickTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilGen)
{
// If this is a Building_DoorRegionHandler, all Tick logic is delegated to Building_DoorExpanded,
// which syncs its fields with its invis doors as needed. So all the Tick logic in Building_Door itself should be skipped,
// other than that in Building.Tick, or rather ThingWithComps.Tick, since Building.Tick doesn't exist.
// Not replicating the logic in ThingWithComps.Tick, in case the logic changes or another mod patches that method.
// Historical: this used to be done in Building_DoorRegionHandler.Tick override, but the way it was done
// no longer seems to work in RW 1.3.3116+ (due to the Unity update?); hence, this Harmony patch.
return InvisDoorBaseCallTranspiler(instructions, ilGen, AccessTools.Method(typeof(Building), nameof(Building.Tick)));
}

// Generic transpiler that transforms all following instances of code:
// base.<baseMethod>(...)
// into:
// base.<baseMethod>(...)
// if (this is Building_DoorRegionHandler)
// return;
private static IEnumerable<CodeInstruction> InvisDoorBaseCallTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilGen,
MethodInfo baseMethod)
{
var instructionsList = instructions.AsList();

var baseCallIndex = instructionsList.FindIndex(instr => instr.Calls(baseMethod));
var afterBaseCallLabel = ilGen.DefineLabel();
var afterBaseCallInstr = instructionsList[baseCallIndex + 1];
instructionsList.SafeInsertRange(baseCallIndex + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Isinst, typeof(Building_DoorRegionHandler)),
new CodeInstruction(OpCodes.Brfalse, afterBaseCallLabel),
new CodeInstruction(OpCodes.Ret),
});
afterBaseCallInstr.labels.Add(afterBaseCallLabel);

return instructionsList;
}

// GenStep_Terrain.Generate
// GenGrid.CanBeSeenOver
// GridsUtility.Filled
Expand Down
4 changes: 2 additions & 2 deletions Source/PlaceWorker_OnTopOfWalls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public override AcceptanceReport AllowsPlacing(BuildableDef checkingDef, IntVec3

static bool IsWall(Thing thing)
{
// In RW 1.3+, it seems like BuildingProperties.isPlaceOverableWall indicates whether something is a "wall".
if (thing.def.building?.isPlaceOverableWall ?? false)
// In RW 1.3+, it seems like BuildingProperties.isPlaceOverableWall or ThingDef.IsSmoothed indicates whether something is a "wall".
if (thing.def is { building: { isPlaceOverableWall: true } } or { IsSmoothed: true })
return true;
// Legacy heuristic for mods that don't use isPlaceOverableWall.
if (thing.def.defName.Contains("Wall"))
Expand Down
2 changes: 1 addition & 1 deletion Source/ProjectHeron.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<NoStdLib>true</NoStdLib>
<LangVersion>9.0</LangVersion>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Version>1.4.0.0</Version>
<Version>1.4.0.1</Version>
</PropertyGroup>
<PropertyGroup>
<!-- Always using "Release" build -->
Expand Down
7 changes: 4 additions & 3 deletions Source/TLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ public static void Log(object obj, string message = null, [CallerMemberName] str
return;
}

var context = Current.Game is { } game ? $"Tick {game.tickManager.TicksGame}" : $"{Current.ProgramState}";
message ??= obj.ToString();
if (logLevel is TLogLevel.Debug)
{
message = $"[Tick {Find.TickManager.TicksGame}] {message} called from {obj.GetType()}:{callerMemberName}";
message = $"[{context}] {message} called from {obj as Type ?? obj.GetType()}:{callerMemberName}";
}
else // if (logLevel is TLogLevel.StackTrace)
{
Expand All @@ -58,12 +59,12 @@ public static void Log(object obj, string message = null, [CallerMemberName] str
updateValueFactory: (_, counter) => counter + 1);
return $"{methodName} {{{counter}}}";
});
message = $"[Tick {Find.TickManager.TicksGame}] {message} called from {id}";
message = $"[{context}] {message} called from {id}";
if (newId)
message += "\n" + stackTraceStr;
}

// Workaround for RW 1.2 now monitoring all Unity Debug usage for the 1000 max message limit:
// Workaround for RW 1.2+ now monitoring all Unity Debug usage for the 1000 max message limit:
if (!UnityEngine.Debug.unityLogger.logEnabled)
Verse.Log.ResetMessageCount();

Expand Down

0 comments on commit b1d26eb

Please sign in to comment.