Skip to content

Allow public flint, branch, and stone pickups inside wards - #144

Merged
jneb802 merged 3 commits into
mainfrom
feature/public-basic-pickables
Oct 17, 2026
Merged

jneb802 merged 3 commits into
mainfrom
feature/public-basic-pickables

Conversation

@jneb802

@jneb802 jneb802 commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Players without ward permission can now gather Pickable_Flint, Pickable_Branch, and Pickable_Stone through Protective Wards. The optional compatibility patch skips its denial only for these exact world pickables. Other plants, containers, dropped items, and ward permissions retain their existing rules.

Version: 0.1.101. Protective Wards remains optional. If its target method changes, the mod logs a warning and leaves protection unchanged.

Validation:

  • Release build passed: zero errors, 84 existing warnings.
  • Reproduced blocked pickups on Valnet client 02 using production PraetorisClient 0.1.98.
  • With 0.1.101, an unpermitted guest collected wood, flint, and stone inside another character's active ward. Mushrooms stayed blocked until the ward was removed.
  • Valheim 1.0.20 and Season 8 modpack 8.0.45 content, Protective Wards 2.1.0. BepInEx remained 5.4.2350 instead of 5.4.2351.
  • This was a local world on Valnet, not a separate dedicated-server session. Existing dropped-item mode rules still apply; tested AllowNonPlayerDropped.

@github-actions

Copy link
Copy Markdown

Now I have what I need. Looking at CraftyBoxesWardGuard.cs reveals the established pattern for reaching into Protective Wards — it uses AccessTools.TypeByName("ProtectiveWards.ProtectiveWards"). The new code uses plugin.Instance.GetType() instead, which I need to evaluate for equivalence.


No blocking issues found.

Source/Compatibility/PublicBasicPickables.cs

The Harmony prefix logic is correct. __result = false; return false; skips BlockUnauthorizedWardInteraction and returns false (meaning "do not block") to callers, which is the right semantics for a method named Block…. The __0 parameter maps correctly to the first argument (Component) of the target signature (Component, Humanoid, bool). Harmony permits prefixes to declare only the parameters they use, so omitting Humanoid and bool is fine.

The method-lookup approach deviates from the pattern established in CraftyBoxesWardGuard.cs, which calls AccessTools.TypeByName("ProtectiveWards.ProtectiveWards") directly. The new code reaches the type via plugin.Instance.GetType(). Because ProtectiveWards.ProtectiveWards is the BepInEx plugin class itself (its type name matches the module name), both paths resolve to the same type at runtime — validation with 2.1.0 confirms the method was found and the patch applied. If a future Protective Wards update moves or renames the method, the existing catch block logs a warning and leaves ward protection unchanged, which is the correct fail-safe.

Source/Core/Plugin.cs

The [BepInDependency(PublicBasicPickables.ProtectiveWardsGuid, SoftDependency)] attribute and the TryApply call are placed correctly. Load order is respected; TryApply runs after Chainloader.PluginInfos is populated.

thunderstore.toml version

The file was at 0.1.98 while manifest.json was already at 0.1.100, a pre-existing mismatch. This PR brings both to 0.1.101 — a sync, not a regression. Thunderstore accepts any version higher than the last published one, so the jump from 0.1.98 to 0.1.101 on that platform is valid.

Testing gap (non-blocking)

Validation was on a local world only; a dedicated-server session was not tested. If Protective Wards' BlockUnauthorizedWardInteraction is invoked server-side on a headless server (where this mod may not be loaded), the client-only patch would be ineffective. This is an acknowledged limitation in the validation doc, not a code defect — the fail-safe path leaves protection unchanged in that case.

@github-actions

Copy link
Copy Markdown

No blocking issues found.

PublicBasicPickables — prefix patch logic

AllowBasicPickable (Source/Compatibility/PublicBasicPickables.cs:64) correctly short-circuits for the three named pickables and falls through for everything else:

  • __0 is not Pickable handles a null or non-Pickable Component correctly (returns true, runs original).
  • __result = false with return false (skip original) matches the expected semantics of BlockUnauthorizedWardInteraction: setting the return value to false = "not blocked" = allow, which is what the validation describes.
  • For all other pickables and component types the prefix returns true so the original blocking logic runs unchanged.

Method lookup

AccessTools.DeclaredMethod with an explicit parameter type list (Component, Humanoid, bool) pinpoints the exact overload. The ?? throw new MissingMethodException(...) wrapping in the outer try/catch means any version mismatch in Protective Wards logs a warning and leaves ward protection intact rather than silently mispatching or crashing.

Soft-dependency declaration

[BepInDependency(PublicBasicPickables.ProtectiveWardsGuid, BepInDependency.DependencyFlags.SoftDependency)] is correctly added to Plugin.cs:85. BepInEx will load Protective Wards before PraetorisClient when both are present, so Chainloader.PluginInfos is fully populated when TryApply is called from Awake.

Version numbers

Plugin.cs, manifest.json, and thunderstore.toml all reach 0.1.101 consistently. The prior gap between thunderstore.toml (was 0.1.98) and manifest.json/Plugin.cs (were 0.1.100) is a pre-existing discrepancy, not introduced by this diff.

@github-actions

Copy link
Copy Markdown

No blocking issues found.

Source/Compatibility/PublicBasicPickables.cs — The implementation is sound end to end.

  • Load ordering (Plugin.cs:70): The SoftDependency attribute on PublicBasicPickables.ProtectiveWardsGuid ensures BepInEx initialises Protective Wards before PraetorisClient when both are present, so Chainloader.PluginInfos is fully populated when TryApply runs in Awake. Without the attribute the TryGetValue could race and miss the entry; with it the ordering is guaranteed.

  • Method lookup and fallback (PublicBasicPickables.cs:37-46): AccessTools.DeclaredMethod on plugin.Instance.GetType() is reasonable for a plugin whose blocking logic sits on the plugin class. If the signature ever changes or the method lives on a different type, the ?? throw path is caught immediately, a warning is logged, and ward protection is left entirely unchanged — correct defensive behaviour.

  • Prefix logic (PublicBasicPickables.cs:49-58): The guard chain is correct. Anything that is not a Pickable, or is a Pickable with any other prefab name, returns true (lets the original run). Only the three named world-spawn pickables skip Protective Wards' denial by setting __result = false and returning false. Vanilla Pickable.Interact authority and RPC checks are unaffected.

  • Version numbers: Plugin.cs and manifest.json move consistently from 0.1.100 → 0.1.101. thunderstore.toml was already out of sync at 0.1.98 before this PR; the diff brings it to 0.1.101, which aligns all three files — this corrects a pre-existing skew rather than introducing one.

No other surfaces (CHANGELOG, namespace, soft-dependency registration) have issues.

@jneb802
jneb802 merged commit a3b8538 into main Oct 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant