From 573a41b3b795d70ce1faaff8a76df3eae389a0a6 Mon Sep 17 00:00:00 2001 From: Kenneth VanderLinde Date: Thu, 16 Nov 2023 12:37:02 -0800 Subject: [PATCH 1/5] Add new ownership methods to Token for easier checking - `Token.isOwnedByAny(Collection)` check if any provided players own the token. - `Token.isOwnedByNone()` if the token is not owned by anyone. Both of these respect `Token.isOwnedByAll()` so it doesn't have to be checked separately. These places were simplified as a result: - `AppUtil.ownedByOnePlayer()` - `EditTokenDialog.commit()` Also `TokenPropertyFunctions.getOwners()` was simplified, but that was possible without these new methods. --- .../net/rptools/maptool/client/AppUtil.java | 12 +----- .../functions/TokenPropertyFunctions.java | 5 +-- .../ui/token/dialog/edit/EditTokenDialog.java | 10 +---- .../java/net/rptools/maptool/model/Token.java | 39 ++++++++++++++++++- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/AppUtil.java b/src/main/java/net/rptools/maptool/client/AppUtil.java index e89e94caae..7d6a5813aa 100644 --- a/src/main/java/net/rptools/maptool/client/AppUtil.java +++ b/src/main/java/net/rptools/maptool/client/AppUtil.java @@ -21,7 +21,6 @@ import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.security.CodeSource; -import java.util.List; import java.util.UUID; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -322,16 +321,7 @@ public static boolean playerOwns(Token token) { * @return true if owned by all, or one of the owners is online and not a gm. */ public static boolean ownedByOnePlayer(Token token) { - if (token.isOwnedByAll()) { - return true; - } - List players = MapTool.getNonGMs(); - for (String owner : token.getOwners()) { - if (players.contains(owner)) { - return true; - } - } - return false; + return token.isOwnedByAny(MapTool.getNonGMs()); } /** diff --git a/src/main/java/net/rptools/maptool/client/functions/TokenPropertyFunctions.java b/src/main/java/net/rptools/maptool/client/functions/TokenPropertyFunctions.java index 6b4684f879..0af206126b 100644 --- a/src/main/java/net/rptools/maptool/client/functions/TokenPropertyFunctions.java +++ b/src/main/java/net/rptools/maptool/client/functions/TokenPropertyFunctions.java @@ -1337,11 +1337,10 @@ private String getPropertyNames(Token token, String delim, String pattern, boole * @return a string list of the token owners. */ public String getOwners(Token token, String delim) { - String[] owners = new String[token.getOwners().size()]; - token.getOwners().toArray(owners); + var owners = new ArrayList<>(token.getOwners()); if ("json".endsWith(delim)) { JsonArray jarr = new JsonArray(); - Arrays.stream(owners).forEach(o -> jarr.add(new JsonPrimitive(o))); + owners.forEach(o -> jarr.add(new JsonPrimitive(o))); return jarr.toString(); } else { return StringFunctions.getInstance().join(owners, delim); diff --git a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java index d2bc07cdae..316e9e1c7f 100644 --- a/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java +++ b/src/main/java/net/rptools/maptool/client/ui/token/dialog/edit/EditTokenDialog.java @@ -833,15 +833,7 @@ public boolean commit() { // If we are not a GM and the only non GM owner make sure we can't // take our selves off of the owners list if (!MapTool.getPlayer().isGM()) { - boolean hasPlayer = false; - Set owners = token.getOwners(); - if (owners != null) { - for (Player pl : MapTool.getPlayerList()) { - if (!pl.isGM() && owners.contains(pl.getName())) { - hasPlayer = true; - } - } - } + boolean hasPlayer = token.isOwnedByAny(MapTool.getNonGMs()); if (!hasPlayer) { token.addOwner(MapTool.getPlayer().getName()); } diff --git a/src/main/java/net/rptools/maptool/model/Token.java b/src/main/java/net/rptools/maptool/model/Token.java index 895515b068..fa048a585a 100644 --- a/src/main/java/net/rptools/maptool/model/Token.java +++ b/src/main/java/net/rptools/maptool/model/Token.java @@ -1109,8 +1109,45 @@ public synchronized void clearAllOwners() { ownerList.clear(); } + /** + * Check if the token is owned by the provided player. + * + *

This method allows implicit ownership when the token is owned by all players. + * + * @param playerId The player name to check ownership against. + * @return {@code true} if {@code playerId} identifies an owner of the token. + */ public synchronized boolean isOwner(String playerId) { - return (ownerType == OWNER_TYPE_ALL || ownerList.contains(playerId)); + return ownerType == OWNER_TYPE_ALL || ownerList.contains(playerId); + } + + /** + * Checks if the token is owned by any of the provided players. + * + *

This method allows implicit ownership when the token is owned by all players. It is as if + * each player was checked individually via {@link #isOwner(String)}, but more convenient and + * efficient. + * + * @param playerIds The player names to check ownership against. + * @return {@code true} if there is a player in {@code playerIds} that is an owner of this token. + */ + public synchronized boolean isOwnedByAny(Collection playerIds) { + if (playerIds.isEmpty()) { + return false; + } + + return ownerType == OWNER_TYPE_ALL || !Collections.disjoint(ownerList, playerIds); + } + + /** + * Checks if the token is not owned by anyone. + * + *

If the token is owned by all players, this will return {@code false} + * + * @return {@code true} if the token has any owners. + */ + public synchronized boolean isOwnedByNone() { + return ownerType != OWNER_TYPE_ALL && ownerList.isEmpty(); } @Override From c30ebdeba7287c885e61c4224b50d6d636f8a727 Mon Sep 17 00:00:00 2001 From: Kenneth VanderLinde Date: Thu, 16 Nov 2023 12:46:31 -0800 Subject: [PATCH 2/5] Fix look up of event macros to respect ownership We had checks to ensure that lib:tokens owned by any players did not have their onCampaignLoad even executed. But the check didn't `continue` the correct loop, so outside of `Token.isOwnedByAll()`, it had no effect. This changes that to use `Token.isOwnedByAny()`, and now the right loop is naturally manipulated. Also removes some unecessary nesting of blocks and an unused method. --- .../rptools/maptool/util/EventMacroUtil.java | 39 ++++--------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/rptools/maptool/util/EventMacroUtil.java b/src/main/java/net/rptools/maptool/util/EventMacroUtil.java index 6f870f3a97..ce5d1f0e17 100644 --- a/src/main/java/net/rptools/maptool/util/EventMacroUtil.java +++ b/src/main/java/net/rptools/maptool/util/EventMacroUtil.java @@ -25,7 +25,6 @@ import net.rptools.maptool.model.TextMessage; import net.rptools.maptool.model.Token; import net.rptools.maptool.model.library.LibraryManager; -import net.rptools.maptool.model.player.Player; import net.rptools.parser.ParserException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,17 +32,6 @@ /** Utility class to facilitate macro events like onTokenMove and onInitiativeChange. */ public class EventMacroUtil { private static final Logger LOGGER = LogManager.getLogger(EventMacroUtil.class); - /** - * Scans all maps to find the first Lib:Token containing a macro that matches the given "callback" - * string. If more than one token has such a macro, the first one encountered is returned - - * because this order is unpredictable, this is very much not encouraged. - * - * @param macroCallback the macro name to find - * @return the first Lib:token found that contains the requested macro, or null if none - */ - public static Token getEventMacroToken(final String macroCallback) { - return getEventMacroTokens(macroCallback).stream().findFirst().orElse(null); - } /** * Scans all maps to find any Lib:Tokens that contain a macro matching the given "callback" label. @@ -57,27 +45,16 @@ public static List getEventMacroTokens(final String macroCallback) { for (ZoneRenderer zr : zrenderers) { List tokenList = zr.getZone().getTokensFiltered(t -> t.getName().toLowerCase().startsWith("lib:")); + var nonGms = MapTool.getNonGMs(); for (Token token : tokenList) { - // If the token is not owned by everyone and all owners are GMs - // then we are in + // If the token is not owned by everyone and all owners are GMs then we are in // its a trusted Lib:token so we can run the macro - if (token != null) { - if (token.isOwnedByAll()) { - continue; - } else { - Set gmPlayers = new HashSet(); - for (Object o : MapTool.getPlayerList()) { - Player p = (Player) o; - if (p.isGM()) { - gmPlayers.add(p.getName()); - } - } - for (String owner : token.getOwners()) { - if (!gmPlayers.contains(owner)) { - continue; - } - } - } + if (token.isOwnedByAll()) { + continue; + } + if (token.isOwnedByAny(nonGms)) { + // Not trusted, don't run. + continue; } if (token.getMacro(macroCallback, false) != null) { found.add(token); From f7bc95ad871d8b054078cbc96d4af0a0954b46f8 Mon Sep 17 00:00:00 2001 From: Kenneth VanderLinde Date: Thu, 16 Nov 2023 12:47:15 -0800 Subject: [PATCH 3/5] Fix library trusted check regarding ownership The existing check marked macros as trusted if the library had no listed owners, but did not consider whether it was owned by _All Players_. The updated version does that implicitly by using `Token.isOwnedByNone()`. --- .../net/rptools/maptool/model/library/token/LibraryToken.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/rptools/maptool/model/library/token/LibraryToken.java b/src/main/java/net/rptools/maptool/model/library/token/LibraryToken.java index 79df513384..b6bf0504a5 100644 --- a/src/main/java/net/rptools/maptool/model/library/token/LibraryToken.java +++ b/src/main/java/net/rptools/maptool/model/library/token/LibraryToken.java @@ -319,7 +319,7 @@ public CompletableFuture> getMTScriptMacroInfo(Strin new MTScriptMacroInfo( macroName, buttonProps.getCommand(), - library.getOwners().size() == 0 || !buttonProps.getAllowPlayerEdits(), + library.isOwnedByNone() || !buttonProps.getAllowPlayerEdits(), !buttonProps.getAllowPlayerEdits() && buttonProps.getAutoExecute(), buttonProps.getEvaluatedToolTip())); }); From 52dfbe662e90e30e7d5b54790416c287f16e037c Mon Sep 17 00:00:00 2001 From: Kenneth VanderLinde Date: Thu, 16 Nov 2023 12:49:33 -0800 Subject: [PATCH 4/5] Fix getTokens() when provided an array of owners The existing check did not return tokens that were owned by _All Players_. The new check handles this implicitly via `Token.isOwnedByAny()`. --- .../rptools/maptool/client/functions/FindTokenFunctions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/rptools/maptool/client/functions/FindTokenFunctions.java b/src/main/java/net/rptools/maptool/client/functions/FindTokenFunctions.java index e78210ce87..9ee0516725 100644 --- a/src/main/java/net/rptools/maptool/client/functions/FindTokenFunctions.java +++ b/src/main/java/net/rptools/maptool/client/functions/FindTokenFunctions.java @@ -225,7 +225,7 @@ public boolean matchToken(Token t) { if (ownership == Ownership.NONE) return (!t.hasOwners()); if (ownership == Ownership.MULTIPLE) return (t.isOwnedByAll() || t.getOwners().size() > 1); if (ownership == Ownership.SINGLE) return (!t.isOwnedByAll() && t.getOwners().size() == 1); - if (ownership == Ownership.ARRAY) return (!Collections.disjoint(t.getOwners(), ownerList)); + if (ownership == Ownership.ARRAY) return (t.isOwnedByAny(ownerList)); boolean isOwner = t.isOwner(playerName); if (ownership == Ownership.SELF) return (isOwner); From ef87f67499fa6d62d2bf3a122d2dee53ba750538 Mon Sep 17 00:00:00 2001 From: Kenneth VanderLinde Date: Thu, 16 Nov 2023 13:21:54 -0800 Subject: [PATCH 5/5] Fix owner-only auras not showing when token is owned by All Players Also move some check out of the inner loop so we save on a bunch of unnecessary work. --- .../rptools/maptool/client/ui/zone/ZoneView.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java b/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java index e1ebef3454..ae85f92eb7 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java @@ -650,6 +650,14 @@ public List getDrawableAuras() { if (token == null) { continue; } + if (!token.isVisible() && !MapTool.getPlayer().isEffectiveGM()) { + continue; + } + if (token.isVisibleOnlyToOwner() && !AppUtil.playerOwns(token)) { + continue; + } + boolean isOwner = token.isOwner(MapTool.getPlayer().getName()); + Point p = FogUtil.calculateVisionCenter(token, zone); for (AttachedLightSource als : token.getLightSources()) { @@ -680,16 +688,9 @@ public List getDrawableAuras() { if (light.getPaint() == null) { continue; } - boolean isOwner = token.getOwners().contains(MapTool.getPlayer().getName()); if ((light.isGM() && !MapTool.getPlayer().isEffectiveGM())) { continue; } - if ((!token.isVisible()) && !MapTool.getPlayer().isEffectiveGM()) { - continue; - } - if (token.isVisibleOnlyToOwner() && !AppUtil.playerOwns(token)) { - continue; - } if (light.isOwnerOnly() && !isOwner && !MapTool.getPlayer().isEffectiveGM()) { continue; }