feat: deny non-members from modifying claim link buffer zones, bump to 0.4.0

This commit is contained in:
2026-08-09 20:05:15 +02:00
parent 636a7dee98
commit 095b826178
8 changed files with 166 additions and 1 deletions
+6
View File
@@ -34,4 +34,10 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="assets\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project> </Project>
+67
View File
@@ -0,0 +1,67 @@
using System.Collections.Generic;
using Vintagestory.API.MathTools;
namespace ClaimLink;
internal static class ClaimLinkBufferIndex
{
private static readonly Dictionary<long, HashSet<int>> groupIdsByRegion = new();
private static readonly Dictionary<int, List<Cuboidi>> buffersByGroupId = new();
internal static void Rebuild()
{
groupIdsByRegion.Clear();
buffersByGroupId.Clear();
int regionSize = ClaimLinkModSystem.WorldManager.RegionSize;
if (regionSize <= 0)
return;
foreach (int groupId in ClaimLinkModSystem.Registry.All)
{
List<Cuboidi> buffers = ClaimLinkBuffer.GetBuffer(groupId);
buffersByGroupId[groupId] = buffers;
HashSet<long> regionKeys = new();
foreach (Cuboidi buffer in buffers)
{
int minRegionX = buffer.MinX / regionSize;
int maxRegionX = buffer.MaxX / regionSize;
int minRegionZ = buffer.MinZ / regionSize;
int maxRegionZ = buffer.MaxZ / regionSize;
for (int rx = minRegionX; rx <= maxRegionX; rx++)
for (int rz = minRegionZ; rz <= maxRegionZ; rz++)
regionKeys.Add(ClaimLinkModSystem.WorldManager.MapRegionIndex2D(rx, rz));
}
foreach (long regionKey in regionKeys)
{
if (!groupIdsByRegion.TryGetValue(regionKey, out HashSet<int>? groupIds))
groupIdsByRegion[regionKey] = groupIds = new();
groupIds.Add(groupId);
}
}
}
internal static IEnumerable<int> CandidateGroupIds(BlockPos pos)
{
int regionSize = ClaimLinkModSystem.WorldManager.RegionSize;
if (regionSize <= 0)
return System.Array.Empty<int>();
long regionKey = ClaimLinkModSystem.WorldManager.MapRegionIndex2D(
pos.X / regionSize,
pos.Z / regionSize
);
return groupIdsByRegion.TryGetValue(regionKey, out HashSet<int>? groupIds)
? groupIds
: System.Array.Empty<int>();
}
internal static List<Cuboidi> CachedBuffer(int groupId) =>
buffersByGroupId.TryGetValue(groupId, out List<Cuboidi>? buffers)
? buffers
: new List<Cuboidi>();
}
+78
View File
@@ -0,0 +1,78 @@
using Vintagestory.API.Common;
using Vintagestory.API.MathTools;
using Vintagestory.API.Server;
namespace ClaimLink;
internal static class ClaimLinkBufferProtection
{
internal static void Register(ICoreServerAPI api)
{
api.Event.CanPlaceOrBreakBlock += OnCanPlaceOrBreakBlock;
api.Event.CanUseBlock += OnCanUseBlock;
}
private static bool OnCanUseBlock(IServerPlayer byPlayer, BlockSelection blockSel) =>
IsAllowed(byPlayer, blockSel.Position, EnumBlockAccessFlags.Use);
private static bool OnCanPlaceOrBreakBlock(
IServerPlayer byPlayer,
BlockSelection blockSel,
out string claimant
)
{
bool allowed = IsAllowed(byPlayer, blockSel.Position, EnumBlockAccessFlags.BuildOrBreak);
claimant = allowed ? null! : "custommessage-claimlink-buffer-zone";
return allowed;
}
private static bool IsAllowed(
IServerPlayer byPlayer,
BlockPos pos,
EnumBlockAccessFlags accessFlag
)
{
bool allowed = true;
foreach (int groupId in ClaimLinkBufferIndex.CandidateGroupIds(pos))
{
if (IsMember(byPlayer, groupId))
continue;
foreach (Cuboidi buffer in ClaimLinkBufferIndex.CachedBuffer(groupId))
if (buffer.Contains(pos))
allowed = false;
}
return allowed || HasClaimAccess(byPlayer, pos, accessFlag);
}
private static bool IsMember(IServerPlayer player, int groupId)
{
foreach (PlayerGroupMembership membership in player.Groups)
if (membership.GroupUid == groupId)
return true;
return false;
}
private static bool HasClaimAccess(
IServerPlayer player,
BlockPos pos,
EnumBlockAccessFlags accessFlag
)
{
LandClaim[]? claims = ClaimLinkModSystem.LandClaimAPI.Get(pos);
if (claims == null)
return false;
foreach (LandClaim claim in claims)
{
if (claim.TestPlayerAccess(player, accessFlag) != EnumPlayerAccessResult.Denied)
return true;
if (accessFlag == EnumBlockAccessFlags.Use && claim.AllowUseEveryone)
return true;
}
return false;
}
}
+5
View File
@@ -12,6 +12,7 @@ public class ClaimLinkModSystem : ModSystem
internal static ClaimLinkRegistry Registry = null!; internal static ClaimLinkRegistry Registry = null!;
internal static IGroupManager Groups = null!; internal static IGroupManager Groups = null!;
internal static IWorldAccessor World = null!; internal static IWorldAccessor World = null!;
internal static IWorldManagerAPI WorldManager = null!;
internal static IPlayerDataManager PlayerData = null!; internal static IPlayerDataManager PlayerData = null!;
internal static ClaimLinkConfig Config = null!; internal static ClaimLinkConfig Config = null!;
internal CommandListener? CmdListener; internal CommandListener? CmdListener;
@@ -23,12 +24,15 @@ public class ClaimLinkModSystem : ModSystem
{ {
LandClaimAPI = api.World.Claims; LandClaimAPI = api.World.Claims;
World = api.World; World = api.World;
WorldManager = api.WorldManager;
Groups = api.Groups; Groups = api.Groups;
Logger = api.Logger; Logger = api.Logger;
PlayerData = api.PlayerData; PlayerData = api.PlayerData;
Config = api.LoadModConfig<ClaimLinkConfig>("claimlink.json") ?? new ClaimLinkConfig(); Config = api.LoadModConfig<ClaimLinkConfig>("claimlink.json") ?? new ClaimLinkConfig();
api.StoreModConfig(Config, "claimlink.json"); api.StoreModConfig(Config, "claimlink.json");
Registry = new ClaimLinkRegistry(api.WorldManager.SaveGame); Registry = new ClaimLinkRegistry(api.WorldManager.SaveGame);
ClaimLinkBufferIndex.Rebuild();
api.Event.SaveGameLoaded += ClaimLinkBufferIndex.Rebuild;
CmdListener = new CommandListener(); CmdListener = new CommandListener();
CommandHookModSystem.Register(CmdListener); CommandHookModSystem.Register(CmdListener);
@@ -36,6 +40,7 @@ public class ClaimLinkModSystem : ModSystem
CommandHookModSystem.Register(GroupCmdListener); CommandHookModSystem.Register(GroupCmdListener);
ClaimLinkChatCommand.Register(api); ClaimLinkChatCommand.Register(api);
ClaimLinkVisualizerNetwork.Start(api); ClaimLinkVisualizerNetwork.Start(api);
ClaimLinkBufferProtection.Register(api);
api.Event.PlayerDisconnect += CommandListener.OnPlayerDisconnect; api.Event.PlayerDisconnect += CommandListener.OnPlayerDisconnect;
api.Event.PlayerDisconnect += GroupCommandListener.OnPlayerDisconnect; api.Event.PlayerDisconnect += GroupCommandListener.OnPlayerDisconnect;
+2
View File
@@ -197,5 +197,7 @@ public class ClaimLinkRegistry
SaveKey, SaveKey,
new ClaimLinkSaveData { Links = claimLinks, Entries = entryList } new ClaimLinkSaveData { Links = claimLinks, Entries = entryList }
); );
ClaimLinkBufferIndex.Rebuild();
} }
} }
+3
View File
@@ -113,7 +113,10 @@ public class CommandListener : ICommandHookListener
); );
if (!violatesClaimLink && !violatesClaim) if (!violatesClaimLink && !violatesClaim)
{
ClaimLinkBufferIndex.Rebuild();
return; return;
}
ClaimLinkModSystem.LandClaimAPI.Remove(savedClaim); ClaimLinkModSystem.LandClaimAPI.Remove(savedClaim);
+4
View File
@@ -0,0 +1,4 @@
{
"custommessage-claimlink-buffer-zone": "This area is a claim link buffer zone.",
"ingameerror-noprivilege-buildbreak-claimlink-buffer-zone": "This area is a claim link buffer zone."
}
+1 -1
View File
@@ -8,7 +8,7 @@
"anth64" "anth64"
], ],
"description": "Link claims together with groups.", "description": "Link claims together with groups.",
"version": "0.3.0", "version": "0.4.0",
"dependencies": { "dependencies": {
"game": "1.22.5", "game": "1.22.5",
"commandhook": "2.1.0" "commandhook": "2.1.0"