Compare commits
2
Commits
c1158d50c3
...
a03219c114
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a03219c114 | ||
|
|
095b826178 |
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using ProtoBuf;
|
using ProtoBuf;
|
||||||
|
using Vintagestory.API.Common;
|
||||||
|
|
||||||
namespace ClaimLink;
|
namespace ClaimLink;
|
||||||
|
|
||||||
@@ -11,6 +12,22 @@ public class ClaimLinkSaveData
|
|||||||
|
|
||||||
[ProtoMember(2)]
|
[ProtoMember(2)]
|
||||||
public List<ClaimLinkEntrySaveData> Entries = new();
|
public List<ClaimLinkEntrySaveData> Entries = new();
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public List<ClaimLinkPermissionSaveData> Permissions = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ProtoContract]
|
||||||
|
public class ClaimLinkPermissionSaveData
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int GroupId;
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public Dictionary<string, EnumBlockAccessFlags> PermittedPlayerUids = new();
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public Dictionary<int, EnumBlockAccessFlags> PermittedPlayerGroupIds = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProtoContract]
|
[ProtoContract]
|
||||||
|
|||||||
@@ -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,81 @@
|
|||||||
|
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)
|
||||||
|
&& !ClaimLinkModSystem.Registry.HasPermission(groupId, byPlayer, accessFlag)
|
||||||
|
)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,6 +113,44 @@ public static class ClaimLinkChatCommand
|
|||||||
true,
|
true,
|
||||||
Kick
|
Kick
|
||||||
),
|
),
|
||||||
|
new(
|
||||||
|
new[] { "allow" },
|
||||||
|
"Grant a player access inside the claim link's buffer zone.",
|
||||||
|
new ICommandArgumentParser[]
|
||||||
|
{
|
||||||
|
p.Word("playername"),
|
||||||
|
p.WordRange("access", "use", "build", "all"),
|
||||||
|
p.OptionalWord("groupname"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
Allow
|
||||||
|
),
|
||||||
|
new(
|
||||||
|
new[] { "allowgroup" },
|
||||||
|
"Grant a player group access inside the claim link's buffer zone.",
|
||||||
|
new ICommandArgumentParser[]
|
||||||
|
{
|
||||||
|
p.Word("targetgroup"),
|
||||||
|
p.WordRange("access", "use", "build", "all"),
|
||||||
|
p.OptionalWord("groupname"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
AllowGroup
|
||||||
|
),
|
||||||
|
new(
|
||||||
|
new[] { "deny" },
|
||||||
|
"Revoke a player's buffer-zone access.",
|
||||||
|
new ICommandArgumentParser[] { p.Word("groupname"), p.OptionalWord("playername") },
|
||||||
|
true,
|
||||||
|
Deny
|
||||||
|
),
|
||||||
|
new(
|
||||||
|
new[] { "denygroup" },
|
||||||
|
"Revoke a player group's buffer-zone access.",
|
||||||
|
new ICommandArgumentParser[] { p.Word("groupname"), p.OptionalWord("targetgroup") },
|
||||||
|
true,
|
||||||
|
DenyGroup
|
||||||
|
),
|
||||||
new(
|
new(
|
||||||
new[] { "delete" },
|
new[] { "delete" },
|
||||||
"Delete a claim link.",
|
"Delete a claim link.",
|
||||||
@@ -184,6 +222,44 @@ public static class ClaimLinkChatCommand
|
|||||||
false,
|
false,
|
||||||
AdminTransferOwnership
|
AdminTransferOwnership
|
||||||
),
|
),
|
||||||
|
new(
|
||||||
|
new[] { "allow" },
|
||||||
|
"Grant a player access inside any claim link's buffer zone.",
|
||||||
|
new ICommandArgumentParser[]
|
||||||
|
{
|
||||||
|
p.Word("playername"),
|
||||||
|
p.WordRange("access", "use", "build", "all"),
|
||||||
|
p.OptionalWord("groupname"),
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
AdminAllow
|
||||||
|
),
|
||||||
|
new(
|
||||||
|
new[] { "allowgroup" },
|
||||||
|
"Grant a player group access inside any claim link's buffer zone.",
|
||||||
|
new ICommandArgumentParser[]
|
||||||
|
{
|
||||||
|
p.Word("targetgroup"),
|
||||||
|
p.WordRange("access", "use", "build", "all"),
|
||||||
|
p.OptionalWord("groupname"),
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
AdminAllowGroup
|
||||||
|
),
|
||||||
|
new(
|
||||||
|
new[] { "deny" },
|
||||||
|
"Revoke a player's buffer-zone access to any claim link.",
|
||||||
|
new ICommandArgumentParser[] { p.Word("groupname"), p.OptionalWord("playername") },
|
||||||
|
false,
|
||||||
|
AdminDeny
|
||||||
|
),
|
||||||
|
new(
|
||||||
|
new[] { "denygroup" },
|
||||||
|
"Revoke a player group's buffer-zone access to any claim link.",
|
||||||
|
new ICommandArgumentParser[] { p.Word("groupname"), p.OptionalWord("targetgroup") },
|
||||||
|
false,
|
||||||
|
AdminDenyGroup
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var spec in adminCommands)
|
foreach (var spec in adminCommands)
|
||||||
@@ -532,6 +608,180 @@ public static class ClaimLinkChatCommand
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static EnumBlockAccessFlags ParseAccess(string word) =>
|
||||||
|
word switch
|
||||||
|
{
|
||||||
|
"use" => EnumBlockAccessFlags.Use,
|
||||||
|
"build" => EnumBlockAccessFlags.BuildOrBreak,
|
||||||
|
_ => EnumBlockAccessFlags.Use | EnumBlockAccessFlags.BuildOrBreak,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static TextCommandResult ExecuteAllow(
|
||||||
|
PlayerGroup group,
|
||||||
|
string targetName,
|
||||||
|
string accessWord
|
||||||
|
)
|
||||||
|
{
|
||||||
|
string? targetUid = ClaimLinkModSystem
|
||||||
|
.PlayerData.GetPlayerDataByLastKnownName(targetName)
|
||||||
|
?.PlayerUID;
|
||||||
|
if (targetUid == null)
|
||||||
|
return TextCommandResult.Error($"No such player '{targetName}'.");
|
||||||
|
|
||||||
|
ClaimLinkModSystem.Registry.GrantPlayer(group.Uid, targetUid, ParseAccess(accessWord));
|
||||||
|
return TextCommandResult.Success(
|
||||||
|
$"Granted {targetName} {accessWord} access to '{group.Name}''s buffer."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextCommandResult ExecuteDeny(PlayerGroup group, string targetName)
|
||||||
|
{
|
||||||
|
string? targetUid = ClaimLinkModSystem
|
||||||
|
.PlayerData.GetPlayerDataByLastKnownName(targetName)
|
||||||
|
?.PlayerUID;
|
||||||
|
if (targetUid == null)
|
||||||
|
return TextCommandResult.Error($"No such player '{targetName}'.");
|
||||||
|
|
||||||
|
return ClaimLinkModSystem.Registry.RevokePlayer(group.Uid, targetUid)
|
||||||
|
? TextCommandResult.Success($"Revoked {targetName}'s buffer access to '{group.Name}'.")
|
||||||
|
: TextCommandResult.Error($"{targetName} has no buffer access granted in '{group.Name}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextCommandResult ExecuteAllowGroup(
|
||||||
|
PlayerGroup group,
|
||||||
|
string targetGroupName,
|
||||||
|
string accessWord
|
||||||
|
)
|
||||||
|
{
|
||||||
|
TextCommandResult? err = TryResolveGroup(targetGroupName, out PlayerGroup targetGroup);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ClaimLinkModSystem.Registry.GrantGroup(group.Uid, targetGroup.Uid, ParseAccess(accessWord));
|
||||||
|
return TextCommandResult.Success(
|
||||||
|
$"Granted group '{targetGroup.Name}' {accessWord} access to '{group.Name}''s buffer."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextCommandResult ExecuteDenyGroup(PlayerGroup group, string targetGroupName)
|
||||||
|
{
|
||||||
|
TextCommandResult? err = TryResolveGroup(targetGroupName, out PlayerGroup targetGroup);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ClaimLinkModSystem.Registry.RevokeGroup(group.Uid, targetGroup.Uid)
|
||||||
|
? TextCommandResult.Success(
|
||||||
|
$"Revoked group '{targetGroup.Name}''s buffer access to '{group.Name}'."
|
||||||
|
)
|
||||||
|
: TextCommandResult.Error(
|
||||||
|
$"Group '{targetGroup.Name}' has no buffer access granted in '{group.Name}'."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextCommandResult? ResolveClaimLinkFromWordPair(
|
||||||
|
TextCommandCallingArgs args,
|
||||||
|
out PlayerGroup group,
|
||||||
|
out string targetName
|
||||||
|
)
|
||||||
|
{
|
||||||
|
string word1 = (string)args[0];
|
||||||
|
string? word2 = args.Parsers[1].IsMissing ? null : (string)args[1];
|
||||||
|
|
||||||
|
if (word2 != null)
|
||||||
|
{
|
||||||
|
TextCommandResult? groupErr = TryResolveGroup(word1, out group);
|
||||||
|
targetName = word2;
|
||||||
|
return groupErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetName = word1;
|
||||||
|
int chatGroupId = args.Caller.FromChatGroupId;
|
||||||
|
if (!ClaimLinkModSystem.Groups.PlayerGroupsById.TryGetValue(chatGroupId, out group!))
|
||||||
|
return TextCommandResult.Error(
|
||||||
|
"No group specified and you are not sending this from a group chat channel."
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextCommandResult Allow(TextCommandCallingArgs args)
|
||||||
|
{
|
||||||
|
string targetName = (string)args[0];
|
||||||
|
string accessWord = (string)args[1];
|
||||||
|
|
||||||
|
TextCommandResult? err = TryResolveGroupArg(args, 2, out PlayerGroup group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = TryResolveClaimLink(group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = RequireOpOrOwner(args.Caller.Player, group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ExecuteAllow(group, targetName, accessWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextCommandResult AllowGroup(TextCommandCallingArgs args)
|
||||||
|
{
|
||||||
|
string targetGroupName = (string)args[0];
|
||||||
|
string accessWord = (string)args[1];
|
||||||
|
|
||||||
|
TextCommandResult? err = TryResolveGroupArg(args, 2, out PlayerGroup group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = TryResolveClaimLink(group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = RequireOpOrOwner(args.Caller.Player, group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ExecuteAllowGroup(group, targetGroupName, accessWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextCommandResult Deny(TextCommandCallingArgs args)
|
||||||
|
{
|
||||||
|
TextCommandResult? err = ResolveClaimLinkFromWordPair(args, out PlayerGroup group, out string targetName);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = TryResolveClaimLink(group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = RequireOpOrOwner(args.Caller.Player, group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ExecuteDeny(group, targetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextCommandResult DenyGroup(TextCommandCallingArgs args)
|
||||||
|
{
|
||||||
|
TextCommandResult? err = ResolveClaimLinkFromWordPair(
|
||||||
|
args,
|
||||||
|
out PlayerGroup group,
|
||||||
|
out string targetGroupName
|
||||||
|
);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = TryResolveClaimLink(group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = RequireOpOrOwner(args.Caller.Player, group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ExecuteDenyGroup(group, targetGroupName);
|
||||||
|
}
|
||||||
|
|
||||||
public static TextCommandResult Delete(TextCommandCallingArgs args)
|
public static TextCommandResult Delete(TextCommandCallingArgs args)
|
||||||
{
|
{
|
||||||
string playerUid = args.Caller.Player.PlayerUID;
|
string playerUid = args.Caller.Player.PlayerUID;
|
||||||
@@ -803,9 +1053,45 @@ public static class ClaimLinkChatCommand
|
|||||||
sb.AppendLine($" {name}: [{string.Join(", ", claims)}]");
|
sb.AppendLine($" {name}: [{string.Join(", ", claims)}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!showDetails)
|
||||||
|
return sb.ToString();
|
||||||
|
|
||||||
|
var permittedPlayers = ClaimLinkModSystem.Registry.PermittedPlayersForGroup(groupId);
|
||||||
|
var permittedGroups = ClaimLinkModSystem.Registry.PermittedGroupsForGroup(groupId);
|
||||||
|
if (permittedPlayers.Count == 0 && permittedGroups.Count == 0)
|
||||||
|
return sb.ToString();
|
||||||
|
|
||||||
|
sb.AppendLine("Buffer access granted:");
|
||||||
|
foreach (var (uid, flags) in permittedPlayers)
|
||||||
|
{
|
||||||
|
string name = ClaimLinkModSystem.World.PlayerByUid(uid)?.PlayerName ?? uid;
|
||||||
|
sb.AppendLine($" {name}: {DescribeAccess(flags)}");
|
||||||
|
}
|
||||||
|
foreach (var (targetGroupId, flags) in permittedGroups)
|
||||||
|
{
|
||||||
|
string groupLabel = ClaimLinkModSystem.Groups.PlayerGroupsById.TryGetValue(
|
||||||
|
targetGroupId,
|
||||||
|
out PlayerGroup? targetGroup
|
||||||
|
)
|
||||||
|
? targetGroup!.Name
|
||||||
|
: $"group {targetGroupId}";
|
||||||
|
sb.AppendLine($" {groupLabel} (group): {DescribeAccess(flags)}");
|
||||||
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string DescribeAccess(EnumBlockAccessFlags flags)
|
||||||
|
{
|
||||||
|
bool use = (flags & EnumBlockAccessFlags.Use) != 0;
|
||||||
|
bool buildOrBreak = (flags & EnumBlockAccessFlags.BuildOrBreak) != 0;
|
||||||
|
if (use && buildOrBreak)
|
||||||
|
return "all";
|
||||||
|
if (buildOrBreak)
|
||||||
|
return "build";
|
||||||
|
return "use";
|
||||||
|
}
|
||||||
|
|
||||||
public static TextCommandResult AdminDelete(TextCommandCallingArgs args)
|
public static TextCommandResult AdminDelete(TextCommandCallingArgs args)
|
||||||
{
|
{
|
||||||
TextCommandResult? err = TryResolveGroupArg(args, 0, out PlayerGroup group);
|
TextCommandResult? err = TryResolveGroupArg(args, 0, out PlayerGroup group);
|
||||||
@@ -850,6 +1136,68 @@ public static class ClaimLinkChatCommand
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TextCommandResult AdminAllow(TextCommandCallingArgs args)
|
||||||
|
{
|
||||||
|
string targetName = (string)args[0];
|
||||||
|
string accessWord = (string)args[1];
|
||||||
|
|
||||||
|
TextCommandResult? err = TryResolveGroupArg(args, 2, out PlayerGroup group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = TryResolveClaimLink(group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ExecuteAllow(group, targetName, accessWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextCommandResult AdminAllowGroup(TextCommandCallingArgs args)
|
||||||
|
{
|
||||||
|
string targetGroupName = (string)args[0];
|
||||||
|
string accessWord = (string)args[1];
|
||||||
|
|
||||||
|
TextCommandResult? err = TryResolveGroupArg(args, 2, out PlayerGroup group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = TryResolveClaimLink(group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ExecuteAllowGroup(group, targetGroupName, accessWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextCommandResult AdminDeny(TextCommandCallingArgs args)
|
||||||
|
{
|
||||||
|
TextCommandResult? err = ResolveClaimLinkFromWordPair(args, out PlayerGroup group, out string targetName);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = TryResolveClaimLink(group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ExecuteDeny(group, targetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextCommandResult AdminDenyGroup(TextCommandCallingArgs args)
|
||||||
|
{
|
||||||
|
TextCommandResult? err = ResolveClaimLinkFromWordPair(
|
||||||
|
args,
|
||||||
|
out PlayerGroup group,
|
||||||
|
out string targetGroupName
|
||||||
|
);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = TryResolveClaimLink(group);
|
||||||
|
if (err != null)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ExecuteDenyGroup(group, targetGroupName);
|
||||||
|
}
|
||||||
|
|
||||||
public static TextCommandResult AdminKick(TextCommandCallingArgs args)
|
public static TextCommandResult AdminKick(TextCommandCallingArgs args)
|
||||||
{
|
{
|
||||||
string word1 = (string)args[0];
|
string word1 = (string)args[0];
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Vintagestory.API.Common;
|
||||||
using Vintagestory.API.Server;
|
using Vintagestory.API.Server;
|
||||||
|
|
||||||
namespace ClaimLink;
|
namespace ClaimLink;
|
||||||
@@ -7,10 +8,17 @@ public class ClaimLinkRegistry
|
|||||||
{
|
{
|
||||||
private const string SaveKey = "claimlink:claimlinkdata";
|
private const string SaveKey = "claimlink:claimlinkdata";
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, EnumBlockAccessFlags> EmptyPlayerFlags = new();
|
||||||
|
private static readonly Dictionary<int, EnumBlockAccessFlags> EmptyGroupFlags = new();
|
||||||
|
|
||||||
private readonly ISaveGame saveGame;
|
private readonly ISaveGame saveGame;
|
||||||
|
|
||||||
private readonly List<int> claimLinks = new();
|
private readonly List<int> claimLinks = new();
|
||||||
private readonly Dictionary<string, Dictionary<int, List<int>>> entries = new();
|
private readonly Dictionary<string, Dictionary<int, List<int>>> entries = new();
|
||||||
|
private readonly Dictionary<int, Dictionary<string, EnumBlockAccessFlags>> permittedPlayerUids =
|
||||||
|
new();
|
||||||
|
private readonly Dictionary<int, Dictionary<int, EnumBlockAccessFlags>> permittedPlayerGroupIds =
|
||||||
|
new();
|
||||||
|
|
||||||
public ClaimLinkRegistry(ISaveGame saveGame)
|
public ClaimLinkRegistry(ISaveGame saveGame)
|
||||||
{
|
{
|
||||||
@@ -29,6 +37,14 @@ public class ClaimLinkRegistry
|
|||||||
|
|
||||||
entries[entry.OwnerPlayerUid][entry.GroupId] = new(entry.ClaimIndicies);
|
entries[entry.OwnerPlayerUid][entry.GroupId] = new(entry.ClaimIndicies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var permission in data.Permissions)
|
||||||
|
{
|
||||||
|
if (permission.PermittedPlayerUids.Count > 0)
|
||||||
|
permittedPlayerUids[permission.GroupId] = new(permission.PermittedPlayerUids);
|
||||||
|
if (permission.PermittedPlayerGroupIds.Count > 0)
|
||||||
|
permittedPlayerGroupIds[permission.GroupId] = new(permission.PermittedPlayerGroupIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<int> All => claimLinks;
|
public IReadOnlyList<int> All => claimLinks;
|
||||||
@@ -46,10 +62,96 @@ public class ClaimLinkRegistry
|
|||||||
claimLinks.Remove(groupId);
|
claimLinks.Remove(groupId);
|
||||||
foreach (var groups in entries.Values)
|
foreach (var groups in entries.Values)
|
||||||
groups.Remove(groupId);
|
groups.Remove(groupId);
|
||||||
|
permittedPlayerUids.Remove(groupId);
|
||||||
|
permittedPlayerGroupIds.Remove(groupId);
|
||||||
Prune();
|
Prune();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GrantPlayer(int groupId, string uid, EnumBlockAccessFlags flags)
|
||||||
|
{
|
||||||
|
if (!permittedPlayerUids.TryGetValue(groupId, out var uids))
|
||||||
|
permittedPlayerUids[groupId] = uids = new();
|
||||||
|
uids[uid] = flags;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RevokePlayer(int groupId, string uid)
|
||||||
|
{
|
||||||
|
bool removed = permittedPlayerUids.TryGetValue(groupId, out var uids) && uids.Remove(uid);
|
||||||
|
if (!removed)
|
||||||
|
return false;
|
||||||
|
if (uids!.Count == 0)
|
||||||
|
permittedPlayerUids.Remove(groupId);
|
||||||
|
Save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GrantGroup(int groupId, int targetGroupId, EnumBlockAccessFlags flags)
|
||||||
|
{
|
||||||
|
if (!permittedPlayerGroupIds.TryGetValue(groupId, out var groups))
|
||||||
|
permittedPlayerGroupIds[groupId] = groups = new();
|
||||||
|
groups[targetGroupId] = flags;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RevokeGroup(int groupId, int targetGroupId)
|
||||||
|
{
|
||||||
|
bool removed =
|
||||||
|
permittedPlayerGroupIds.TryGetValue(groupId, out var groups)
|
||||||
|
&& groups.Remove(targetGroupId);
|
||||||
|
if (!removed)
|
||||||
|
return false;
|
||||||
|
if (groups!.Count == 0)
|
||||||
|
permittedPlayerGroupIds.Remove(groupId);
|
||||||
|
Save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasPermission(int groupId, IPlayer player, EnumBlockAccessFlags accessFlag)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
permittedPlayerUids.TryGetValue(groupId, out var uids)
|
||||||
|
&& uids.TryGetValue(player.PlayerUID, out EnumBlockAccessFlags flags)
|
||||||
|
&& (flags & accessFlag) != 0
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (permittedPlayerGroupIds.TryGetValue(groupId, out var groups))
|
||||||
|
foreach (PlayerGroupMembership membership in player.Groups)
|
||||||
|
if (groups.TryGetValue(membership.GroupUid, out flags) && (flags & accessFlag) != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, EnumBlockAccessFlags> PermittedPlayersForGroup(int groupId) =>
|
||||||
|
permittedPlayerUids.TryGetValue(groupId, out var uids) ? uids : EmptyPlayerFlags;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<int, EnumBlockAccessFlags> PermittedGroupsForGroup(int groupId) =>
|
||||||
|
permittedPlayerGroupIds.TryGetValue(groupId, out var groups) ? groups : EmptyGroupFlags;
|
||||||
|
|
||||||
|
public void RemoveGroupFromAllPermissions(int targetGroupId)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
foreach (var groups in permittedPlayerGroupIds.Values)
|
||||||
|
if (groups.Remove(targetGroupId))
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<int>? emptyGroupIds = null;
|
||||||
|
foreach (var (groupId, groups) in permittedPlayerGroupIds)
|
||||||
|
if (groups.Count == 0)
|
||||||
|
(emptyGroupIds ??= new()).Add(groupId);
|
||||||
|
if (emptyGroupIds != null)
|
||||||
|
foreach (int groupId in emptyGroupIds)
|
||||||
|
permittedPlayerGroupIds.Remove(groupId);
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsClaimLinked(string uid, int claimIndex) =>
|
public bool IsClaimLinked(string uid, int claimIndex) =>
|
||||||
FindGroupContaining(uid, claimIndex) != null;
|
FindGroupContaining(uid, claimIndex) != null;
|
||||||
|
|
||||||
@@ -193,9 +295,37 @@ public class ClaimLinkRegistry
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var permissionGroupIds = new HashSet<int>();
|
||||||
|
foreach (int groupId in permittedPlayerUids.Keys)
|
||||||
|
permissionGroupIds.Add(groupId);
|
||||||
|
foreach (int groupId in permittedPlayerGroupIds.Keys)
|
||||||
|
permissionGroupIds.Add(groupId);
|
||||||
|
|
||||||
|
var permissionList = new List<ClaimLinkPermissionSaveData>();
|
||||||
|
foreach (int groupId in permissionGroupIds)
|
||||||
|
{
|
||||||
|
permittedPlayerUids.TryGetValue(groupId, out var uids);
|
||||||
|
permittedPlayerGroupIds.TryGetValue(groupId, out var groups);
|
||||||
|
permissionList.Add(
|
||||||
|
new ClaimLinkPermissionSaveData
|
||||||
|
{
|
||||||
|
GroupId = groupId,
|
||||||
|
PermittedPlayerUids = uids != null ? new(uids) : new(),
|
||||||
|
PermittedPlayerGroupIds = groups != null ? new(groups) : new(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
saveGame.StoreData(
|
saveGame.StoreData(
|
||||||
SaveKey,
|
SaveKey,
|
||||||
new ClaimLinkSaveData { Links = claimLinks, Entries = entryList }
|
new ClaimLinkSaveData
|
||||||
|
{
|
||||||
|
Links = claimLinks,
|
||||||
|
Entries = entryList,
|
||||||
|
Permissions = permissionList,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
@@ -387,6 +390,8 @@ public class GroupCommandListener : ICommandHookListener
|
|||||||
|
|
||||||
if (ClaimLinkModSystem.Registry.Exists(disbandedGroupId))
|
if (ClaimLinkModSystem.Registry.Exists(disbandedGroupId))
|
||||||
ClaimLinkModSystem.Registry.Remove(disbandedGroupId);
|
ClaimLinkModSystem.Registry.Remove(disbandedGroupId);
|
||||||
|
|
||||||
|
ClaimLinkModSystem.Registry.RemoveGroupFromAllPermissions(disbandedGroupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.5.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