Conversation
MpCompatLoader logged "Exception loading hatti.qualitybuilder: System.NullReferenceException" on startup with QualityBuilder installed. Root cause: the patch resolved the skilled-builder designator's right-click "set min quality" lambda by hard-coding the compiler-generated closure type name "QualityBuilder._Designator_SkilledBuilder+<>c". That cached <>c closure only exists while the lambda captures nothing. A recent QualityBuilder change made the designator's quality field (curQualityCat) a per-instance field instead of static, so the lambda now captures `this` and the compiler emits it as an instance method on the designator itself -- the <>c type no longer exists. AccessTools.TypeByName then returned null, and GetFirstMethodBySignature(null, ...) dereferenced it, throwing the NRE and aborting the whole compat class (so ToggleSkilled and the gizmo menu weren't synced either). Fix: sync the designator's state instead of the choice-setting lambda. Register a SyncWorker<Designator> for _Designator_SkilledBuilder that serializes curQualityCat, so MP replays each designation with the chosen quality and every client builds it at the same quality -- the same idiom other modded designators use (e.g. Dubs Bad Hygiene). This is also the only mechanism that still carries the quality after the refactor: the old approach synced the pick into a *static* field, but curQualityCat is now per-instance, so syncing the pick alone would no longer reach the designation (a latent desync even once the NRE is fixed). Each player's own menu pick stays local to their designator; the applied quality is what syncs. Also null-guard the three TypeByName lookups so a future internal rename in QualityBuilder degrades to a warning instead of taking down the compat class. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I can confirm that this commit fixes MP Compat for Qualitybuilder Unofficial 1.6 7/10 update, which otherwise does not work properly and throws NRE at launch. |
| MethodInfo designatorMethod = MpMethodUtil.GetFirstMethodBySignature(designatorType, argTypes); | ||
| // --- "Skilled" toggle gizmo on the building comp --- | ||
| Type builderCompType = AccessTools.TypeByName("QualityBuilder.CompQualityBuilder"); | ||
| if (builderCompType != null) |
There was a problem hiding this comment.
Failing silently here. Just let the whole thing fail with NPE.
There was a problem hiding this comment.
I know it had added something so it didn't just break it or something if QB wasn't working with it. I don't play with multiplayer and wouldn't have anyone to test stuff with if I did. So I'll just hope either you or someone else can do what's needed to fix it.
| // client resolves the same desired quality. This mirrors how other modded designators are synced | ||
| // (e.g. Dubs Bad Hygiene) and is robust to that closure refactor. | ||
| Type designatorType = AccessTools.TypeByName("QualityBuilder._Designator_SkilledBuilder"); | ||
| if (designatorType != null) |
There was a problem hiding this comment.
Assume it exists, and let it fail if it doesn't.
|
Not sure what changed, but now I get |
|
On top of that, multiple things are still not synced, like marking buildings that finish below target quality for deconstruction. |
I did change quite a few things after this. I don't think I need to change anything else, I haven't had anything fail due to QB recently. Since I can't test it and there were things notfood didn't like, I'll leave it up to them to fix it. |
Code by claude code, multiple people who use MPCompat have said this change seems to work fine. I haven't personally tested it since I don't play multiplayer.
MpCompatLoader logged "Exception loading hatti.qualitybuilder: System.NullReferenceException" on startup with QualityBuilder installed.
Root cause: the patch resolved the skilled-builder designator's right-click "set min quality" lambda by hard-coding the compiler-generated closure type name "QualityBuilder._Designator_SkilledBuilder+<>c". That cached <>c closure only exists while the lambda captures nothing. A recent QualityBuilder change made the designator's quality field (curQualityCat) a per-instance field instead of static, so the lambda now captures
thisand the compiler emits it as an instance method on the designator itself -- the <>c type no longer exists. AccessTools.TypeByName then returned null, and GetFirstMethodBySignature(null, ...) dereferenced it, throwing the NRE and aborting the whole compat class (so ToggleSkilled and the gizmo menu weren't synced either).Fix: sync the designator's state instead of the choice-setting lambda. Register a SyncWorker for _Designator_SkilledBuilder that serializes curQualityCat, so MP replays each designation with the chosen quality and every client builds it at the same quality -- the same idiom other modded designators use (e.g. Dubs Bad Hygiene). This is also the only mechanism that still carries the quality after the refactor: the old approach synced the pick into a static field, but curQualityCat is now per-instance, so syncing the pick alone would no longer reach the designation (a latent desync even once the NRE is fixed). Each player's own menu pick stays local to their designator; the applied quality is what syncs.
Also null-guard the three TypeByName lookups so a future internal rename in QualityBuilder degrades to a warning instead of taking down the compat class.