-
-
Notifications
You must be signed in to change notification settings - Fork 23
v0.4.4f6 compatibility #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1579,8 +1579,29 @@ private static bool NPCHealth_Revive_Prefix(S1NPCs.NPCHealth __instance) | |
| if (apiNpc == null || !apiNpc.IsCustomNPC) | ||
| return true; // use original for base NPCs | ||
|
|
||
| // Skip S1API NPCs for now | ||
| return false; | ||
| try | ||
| { | ||
| // Local-only revive: set state flags via reflection (read-only properties) | ||
| Utils.ReflectionUtils.TrySetFieldOrProperty(__instance, "IsDead", false); | ||
| Utils.ReflectionUtils.TrySetFieldOrProperty(__instance, "IsKnockedOut", false); | ||
|
|
||
| // Set health backing field directly to bypass SyncVar setter | ||
| Utils.ReflectionUtils.TrySetFieldOrProperty( | ||
| __instance, "<Health>k__BackingField", __instance.MaxHealth); | ||
|
|
||
| // Disable behaviours locally (non-networked equivalent of Disable_Server) | ||
| baseNpc.Behaviour.DeadBehaviour?.Disable(); | ||
| baseNpc.Behaviour.UnconsciousBehaviour?.Disable(); | ||
|
|
||
| // Fire revive event so downstream listeners still react | ||
| __instance.onRevive?.Invoke(); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| MelonLogger.Warning($"[S1API] Revive guard failed for custom NPC: {ex.Message}"); | ||
| } | ||
|
|
||
| return false; // skip original to avoid SyncVar/networking calls | ||
| } | ||
|
Comment on lines
+1582
to
1612
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t skip original revive when the guard path fails. On Line 1604 the patch always returns 💡 Suggested fail-safe patch- try
+ bool localReviveApplied = false;
+ try
{
// Local-only revive: set state flags via reflection (read-only properties)
- Utils.ReflectionUtils.TrySetFieldOrProperty(__instance, "IsDead", false);
- Utils.ReflectionUtils.TrySetFieldOrProperty(__instance, "IsKnockedOut", false);
+ bool isDeadSet = Utils.ReflectionUtils.TrySetFieldOrProperty(__instance, "IsDead", false);
+ bool isKoSet = Utils.ReflectionUtils.TrySetFieldOrProperty(__instance, "IsKnockedOut", false);
// Set health backing field directly to bypass SyncVar setter
- Utils.ReflectionUtils.TrySetFieldOrProperty(
+ bool healthSet = Utils.ReflectionUtils.TrySetFieldOrProperty(
__instance, "<Health>k__BackingField", __instance.MaxHealth);
+ if (!(isDeadSet && isKoSet && healthSet))
+ {
+ MelonLogger.Warning("[S1API] Revive guard could not apply all local state updates; falling back to original Revive().");
+ return true;
+ }
+
// Disable behaviours locally (non-networked equivalent of Disable_Server)
- baseNpc.Behaviour.DeadBehaviour?.Disable();
- baseNpc.Behaviour.UnconsciousBehaviour?.Disable();
+ baseNpc?.Behaviour?.DeadBehaviour?.Disable();
+ baseNpc?.Behaviour?.UnconsciousBehaviour?.Disable();
// Fire revive event so downstream listeners still react
__instance.onRevive?.Invoke();
+ localReviveApplied = true;
}
catch (Exception ex)
{
MelonLogger.Warning($"[S1API] Revive guard failed for custom NPC: {ex.Message}");
+ return true;
}
- return false; // skip original to avoid SyncVar/networking calls
+ return !localReviveApplied ? true : false; // skip original only on successful local revive🤖 Prompt for AI Agents |
||
|
|
||
| /// <summary> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: ifBars/S1API
Length of output: 87
🏁 Script executed:
Repository: ifBars/S1API
Length of output: 79
🏁 Script executed:
Repository: ifBars/S1API
Length of output: 84
🏁 Script executed:
sed -n '1720,1730p' ./S1API/Entities/NPC.csRepository: ifBars/S1API
Length of output: 415
🏁 Script executed:
rg -n --type=cs 'SafeIsServer' -A2 -B2Repository: ifBars/S1API
Length of output: 270
🏁 Script executed:
sed -n '1535,1545p' ./S1API/Entities/NPC.csRepository: ifBars/S1API
Length of output: 334
🏁 Script executed:
rg -n 'Panic()' ./S1API/Entities/NPC.csRepository: ifBars/S1API
Length of output: 787
🏁 Script executed:
Repository: ifBars/S1API
Length of output: 38
🏁 Script executed:
Repository: ifBars/S1API
Length of output: 38
🏁 Script executed:
rg -n 'SetPanicked_Server' --type=csRepository: ifBars/S1API
Length of output: 122
🏁 Script executed:
rg -n 'SetPanicked_Server' --type=cs -A2 -B2Repository: ifBars/S1API
Length of output: 307
🏁 Script executed:
grep -n 'SafeIsServer()' ./S1API/Entities/NPC.csRepository: ifBars/S1API
Length of output: 103
🏁 Script executed:
Repository: ifBars/S1API
Length of output: 100
🏁 Script executed:
sed -n '1700,1750p' ./S1API/Entities/NPC.csRepository: ifBars/S1API
Length of output: 2103
🏁 Script executed:
sed -n '1700,1780p' ./S1API/Entities/NPC.csRepository: ifBars/S1API
Length of output: 3363
🏁 Script executed:
rg -n 'SafeIsServer\(\)' --type=cs -B5 -A5Repository: ifBars/S1API
Length of output: 722
🏁 Script executed:
Repository: ifBars/S1API
Length of output: 115
Add server-context guard to
Panic()method to prevent client-side breakage.The
Panic()method is a public API that directly callsSetPanicked_Server(), a server-only method. This creates a mismatch: the public method can be invoked from client contexts, but the underlying implementation will fail outside server execution. The codebase already providesSafeIsServer()for exactly this purpose.Suggested safeguard
📝 Committable suggestion
🤖 Prompt for AI Agents