Compare commits
4
Commits
8f0e4bd687
...
05ff998287
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05ff998287 | ||
|
|
cac8e590d3 | ||
|
|
62c94bc775 | ||
|
|
b14eb5c538 |
@@ -1,25 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Vintagestory.API.MathTools;
|
||||
|
||||
namespace ClaimLink;
|
||||
|
||||
internal static class ClaimLinkBufferIndex
|
||||
internal class ClaimLinkBufferIndex
|
||||
{
|
||||
private static readonly Dictionary<long, HashSet<int>> groupIdsByRegion = new();
|
||||
private static readonly Dictionary<int, List<Cuboidi>> buffersByGroupId = new();
|
||||
private readonly Func<int, int, long> regionIndex;
|
||||
private readonly Dictionary<long, HashSet<int>> groupIdsByRegion = new();
|
||||
private readonly Dictionary<int, List<Cuboidi>> buffersByGroupId = new();
|
||||
private int regionSize;
|
||||
|
||||
internal static void Rebuild()
|
||||
internal ClaimLinkBufferIndex(Func<int, int, long> regionIndex)
|
||||
{
|
||||
this.regionIndex = regionIndex;
|
||||
}
|
||||
|
||||
internal void Rebuild(IEnumerable<(int groupId, List<Cuboidi> buffers)> source, int regionSize)
|
||||
{
|
||||
this.regionSize = regionSize;
|
||||
groupIdsByRegion.Clear();
|
||||
buffersByGroupId.Clear();
|
||||
|
||||
int regionSize = ClaimLinkModSystem.WorldManager.RegionSize;
|
||||
if (regionSize <= 0)
|
||||
return;
|
||||
|
||||
foreach (int groupId in ClaimLinkModSystem.Registry.All)
|
||||
foreach ((int groupId, List<Cuboidi> buffers) in source)
|
||||
{
|
||||
List<Cuboidi> buffers = ClaimLinkBuffer.GetBuffer(groupId);
|
||||
buffersByGroupId[groupId] = buffers;
|
||||
|
||||
HashSet<long> regionKeys = new();
|
||||
@@ -32,7 +39,7 @@ internal static class ClaimLinkBufferIndex
|
||||
|
||||
for (int rx = minRegionX; rx <= maxRegionX; rx++)
|
||||
for (int rz = minRegionZ; rz <= maxRegionZ; rz++)
|
||||
regionKeys.Add(ClaimLinkModSystem.WorldManager.MapRegionIndex2D(rx, rz));
|
||||
regionKeys.Add(regionIndex(rx, rz));
|
||||
}
|
||||
|
||||
foreach (long regionKey in regionKeys)
|
||||
@@ -44,23 +51,19 @@ internal static class ClaimLinkBufferIndex
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEnumerable<int> CandidateGroupIds(BlockPos pos)
|
||||
internal IEnumerable<int> CandidateGroupIds(BlockPos pos)
|
||||
{
|
||||
int regionSize = ClaimLinkModSystem.WorldManager.RegionSize;
|
||||
if (regionSize <= 0)
|
||||
return System.Array.Empty<int>();
|
||||
return Array.Empty<int>();
|
||||
|
||||
long regionKey = ClaimLinkModSystem.WorldManager.MapRegionIndex2D(
|
||||
pos.X / regionSize,
|
||||
pos.Z / regionSize
|
||||
);
|
||||
long regionKey = regionIndex(pos.X / regionSize, pos.Z / regionSize);
|
||||
|
||||
return groupIdsByRegion.TryGetValue(regionKey, out HashSet<int>? groupIds)
|
||||
? groupIds
|
||||
: System.Array.Empty<int>();
|
||||
: Array.Empty<int>();
|
||||
}
|
||||
|
||||
internal static List<Cuboidi> CachedBuffer(int groupId) =>
|
||||
internal List<Cuboidi> CachedBuffer(int groupId) =>
|
||||
buffersByGroupId.TryGetValue(groupId, out List<Cuboidi>? buffers)
|
||||
? buffers
|
||||
: new List<Cuboidi>();
|
||||
|
||||
@@ -34,12 +34,12 @@ internal static class ClaimLinkBufferProtection
|
||||
{
|
||||
bool allowed = true;
|
||||
|
||||
foreach (int groupId in ClaimLinkBufferIndex.CandidateGroupIds(pos))
|
||||
foreach (int groupId in ClaimLinkModSystem.BufferIndex.CandidateGroupIds(pos))
|
||||
{
|
||||
if (IsMember(byPlayer, groupId))
|
||||
continue;
|
||||
|
||||
foreach (Cuboidi buffer in ClaimLinkBufferIndex.CachedBuffer(groupId))
|
||||
foreach (Cuboidi buffer in ClaimLinkModSystem.BufferIndex.CachedBuffer(groupId))
|
||||
if (
|
||||
buffer.Contains(pos)
|
||||
&& !ClaimLinkModSystem.Registry.HasPermission(groupId, byPlayer, accessFlag)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using CommandHook;
|
||||
using Vintagestory.API.Client;
|
||||
using Vintagestory.API.Common;
|
||||
using Vintagestory.API.Server;
|
||||
|
||||
@@ -15,10 +15,9 @@ public class ClaimLinkModSystem : ModSystem
|
||||
internal static IWorldManagerAPI WorldManager = null!;
|
||||
internal static IPlayerDataManager PlayerData = null!;
|
||||
internal static ClaimLinkConfig Config = null!;
|
||||
internal CommandListener? CmdListener;
|
||||
internal GroupCommandListener? GroupCmdListener;
|
||||
|
||||
public override bool ShouldLoad(EnumAppSide forSide) => forSide == EnumAppSide.Server;
|
||||
internal static ClaimLinkBufferIndex BufferIndex = null!;
|
||||
private object? cmdListenerProxy;
|
||||
private object? groupCmdListenerProxy;
|
||||
|
||||
public override void StartServerSide(ICoreServerAPI api)
|
||||
{
|
||||
@@ -31,28 +30,79 @@ public class ClaimLinkModSystem : ModSystem
|
||||
Config = api.LoadModConfig<ClaimLinkConfig>("claimlink.json") ?? new ClaimLinkConfig();
|
||||
api.StoreModConfig(Config, "claimlink.json");
|
||||
Registry = new ClaimLinkRegistry(api.WorldManager.SaveGame);
|
||||
ClaimLinkBufferIndex.Rebuild();
|
||||
api.Event.SaveGameLoaded += ClaimLinkBufferIndex.Rebuild;
|
||||
BufferIndex = new ClaimLinkBufferIndex(WorldManager.MapRegionIndex2D);
|
||||
RebuildBufferIndex();
|
||||
api.Event.SaveGameLoaded += RebuildBufferIndex;
|
||||
|
||||
if (api.ModLoader.IsModEnabled("commandhook"))
|
||||
{
|
||||
api.Event.SaveGameLoaded += () => RegisterCommandHooks(api);
|
||||
}
|
||||
else
|
||||
{
|
||||
api.Logger.Fatal(
|
||||
"ClaimLink requires the CommandHook mod on the server for /land and /group integration. That integration is disabled; /claimlink commands and buffer protection still work."
|
||||
);
|
||||
}
|
||||
|
||||
CmdListener = new CommandListener();
|
||||
CommandHookModSystem.Register(CmdListener);
|
||||
GroupCmdListener = new GroupCommandListener();
|
||||
CommandHookModSystem.Register(GroupCmdListener);
|
||||
ClaimLinkChatCommand.Register(api);
|
||||
ClaimLinkVisualizerNetwork.Start(api);
|
||||
ClaimLinkBufferProtection.Register(api);
|
||||
|
||||
api.Event.PlayerDisconnect += CommandListener.OnPlayerDisconnect;
|
||||
api.Event.PlayerDisconnect += GroupCommandListener.OnPlayerDisconnect;
|
||||
api.Event.PlayerDisconnect += ClaimLinkChatCommand.OnPlayerDisconnect;
|
||||
}
|
||||
|
||||
private void RegisterCommandHooks(ICoreServerAPI api)
|
||||
{
|
||||
CommandListener cmdListener = new();
|
||||
CommandHook.ICommandHookListener cmdProxy = CommandHookListenerProxy.Create(
|
||||
cmdListener.ModId,
|
||||
cmdListener.Commands,
|
||||
cmdListener.Before,
|
||||
cmdListener.After
|
||||
);
|
||||
CommandHook.CommandHookModSystem.Register(cmdProxy);
|
||||
cmdListenerProxy = cmdProxy;
|
||||
|
||||
GroupCommandListener groupListener = new();
|
||||
CommandHook.ICommandHookListener groupProxy = CommandHookListenerProxy.Create(
|
||||
groupListener.ModId,
|
||||
groupListener.Commands,
|
||||
groupListener.Before,
|
||||
groupListener.After
|
||||
);
|
||||
CommandHook.CommandHookModSystem.Register(groupProxy);
|
||||
groupCmdListenerProxy = groupProxy;
|
||||
|
||||
api.Event.PlayerDisconnect += CommandListener.OnPlayerDisconnect;
|
||||
api.Event.PlayerDisconnect += GroupCommandListener.OnPlayerDisconnect;
|
||||
}
|
||||
|
||||
internal static void RebuildBufferIndex()
|
||||
{
|
||||
List<(int groupId, List<Vintagestory.API.MathTools.Cuboidi> buffers)> source = new();
|
||||
foreach (int groupId in Registry.All)
|
||||
source.Add((groupId, ClaimLinkBuffer.GetBuffer(groupId)));
|
||||
BufferIndex.Rebuild(source, WorldManager.RegionSize);
|
||||
}
|
||||
|
||||
public override void StartClientSide(ICoreClientAPI api) => ClaimLinkVisualizerClient.Start(api);
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (CmdListener != null)
|
||||
CommandHookModSystem.Unregister(CmdListener);
|
||||
if (GroupCmdListener != null)
|
||||
CommandHookModSystem.Unregister(GroupCmdListener);
|
||||
if (cmdListenerProxy != null || groupCmdListenerProxy != null)
|
||||
UnregisterCommandHooks();
|
||||
}
|
||||
|
||||
private void UnregisterCommandHooks()
|
||||
{
|
||||
if (cmdListenerProxy != null)
|
||||
CommandHook.CommandHookModSystem.Unregister(
|
||||
(CommandHook.ICommandHookListener)cmdListenerProxy
|
||||
);
|
||||
if (groupCmdListenerProxy != null)
|
||||
CommandHook.CommandHookModSystem.Unregister(
|
||||
(CommandHook.ICommandHookListener)groupCmdListenerProxy
|
||||
);
|
||||
}
|
||||
|
||||
private static IEnumerable<(int claimIndex, LandClaim claim)> EnumerateOwnedClaims(
|
||||
|
||||
@@ -58,7 +58,7 @@ public class ClaimLinkRegistry
|
||||
public void Add(int groupId)
|
||||
{
|
||||
claimLinks.Add(groupId);
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
}
|
||||
|
||||
public void Remove(int groupId)
|
||||
@@ -69,7 +69,7 @@ public class ClaimLinkRegistry
|
||||
permittedPlayerUids.Remove(groupId);
|
||||
permittedPlayerGroupIds.Remove(groupId);
|
||||
Prune();
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
}
|
||||
|
||||
public void GrantPlayer(int groupId, string uid, EnumBlockAccessFlags flags)
|
||||
@@ -77,7 +77,7 @@ public class ClaimLinkRegistry
|
||||
if (!permittedPlayerUids.TryGetValue(groupId, out var uids))
|
||||
permittedPlayerUids[groupId] = uids = new();
|
||||
uids[uid] = flags;
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
}
|
||||
|
||||
public bool RevokePlayer(int groupId, string uid)
|
||||
@@ -87,7 +87,7 @@ public class ClaimLinkRegistry
|
||||
return false;
|
||||
if (uids!.Count == 0)
|
||||
permittedPlayerUids.Remove(groupId);
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class ClaimLinkRegistry
|
||||
if (!permittedPlayerGroupIds.TryGetValue(groupId, out var groups))
|
||||
permittedPlayerGroupIds[groupId] = groups = new();
|
||||
groups[targetGroupId] = flags;
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
}
|
||||
|
||||
public bool RevokeGroup(int groupId, int targetGroupId)
|
||||
@@ -108,7 +108,7 @@ public class ClaimLinkRegistry
|
||||
return false;
|
||||
if (groups!.Count == 0)
|
||||
permittedPlayerGroupIds.Remove(groupId);
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -138,12 +138,12 @@ public class ClaimLinkRegistry
|
||||
|
||||
public void RemoveGroupFromAllPermissions(int targetGroupId)
|
||||
{
|
||||
bool changed = false;
|
||||
foreach (var groups in permittedPlayerGroupIds.Values)
|
||||
List<int>? changedGroupIds = null;
|
||||
foreach (var (groupId, groups) in permittedPlayerGroupIds)
|
||||
if (groups.Remove(targetGroupId))
|
||||
changed = true;
|
||||
(changedGroupIds ??= new()).Add(groupId);
|
||||
|
||||
if (!changed)
|
||||
if (changedGroupIds == null)
|
||||
return;
|
||||
|
||||
List<int>? emptyGroupIds = null;
|
||||
@@ -154,7 +154,7 @@ public class ClaimLinkRegistry
|
||||
foreach (int groupId in emptyGroupIds)
|
||||
permittedPlayerGroupIds.Remove(groupId);
|
||||
|
||||
Save();
|
||||
Save(changedGroupIds);
|
||||
}
|
||||
|
||||
public bool IsClaimLinked(string uid, int claimIndex) =>
|
||||
@@ -181,7 +181,7 @@ public class ClaimLinkRegistry
|
||||
if (!groups.TryGetValue(groupId, out var claims))
|
||||
groups[groupId] = claims = new();
|
||||
claims.Add(claimIndex);
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ public class ClaimLinkRegistry
|
||||
if (entries.TryGetValue(uid, out var groups) && groups.TryGetValue(groupId, out var claims))
|
||||
claims.Remove(claimIndex);
|
||||
Prune();
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
}
|
||||
|
||||
public void ClaimRemoved(string uid, int removedIndex)
|
||||
@@ -198,6 +198,8 @@ public class ClaimLinkRegistry
|
||||
if (!entries.TryGetValue(uid, out var groups))
|
||||
return;
|
||||
|
||||
int[] affectedGroupIds = new List<int>(groups.Keys).ToArray();
|
||||
|
||||
foreach (var claims in groups.Values)
|
||||
{
|
||||
claims.Remove(removedIndex);
|
||||
@@ -207,7 +209,7 @@ public class ClaimLinkRegistry
|
||||
}
|
||||
|
||||
Prune();
|
||||
Save();
|
||||
Save(affectedGroupIds);
|
||||
}
|
||||
|
||||
public void ClaimSaved(string uid, int claimIndex)
|
||||
@@ -215,6 +217,8 @@ public class ClaimLinkRegistry
|
||||
if (!entries.TryGetValue(uid, out var groups))
|
||||
return;
|
||||
|
||||
int[] affectedGroupIds = new List<int>(groups.Keys).ToArray();
|
||||
|
||||
int count = 0;
|
||||
foreach (Vintagestory.API.Common.LandClaim c in ClaimLinkModSystem.LandClaimAPI.All)
|
||||
if (c.OwnedByPlayerUid == uid)
|
||||
@@ -233,7 +237,7 @@ public class ClaimLinkRegistry
|
||||
claims[i] = newIndex;
|
||||
}
|
||||
|
||||
Save();
|
||||
Save(affectedGroupIds);
|
||||
}
|
||||
|
||||
public bool HasAnyEntry(string uid, int groupId) =>
|
||||
@@ -246,13 +250,17 @@ public class ClaimLinkRegistry
|
||||
if (entries.TryGetValue(uid, out var groups))
|
||||
groups.Remove(groupId);
|
||||
Prune();
|
||||
Save();
|
||||
Save(new[] { groupId });
|
||||
}
|
||||
|
||||
public void ClearAllForPlayer(string uid)
|
||||
{
|
||||
int[] affectedGroupIds = entries.TryGetValue(uid, out var groups)
|
||||
? new List<int>(groups.Keys).ToArray()
|
||||
: System.Array.Empty<int>();
|
||||
|
||||
entries.Remove(uid);
|
||||
Save();
|
||||
Save(affectedGroupIds);
|
||||
}
|
||||
|
||||
public IEnumerable<string> MemberUidsForGroup(int groupId)
|
||||
@@ -286,7 +294,7 @@ public class ClaimLinkRegistry
|
||||
entries.Remove(uid);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
private void Save(IReadOnlyCollection<int> affectedGroupIds)
|
||||
{
|
||||
var entryList = new List<ClaimLinkEntrySaveData>();
|
||||
foreach (var (uid, groups) in entries)
|
||||
@@ -331,6 +339,7 @@ public class ClaimLinkRegistry
|
||||
}
|
||||
);
|
||||
|
||||
ClaimLinkBufferIndex.Rebuild();
|
||||
ClaimLinkModSystem.RebuildBufferIndex();
|
||||
ClaimLinkVisualizerNetwork.BroadcastDelta(affectedGroupIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
using System.Collections.Generic;
|
||||
using Vintagestory.API.Client;
|
||||
using Vintagestory.API.Common;
|
||||
using Vintagestory.API.MathTools;
|
||||
|
||||
namespace ClaimLink;
|
||||
|
||||
internal static class ClaimLinkVisualizerClient
|
||||
{
|
||||
private const int HighlightSlotId = 8341;
|
||||
private const int ClientRegionSize = 512;
|
||||
|
||||
private static ICoreClientAPI capi = null!;
|
||||
private static bool visible;
|
||||
private static ClaimLinkVisualizerConfig config = null!;
|
||||
private static IClientNetworkChannel channel = null!;
|
||||
private static readonly Dictionary<int, ClaimLinkBufferEntryDto> knownEntries = new();
|
||||
private static readonly ClaimLinkBufferIndex bufferIndex = new(ClientRegionKey);
|
||||
|
||||
internal static void Start(ICoreClientAPI api)
|
||||
{
|
||||
capi = api;
|
||||
config = api.LoadModConfig<ClaimLinkVisualizerConfig>("claimlinkvisualizer.json") ?? new ClaimLinkVisualizerConfig();
|
||||
api.StoreModConfig(config, "claimlinkvisualizer.json");
|
||||
|
||||
channel = api.Network.RegisterChannel(ClaimLinkVisualizerNetwork.ChannelName)
|
||||
.RegisterMessageType<ClaimLinkBufferResponse>()
|
||||
.SetMessageHandler<ClaimLinkBufferResponse>(OnBuffersPushed);
|
||||
|
||||
api.Input.RegisterHotKey(
|
||||
"claimlinkvisualizertoggle",
|
||||
"Toggle ClaimLink buffer visualization",
|
||||
GlKeys.R,
|
||||
HotkeyType.CharacterControls,
|
||||
ctrlPressed: true
|
||||
);
|
||||
api.Input.SetHotKeyHandler("claimlinkvisualizertoggle", OnToggle);
|
||||
|
||||
api.Event.TestBlockAccess += OnTestBlockAccess;
|
||||
}
|
||||
|
||||
private static long ClientRegionKey(int regionX, int regionZ) => ((long)regionX << 32) | (uint)regionZ;
|
||||
|
||||
private static bool OnToggle(KeyCombination comb)
|
||||
{
|
||||
visible = !visible;
|
||||
if (visible)
|
||||
RenderHighlights();
|
||||
else
|
||||
Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void OnBuffersPushed(ClaimLinkBufferResponse response)
|
||||
{
|
||||
if (response.IsFullSync)
|
||||
knownEntries.Clear();
|
||||
|
||||
foreach (ClaimLinkBufferEntryDto entry in response.Entries)
|
||||
{
|
||||
if (entry.Cuboids.Count == 0)
|
||||
knownEntries.Remove(entry.GroupId);
|
||||
else
|
||||
knownEntries[entry.GroupId] = entry;
|
||||
}
|
||||
|
||||
RebuildIndex();
|
||||
|
||||
if (visible)
|
||||
RenderHighlights();
|
||||
}
|
||||
|
||||
private static void RebuildIndex()
|
||||
{
|
||||
List<(int groupId, List<Cuboidi> buffers)> source = new();
|
||||
|
||||
foreach ((int groupId, ClaimLinkBufferEntryDto entry) in knownEntries)
|
||||
{
|
||||
List<Cuboidi> cuboids = new();
|
||||
foreach (ClaimLinkBufferCuboidDto c in entry.Cuboids)
|
||||
cuboids.Add(new Cuboidi(c.MinX, c.MinY, c.MinZ, c.MaxX, c.MaxY, c.MaxZ));
|
||||
source.Add((groupId, cuboids));
|
||||
}
|
||||
|
||||
bufferIndex.Rebuild(source, ClientRegionSize);
|
||||
}
|
||||
|
||||
private static EnumWorldAccessResponse OnTestBlockAccess(
|
||||
IPlayer player,
|
||||
BlockSelection blockSel,
|
||||
EnumBlockAccessFlags accessType,
|
||||
ref string claimant,
|
||||
EnumWorldAccessResponse response
|
||||
)
|
||||
{
|
||||
if (response != EnumWorldAccessResponse.Granted)
|
||||
return response;
|
||||
|
||||
BlockPos pos = blockSel.Position;
|
||||
|
||||
foreach (int groupId in bufferIndex.CandidateGroupIds(pos))
|
||||
{
|
||||
if (IsMember(groupId))
|
||||
continue;
|
||||
|
||||
bool inBuffer = false;
|
||||
foreach (Cuboidi buffer in bufferIndex.CachedBuffer(groupId))
|
||||
if (buffer.Contains(pos))
|
||||
{
|
||||
inBuffer = true;
|
||||
break;
|
||||
}
|
||||
if (!inBuffer)
|
||||
continue;
|
||||
|
||||
EnumBlockAccessFlags granted = knownEntries.TryGetValue(groupId, out var entry)
|
||||
? (EnumBlockAccessFlags)entry.GrantedAccess
|
||||
: EnumBlockAccessFlags.None;
|
||||
if ((granted & accessType) != 0)
|
||||
continue;
|
||||
|
||||
if (HasClaimAccess(pos, accessType))
|
||||
continue;
|
||||
|
||||
claimant = "custommessage-claimlink-buffer-zone";
|
||||
return EnumWorldAccessResponse.DeniedByMod;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static bool IsMember(int groupId)
|
||||
{
|
||||
foreach (PlayerGroupMembership membership in capi.World.Player.Groups)
|
||||
if (membership.GroupUid == groupId)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasClaimAccess(BlockPos pos, EnumBlockAccessFlags accessFlag)
|
||||
{
|
||||
LandClaim[]? claims = capi.World.Claims.Get(pos);
|
||||
if (claims == null)
|
||||
return false;
|
||||
|
||||
foreach (LandClaim claim in claims)
|
||||
{
|
||||
if (claim.TestPlayerAccess(capi.World.Player, accessFlag) != EnumPlayerAccessResult.Denied)
|
||||
return true;
|
||||
if (accessFlag == EnumBlockAccessFlags.Use && claim.AllowUseEveryone)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void RenderHighlights()
|
||||
{
|
||||
List<BlockPos> positions = new();
|
||||
List<int> colors = new();
|
||||
|
||||
foreach ((int groupId, ClaimLinkBufferEntryDto entry) in knownEntries)
|
||||
{
|
||||
int color = HashToColor(groupId);
|
||||
foreach (ClaimLinkBufferCuboidDto cuboid in entry.Cuboids)
|
||||
{
|
||||
positions.Add(new BlockPos(cuboid.MinX, cuboid.MinY, cuboid.MinZ));
|
||||
positions.Add(new BlockPos(cuboid.MaxX, cuboid.MaxY, cuboid.MaxZ));
|
||||
colors.Add(color);
|
||||
}
|
||||
}
|
||||
|
||||
capi.World.HighlightBlocks(
|
||||
capi.World.Player,
|
||||
HighlightSlotId,
|
||||
positions,
|
||||
colors,
|
||||
EnumHighlightBlocksMode.Absolute,
|
||||
EnumHighlightShape.Cubes
|
||||
);
|
||||
}
|
||||
|
||||
private static void Clear() =>
|
||||
capi.World.HighlightBlocks(
|
||||
capi.World.Player,
|
||||
HighlightSlotId,
|
||||
new List<BlockPos>(),
|
||||
new List<int>()
|
||||
);
|
||||
|
||||
private static int HashToColor(int groupId)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint h = (uint)groupId * config.ColorHashSeed;
|
||||
int r = (byte)(h >> 16);
|
||||
int g = (byte)(h >> 8);
|
||||
int b = (byte)h;
|
||||
return ColorUtil.ColorFromRgba(r, g, b, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace ClaimLink;
|
||||
|
||||
internal class ClaimLinkVisualizerConfig
|
||||
{
|
||||
public uint ColorHashSeed = 2654435761u;
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using ProtoBuf;
|
||||
using Vintagestory.API.Common;
|
||||
using Vintagestory.API.MathTools;
|
||||
using Vintagestory.API.Server;
|
||||
|
||||
namespace ClaimLink;
|
||||
|
||||
[ProtoContract]
|
||||
public class ClaimLinkBufferRequest { }
|
||||
|
||||
[ProtoContract]
|
||||
public class ClaimLinkBufferCuboidDto
|
||||
{
|
||||
@@ -38,6 +36,9 @@ public class ClaimLinkBufferEntryDto
|
||||
|
||||
[ProtoMember(2)]
|
||||
public List<ClaimLinkBufferCuboidDto> Cuboids = new();
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int GrantedAccess;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
@@ -45,37 +46,67 @@ public class ClaimLinkBufferResponse
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public List<ClaimLinkBufferEntryDto> Entries = new();
|
||||
|
||||
[ProtoMember(2)]
|
||||
public bool IsFullSync;
|
||||
}
|
||||
|
||||
internal static class ClaimLinkVisualizerNetwork
|
||||
{
|
||||
internal const string ChannelName = "claimlinkvisualizer";
|
||||
private static IServerNetworkChannel channel = null!;
|
||||
|
||||
internal static void Start(ICoreServerAPI api)
|
||||
{
|
||||
api.Network.RegisterChannel(ChannelName)
|
||||
.RegisterMessageType<ClaimLinkBufferRequest>()
|
||||
.RegisterMessageType<ClaimLinkBufferResponse>()
|
||||
.SetMessageHandler<ClaimLinkBufferRequest>(
|
||||
(fromPlayer, _) => SendBuffers(api, fromPlayer)
|
||||
);
|
||||
channel = api.Network.RegisterChannel(ChannelName).RegisterMessageType<ClaimLinkBufferResponse>();
|
||||
|
||||
api.Event.PlayerNowPlaying += SendFullSync;
|
||||
}
|
||||
|
||||
private static void SendBuffers(ICoreServerAPI api, IServerPlayer toPlayer)
|
||||
private static void SendFullSync(IServerPlayer toPlayer)
|
||||
{
|
||||
ClaimLinkBufferResponse response = new();
|
||||
|
||||
BlockPos playerPos = toPlayer.Entity.Pos.AsBlockPos;
|
||||
int radius = toPlayer.WorldData.LastApprovedViewDistance;
|
||||
ClaimLinkBufferResponse response = new() { IsFullSync = true };
|
||||
|
||||
foreach (int groupId in ClaimLinkModSystem.Registry.All)
|
||||
{
|
||||
ClaimLinkBufferEntryDto entry = new() { GroupId = groupId };
|
||||
foreach (Cuboidi cuboid in ClaimLinkBuffer.GetBuffer(groupId))
|
||||
ClaimLinkBufferEntryDto entry = BuildEntry(groupId, toPlayer);
|
||||
if (entry.Cuboids.Count > 0)
|
||||
response.Entries.Add(entry);
|
||||
}
|
||||
|
||||
channel.SendPacket(response, toPlayer);
|
||||
}
|
||||
|
||||
internal static void BroadcastDelta(IReadOnlyCollection<int> affectedGroupIds)
|
||||
{
|
||||
if (!IsNearby(cuboid, playerPos, radius))
|
||||
if (affectedGroupIds.Count == 0 || channel == null)
|
||||
return;
|
||||
|
||||
foreach (IPlayer onlinePlayer in ClaimLinkModSystem.World.AllOnlinePlayers)
|
||||
{
|
||||
if (onlinePlayer is not IServerPlayer serverPlayer)
|
||||
continue;
|
||||
|
||||
ClaimLinkBufferResponse response = new() { IsFullSync = false };
|
||||
foreach (int groupId in affectedGroupIds)
|
||||
response.Entries.Add(BuildEntry(groupId, serverPlayer));
|
||||
|
||||
channel.SendPacket(response, serverPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
private static ClaimLinkBufferEntryDto BuildEntry(int groupId, IServerPlayer forPlayer)
|
||||
{
|
||||
EnumBlockAccessFlags granted = EnumBlockAccessFlags.None;
|
||||
if (ClaimLinkModSystem.Registry.HasPermission(groupId, forPlayer, EnumBlockAccessFlags.BuildOrBreak))
|
||||
granted |= EnumBlockAccessFlags.BuildOrBreak;
|
||||
if (ClaimLinkModSystem.Registry.HasPermission(groupId, forPlayer, EnumBlockAccessFlags.Use))
|
||||
granted |= EnumBlockAccessFlags.Use;
|
||||
|
||||
ClaimLinkBufferEntryDto entry = new() { GroupId = groupId, GrantedAccess = (int)granted };
|
||||
|
||||
foreach (Cuboidi cuboid in ClaimLinkBuffer.GetBuffer(groupId))
|
||||
{
|
||||
entry.Cuboids.Add(
|
||||
new ClaimLinkBufferCuboidDto
|
||||
{
|
||||
@@ -89,18 +120,6 @@ internal static class ClaimLinkVisualizerNetwork
|
||||
);
|
||||
}
|
||||
|
||||
if (entry.Cuboids.Count > 0)
|
||||
response.Entries.Add(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
api.Network.GetChannel(ChannelName).SendPacket(response, toPlayer);
|
||||
}
|
||||
|
||||
private static bool IsNearby(Cuboidi cuboid, BlockPos playerPos, int radius) =>
|
||||
cuboid.MinX <= playerPos.X + radius
|
||||
&& cuboid.MaxX >= playerPos.X - radius
|
||||
&& cuboid.MinY <= playerPos.Y + radius
|
||||
&& cuboid.MaxY >= playerPos.Y - radius
|
||||
&& cuboid.MinZ <= playerPos.Z + radius
|
||||
&& cuboid.MaxZ >= playerPos.Z - radius;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Vintagestory.API.Common;
|
||||
|
||||
namespace ClaimLink;
|
||||
|
||||
internal delegate TextCommandResult? HookBeforeDelegate(TextCommandCallingArgs args);
|
||||
|
||||
internal delegate void HookAfterDelegate(TextCommandCallingArgs args, TextCommandResult result);
|
||||
|
||||
internal class CommandHookListenerProxy : DispatchProxy
|
||||
{
|
||||
private string modId = null!;
|
||||
private IReadOnlyList<string> commands = null!;
|
||||
private HookBeforeDelegate? before;
|
||||
private HookAfterDelegate? after;
|
||||
|
||||
internal static CommandHook.ICommandHookListener Create(
|
||||
string modId,
|
||||
IReadOnlyList<string> commands,
|
||||
HookBeforeDelegate? before,
|
||||
HookAfterDelegate? after
|
||||
)
|
||||
{
|
||||
object proxy = Create<CommandHook.ICommandHookListener, CommandHookListenerProxy>()!;
|
||||
CommandHookListenerProxy p = (CommandHookListenerProxy)proxy;
|
||||
p.modId = modId;
|
||||
p.commands = commands;
|
||||
p.before = before;
|
||||
p.after = after;
|
||||
return (CommandHook.ICommandHookListener)proxy;
|
||||
}
|
||||
|
||||
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args) =>
|
||||
targetMethod!.Name switch
|
||||
{
|
||||
"get_ModId" => modId,
|
||||
"get_Commands" => commands,
|
||||
"get_Registration" => new CommandHook.CommandRegistration(
|
||||
before == null ? null : new CommandHook.CommandBeforeDelegate(before),
|
||||
after == null ? null : new CommandHook.CommandAfterDelegate(after)
|
||||
),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using CommandHook;
|
||||
using Vintagestory.API.Common;
|
||||
using Vintagestory.API.Config;
|
||||
using Vintagestory.API.MathTools;
|
||||
@@ -7,14 +6,12 @@ using Vintagestory.API.Server;
|
||||
|
||||
namespace ClaimLink;
|
||||
|
||||
public class CommandListener : ICommandHookListener
|
||||
public class CommandListener
|
||||
{
|
||||
public string ModId => "claimlink";
|
||||
|
||||
public IReadOnlyList<string> Commands => new[] { "land" };
|
||||
|
||||
public CommandRegistration Registration => new(Before, After);
|
||||
|
||||
internal static Dictionary<string, int> PendingLoads = new Dictionary<string, int>();
|
||||
internal static Dictionary<string, List<Cuboidi>?> PendingSaveChecks =
|
||||
new Dictionary<string, List<Cuboidi>?>();
|
||||
@@ -57,13 +54,13 @@ public class CommandListener : ICommandHookListener
|
||||
}
|
||||
}
|
||||
|
||||
private TextCommandResult? Before(TextCommandCallingArgs args)
|
||||
internal TextCommandResult? Before(TextCommandCallingArgs args)
|
||||
{
|
||||
Dispatch(args);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void After(TextCommandCallingArgs args, TextCommandResult result)
|
||||
internal void After(TextCommandCallingArgs args, TextCommandResult result)
|
||||
{
|
||||
if (args.Caller.Player == null)
|
||||
return;
|
||||
@@ -114,7 +111,7 @@ public class CommandListener : ICommandHookListener
|
||||
|
||||
if (!violatesClaimLink && !violatesClaim)
|
||||
{
|
||||
ClaimLinkBufferIndex.Rebuild();
|
||||
ClaimLinkModSystem.RebuildBufferIndex();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -276,19 +273,17 @@ public class CommandListener : ICommandHookListener
|
||||
}
|
||||
}
|
||||
|
||||
public class GroupCommandListener : ICommandHookListener
|
||||
public class GroupCommandListener
|
||||
{
|
||||
public string ModId => "claimlink.group";
|
||||
|
||||
public IReadOnlyList<string> Commands => new[] { "group" };
|
||||
|
||||
public CommandRegistration Registration => new(Before, After);
|
||||
|
||||
internal static Dictionary<string, int> PendingDisbands = new Dictionary<string, int>();
|
||||
internal static Dictionary<string, (string AffectedUid, int GroupId)> PendingMembershipChecks =
|
||||
new Dictionary<string, (string, int)>();
|
||||
|
||||
private TextCommandResult? Before(TextCommandCallingArgs args)
|
||||
internal TextCommandResult? Before(TextCommandCallingArgs args)
|
||||
{
|
||||
if (args.Caller.Player == null)
|
||||
return null;
|
||||
@@ -370,7 +365,7 @@ public class GroupCommandListener : ICommandHookListener
|
||||
PendingMembershipChecks[callerUid] = (targetUid, (int)groupId);
|
||||
}
|
||||
|
||||
private void After(TextCommandCallingArgs args, TextCommandResult result)
|
||||
internal void After(TextCommandCallingArgs args, TextCommandResult result)
|
||||
{
|
||||
if (args.Caller.Player == null)
|
||||
return;
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
"type": "Code",
|
||||
"modid": "claimlink",
|
||||
"name": "ClaimLink",
|
||||
"side": "Server",
|
||||
"side": "Universal",
|
||||
"authors": [
|
||||
"anth64"
|
||||
],
|
||||
"description": "Link claims together with groups.",
|
||||
"version": "0.5.1",
|
||||
"version": "0.6.0",
|
||||
"dependencies": {
|
||||
"game": "1.22.5",
|
||||
"commandhook": "2.1.0"
|
||||
"game": "1.22.5"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user