Actively protect claim link buffer zones from non-members
Adds a region-indexed lookup (ClaimLinkBufferIndex), keyed via the game's own WorldManager.MapRegionIndex2D and rebuilt on every registry mutation and on SaveGameLoaded, feeding a block break/place/use gate (ClaimLinkBufferProtection) that denies players who aren't members of the relevant claim link from modifying buffer-zone land. Any existing vanilla claim access (owner, claim's group, explicit permission grant, or AllowUseEveryone) always overrides the buffer check. Bump to 0.4.0.
This commit is contained in:
@@ -34,4 +34,10 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="assets\**">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -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>();
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -197,5 +197,7 @@ public class ClaimLinkRegistry
|
|||||||
SaveKey,
|
SaveKey,
|
||||||
new ClaimLinkSaveData { Links = claimLinks, Entries = entryList }
|
new ClaimLinkSaveData { Links = claimLinks, Entries = entryList }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ClaimLinkBufferIndex.Rebuild();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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."
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user