feat: infer claim link group from chat context when omitted; drop redundant admin info

This commit is contained in:
2026-07-26 11:21:13 +02:00
parent 99238c4744
commit 4d98a115ed
+139 -65
View File
@@ -69,14 +69,18 @@ public static class ClaimLinkChatCommand
new( new(
new[] { "new", "n" }, new[] { "new", "n" },
"Promote a group you own to a claim link.", "Promote a group you own to a claim link.",
new ICommandArgumentParser[] { p.Word("groupname") }, new ICommandArgumentParser[] { p.OptionalWord("groupname") },
true, true,
New New
), ),
new( new(
new[] { "link", "l" }, new[] { "link", "l" },
"Link a claim you own to a claim link.", "Link a claim you own to a claim link.",
new ICommandArgumentParser[] { p.Word("groupname"), p.IntRange("claim", 0, 999) }, new ICommandArgumentParser[]
{
p.IntRange("claim", 0, 999),
p.OptionalWord("groupname"),
},
true, true,
Link Link
), ),
@@ -104,28 +108,28 @@ public static class ClaimLinkChatCommand
new( new(
new[] { "kick" }, new[] { "kick" },
"Force-unlink all of a player's claims from the claim link.", "Force-unlink all of a player's claims from the claim link.",
new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, new ICommandArgumentParser[] { p.Word("groupname"), p.OptionalWord("playername") },
true, true,
Kick Kick
), ),
new( new(
new[] { "delete" }, new[] { "delete" },
"Delete a claim link.", "Delete a claim link.",
new ICommandArgumentParser[] { p.Word("groupname") }, new ICommandArgumentParser[] { p.OptionalWord("groupname") },
true, true,
Delete Delete
), ),
new( new(
new[] { "transferownership", "transfer", "to" }, new[] { "transferownership", "transfer", "to" },
"Transfer ownership of the claim link to another player.", "Transfer ownership of the claim link to another player.",
new ICommandArgumentParser[] { p.Word("groupname"), p.PlayerUids("playername") }, new ICommandArgumentParser[] { p.Word("groupname"), p.OptionalWord("playername") },
true, true,
TransferOwnership TransferOwnership
), ),
new( new(
new[] { "info", "i" }, new[] { "info", "i" },
"Show a claim link's members and linked claims.", "Show a claim link's members and linked claims.",
new ICommandArgumentParser[] { p.Word("groupname") }, new ICommandArgumentParser[] { p.OptionalWord("groupname") },
true, true,
Info Info
), ),
@@ -171,17 +175,14 @@ public static class ClaimLinkChatCommand
new( new(
new[] { "transferownership", "transfer", "to" }, new[] { "transferownership", "transfer", "to" },
"Transfer ownership of any claim link to another player.", "Transfer ownership of any claim link to another player.",
new ICommandArgumentParser[] { p.Word("groupname"), p.PlayerUids("playername") }, new ICommandArgumentParser[]
{
p.PlayerUids("playername"),
p.OptionalWord("groupname"),
},
false, false,
AdminTransferOwnership AdminTransferOwnership
), ),
new(
new[] { "info", "i" },
"Show a claim link's members and linked claims.",
new ICommandArgumentParser[] { p.Word("groupname") },
false,
AdminInfo
),
}; };
foreach (var spec in adminCommands) foreach (var spec in adminCommands)
@@ -245,6 +246,24 @@ public static class ClaimLinkChatCommand
: null; : null;
} }
private static TextCommandResult? TryResolveGroupArg(
TextCommandCallingArgs args,
int argIndex,
out PlayerGroup group
)
{
if (!args.Parsers[argIndex].IsMissing)
return TryResolveGroup((string)args[argIndex], out group);
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;
}
private static TextCommandResult? TryResolveClaimLink(PlayerGroup group) private static TextCommandResult? TryResolveClaimLink(PlayerGroup group)
{ {
return !ClaimLinkModSystem.Registry.Exists(group.Uid) return !ClaimLinkModSystem.Registry.Exists(group.Uid)
@@ -279,10 +298,9 @@ public static class ClaimLinkChatCommand
public static TextCommandResult New(TextCommandCallingArgs args) public static TextCommandResult New(TextCommandCallingArgs args)
{ {
string groupName = (string)args[0];
string playerUid = args.Caller.Player.PlayerUID; string playerUid = args.Caller.Player.PlayerUID;
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); TextCommandResult? err = TryResolveGroupArg(args, 0, out PlayerGroup group);
if (err != null) if (err != null)
return err; return err;
@@ -291,9 +309,10 @@ public static class ClaimLinkChatCommand
return err; return err;
if (ClaimLinkModSystem.Registry.Exists(group.Uid)) if (ClaimLinkModSystem.Registry.Exists(group.Uid))
return TextCommandResult.Error($"'{groupName}' is already a claim link."); return TextCommandResult.Error($"'{group.Name}' is already a claim link.");
int groupId = group.Uid; int groupId = group.Uid;
string groupName = group.Name;
return Stage( return Stage(
playerUid, playerUid,
groupId, groupId,
@@ -337,10 +356,9 @@ public static class ClaimLinkChatCommand
public static TextCommandResult Link(TextCommandCallingArgs args) public static TextCommandResult Link(TextCommandCallingArgs args)
{ {
string groupName = (string)args[0]; int claimIndex = (int)args[0];
int claimIndex = (int)args[1];
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); TextCommandResult? err = TryResolveGroupArg(args, 1, out PlayerGroup group);
if (err != null) if (err != null)
return err; return err;
@@ -355,6 +373,7 @@ public static class ClaimLinkChatCommand
string playerUid = player.PlayerUID; string playerUid = player.PlayerUID;
int groupId = group.Uid; int groupId = group.Uid;
string groupName = group.Name;
if (!ClaimLinkModSystem.TryResolveOwnedClaim(playerUid, claimIndex, out _)) if (!ClaimLinkModSystem.TryResolveOwnedClaim(playerUid, claimIndex, out _))
return TextCommandResult.Error("You do not own that claim."); return TextCommandResult.Error("You do not own that claim.");
@@ -414,33 +433,52 @@ public static class ClaimLinkChatCommand
public static TextCommandResult Kick(TextCommandCallingArgs args) public static TextCommandResult Kick(TextCommandCallingArgs args)
{ {
string groupName = (string)args[0]; string word1 = (string)args[0];
IPlayer target = (IPlayer)args[1]; string? word2 = args.Parsers[1].IsMissing ? null : (string)args[1];
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); string targetName;
PlayerGroup group;
if (word2 != null)
{
TextCommandResult? err = TryResolveGroup(word1, out group);
if (err != null) if (err != null)
return err; return err;
targetName = word2;
}
else
{
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."
);
targetName = word1;
}
err = TryResolveClaimLink(group); TextCommandResult? linkErr = TryResolveClaimLink(group);
if (err != null) if (linkErr != null)
return err; return linkErr;
err = RequireOpOrOwner(args.Caller.Player, group); linkErr = RequireOpOrOwner(args.Caller.Player, group);
if (err != null) if (linkErr != null)
return err; return linkErr;
string? targetUid = ClaimLinkModSystem
.PlayerData.GetPlayerDataByLastKnownName(targetName)
?.PlayerUID;
if (targetUid == null)
return TextCommandResult.Error($"No such player '{targetName}'.");
string targetUid = target.PlayerUID;
string playerUid = args.Caller.Player.PlayerUID; string playerUid = args.Caller.Player.PlayerUID;
string groupName = group.Name;
if (!ClaimLinkModSystem.Registry.HasAnyEntry(targetUid, group.Uid)) if (!ClaimLinkModSystem.Registry.HasAnyEntry(targetUid, group.Uid))
return TextCommandResult.Error( return TextCommandResult.Error($"{targetName} has no claims linked in '{groupName}'.");
$"{target.PlayerName} has no claims linked in '{groupName}'."
);
return Stage( return Stage(
playerUid, playerUid,
group.Uid, group.Uid,
$"All of {target.PlayerName}'s claims will be unlinked from '{groupName}'.", $"All of {targetName}'s claims will be unlinked from '{groupName}'.",
() => () =>
{ {
ClaimLinkModSystem.Registry.RemoveAllForPlayerInGroup(targetUid, group.Uid); ClaimLinkModSystem.Registry.RemoveAllForPlayerInGroup(targetUid, group.Uid);
@@ -448,7 +486,7 @@ public static class ClaimLinkChatCommand
PendingActions.Remove(targetUid); PendingActions.Remove(targetUid);
return TextCommandResult.Success( return TextCommandResult.Success(
$"Unlinked all claims of {target.PlayerName} from '{groupName}'." $"Unlinked all claims of {targetName} from '{groupName}'."
); );
} }
); );
@@ -456,10 +494,9 @@ public static class ClaimLinkChatCommand
public static TextCommandResult Delete(TextCommandCallingArgs args) public static TextCommandResult Delete(TextCommandCallingArgs args)
{ {
string groupName = (string)args[0];
string playerUid = args.Caller.Player.PlayerUID; string playerUid = args.Caller.Player.PlayerUID;
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); TextCommandResult? err = TryResolveGroupArg(args, 0, out PlayerGroup group);
if (err != null) if (err != null)
return err; return err;
@@ -472,6 +509,7 @@ public static class ClaimLinkChatCommand
return err; return err;
int groupId = group.Uid; int groupId = group.Uid;
string groupName = group.Name;
return Stage( return Stage(
playerUid, playerUid,
groupId, groupId,
@@ -487,18 +525,32 @@ public static class ClaimLinkChatCommand
public static TextCommandResult TransferOwnership(TextCommandCallingArgs args) public static TextCommandResult TransferOwnership(TextCommandCallingArgs args)
{ {
string groupName = (string)args[0]; string word1 = (string)args[0];
string? word2 = args.Parsers[1].IsMissing ? null : (string)args[1];
string targetName;
PlayerGroup group;
if (word2 != null)
{
TextCommandResult? groupErr = TryResolveGroup(word1, out group);
if (groupErr != null)
return groupErr;
targetName = word2;
}
else
{
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."
);
targetName = word1;
}
IPlayer player = args.Caller.Player; IPlayer player = args.Caller.Player;
string playerUid = player.PlayerUID; string playerUid = player.PlayerUID;
TextCommandResult? err = TryResolveTargetPlayer( TextCommandResult? err = TryResolveOnlinePlayerByName(targetName, out IPlayer target);
(PlayerUidName[])args[1],
out IPlayer target
);
if (err != null)
return err;
err = TryResolveGroup(groupName, out PlayerGroup group);
if (err != null) if (err != null)
return err; return err;
@@ -518,6 +570,7 @@ public static class ClaimLinkChatCommand
if (err != null) if (err != null)
return err; return err;
string groupName = group.Name;
return Stage( return Stage(
playerUid, playerUid,
group.Uid, group.Uid,
@@ -526,6 +579,22 @@ public static class ClaimLinkChatCommand
); );
} }
private static TextCommandResult? TryResolveOnlinePlayerByName(string name, out IPlayer target)
{
target = null!;
foreach (IPlayer online in ClaimLinkModSystem.World.AllOnlinePlayers)
{
if (string.Equals(online.PlayerName, name, StringComparison.OrdinalIgnoreCase))
{
target = online;
return null;
}
}
return TextCommandResult.Error($"No such player '{name}' online.");
}
private static TextCommandResult? TryResolveTargetPlayer( private static TextCommandResult? TryResolveTargetPlayer(
PlayerUidName[] matches, PlayerUidName[] matches,
out IPlayer target out IPlayer target
@@ -588,9 +657,7 @@ public static class ClaimLinkChatCommand
public static TextCommandResult Info(TextCommandCallingArgs args) public static TextCommandResult Info(TextCommandCallingArgs args)
{ {
string groupName = (string)args[0]; TextCommandResult? err = TryResolveGroupArg(args, 0, out PlayerGroup group);
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group);
if (err != null) if (err != null)
return err; return err;
@@ -602,16 +669,13 @@ public static class ClaimLinkChatCommand
bool showDetails = bool showDetails =
player.HasPrivilege(Privilege.controlserver) || player.GetGroup(group.Uid) != null; player.HasPrivilege(Privilege.controlserver) || player.GetGroup(group.Uid) != null;
return TextCommandResult.Success(FormatInfo(groupName, group.Uid, showDetails)); return TextCommandResult.Success(FormatInfo(group.Name, group.Uid, showDetails));
} }
public static TextCommandResult List(TextCommandCallingArgs args) public static TextCommandResult List(TextCommandCallingArgs args)
{ {
List<int> groupIds = ClaimLinkModSystem List<int> groupIds = new(ClaimLinkModSystem.Registry.All);
.Registry.All.OrderByDescending(id => groupIds.Sort((a, b) => GroupMemberCount(b).CompareTo(GroupMemberCount(a)));
ClaimLinkModSystem.Registry.MemberCountForGroup(id)
)
.ToList();
if (groupIds.Count == 0) if (groupIds.Count == 0)
return TextCommandResult.Success("There are no claim links."); return TextCommandResult.Success("There are no claim links.");
@@ -633,7 +697,7 @@ public static class ClaimLinkChatCommand
continue; continue;
} }
int memberCount = ClaimLinkModSystem.Registry.MemberCountForGroup(groupId); int memberCount = GroupMemberCount(groupId);
sb.AppendLine($" {group.Name}: {memberCount} member{(memberCount == 1 ? "" : "s")}"); sb.AppendLine($" {group.Name}: {memberCount} member{(memberCount == 1 ? "" : "s")}");
shown++; shown++;
} }
@@ -644,6 +708,21 @@ public static class ClaimLinkChatCommand
return TextCommandResult.Success($"Claim links ({shown}):\n{sb}"); return TextCommandResult.Success($"Claim links ({shown}):\n{sb}");
} }
private static IEnumerable<string> GroupMemberUids(int groupId)
{
foreach (var playerData in ClaimLinkModSystem.PlayerData.PlayerDataByUid.Values)
if (playerData.PlayerGroupMemberships.ContainsKey(groupId))
yield return playerData.PlayerUID;
}
private static int GroupMemberCount(int groupId)
{
int count = 0;
foreach (string _ in GroupMemberUids(groupId))
count++;
return count;
}
private static string DescribeClaim(string ownerPlayerUid, int claimIndex) private static string DescribeClaim(string ownerPlayerUid, int claimIndex)
{ {
if ( if (
@@ -661,14 +740,14 @@ public static class ClaimLinkChatCommand
private static string FormatInfo(string groupName, int groupId, bool showDetails) private static string FormatInfo(string groupName, int groupId, bool showDetails)
{ {
int memberCount = ClaimLinkModSystem.Registry.MemberCountForGroup(groupId); int memberCount = GroupMemberCount(groupId);
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine( sb.AppendLine(
$"Claim link '{groupName}' ({memberCount} member{(memberCount == 1 ? "" : "s")}):" $"Claim link '{groupName}' ({memberCount} member{(memberCount == 1 ? "" : "s")}):"
); );
foreach (string uid in ClaimLinkModSystem.Registry.MemberUidsForGroup(groupId)) foreach (string uid in GroupMemberUids(groupId))
{ {
string name = ClaimLinkModSystem.World.PlayerByUid(uid)?.PlayerName ?? uid; string name = ClaimLinkModSystem.World.PlayerByUid(uid)?.PlayerName ?? uid;
@@ -698,16 +777,14 @@ public static class ClaimLinkChatCommand
public static TextCommandResult AdminTransferOwnership(TextCommandCallingArgs args) public static TextCommandResult AdminTransferOwnership(TextCommandCallingArgs args)
{ {
string groupName = (string)args[0];
TextCommandResult? err = TryResolveTargetPlayer( TextCommandResult? err = TryResolveTargetPlayer(
(PlayerUidName[])args[1], (PlayerUidName[])args[0],
out IPlayer target out IPlayer target
); );
if (err != null) if (err != null)
return err; return err;
err = TryResolveGroup(groupName, out PlayerGroup group); err = TryResolveGroupArg(args, 1, out PlayerGroup group);
if (err != null) if (err != null)
return err; return err;
@@ -726,9 +803,6 @@ public static class ClaimLinkChatCommand
return ExecuteTransferOwnership(group, target); return ExecuteTransferOwnership(group, target);
} }
public static TextCommandResult AdminInfo(TextCommandCallingArgs args) =>
TextCommandResult.Success("stub: claimlink admin info");
internal static void OnPlayerDisconnect(IServerPlayer player) internal static void OnPlayerDisconnect(IServerPlayer player)
{ {
PendingActions.Remove(player.PlayerUID); PendingActions.Remove(player.PlayerUID);