[Idea]: Folia support for OpenInv #196

Closed
reabuc wants to merge 137 commits from master into master
68 changed files with 6724 additions and 2740 deletions
Showing only changes of commit 6407f1d8e4 - Show all commits

View File

@@ -70,51 +70,114 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
private boolean isSpigot = false;
private OfflineHandler offlineHandler;
/**
* Evict all viewers lacking cross-world permissions from a Player's inventory.
*
* @param player the Player
*/
void changeWorld(@NotNull Player player) {
UUID key = player.getUniqueId();
if (this.inventories.containsKey(key)) {
kickCrossWorldViewers(player, this.inventories.get(key));
@Override
public void reloadConfig() {
super.reloadConfig();
this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
}
if (this.enderChests.containsKey(key)) {
kickCrossWorldViewers(player, this.enderChests.get(key));
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!isSpigot || !this.accessor.isSupported()) {
this.sendVersionError(sender::sendMessage);
return true;
}
return false;
}
private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ejectViewers(
inventory,
viewer ->
!Permissions.CROSSWORLD.hasPermission(viewer)
&& Objects.equals(viewer.getWorld(), player.getWorld()));
@Override
public void onDisable() {
if (this.disableSaving()) {
return;
}
static void ejectViewers(@NotNull ISpecialInventory inventory, Predicate<HumanEntity> predicate) {
for (HumanEntity viewer : new ArrayList<>(inventory.getBukkitInventory().getViewers())) {
if (predicate.test(viewer)) {
viewer.closeInventory();
Stream.concat(inventories.values().stream(), enderChests.values().stream())
.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
public void onEnable() {
// Save default configuration if not present.
this.saveDefaultConfig();
// Get plugin manager
PluginManager pm = this.getServer().getPluginManager();
this.accessor = new InternalAccessor(this);
this.languageManager = new LanguageManager(this, "en_us");
this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
try {
Class.forName("org.bukkit.entity.Player$Spigot");
isSpigot = true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
isSpigot = false;
}
// Version check
if (isSpigot && this.accessor.isSupported()) {
// Update existing configuration. May require internal access.
new ConfigUpdater(this).checkForUpdates();
// Register listeners
pm.registerEvents(new PlayerListener(this), this);
pm.registerEvents(new InventoryListener(this), this);
// Register commands to their executors
this.setCommandExecutor(new OpenInvCommand(this), "openinv", "openender");
this.setCommandExecutor(new SearchContainerCommand(this), "searchcontainer");
this.setCommandExecutor(new SearchInvCommand(this), "searchinv", "searchender");
this.setCommandExecutor(new SearchEnchantCommand(this), "searchenchant");
this.setCommandExecutor(new ContainerSettingCommand(this), "silentcontainer", "anycontainer");
} else {
this.sendVersionError(this.getLogger()::warning);
}
}
private void setCommandExecutor(@NotNull CommandExecutor executor, String @NotNull ... commands) {
for (String commandName : commands) {
PluginCommand command = this.getCommand(commandName);
if (command != null) {
command.setExecutor(executor);
}
}
}
/**
* Convert a raw slot number into a player inventory slot number.
*
* <p>Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular
* player inventory slot number.
*
* @param view the open inventory view
* @param rawSlot the raw slot in the view
* @return the converted slot number
*/
int convertToPlayerSlot(InventoryView view, int rawSlot) {
return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot);
private void sendVersionError(@NotNull Consumer<String> messageMethod) {
if (!this.accessor.isSupported()) {
messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported.");
messageMethod.accept("Please download the correct version of OpenInv here: " + this.accessor.getReleasesLink());
}
if (!isSpigot) {
messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread");
messageMethod.accept("(https://www.spigotmc.org/threads/369724/ \"A Note on CraftBukkit\"), if you are");
messageMethod.accept("encountering an inconsistency with vanilla that prevents you from using Spigot,");
messageMethod.accept("that is considered a Spigot bug and should be reported as such.");
}
}
@Override
public boolean isSupportedVersion() {
return this.accessor != null && this.accessor.isSupported();
}
@Override
@@ -146,6 +209,12 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return this.getConfig().getBoolean("toggles.any-chest." + offline.getUniqueId(), defaultState);
}
@Override
public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) {
this.getConfig().set("toggles.any-chest." + offline.getUniqueId(), status);
this.saveConfig();
}
@Override
public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) {
boolean defaultState = false;
@@ -160,6 +229,12 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return this.getConfig().getBoolean("toggles.silent-chest." + offline.getUniqueId(), defaultState);
}
@Override
public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) {
this.getConfig().set("toggles.silent-chest." + offline.getUniqueId(), status);
this.saveConfig();
}
@Override
public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online)
throws InstantiationException {
@@ -189,8 +264,8 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
}
@Override
public boolean isSupportedVersion() {
return this.accessor != null && this.accessor.isSupported();
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
return this.accessor.getPlayerDataManager().openInventory(player, inventory);
}
@Override
@@ -237,20 +312,78 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
}
@Override
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
return this.accessor.getPlayerDataManager().openInventory(player, inventory);
public void unload(@NotNull final OfflinePlayer offline) {
setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE);
}
/**
* Evict all viewers lacking cross-world permissions when a {@link Player} changes worlds.
*
* @param player the Player
*/
void changeWorld(@NotNull Player player) {
UUID key = player.getUniqueId();
if (this.inventories.containsKey(key)) {
kickCrossWorldViewers(player, this.inventories.get(key));
}
if (this.enderChests.containsKey(key)) {
kickCrossWorldViewers(player, this.enderChests.get(key));
}
}
private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ejectViewers(
inventory,
viewer ->
!Permissions.CROSSWORLD.hasPermission(viewer)
&& Objects.equals(viewer.getWorld(), player.getWorld()));
}
/**
* Convert a raw slot number into a player inventory slot number.
*
* <p>Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular
* player inventory slot number.
*
* @param view the open inventory view
* @param rawSlot the raw slot in the view
* @return the converted slot number
*/
int convertToPlayerSlot(InventoryView view, int rawSlot) {
return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot);
}
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) {
return this.languageManager.getValue(key, getLocale(sender));
}
public @Nullable String getLocalizedMessage(
@NotNull CommandSender sender,
@NotNull String key,
String @NotNull ... replacements) {
return this.languageManager.getValue(key, getLocale(sender), replacements);
}
private @NotNull String getLocale(@NotNull CommandSender sender) {
if (sender instanceof Player) {
return ((Player) sender).getLocale();
} else {
return this.getConfig().getString("settings.locale", "en_us");
}
}
public void sendMessage(@NotNull CommandSender sender, @NotNull String key) {
String message = this.languageManager.getValue(key, getLocale(sender));
String message = getLocalizedMessage(sender, key);
if (message != null && !message.isEmpty()) {
sender.sendMessage(message);
}
}
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String... replacements) {
String message = this.languageManager.getValue(key, getLocale(sender), replacements);
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String @NotNull... replacements) {
String message = getLocalizedMessage(sender, key, replacements);
if (message != null && !message.isEmpty()) {
sender.sendMessage(message);
@@ -258,7 +391,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
}
public void sendSystemMessage(@NotNull Player player, @NotNull String key) {
String message = this.languageManager.getValue(key, getLocale(player));
String message = getLocalizedMessage(player, key);
if (message == null) {
return;
@@ -277,135 +410,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
}
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) {
return this.languageManager.getValue(key, getLocale(sender));
}
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key, String... replacements) {
return this.languageManager.getValue(key, getLocale(sender), replacements);
}
private @NotNull String getLocale(@NotNull CommandSender sender) {
if (sender instanceof Player) {
return ((Player) sender).getLocale();
} else {
return this.getConfig().getString("settings.locale", "en_us");
}
}
@Override
public void onDisable() {
if (this.disableSaving()) {
return;
}
Stream.concat(inventories.values().stream(), enderChests.values().stream())
.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
public void onEnable() {
// Save default configuration if not present.
this.saveDefaultConfig();
// Get plugin manager
PluginManager pm = this.getServer().getPluginManager();
this.accessor = new InternalAccessor(this);
this.languageManager = new LanguageManager(this, "en_us");
this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
try {
Class.forName("org.bukkit.entity.Player$Spigot");
isSpigot = true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
isSpigot = false;
}
// Version check
if (isSpigot && this.accessor.isSupported()) {
// Update existing configuration. May require internal access.
new ConfigUpdater(this).checkForUpdates();
// Register listeners
pm.registerEvents(new PlayerListener(this), this);
pm.registerEvents(new InventoryListener(this), this);
// Register commands to their executors
this.setCommandExecutor(new OpenInvCommand(this), "openinv", "openender");
this.setCommandExecutor(new SearchContainerCommand(this), "searchcontainer");
this.setCommandExecutor(new SearchInvCommand(this), "searchinv", "searchender");
this.setCommandExecutor(new SearchEnchantCommand(this), "searchenchant");
this.setCommandExecutor(new ContainerSettingCommand(this), "silentcontainer", "anycontainer");
} else {
this.sendVersionError(this.getLogger()::warning);
}
}
private void sendVersionError(Consumer<String> messageMethod) {
if (!this.accessor.isSupported()) {
messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported.");
messageMethod.accept("Please download the correct version of OpenInv here: " + this.accessor.getReleasesLink());
}
if (!isSpigot) {
messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread");
messageMethod.accept("(https://www.spigotmc.org/threads/369724/ \"A Note on CraftBukkit\"), if you are");
messageMethod.accept("encountering an inconsistency with vanilla that prevents you from using Spigot,");
messageMethod.accept("that is considered a Spigot bug and should be reported as such.");
}
}
private void setCommandExecutor(CommandExecutor executor, String... commands) {
for (String commandName : commands) {
PluginCommand command = this.getCommand(commandName);
if (command != null) {
command.setExecutor(executor);
}
}
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!isSpigot || !this.accessor.isSupported()) {
this.sendVersionError(sender::sendMessage);
return true;
}
return false;
}
@Override
public void reloadConfig() {
super.reloadConfig();
this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
}
@Override
public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) {
this.getConfig().set("toggles.any-chest." + offline.getUniqueId(), status);
this.saveConfig();
}
/**
* Method for handling a Player going offline.
*
@@ -448,6 +452,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return;
}
// This should only be possible if a plugin is doing funky things with our inventories.
if (loaded != inventory) {
Inventory bukkitInventory = inventory.getBukkitInventory();
// Just in case, respect contents of the inventory that was just used.
@@ -455,7 +460,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
// 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()) {
if (inventory.isInUse()) {
getServer().getScheduler().runTask(this, () -> ejectViewers(inventory, viewer -> true));
}
}
@@ -514,15 +519,13 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
}
}
@Override
public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) {
this.getConfig().set("toggles.silent-chest." + offline.getUniqueId(), status);
this.saveConfig();
static void ejectViewers(@NotNull ISpecialInventory inventory, @NotNull Predicate<@NotNull HumanEntity> predicate) {
Inventory bukkitInventory = inventory.getBukkitInventory();
for (HumanEntity viewer : new ArrayList<>(bukkitInventory.getViewers())) {
if (predicate.test(viewer)) {
viewer.closeInventory();
}
}
@Override
public void unload(@NotNull final OfflinePlayer offline) {
setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE);
}
}