feat: open info to any player, add claimlink list, bump to 0.0.6

Modified command messages.
This commit is contained in:
2026-07-12 21:19:24 +02:00
parent acbc45483e
commit 979f02e7f8
3 changed files with 68 additions and 33 deletions
+65 -32
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using Vintagestory.API.Common; using Vintagestory.API.Common;
using Vintagestory.API.Server; using Vintagestory.API.Server;
@@ -51,61 +52,64 @@ public static class ClaimLinkChatCommand
var root = api.ChatCommands.Create("claimlink") var root = api.ChatCommands.Create("claimlink")
.WithAlias("clink", "claiml", "cl") .WithAlias("clink", "claiml", "cl")
.WithDescription("Link vanilla land claims into shared, derived protective territory.") .WithDescription("Link vanilla land claims together via group.")
.RequiresPrivilege(Privilege.chat); .RequiresPrivilege(Privilege.chat);
CommandSpec[] playerCommands = CommandSpec[] playerCommands =
{ {
new(new[] { "new", "n" }, "Promote a group you own into a claim link (requires confirm).", new(new[] { "new", "n" }, "Promote a group you own to a claim link.",
new ICommandArgumentParser[] { p.Word("groupname") }, true, New), new ICommandArgumentParser[] { p.Word("groupname") }, true, New),
new(new[] { "link", "l" }, "Link a claim you own into a claim link (requires confirm).", 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 ICommandArgumentParser[] { p.Word("groupname"), p.IntRange("claim", 0, 999) }, true, Link),
new(new[] { "confirm", "c" }, "Confirm your pending action.", new(new[] { "confirm", "c" }, "Confirm pending action.",
Array.Empty<ICommandArgumentParser>(), true, Confirm), Array.Empty<ICommandArgumentParser>(), true, Confirm),
new(new[] { "cancel" }, "Discard your pending action.", new(new[] { "cancel" }, "Cancel pending action.",
Array.Empty<ICommandArgumentParser>(), true, Cancel), Array.Empty<ICommandArgumentParser>(), true, Cancel),
new(new[] { "unlink", "ul" }, "Remove a claim you own from its claim link (requires confirm).", new(new[] { "unlink", "ul" }, "Remove a claim you own from its claim link.",
new ICommandArgumentParser[] { p.IntRange("claim", 0, 999) }, true, Unlink), new ICommandArgumentParser[] { p.IntRange("claim", 0, 999) }, true, Unlink),
new(new[] { "kick" }, "Force-unlink all of a player's claims from the link (Owner/Op, requires confirm).", 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 ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, true, Kick),
new(new[] { "delete" }, "Delete the claim link entirely (Owner).", new(new[] { "delete" }, "Delete a claim link.",
new ICommandArgumentParser[] { p.Word("groupname") }, true, Delete), new ICommandArgumentParser[] { p.Word("groupname") }, true, Delete),
new(new[] { "transferownership", "transfer", "to" }, "Transfer ownership of the claim link to another player (Owner).", 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 ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, true, TransferOwnership),
new(new[] { "info", "i" }, "Show a claim link's linked claims, members, and territory (Owner/Op or admin).", new(new[] { "info", "i" }, "Show a claim link's members and linked claims.",
new ICommandArgumentParser[] { p.Word("groupname") }, true, Info), new ICommandArgumentParser[] { p.Word("groupname") }, true, Info),
new(new[] { "list", "ls" }, "List all claim links.",
Array.Empty<ICommandArgumentParser>(), true, List),
}; };
foreach (var spec in playerCommands) foreach (var spec in playerCommands)
BuildSubCommand(root, spec); BuildSubCommand(root, spec);
var admin = root.BeginSubCommands("admin", "a") var admin = root.BeginSubCommands("admin", "a")
.WithDescription("Admin management for any claim link (op or console).") .WithDescription("Admin commands for claim link.")
.RequiresPrivilege(Privilege.controlserver); .RequiresPrivilege(Privilege.controlserver);
CommandSpec[] adminCommands = CommandSpec[] adminCommands =
{ {
new(new[] { "delete", "del" }, "Delete any claim link entirely (claims and group untouched).", new(new[] { "delete", "del" }, "Delete a claim link.",
new ICommandArgumentParser[] { p.Word("groupname") }, false, AdminDelete), new ICommandArgumentParser[] { p.Word("groupname") }, false, AdminDelete),
new(new[] { "unlink" }, "Force a single claim out of its link by player and claim (claim untouched).", new(new[] { "unlink" }, "Unlink a claim.",
new ICommandArgumentParser[] { p.OnlinePlayer("playername"), p.Word("claim") }, false, AdminUnlink), new ICommandArgumentParser[] { p.OnlinePlayer("playername"), p.Word("claim") }, false, AdminUnlink),
new(new[] { "kick" }, "Force-unlink all of a player's claims from a link (claims untouched).", new(new[] { "kick" }, "Force-unlink all of a player's claims.",
new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, false, AdminKick), 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(new[] { "transferownership", "transfer", "to" }, "Transfer ownership of any claim link to another player.",
new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, false, AdminTransferOwnership), new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, false, AdminTransferOwnership),
new(new[] { "info", "i" }, "Show any claim link's linked claims, members, and territory.", new(new[] { "info", "i" }, "Show a claim link's members and linked claims.",
new ICommandArgumentParser[] { p.Word("groupname") }, false, AdminInfo), new ICommandArgumentParser[] { p.Word("groupname") }, false, AdminInfo),
}; };
@@ -122,7 +126,7 @@ public static class ClaimLinkChatCommand
private static TextCommandResult Stage(string playerUid, string prompt, Func<TextCommandResult> action) private static TextCommandResult Stage(string playerUid, string prompt, Func<TextCommandResult> action)
{ {
pendingActions[playerUid] = action; pendingActions[playerUid] = action;
return TextCommandResult.Success($"{prompt} Use /claimlink confirm to proceed, or /claimlink cancel to discard."); return TextCommandResult.Success($"{prompt} Use /claimlink confirm to proceed, or /claimlink cancel to cancel.");
} }
private static TextCommandResult? TryResolveGroup(string groupName, out PlayerGroup group) private static TextCommandResult? TryResolveGroup(string groupName, out PlayerGroup group)
@@ -144,7 +148,7 @@ public static class ClaimLinkChatCommand
private static TextCommandResult? RequireOwner(IPlayer player, PlayerGroup group) private static TextCommandResult? RequireOwner(IPlayer player, PlayerGroup group)
{ {
return group.OwnerUID != player.PlayerUID ? TextCommandResult.Error($"You do not own the group '{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) private static TextCommandResult? RequireOpOrOwner(IPlayer player, PlayerGroup group)
@@ -170,7 +174,7 @@ public static class ClaimLinkChatCommand
return TextCommandResult.Error($"'{groupName}' is already a claim link."); return TextCommandResult.Error($"'{groupName}' is already a claim link.");
int groupId = group.Uid; int groupId = group.Uid;
return Stage(playerUid, $"'{groupName}' will be promoted into a claim link.", () => return Stage(playerUid, $"'{groupName}' will become a claim link.", () =>
{ {
ClaimLinkModSystem.Registry.Add(new ClaimLink { GroupId = groupId }); ClaimLinkModSystem.Registry.Add(new ClaimLink { GroupId = groupId });
return TextCommandResult.Success($"'{groupName}' is now a claim link."); return TextCommandResult.Success($"'{groupName}' is now a claim link.");
@@ -195,7 +199,7 @@ public static class ClaimLinkChatCommand
if (!pendingActions.Remove(playerUid)) if (!pendingActions.Remove(playerUid))
return TextCommandResult.Error("You do not have a pending action."); return TextCommandResult.Error("You do not have a pending action.");
return TextCommandResult.Success("Pending action discarded."); return TextCommandResult.Success("Pending action cancelled.");
} }
public static TextCommandResult Link(TextCommandCallingArgs args) public static TextCommandResult Link(TextCommandCallingArgs args)
@@ -216,12 +220,13 @@ public static class ClaimLinkChatCommand
string playerUid = player.PlayerUID; string playerUid = player.PlayerUID;
if (!ClaimLinkModSystem.TryResolveOwnedClaim(playerUid, claimIndex, out _, out _)) if (!ClaimLinkModSystem.TryResolveOwnedClaim(playerUid, claimIndex, out _, out _))
return TextCommandResult.Error("You do not own a claim with that index."); return TextCommandResult.Error("You do not own that claim.");
if (ClaimLinkModSystem.Registry.IsClaimLinked(playerUid, claimIndex)) if (ClaimLinkModSystem.Registry.IsClaimLinked(playerUid, claimIndex))
return TextCommandResult.Error("That claim is already part of a claim link."); return TextCommandResult.Error("That claim is already part of a claim link.");
return Stage(playerUid, $"Claim {claimIndex} will be linked into '{groupName}'.", () => string claimDesc = DescribeClaim(playerUid, claimIndex);
return Stage(playerUid, $"{claimDesc} will be linked into '{groupName}'.", () =>
{ {
ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == playerUid); ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == playerUid);
if (member == null) if (member == null)
@@ -233,7 +238,7 @@ public static class ClaimLinkChatCommand
member.LocalClaimIndices.Add(claimIndex); member.LocalClaimIndices.Add(claimIndex);
ClaimLinkModSystem.Registry.Save(); ClaimLinkModSystem.Registry.Save();
return TextCommandResult.Success($"Linked claim {claimIndex} into '{groupName}'."); return TextCommandResult.Success($"Linked {claimDesc} to '{groupName}'.");
}); });
} }
@@ -247,8 +252,9 @@ public static class ClaimLinkChatCommand
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 groupName = ClaimLinkModSystem.Groups.PlayerGroupsById[link.GroupId].Name;
string claimDesc = DescribeClaim(playerUid, claimIndex);
return Stage(playerUid, $"Claim {claimIndex} will be unlinked from '{groupName}'.", () => return Stage(playerUid, $"{claimDesc} will be unlinked from '{groupName}'.", () =>
{ {
ClaimLinkMember member = link.Members.Find(m => m.OwnerPlayerUid == playerUid)!; ClaimLinkMember member = link.Members.Find(m => m.OwnerPlayerUid == playerUid)!;
member.LocalClaimIndices.Remove(claimIndex); member.LocalClaimIndices.Remove(claimIndex);
@@ -256,7 +262,7 @@ public static class ClaimLinkChatCommand
link.Members.Remove(member); link.Members.Remove(member);
ClaimLinkModSystem.Registry.Save(); ClaimLinkModSystem.Registry.Save();
return TextCommandResult.Success($"Unlinked claim {claimIndex} from '{groupName}'."); return TextCommandResult.Success($"Unlinked {claimDesc} from '{groupName}'.");
}); });
} }
@@ -279,15 +285,15 @@ public static class ClaimLinkChatCommand
ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == targetUid); ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == targetUid);
if (member == null) if (member == null)
return TextCommandResult.Error($"{target.PlayerName} has no claims linked into '{groupName}'."); return TextCommandResult.Error($"{target.PlayerName} has no claims linked in '{groupName}'.");
return Stage(playerUid, $"All of {target.PlayerName}'s claims will be kicked from '{groupName}'.", () => return Stage(playerUid, $"All of {target.PlayerName}'s claims will be unlinked from '{groupName}'.", () =>
{ {
link.Members.Remove(member); link.Members.Remove(member);
RemovePending(targetUid); RemovePending(targetUid);
ClaimLinkModSystem.Registry.Save(); ClaimLinkModSystem.Registry.Save();
return TextCommandResult.Success($"Kicked all of {target.PlayerName}'s claims from '{groupName}'."); return TextCommandResult.Success($"Unlinked all claims of {target.PlayerName} from '{groupName}'.");
}); });
} }
@@ -327,21 +333,48 @@ public static class ClaimLinkChatCommand
err = TryResolveClaimLink(group, out ClaimLink link); 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;
return TextCommandResult.Success(FormatInfo(groupName, link)); return TextCommandResult.Success(FormatInfo(groupName, link));
} }
public static TextCommandResult List(TextCommandCallingArgs args)
{
List<ClaimLink> links = ClaimLinkModSystem.Registry.All
.OrderByDescending(l => l.Members.Count)
.ToList();
if (links.Count == 0)
return TextCommandResult.Success("There are no claim links.");
StringBuilder sb = new();
sb.AppendLine($"Claim links ({links.Count}):");
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")}");
}
return TextCommandResult.Success(sb.ToString());
}
private static string DescribeClaim(string ownerPlayerUid, int localIndex)
{
if (!ClaimLinkModSystem.TryResolveOwnedClaim(ownerPlayerUid, localIndex, out _, out LandClaim? claim) || claim == null)
return $"claim {localIndex}";
return string.IsNullOrEmpty(claim.Description) ? $"claim {localIndex}" : claim.Description;
}
private static string FormatInfo(string groupName, ClaimLink link) private static string FormatInfo(string groupName, ClaimLink link)
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine($"Claim link '{groupName}' ({link.Members.Count} member(s)):"); sb.AppendLine($"Claim link '{groupName}' ({link.Members.Count} member{(link.Members.Count == 1 ? "" : "s")}):");
foreach (ClaimLinkMember member in link.Members) foreach (ClaimLinkMember member in link.Members)
{ {
string name = ClaimLinkModSystem.World.PlayerByUid(member.OwnerPlayerUid)?.PlayerName ?? member.OwnerPlayerUid; string name = ClaimLinkModSystem.World.PlayerByUid(member.OwnerPlayerUid)?.PlayerName ?? member.OwnerPlayerUid;
sb.AppendLine($" {name}: claims [{string.Join(", ", member.LocalClaimIndices)}]"); IEnumerable<string> claims = member.LocalClaimIndices.Select(i => DescribeClaim(member.OwnerPlayerUid, i));
sb.AppendLine($" {name}: claims [{string.Join(", ", claims)}]");
} }
return sb.ToString(); return sb.ToString();
+2
View File
@@ -32,6 +32,8 @@ public class ClaimLinkRegistry
public ClaimLink? Get(int groupId) => byGroupId.TryGetValue(groupId, out ClaimLink? link) ? link : null; public ClaimLink? Get(int groupId) => byGroupId.TryGetValue(groupId, out ClaimLink? link) ? link : null;
public IReadOnlyCollection<ClaimLink> All => byGroupId.Values;
public void Add(ClaimLink link) public void Add(ClaimLink link)
{ {
byGroupId[link.GroupId] = link; byGroupId[link.GroupId] = link;
+1 -1
View File
@@ -8,7 +8,7 @@
"anth64" "anth64"
], ],
"description": "Link claims together with groups.", "description": "Link claims together with groups.",
"version": "0.0.5", "version": "0.0.6",
"dependencies": { "dependencies": {
"game": "1.22.3", "game": "1.22.3",
"commandhook": "2.1.0" "commandhook": "2.1.0"