[Idea]: Folia support for OpenInv #196
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -42,15 +42,23 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
public interface IOpenInv {
|
public interface IOpenInv {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the configuration value for whether or not OpenInv saves player data when unloading
|
* Check the configuration value for whether OpenInv saves player data when unloading players. This is exclusively
|
||||||
* players. This is exclusively for users who do not allow editing of inventories, only viewing,
|
* for users who do not allow editing of inventories, only viewing, and wish to prevent any possibility of bugs such
|
||||||
* and wish to prevent any possibility of bugs such as lishid#40. If true, OpenInv will not ever
|
* as lishid#40. If true, OpenInv will not ever save any edits made to players.
|
||||||
* save any edits made to players.
|
|
||||||
*
|
*
|
||||||
* @return false unless configured otherwise
|
* @return false unless configured otherwise
|
||||||
*/
|
*/
|
||||||
boolean disableSaving();
|
boolean disableSaving();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the configuration value for whether OpenInv allows offline access. This does not prevent other plugins from
|
||||||
|
* using existing loaded players while offline.
|
||||||
|
*
|
||||||
|
* @return false unless configured otherwise
|
||||||
|
* @since 4.2.0
|
||||||
|
*/
|
||||||
|
boolean disableOfflineAccess();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the active ISilentContainer implementation.
|
* Gets the active ISilentContainer implementation.
|
||||||
*
|
*
|
||||||
@@ -60,12 +68,9 @@ public interface IOpenInv {
|
|||||||
@NotNull IAnySilentContainer getAnySilentContainer();
|
@NotNull IAnySilentContainer getAnySilentContainer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the active IInventoryAccess implementation.
|
* @deprecated Use static {@link InventoryAccess} methods.
|
||||||
*
|
|
||||||
* @return the IInventoryAccess
|
|
||||||
* @throws IllegalStateException if the server version is unsupported
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated(forRemoval = true)
|
||||||
default @NotNull IInventoryAccess getInventoryAccess() {
|
default @NotNull IInventoryAccess getInventoryAccess() {
|
||||||
return new InventoryAccess();
|
return new InventoryAccess();
|
||||||
}
|
}
|
||||||
@@ -129,6 +134,15 @@ public interface IOpenInv {
|
|||||||
*/
|
*/
|
||||||
boolean isSupportedVersion();
|
boolean isSupportedVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a {@link Player} is currently loaded by OpenInv.
|
||||||
|
*
|
||||||
|
* @param playerUuid the {@link UUID} of the {@code Player}
|
||||||
|
* @return whether the {@code Player} is loaded
|
||||||
|
* @since 4.2.0
|
||||||
|
*/
|
||||||
|
boolean isPlayerLoaded(@NotNull UUID playerUuid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a Player from an OfflinePlayer. May return null under some circumstances.
|
* Load a Player from an OfflinePlayer. May return null under some circumstances.
|
||||||
*
|
*
|
||||||
@@ -227,57 +241,51 @@ public interface IOpenInv {
|
|||||||
* @return true unless configured otherwise
|
* @return true unless configured otherwise
|
||||||
* @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings.
|
* @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated(forRemoval = true)
|
||||||
default boolean notifyAnyChest() {
|
default boolean notifyAnyChest() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the configuration value for whether or not OpenInv displays a notification to the user
|
* @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings.
|
||||||
* when a container is activated with SilentChest.
|
|
||||||
*
|
|
||||||
* @return true unless configured otherwise
|
|
||||||
* @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings.
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated(forRemoval = true)
|
||||||
default boolean notifySilentChest() {
|
default boolean notifySilentChest() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark a Player as no longer in use by a Plugin to allow OpenInv to remove it from the cache
|
* @deprecated see {@link #retainPlayer(Player, Plugin)}
|
||||||
* when eligible.
|
|
||||||
*
|
|
||||||
* @param player the Player
|
|
||||||
* @param plugin the Plugin no longer holding a reference to the Player
|
|
||||||
* @throws IllegalStateException if the server version is unsupported
|
|
||||||
*/
|
*/
|
||||||
void releasePlayer(@NotNull Player player, @NotNull Plugin plugin);
|
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||||
|
default void releasePlayer(@NotNull Player player, @NotNull Plugin plugin) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark a Player as in use by a Plugin to prevent it from being removed from the cache. Used to
|
* @deprecated OpenInv no longer uses an internal cache beyond maintaining copies of currently open inventories.
|
||||||
* prevent issues with multiple copies of the same Player being loaded such as lishid#49.
|
* If you wish to use/modify a player, ensure either {@link IOpenInv#isPlayerLoaded(UUID)} is false or the player
|
||||||
* Changes made to loaded copies overwrite changes to the others when saved, leading to
|
* instance is the same memory address as the one in use by OpenInv.
|
||||||
* duplication bugs and more.
|
* <pre>
|
||||||
* <p>
|
* public @NotNull Player savePlayerData(@NotNull Player player) {
|
||||||
* When finished with the Player object, be sure to call {@link #releasePlayer(Player, Plugin)}
|
* IOpenInv openInv = ...
|
||||||
* to prevent the cache from keeping it stored until the plugin is disabled.
|
* if (!openInv.disableSaving() && openInv.isPlayerLoaded(player.getUniqueId())) {
|
||||||
* <p>
|
* Player openInvLoadedPlayer = openInv.loadPlayer(myInUsePlayer);
|
||||||
* When using a Player object from OpenInv, you must handle the Player coming online, replacing
|
* if (openInvLoadedPlayer != player) {
|
||||||
* your Player reference with the Player from the PlayerJoinEvent. In addition, you must change
|
* // The copy loaded by OpenInv is not the same as our loaded copy. Push our changes.
|
||||||
* any values in the Player to reflect any unsaved alterations to the existing Player which do
|
* copyPlayerModifications(player, openInvLoadedPlayer);
|
||||||
* not affect the inventory or ender chest contents.
|
* }
|
||||||
* <p>
|
* // OpenInv will handle saving data when the player is unloaded.
|
||||||
* OpenInv only saves player data when unloading a Player from the cache, and then only if
|
* // Optionally, to be sure our changes will persist, save now.
|
||||||
* {@link #disableSaving()} returns false. If you are making changes that OpenInv does not cause
|
* // openInvLoadedPlayer.saveData();
|
||||||
* to persist when a Player logs in as noted above, it is suggested that you manually call
|
* return openInvLoadedPlayer;
|
||||||
* {@link Player#saveData()} when releasing your reference to ensure your changes persist.
|
* }
|
||||||
*
|
*
|
||||||
* @param player the Player
|
* player.saveData();
|
||||||
* @param plugin the Plugin holding the reference to the Player
|
* return player;
|
||||||
* @throws IllegalStateException if the server version is unsupported
|
* }
|
||||||
|
* </pre>
|
||||||
*/
|
*/
|
||||||
void retainPlayer(@NotNull Player player, @NotNull Plugin plugin);
|
@Deprecated(forRemoval = true, since = "4.2.0")
|
||||||
|
default void retainPlayer(@NotNull Player player, @NotNull Plugin plugin) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a player's AnyChest setting.
|
* Sets a player's AnyChest setting.
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.lishid.openinv;
|
package com.lishid.openinv;
|
||||||
|
|
||||||
|
import com.lishid.openinv.internal.ISpecialInventory;
|
||||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||||
import com.lishid.openinv.util.InventoryAccess;
|
import com.lishid.openinv.util.InventoryAccess;
|
||||||
import com.lishid.openinv.util.Permissions;
|
import com.lishid.openinv.util.Permissions;
|
||||||
@@ -47,7 +48,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
record InventoryListener(OpenInv plugin) implements Listener {
|
record InventoryListener(OpenInv plugin) implements Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onInventoryClose(@NotNull final InventoryCloseEvent event) {
|
private void onInventoryClose(@NotNull final InventoryCloseEvent event) {
|
||||||
if (!(event.getPlayer() instanceof Player player)) {
|
if (!(event.getPlayer() instanceof Player player)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -55,10 +56,20 @@ record InventoryListener(OpenInv plugin) implements Listener {
|
|||||||
if (this.plugin.getPlayerSilentChestStatus(player)) {
|
if (this.plugin.getPlayerSilentChestStatus(player)) {
|
||||||
this.plugin.getAnySilentContainer().deactivateContainer(player);
|
this.plugin.getAnySilentContainer().deactivateContainer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISpecialInventory specialInventory = InventoryAccess.getEnderChest(event.getInventory());
|
||||||
|
if (specialInventory != null) {
|
||||||
|
this.plugin.handleCloseInventory(event.getPlayer(), specialInventory);
|
||||||
|
} else {
|
||||||
|
specialInventory = InventoryAccess.getPlayerInventory(event.getInventory());
|
||||||
|
if (specialInventory != null) {
|
||||||
|
this.plugin.handleCloseInventory(event.getPlayer(), specialInventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onInventoryClick(@NotNull final InventoryClickEvent event) {
|
private void onInventoryClick(@NotNull final InventoryClickEvent event) {
|
||||||
if (handleInventoryInteract(event)) {
|
if (handleInventoryInteract(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -92,7 +103,7 @@ record InventoryListener(OpenInv plugin) implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onInventoryDrag(@NotNull final InventoryDragEvent event) {
|
private void onInventoryDrag(@NotNull final InventoryDragEvent event) {
|
||||||
if (handleInventoryInteract(event)) {
|
if (handleInventoryInteract(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
41
plugin/src/main/java/com/lishid/openinv/OfflineHandler.java
Normal file
41
plugin/src/main/java/com/lishid/openinv/OfflineHandler.java
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.lishid.openinv;
|
||||||
|
|
||||||
|
import com.lishid.openinv.internal.ISpecialInventory;
|
||||||
|
import com.lishid.openinv.util.Permissions;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
record OfflineHandler(
|
||||||
|
@NotNull BiFunction<Map<UUID, ? extends ISpecialInventory>, UUID, ISpecialInventory> fetch,
|
||||||
|
@NotNull Consumer<@NotNull ISpecialInventory> handle) {
|
||||||
|
|
||||||
|
static final OfflineHandler REMOVE_AND_CLOSE = new OfflineHandler(
|
||||||
|
Map::remove,
|
||||||
|
inventory -> OpenInv.ejectViewers(inventory, viewer -> true)
|
||||||
|
);
|
||||||
|
|
||||||
|
static final OfflineHandler REQUIRE_PERMISSIONS = new OfflineHandler(
|
||||||
|
Map::get,
|
||||||
|
inventory -> OpenInv.ejectViewers(inventory, viewer -> !Permissions.OPENOFFLINE.hasPermission(viewer))
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package com.lishid.openinv;
|
package com.lishid.openinv;
|
||||||
|
|
||||||
import com.google.common.collect.HashMultimap;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import com.lishid.openinv.commands.ContainerSettingCommand;
|
import com.lishid.openinv.commands.ContainerSettingCommand;
|
||||||
import com.lishid.openinv.commands.OpenInvCommand;
|
import com.lishid.openinv.commands.OpenInvCommand;
|
||||||
import com.lishid.openinv.commands.SearchContainerCommand;
|
import com.lishid.openinv.commands.SearchContainerCommand;
|
||||||
@@ -27,23 +25,19 @@ import com.lishid.openinv.internal.IAnySilentContainer;
|
|||||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||||
import com.lishid.openinv.internal.ISpecialInventory;
|
import com.lishid.openinv.internal.ISpecialInventory;
|
||||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||||
import com.lishid.openinv.listeners.InventoryListener;
|
|
||||||
import com.lishid.openinv.listeners.PlayerListener;
|
|
||||||
import com.lishid.openinv.listeners.PluginListener;
|
|
||||||
import com.lishid.openinv.util.Cache;
|
|
||||||
import com.lishid.openinv.util.ConfigUpdater;
|
import com.lishid.openinv.util.ConfigUpdater;
|
||||||
import com.lishid.openinv.util.InternalAccessor;
|
|
||||||
import com.lishid.openinv.util.LanguageManager;
|
import com.lishid.openinv.util.LanguageManager;
|
||||||
import com.lishid.openinv.util.Permissions;
|
import com.lishid.openinv.util.Permissions;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import net.md_5.bungee.api.ChatMessageType;
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -56,10 +50,8 @@ import org.bukkit.entity.HumanEntity;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryView;
|
import org.bukkit.inventory.InventoryView;
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -70,63 +62,21 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
*/
|
*/
|
||||||
public class OpenInv extends JavaPlugin implements IOpenInv {
|
public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||||
|
|
||||||
private final Map<String, ISpecialPlayerInventory> inventories = new HashMap<>();
|
private final Map<UUID, ISpecialPlayerInventory> inventories = new ConcurrentHashMap<>();
|
||||||
private final Map<String, ISpecialEnderChest> enderChests = new HashMap<>();
|
private final Map<UUID, ISpecialEnderChest> enderChests = new ConcurrentHashMap<>();
|
||||||
private final Multimap<String, Class<? extends Plugin>> pluginUsage = HashMultimap.create();
|
|
||||||
|
|
||||||
private final Cache<String, Player> playerCache = new Cache<>(300000L,
|
|
||||||
value -> {
|
|
||||||
String key = OpenInv.this.getPlayerID(value);
|
|
||||||
|
|
||||||
return OpenInv.this.inventories.containsKey(key)
|
|
||||||
&& OpenInv.this.inventories.get(key).isInUse()
|
|
||||||
|| OpenInv.this.enderChests.containsKey(key)
|
|
||||||
&& OpenInv.this.enderChests.get(key).isInUse()
|
|
||||||
|| OpenInv.this.pluginUsage.containsKey(key);
|
|
||||||
},
|
|
||||||
value -> {
|
|
||||||
String key = OpenInv.this.getPlayerID(value);
|
|
||||||
|
|
||||||
// Check if inventory is stored, and if it is, remove it and eject all viewers
|
|
||||||
if (OpenInv.this.inventories.containsKey(key)) {
|
|
||||||
Inventory inv = OpenInv.this.inventories.remove(key).getBukkitInventory();
|
|
||||||
List<HumanEntity> viewers = new ArrayList<>(inv.getViewers());
|
|
||||||
for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) {
|
|
||||||
entity.closeInventory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if ender chest is stored, and if it is, remove it and eject all viewers
|
|
||||||
if (OpenInv.this.enderChests.containsKey(key)) {
|
|
||||||
Inventory inv = OpenInv.this.enderChests.remove(key).getBukkitInventory();
|
|
||||||
List<HumanEntity> viewers = new ArrayList<>(inv.getViewers());
|
|
||||||
for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) {
|
|
||||||
entity.closeInventory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!OpenInv.this.disableSaving() && !value.isOnline()) {
|
|
||||||
value.saveData();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
private InternalAccessor accessor;
|
private InternalAccessor accessor;
|
||||||
private LanguageManager languageManager;
|
private LanguageManager languageManager;
|
||||||
private boolean isSpigot = false;
|
private boolean isSpigot = false;
|
||||||
|
private OfflineHandler offlineHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evicts all viewers lacking cross-world permissions from a Player's inventory.
|
* Evict all viewers lacking cross-world permissions from a Player's inventory.
|
||||||
*
|
*
|
||||||
* @param player the Player
|
* @param player the Player
|
||||||
*/
|
*/
|
||||||
public void changeWorld(final Player player) {
|
void changeWorld(@NotNull Player player) {
|
||||||
|
UUID key = player.getUniqueId();
|
||||||
String key = this.getPlayerID(player);
|
|
||||||
|
|
||||||
// Check if the player is cached. If not, neither of their inventories is open.
|
|
||||||
if (!this.playerCache.containsKey(key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.inventories.containsKey(key)) {
|
if (this.inventories.containsKey(key)) {
|
||||||
kickCrossWorldViewers(player, this.inventories.get(key));
|
kickCrossWorldViewers(player, this.inventories.get(key));
|
||||||
@@ -137,15 +87,19 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void kickCrossWorldViewers(Player player, ISpecialInventory inventory) {
|
private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||||
List<HumanEntity> viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers());
|
ejectViewers(
|
||||||
for (HumanEntity human : viewers) {
|
inventory,
|
||||||
// If player has permission or is in the same world, allow continued access
|
viewer ->
|
||||||
// Just in case, also allow null worlds.
|
!Permissions.CROSSWORLD.hasPermission(viewer)
|
||||||
if (Permissions.CROSSWORLD.hasPermission(human) || Objects.equals(human.getWorld(), player.getWorld())) {
|
&& Objects.equals(viewer.getWorld(), player.getWorld()));
|
||||||
continue;
|
}
|
||||||
|
|
||||||
|
static void ejectViewers(@NotNull ISpecialInventory inventory, Predicate<HumanEntity> predicate) {
|
||||||
|
for (HumanEntity viewer : new ArrayList<>(inventory.getBukkitInventory().getViewers())) {
|
||||||
|
if (predicate.test(viewer)) {
|
||||||
|
viewer.closeInventory();
|
||||||
}
|
}
|
||||||
human.closeInventory();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +113,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
* @param rawSlot the raw slot in the view
|
* @param rawSlot the raw slot in the view
|
||||||
* @return the converted slot number
|
* @return the converted slot number
|
||||||
*/
|
*/
|
||||||
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
|
int convertToPlayerSlot(InventoryView view, int rawSlot) {
|
||||||
return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot);
|
return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,9 +122,13 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
return this.getConfig().getBoolean("settings.disable-saving", false);
|
return this.getConfig().getBoolean("settings.disable-saving", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public IAnySilentContainer getAnySilentContainer() {
|
public boolean disableOfflineAccess() {
|
||||||
|
return this.getConfig().getBoolean("settings.disable-offline-access", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull IAnySilentContainer getAnySilentContainer() {
|
||||||
return this.accessor.getAnySilentContainer();
|
return this.accessor.getAnySilentContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,31 +160,31 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
return this.getConfig().getBoolean("toggles.silent-chest." + this.getPlayerID(offline), defaultState);
|
return this.getConfig().getBoolean("toggles.silent-chest." + this.getPlayerID(offline), defaultState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online)
|
public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online)
|
||||||
throws InstantiationException {
|
throws InstantiationException {
|
||||||
String id = this.getPlayerID(player);
|
UUID key = player.getUniqueId();
|
||||||
if (this.enderChests.containsKey(id)) {
|
|
||||||
return this.enderChests.get(id);
|
if (this.enderChests.containsKey(key)) {
|
||||||
|
return this.enderChests.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player, online);
|
ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player, online);
|
||||||
this.enderChests.put(id, inv);
|
this.enderChests.put(key, inv);
|
||||||
this.playerCache.put(id, player);
|
|
||||||
return inv;
|
return inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online)
|
public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online)
|
||||||
throws InstantiationException {
|
throws InstantiationException {
|
||||||
String id = this.getPlayerID(player);
|
UUID key = player.getUniqueId();
|
||||||
if (this.inventories.containsKey(id)) {
|
|
||||||
return this.inventories.get(id);
|
if (this.inventories.containsKey(key)) {
|
||||||
|
return this.inventories.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player, online);
|
ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player, online);
|
||||||
this.inventories.put(id, inv);
|
this.inventories.put(key, inv);
|
||||||
this.playerCache.put(id, player);
|
|
||||||
return inv;
|
return inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,20 +194,28 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
public boolean isPlayerLoaded(@NotNull UUID playerUuid) {
|
||||||
|
return this.inventories.containsKey(playerUuid) || this.enderChests.containsKey(playerUuid);
|
||||||
|
}
|
||||||
|
|
||||||
String key = this.getPlayerID(offline);
|
@Override
|
||||||
if (this.playerCache.containsKey(key)) {
|
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||||
return this.playerCache.get(key);
|
UUID key = offline.getUniqueId();
|
||||||
|
|
||||||
|
if (this.inventories.containsKey(key)) {
|
||||||
|
return (Player) this.inventories.get(key).getPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.enderChests.containsKey(key)) {
|
||||||
|
return (Player) this.enderChests.get(key).getPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
Player player = offline.getPlayer();
|
Player player = offline.getPlayer();
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
this.playerCache.put(key, player);
|
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isSupportedVersion()) {
|
if (disableOfflineAccess() || !this.isSupportedVersion()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,10 +233,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player != null) {
|
|
||||||
this.playerCache.put(key, player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,9 +300,23 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isSupportedVersion()) {
|
Stream.concat(inventories.values().stream(), enderChests.values().stream())
|
||||||
this.playerCache.invalidateAll();
|
.map(inventory -> {
|
||||||
}
|
// Cheat a bit - rather than stream twice, evict all viewers during remapping.
|
||||||
|
ejectViewers(inventory, viewer -> true);
|
||||||
|
if (inventory.getPlayer() instanceof Player player) {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.distinct()
|
||||||
|
.forEach(player -> {
|
||||||
|
if (!player.isOnline()) {
|
||||||
|
player = accessor.getPlayerDataManager().inject(player);
|
||||||
|
}
|
||||||
|
player.saveData();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -355,6 +331,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
this.accessor = new InternalAccessor(this);
|
this.accessor = new InternalAccessor(this);
|
||||||
|
|
||||||
this.languageManager = new LanguageManager(this, "en_us");
|
this.languageManager = new LanguageManager(this, "en_us");
|
||||||
|
this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class.forName("org.bukkit.entity.Player$Spigot");
|
Class.forName("org.bukkit.entity.Player$Spigot");
|
||||||
@@ -371,7 +348,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
|
|
||||||
// Register listeners
|
// Register listeners
|
||||||
pm.registerEvents(new PlayerListener(this), this);
|
pm.registerEvents(new PlayerListener(this), this);
|
||||||
pm.registerEvents(new PluginListener(this), this);
|
|
||||||
pm.registerEvents(new InventoryListener(this), this);
|
pm.registerEvents(new InventoryListener(this), this);
|
||||||
|
|
||||||
// Register commands to their executors
|
// Register commands to their executors
|
||||||
@@ -390,7 +366,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
private void sendVersionError(Consumer<String> messageMethod) {
|
private void sendVersionError(Consumer<String> messageMethod) {
|
||||||
if (!this.accessor.isSupported()) {
|
if (!this.accessor.isSupported()) {
|
||||||
messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported.");
|
messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported.");
|
||||||
messageMethod.accept("Please obtain an appropriate version here: " + this.accessor.getReleasesLink());
|
messageMethod.accept("Please download the correct version of OpenInv here: " + this.accessor.getReleasesLink());
|
||||||
}
|
}
|
||||||
if (!isSpigot) {
|
if (!isSpigot) {
|
||||||
messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread");
|
messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread");
|
||||||
@@ -418,40 +394,10 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void releaseAllPlayers(final Plugin plugin) {
|
|
||||||
Iterator<Map.Entry<String, Class<? extends Plugin>>> iterator = this.pluginUsage.entries().iterator();
|
|
||||||
|
|
||||||
if (!iterator.hasNext()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<String, Class<? extends Plugin>> entry = iterator.next(); iterator.hasNext(); entry = iterator.next()) {
|
|
||||||
if (entry.getValue().equals(plugin.getClass())) {
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releasePlayer(@NotNull final Player player, @NotNull final Plugin plugin) {
|
public void reloadConfig() {
|
||||||
String key = this.getPlayerID(player);
|
super.reloadConfig();
|
||||||
|
this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
|
||||||
if (!this.pluginUsage.containsEntry(key, plugin.getClass())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pluginUsage.remove(key, plugin.getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void retainPlayer(@NotNull final Player player, @NotNull final Plugin plugin) {
|
|
||||||
String key = this.getPlayerID(player);
|
|
||||||
|
|
||||||
if (this.pluginUsage.containsEntry(key, plugin.getClass())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pluginUsage.put(key, plugin.getClass());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -464,27 +410,71 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
* Method for handling a Player going offline.
|
* Method for handling a Player going offline.
|
||||||
*
|
*
|
||||||
* @param player the Player
|
* @param player the Player
|
||||||
* @throws IllegalStateException if the server version is unsupported
|
|
||||||
*/
|
*/
|
||||||
public void setPlayerOffline(final Player player) {
|
void setPlayerOffline(@NotNull Player player) {
|
||||||
|
setPlayerOffline(player, offlineHandler);
|
||||||
|
}
|
||||||
|
|
||||||
String key = this.getPlayerID(player);
|
private void setPlayerOffline(@NotNull OfflinePlayer player, @NotNull OfflineHandler handler) {
|
||||||
|
UUID key = player.getUniqueId();
|
||||||
|
|
||||||
// Check if the player is cached. If not, neither of their inventories is open.
|
setPlayerOffline(inventories, key, handler);
|
||||||
if (!this.playerCache.containsKey(key)) {
|
setPlayerOffline(enderChests, key, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPlayerOffline(
|
||||||
|
@NotNull Map<UUID, ? extends ISpecialInventory> map,
|
||||||
|
@NotNull UUID key,
|
||||||
|
@NotNull OfflineHandler handler) {
|
||||||
|
ISpecialInventory inventory = handler.fetch().apply(map, key);
|
||||||
|
if (inventory == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inventory.setPlayerOffline();
|
||||||
|
if (!inventory.isInUse()) {
|
||||||
|
map.remove(key);
|
||||||
|
} else {
|
||||||
|
handler.handle().accept(inventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleCloseInventory(@NotNull HumanEntity exViewer, @NotNull ISpecialInventory inventory) {
|
||||||
|
Map<UUID, ? extends ISpecialInventory> map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests;
|
||||||
|
UUID key = inventory.getPlayer().getUniqueId();
|
||||||
|
@Nullable ISpecialInventory loaded = map.get(key);
|
||||||
|
|
||||||
|
if (loaded == null) {
|
||||||
|
// Loaded inventory has already been removed. Removal will handle saving if necessary.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace stored player with our own version
|
if (loaded != inventory) {
|
||||||
this.playerCache.put(key, this.accessor.getPlayerDataManager().inject(player));
|
Inventory bukkitInventory = inventory.getBukkitInventory();
|
||||||
|
// Just in case, respect contents of the inventory that was just used.
|
||||||
if (this.inventories.containsKey(key)) {
|
loaded.getBukkitInventory().setContents(bukkitInventory.getContents());
|
||||||
this.inventories.get(key).setPlayerOffline();
|
// We need to close this inventory to reduce risk of duplication bugs if the user is offline.
|
||||||
|
// We don't want to risk recursively closing the same inventory repeatedly, so we schedule dumping viewers.
|
||||||
|
// Worst case we schedule a couple redundant tasks if several people had the inventory open.
|
||||||
|
if (!bukkitInventory.getViewers().isEmpty()) {
|
||||||
|
getServer().getScheduler().runTask(this, () -> ejectViewers(inventory, viewer -> true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.enderChests.containsKey(key)) {
|
// Schedule task to check in use status later this tick. Closing user is still in viewer list.
|
||||||
this.enderChests.get(key).setPlayerOffline();
|
getServer().getScheduler().runTask(this, () -> {
|
||||||
}
|
if (loaded.isInUse()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-fetch from map - prevents duplicate saves on multi-close.
|
||||||
|
ISpecialInventory current = map.remove(key);
|
||||||
|
|
||||||
|
if (!disableSaving()
|
||||||
|
&& current != null
|
||||||
|
&& current.getPlayer() instanceof Player player && !player.isOnline()) {
|
||||||
|
this.accessor.getPlayerDataManager().inject(player).saveData();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -493,31 +483,34 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
* @param player the Player
|
* @param player the Player
|
||||||
* @throws IllegalStateException if the server version is unsupported
|
* @throws IllegalStateException if the server version is unsupported
|
||||||
*/
|
*/
|
||||||
public void setPlayerOnline(final Player player) {
|
void setPlayerOnline(@NotNull Player player) {
|
||||||
|
setPlayerOnline(inventories, player, player::updateInventory);
|
||||||
|
setPlayerOnline(enderChests, player, null);
|
||||||
|
}
|
||||||
|
|
||||||
String key = this.getPlayerID(player);
|
private void setPlayerOnline(
|
||||||
|
@NotNull Map<UUID, ? extends ISpecialInventory> map,
|
||||||
|
@NotNull Player player,
|
||||||
|
@Nullable Runnable task) {
|
||||||
|
ISpecialInventory inventory = map.get(player.getUniqueId());
|
||||||
|
|
||||||
// Check if the player is cached. If not, neither of their inventories is open.
|
if (inventory == null) {
|
||||||
if (!this.playerCache.containsKey(key)) {
|
// Inventory not open.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.playerCache.put(key, player);
|
inventory.setPlayerOnline(player);
|
||||||
|
|
||||||
if (this.inventories.containsKey(key)) {
|
// Eject viewers lacking permission.
|
||||||
this.inventories.get(key).setPlayerOnline(player);
|
ejectViewers(
|
||||||
new BukkitRunnable() {
|
inventory,
|
||||||
@Override
|
viewer ->
|
||||||
public void run() {
|
!Permissions.OPENONLINE.hasPermission(viewer)
|
||||||
if (player.isOnline()) {
|
|| !Permissions.CROSSWORLD.hasPermission(viewer)
|
||||||
player.updateInventory();
|
&& !Objects.equals(viewer.getWorld(), inventory.getPlayer().getWorld()));
|
||||||
}
|
|
||||||
}
|
|
||||||
}.runTask(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.enderChests.containsKey(key)) {
|
if (task != null) {
|
||||||
this.enderChests.get(key).setPlayerOnline(player);
|
getServer().getScheduler().runTask(this, task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +522,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unload(@NotNull final OfflinePlayer offline) {
|
public void unload(@NotNull final OfflinePlayer offline) {
|
||||||
this.playerCache.invalidate(this.getPlayerID(offline));
|
setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -32,22 +32,22 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
record PlayerListener(OpenInv plugin) implements Listener {
|
record PlayerListener(OpenInv plugin) implements Listener {
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onPlayerJoin(@NotNull PlayerJoinEvent event) {
|
private void onPlayerJoin(@NotNull PlayerJoinEvent event) {
|
||||||
plugin.setPlayerOnline(event.getPlayer());
|
plugin.setPlayerOnline(event.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
public void onPlayerQuit(@NotNull PlayerQuitEvent event) {
|
private void onPlayerQuit(@NotNull PlayerQuitEvent event) {
|
||||||
plugin.setPlayerOffline(event.getPlayer());
|
plugin.setPlayerOffline(event.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onWorldChange(@NotNull PlayerChangedWorldEvent event) {
|
private void onWorldChange(@NotNull PlayerChangedWorldEvent event) {
|
||||||
plugin.changeWorld(event.getPlayer());
|
plugin.changeWorld(event.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
public void onPlayerInteract(@NotNull PlayerInteractEvent event) {
|
private void onPlayerInteract(@NotNull PlayerInteractEvent event) {
|
||||||
|
|
||||||
// Do not cancel 3rd party plugins' custom events
|
// Do not cancel 3rd party plugins' custom events
|
||||||
if (!PlayerInteractEvent.class.equals(event.getClass())) {
|
if (!PlayerInteractEvent.class.equals(event.getClass())) {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -133,7 +133,7 @@ public class OpenInvCommand implements TabExecutor {
|
|||||||
boolean online = target.isOnline();
|
boolean online = target.isOnline();
|
||||||
|
|
||||||
if (!online) {
|
if (!online) {
|
||||||
if (Permissions.OPENOFFLINE.hasPermission(player)) {
|
if (!plugin.disableOfflineAccess() && Permissions.OPENOFFLINE.hasPermission(player)) {
|
||||||
// Try loading the player's data
|
// Try loading the player's data
|
||||||
onlineTarget = this.plugin.loadPlayer(target);
|
onlineTarget = this.plugin.loadPlayer(target);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.lishid.openinv.listeners;
|
|
||||||
|
|
||||||
import com.lishid.openinv.OpenInv;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.server.PluginDisableEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for plugin-related events.
|
|
||||||
*
|
|
||||||
* @author Jikoo
|
|
||||||
*/
|
|
||||||
public class PluginListener implements Listener {
|
|
||||||
|
|
||||||
private final OpenInv plugin;
|
|
||||||
|
|
||||||
public PluginListener(OpenInv plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginDisable(PluginDisableEvent event) {
|
|
||||||
plugin.releaseAllPlayers(event.getPlugin());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,187 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.lishid.openinv.util;
|
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import com.google.common.collect.TreeMultimap;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A minimal thread-safe time-based cache implementation backed by a HashMap and TreeMultimap.
|
|
||||||
*
|
|
||||||
* @author Jikoo
|
|
||||||
*/
|
|
||||||
public class Cache<K, V> {
|
|
||||||
|
|
||||||
private final Map<K, V> internal;
|
|
||||||
private final Multimap<Long, K> expiry;
|
|
||||||
private final long retention;
|
|
||||||
private final Predicate<V> inUseCheck;
|
|
||||||
private final Consumer<V> postRemoval;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a Cache with the specified retention duration, in use function, and post-removal function.
|
|
||||||
*
|
|
||||||
* @param retention duration after which keys are automatically invalidated if not in use
|
|
||||||
* @param inUseCheck Predicate used to check if a key is considered in use
|
|
||||||
* @param postRemoval Consumer used to perform any operations required when a key is invalidated
|
|
||||||
*/
|
|
||||||
public Cache(final long retention, final Predicate<V> inUseCheck, final Consumer<V> postRemoval) {
|
|
||||||
this.internal = new HashMap<>();
|
|
||||||
|
|
||||||
this.expiry = TreeMultimap.create(Long::compareTo, (k1, k2) -> Objects.equals(k1, k2) ? 0 : 1);
|
|
||||||
|
|
||||||
this.retention = retention;
|
|
||||||
this.inUseCheck = inUseCheck;
|
|
||||||
this.postRemoval = postRemoval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a key and value pair. Keys are unique. Using an existing key will cause the old value to
|
|
||||||
* be overwritten and the expiration timer to be reset.
|
|
||||||
*
|
|
||||||
* @param key key with which the specified value is to be associated
|
|
||||||
* @param value value to be associated with the specified key
|
|
||||||
*/
|
|
||||||
public void put(final K key, final V value) {
|
|
||||||
// Invalidate key - runs lazy check and ensures value won't be cleaned up early
|
|
||||||
this.invalidate(key);
|
|
||||||
|
|
||||||
synchronized (this.internal) {
|
|
||||||
this.internal.put(key, value);
|
|
||||||
this.expiry.put(System.currentTimeMillis() + this.retention, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value to which the specified key is mapped, or null if no value is mapped for the key.
|
|
||||||
*
|
|
||||||
* @param key the key whose associated value is to be returned
|
|
||||||
* @return the value to which the specified key is mapped, or null if no value is mapped for the key
|
|
||||||
*/
|
|
||||||
public V get(final K key) {
|
|
||||||
// Run lazy check to clean cache
|
|
||||||
this.lazyCheck();
|
|
||||||
|
|
||||||
synchronized (this.internal) {
|
|
||||||
return this.internal.get(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the specified key is mapped to a value.
|
|
||||||
*
|
|
||||||
* @param key key to check if a mapping exists for
|
|
||||||
* @return true if a mapping exists for the specified key
|
|
||||||
*/
|
|
||||||
public boolean containsKey(final K key) {
|
|
||||||
// Run lazy check to clean cache
|
|
||||||
this.lazyCheck();
|
|
||||||
|
|
||||||
synchronized (this.internal) {
|
|
||||||
return this.internal.containsKey(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forcibly invalidates a key, even if it is considered to be in use.
|
|
||||||
*
|
|
||||||
* @param key key to invalidate
|
|
||||||
*/
|
|
||||||
public void invalidate(final K key) {
|
|
||||||
// Run lazy check to clean cache
|
|
||||||
this.lazyCheck();
|
|
||||||
|
|
||||||
synchronized (this.internal) {
|
|
||||||
if (!this.internal.containsKey(key)) {
|
|
||||||
// Value either not present or cleaned by lazy check. Either way, we're good
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove stored object
|
|
||||||
this.internal.remove(key);
|
|
||||||
|
|
||||||
// Remove expiration entry - prevents more work later, plus prevents issues with values invalidating early
|
|
||||||
for (Iterator<Map.Entry<Long, K>> iterator = this.expiry.entries().iterator(); iterator.hasNext();) {
|
|
||||||
if (key.equals(iterator.next().getValue())) {
|
|
||||||
iterator.remove();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forcibly invalidates all keys, even if they are considered to be in use.
|
|
||||||
*/
|
|
||||||
public void invalidateAll() {
|
|
||||||
synchronized (this.internal) {
|
|
||||||
for (V value : this.internal.values()) {
|
|
||||||
this.postRemoval.accept(value);
|
|
||||||
}
|
|
||||||
this.expiry.clear();
|
|
||||||
this.internal.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidate all expired keys that are not considered in use. If a key is expired but is
|
|
||||||
* considered in use by the provided Function, its expiration time is reset.
|
|
||||||
*/
|
|
||||||
private void lazyCheck() {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
synchronized (this.internal) {
|
|
||||||
List<K> inUse = new ArrayList<>();
|
|
||||||
for (Iterator<Map.Entry<Long, K>> iterator = this.expiry.entries().iterator(); iterator
|
|
||||||
.hasNext();) {
|
|
||||||
Map.Entry<Long, K> entry = iterator.next();
|
|
||||||
|
|
||||||
if (entry.getKey() > now) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator.remove();
|
|
||||||
|
|
||||||
if (this.inUseCheck.test(this.internal.get(entry.getValue()))) {
|
|
||||||
inUse.add(entry.getValue());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
V value = this.internal.remove(entry.getValue());
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.postRemoval.accept(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
long nextExpiry = now + this.retention;
|
|
||||||
for (K value : inUse) {
|
|
||||||
this.expiry.put(nextExpiry, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -25,13 +25,7 @@ import java.util.Set;
|
|||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
public class ConfigUpdater {
|
public record ConfigUpdater(OpenInv plugin) {
|
||||||
|
|
||||||
private final OpenInv plugin;
|
|
||||||
|
|
||||||
public ConfigUpdater(OpenInv plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkForUpdates() {
|
public void checkForUpdates() {
|
||||||
final int version = plugin.getConfig().getInt("config-version", 1);
|
final int version = plugin.getConfig().getInt("config-version", 1);
|
||||||
@@ -60,6 +54,9 @@ public class ConfigUpdater {
|
|||||||
if (version < 4) {
|
if (version < 4) {
|
||||||
updateConfig3To4();
|
updateConfig3To4();
|
||||||
}
|
}
|
||||||
|
if (version < 5) {
|
||||||
|
updateConfig4To5();
|
||||||
|
}
|
||||||
|
|
||||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||||
plugin.saveConfig();
|
plugin.saveConfig();
|
||||||
@@ -68,6 +65,13 @@ public class ConfigUpdater {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateConfig4To5() {
|
||||||
|
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||||
|
plugin.getConfig().set("settings.disable-offline-access", false);
|
||||||
|
plugin.getConfig().set("config-version", 5);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void updateConfig3To4() {
|
private void updateConfig3To4() {
|
||||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||||
plugin.getConfig().set("notify", null);
|
plugin.getConfig().set("notify", null);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.lishid.openinv.util;
|
package com.lishid.openinv.util;
|
||||||
|
|
||||||
import org.bukkit.permissions.Permissible;
|
import org.bukkit.permissions.Permissible;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public enum Permissions {
|
public enum Permissions {
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ public enum Permissions {
|
|||||||
this.uninheritable = uninheritable;
|
this.uninheritable = uninheritable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPermission(Permissible permissible) {
|
public boolean hasPermission(@NotNull Permissible permissible) {
|
||||||
|
|
||||||
boolean hasPermission = permissible.hasPermission(permission);
|
boolean hasPermission = permissible.hasPermission(permission);
|
||||||
if (uninheritable || hasPermission || permissible.isPermissionSet(permission)) {
|
if (uninheritable || hasPermission || permissible.isPermissionSet(permission)) {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
config-version: 4
|
config-version: 5
|
||||||
settings:
|
settings:
|
||||||
|
disable-offline-access: false
|
||||||
disable-saving: false
|
disable-saving: false
|
||||||
locale: 'en_us'
|
locale: 'en_us'
|
||||||
|
Reference in New Issue
Block a user