feat: add ClaimLink persistence via savegame-backed registry

Adds a ClaimLink data model and ClaimLinkRegistry that loads from
and writes to the savegame's keyed data store (protobuf-net, via
WorldManager.SaveGame), so claim link state survives restarts
without touching vanilla claim data. Also silences nullable
warnings on statics that are guaranteed set by StartServerSide
before any use, and on TryGetValue out-params guarded by the if.
This commit is contained in:
2026-07-12 18:31:21 +02:00
parent a470456dcc
commit 02c9a22bf1
5 changed files with 71 additions and 4 deletions
+10
View File
@@ -0,0 +1,10 @@
using ProtoBuf;
namespace ClaimLink;
[ProtoContract]
public class ClaimLink
{
[ProtoMember(1)]
public int GroupId;
}
+4
View File
@@ -16,6 +16,10 @@
<HintPath>$(COMMANDHOOK)/CommandHook.dll</HintPath> <HintPath>$(COMMANDHOOK)/CommandHook.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="protobuf-net">
<HintPath>$(VINTAGE_STORY)/Lib/protobuf-net.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+4 -2
View File
@@ -6,8 +6,9 @@ namespace ClaimLink;
public class ClaimLinkModSystem : ModSystem public class ClaimLinkModSystem : ModSystem
{ {
internal static ILandClaimAPI LandClaimAPI; internal static ILandClaimAPI LandClaimAPI = null!;
internal static ILogger Logger; internal static ILogger Logger = null!;
internal static ClaimLinkRegistry Registry = null!;
internal ClaimLinkCommandListener? cmdListener; internal ClaimLinkCommandListener? cmdListener;
public override bool ShouldLoad(EnumAppSide forSide) => forSide == EnumAppSide.Server; public override bool ShouldLoad(EnumAppSide forSide) => forSide == EnumAppSide.Server;
@@ -16,6 +17,7 @@ public class ClaimLinkModSystem : ModSystem
{ {
LandClaimAPI = api.World.Claims; LandClaimAPI = api.World.Claims;
Logger = api.Logger; Logger = api.Logger;
Registry = new ClaimLinkRegistry(api.WorldManager.SaveGame);
cmdListener = new ClaimLinkCommandListener(); cmdListener = new ClaimLinkCommandListener();
CommandHookModSystem.Register(cmdListener); CommandHookModSystem.Register(cmdListener);
+51
View File
@@ -0,0 +1,51 @@
using System.Collections.Generic;
using ProtoBuf;
using Vintagestory.API.Server;
namespace ClaimLink;
[ProtoContract]
public class ClaimLinkData
{
[ProtoMember(1)]
public List<ClaimLink> Links = new();
}
public class ClaimLinkRegistry
{
private const string SaveKey = "claimlink:links";
private readonly ISaveGame saveGame;
private readonly Dictionary<int, ClaimLink> byGroupId = new();
public ClaimLinkRegistry(ISaveGame saveGame)
{
this.saveGame = saveGame;
ClaimLinkData? data = saveGame.GetData<ClaimLinkData?>(SaveKey, null);
if (data == null)
return;
foreach (ClaimLink link in data.Links)
byGroupId[link.GroupId] = link;
}
public ClaimLink? Get(int groupId) => byGroupId.TryGetValue(groupId, out ClaimLink? link) ? link : null;
public void Add(ClaimLink link)
{
byGroupId[link.GroupId] = link;
Save();
}
public void Remove(int groupId)
{
byGroupId.Remove(groupId);
Save();
}
private void Save()
{
saveGame.StoreData(SaveKey, new ClaimLinkData { Links = new List<ClaimLink>(byGroupId.Values) });
}
}
+2 -2
View File
@@ -55,7 +55,7 @@ public class ClaimLinkCommandListener : ICommandHookListener
{ {
string word = rawArgs.PeekWord(); string word = rawArgs.PeekWord();
if (topLevel.TryGetValue(word, out SubHandler handler)) if (topLevel.TryGetValue(word, out SubHandler? handler))
{ {
rawArgs.PopWord(); rawArgs.PopWord();
handler(args.Caller, rawArgs); handler(args.Caller, rawArgs);
@@ -77,7 +77,7 @@ public class ClaimLinkCommandListener : ICommandHookListener
{ {
string word = args.PeekWord(); string word = args.PeekWord();
if (claimSub.TryGetValue(word, out SubHandler handler)) if (claimSub.TryGetValue(word, out SubHandler? handler))
{ {
args.PopWord(); args.PopWord();
handler(caller, args); handler(caller, args);