[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 boolean isSpigot = false;
private OfflineHandler offlineHandler; private OfflineHandler offlineHandler;
/** @Override
* Evict all viewers lacking cross-world permissions from a Player's inventory. public void reloadConfig() {
* super.reloadConfig();
* @param player the Player this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
*/
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) { @Override
ejectViewers( public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
inventory, if (!isSpigot || !this.accessor.isSupported()) {
viewer -> this.sendVersionError(sender::sendMessage);
!Permissions.CROSSWORLD.hasPermission(viewer) return true;
&& Objects.equals(viewer.getWorld(), player.getWorld())); }
return false;
} }
static void ejectViewers(@NotNull ISpecialInventory inventory, Predicate<HumanEntity> predicate) { @Override
for (HumanEntity viewer : new ArrayList<>(inventory.getBukkitInventory().getViewers())) { public void onDisable() {
if (predicate.test(viewer)) { if (this.disableSaving()) {
viewer.closeInventory(); 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 setCommandExecutor(@NotNull CommandExecutor executor, String @NotNull ... commands) {
for (String commandName : commands) {
PluginCommand command = this.getCommand(commandName);
if (command != null) {
command.setExecutor(executor);
} }
} }
} }
/** private void sendVersionError(@NotNull Consumer<String> messageMethod) {
* Convert a raw slot number into a player inventory slot number. if (!this.accessor.isSupported()) {
* messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported.");
* <p>Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular messageMethod.accept("Please download the correct version of OpenInv here: " + this.accessor.getReleasesLink());
* player inventory slot number. }
* if (!isSpigot) {
* @param view the open inventory view messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread");
* @param rawSlot the raw slot in the view messageMethod.accept("(https://www.spigotmc.org/threads/369724/ \"A Note on CraftBukkit\"), if you are");
* @return the converted slot number 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.");
int convertToPlayerSlot(InventoryView view, int rawSlot) { }
return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot); }
@Override
public boolean isSupportedVersion() {
return this.accessor != null && this.accessor.isSupported();
} }
@Override @Override
@@ -146,6 +209,12 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return this.getConfig().getBoolean("toggles.any-chest." + offline.getUniqueId(), defaultState); 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 @Override
public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) { public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) {
boolean defaultState = false; boolean defaultState = false;
@@ -160,6 +229,12 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return this.getConfig().getBoolean("toggles.silent-chest." + offline.getUniqueId(), defaultState); 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 @Override
public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online)
throws InstantiationException { throws InstantiationException {
@@ -189,8 +264,8 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
} }
@Override @Override
public boolean isSupportedVersion() { public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
return this.accessor != null && this.accessor.isSupported(); return this.accessor.getPlayerDataManager().openInventory(player, inventory);
} }
@Override @Override
@@ -237,20 +312,78 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
} }
@Override @Override
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { public void unload(@NotNull final OfflinePlayer offline) {
return this.accessor.getPlayerDataManager().openInventory(player, inventory); 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) { 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()) { if (message != null && !message.isEmpty()) {
sender.sendMessage(message); sender.sendMessage(message);
} }
} }
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String... replacements) { public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String @NotNull... replacements) {
String message = this.languageManager.getValue(key, getLocale(sender), replacements); String message = getLocalizedMessage(sender, key, replacements);
if (message != null && !message.isEmpty()) { if (message != null && !message.isEmpty()) {
sender.sendMessage(message); sender.sendMessage(message);
@@ -258,7 +391,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
} }
public void sendSystemMessage(@NotNull Player player, @NotNull String key) { 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) { if (message == null) {
return; return;
@@ -277,135 +410,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message)); 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. * Method for handling a Player going offline.
* *
@@ -448,6 +452,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return; return;
} }
// This should only be possible if a plugin is doing funky things with our inventories.
if (loaded != inventory) { if (loaded != inventory) {
Inventory bukkitInventory = inventory.getBukkitInventory(); Inventory bukkitInventory = inventory.getBukkitInventory();
// Just in case, respect contents of the inventory that was just used. // 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 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. // 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. // 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)); getServer().getScheduler().runTask(this, () -> ejectViewers(inventory, viewer -> true));
} }
} }
@@ -514,15 +519,13 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
} }
} }
@Override static void ejectViewers(@NotNull ISpecialInventory inventory, @NotNull Predicate<@NotNull HumanEntity> predicate) {
public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { Inventory bukkitInventory = inventory.getBukkitInventory();
this.getConfig().set("toggles.silent-chest." + offline.getUniqueId(), status); for (HumanEntity viewer : new ArrayList<>(bukkitInventory.getViewers())) {
this.saveConfig(); if (predicate.test(viewer)) {
} viewer.closeInventory();
}
@Override }
public void unload(@NotNull final OfflinePlayer offline) {
setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE);
} }
} }