refactor: replace pending-link buffer with generic pending-action confirm/cancel
new/link/unlink/kick now stage a validated action and require confirm to execute, instead of new alone gating a multi-edit draft. Bump to 0.0.5.
This commit is contained in:
@@ -56,22 +56,22 @@ public static class ClaimLinkChatCommand
|
|||||||
|
|
||||||
CommandSpec[] playerCommands =
|
CommandSpec[] playerCommands =
|
||||||
{
|
{
|
||||||
new(new[] { "new", "n" }, "Promote a group you own into a claim link (enters pending state).",
|
new(new[] { "new", "n" }, "Promote a group you own into a claim link (requires confirm).",
|
||||||
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.",
|
new(new[] { "link", "l" }, "Link a claim you own into a claim link (requires confirm).",
|
||||||
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" }, "Commit your pending claim link.",
|
new(new[] { "confirm", "c" }, "Confirm your pending action.",
|
||||||
Array.Empty<ICommandArgumentParser>(), true, Confirm),
|
Array.Empty<ICommandArgumentParser>(), true, Confirm),
|
||||||
|
|
||||||
new(new[] { "cancel" }, "Discard your pending claim link.",
|
new(new[] { "cancel" }, "Discard your 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.",
|
new(new[] { "unlink", "ul" }, "Remove a claim you own from its claim link (requires confirm).",
|
||||||
new ICommandArgumentParser[] { p.Word("groupname"), p.IntRange("claim", 0, 999) }, true, Unlink),
|
new ICommandArgumentParser[] { p.Word("groupname"), p.IntRange("claim", 0, 999) }, true, Unlink),
|
||||||
|
|
||||||
new(new[] { "kick" }, "Force-unlink all of a player's claims from the link (Owner/Op).",
|
new(new[] { "kick" }, "Force-unlink all of a player's claims from the link (Owner/Op, requires confirm).",
|
||||||
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 the claim link entirely (Owner).",
|
||||||
@@ -115,9 +115,15 @@ public static class ClaimLinkChatCommand
|
|||||||
admin.EndSubCommand();
|
admin.EndSubCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<string, ClaimLink> pendingLinks = new();
|
private static readonly Dictionary<string, Func<TextCommandResult>> pendingActions = new();
|
||||||
|
|
||||||
internal static void RemovePending(string playerUid) => pendingLinks.Remove(playerUid);
|
internal static void RemovePending(string playerUid) => pendingActions.Remove(playerUid);
|
||||||
|
|
||||||
|
private static TextCommandResult Stage(string playerUid, string prompt, Func<TextCommandResult> action)
|
||||||
|
{
|
||||||
|
pendingActions[playerUid] = action;
|
||||||
|
return TextCommandResult.Success($"{prompt} Use /claimlink confirm to proceed, or /claimlink cancel to discard.");
|
||||||
|
}
|
||||||
|
|
||||||
private static TextCommandResult? TryResolveGroup(string groupName, out PlayerGroup group)
|
private static TextCommandResult? TryResolveGroup(string groupName, out PlayerGroup group)
|
||||||
{
|
{
|
||||||
@@ -131,19 +137,6 @@ public static class ClaimLinkChatCommand
|
|||||||
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? TryResolveClaimLinkForEdit(string playerUid, PlayerGroup group, out ClaimLink link, out bool isPending)
|
|
||||||
{
|
|
||||||
if (pendingLinks.TryGetValue(playerUid, out ClaimLink? pending) && pending.GroupId == group.Uid)
|
|
||||||
{
|
|
||||||
link = pending;
|
|
||||||
isPending = true;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
isPending = false;
|
|
||||||
return TryResolveClaimLink(group, out link);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TextCommandResult? RequireMember(IPlayer player, PlayerGroup group)
|
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($"You are not a member of '{group.Name}'.") : null;
|
||||||
@@ -173,37 +166,36 @@ public static class ClaimLinkChatCommand
|
|||||||
err = RequireOwner(args.Caller.Player, group);
|
err = RequireOwner(args.Caller.Player, group);
|
||||||
if (err != null) return err;
|
if (err != null) return err;
|
||||||
|
|
||||||
if (pendingLinks.ContainsKey(playerUid))
|
|
||||||
return TextCommandResult.Error("You already have a pending claim link. Confirm or cancel it first.");
|
|
||||||
|
|
||||||
if (ClaimLinkModSystem.Registry.Get(group.Uid) != null)
|
if (ClaimLinkModSystem.Registry.Get(group.Uid) != null)
|
||||||
return TextCommandResult.Error($"'{groupName}' is already a claim link.");
|
return TextCommandResult.Error($"'{groupName}' is already a claim link.");
|
||||||
|
|
||||||
pendingLinks[playerUid] = new ClaimLink { GroupId = group.Uid };
|
int groupId = group.Uid;
|
||||||
return TextCommandResult.Success($"'{groupName}' is now a pending claim link. Use /claimlink link to add claims, then confirm.");
|
return Stage(playerUid, $"'{groupName}' will be promoted into 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)
|
public static TextCommandResult Confirm(TextCommandCallingArgs args)
|
||||||
{
|
{
|
||||||
string playerUid = args.Caller.Player.PlayerUID;
|
string playerUid = args.Caller.Player.PlayerUID;
|
||||||
|
|
||||||
if (!pendingLinks.TryGetValue(playerUid, out ClaimLink? pending))
|
if (!pendingActions.TryGetValue(playerUid, out Func<TextCommandResult>? action))
|
||||||
return TextCommandResult.Error("You do not have a pending claim link.");
|
return TextCommandResult.Error("You do not have a pending action.");
|
||||||
|
|
||||||
ClaimLinkModSystem.Registry.Add(pending);
|
pendingActions.Remove(playerUid);
|
||||||
pendingLinks.Remove(playerUid);
|
return action();
|
||||||
|
|
||||||
return TextCommandResult.Success("Claim link confirmed.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextCommandResult Cancel(TextCommandCallingArgs args)
|
public static TextCommandResult Cancel(TextCommandCallingArgs args)
|
||||||
{
|
{
|
||||||
string playerUid = args.Caller.Player.PlayerUID;
|
string playerUid = args.Caller.Player.PlayerUID;
|
||||||
|
|
||||||
if (!pendingLinks.Remove(playerUid))
|
if (!pendingActions.Remove(playerUid))
|
||||||
return TextCommandResult.Error("You do not have a pending claim link.");
|
return TextCommandResult.Error("You do not have a pending action.");
|
||||||
|
|
||||||
return TextCommandResult.Success("Pending claim link discarded.");
|
return TextCommandResult.Success("Pending action discarded.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextCommandResult Link(TextCommandCallingArgs args)
|
public static TextCommandResult Link(TextCommandCallingArgs args)
|
||||||
@@ -214,21 +206,23 @@ public static class ClaimLinkChatCommand
|
|||||||
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group);
|
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group);
|
||||||
if (err != null) return err;
|
if (err != null) return err;
|
||||||
|
|
||||||
IPlayer player = args.Caller.Player;
|
err = TryResolveClaimLink(group, out ClaimLink link);
|
||||||
string playerUid = player.PlayerUID;
|
|
||||||
|
|
||||||
err = TryResolveClaimLinkForEdit(playerUid, group, out ClaimLink link, out bool isPending);
|
|
||||||
if (err != null) return err;
|
if (err != null) return err;
|
||||||
|
|
||||||
|
IPlayer player = args.Caller.Player;
|
||||||
err = RequireMember(player, group);
|
err = RequireMember(player, group);
|
||||||
if (err != null) return err;
|
if (err != null) return err;
|
||||||
|
|
||||||
|
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 a claim with that index.");
|
||||||
|
|
||||||
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}'.", () =>
|
||||||
|
{
|
||||||
ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == playerUid);
|
ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == playerUid);
|
||||||
if (member == null)
|
if (member == null)
|
||||||
{
|
{
|
||||||
@@ -237,10 +231,10 @@ public static class ClaimLinkChatCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
member.LocalClaimIndices.Add(claimIndex);
|
member.LocalClaimIndices.Add(claimIndex);
|
||||||
if (!isPending)
|
|
||||||
ClaimLinkModSystem.Registry.Save();
|
ClaimLinkModSystem.Registry.Save();
|
||||||
|
|
||||||
return TextCommandResult.Success($"Linked claim {claimIndex} into '{groupName}'.");
|
return TextCommandResult.Success($"Linked claim {claimIndex} into '{groupName}'.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextCommandResult Unlink(TextCommandCallingArgs args)
|
public static TextCommandResult Unlink(TextCommandCallingArgs args)
|
||||||
@@ -251,23 +245,26 @@ public static class ClaimLinkChatCommand
|
|||||||
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group);
|
TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group);
|
||||||
if (err != null) return err;
|
if (err != null) return err;
|
||||||
|
|
||||||
string playerUid = args.Caller.Player.PlayerUID;
|
err = TryResolveClaimLink(group, out ClaimLink link);
|
||||||
|
|
||||||
err = TryResolveClaimLinkForEdit(playerUid, group, out ClaimLink link, out bool isPending);
|
|
||||||
if (err != null) return err;
|
if (err != null) return err;
|
||||||
|
|
||||||
|
string playerUid = args.Caller.Player.PlayerUID;
|
||||||
|
|
||||||
ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == playerUid);
|
ClaimLinkMember? member = link.Members.Find(m => m.OwnerPlayerUid == playerUid);
|
||||||
if (member == null || !member.LocalClaimIndices.Remove(claimIndex))
|
if (member == null || !member.LocalClaimIndices.Contains(claimIndex))
|
||||||
return TextCommandResult.Error($"Claim {claimIndex} is not linked into '{groupName}' by you.");
|
return TextCommandResult.Error($"Claim {claimIndex} is not linked into '{groupName}' by you.");
|
||||||
|
|
||||||
|
return Stage(playerUid, $"Claim {claimIndex} will be unlinked from '{groupName}'.", () =>
|
||||||
|
{
|
||||||
|
member.LocalClaimIndices.Remove(claimIndex);
|
||||||
if (member.LocalClaimIndices.Count == 0)
|
if (member.LocalClaimIndices.Count == 0)
|
||||||
link.Members.Remove(member);
|
link.Members.Remove(member);
|
||||||
|
|
||||||
if (!isPending)
|
|
||||||
ClaimLinkModSystem.Registry.Save();
|
ClaimLinkModSystem.Registry.Save();
|
||||||
|
|
||||||
return TextCommandResult.Success($"Unlinked claim {claimIndex} from '{groupName}'.");
|
return TextCommandResult.Success($"Unlinked claim {claimIndex} from '{groupName}'.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextCommandResult Kick(TextCommandCallingArgs args)
|
public static TextCommandResult Kick(TextCommandCallingArgs args)
|
||||||
{
|
{
|
||||||
string groupName = (string)args[0];
|
string groupName = (string)args[0];
|
||||||
@@ -283,18 +280,24 @@ public static class ClaimLinkChatCommand
|
|||||||
if (err != null) return err;
|
if (err != null) return err;
|
||||||
|
|
||||||
string targetUid = target.PlayerUID;
|
string targetUid = target.PlayerUID;
|
||||||
|
string playerUid = args.Caller.Player.PlayerUID;
|
||||||
|
|
||||||
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 into '{groupName}'.");
|
||||||
|
|
||||||
|
return Stage(playerUid, $"All of {target.PlayerName}'s claims will be kicked from '{groupName}'.", () =>
|
||||||
|
{
|
||||||
link.Members.Remove(member);
|
link.Members.Remove(member);
|
||||||
ClaimLinkModSystem.Registry.Save();
|
ClaimLinkModSystem.Registry.Save();
|
||||||
|
|
||||||
return TextCommandResult.Success($"Kicked all of {target.PlayerName}'s claims from '{groupName}'.");
|
return TextCommandResult.Success($"Kicked all of {target.PlayerName}'s claims from '{groupName}'.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextCommandResult Delete(TextCommandCallingArgs args) => TextCommandResult.Success("stub: claimlink delete");
|
public static TextCommandResult Delete(TextCommandCallingArgs args) => TextCommandResult.Success("stub: claimlink delete");
|
||||||
public static TextCommandResult TransferOwnership(TextCommandCallingArgs args) => TextCommandResult.Success("stub: claimlink transferownership");
|
public static TextCommandResult TransferOwnership(TextCommandCallingArgs args) => TextCommandResult.Success("stub: claimlink transferownership");
|
||||||
|
|
||||||
public static TextCommandResult Info(TextCommandCallingArgs args)
|
public static TextCommandResult Info(TextCommandCallingArgs args)
|
||||||
{
|
{
|
||||||
string groupName = (string)args[0];
|
string groupName = (string)args[0];
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"anth64"
|
"anth64"
|
||||||
],
|
],
|
||||||
"description": "Link claims together with groups.",
|
"description": "Link claims together with groups.",
|
||||||
"version": "0.0.4",
|
"version": "0.0.5",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"game": "1.22.3",
|
"game": "1.22.3",
|
||||||
"commandhook": "2.1.0"
|
"commandhook": "2.1.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user