Skip to content

Commit 1283770

Browse files
authored
Merge pull request #2730 from BentoBoxWorld/develop
Version 3.7.4
2 parents d95b021 + ac86b9a commit 1283770

File tree

208 files changed

+2957
-1026
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

208 files changed

+2957
-1026
lines changed

pom.xml

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
<!-- Do not change unless you want different name for local builds. -->
7676
<build.number>-LOCAL</build.number>
7777
<!-- This allows to change between versions. -->
78-
<build.version>3.7.3</build.version>
78+
<build.version>3.7.4</build.version>
7979
<sonar.organization>bentobox-world</sonar.organization>
8080
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
8181
<server.jars>${project.basedir}/lib</server.jars>
@@ -201,6 +201,12 @@
201201
<id>matteodev</id>
202202
<url>https://maven.devs.beer/</url>
203203
</repository>
204+
<!-- Oraxen repo-->
205+
<repository>
206+
<id>oraxen</id>
207+
<name>Oraxen Repository</name>
208+
<url>https://repo.oraxen.com/releases</url>
209+
</repository>
204210
<!-- BentoBox Addons -->
205211
<repository>
206212
<id>bentoboxworld</id>
@@ -387,6 +393,59 @@
387393
<version>4.0.2-beta-release-11</version>
388394
<scope>provided</scope>
389395
</dependency>
396+
<!-- Oraxen -->
397+
<dependency>
398+
<groupId>io.th0rgal</groupId>
399+
<artifactId>oraxen</artifactId>
400+
<version>1.193.1</version>
401+
<exclusions>
402+
<exclusion>
403+
<groupId>me.gabytm.util</groupId>
404+
<artifactId>actions-spigot</artifactId>
405+
</exclusion>
406+
<exclusion>
407+
<groupId>org.jetbrains</groupId>
408+
<artifactId>annotations</artifactId>
409+
</exclusion>
410+
<exclusion>
411+
<groupId>com.ticxo</groupId>
412+
<artifactId>PlayerAnimator</artifactId>
413+
</exclusion>
414+
<exclusion>
415+
<groupId>com.github.stefvanschie.inventoryframework</groupId>
416+
<artifactId>IF</artifactId>
417+
</exclusion>
418+
<exclusion>
419+
<groupId>io.th0rgal</groupId>
420+
<artifactId>protectionlib</artifactId>
421+
</exclusion>
422+
<exclusion>
423+
<groupId>dev.triumphteam</groupId>
424+
<artifactId>triumph-gui</artifactId>
425+
</exclusion>
426+
<exclusion>
427+
<groupId>org.bstats</groupId>
428+
<artifactId>bstats-bukkit</artifactId>
429+
</exclusion>
430+
<exclusion>
431+
<groupId>com.jeff-media</groupId>
432+
<artifactId>custom-block-data</artifactId>
433+
</exclusion>
434+
<exclusion>
435+
<groupId>com.jeff-media</groupId>
436+
<artifactId>persistent-data-serializer</artifactId>
437+
</exclusion>
438+
<exclusion>
439+
<groupId>com.jeff_media</groupId>
440+
<artifactId>MorePersistentDataTypes</artifactId>
441+
</exclusion>
442+
<exclusion>
443+
<groupId>gs.mclo</groupId>
444+
<artifactId>java</artifactId>
445+
</exclusion>
446+
</exclusions>
447+
<scope>provided</scope>
448+
</dependency>
390449
<!-- Multipaper -->
391450
<dependency>
392451
<groupId>com.github.puregero</groupId>
@@ -461,10 +520,9 @@
461520
<plugin>
462521
<groupId>org.apache.maven.plugins</groupId>
463522
<artifactId>maven-compiler-plugin</artifactId>
464-
<version>3.8.1</version>
523+
<version>3.13.0</version>
465524
<configuration>
466525
<release>${java.version}</release>
467-
<!-- <source>${java.version}</source> <target>${java.version}</target> -->
468526
</configuration>
469527
</plugin>
470528
<plugin>
@@ -608,7 +666,7 @@
608666
<plugin>
609667
<groupId>org.jacoco</groupId>
610668
<artifactId>jacoco-maven-plugin</artifactId>
611-
<version>0.8.10</version>
669+
<version>0.8.13</version>
612670
<configuration>
613671
<append>true</append>
614672
<excludes>

src/main/java/world/bentobox/bentobox/BentoBox.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import world.bentobox.bentobox.hooks.MultiverseCore5Hook;
3535
import world.bentobox.bentobox.hooks.MyWorldsHook;
3636
import world.bentobox.bentobox.hooks.MythicMobsHook;
37+
import world.bentobox.bentobox.hooks.OraxenHook;
3738
import world.bentobox.bentobox.hooks.SlimefunHook;
3839
import world.bentobox.bentobox.hooks.VaultHook;
3940
import world.bentobox.bentobox.hooks.ZNPCsPlusHook;
@@ -257,6 +258,9 @@ private void completeSetup(long loadTime) {
257258

258259
// Register ItemsAdder
259260
hooksManager.registerHook(new ItemsAdderHook(this));
261+
262+
// Register Oraxen
263+
hooksManager.registerHook(new OraxenHook(this));
260264

261265
// TODO: re-enable after implementation
262266
//hooksManager.registerHook(new DynmapHook());

src/main/java/world/bentobox/bentobox/api/addons/Addon.java

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,34 @@
3131
import world.bentobox.bentobox.util.Util;
3232

3333
/**
34-
* Add-on class for BentoBox. Extend this to create an add-on. The operation
35-
* and methods are very similar to Bukkit's JavaPlugin.
34+
* The main class for a BentoBox addon.
35+
* <p>
36+
* To create an addon, you must extend this class. The operation and methods are
37+
* very similar to Bukkit's {@code JavaPlugin}.
38+
* <p>
39+
* Each addon must have an {@code addon.yml} file in its root directory. This file
40+
* contains metadata about the addon, such as its name, version, and main class.
41+
* <p>
42+
* Example of a simple addon main class:
43+
* <pre>
44+
* public class MyAddon extends Addon {
45+
*
46+
* {@literal @}Override
47+
* public void onEnable() {
48+
* // Addon startup logic
49+
* getLogger().info("MyAddon has been enabled!");
50+
* }
51+
*
52+
* {@literal @}Override
53+
* public void onDisable() {
54+
* // Addon shutdown logic
55+
* getLogger().info("MyAddon has been disabled!");
56+
* }
57+
* }
58+
* </pre>
3659
*
3760
* @author tastybento, ComminQ_Q
61+
* @since 1.0
3862
*/
3963
public abstract class Addon {
4064

@@ -71,7 +95,7 @@ protected Addon() {
7195
/**
7296
* Executes code when loading the addon.
7397
* This is called before {@link #onEnable()}.
74-
* This <b>must</b> be used to setup configuration, worlds and commands.
98+
* This <b>must</b> be used to set up configuration, worlds and commands.
7599
*/
76100
public void onLoad() {}
77101

@@ -196,7 +220,7 @@ private FileConfiguration loadYamlFile() {
196220
yamlConfig = new YamlConfiguration();
197221
yamlConfig.load(yamlFile);
198222
} catch (Exception e) {
199-
Bukkit.getLogger().severe(() -> "Could not load config.yml: " + e.getMessage());
223+
BentoBox.getInstance().logError("Could not load config.yml: " + e.getMessage());
200224
}
201225
}
202226
return yamlConfig;
@@ -218,7 +242,7 @@ public void saveConfig() {
218242
try {
219243
getConfig().save(new File(dataFolder, ADDON_CONFIG_FILENAME));
220244
} catch (IOException e) {
221-
Bukkit.getLogger().severe("Could not save config! " + this.getDescription().getName() + " " + e.getMessage());
245+
BentoBox.getInstance().logError("Could not save config! " + this.getDescription().getName() + " " + e.getMessage());
222246
}
223247
}
224248

@@ -267,7 +291,7 @@ public void saveResource(String resourcePath, boolean replace) {
267291
* @return file written, or null if none
268292
*/
269293
public File saveResource(String jarResource, File destinationFolder, boolean replace, boolean noPath) {
270-
if (jarResource == null || jarResource.equals("")) {
294+
if (jarResource == null || jarResource.isEmpty()) {
271295
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
272296
}
273297

@@ -300,6 +324,7 @@ public File saveResource(String jarResource, File destinationFolder, boolean rep
300324
"The embedded resource '" + jarResource + "' cannot be found in " + jar.getName());
301325
}
302326
} catch (IOException e) {
327+
e.printStackTrace();
303328
BentoBox.getInstance().logError(
304329
"Could not save from jar file. From " + jarResource + " to " + destinationFolder.getAbsolutePath());
305330
}
@@ -314,7 +339,7 @@ public File saveResource(String jarResource, File destinationFolder, boolean rep
314339
* @throws InvalidConfigurationException - if the yaml is malformed
315340
*/
316341
public YamlConfiguration getYamlFromJar(String jarResource) throws IOException, InvalidConfigurationException {
317-
if (jarResource == null || jarResource.equals("")) {
342+
if (jarResource == null || jarResource.isEmpty()) {
318343
throw new IllegalArgumentException("jarResource cannot be null or empty");
319344
}
320345
YamlConfiguration result = new YamlConfiguration();
@@ -336,7 +361,7 @@ public YamlConfiguration getYamlFromJar(String jarResource) throws IOException,
336361
* @return resource or null if there is a problem
337362
*/
338363
public InputStream getResource(String jarResource) {
339-
if (jarResource == null || jarResource.equals("")) {
364+
if (jarResource == null || jarResource.isEmpty()) {
340365
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
341366
}
342367

@@ -349,7 +374,7 @@ public InputStream getResource(String jarResource) {
349374
}
350375
}
351376
} catch (IOException e) {
352-
Bukkit.getLogger().severe("Could not open from jar file. " + jarResource);
377+
BentoBox.getInstance().logError("Could not open from jar file. " + jarResource);
353378
}
354379
return null;
355380
}

src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,31 @@
2525
import world.bentobox.bentobox.managers.AddonsManager;
2626

2727
/**
28-
* Loads addons and sets up permissions
29-
* @author Tastybento, ComminQ
28+
* This class loader is responsible for loading an addon's JAR file, its classes,
29+
* and its {@code addon.yml} metadata.
30+
* <p>
31+
* Each addon is loaded with its own {@link AddonClassLoader}, which allows for
32+
* class isolation and management. This loader also facilitates inter-addon
33+
* class sharing by coordinating with the {@link AddonsManager}.
34+
*
35+
* This approach is now rarely used as most addons are now Plugin-based and so are loaded by the server as plugins.
36+
*
37+
* @author tastybento, ComminQ
38+
* @since 1.0
3039
*/
3140
public class AddonClassLoader extends URLClassLoader {
3241

42+
/**
43+
* A cache of classes that have been loaded by this class loader.
44+
*/
3345
private final Map<String, Class<?>> classes = new HashMap<>();
46+
/**
47+
* The addon instance that was loaded by this class loader.
48+
*/
3449
private final Addon addon;
50+
/**
51+
* The addon manager that created this class loader.
52+
*/
3553
private final AddonsManager loader;
3654

3755
/**
@@ -47,6 +65,22 @@ protected AddonClassLoader(Addon addon, AddonsManager loader, File jarFile) thro
4765
this.loader = loader;
4866
}
4967

68+
/**
69+
* Constructs a new AddonClassLoader for a given addon.
70+
*
71+
* @param addonsManager The addon manager instance.
72+
* @param data The addon's metadata from {@code addon.yml}.
73+
* @param jarFile The addon's JAR file.
74+
* @param parent The parent class loader.
75+
* @throws InvalidAddonInheritException If the main class does not extend {@link Addon}.
76+
* @throws MalformedURLException If the JAR file path is invalid.
77+
* @throws InvalidDescriptionException If the addon cannot be loaded.
78+
* @throws InvalidAddonDescriptionException If the {@code addon.yml} is invalid.
79+
* @throws InstantiationException If the addon's main class cannot be instantiated.
80+
* @throws IllegalAccessException If the addon's main class constructor is not accessible.
81+
* @throws InvocationTargetException If the addon's constructor throws an exception.
82+
* @throws NoSuchMethodException If the addon's main class has no default constructor.
83+
*/
5084
public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File jarFile, ClassLoader parent)
5185
throws InvalidAddonInheritException,
5286
MalformedURLException,
@@ -64,32 +98,36 @@ public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, Fil
6498
if (mainClass == null) {
6599
throw new InvalidAddonFormatException("addon.yml does not define a main class!");
66100
}
67-
javaClass = Class.forName(mainClass, true, this);
101+
// Do not allow addons to use BentoBox's package.
68102
if(mainClass.startsWith("world.bentobox.bentobox")){
69103
throw new InvalidAddonFormatException("Package declaration cannot start with 'world.bentobox.bentobox'");
70104
}
105+
javaClass = Class.forName(mainClass, true, this);
71106
} catch (Exception e) {
72107
throw new InvalidDescriptionException("Could not load '" + jarFile.getName() + "' in folder '" + jarFile.getParent() + "' - " + e.getMessage());
73108
}
74109

75110
Class<? extends Addon> addonClass;
76111
try {
112+
// Check if the main class extends Addon.
77113
addonClass = javaClass.asSubclass(Addon.class);
78114
} catch (ClassCastException e) {
79115
throw new InvalidAddonInheritException("Main class does not extend 'Addon'");
80116
}
81117

118+
// Instantiate the addon and set its description from addon.yml.
82119
addon = addonClass.getDeclaredConstructor().newInstance();
83120
addon.setDescription(asDescription(data));
84121
}
85122

86123

87124

88125
/**
89-
* Converts the addon.yml to an AddonDescription
90-
* @param data - yaml config (addon.yml)
91-
* @return Addon Description
92-
* @throws InvalidAddonDescriptionException - if there's a bug in the addon.yml
126+
* Parses the addon.yml configuration into an {@link AddonDescription} object.
127+
*
128+
* @param data The YAML configuration from {@code addon.yml}.
129+
* @return An {@link AddonDescription} instance.
130+
* @throws InvalidAddonDescriptionException If the {@code addon.yml} is missing required fields or contains invalid values.
93131
*/
94132
@NonNull
95133
public static AddonDescription asDescription(YamlConfiguration data) throws InvalidAddonDescriptionException {
@@ -159,32 +197,49 @@ protected Class<?> findClass(String name) {
159197
}
160198

161199
/**
162-
* This is a custom findClass that enables classes in other addons to be found
163-
* @param name - class name
164-
* @param checkGlobal - check globally or not when searching
165-
* @return Class - class if found
200+
* Finds and loads the specified class.
201+
* <p>
202+
* This custom implementation enables classes in one addon to be found by another.
203+
* The loading strategy is as follows:
204+
* <ol>
205+
* <li>Check the local cache for this class loader.</li>
206+
* <li>If not found, check the global cache in {@link AddonsManager} (if {@code checkGlobal} is true).</li>
207+
* <li>If still not found, try to load the class from the addon's JAR file.</li>
208+
* <li>If loaded successfully from the JAR, register it in the global cache.</li>
209+
* <li>Finally, cache the class locally.</li>
210+
* </ol>
211+
*
212+
* @param name The fully qualified name of the class.
213+
* @param checkGlobal If true, check for classes loaded by other addons.
214+
* @return The loaded {@link Class}, or {@code null} if the class could not be found.
166215
*/
167216
public Class<?> findClass(String name, boolean checkGlobal) {
217+
// Do not allow addons to load BentoBox classes.
168218
if (name.startsWith("world.bentobox.bentobox")) {
169219
return null;
170220
}
221+
// Check local cache first.
171222
Class<?> result = classes.get(name);
172223
if (result == null) {
224+
// Check global cache for classes from other addons.
173225
if (checkGlobal) {
174226
result = loader.getClassByName(name);
175227
}
176228

177229
if (result == null) {
230+
// Try to find the class in this addon's jar.
178231
try {
179232
result = super.findClass(name);
180233
} catch (ClassNotFoundException | NoClassDefFoundError e) {
181-
// Do nothing.
234+
// Do nothing. The class is not in this jar.
182235
}
183236
if (result != null) {
237+
// Class found in this addon's jar, so add it to the global cache.
184238
loader.setClass(name, result);
185239

186240
}
187241
}
242+
// Add the class to the local cache.
188243
classes.put(name, result);
189244
}
190245
return result;
@@ -198,7 +253,8 @@ public Addon getAddon() {
198253
}
199254

200255
/**
201-
* @return class list
256+
* Gets the set of fully qualified class names loaded by this class loader.
257+
* @return A set of class names.
202258
*/
203259
public Set<String> getClasses() {
204260
return classes.keySet();

0 commit comments

Comments
 (0)