From 4d98a115ede498aecf20ea0a06fce8e6f172a510 Mon Sep 17 00:00:00 2001 From: anth64 Date: Sun, 26 Jul 2026 11:21:13 +0200 Subject: [PATCH] feat: infer claim link group from chat context when omitted; drop redundant admin info --- ClaimLink/ClaimLinkChatCommand.cs | 208 ++++++++++++++++++++---------- 1 file changed, 141 insertions(+), 67 deletions(-) diff --git a/ClaimLink/ClaimLinkChatCommand.cs b/ClaimLink/ClaimLinkChatCommand.cs index d2beda9..a62df83 100644 --- a/ClaimLink/ClaimLinkChatCommand.cs +++ b/ClaimLink/ClaimLinkChatCommand.cs @@ -69,14 +69,18 @@ public static class ClaimLinkChatCommand new( new[] { "new", "n" }, "Promote a group you own to a claim link.", - new ICommandArgumentParser[] { p.Word("groupname") }, + new ICommandArgumentParser[] { p.OptionalWord("groupname") }, true, New ), new( new[] { "link", "l" }, "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, Link ), @@ -104,28 +108,28 @@ public static class ClaimLinkChatCommand new( new[] { "kick" }, "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, Kick ), new( new[] { "delete" }, "Delete a claim link.", - new ICommandArgumentParser[] { p.Word("groupname") }, + new ICommandArgumentParser[] { p.OptionalWord("groupname") }, true, Delete ), new( new[] { "transferownership", "transfer", "to" }, "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, TransferOwnership ), new( new[] { "info", "i" }, "Show a claim link's members and linked claims.", - new ICommandArgumentParser[] { p.Word("groupname") }, + new ICommandArgumentParser[] { p.OptionalWord("groupname") }, true, Info ), @@ -171,17 +175,14 @@ public static class ClaimLinkChatCommand new( new[] { "transferownership", "transfer", "to" }, "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, 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) @@ -245,6 +246,24 @@ public static class ClaimLinkChatCommand : 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) { return !ClaimLinkModSystem.Registry.Exists(group.Uid) @@ -279,10 +298,9 @@ public static class ClaimLinkChatCommand public static TextCommandResult New(TextCommandCallingArgs args) { - string groupName = (string)args[0]; string playerUid = args.Caller.Player.PlayerUID; - TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); + TextCommandResult? err = TryResolveGroupArg(args, 0, out PlayerGroup group); if (err != null) return err; @@ -291,9 +309,10 @@ public static class ClaimLinkChatCommand return err; 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; + string groupName = group.Name; return Stage( playerUid, groupId, @@ -337,10 +356,9 @@ public static class ClaimLinkChatCommand public static TextCommandResult Link(TextCommandCallingArgs args) { - string groupName = (string)args[0]; - int claimIndex = (int)args[1]; + int claimIndex = (int)args[0]; - TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); + TextCommandResult? err = TryResolveGroupArg(args, 1, out PlayerGroup group); if (err != null) return err; @@ -355,6 +373,7 @@ public static class ClaimLinkChatCommand string playerUid = player.PlayerUID; int groupId = group.Uid; + string groupName = group.Name; if (!ClaimLinkModSystem.TryResolveOwnedClaim(playerUid, claimIndex, out _)) return TextCommandResult.Error("You do not own that claim."); @@ -414,33 +433,52 @@ public static class ClaimLinkChatCommand public static TextCommandResult Kick(TextCommandCallingArgs args) { - string groupName = (string)args[0]; - IPlayer target = (IPlayer)args[1]; + string word1 = (string)args[0]; + string? word2 = args.Parsers[1].IsMissing ? null : (string)args[1]; - TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); - if (err != null) - return err; + string targetName; + PlayerGroup group; + if (word2 != null) + { + TextCommandResult? err = TryResolveGroup(word1, out group); + if (err != null) + 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); - if (err != null) - return err; + TextCommandResult? linkErr = TryResolveClaimLink(group); + if (linkErr != null) + return linkErr; - err = RequireOpOrOwner(args.Caller.Player, group); - if (err != null) - return err; + linkErr = RequireOpOrOwner(args.Caller.Player, group); + if (linkErr != null) + 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 groupName = group.Name; if (!ClaimLinkModSystem.Registry.HasAnyEntry(targetUid, group.Uid)) - return TextCommandResult.Error( - $"{target.PlayerName} has no claims linked in '{groupName}'." - ); + return TextCommandResult.Error($"{targetName} has no claims linked in '{groupName}'."); return Stage( playerUid, 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); @@ -448,7 +486,7 @@ public static class ClaimLinkChatCommand PendingActions.Remove(targetUid); 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) { - string groupName = (string)args[0]; string playerUid = args.Caller.Player.PlayerUID; - TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); + TextCommandResult? err = TryResolveGroupArg(args, 0, out PlayerGroup group); if (err != null) return err; @@ -472,6 +509,7 @@ public static class ClaimLinkChatCommand return err; int groupId = group.Uid; + string groupName = group.Name; return Stage( playerUid, groupId, @@ -487,18 +525,32 @@ public static class ClaimLinkChatCommand 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; string playerUid = player.PlayerUID; - TextCommandResult? err = TryResolveTargetPlayer( - (PlayerUidName[])args[1], - out IPlayer target - ); - if (err != null) - return err; - - err = TryResolveGroup(groupName, out PlayerGroup group); + TextCommandResult? err = TryResolveOnlinePlayerByName(targetName, out IPlayer target); if (err != null) return err; @@ -518,6 +570,7 @@ public static class ClaimLinkChatCommand if (err != null) return err; + string groupName = group.Name; return Stage( playerUid, 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( PlayerUidName[] matches, out IPlayer target @@ -588,9 +657,7 @@ public static class ClaimLinkChatCommand public static TextCommandResult Info(TextCommandCallingArgs args) { - string groupName = (string)args[0]; - - TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); + TextCommandResult? err = TryResolveGroupArg(args, 0, out PlayerGroup group); if (err != null) return err; @@ -602,16 +669,13 @@ public static class ClaimLinkChatCommand bool showDetails = 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) { - List groupIds = ClaimLinkModSystem - .Registry.All.OrderByDescending(id => - ClaimLinkModSystem.Registry.MemberCountForGroup(id) - ) - .ToList(); + List groupIds = new(ClaimLinkModSystem.Registry.All); + groupIds.Sort((a, b) => GroupMemberCount(b).CompareTo(GroupMemberCount(a))); if (groupIds.Count == 0) return TextCommandResult.Success("There are no claim links."); @@ -633,7 +697,7 @@ public static class ClaimLinkChatCommand continue; } - int memberCount = ClaimLinkModSystem.Registry.MemberCountForGroup(groupId); + int memberCount = GroupMemberCount(groupId); sb.AppendLine($" {group.Name}: {memberCount} member{(memberCount == 1 ? "" : "s")}"); shown++; } @@ -644,6 +708,21 @@ public static class ClaimLinkChatCommand return TextCommandResult.Success($"Claim links ({shown}):\n{sb}"); } + private static IEnumerable 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) { if ( @@ -661,14 +740,14 @@ public static class ClaimLinkChatCommand private static string FormatInfo(string groupName, int groupId, bool showDetails) { - int memberCount = ClaimLinkModSystem.Registry.MemberCountForGroup(groupId); + int memberCount = GroupMemberCount(groupId); StringBuilder sb = new(); sb.AppendLine( $"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; @@ -698,16 +777,14 @@ public static class ClaimLinkChatCommand public static TextCommandResult AdminTransferOwnership(TextCommandCallingArgs args) { - string groupName = (string)args[0]; - TextCommandResult? err = TryResolveTargetPlayer( - (PlayerUidName[])args[1], + (PlayerUidName[])args[0], out IPlayer target ); if (err != null) return err; - err = TryResolveGroup(groupName, out PlayerGroup group); + err = TryResolveGroupArg(args, 1, out PlayerGroup group); if (err != null) return err; @@ -726,9 +803,6 @@ public static class ClaimLinkChatCommand return ExecuteTransferOwnership(group, target); } - public static TextCommandResult AdminInfo(TextCommandCallingArgs args) => - TextCommandResult.Success("stub: claimlink admin info"); - internal static void OnPlayerDisconnect(IServerPlayer player) { PendingActions.Remove(player.PlayerUID);