Skip to content

Commit

Permalink
Allow specific luckperms contexts to be disabled (#5898)
Browse files Browse the repository at this point in the history
* Allow specific luckperms contexts to be disabled.

* Clarify message

* Add notnull annotations to playercache worldcoord

* Fix double register
  • Loading branch information
Warriorrrr authored May 8, 2022
1 parent f0a1753 commit 1232f0c
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 60 deletions.
9 changes: 9 additions & 0 deletions src/com/palmergames/bukkit/config/ConfigNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,15 @@ public enum ConfigNodes {
"# If enabled, Towny contexts will be available in LuckPerms. https://luckperms.net/wiki/Context",
"# Towny will supply for LuckPerms: townyperms' ranks contexts, as well as location-based contexts."),

PLUGIN_ENABLED_CONTEXTS(
"plugin.interfacing.enabled_contexts",
"*",
"",
"# Configure what contexts to enable/disable here, contexts must be separated by a comma.",
"# Available contexts: towny:resident, towny:mayor, towny:king, towny:insidetown, towny:insideowntown, towny:insideownplot, towny:townrank",
"# towny:nationrank, towny:town, towny:nation"
),

PLUGIN_WEB_MAP_USING_STATUSSCREEN(
"plugin.interfacing.web_map.enabled",
"false",
Expand Down
17 changes: 16 additions & 1 deletion src/com/palmergames/bukkit/towny/Towny.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public class Towny extends JavaPlugin {
private final TownyLoginListener loginListener = new TownyLoginListener();
private final HUDManager HUDManager = new HUDManager(this);
private final TownyPaperEvents paperEvents = new TownyPaperEvents(this);
private LuckPermsContexts luckPermsContexts;

private TownyUniverse townyUniverse;

Expand Down Expand Up @@ -453,6 +454,11 @@ public void onDisable() {
adventure.close();
adventure = null;
}

if (luckPermsContexts != null) {
luckPermsContexts.unregisterContexts();
luckPermsContexts = null;
}

this.townyUniverse = null;

Expand Down Expand Up @@ -544,7 +550,8 @@ private void checkPlugins() {

test = getServer().getPluginManager().getPlugin("LuckPerms");
if (test != null && TownySettings.isContextsEnabled()) {
new LuckPermsContexts();
this.luckPermsContexts = new LuckPermsContexts(this);
luckPermsContexts.registerContexts();
addons.add(String.format("%s v%s", "LuckPerms", test.getDescription().getVersion()));
}

Expand Down Expand Up @@ -845,6 +852,14 @@ public PlayerCache getCache(Player player) {
return cache;
}

/**
* @param uuid - UUID of the player to get the cache for.
* @return The cache, or <code>null</code> if it doesn't exist.
*/
public PlayerCache getCacheOrNull(@NotNull UUID uuid) {
return playerCache.get(uuid);
}

/**
* Resets all Online player caches, retaining their location info.
*/
Expand Down
7 changes: 7 additions & 0 deletions src/com/palmergames/bukkit/towny/TownySettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -3144,5 +3144,12 @@ public static boolean isLanguageEnabled(@NotNull String locale) {
public static boolean doCapitalsPayNationTax() {
return getBoolean(ConfigNodes.ECO_DAILY_TAXES_DO_CAPITALS_PAY_NATION_TAX);
}

public static boolean isContextEnabled(String id) {
if (getString(ConfigNodes.PLUGIN_ENABLED_CONTEXTS).equals("*"))
return true;

return getStrArr(ConfigNodes.PLUGIN_ENABLED_CONTEXTS).contains(id);
}
}

125 changes: 70 additions & 55 deletions src/com/palmergames/bukkit/towny/hooks/LuckPermsContexts.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.palmergames.bukkit.towny.hooks;

import com.github.bsideup.jabel.Desugar;
import com.palmergames.bukkit.towny.Towny;
import com.palmergames.bukkit.towny.TownyAPI;
import com.palmergames.bukkit.towny.TownySettings;
import com.palmergames.bukkit.towny.TownyUniverse;
import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.PlayerCache;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town;
import com.palmergames.bukkit.towny.object.WorldCoord;
import com.palmergames.bukkit.towny.permissions.TownyPerms;
import com.palmergames.bukkit.towny.utils.PlayerCacheUtil;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.context.ContextCalculator;
import net.luckperms.api.context.ContextConsumer;
Expand All @@ -19,80 +21,93 @@
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.List;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public class LuckPermsContexts implements ContextCalculator<Player> {
private Towny plugin;
private LuckPerms luckPerms;
private final Set<Calculator> calculators = new HashSet<>();

public LuckPermsContexts(@NotNull Towny plugin) {
this.plugin = plugin;

registerContext("towny:resident", resident -> Collections.singleton(String.valueOf(resident.hasTown())), () -> Arrays.asList("true", "false"));
registerContext("towny:mayor", resident -> Collections.singleton(String.valueOf(resident.isMayor())), () -> Arrays.asList("true", "false"));
registerContext("towny:king", resident -> Collections.singleton(String.valueOf(resident.isKing())), () -> Arrays.asList("true", "false"));
registerContext("towny:insidetown", resident -> {
PlayerCache cache = plugin.getCacheOrNull(resident.getUUID());
if (cache == null)
return Collections.emptyList();

return Collections.singleton(String.valueOf(cache.getLastTownBlock().hasTownBlock()));
}, () -> Arrays.asList("true", "false"));
registerContext("towny:insideowntown", resident -> {
PlayerCache cache = plugin.getCacheOrNull(resident.getUUID());
if (cache == null)
return Collections.emptyList();

return Optional.ofNullable(cache.getLastTownBlock().getTownOrNull()).map(town -> Collections.singleton(String.valueOf(town.hasResident(resident)))).orElse(Collections.emptySet());
}, () -> Arrays.asList("true", "false"));
registerContext("towny:insideownplot", resident -> {
PlayerCache cache = plugin.getCacheOrNull(resident.getUUID());
if (cache == null)
return Collections.emptyList();

return Optional.ofNullable(cache.getLastTownBlock().getTownBlockOrNull()).map(townBlock -> Collections.singleton(String.valueOf(townBlock.hasResident(resident)))).orElse(Collections.emptySet());
}, () -> Arrays.asList("true", "false"));
registerContext("towny:townrank", Resident::getTownRanks, TownyPerms::getTownRanks);
registerContext("towny:nationrank", Resident::getNationRanks, TownyPerms::getNationRanks);
registerContext("towny:town", resident -> resident.hasTown() ? Collections.singleton(resident.getTownOrNull().getName()) : Collections.emptyList(), () -> TownyUniverse.getInstance().getTowns().stream().map(Town::getName).collect(Collectors.toSet()));
registerContext("towny:nation", resident -> resident.hasNation() ? Collections.singleton(resident.getNationOrNull().getName()) : Collections.emptyList(), () -> TownyUniverse.getInstance().getNations().stream().map(Nation::getName).collect(Collectors.toSet()));

private static final String RESIDENT_CONTEXT = "towny:resident";
private static final String MAYOR_CONTEXT = "towny:mayor";
private static final String KING_CONTEXT = "towny:king";
private static final String INSIDETOWN_CONTEXT = "towny:insidetown";
private static final String INSIDEOWNTOWN_CONTEXT = "towny:insideowntown";
private static final String INSIDEOWNPLOT_CONTEXT = "towny:insideownplot";
private static final String TOWN_RANK_CONTEXT = "towny:townrank";
private static final String NATION_RANK_CONTEXT = "towny:nationrank";
private static final String TOWN_CONTEXT = "towny:town";
private static final String NATION_CONTEXT = "towny:nation";

private static final List<String> booleanContexts = Arrays.asList(RESIDENT_CONTEXT, MAYOR_CONTEXT, KING_CONTEXT, INSIDETOWN_CONTEXT, INSIDEOWNTOWN_CONTEXT, INSIDEOWNPLOT_CONTEXT);
this.calculators.removeIf(calculator -> !TownySettings.isContextEnabled(calculator.context));
plugin.getLogger().info("Enabled LuckPerms contexts: " + this.calculators.stream().map(Calculator::context).collect(Collectors.joining(", ")));
}

private static LuckPerms luckPerms;

public LuckPermsContexts() {
public void registerContexts() {
RegisteredServiceProvider<LuckPerms> provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class);
if (provider != null) {
luckPerms = provider.getProvider();
this.luckPerms = provider.getProvider();
luckPerms.getContextManager().registerCalculator(this);
}
} else
this.luckPerms = null;
}

public void unregisterContexts() {
if (this.luckPerms != null)
this.luckPerms.getContextManager().unregisterCalculator(this);
}

private void registerContext(String context, Function<Resident, Iterable<String>> calculator, Supplier<Iterable<String>> suggestions) {
calculators.add(new Calculator(context, calculator, suggestions));
}

@Override
public void calculate(@NotNull Player player, @NotNull ContextConsumer contextConsumer) {
Resident resident = TownyAPI.getInstance().getResident(player);
if (resident == null)
return;

for (String townrank : resident.getTownRanks()) contextConsumer.accept(TOWN_RANK_CONTEXT, townrank);
for (String nationrank : resident.getNationRanks()) contextConsumer.accept(NATION_RANK_CONTEXT, nationrank);
Town town = resident.getTownOrNull();
if(town != null) {
contextConsumer.accept(TOWN_CONTEXT, town.getName());
// There is no point to check for nation if town is not registered
Nation nation = resident.getNationOrNull();
if(nation != null) {
contextConsumer.accept(NATION_CONTEXT, nation.getName());
}
}

contextConsumer.accept(RESIDENT_CONTEXT, Boolean.toString(resident.hasTown()));
contextConsumer.accept(MAYOR_CONTEXT, Boolean.toString(resident.isMayor()));
contextConsumer.accept(KING_CONTEXT, Boolean.toString(resident.isKing()));

WorldCoord wc = PlayerCacheUtil.getCache(player).getLastTownBlock();
if (wc == null || TownyAPI.getInstance().isWilderness(wc)) {
contextConsumer.accept(INSIDETOWN_CONTEXT, "false");
contextConsumer.accept(INSIDEOWNPLOT_CONTEXT, "false");
contextConsumer.accept(INSIDEOWNTOWN_CONTEXT, "false");
} else {
contextConsumer.accept(INSIDETOWN_CONTEXT, "true");

contextConsumer.accept(INSIDEOWNTOWN_CONTEXT, Boolean.toString(wc.getTownBlockOrNull().getTownOrNull().hasResident(resident)));
contextConsumer.accept(INSIDEOWNPLOT_CONTEXT, Boolean.toString(wc.getTownBlockOrNull().hasResident() && wc.getTownBlockOrNull().getResidentOrNull().equals(resident)));
}
for (Calculator calculator : this.calculators)
calculator.function().apply(resident).forEach(value -> contextConsumer.accept(calculator.context, value));
}

@Override
public ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = ImmutableContextSet.builder();
for (String context : booleanContexts) {
builder.add(context, "true");
builder.add(context, "false");
}
for (String nationrank : TownyPerms.getNationRanks()) builder.add(NATION_RANK_CONTEXT, nationrank);
for (String townrank : TownyPerms.getTownRanks()) builder.add(TOWN_RANK_CONTEXT, townrank);
for (Town town : TownyUniverse.getInstance().getTowns()) builder.add(TOWN_CONTEXT, town.getName());
for (Nation nation : TownyUniverse.getInstance().getNations()) builder.add(NATION_CONTEXT, nation.getName());

for (Calculator calculator : this.calculators)
calculator.suggestions().get().forEach(value -> builder.add(calculator.context, value));

return builder.build();
}

@Desugar
private record Calculator(String context, Function<Resident, Iterable<String>> function, Supplier<Iterable<String>> suggestions) {}
}
10 changes: 6 additions & 4 deletions src/com/palmergames/bukkit/towny/object/PlayerCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;

Expand All @@ -27,7 +28,7 @@ public PlayerCache(TownyWorld world, Player player) {
setLastLocation(player.getLocation());
}

public PlayerCache(WorldCoord worldCoord) {
public PlayerCache(@NotNull WorldCoord worldCoord) {

this.setLastTownBlock(worldCoord);
}
Expand All @@ -37,7 +38,7 @@ public PlayerCache(WorldCoord worldCoord) {
*
* @param worldCoord - World Coordinate to set as the lastWorldCoord
*/
public void setLastTownBlock(WorldCoord worldCoord) {
public void setLastTownBlock(@NotNull WorldCoord worldCoord) {

this.lastWorldCoord = worldCoord;
}
Expand All @@ -47,7 +48,7 @@ public void setLastTownBlock(WorldCoord worldCoord) {
*
* @param worldCoord - World Coordinate to setLastTownBlock
*/
public void resetAndUpdate(WorldCoord worldCoord) {
public void resetAndUpdate(@NotNull WorldCoord worldCoord) {

reset();
setLastTownBlock(worldCoord);
Expand All @@ -58,6 +59,7 @@ public void resetAndUpdate(WorldCoord worldCoord) {
*
* @return WorldCoord of the last acted upon TownBlock
*/
@NotNull
public WorldCoord getLastTownBlock() {

return lastWorldCoord;
Expand All @@ -69,7 +71,7 @@ public WorldCoord getLastTownBlock() {
* @param pos - WorldCoord to setLastTownBlock
* @return true if changed.
*/
public boolean updateCoord(WorldCoord pos) {
public boolean updateCoord(@NotNull WorldCoord pos) {

if (!getLastTownBlock().equals(pos)) {
reset();
Expand Down

0 comments on commit 1232f0c

Please sign in to comment.