Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OpenShulker

OpenShulker is a plugin for 1.14 to 1.20.1 that makes Shulkerboxes more useful.
OpenShulker is a plugin for 1.14 to 26.2 that makes Shulkerboxes more useful.

You can open a Shulker by Shift-Rightclicking it in your hand or Inventory.

Expand All @@ -21,11 +21,19 @@ You can download it on spigotmc: [OpenShulker](https://www.spigotmc.org/resource
OpenSound: "BLOCK_SHULKER_BOX_OPEN"
# Sound for closing a Shulker
CloseSound: "BLOCK_SHULKER_BOX_CLOSE"
Hooks:
# If ChestSort is installed and this is true, opened shulker inventories are marked as unsortable.
ChestSort: true
# If WorldGuard is installed and this is true, shulkers inside protected containers can only be used where the player has container access.
WorldGuard: true
# Ingame admin commands:
# /openshulker reload - reloads this config
# /openshulker hooks - shows if ChestSort/WorldGuard hooks are enabled and loaded
Messages:
Prefix: "&8[&2OpenShulker&8] &7"
CannotBreakContainer: "§cYou cannot break this container, since there's an opened shulker in it"
CannotBreakContainer: "&cYou cannot break this container, since there's an opened shulker in it"
OpenShulkerCommand:
Syntax: "&cSyntax: &4/<LABEL> <Reload>"
Syntax: "&cSyntax: &4/<LABEL> <Reload|Hooks>"
Reloaded: "The plugin was reloaded!"
```

Expand All @@ -38,6 +46,7 @@ Messages:
# Commands

- `openshulker reload`
- `openshulker hooks`

# Support

Expand All @@ -47,7 +56,4 @@ Github: [Issues Page](https://github.com/Test-Account666/OpenShulker/issues)

# Todo

- Add ability to input items by right clicking an item on/with a shulker box
- Add ChestSort Hook
- Add WorldGuard Hook
- Add update checker
15 changes: 13 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@

<groupId>me.entity303</groupId>
<artifactId>OpenShulker</artifactId>
<version>1.3.0</version>
<version>1.4.0</version>
<packaging>jar</packaging>

<name>OpenShulker</name>

<properties>
<java.version>1.8</java.version>
<paper.version>26.2.build.31-alpha</paper.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

Expand Down Expand Up @@ -61,13 +62,23 @@
<id>sonatype</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>enginehub-repo</id>
<url>https://maven.enginehub.org/repo/</url>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.19.4-R0.1-SNAPSHOT</version>
<version>${paper.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
<version>7.0.17</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand Down
62 changes: 61 additions & 1 deletion src/main/java/me/entity303/openshulker/OpenShulker.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.entity303.openshulker;

import me.entity303.openshulker.commands.OpenShulkerCommand;
import me.entity303.openshulker.hooks.ChestSortHook;
import me.entity303.openshulker.listener.ShulkerDupeListener;
import me.entity303.openshulker.listener.ShulkerOpenCloseListener;
import me.entity303.openshulker.listener.ShulkerReadOnlyListener;
Expand All @@ -18,10 +19,15 @@ public final class OpenShulker extends JavaPlugin implements Listener {
public boolean _allowContainerOpen = true;
public boolean _allowEnderChestOpen = true;
public boolean _allowHandOpen = true;
public boolean _hookChestSort = true;
public boolean _debugChestSort = false;
public boolean _hookWorldGuard = true;
private ShulkerActions _shulkerActions;

@Override
public void onDisable() {
if (this._shulkerActions == null) return;

for (Player all : Bukkit.getOnlinePlayers()) {
if (!this._shulkerActions.HasOpenShulkerBox(all)) continue;

Expand Down Expand Up @@ -51,13 +57,17 @@ public void onEnable() {

command.setExecutor(openShulkerCommand);
command.setTabCompleter(openShulkerCommand);

this.LogHookStatus();
}

public void InitializeConfig() {
this.saveDefaultConfig();

this.reloadConfig();

this.AddConfigDefaults();

String openSound = this.getConfig().getString("OpenSound");

try {
Expand All @@ -80,7 +90,7 @@ public void InitializeConfig() {
} catch (Throwable ignored) {
if (closeSound == null) {
Bukkit.getLogger().severe("You did not specify CloseSound, using default");
this.getConfig().set("OpenSound", "BLOCK_SHULKER_BOX_CLOSE");
this.getConfig().set("CloseSound", "BLOCK_SHULKER_BOX_CLOSE");
this.saveConfig();
this.reloadConfig();
} else Bukkit.getLogger()
Expand All @@ -92,6 +102,56 @@ public void InitializeConfig() {
this._allowContainerOpen = this.getConfig().getBoolean("OpenMethods.AllowContainerOpen");
this._allowEnderChestOpen = this.getConfig().getBoolean("OpenMethods.AllowEnderChestOpen");
this._allowHandOpen = this.getConfig().getBoolean("OpenMethods.AllowHandOpen");
this._hookChestSort = this.getConfig().getBoolean("Hooks.ChestSort", true);
this._debugChestSort = this.getConfig().getBoolean("Hooks.ChestSortDebug", false);
this._hookWorldGuard = this.getConfig().getBoolean("Hooks.WorldGuard", true);
}

private void AddConfigDefaults() {
boolean changed = false;

changed |= this.AddConfigDefault("OpenMethods.AllowInventoryOpen", true);
changed |= this.AddConfigDefault("OpenMethods.AllowContainerOpen", true);
changed |= this.AddConfigDefault("OpenMethods.AllowEnderChestOpen", true);
changed |= this.AddConfigDefault("OpenMethods.AllowHandOpen", true);
changed |= this.AddConfigDefault("WaitSecondsBeforeOpen", 0);
changed |= this.AddConfigDefault("OpenSound", "BLOCK_SHULKER_BOX_OPEN");
changed |= this.AddConfigDefault("CloseSound", "BLOCK_SHULKER_BOX_CLOSE");
changed |= this.AddConfigDefault("Hooks.ChestSort", true);
changed |= this.AddConfigDefault("Hooks.ChestSortDebug", false);
changed |= this.AddConfigDefault("Hooks.WorldGuard", true);
changed |= this.AddConfigDefault("Messages.Prefix", "&8[&2OpenShulker&8] &7");
changed |= this.AddConfigDefault("Messages.CannotBreakContainer", "&cYou cannot break this container, since there's an opened shulker in it");
changed |= this.AddConfigDefault("Messages.OpenShulkerCommand.Syntax", "&cSyntax: &4/<LABEL> <Reload|Hooks>");
changed |= this.AddConfigDefault("Messages.OpenShulkerCommand.Reloaded", "The plugin was reloaded!");

if (!changed) return;

this.getConfig().options().copyDefaults(true);
this.saveConfig();
this.reloadConfig();
}

private boolean AddConfigDefault(String path, Object value) {
boolean missing = !this.getConfig().isSet(path);
this.getConfig().addDefault(path, value);
return missing;
}

private void LogHookStatus() {
this.getLogger().info("ChestSort hook: " + this.FormatHookStatus(this._hookChestSort, ChestSortHook.IsLoaded()));
this.getLogger().info("WorldGuard hook: " +
this.FormatHookStatus(this._hookWorldGuard, Bukkit.getPluginManager().getPlugin("WorldGuard") != null));

if (this._hookChestSort && this._debugChestSort && ChestSortHook.IsLoaded() && !ChestSortHook.IsApiAvailable()) {
this.getLogger().warning("ChestSort is loaded, but OpenShulker could not find a supported ChestSortAPI#setUnsortable method.");
}
}

private String FormatHookStatus(boolean enabledInConfig, boolean pluginLoaded) {
if (!enabledInConfig) return "disabled in config";
if (!pluginLoaded) return "enabled in config, plugin not loaded";
return "enabled and plugin loaded";
}

public ShulkerActions GetShulkerActions() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package me.entity303.openshulker.commands;

import me.entity303.openshulker.OpenShulker;
import me.entity303.openshulker.hooks.ChestSortHook;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
Expand All @@ -9,7 +11,6 @@
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class OpenShulkerCommand implements TabExecutor {
Expand Down Expand Up @@ -42,6 +43,14 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command
return true;
}

if (args[0].equalsIgnoreCase("hooks")) {
commandSender.sendMessage(prefix + "ChestSort hook: " + this.FormatHookStatus(this._openShulker._hookChestSort, ChestSortHook.IsLoaded()));
commandSender.sendMessage(prefix + "WorldGuard hook: " +
this.FormatHookStatus(this._openShulker._hookWorldGuard,
Bukkit.getPluginManager().getPlugin("WorldGuard") != null));
return true;
}

commandSender.sendMessage(prefix + ChatColor.translateAlternateColorCodes('&', this._openShulker.getConfig()
.getString("Messages.OpenShulkerCommand.Syntax")
.replace("<LABEL>", label)));
Expand All @@ -51,8 +60,19 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] args) {
if (args.length == 1) return Collections.singletonList("reload");
if (args.length == 1) {
List<String> completions = new ArrayList<>();
completions.add("reload");
completions.add("hooks");
return completions;
}

return new ArrayList<>();
}

private String FormatHookStatus(boolean enabledInConfig, boolean pluginLoaded) {
if (!enabledInConfig) return ChatColor.RED + "disabled in config";
if (!pluginLoaded) return ChatColor.YELLOW + "enabled in config, plugin not loaded";
return ChatColor.GREEN + "enabled";
}
}
92 changes: 92 additions & 0 deletions src/main/java/me/entity303/openshulker/hooks/ChestSortHook.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package me.entity303.openshulker.hooks;

import org.bukkit.Bukkit;
import org.bukkit.inventory.Inventory;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public final class ChestSortHook {
private static final String[] API_CLASSES = {
"de.jeff_media.chestsort.api.ChestSortAPI",
"de.jeff_media.chestsort.ChestSortAPI"
};
private static boolean _warnedMissingApi = false;

private ChestSortHook() {
}

public static void SetUnsortable(Inventory inventory, boolean debug) {
if (!IsLoaded()) return;
if (inventory == null) return;

for (String apiClass : API_CLASSES) {
if (TrySetUnsortable(apiClass, inventory)) return;
}

if (debug && !_warnedMissingApi) {
Bukkit.getLogger().warning("[OpenShulker] ChestSort is loaded, but no supported ChestSortAPI#setUnsortable method was found.");
_warnedMissingApi = true;
}
}

public static boolean IsLoaded() {
return Bukkit.getPluginManager().getPlugin("ChestSort") != null;
}

public static boolean IsApiAvailable() {
if (!IsLoaded()) return false;

for (String apiClass : API_CLASSES) {
try {
Class<?> chestSortApi = Class.forName(apiClass);
if (FindUnsortableMethod(chestSortApi) != null) return true;
} catch (Throwable ignored) {
}
}

return false;
}

private static boolean TrySetUnsortable(String apiClass, Inventory inventory) {
try {
Class<?> chestSortApi = Class.forName(apiClass);

try {
Method setUnsortable = chestSortApi.getMethod("setUnsortable", Inventory.class);
setUnsortable.invoke(null, inventory);
return true;
} catch (NoSuchMethodException ignored) {
return TryMatchingUnsortableMethod(chestSortApi, inventory);
}
} catch (Throwable ignored) {
return false;
}
}

private static boolean TryMatchingUnsortableMethod(Class<?> chestSortApi, Inventory inventory) {
Method method = FindUnsortableMethod(chestSortApi);

if (method == null) return false;

try {
method.invoke(null, inventory);
return true;
} catch (Throwable ignored) {
return false;
}
}

private static Method FindUnsortableMethod(Class<?> chestSortApi) {
for (Method method : chestSortApi.getMethods()) {
if (!Modifier.isStatic(method.getModifiers())) continue;
if (!method.getName().toLowerCase().contains("unsort")) continue;
if (method.getParameterTypes().length != 1) continue;
if (!method.getParameterTypes()[0].isAssignableFrom(Inventory.class)) continue;

return method;
}

return null;
}
}
37 changes: 37 additions & 0 deletions src/main/java/me/entity303/openshulker/hooks/WorldGuardHook.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package me.entity303.openshulker.hooks;

import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import com.sk89q.worldguard.protection.regions.RegionQuery;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;

public final class WorldGuardHook {
private WorldGuardHook() {
}

public static boolean CanAccessContainer(Player player, Location location) {
if (Bukkit.getPluginManager().getPlugin("WorldGuard") == null) return true;
if (location == null || location.getWorld() == null) return true;

try {
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);

if (WorldGuard.getInstance().getPlatform().getSessionManager().hasBypass(localPlayer, BukkitAdapter.adapt(location.getWorld()))) {
return true;
}

RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
RegionQuery query = container.createQuery();

return query.testState(BukkitAdapter.adapt(location), localPlayer, Flags.CHEST_ACCESS, Flags.INTERACT);
} catch (Throwable ignored) {
return true;
}
}
}
Loading