Skip to content

Commit

Permalink
some api changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Jsinco committed Dec 11, 2024
1 parent a481623 commit 1995e74
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 107 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ val langVersion = 17
val encoding = "UTF-8"

group = "com.dre.brewery"
version = "3.4.4-SNAPSHOT"
version = "3.4.4"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/dre/brewery/BCauldron.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public boolean fill(Player player, Block block) {
lang.sendEntry(player, "Perms_NoCauldronFill");
return true;
}
ItemStack potion = ingredients.cook(state, player.getName());
ItemStack potion = ingredients.cook(state, player);
if (potion == null) return false;

if (VERSION.isOrLater(MinecraftVersion.V1_13)) {
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/com/dre/brewery/BIngredients.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta;
Expand Down Expand Up @@ -134,7 +135,7 @@ public void add(ItemStack ingredient, RecipeItem rItem) {
/**
* returns an Potion item with cooked ingredients
*/
public ItemStack cook(int state, String brewer) {
public ItemStack cook(int state, Player brewer) {

ItemStack potion = new ItemStack(Material.POTION);
PotionMeta potionMeta = (PotionMeta) potion.getItemMeta();
Expand All @@ -158,7 +159,7 @@ public ItemStack cook(int state, String brewer) {
lore.updateQualityStars(false);
lore.updateCustomLore();
lore.updateAlc(false);
lore.updateBrewer(brewer);
lore.updateBrewer(brewer.getDisplayName());
lore.addOrReplaceEffects(brew.getEffects(), brew.getQuality());
lore.write();

Expand Down Expand Up @@ -210,7 +211,7 @@ public ItemStack cook(int state, String brewer) {
//potionMeta.addCustomEffect((PotionEffectType.REGENERATION).createEffect((uid * 4), 0), true);

brew.touch();
BrewModifyEvent modifyEvent = new BrewModifyEvent(brew, potionMeta, BrewModifyEvent.Type.FILL);
BrewModifyEvent modifyEvent = new BrewModifyEvent(brew, potionMeta, BrewModifyEvent.Type.FILL, brewer);
plugin.getServer().getPluginManager().callEvent(modifyEvent);
if (modifyEvent.isCancelled()) {
return null;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/dre/brewery/BSealer.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ private void itemChecking() {
slotTime[i] = -1;
Brew brew = Brew.get(contents[i]);
if (brew != null && !brew.isStripped()) {
brew.seal(contents[i]);
brew.seal(contents[i], player);
if (playerValid && BreweryPlugin.getMCVersion().isOrLater(MinecraftVersion.V1_9)) {
player.playSound(player.getLocation(), Sound.ITEM_BOTTLE_FILL_DRAGONBREATH, 1, 1.5f + (float) (Math.random() * 0.2));
}
Expand Down
23 changes: 18 additions & 5 deletions src/main/java/com/dre/brewery/Brew.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.BrewerInventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
Expand Down Expand Up @@ -510,7 +511,7 @@ public void unLabel(ItemStack item) {
*
* @param potion The Item this Brew is on
*/
public void seal(ItemStack potion) {
public void seal(ItemStack potion, @Nullable Player player) {
if (stripped) return;
ItemMeta origMeta = potion.getItemMeta();
if (!(origMeta instanceof PotionMeta)) return;
Expand All @@ -535,7 +536,7 @@ public void seal(ItemStack potion) {
wood = BarrelWoodType.NONE;
touch();

BrewModifyEvent modifyEvent = new BrewModifyEvent(this, meta, BrewModifyEvent.Type.SEAL);
BrewModifyEvent modifyEvent = new BrewModifyEvent(this, meta, BrewModifyEvent.Type.SEAL, player);
BreweryPlugin.getInstance().getServer().getPluginManager().callEvent(modifyEvent);

if (modifyEvent.isCancelled()) {
Expand Down Expand Up @@ -776,14 +777,26 @@ public void woodShift(float time, BarrelWoodType to) {
}
}

public ItemStack createItem() {
return createItem(null, true, null);
}

/**
* Create a new Item of this Brew. A BrewModifyEvent type CREATE will be called.
*
* @param recipe Recipe is required if the brew doesn't have a currentRecipe
* @return The created Item, null if the Event is cancelled
*/
public ItemStack createItem(@Nullable BRecipe recipe) {
return createItem(recipe, true);
return createItem(recipe, true, null);
}

public ItemStack createItem(@Nullable BRecipe recipe, @Nullable Player player) {
return createItem(recipe, true, player);
}

public ItemStack createItem(@Nullable BRecipe recipe, boolean event) {
return createItem(recipe, true, null);
}

/**
Expand All @@ -794,7 +807,7 @@ public ItemStack createItem(@Nullable BRecipe recipe) {
* @return The created Item, null if the Event is cancelled
*/
@Contract("_, false -> !null")
public ItemStack createItem(@Nullable BRecipe recipe, boolean event) {
public ItemStack createItem(@Nullable BRecipe recipe, boolean event, @Nullable Player player) {
if (recipe == null) {
recipe = getCurrentRecipe();
}
Expand Down Expand Up @@ -828,7 +841,7 @@ public ItemStack createItem(@Nullable BRecipe recipe, boolean event) {
lore.write();
touch();
if (event) {
BrewModifyEvent modifyEvent = new BrewModifyEvent(this, potionMeta, BrewModifyEvent.Type.CREATE);
BrewModifyEvent modifyEvent = new BrewModifyEvent(this, potionMeta, BrewModifyEvent.Type.CREATE, player);
BreweryPlugin.getInstance().getServer().getPluginManager().callEvent(modifyEvent);
if (modifyEvent.isCancelled()) {
return null;
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/com/dre/brewery/api/BreweryApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,14 @@ public static Brew createBrew(BRecipe recipe, int quality) {
*/
@Nullable
public static ItemStack createBrewItem(String recipeName, int quality) {
return createBrewItem(recipeName, quality, null);
}

@Nullable
public static ItemStack createBrewItem(String recipeName, int quality, Player player) {
BRecipe matching = BRecipe.getMatching(recipeName);
if (matching != null) {
return matching.create(quality);
return matching.create(quality, player);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.jetbrains.annotations.Nullable;

import java.nio.file.Path;
import java.util.Collection;
import java.util.List;

/**
* Management of addon configuration files.
Expand All @@ -39,6 +41,7 @@ public class AddonConfigManager {
private final ConfigHead INSTANCE;



public AddonConfigManager(BreweryAddon addon) {
this.INSTANCE = new ConfigHead(BreweryPlugin.getInstance().getDataFolder().toPath().resolve("addons").resolve(addon.getAddonInfo().name()));
}
Expand Down Expand Up @@ -78,6 +81,10 @@ public <T extends AddonConfigFile> Path getFilePath(Class<T> configClass) {
}


public Collection<AddonConfigFile> getLoadedConfigs() {
return INSTANCE.LOADED_CONFIGS.values().stream().map(AddonConfigFile.class::cast).toList();
}



/**
Expand Down
163 changes: 81 additions & 82 deletions src/main/java/com/dre/brewery/api/addons/AddonManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.dre.brewery.BreweryPlugin;
import com.dre.brewery.utility.Logging;
import com.dre.brewery.utility.Tuple;

import java.io.File;
import java.io.FileInputStream;
Expand Down Expand Up @@ -137,55 +138,63 @@ public void loadAddons() {
* @param file The jar file to load the addon from
*/
public void loadAddon(File file) {
try {
List<Class<?>> classes = loadAllClassesFromJar(file); // Get all our loaded classes.
try (URLClassLoader classLoader = new URLClassLoader(new URL[]{file.toURI().toURL()}, getClass().getClassLoader())) {
var pair = getClassesFromJar(file, classLoader); // Get all our loaded classes.
Class<? extends BreweryAddon> mainClass = pair.first();
List<Class<?>> classes = pair.second();

// I actually don't like that I used a loop here, should replace with like findFirst or something.
for (Class<?> clazz : classes) {
if (!BreweryAddon.class.isAssignableFrom(clazz)) {
continue;
}
Class<? extends BreweryAddon> addonClass = clazz.asSubclass(BreweryAddon.class);
BreweryAddon addon;
try {
addon = addonClass.getConstructor().newInstance(); // Instantiate our main class, the class shouldn't have constructor args.
} catch (Exception e) {
Logging.errorLog("Failed to load addon: " + file.getName(), e);
continue;
}
try {
BreweryAddon addon;
try {
addon = mainClass.getConstructor().newInstance(); // Instantiate our main class, the class shouldn't have constructor args.
} catch (Exception e) {
Logging.errorLog("Failed to load addon: " + file.getName(), e);
return;
}
try {

Class<BreweryAddon> breweryAddonClass = BreweryAddon.class;
// Set the logger and file manager
Field infoField = breweryAddonClass.getDeclaredField("addonInfo"); infoField.setAccessible(true);
infoField.set(addon, addonClass.getAnnotation(AddonInfo.class));
// Set the logger and file manager

if (addon.getAddonInfo() == null) { // This CAN be null for us. It's only annotated NotNull for addons.
Logging.errorLog("Addon " + addonClass.getSimpleName() + " is missing the AddonInfo annotation. It will not be loaded.");
continue;
}
Field infoField = BreweryAddon.class.getDeclaredField("addonInfo"); infoField.setAccessible(true);
infoField.set(addon, mainClass.getAnnotation(AddonInfo.class));

// Set all the fields for our addon reflectively.
if (addon.getAddonInfo() == null) { // This CAN be null for us. It's only annotated NotNull for addons.
Logging.errorLog("Addon " + mainClass.getSimpleName() + " is missing the AddonInfo annotation. It will not be loaded.");
return;
}

// Set all the fields for our addon reflectively.

Field loggerField = breweryAddonClass.getDeclaredField("logger"); loggerField.setAccessible(true);
Field fileManagerField = breweryAddonClass.getDeclaredField("addonFileManager"); fileManagerField.setAccessible(true);
Field addonConfigManagerField = breweryAddonClass.getDeclaredField("addonConfigManager"); addonConfigManagerField.setAccessible(true);
Field loggerField = BreweryAddon.class.getDeclaredField("logger"); loggerField.setAccessible(true);
Field fileManagerField = BreweryAddon.class.getDeclaredField("addonFileManager"); fileManagerField.setAccessible(true);
Field addonConfigManagerField = BreweryAddon.class.getDeclaredField("addonConfigManager"); addonConfigManagerField.setAccessible(true);

loggerField.set(addon, new AddonLogger(addon.getAddonInfo()));
fileManagerField.set(addon, new AddonFileManager(addon, file));
addonConfigManagerField.set(addon, new AddonConfigManager(addon));
loggerField.set(addon, new AddonLogger(addon.getAddonInfo()));
fileManagerField.set(addon, new AddonFileManager(addon, file));
addonConfigManagerField.set(addon, new AddonConfigManager(addon));


addon.getAddonLogger().info("Loading &a" + addon.getAddonInfo().name() + " &f-&a v" + addon.getAddonInfo().version() + " &fby &a" + addon.getAddonInfo().author());
LOADED_ADDONS.add(addon); // Add to our list of addons
addon.getAddonLogger().info("Loading &a" + addon.getAddonInfo().name() + " &f-&a v" + addon.getAddonInfo().version() + " &fby &a" + addon.getAddonInfo().author());
LOADED_ADDONS.add(addon); // Add to our list of addons

// let the addon know it has been loaded, it can do some pre-enable stuff here.
addon.onAddonPreEnable();
} catch (Exception e) {
Logging.errorLog("Failed to load addon: " + file.getName(), e);
unloadAddon(addon);
}

// let the addon know it has been loaded, it can do some pre-enable stuff here.
addon.onAddonPreEnable();
} catch (Exception e) {
Logging.errorLog("Failed to load addon: " + file.getName(), e);
unloadAddon(addon);
// Now that we're done loading our main class, we can go ahead and load the rest of our classes. We don't care about the order of these classes.
for (Class<?> clazz : classes) {
if (BreweryAddon.class.isAssignableFrom(clazz)) { // Just make sure it's not our main class we're trying to load again.
continue;
}
try {
classLoader.loadClass(clazz.getName());
} catch (ClassNotFoundException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to load class " + clazz.getName(), e);
}
}

} catch (Throwable ex) {
Logging.errorLog("Failed to load addon classes from jar " + file.getName(), ex);
}
Expand All @@ -195,7 +204,7 @@ public void loadAddon(File file) {
* It's about time I document this...
* <p>
* What we're doing here is trying to recreate what Bukkit does to load plugins. Obviously the code could be cleaned up and spread out to
* multiple functions but I honestly can't be bothered and this is fine I guess.
* multiple functions, but I honestly can't be bothered and this is fine I guess.
* <p>
* We have to load each class file, but first, we must load the class which extends BreweryAddon (our main class) before all else. If we
* don't load this class first we can get some nasty race conditions. Also, plugin developers expect their main class to be loaded first
Expand All @@ -204,62 +213,52 @@ public void loadAddon(File file) {
* @param jarFile The jar file to load classes from
* @return A list of classes loaded from the jar
*/
private List<Class<?>> loadAllClassesFromJar(File jarFile) {
private Tuple<Class<? extends BreweryAddon>, List<Class<?>>> getClassesFromJar(File jarFile, ClassLoader classLoader) {
List<Class<?>> classes = new ArrayList<>();
Class<? extends BreweryAddon> mainClass = null;

// We have to use the same class loader used to load this class AKA, the 'PluginLoader' class provided by Bukkit.
// ClassLoaders in Java are pretty interesting, and only classes loaded by the same ClassLoader can access each other.
// So to prevent any issues, we're using the same ClassLoader that loaded this class to load the classes from the jar.
try (URLClassLoader classLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()}, getClass().getClassLoader())) {

try (JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile))) {
JarEntry jarEntry;
String mainDir = "";
while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { // Just iterate through every file in the jar file and check if it's a compiled java class.
if (jarEntry.getName().endsWith(".class")) {

// We have to replace the '/' with '.' and remove the '.class' extension to get the canonical name of the class. (org.example.Whatever)
String className = jarEntry.getName().replaceAll("/", ".").replace(".class", "");
try (JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile))) {
JarEntry jarEntry;
String mainDir = "";
while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { // Just iterate through every file in the jar file and check if it's a compiled java class.
if (jarEntry.getName().endsWith(".class")) {

// We have to replace the '/' with '.' and remove the '.class' extension to get the canonical name of the class. (org.example.Whatever)
String className = jarEntry.getName().replaceAll("/", ".").replace(".class", "");
try {
Class<?> clazz;
try {
Class<?> clazz;
try {
// It's important that we don't initialize any other classes before our main class.
clazz = Class.forName(className, false, classLoader);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
continue;
}
if (BreweryAddon.class.isAssignableFrom(clazz)) {
// Found our main class, we're going to load it now.
classLoader.loadClass(className);
mainDir = className.substring(0, className.lastIndexOf('.'));
}
// Prevents loading classes that aren't in the same package. Addons that have dependencies better shade em in I guess. (TODO: remove this?)
if (!clazz.getName().contains(mainDir)) {
continue;
}
classes.add(clazz); // And finally... add the class to our list of classes.

} catch (ClassNotFoundException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to load class " + className, e);
// It's important that we don't initialize any other classes before our main class.
clazz = Class.forName(className, false, classLoader);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
continue;
}
}
}

// Now that we're done loading our main class, we can go ahead and load the rest of our classes. We don't care about the order of these classes.
for (Class<?> clazz : classes) {
if (!BreweryAddon.class.isAssignableFrom(clazz)) { // Just make sure it's not our main class we're trying to load again.
try {
classLoader.loadClass(clazz.getName());
} catch (ClassNotFoundException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to load class " + clazz.getName(), e);
if (BreweryAddon.class.isAssignableFrom(clazz)) {
// Found our main class, we're going to load it now.
classLoader.loadClass(className);
mainDir = className.substring(0, className.lastIndexOf('.'));
mainClass = clazz.asSubclass(BreweryAddon.class);
}
// Prevents loading classes that aren't in the same package. Addons that have dependencies better shade em in I guess. (TODO: remove this?)
if (!clazz.getName().contains(mainDir)) {
continue;
}
classes.add(clazz); // And finally... add the class to our list of classes.

} catch (ClassNotFoundException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to load class " + className, e);
}
}
}


} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Error loading classes from JAR", e);
plugin.getLogger().log(Level.SEVERE, "Failed to load classes from jar " + jarFile.getName(), e);
}
return classes;
return new Tuple<>(mainClass, classes);
}

}
Loading

0 comments on commit 1995e74

Please sign in to comment.