From eaa79f8f52fc1b30791a2194a20d13dd627f542c Mon Sep 17 00:00:00 2001 From: anth64 Date: Tue, 14 Jul 2026 22:49:24 +0200 Subject: [PATCH] fix: transferownership correctly sets owner level and demotes old owner to op, works for offline players, notifies members --- ClaimLink/ClaimLinkChatCommand.cs | 91 +++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/ClaimLink/ClaimLinkChatCommand.cs b/ClaimLink/ClaimLinkChatCommand.cs index 25128e5..7a450b8 100644 --- a/ClaimLink/ClaimLinkChatCommand.cs +++ b/ClaimLink/ClaimLinkChatCommand.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Vintagestory.API.Common; +using Vintagestory.API.Config; using Vintagestory.API.Server; namespace ClaimLink; @@ -117,7 +118,7 @@ public static class ClaimLinkChatCommand new( new[] { "transferownership", "transfer", "to" }, "Transfer ownership of the claim link to another player.", - new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, + new ICommandArgumentParser[] { p.Word("groupname"), p.PlayerUids("playername") }, true, TransferOwnership ), @@ -170,7 +171,7 @@ public static class ClaimLinkChatCommand new( new[] { "transferownership", "transfer", "to" }, "Transfer ownership of any claim link to another player.", - new ICommandArgumentParser[] { p.Word("groupname"), p.OnlinePlayer("playername") }, + new ICommandArgumentParser[] { p.Word("groupname"), p.PlayerUids("playername") }, false, AdminTransferOwnership ), @@ -449,15 +450,21 @@ public static class ClaimLinkChatCommand public static TextCommandResult TransferOwnership(TextCommandCallingArgs args) { string groupName = (string)args[0]; - IPlayer target = (IPlayer)args[1]; IPlayer player = args.Caller.Player; string playerUid = player.PlayerUID; - TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); + TextCommandResult? err = TryResolveTargetPlayer( + (PlayerUidName[])args[1], + out IPlayer target + ); if (err != null) return err; - err = TryResolveClaimLink(group, out _); + err = TryResolveGroup(groupName, out PlayerGroup group); + if (err != null) + return err; + + err = TryResolveClaimLink(group, out ClaimLink link); if (err != null) return err; @@ -476,10 +483,28 @@ public static class ClaimLinkChatCommand return Stage( playerUid, $"Ownership of '{groupName}' will be transferred to {target.PlayerName}.", - () => ExecuteTransferOwnership(group, target) + () => ExecuteTransferOwnership(group, link, target) ); } + private static TextCommandResult? TryResolveTargetPlayer( + PlayerUidName[] matches, + out IPlayer target + ) + { + target = null!; + + if (matches.Length != 1) + return TextCommandResult.Error("Name a single player."); + + IPlayer? resolved = ClaimLinkModSystem.World.PlayerByUid(matches[0].Uid); + if (resolved == null) + return TextCommandResult.Error($"No such player '{matches[0].Name}'."); + + target = resolved; + return null; + } + private static TextCommandResult? RequireNotAlreadyOwner(PlayerGroup group, IPlayer target) { return target.PlayerUID == group.OwnerUID @@ -487,27 +512,39 @@ public static class ClaimLinkChatCommand : null; } - private static TextCommandResult ExecuteTransferOwnership(PlayerGroup group, IPlayer target) + private static TextCommandResult ExecuteTransferOwnership( + PlayerGroup group, + ClaimLink link, + IPlayer target + ) { string oldOwnerUid = group.OwnerUID; - string targetUid = target.PlayerUID; IPlayer? oldOwner = ClaimLinkModSystem.World.PlayerByUid(oldOwnerUid); - EnumPlayerGroupMemberShip? oldOwnerLevel = oldOwner?.GetGroup(group.Uid)?.Level; - EnumPlayerGroupMemberShip targetLevel = target.GetGroup(group.Uid)!.Level; + PlayerGroupMembership? oldOwnerMembership = oldOwner?.GetGroup(group.Uid); + PlayerGroupMembership targetMembership = target.GetGroup(group.Uid)!; - // 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})" - ); + group.OwnerUID = target.PlayerUID; + targetMembership.Level = EnumPlayerGroupMemberShip.Owner; - group.OwnerUID = targetUid; + if (oldOwnerMembership != null) + oldOwnerMembership.Level = EnumPlayerGroupMemberShip.Op; - ClaimLinkModSystem.Logger.Notification( - $"claimlink: transferownership '{group.Name}' (uid {group.Uid}) AFTER OwnerUID={group.OwnerUID}" - ); + string notice = $"'{group.Name}' is now owned by {target.PlayerName}."; - return TextCommandResult.Success($"'{group.Name}' is now owned by {target.PlayerName}."); + HashSet notifyUids = link.Members.Select(m => m.OwnerPlayerUid).ToHashSet(); + notifyUids.Add(oldOwnerUid); + notifyUids.Add(target.PlayerUID); + + foreach (string uid in notifyUids) + NotifyIfOnline(ClaimLinkModSystem.World.PlayerByUid(uid), notice); + + return TextCommandResult.Success(notice); + } + + private static void NotifyIfOnline(IPlayer? player, string message) + { + if (player is IServerPlayer sp && sp.ConnectionState == EnumClientState.Playing) + sp.SendMessage(GlobalConstants.GeneralChatGroup, message, EnumChatType.Notification); } public static TextCommandResult Info(TextCommandCallingArgs args) @@ -597,13 +634,19 @@ public static class ClaimLinkChatCommand public static TextCommandResult AdminTransferOwnership(TextCommandCallingArgs args) { string groupName = (string)args[0]; - IPlayer target = (IPlayer)args[1]; - TextCommandResult? err = TryResolveGroup(groupName, out PlayerGroup group); + TextCommandResult? err = TryResolveTargetPlayer( + (PlayerUidName[])args[1], + out IPlayer target + ); if (err != null) return err; - err = TryResolveClaimLink(group, out _); + err = TryResolveGroup(groupName, out PlayerGroup group); + if (err != null) + return err; + + err = TryResolveClaimLink(group, out ClaimLink link); if (err != null) return err; @@ -615,7 +658,7 @@ public static class ClaimLinkChatCommand if (err != null) return err; - return ExecuteTransferOwnership(group, target); + return ExecuteTransferOwnership(group, link, target); } public static TextCommandResult AdminInfo(TextCommandCallingArgs args) =>