diff --git a/ClaimLink/ClaimLinkChatCommand.cs b/ClaimLink/ClaimLinkChatCommand.cs index 90ce34f..71385af 100644 --- a/ClaimLink/ClaimLinkChatCommand.cs +++ b/ClaimLink/ClaimLinkChatCommand.cs @@ -17,7 +17,13 @@ public static class ClaimLinkChatCommand public readonly bool IsPlayerOnly; public readonly OnCommandDelegate Handler; - public CommandSpec(string[] names, string description, ICommandArgumentParser[] args, bool isPlayerOnly, OnCommandDelegate handler) + public CommandSpec( + string[] names, + string description, + ICommandArgumentParser[] args, + bool isPlayerOnly, + OnCommandDelegate handler + ) { Names = names; Description = description; @@ -29,9 +35,10 @@ public static class ClaimLinkChatCommand private static void BuildSubCommand(IChatCommand parent, CommandSpec spec) { - var sub = spec.Names.Length > 1 - ? parent.BeginSubCommands(spec.Names) - : parent.BeginSubCommand(spec.Names[0]); + var sub = + spec.Names.Length > 1 + ? parent.BeginSubCommands(spec.Names) + : parent.BeginSubCommand(spec.Names[0]); sub.WithDescription(spec.Description); @@ -50,42 +57,84 @@ public static class ClaimLinkChatCommand { var p = api.ChatCommands.Parsers; - var root = api.ChatCommands.Create("claimlink") + var root = api + .ChatCommands.Create("claimlink") .WithAlias("clink", "claiml", "cl") .WithDescription("Link vanilla land claims together via group.") .RequiresPrivilege(Privilege.chat); CommandSpec[] playerCommands = { - new(new[] { "new", "n" }, "Promote a group you own to a claim link.", - new ICommandArgumentParser[] { p.Word("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) }, true, Link), - - new(new[] { "confirm", "c" }, "Confirm pending action.", - Array.Empty(), true, Confirm), - - new(new[] { "cancel" }, "Cancel pending action.", - Array.Empty(), true, Cancel), - - new(new[] { "unlink", "ul" }, "Remove a claim you own from its claim link.", - new ICommandArgumentParser[] { p.IntRange("claim", 0, 999) }, true, Unlink), - - new(new[] { "kick" }, "Force-unlink all of a player's claims from the claim link.", - new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, true, Kick), - - new(new[] { "delete" }, "Delete a claim link.", - new ICommandArgumentParser[] { p.Word("groupname") }, true, Delete), - - new(new[] { "transferownership", "transfer", "to" }, "Transfer ownership of the claim link to another player.", - new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, true, TransferOwnership), - - new(new[] { "info", "i" }, "Show a claim link's members and linked claims.", - new ICommandArgumentParser[] { p.Word("groupname") }, true, Info), - - new(new[] { "list", "ls" }, "List all claim links.", - Array.Empty(), true, List), + new( + new[] { "new", "n" }, + "Promote a group you own to a claim link.", + new ICommandArgumentParser[] { p.Word("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) }, + true, + Link + ), + new( + new[] { "confirm", "c" }, + "Confirm pending action.", + Array.Empty(), + true, + Confirm + ), + new( + new[] { "cancel" }, + "Cancel pending action.", + Array.Empty(), + true, + Cancel + ), + new( + new[] { "unlink", "ul" }, + "Remove a claim you own from its claim link.", + new ICommandArgumentParser[] { p.IntRange("claim", 0, 999) }, + true, + Unlink + ), + new( + new[] { "kick" }, + "Force-unlink all of a player's claims from the claim link.", + new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, + true, + Kick + ), + new( + new[] { "delete" }, + "Delete a claim link.", + new ICommandArgumentParser[] { p.Word("groupname") }, + true, + Delete + ), + new( + new[] { "transferownership", "transfer", "to" }, + "Transfer ownership of the claim link to another player.", + new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, + true, + TransferOwnership + ), + new( + new[] { "info", "i" }, + "Show a claim link's members and linked claims.", + new ICommandArgumentParser[] { p.Word("groupname") }, + true, + Info + ), + new( + new[] { "list", "ls" }, + "List all claim links.", + Array.Empty(), + true, + List + ), }; foreach (var spec in playerCommands) @@ -97,20 +146,41 @@ public static class ClaimLinkChatCommand CommandSpec[] adminCommands = { - new(new[] { "delete", "del" }, "Delete a claim link.", - new ICommandArgumentParser[] { p.Word("groupname") }, false, AdminDelete), - - new(new[] { "unlink" }, "Unlink a claim.", - new ICommandArgumentParser[] { p.OnlinePlayer("playername"), p.Word("claim") }, false, AdminUnlink), - - new(new[] { "kick" }, "Force-unlink all of a player's claims.", - new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, false, AdminKick), - - new(new[] { "transferownership", "transfer", "to" }, "Transfer ownership of any claim link to another player.", - new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, false, AdminTransferOwnership), - - new(new[] { "info", "i" }, "Show a claim link's members and linked claims.", - new ICommandArgumentParser[] { p.Word("groupname") }, false, AdminInfo), + new( + new[] { "delete", "del" }, + "Delete a claim link.", + new ICommandArgumentParser[] { p.Word("groupname") }, + false, + AdminDelete + ), + new( + new[] { "unlink" }, + "Unlink a claim.", + new ICommandArgumentParser[] { p.OnlinePlayer("playername"), p.Word("claim") }, + false, + AdminUnlink + ), + new( + new[] { "kick" }, + "Force-unlink all of a player's claims.", + new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, + false, + AdminKick + ), + new( + new[] { "transferownership", "transfer", "to" }, + "Transfer ownership of any claim link to another player.", + new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, + 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) @@ -123,32 +193,46 @@ public static class ClaimLinkChatCommand internal static void RemovePending(string playerUid) => pendingActions.Remove(playerUid); - private static TextCommandResult Stage(string playerUid, string prompt, Func action) + private static TextCommandResult Stage( + string playerUid, + string prompt, + Func action + ) { pendingActions[playerUid] = action; - return TextCommandResult.Success($"{prompt} Use /claimlink confirm to proceed, or /claimlink cancel to cancel."); + return TextCommandResult.Success( + $"{prompt} Use /claimlink confirm to proceed, or /claimlink cancel to cancel." + ); } private static TextCommandResult? TryResolveGroup(string groupName, out PlayerGroup group) { group = ClaimLinkModSystem.Groups.GetPlayerGroupByName(groupName)!; - return group == null ? TextCommandResult.Error($"No group named '{groupName}' exists.") : null; + return group == null + ? TextCommandResult.Error($"No group named '{groupName}' exists.") + : null; } private static TextCommandResult? TryResolveClaimLink(PlayerGroup group, out ClaimLink link) { link = ClaimLinkModSystem.Registry.Get(group.Uid)!; - return link == null ? TextCommandResult.Error($"'{group.Name}' is not a claim link.") : null; + return link == null + ? TextCommandResult.Error($"'{group.Name}' is not a claim link.") + : null; } private static TextCommandResult? RequireMember(IPlayer player, PlayerGroup group) { - return player.GetGroup(group.Uid) == null ? TextCommandResult.Error($"You are not a member of '{group.Name}'.") : null; + return player.GetGroup(group.Uid) == null + ? TextCommandResult.Error($"'{player.PlayerName}' is not a member of '{group.Name}'.") + : null; } private static TextCommandResult? RequireOwner(IPlayer player, PlayerGroup group) { - return group.OwnerUID != player.PlayerUID ? TextCommandResult.Error($"You do not own '{group.Name}'.") : null; + return group.OwnerUID != player.PlayerUID + ? TextCommandResult.Error($"You do not own '{group.Name}'.") + : null; } private static TextCommandResult? RequireOpOrOwner(IPlayer player, PlayerGroup group) @@ -168,20 +252,26 @@ public static class ClaimLinkChatCommand string playerUid = args.Caller.Player.PlayerUID; TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); - if (err != null) return err; + if (err != null) + return err; err = RequireOwner(args.Caller.Player, group); - if (err != null) return err; + if (err != null) + return err; if (ClaimLinkModSystem.Registry.Get(group.Uid) != null) return TextCommandResult.Error($"'{groupName}' is already a claim link."); int groupId = group.Uid; - return Stage(playerUid, $"'{groupName}' will become a claim link.", () => - { - ClaimLinkModSystem.Registry.Add(new ClaimLink { GroupId = groupId }); - return TextCommandResult.Success($"'{groupName}' is now a claim link."); - }); + return Stage( + playerUid, + $"'{groupName}' will become a claim link.", + () => + { + ClaimLinkModSystem.Registry.Add(new ClaimLink { GroupId = groupId }); + return TextCommandResult.Success($"'{groupName}' is now a claim link."); + } + ); } public static TextCommandResult Confirm(TextCommandCallingArgs args) @@ -211,14 +301,17 @@ public static class ClaimLinkChatCommand int claimIndex = (int)args[1]; TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); - if (err != null) return err; + if (err != null) + return err; err = TryResolveClaimLink(group, out ClaimLink link); - if (err != null) return err; + if (err != null) + return err; IPlayer player = args.Caller.Player; err = RequireMember(player, group); - if (err != null) return err; + if (err != null) + return err; string playerUid = player.PlayerUID; @@ -229,20 +322,24 @@ public static class ClaimLinkChatCommand return TextCommandResult.Error("That claim is already part of a claim link."); string claimDesc = DescribeClaim(playerUid, claimIndex); - return Stage(playerUid, $"{claimDesc} will be linked into '{groupName}'.", () => - { - ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == playerUid); - if (member == null) + return Stage( + playerUid, + $"{claimDesc} will be linked into '{groupName}'.", + () => { - member = new ClaimLinkMember { OwnerPlayerUid = playerUid }; - link.Members.Add(member); + ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == playerUid); + if (member == null) + { + member = new ClaimLinkMember { OwnerPlayerUid = playerUid }; + link.Members.Add(member); + } + + member.LocalClaimIndices.Add(claimIndex); + ClaimLinkModSystem.Registry.Save(); + + return TextCommandResult.Success($"Linked {claimDesc} to '{groupName}'."); } - - member.LocalClaimIndices.Add(claimIndex); - ClaimLinkModSystem.Registry.Save(); - - return TextCommandResult.Success($"Linked {claimDesc} to '{groupName}'."); - }); + ); } public static TextCommandResult Unlink(TextCommandCallingArgs args) @@ -252,21 +349,27 @@ public static class ClaimLinkChatCommand ClaimLink? link = ClaimLinkModSystem.Registry.FindLinkContaining(playerUid, claimIndex); if (link == null) - return TextCommandResult.Error($"Claim {claimIndex} is not linked into any claim link by you."); + return TextCommandResult.Error( + $"Claim {claimIndex} is not linked into any claim link by you." + ); string groupName = ClaimLinkModSystem.Groups.PlayerGroupsById[link.GroupId].Name; string claimDesc = DescribeClaim(playerUid, claimIndex); - return Stage(playerUid, $"{claimDesc} will be unlinked from '{groupName}'.", () => - { - ClaimLinkMember member = link.Members.Find(m => m.OwnerPlayerUid == playerUid)!; - member.LocalClaimIndices.Remove(claimIndex); - if (member.LocalClaimIndices.Count == 0) - link.Members.Remove(member); + return Stage( + playerUid, + $"{claimDesc} will be unlinked from '{groupName}'.", + () => + { + ClaimLinkMember member = link.Members.Find(m => m.OwnerPlayerUid == playerUid)!; + member.LocalClaimIndices.Remove(claimIndex); + if (member.LocalClaimIndices.Count == 0) + link.Members.Remove(member); - ClaimLinkModSystem.Registry.Save(); - return TextCommandResult.Success($"Unlinked {claimDesc} from '{groupName}'."); - }); + ClaimLinkModSystem.Registry.Save(); + return TextCommandResult.Success($"Unlinked {claimDesc} from '{groupName}'."); + } + ); } public static TextCommandResult Kick(TextCommandCallingArgs args) @@ -275,29 +378,40 @@ public static class ClaimLinkChatCommand IPlayer target = (IPlayer)args[1]; TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); - if (err != null) return err; + if (err != null) + return err; err = TryResolveClaimLink(group, out ClaimLink link); - if (err != null) return err; + if (err != null) + return err; err = RequireOpOrOwner(args.Caller.Player, group); - if (err != null) return err; + if (err != null) + return err; string targetUid = target.PlayerUID; string playerUid = args.Caller.Player.PlayerUID; ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == targetUid); if (member == null) - return TextCommandResult.Error($"{target.PlayerName} has no claims linked in '{groupName}'."); + return TextCommandResult.Error( + $"{target.PlayerName} has no claims linked in '{groupName}'." + ); - return Stage(playerUid, $"All of {target.PlayerName}'s claims will be unlinked from '{groupName}'.", () => - { - link.Members.Remove(member); - RemovePending(targetUid); - ClaimLinkModSystem.Registry.Save(); + return Stage( + playerUid, + $"All of {target.PlayerName}'s claims will be unlinked from '{groupName}'.", + () => + { + link.Members.Remove(member); + RemovePending(targetUid); + ClaimLinkModSystem.Registry.Save(); - return TextCommandResult.Success($"Unlinked all claims of {target.PlayerName} from '{groupName}'."); - }); + return TextCommandResult.Success( + $"Unlinked all claims of {target.PlayerName} from '{groupName}'." + ); + } + ); } public static TextCommandResult Delete(TextCommandCallingArgs args) @@ -306,24 +420,32 @@ public static class ClaimLinkChatCommand string playerUid = args.Caller.Player.PlayerUID; TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); - if (err != null) return err; + if (err != null) + return err; err = TryResolveClaimLink(group, out ClaimLink link); - if (err != null) return err; + if (err != null) + return err; err = RequireOwner(args.Caller.Player, group); - if (err != null) return err; + if (err != null) + return err; int groupId = group.Uid; - return Stage(playerUid, $"'{groupName}' will be deleted as a claim link.", () => - { - foreach (ClaimLinkMember member in link.Members) - RemovePending(member.OwnerPlayerUid); + return Stage( + playerUid, + $"'{groupName}' will be deleted as a claim link.", + () => + { + foreach (ClaimLinkMember member in link.Members) + RemovePending(member.OwnerPlayerUid); - ClaimLinkModSystem.Registry.Remove(groupId); - return TextCommandResult.Success($"'{groupName}' is no longer a claim link."); - }); + ClaimLinkModSystem.Registry.Remove(groupId); + return TextCommandResult.Success($"'{groupName}' is no longer a claim link."); + } + ); } + public static TextCommandResult TransferOwnership(TextCommandCallingArgs args) { string groupName = (string)args[0]; @@ -332,22 +454,30 @@ public static class ClaimLinkChatCommand string playerUid = player.PlayerUID; TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); - if (err != null) return err; + if (err != null) + return err; err = TryResolveClaimLink(group, out _); - if (err != null) return err; + if (err != null) + return err; err = RequireOwner(player, group); - if (err != null) return err; + if (err != null) + return err; err = RequireMember(target, group); - if (err != null) return err; + if (err != null) + return err; err = RequireNotAlreadyOwner(group, target); - if (err != null) return err; + if (err != null) + return err; - return Stage(playerUid, $"Ownership of '{groupName}' will be transferred to {target.PlayerName}.", - () => ExecuteTransferOwnership(group, target)); + return Stage( + playerUid, + $"Ownership of '{groupName}' will be transferred to {target.PlayerName}.", + () => ExecuteTransferOwnership(group, target) + ); } private static TextCommandResult? RequireNotAlreadyOwner(PlayerGroup group, IPlayer target) @@ -367,13 +497,15 @@ public static class ClaimLinkChatCommand // TODO remove debug logging once transfer ownership persistence is confirmed tested ClaimLinkModSystem.Logger.Notification( - $"claimlink: transferownership '{group.Name}' (uid {group.Uid}) BEFORE OwnerUID={oldOwnerUid} " + - $"(old owner {oldOwner?.PlayerName ?? oldOwnerUid} level={oldOwnerLevel}, new owner {target.PlayerName}/{targetUid} level={targetLevel})"); + $"claimlink: transferownership '{group.Name}' (uid {group.Uid}) BEFORE OwnerUID={oldOwnerUid} " + + $"(old owner {oldOwner?.PlayerName ?? oldOwnerUid} level={oldOwnerLevel}, new owner {target.PlayerName}/{targetUid} level={targetLevel})" + ); group.OwnerUID = targetUid; ClaimLinkModSystem.Logger.Notification( - $"claimlink: transferownership '{group.Name}' (uid {group.Uid}) AFTER OwnerUID={group.OwnerUID}"); + $"claimlink: transferownership '{group.Name}' (uid {group.Uid}) AFTER OwnerUID={group.OwnerUID}" + ); return TextCommandResult.Success($"'{group.Name}' is now owned by {target.PlayerName}."); } @@ -383,18 +515,20 @@ public static class ClaimLinkChatCommand string groupName = (string)args[0]; TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); - if (err != null) return err; + if (err != null) + return err; err = TryResolveClaimLink(group, out ClaimLink link); - if (err != null) return err; + if (err != null) + return err; return TextCommandResult.Success(FormatInfo(groupName, link)); } public static TextCommandResult List(TextCommandCallingArgs args) { - List links = ClaimLinkModSystem.Registry.All - .OrderByDescending(l => l.Members.Count) + List links = ClaimLinkModSystem + .Registry.All.OrderByDescending(l => l.Members.Count) .ToList(); if (links.Count == 0) @@ -406,7 +540,9 @@ public static class ClaimLinkChatCommand foreach (ClaimLink link in links) { string groupName = ClaimLinkModSystem.Groups.PlayerGroupsById[link.GroupId].Name; - sb.AppendLine($" {groupName}: {link.Members.Count} member{(link.Members.Count == 1 ? "" : "s")}"); + sb.AppendLine( + $" {groupName}: {link.Members.Count} member{(link.Members.Count == 1 ? "" : "s")}" + ); } return TextCommandResult.Success(sb.ToString()); @@ -414,7 +550,15 @@ public static class ClaimLinkChatCommand private static string DescribeClaim(string ownerPlayerUid, int localIndex) { - if (!ClaimLinkModSystem.TryResolveOwnedClaim(ownerPlayerUid, localIndex, out _, out LandClaim? claim) || claim == null) + if ( + !ClaimLinkModSystem.TryResolveOwnedClaim( + ownerPlayerUid, + localIndex, + out _, + out LandClaim? claim + ) + || claim == null + ) return $"claim {localIndex}"; return string.IsNullOrEmpty(claim.Description) ? $"claim {localIndex}" : claim.Description; @@ -423,39 +567,57 @@ public static class ClaimLinkChatCommand private static string FormatInfo(string groupName, ClaimLink link) { StringBuilder sb = new(); - sb.AppendLine($"Claim link '{groupName}' ({link.Members.Count} member{(link.Members.Count == 1 ? "" : "s")}):"); + sb.AppendLine( + $"Claim link '{groupName}' ({link.Members.Count} member{(link.Members.Count == 1 ? "" : "s")}):" + ); foreach (ClaimLinkMember member in link.Members) { - string name = ClaimLinkModSystem.World.PlayerByUid(member.OwnerPlayerUid)?.PlayerName ?? member.OwnerPlayerUid; - IEnumerable claims = member.LocalClaimIndices.Select(i => DescribeClaim(member.OwnerPlayerUid, i)); + string name = + ClaimLinkModSystem.World.PlayerByUid(member.OwnerPlayerUid)?.PlayerName + ?? member.OwnerPlayerUid; + IEnumerable claims = member.LocalClaimIndices.Select(i => + DescribeClaim(member.OwnerPlayerUid, i) + ); sb.AppendLine($" {name}: claims [{string.Join(", ", claims)}]"); } return sb.ToString(); } - public static TextCommandResult AdminDelete(TextCommandCallingArgs args) => TextCommandResult.Success("stub: claimlink admin delete"); - public static TextCommandResult AdminUnlink(TextCommandCallingArgs args) => TextCommandResult.Success("stub: claimlink admin unlink"); - public static TextCommandResult AdminKick(TextCommandCallingArgs args) => TextCommandResult.Success("stub: claimlink admin kick"); + public static TextCommandResult AdminDelete(TextCommandCallingArgs args) => + TextCommandResult.Success("stub: claimlink admin delete"); + + public static TextCommandResult AdminUnlink(TextCommandCallingArgs args) => + TextCommandResult.Success("stub: claimlink admin unlink"); + + public static TextCommandResult AdminKick(TextCommandCallingArgs args) => + TextCommandResult.Success("stub: claimlink admin kick"); + public static TextCommandResult AdminTransferOwnership(TextCommandCallingArgs args) { string groupName = (string)args[0]; IPlayer target = (IPlayer)args[1]; TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); - if (err != null) return err; + if (err != null) + return err; err = TryResolveClaimLink(group, out _); - if (err != null) return err; + if (err != null) + return err; err = RequireMember(target, group); - if (err != null) return err; + if (err != null) + return err; err = RequireNotAlreadyOwner(group, target); - if (err != null) return err; + if (err != null) + return err; return ExecuteTransferOwnership(group, target); } - public static TextCommandResult AdminInfo(TextCommandCallingArgs args) => TextCommandResult.Success("stub: claimlink admin info"); + + public static TextCommandResult AdminInfo(TextCommandCallingArgs args) => + TextCommandResult.Success("stub: claimlink admin info"); }