fix: track groupId per pending action for precise cleanup on delete/disband; split group hook into its own listener; self-heal orphaned registry entries in list
This commit is contained in:
@@ -190,20 +190,38 @@ public static class ClaimLinkChatCommand
|
||||
admin.EndSubCommand();
|
||||
}
|
||||
|
||||
internal static readonly Dictionary<string, Func<TextCommandResult>> PendingActions = new();
|
||||
internal static readonly Dictionary<
|
||||
string,
|
||||
(int GroupId, Func<TextCommandResult> Action)
|
||||
> PendingActions = new();
|
||||
|
||||
private static TextCommandResult Stage(
|
||||
string playerUid,
|
||||
int groupId,
|
||||
string prompt,
|
||||
Func<TextCommandResult> action
|
||||
)
|
||||
{
|
||||
PendingActions[playerUid] = action;
|
||||
PendingActions[playerUid] = (groupId, action);
|
||||
return TextCommandResult.Success(
|
||||
$"{prompt} Use /claimlink confirm to proceed, or /claimlink cancel to cancel."
|
||||
);
|
||||
}
|
||||
|
||||
internal static void RemovePendingActionsForGroup(int groupId)
|
||||
{
|
||||
List<string>? toRemove = null;
|
||||
foreach (var (uid, entry) in PendingActions)
|
||||
if (entry.GroupId == groupId)
|
||||
(toRemove ??= new()).Add(uid);
|
||||
|
||||
if (toRemove == null)
|
||||
return;
|
||||
|
||||
foreach (string uid in toRemove)
|
||||
PendingActions.Remove(uid);
|
||||
}
|
||||
|
||||
private static TextCommandResult? TryResolveGroup(string groupName, out PlayerGroup group)
|
||||
{
|
||||
group = ClaimLinkModSystem.Groups.GetPlayerGroupByName(groupName)!;
|
||||
@@ -263,6 +281,7 @@ public static class ClaimLinkChatCommand
|
||||
int groupId = group.Uid;
|
||||
return Stage(
|
||||
playerUid,
|
||||
groupId,
|
||||
$"'{groupName}' will become a claim link.",
|
||||
() =>
|
||||
{
|
||||
@@ -276,11 +295,11 @@ public static class ClaimLinkChatCommand
|
||||
{
|
||||
string playerUid = args.Caller.Player.PlayerUID;
|
||||
|
||||
if (!PendingActions.TryGetValue(playerUid, out Func<TextCommandResult>? action))
|
||||
if (!PendingActions.TryGetValue(playerUid, out var pending))
|
||||
return TextCommandResult.Error("You do not have a pending action.");
|
||||
|
||||
PendingActions.Remove(playerUid);
|
||||
return action();
|
||||
return pending.Action();
|
||||
}
|
||||
|
||||
public static TextCommandResult Cancel(TextCommandCallingArgs args)
|
||||
@@ -323,6 +342,7 @@ public static class ClaimLinkChatCommand
|
||||
string claimDesc = DescribeClaim(playerUid, claimIndex);
|
||||
return Stage(
|
||||
playerUid,
|
||||
groupId,
|
||||
$"{claimDesc} will be linked into '{groupName}'.",
|
||||
() =>
|
||||
{
|
||||
@@ -359,6 +379,7 @@ public static class ClaimLinkChatCommand
|
||||
|
||||
return Stage(
|
||||
playerUid,
|
||||
(int)groupId,
|
||||
$"{claimDesc} will be unlinked from '{groupName}'.",
|
||||
() =>
|
||||
{
|
||||
@@ -395,11 +416,12 @@ public static class ClaimLinkChatCommand
|
||||
|
||||
return Stage(
|
||||
playerUid,
|
||||
group.Uid,
|
||||
$"All of {target.PlayerName}'s claims will be unlinked from '{groupName}'.",
|
||||
() =>
|
||||
{
|
||||
ClaimLinkModSystem.Registry.RemoveAllForPlayerInGroup(targetUid, group.Uid);
|
||||
ClaimLinkCommandListener.PendingLoads.Remove(targetUid);
|
||||
CommandListener.PendingLoads.Remove(targetUid);
|
||||
PendingActions.Remove(targetUid);
|
||||
|
||||
return TextCommandResult.Success(
|
||||
@@ -429,12 +451,11 @@ public static class ClaimLinkChatCommand
|
||||
int groupId = group.Uid;
|
||||
return Stage(
|
||||
playerUid,
|
||||
groupId,
|
||||
$"'{groupName}' will be deleted as a claim link.",
|
||||
() =>
|
||||
{
|
||||
foreach (string uid in ClaimLinkModSystem.Registry.MemberUidsForGroup(groupId))
|
||||
PendingActions.Remove(uid);
|
||||
|
||||
RemovePendingActionsForGroup(groupId);
|
||||
ClaimLinkModSystem.Registry.Remove(groupId);
|
||||
return TextCommandResult.Success($"'{groupName}' is no longer a claim link.");
|
||||
}
|
||||
@@ -476,6 +497,7 @@ public static class ClaimLinkChatCommand
|
||||
|
||||
return Stage(
|
||||
playerUid,
|
||||
group.Uid,
|
||||
$"Ownership of '{groupName}' will be transferred to {target.PlayerName}.",
|
||||
() => ExecuteTransferOwnership(group, target)
|
||||
);
|
||||
@@ -572,16 +594,31 @@ public static class ClaimLinkChatCommand
|
||||
return TextCommandResult.Success("There are no claim links.");
|
||||
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine($"Claim links ({groupIds.Count}):");
|
||||
int shown = 0;
|
||||
|
||||
foreach (int groupId in groupIds)
|
||||
{
|
||||
string groupName = ClaimLinkModSystem.Groups.PlayerGroupsById[groupId].Name;
|
||||
int memberCount = ClaimLinkModSystem.Registry.MemberCountForGroup(groupId);
|
||||
sb.AppendLine($" {groupName}: {memberCount} member{(memberCount == 1 ? "" : "s")}");
|
||||
if (
|
||||
!ClaimLinkModSystem.Groups.PlayerGroupsById.TryGetValue(
|
||||
groupId,
|
||||
out PlayerGroup? group
|
||||
)
|
||||
)
|
||||
{
|
||||
RemovePendingActionsForGroup(groupId);
|
||||
ClaimLinkModSystem.Registry.Remove(groupId);
|
||||
continue;
|
||||
}
|
||||
|
||||
return TextCommandResult.Success(sb.ToString());
|
||||
int memberCount = ClaimLinkModSystem.Registry.MemberCountForGroup(groupId);
|
||||
sb.AppendLine($" {group.Name}: {memberCount} member{(memberCount == 1 ? "" : "s")}");
|
||||
shown++;
|
||||
}
|
||||
|
||||
if (shown == 0)
|
||||
return TextCommandResult.Success("There are no claim links.");
|
||||
|
||||
return TextCommandResult.Success($"Claim links ({shown}):\n{sb}");
|
||||
}
|
||||
|
||||
private static string DescribeClaim(string ownerPlayerUid, int claimIndex)
|
||||
|
||||
@@ -13,7 +13,8 @@ public class ClaimLinkModSystem : ModSystem
|
||||
internal static IGroupManager Groups = null!;
|
||||
internal static IWorldAccessor World = null!;
|
||||
internal static IPlayerDataManager PlayerData = null!;
|
||||
internal ClaimLinkCommandListener? CmdListener;
|
||||
internal CommandListener? CmdListener;
|
||||
internal GroupCommandListener? GroupCmdListener;
|
||||
|
||||
public override bool ShouldLoad(EnumAppSide forSide) => forSide == EnumAppSide.Server;
|
||||
|
||||
@@ -26,11 +27,14 @@ public class ClaimLinkModSystem : ModSystem
|
||||
PlayerData = api.PlayerData;
|
||||
Registry = new ClaimLinkRegistry(api.WorldManager.SaveGame);
|
||||
|
||||
CmdListener = new ClaimLinkCommandListener();
|
||||
CmdListener = new CommandListener();
|
||||
CommandHookModSystem.Register(CmdListener);
|
||||
GroupCmdListener = new GroupCommandListener();
|
||||
CommandHookModSystem.Register(GroupCmdListener);
|
||||
ClaimLinkChatCommand.Register(api);
|
||||
|
||||
api.Event.PlayerDisconnect += ClaimLinkCommandListener.OnPlayerDisconnect;
|
||||
api.Event.PlayerDisconnect += CommandListener.OnPlayerDisconnect;
|
||||
api.Event.PlayerDisconnect += GroupCommandListener.OnPlayerDisconnect;
|
||||
api.Event.PlayerDisconnect += ClaimLinkChatCommand.OnPlayerDisconnect;
|
||||
}
|
||||
|
||||
@@ -38,6 +42,8 @@ public class ClaimLinkModSystem : ModSystem
|
||||
{
|
||||
if (CmdListener != null)
|
||||
CommandHookModSystem.Unregister(CmdListener);
|
||||
if (GroupCmdListener != null)
|
||||
CommandHookModSystem.Unregister(GroupCmdListener);
|
||||
}
|
||||
|
||||
private static IEnumerable<(int claimIndex, LandClaim claim)> EnumerateOwnedClaims(
|
||||
|
||||
@@ -65,7 +65,7 @@ public class ClaimLinkRegistry
|
||||
|
||||
public bool AddEntry(string uid, int groupId, int claimIndex)
|
||||
{
|
||||
if (ClaimLinkCommandListener.PendingLoads.ContainsKey(uid))
|
||||
if (CommandListener.PendingLoads.ContainsKey(uid))
|
||||
return false;
|
||||
if (!Exists(groupId))
|
||||
return false;
|
||||
|
||||
@@ -5,16 +5,15 @@ using Vintagestory.API.Server;
|
||||
|
||||
namespace ClaimLink;
|
||||
|
||||
public class ClaimLinkCommandListener : ICommandHookListener
|
||||
public class CommandListener : ICommandHookListener
|
||||
{
|
||||
public string ModId => "claimlink";
|
||||
|
||||
public IReadOnlyList<string> Commands => new[] { "land", "group" };
|
||||
public IReadOnlyList<string> Commands => new[] { "land" };
|
||||
|
||||
public CommandRegistration Registration => new(Before, After);
|
||||
|
||||
internal static Dictionary<string, int> PendingLoads = new Dictionary<string, int>();
|
||||
internal static Dictionary<string, int> PendingDisbands = new Dictionary<string, int>();
|
||||
|
||||
private delegate void SubHandler(Caller caller, CmdArgs args);
|
||||
|
||||
@@ -33,23 +32,15 @@ public class ClaimLinkCommandListener : ICommandHookListener
|
||||
["cancel"] = ClaimCancel,
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, SubHandler> groupTopLevel = new()
|
||||
{
|
||||
["confirmdisband"] = GroupConfirmDisband,
|
||||
};
|
||||
|
||||
private static void Dispatch(TextCommandCallingArgs args)
|
||||
{
|
||||
Dictionary<string, SubHandler> table =
|
||||
args.Command?.Name == "group" ? groupTopLevel : topLevel;
|
||||
|
||||
CmdArgs rawArgs = args.RawArgs.Clone();
|
||||
|
||||
while (rawArgs.Length > 0)
|
||||
{
|
||||
string word = rawArgs.PeekWord();
|
||||
|
||||
if (table.TryGetValue(word, out SubHandler? handler))
|
||||
if (topLevel.TryGetValue(word, out SubHandler? handler))
|
||||
{
|
||||
rawArgs.PopWord();
|
||||
handler(args.Caller, rawArgs);
|
||||
@@ -67,45 +58,7 @@ public class ClaimLinkCommandListener : ICommandHookListener
|
||||
return null;
|
||||
}
|
||||
|
||||
private void After(TextCommandCallingArgs args, TextCommandResult result)
|
||||
{
|
||||
if (args.Caller.Player == null)
|
||||
return;
|
||||
|
||||
string uid = args.Caller.Player.PlayerUID;
|
||||
if (!PendingDisbands.TryGetValue(uid, out int groupId))
|
||||
return;
|
||||
|
||||
PendingDisbands.Remove(uid);
|
||||
|
||||
if (ClaimLinkModSystem.Groups.PlayerGroupsById.ContainsKey(groupId))
|
||||
return;
|
||||
|
||||
if (!ClaimLinkModSystem.Registry.Exists(groupId))
|
||||
return;
|
||||
|
||||
foreach (string memberUid in ClaimLinkModSystem.Registry.MemberUidsForGroup(groupId))
|
||||
ClaimLinkChatCommand.PendingActions.Remove(memberUid);
|
||||
|
||||
ClaimLinkModSystem.Registry.Remove(groupId);
|
||||
}
|
||||
|
||||
private static void GroupConfirmDisband(Caller caller, CmdArgs args)
|
||||
{
|
||||
if (caller.Player == null)
|
||||
return;
|
||||
|
||||
string? name = args.PopWord();
|
||||
int? groupId =
|
||||
name != null
|
||||
? ClaimLinkModSystem.Groups.GetPlayerGroupByName(name)?.Uid
|
||||
: caller.FromChatGroupId;
|
||||
|
||||
if (groupId == null)
|
||||
return;
|
||||
|
||||
PendingDisbands[caller.Player.PlayerUID] = (int)groupId;
|
||||
}
|
||||
private void After(TextCommandCallingArgs args, TextCommandResult result) { }
|
||||
|
||||
private static void Claim(Caller caller, CmdArgs args)
|
||||
{
|
||||
@@ -210,3 +163,62 @@ public class ClaimLinkCommandListener : ICommandHookListener
|
||||
PendingLoads.Remove(player.PlayerUID);
|
||||
}
|
||||
}
|
||||
|
||||
public class GroupCommandListener : ICommandHookListener
|
||||
{
|
||||
public string ModId => "claimlink.group";
|
||||
|
||||
public IReadOnlyList<string> Commands => new[] { "group" };
|
||||
|
||||
public CommandRegistration Registration => new(Before, After);
|
||||
|
||||
internal static Dictionary<string, int> PendingDisbands = new Dictionary<string, int>();
|
||||
|
||||
private TextCommandResult? Before(TextCommandCallingArgs args)
|
||||
{
|
||||
if (args.Caller.Player == null)
|
||||
return null;
|
||||
|
||||
CmdArgs rawArgs = args.RawArgs.Clone();
|
||||
if (rawArgs.PopWord() != "confirmdisband")
|
||||
return null;
|
||||
|
||||
string? name = rawArgs.PopWord();
|
||||
int? groupId =
|
||||
name != null
|
||||
? ClaimLinkModSystem.Groups.GetPlayerGroupByName(name)?.Uid
|
||||
: args.Caller.FromChatGroupId;
|
||||
|
||||
if (groupId == null)
|
||||
return null;
|
||||
|
||||
PendingDisbands[args.Caller.Player.PlayerUID] = (int)groupId;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void After(TextCommandCallingArgs args, TextCommandResult result)
|
||||
{
|
||||
if (args.Caller.Player == null)
|
||||
return;
|
||||
|
||||
string uid = args.Caller.Player.PlayerUID;
|
||||
if (!PendingDisbands.TryGetValue(uid, out int groupId))
|
||||
return;
|
||||
|
||||
PendingDisbands.Remove(uid);
|
||||
|
||||
if (ClaimLinkModSystem.Groups.PlayerGroupsById.ContainsKey(groupId))
|
||||
return;
|
||||
|
||||
if (!ClaimLinkModSystem.Registry.Exists(groupId))
|
||||
return;
|
||||
|
||||
ClaimLinkChatCommand.RemovePendingActionsForGroup(groupId);
|
||||
ClaimLinkModSystem.Registry.Remove(groupId);
|
||||
}
|
||||
|
||||
internal static void OnPlayerDisconnect(IServerPlayer player)
|
||||
{
|
||||
PendingDisbands.Remove(player.PlayerUID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"description": "Link claims together with groups.",
|
||||
"version": "0.0.6",
|
||||
"dependencies": {
|
||||
"game": "1.22.3",
|
||||
"game": "1.22.5",
|
||||
"commandhook": "2.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user