diff --git a/pom.xml b/pom.xml index 6e9b8fb..4e3a88a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.pinont singularitylib - 1.0.0 + 1.3.0-SNAPSHOT jar SingularityLib @@ -52,6 +52,9 @@ 23 + 1.21.8-R0.1-SNAPSHOT + 4.87.0 + ${paperapi.version} UTF-8 UTF-8 ${java.version} @@ -74,6 +77,10 @@ sonatype https://oss.sonatype.org/content/groups/public/ + + jitpack.io + https://jitpack.io + @@ -130,6 +137,40 @@ maven-deploy-plugin 3.1.2 + + + + org.codehaus.gmaven + groovy-maven-plugin + 2.1.1 + + + initialize + + execute + + + + // Extract Paper version from MockBukkit JAR + def mockbukkitJar = project.artifacts.find { + it.artifactId.startsWith('mockbukkit-') + }?.file + + if (mockbukkitJar) { + def jar = new java.util.jar.JarFile(mockbukkitJar) + def manifest = jar.manifest + def paperVersion = manifest.mainAttributes.getValue('Paper-Version') + jar.close() + + if (paperVersion) { + project.properties['paper.version.from.mockbukkit'] = paperVersion + } + } + + + + + @@ -144,7 +185,13 @@ io.papermc.paper paper-api - 1.21.8-R0.1-SNAPSHOT + ${paperapi.version} + provided + + + dev.folia + folia-api + ${paperapi.version} provided @@ -165,7 +212,13 @@ org.mockbukkit.mockbukkit mockbukkit-v1.21 - 4.77.0 + ${mockbukkit.version} + test + + + io.papermc.paper + paper-api + ${paper.version.from.mockbukkit} test @@ -174,5 +227,11 @@ 2.0.16 test + + com.github.Pinont + Singularity-DevTool + cbd0e31e44 + provided + \ No newline at end of file diff --git a/src/main/java/com/github/pinont/singularitylib/plugin/CorePlugin.java b/src/main/java/com/github/pinont/plugin/CorePlugin.java similarity index 88% rename from src/main/java/com/github/pinont/singularitylib/plugin/CorePlugin.java rename to src/main/java/com/github/pinont/plugin/CorePlugin.java index d655e51..97a53c4 100644 --- a/src/main/java/com/github/pinont/singularitylib/plugin/CorePlugin.java +++ b/src/main/java/com/github/pinont/plugin/CorePlugin.java @@ -1,9 +1,9 @@ -package com.github.pinont.singularitylib.plugin; +package com.github.pinont.plugin; +import com.github.pinont.plugin.listener.PlayerListener; import com.github.pinont.singularitylib.api.command.SimpleCommand; import com.github.pinont.singularitylib.api.manager.ConfigManager; -import com.github.pinont.singularitylib.plugin.listener.PlayerListener; -import com.github.pinont.singularitylib.plugin.register.Register; +import com.github.pinont.plugin.register.Register; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.configuration.file.FileConfiguration; @@ -45,12 +45,17 @@ public CorePlugin() { private static String prefix; private static Long startTime; + private static boolean isFolia; /** * Flag indicating if the plugin is running in test mode. */ public boolean isTest = false; + public static boolean isFolia() { + return isFolia; + } + /** * Gets the time when the plugin started loading. * @@ -94,8 +99,8 @@ public static void sendConsoleMessage(String message) { * @return the API version string */ public static String getAPIVersion() { - String version = new ConfigManager("api-version.yml").getConfig().getString("version"); -// String version = "1.0.0"; + new ConfigManager("api-version.yml").saveConfig(); + String version = new ConfigManager("api-version.yml").getConfig().getString("version") == null ? "1.0.0" : new ConfigManager("api-version.yml").getConfig().getString("version"); return "V-" + version; } @@ -164,6 +169,11 @@ public final void onEnable() { // TODO: Move to Devtool // WorldManager.autoLoadWorlds(); + isFolia = foliaCheck(); + if (isFolia) { + sendConsoleMessage(ChatColor.GREEN + "" + ChatColor.ITALIC + "Folia environment detected, enabling Folia compatibility mode..."); + } + // Initialize API To Plugin. sendConsoleMessage(ChatColor.WHITE + "" + ChatColor.ITALIC + "Hooked " + ChatColor.YELLOW + ChatColor.ITALIC + this.getName() + ChatColor.WHITE + ChatColor.ITALIC + " into " + ChatColor.LIGHT_PURPLE + ChatColor.ITALIC + "SingularityAPI! " + getAPIVersion()); onPluginStart(); @@ -178,6 +188,15 @@ public final void onEnable() { } } + private boolean foliaCheck() { + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + private void registerAPIListener(Plugin plugin, Listener... listener) { sendConsoleMessage(ChatColor.GREEN + "" + ChatColor.ITALIC + "Initializing API listeners for " + plugin.getName() + "..."); for (Listener l : listener) { diff --git a/src/main/java/com/github/pinont/plugin/listener/EntityDamageListener.java b/src/main/java/com/github/pinont/plugin/listener/EntityDamageListener.java new file mode 100644 index 0000000..26ac843 --- /dev/null +++ b/src/main/java/com/github/pinont/plugin/listener/EntityDamageListener.java @@ -0,0 +1,21 @@ +package com.github.pinont.plugin.listener; + +import com.github.pinont.singularitylib.api.annotation.AutoRegister; +import com.github.pinont.singularitylib.api.event.PlayerDamageByPlayerEvent; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +@AutoRegister +public class EntityDamageListener implements Listener { + + @EventHandler + public void onEntityDamage(EntityDamageByEntityEvent event) { + if (event.getEntity() instanceof Player player && event.getDamager() instanceof Player damager) { + PlayerDamageByPlayerEvent playerDamageEvent = new PlayerDamageByPlayerEvent(player, damager); + event.setCancelled(playerDamageEvent.callEvent()); + } + } + +} diff --git a/src/main/java/com/github/pinont/singularitylib/plugin/listener/PlayerListener.java b/src/main/java/com/github/pinont/plugin/listener/PlayerListener.java similarity index 89% rename from src/main/java/com/github/pinont/singularitylib/plugin/listener/PlayerListener.java rename to src/main/java/com/github/pinont/plugin/listener/PlayerListener.java index 6adb29e..3c92151 100644 --- a/src/main/java/com/github/pinont/singularitylib/plugin/listener/PlayerListener.java +++ b/src/main/java/com/github/pinont/plugin/listener/PlayerListener.java @@ -1,8 +1,8 @@ -package com.github.pinont.singularitylib.plugin.listener; +package com.github.pinont.plugin.listener; import com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent; +import com.github.pinont.plugin.CorePlugin; import com.github.pinont.singularitylib.api.event.ItemExecuteEvent; -import com.github.pinont.singularitylib.api.items.ItemCreator; import com.github.pinont.singularitylib.api.items.ItemInteraction; import com.github.pinont.singularitylib.api.ui.Button; import com.github.pinont.singularitylib.api.ui.Menu; @@ -24,7 +24,8 @@ import java.util.Objects; -import static com.github.pinont.singularitylib.plugin.CorePlugin.*; +import static com.github.pinont.singularitylib.api.items.ItemInteraction.*; +import static com.github.pinont.plugin.CorePlugin.*; /** * Event listener for handling player interactions and menu operations. @@ -32,11 +33,7 @@ */ public class PlayerListener implements Listener { - /** - * Default constructor for PlayerListener. - */ - public PlayerListener() { - } + private final Plugin plugin = CorePlugin.getInstance(); /** * Handles player interaction events for custom item interactions. @@ -47,11 +44,11 @@ public PlayerListener() { public void interaction(PlayerInteractEvent event) { Player player = event.getPlayer(); ItemStack item = player.getInventory().getItemInMainHand(); - if (!Common.isMainHandEmpty(player) && ItemCreator.isItemHasPersistData(item, "interaction", PersistentDataType.STRING)) { + if (!Common.isMainHandEmpty(player) && isItemHasPersistData(plugin, item, "interaction", PersistentDataType.STRING)) { if (event.getPlayer().getCooldown(item) > 0) return; ItemInteraction itemInteraction; try { - itemInteraction = ItemCreator.getInteraction(item); + itemInteraction = getInteraction(plugin, item); } catch (IllegalArgumentException e) { sendInteractionError(player); return; @@ -171,7 +168,7 @@ private void removePlayerMetadata(Player player, Plugin plugin, String... keys) } private void sendInteractionError(Player player) { - sendConsoleMessage("Interaction ID is not valid: " + ItemCreator.getItemPersistData(player.getInventory().getItemInMainHand(), "interaction", PersistentDataType.STRING)); + sendConsoleMessage("Interaction ID is not valid: " + getItemPersistData(plugin, player.getInventory().getItemInMainHand(), "interaction", PersistentDataType.STRING)); } } diff --git a/src/main/java/com/github/pinont/singularitylib/plugin/register/Register.java b/src/main/java/com/github/pinont/plugin/register/Register.java similarity index 98% rename from src/main/java/com/github/pinont/singularitylib/plugin/register/Register.java rename to src/main/java/com/github/pinont/plugin/register/Register.java index e5c93e0..7cfa69e 100644 --- a/src/main/java/com/github/pinont/singularitylib/plugin/register/Register.java +++ b/src/main/java/com/github/pinont/plugin/register/Register.java @@ -1,4 +1,4 @@ -package com.github.pinont.singularitylib.plugin.register; +package com.github.pinont.plugin.register; import com.github.pinont.singularitylib.api.annotation.AutoRegister; import com.github.pinont.singularitylib.api.command.SimpleCommand; diff --git a/src/main/java/com/github/pinont/singularitylib/api/annotation/AutoRegister.java b/src/main/java/com/github/pinont/singularitylib/api/annotation/AutoRegister.java index 5fe750b..adb95e4 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/annotation/AutoRegister.java +++ b/src/main/java/com/github/pinont/singularitylib/api/annotation/AutoRegister.java @@ -1,5 +1,8 @@ package com.github.pinont.singularitylib.api.annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + /** * Annotation to mark classes for automatic registration by the plugin. * This annotation is used to mark classes that need to be registered during the plugin's startup process. @@ -9,6 +12,8 @@ * Hint: Use this annotation to register commands, events, or custom items. * It should only be used when the class extends {@code CustomItem}, {@code SimpleCommand}, or {@code Listener}. */ + +@Target({ElementType.TYPE}) public @interface AutoRegister { /** * Indicates that the annotated class should be automatically registered by the plugin. diff --git a/src/main/java/com/github/pinont/singularitylib/api/command/SimpleCommand.java b/src/main/java/com/github/pinont/singularitylib/api/command/SimpleCommand.java index 47db203..e9f0ebd 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/command/SimpleCommand.java +++ b/src/main/java/com/github/pinont/singularitylib/api/command/SimpleCommand.java @@ -16,6 +16,14 @@ public interface SimpleCommand extends BasicCommand { */ String getName(); +// /** +// * Gets the description of the command. +// * This description is used in help messages and command documentation. +// * +// * @return the command description +// */ +// String getDescription(); + /** * Gets the usage string for this command. * @@ -25,12 +33,4 @@ public interface SimpleCommand extends BasicCommand { default String usage(Boolean bool) { return "/" + getName(); } - - /** - * Gets the description of the command. - * This description is used in help messages and command documentation. - * - * @return the command description - */ - String description(); } diff --git a/src/main/java/com/github/pinont/singularitylib/api/entity/EntityCreator.java b/src/main/java/com/github/pinont/singularitylib/api/entity/EntityCreator.java index d1c7c74..72445e3 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/entity/EntityCreator.java +++ b/src/main/java/com/github/pinont/singularitylib/api/entity/EntityCreator.java @@ -7,10 +7,10 @@ import org.bukkit.entity.LivingEntity; import org.bukkit.util.Vector; -import java.util.List; +import java.util.HashMap; import java.util.Objects; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendDebugMessage; +import static com.github.pinont.plugin.CorePlugin.sendDebugMessage; /** * Builder class for creating and configuring entities before spawning them. @@ -20,58 +20,7 @@ public class EntityCreator { private final EntityType entityType; - private Entity passenger = null; - - private List ScoreboardTag; - - - private boolean isSetFireTicks = false; - private int fireTicks; - - private boolean isSetGlowing = false; - private boolean glowing; - - private boolean isSetInvulnerable = false; - private boolean invulnerable; - - private boolean isSetSilient = false; - private boolean silent; - - private boolean isSetGravity = false; - private boolean gravity; - - private boolean isSetSilentGravity = false; - private boolean persistent; - - private boolean isSetFreezeTicks = false; - private int freezeTicks; - - private boolean isSetCustomNameVisible = false; - private boolean customNameVisible; - - private boolean isSetProtalCooldown = false; - private int portalCooldown; - - private boolean isSetFallingDistance = false; - private float fallingDistance; - - private boolean isSetRotation = false; - private float[] rotation = null; - - private boolean isSetVector = false; - private Vector vector = null; - - private boolean isSetVisualFire = false; - private boolean visualFire; - - private boolean isSetVisibleByDefault = false; - private boolean visibleByDefault = true; - - private boolean isSetTicksLived = false; - private int ticksLived; - - private boolean isSetMaxHealth = false; - private double maxHealth; + private HashMap properties; /** * Creates a new EntityCreator for the specified entity type. @@ -89,7 +38,7 @@ public EntityCreator(EntityType entityType) { * @return this EntityCreator for method chaining */ public EntityCreator addPassenger(Entity passenger) { - this.passenger = passenger; + properties.put("passenger", passenger); return this; } @@ -100,7 +49,7 @@ public EntityCreator addPassenger(Entity passenger) { * @return this EntityCreator for method chaining */ public EntityCreator addScoreboardTag(String... ScoreboardTag) { - this.ScoreboardTag.addAll(List.of(ScoreboardTag)); + properties.put("scoreboardTag", ScoreboardTag); return this; } @@ -111,8 +60,7 @@ public EntityCreator addScoreboardTag(String... ScoreboardTag) { * @return this EntityCreator for method chaining */ public EntityCreator setMaxHealth(double maxHealth) { - this.isSetMaxHealth = true; - this.maxHealth = maxHealth; + properties.put("maxHealth", maxHealth); return this; } @@ -123,8 +71,7 @@ public EntityCreator setMaxHealth(double maxHealth) { * @return this EntityCreator for method chaining */ public EntityCreator setFireTicks(int ticks) { - this.isSetFireTicks = true; - this.fireTicks = ticks; + properties.put("fireTicks", ticks); return this; } @@ -135,8 +82,7 @@ public EntityCreator setFireTicks(int ticks) { * @return this EntityCreator for method chaining */ public EntityCreator setGlowing(boolean glowing) { - this.isSetGlowing = true; - this.glowing = glowing; + properties.put("glowing", glowing); return this; } @@ -147,8 +93,7 @@ public EntityCreator setGlowing(boolean glowing) { * @return this EntityCreator for method chaining */ public EntityCreator setInvulnerable(boolean invulnerable) { - this.isSetInvulnerable = true; - this.invulnerable = invulnerable; + properties.put("invulnerable", invulnerable); return this; } @@ -159,8 +104,7 @@ public EntityCreator setInvulnerable(boolean invulnerable) { * @return this EntityCreator for method chaining */ public EntityCreator setSilent(boolean silent) { - this.isSetSilient = true; - this.silent = silent; + properties.put("silent", silent); return this; } @@ -171,8 +115,7 @@ public EntityCreator setSilent(boolean silent) { * @return this EntityCreator for method chaining */ public EntityCreator hasGravity(boolean gravity) { - this.isSetGravity = true; - this.gravity = gravity; + properties.put("gravity", gravity); return this; } @@ -183,8 +126,7 @@ public EntityCreator hasGravity(boolean gravity) { * @return this EntityCreator for method chaining */ public EntityCreator setVelocity(Vector vector) { - this.isSetVector = true; - this.vector = vector; + properties.put("vector", vector); return this; } @@ -195,8 +137,7 @@ public EntityCreator setVelocity(Vector vector) { * @return this EntityCreator for method chaining */ public EntityCreator setPersistent(Boolean persistent) { - this.isSetSilentGravity = true; - this.persistent = persistent; + properties.put("persistent", persistent); return this; } @@ -207,8 +148,7 @@ public EntityCreator setPersistent(Boolean persistent) { * @return this EntityCreator for method chaining */ public EntityCreator setFreezeTicks(int ticks) { - this.isSetFreezeTicks = true; - this.freezeTicks = ticks; + properties.put("freezeTicks", ticks); return this; } @@ -219,8 +159,7 @@ public EntityCreator setFreezeTicks(int ticks) { * @return this EntityCreator for method chaining */ public EntityCreator setCustomNameVisible(Boolean visible) { - this.isSetCustomNameVisible = true; - this.customNameVisible = visible; + properties.put("customNameVisible", visible); return this; } @@ -231,8 +170,7 @@ public EntityCreator setCustomNameVisible(Boolean visible) { * @return this EntityCreator for method chaining */ public EntityCreator setPortalCooldown(int ticks) { - this.isSetProtalCooldown = true; - this.portalCooldown = ticks; + properties.put("portalCooldown", ticks); return this; } @@ -243,8 +181,7 @@ public EntityCreator setPortalCooldown(int ticks) { * @return this EntityCreator for method chaining */ public EntityCreator setFallingDistance(float distance) { - this.isSetFallingDistance = true; - this.fallingDistance = distance; + properties.put("fallingDistance", distance); return this; } @@ -256,8 +193,7 @@ public EntityCreator setFallingDistance(float distance) { * @return this EntityCreator for method chaining */ public EntityCreator setRotation(float yaw, float pitch) { - this.isSetRotation = true; - this.rotation = new float[]{yaw, pitch}; + properties.put("rotation", new float[]{yaw, pitch}); return this; } @@ -268,8 +204,7 @@ public EntityCreator setRotation(float yaw, float pitch) { * @return this EntityCreator for method chaining */ public EntityCreator setTicksLived(int ticks) { - this.isSetTicksLived = true; - this.ticksLived = ticks; + properties.put("ticksLived", ticks); return this; } @@ -280,8 +215,7 @@ public EntityCreator setTicksLived(int ticks) { * @return this EntityCreator for method chaining */ public EntityCreator setVisibleByDefault(boolean visible) { - this.isSetVisibleByDefault = true; - this.visibleByDefault = visible; + properties.put("visibleByDefault", visible); return this; } @@ -292,8 +226,7 @@ public EntityCreator setVisibleByDefault(boolean visible) { * @return this EntityCreator for method chaining */ public EntityCreator setVisualFire(boolean fire) { - this.isSetVisualFire = true; - this.visualFire = fire; + properties.put("visualFire", fire); return this; } @@ -306,7 +239,9 @@ public EntityCreator setVisualFire(boolean fire) { public Entity spawn(Location location) { Entity entity = Objects.requireNonNull(location.getWorld()).spawnEntity(location, entityType); sendDebugMessage("Spawned " + entityType + " at " + location); - if (isSetMaxHealth) { + if (properties != null) return entity; + if (properties.containsKey("maxHealth")) { + double maxHealth = (double) properties.get("maxHealth"); if (entity instanceof LivingEntity livingEntity) { livingEntity.setMaxHealth(maxHealth); livingEntity.setHealth(maxHealth); @@ -314,34 +249,75 @@ public Entity spawn(Location location) { sendDebugMessage("Entity " + entityType + " does not support health setting."); } } - if (passenger != null) entity.addPassenger(passenger); - if (ScoreboardTag != null) { - for (String tag : ScoreboardTag) { + if (properties.containsKey("passenger")) { + Entity passenger = (Entity) properties.get("passenger"); + entity.addPassenger(passenger); + } + if (properties.containsKey("scoreboardTag")) { + String[] tags = (String[]) properties.get("scoreboardTag"); + for (String tag : tags) { entity.addScoreboardTag(tag); } } - if (isSetFireTicks) entity.setFireTicks(fireTicks); - if (isSetGlowing) entity.setGlowing(glowing); - if (isSetInvulnerable) entity.setInvulnerable(invulnerable); - if (isSetSilient) entity.setSilent(silent); - if (isSetGravity) entity.setGravity(gravity); - if (isSetSilentGravity) entity.setPersistent(persistent); - if (isSetFreezeTicks) entity.setFreezeTicks(freezeTicks); - if (isSetCustomNameVisible) entity.setCustomNameVisible(customNameVisible); - if (isSetProtalCooldown) entity.setPortalCooldown(portalCooldown); - if (isSetFallingDistance) entity.setFallDistance(fallingDistance); - if (isSetRotation) { - entity.setRotation(rotation[0], rotation[1]); + if (properties.containsKey("fireTicks")) { + int ticks = (int) properties.get("fireTicks"); + entity.setFireTicks(ticks); + } + if (properties.containsKey("glowing")) { + boolean glowing = (boolean) properties.get("glowing"); + entity.setGlowing(glowing); + } + if (properties.containsKey("invulnerable")) { + boolean invulnerable = (boolean) properties.get("invulnerable"); + entity.setInvulnerable(invulnerable); + } + if (properties.containsKey("silent")) { + boolean silent = (boolean) properties.get("silent"); + entity.setSilent(silent); + } + if (properties.containsKey("gravity")) { + boolean gravity = (boolean) properties.get("gravity"); + entity.setGravity(gravity); } - if (isSetVector) { + if (properties.containsKey("vector")) { + Vector vector = (Vector) properties.get("vector"); entity.setVelocity(vector); } - if (isSetVisualFire) entity.setVisualFire(visualFire); - if (isSetVisibleByDefault) { - entity.setVisibleByDefault(visibleByDefault); + if (properties.containsKey("persistent")) { + boolean persistent = (boolean) properties.get("persistent"); + entity.setPersistent(persistent); + } + if (properties.containsKey("freezeTicks")) { + int ticks = (int) properties.get("freezeTicks"); + entity.setFreezeTicks(ticks); + } + if (properties.containsKey("customNameVisible")) { + boolean visible = (boolean) properties.get("customNameVisible"); + entity.setCustomNameVisible(visible); + } + if (properties.containsKey("portalCooldown")) { + int ticks = (int) properties.get("portalCooldown"); + entity.setPortalCooldown(ticks); + } + if (properties.containsKey("fallingDistance")) { + float distance = (float) properties.get("fallingDistance"); + entity.setFallDistance(distance); + } + if (properties.containsKey("rotation")) { + float[] rotation = (float[]) properties.get("rotation"); + entity.setRotation(rotation[0], rotation[1]); + } + if (properties.containsKey("ticksLived")) { + int ticks = (int) properties.get("ticksLived"); + entity.setTicksLived(ticks); + } + if (properties.containsKey("visibleByDefault")) { + boolean visible = (boolean) properties.get("visibleByDefault"); + entity.setVisibleByDefault(visible); } - if (isSetTicksLived) { - entity.setTicksLived(ticksLived); + if (properties.containsKey("visualFire")) { + boolean fire = (boolean) properties.get("visualFire"); + entity.setVisualFire(fire); } return entity; } diff --git a/src/main/java/com/github/pinont/singularitylib/api/event/ItemExecuteEvent.java b/src/main/java/com/github/pinont/singularitylib/api/event/ItemExecuteEvent.java index acfb2e9..562a5fe 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/event/ItemExecuteEvent.java +++ b/src/main/java/com/github/pinont/singularitylib/api/event/ItemExecuteEvent.java @@ -12,7 +12,7 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendDebugMessage; +import static com.github.pinont.plugin.CorePlugin.sendDebugMessage; /** * Event that is fired when a player executes an item interaction. diff --git a/src/main/java/com/github/pinont/singularitylib/api/event/PlayerDamageByPlayerEvent.java b/src/main/java/com/github/pinont/singularitylib/api/event/PlayerDamageByPlayerEvent.java new file mode 100644 index 0000000..6c42216 --- /dev/null +++ b/src/main/java/com/github/pinont/singularitylib/api/event/PlayerDamageByPlayerEvent.java @@ -0,0 +1,53 @@ +package com.github.pinont.singularitylib.api.event; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class PlayerDamageByPlayerEvent extends Event implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private boolean isCancelled = false; + private final Player player; + private final Player damager; + + public PlayerDamageByPlayerEvent(Player player, Player hitter) { + this.player = player; + this.damager = hitter; + } + + public Player getPlayer() { + return this.player; + } + + public Player getDamager() { + return this.damager; + } + + public boolean callEvent() { + Bukkit.getPluginManager().callEvent(this); + return this.isCancelled(); + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public void setCancelled(boolean b) { + this.isCancelled = b; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/com/github/pinont/singularitylib/api/hook/discordJDA/DiscordApp.java b/src/main/java/com/github/pinont/singularitylib/api/hook/discordJDA/DiscordApp.java index 66a3cd1..495a19d 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/hook/discordJDA/DiscordApp.java +++ b/src/main/java/com/github/pinont/singularitylib/api/hook/discordJDA/DiscordApp.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.Collections; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendConsoleMessage; +import static com.github.pinont.plugin.CorePlugin.sendConsoleMessage; /** * Abstract base class for Discord bot applications using JDA (Java Discord API). diff --git a/src/main/java/com/github/pinont/singularitylib/api/items/CrossbowCreator.java b/src/main/java/com/github/pinont/singularitylib/api/items/CrossbowCreator.java index ef0fb72..2e7a0a3 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/items/CrossbowCreator.java +++ b/src/main/java/com/github/pinont/singularitylib/api/items/CrossbowCreator.java @@ -4,6 +4,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.CrossbowMeta; import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; /** * A specialized ItemCreator for creating crossbow items. @@ -14,8 +15,8 @@ public class CrossbowCreator extends ItemCreator { /** * Creates a new CrossbowCreator with a crossbow ItemStack. */ - public CrossbowCreator() { - super(new ItemStack(Material.CROSSBOW)); + public CrossbowCreator(Plugin plugin) { + super(plugin, new ItemStack(Material.CROSSBOW)); } /** diff --git a/src/main/java/com/github/pinont/singularitylib/api/items/ItemCreator.java b/src/main/java/com/github/pinont/singularitylib/api/items/ItemCreator.java index 2e2c394..007cd33 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/items/ItemCreator.java +++ b/src/main/java/com/github/pinont/singularitylib/api/items/ItemCreator.java @@ -23,8 +23,7 @@ import java.util.*; -import static com.github.pinont.singularitylib.plugin.CorePlugin.getInstance; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendConsoleMessage; +import static com.github.pinont.plugin.CorePlugin.sendConsoleMessage; /** * Builder class for creating and modifying ItemStacks with enhanced functionality. @@ -34,16 +33,16 @@ public class ItemCreator { private final Common common = new Common(); - private final ItemStack item; + private ItemStack item; private ItemMeta meta; private short durability = 0; - private final PersistentDataContainer data; + private PersistentDataContainer data; private final ArrayList lore = new ArrayList<>(); private int amount = 1; private Material type; - private final Plugin plugin = getInstance(); + private final Plugin plugin; private static final Set ITEM_INTERACTIONS = Sets.newHashSet(); - private final String name; + private String name; /** * Gets all registered item interactions. @@ -55,7 +54,7 @@ public static Set getInteractions() { } public ItemCreator clone() { - return new ItemCreator(this.create()); + return new ItemCreator(plugin, this.create()); } /** @@ -63,8 +62,8 @@ public ItemCreator clone() { * * @param type the material type for the item */ - public ItemCreator(Material type) { - this(new ItemStack(type)); + public ItemCreator(Plugin plugin, Material type) { + this(plugin, new ItemStack(type)); } /** @@ -73,8 +72,8 @@ public ItemCreator(Material type) { * @param type the material type for the item * @param amount the amount of items in the stack */ - public ItemCreator(Material type, int amount) { - this(new ItemStack(type, amount)); + public ItemCreator(Plugin plugin, Material type, int amount) { + this(plugin, new ItemStack(type, amount)); } /** @@ -82,9 +81,10 @@ public ItemCreator(Material type, int amount) { * * @param item the ItemStack to create from */ - public ItemCreator(@NotNull ItemStack item) { + public ItemCreator(Plugin plugin, @NotNull ItemStack item) { this.item = item; this.meta = item.getItemMeta(); + this.plugin = plugin; this.type = item.getType(); this.amount = item.getAmount(); this.name = item.getItemMeta().getDisplayName().isEmpty() ? Common.normalizeStringName(item.getType().name()) : item.getItemMeta().getDisplayName(); @@ -186,27 +186,6 @@ public ItemCreator setItemMeta(ItemMeta meta) { return this; } - /** - * Gets the interaction associated with the given ItemStack. - * - * @param item the ItemStack to get the interaction for - * @return the ItemInteraction, or null if none found - */ - public static ItemInteraction getInteraction(ItemStack item) { - String id = getItemInteractionName(item); - if (id == null) { - sendConsoleMessage(ChatColor.RED + "Item interaction not found for item: " + item.getType()); - return null; - } - ItemInteraction interaction = ITEM_INTERACTIONS.stream().filter(itemInteraction -> itemInteraction.getName().equals(id)).findFirst().orElse(null); - if (interaction != null) { - return interaction; - } else { - sendConsoleMessage(ChatColor.RED + "Item interaction not found for item: " + item.getType()); - } - return null; - } - /** * Sets the material type of the item. * @@ -504,24 +483,6 @@ public ItemCreator setDurability(int durability) { return this; } - /** - * Gets the persistent data of an item. - * - * @param item the ItemStack to get data from - * @param key the key of the data to get - * @param type the PersistentDataType of the data - * @return the data value, or null if not present - */ - public static Object getItemPersistData(ItemStack item, String key, PersistentDataType type) { - if (isItemHasPersistData(item, key, type)) { - ItemMeta meta = item.getItemMeta(); - if (meta != null) { - return meta.getPersistentDataContainer().get(new NamespacedKey(getInstance(), key), type); - } - } - return null; - } - /** * Sets data in the item's PersistentDataContainer. * @@ -566,16 +527,6 @@ public ItemCreator setDataContainer(String key, Object value, PersisDataType typ return this; } - private static String getItemInteractionName(ItemStack item) { - if (isItemHasPersistData(item, "interaction", PersistentDataType.STRING)) { - ItemMeta meta = item.getItemMeta(); - if (meta != null) { - return Objects.requireNonNull(meta.getPersistentDataContainer().get(new NamespacedKey(getInstance(), "interaction"), PersistentDataType.STRING)); - } - } - return null; - } - /** * Adds an interaction to the item. * @@ -588,17 +539,5 @@ public ItemCreator addInteraction(ItemInteraction itemInteraction) { this.setDataContainer("interaction", itemInteraction.getName(), PersisDataType.STRING); return this; } - - /** - * Checks if the item has persistent data of a specific type. - * - * @param item the ItemStack to check - * @param key the key of the data to check for - * @param type the PersistentDataType of the data - * @return true if the data exists, false otherwise - */ - public static Boolean isItemHasPersistData(ItemStack item, String key, PersistentDataType type) { - return item.getItemMeta().getPersistentDataContainer().has(new NamespacedKey(getInstance(), key), type); - } } diff --git a/src/main/java/com/github/pinont/singularitylib/api/items/ItemHeadCreator.java b/src/main/java/com/github/pinont/singularitylib/api/items/ItemHeadCreator.java index 641e5f5..a96c324 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/items/ItemHeadCreator.java +++ b/src/main/java/com/github/pinont/singularitylib/api/items/ItemHeadCreator.java @@ -3,6 +3,7 @@ import com.github.pinont.singularitylib.api.utils.Common; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; /** @@ -18,8 +19,8 @@ public class ItemHeadCreator extends ItemCreator { * * @param item the ItemStack to create from (should be a player head) */ - public ItemHeadCreator(@NotNull ItemStack item) { - super(item); + public ItemHeadCreator(Plugin plugin, @NotNull ItemStack item) { + super(plugin, item); skullMeta = (SkullMeta) item.getItemMeta(); } diff --git a/src/main/java/com/github/pinont/singularitylib/api/items/ItemInteraction.java b/src/main/java/com/github/pinont/singularitylib/api/items/ItemInteraction.java index 4243c76..32829c4 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/items/ItemInteraction.java +++ b/src/main/java/com/github/pinont/singularitylib/api/items/ItemInteraction.java @@ -1,15 +1,32 @@ package com.github.pinont.singularitylib.api.items; +import org.bukkit.ChatColor; +import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; import org.bukkit.event.block.Action; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.Plugin; +import java.util.Objects; import java.util.Set; +import static com.github.pinont.plugin.CorePlugin.sendConsoleMessage; + /** * Interface for defining custom item interactions. * Items with interactions can respond to player clicks and actions. */ -public interface ItemInteraction { +public abstract class ItemInteraction { + + private String name; + private Set action; + + public ItemInteraction(String name, Set action) { + this.name = name; + this.action = action; + } /** * Gets the number of items to remove when this interaction is executed. @@ -17,7 +34,7 @@ public interface ItemInteraction { * * @return the number of items to remove from the stack */ - default int removeItemAmountOnExecute() { + public int removeItemAmountOnExecute() { return 0; } @@ -26,21 +43,25 @@ default int removeItemAmountOnExecute() { * * @return the interaction name */ - String getName(); + public String getName() { + return name; + } /** * Gets the set of actions that trigger this interaction. * * @return the set of triggering actions */ - Set getAction(); + public Set getAction() { + return action; + } /** * Executes this interaction for the given player. * * @param player the player who triggered the interaction */ - void execute(Player player); + public abstract void execute(Player player); /** * Whether to cancel the original event when this interaction is executed. @@ -48,8 +69,69 @@ default int removeItemAmountOnExecute() { * * @return true to cancel the event, false to allow it to continue */ - default boolean cancelEvent() { + public boolean cancelEvent() { return true; } + /** + * Checks if the item has persistent data of a specific type. + * + * @param item the ItemStack to check + * @param key the key of the data to check for + * @param type the PersistentDataType of the data + * @return true if the data exists, false otherwise + */ + public static Boolean isItemHasPersistData(Plugin plugin, ItemStack item, String key, PersistentDataType type) { + return item.getItemMeta().getPersistentDataContainer().has(new NamespacedKey(plugin, key), type); + } + + private static String getItemInteractionName(Plugin plugin, ItemStack item) { + if (isItemHasPersistData(plugin, item, "interaction", PersistentDataType.STRING)) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + return Objects.requireNonNull(meta.getPersistentDataContainer().get(new NamespacedKey(plugin, "interaction"), PersistentDataType.STRING)); + } + } + return null; + } + + /** + * Gets the persistent data of an item. + * + * @param item the ItemStack to get data from + * @param key the key of the data to get + * @param type the PersistentDataType of the data + * @return the data value, or null if not present + */ + public static Object getItemPersistData(Plugin plugin, ItemStack item, String key, PersistentDataType type) { + if (isItemHasPersistData(plugin, item, key, type)) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + return meta.getPersistentDataContainer().get(new NamespacedKey(plugin, key), type); + } + } + return null; + } + + /** + * Gets the interaction associated with the given ItemStack. + * + * @param item the ItemStack to get the interaction for + * @return the ItemInteraction, or null if none found + */ + public static ItemInteraction getInteraction(Plugin plugin, ItemStack item) { + String id = getItemInteractionName(plugin, item); + if (id == null) { + sendConsoleMessage(ChatColor.RED + "Item interaction not found for item: " + item.getType()); + return null; + } + ItemInteraction interaction = ItemCreator.getInteractions().stream().filter(itemInteraction -> itemInteraction.getName().equals(id)).findFirst().orElse(null); + if (interaction != null) { + return interaction; + } else { + sendConsoleMessage(ChatColor.RED + "Item interaction not found for item: " + item.getType()); + } + return null; + } + } diff --git a/src/main/java/com/github/pinont/singularitylib/api/manager/CommandManager.java b/src/main/java/com/github/pinont/singularitylib/api/manager/CommandManager.java index bf7ed8e..eceeefd 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/manager/CommandManager.java +++ b/src/main/java/com/github/pinont/singularitylib/api/manager/CommandManager.java @@ -10,8 +10,8 @@ import java.util.Arrays; import java.util.List; -import static com.github.pinont.singularitylib.plugin.CorePlugin.getStartTime; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendConsoleMessage; +import static com.github.pinont.plugin.CorePlugin.getStartTime; +import static com.github.pinont.plugin.CorePlugin.sendConsoleMessage; /** * Manages the registration of commands for the plugin. diff --git a/src/main/java/com/github/pinont/singularitylib/api/manager/ConfigManager.java b/src/main/java/com/github/pinont/singularitylib/api/manager/ConfigManager.java index bced8ec..1d0dc7f 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/manager/ConfigManager.java +++ b/src/main/java/com/github/pinont/singularitylib/api/manager/ConfigManager.java @@ -8,7 +8,7 @@ import java.io.File; import java.io.IOException; -import static com.github.pinont.singularitylib.plugin.CorePlugin.getInstance; +import static com.github.pinont.plugin.CorePlugin.getInstance; /** * Manages configuration files for the plugin. @@ -17,7 +17,7 @@ public class ConfigManager { private final File configFile; - private final FileConfiguration config; + private FileConfiguration config; private final String fileName; private final Plugin plugin = getInstance(); private boolean isFirstLoad; @@ -124,6 +124,19 @@ public FileConfiguration getConfig() { return config; } + /** + * Reloads the configuration from disk. + *

+ * Useful when the file is manually modified while the server is running. + */ + public void reloadConfig() { + if (configFile.exists()) { + config = YamlConfiguration.loadConfiguration(configFile); + } else { + Bukkit.getLogger().warning("Config file does not exist: " + fileName); + } + } + /** * Checks if this is the first time the configuration file is being loaded. * diff --git a/src/main/java/com/github/pinont/singularitylib/api/manager/CustomItemManager.java b/src/main/java/com/github/pinont/singularitylib/api/manager/CustomItemManager.java index 7ce4a38..025af1f 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/manager/CustomItemManager.java +++ b/src/main/java/com/github/pinont/singularitylib/api/manager/CustomItemManager.java @@ -1,36 +1,39 @@ package com.github.pinont.singularitylib.api.manager; -import com.github.pinont.singularitylib.api.command.SimpleCommand; +import com.github.pinont.devtool.api.CItemManager; import com.github.pinont.singularitylib.api.items.CustomItem; -import com.github.pinont.singularitylib.api.utils.Common; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; import java.util.*; -import static com.github.pinont.singularitylib.plugin.CorePlugin.getInstance; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendConsoleMessage; +import static com.github.pinont.plugin.CorePlugin.sendConsoleMessage; /** * Manages custom items and provides a give command for them. * This class handles registration of custom items and provides functionality * to give items to players through commands. */ -public class CustomItemManager implements SimpleCommand { +public class CustomItemManager { /** - * List of registered custom items. + * List of registered custom items managed by this manager. + * This list contains all custom items that have been registered + * and are available for use in commands and other operations. */ public List customItems = new ArrayList<>(); + /** + * Integration manager for SingularityDevTool plugin. + * Used when the dev tool plugin is present to provide additional + * development and debugging features for custom items. + */ + private CItemManager customItemManager; + /** * Default constructor for CustomItemManager. + * Initializes the manager with empty collections ready for item registration. */ public CustomItemManager() { } @@ -38,7 +41,7 @@ public CustomItemManager() { /** * Gets the list of registered custom items. * - * @return the list of custom items + * @return an unmodifiable view of the list of custom items currently registered */ public List getCustomItems() { return customItems; @@ -47,13 +50,58 @@ public List getCustomItems() { /** * Registers a list of custom items with the manager. * - * @param item the list of custom items to register + *

This method performs the following operations:

+ *
    + *
  • Validates the input list is not empty
  • + *
  • Logs the registration process to console
  • + *
  • Checks for SingularityDevTool plugin integration
  • + *
  • Registers items with dev tool if available
  • + *
  • Initializes item interactions and properties
  • + *
+ * + * @param item the list of custom items to register, must not be null or empty + * @throws IllegalArgumentException if the item list is null */ public void register(List item) { if (item.isEmpty()) return; sendConsoleMessage("Registering " + item.size() + " custom items"); - for (CustomItem customItem : item) { + registerAllItems(item); + } + + /** + * Internal method to register all items without dev tool integration. + * + * @param items the list of items to register + */ + private void registerAllItems(List items) { + registerAllItems(items, false); + } + + /** + * Internal method to register all items with optional dev tool integration. + * + *

For each custom item, this method:

+ *
    + *
  • Adds the item to the internal registry
  • + *
  • Registers with dev tool if requested and available
  • + *
  • Calls the item's register method for initialization
  • + *
  • Ensures the item stack is created and interactions are set up
  • + *
  • Logs interaction registration if present
  • + *
+ * + * @param items the list of items to register + * @param forDevTool whether to register items with the development tool + */ + private void registerAllItems(List items, boolean forDevTool) { + // WIP: DevTool integration +// if (forDevTool) { +// customItemManager = new CItemManager(); +// } + for (CustomItem customItem : items) { customItems.add(customItem); + if (forDevTool) { + customItemManager.registerCustomItems(customItem); + } customItem.register(); // call register to ensure the item is properly initialized customItem.getItem(); // ensure the item is created and interaction is set up if (customItem.getInteraction() != null) { @@ -62,67 +110,100 @@ public void register(List item) { } } - @Override - public String getName() { - return "give"; - } - - @Override - public String description() { - return "Give item to player"; - } - - @Override - public void execute(CommandSourceStack commandSourceStack, String[] strings) { - if (strings.length < 2) { - commandSourceStack.getSender().sendMessage("Usage: /give [count]"); - return; - } + // WIP: Move to dev tool - String itemName = strings[1]; - int count = strings.length > 2 ? Integer.parseInt(strings[2]) : 1; - - // Find the item - ItemStack item = null; - if (itemName.startsWith("minecraft:")) { - itemName = itemName.replace("minecraft:", ""); - item = new ItemStack(Objects.requireNonNull(Material.getMaterial(itemName.toUpperCase())), count); - } else if (itemName.startsWith(getInstance().getName().toLowerCase() + ":")) { - String finalItemName = itemName.replace(getInstance().getName().toLowerCase() + ":", ""); - item = customItems.stream() - .filter(customItem -> customItem.getName().equalsIgnoreCase(finalItemName)) - .findFirst() - .map(customItem -> customItem.register().setAmount(count).create()) - .orElse(null); - } - - if (item == null) { - commandSourceStack.getSender().sendMessage("Item not found"); - return; - } - - // Give the item to the player - if (strings[0].equalsIgnoreCase("@a")) { - for (Player player : Bukkit.getOnlinePlayers()) { - giveItemToPlayer(player, item); - } - } else if (strings[0].equalsIgnoreCase("@r")) { - List players = new ArrayList<>(Bukkit.getOnlinePlayers()); - Player randomPlayer = players.get(new Random().nextInt(players.size())); - giveItemToPlayer(randomPlayer, item); - } else if (strings[0].equalsIgnoreCase("@s")) { - Player player = (Player) commandSourceStack.getSender(); - giveItemToPlayer(player, item); - } else { - Player player = Bukkit.getPlayer(strings[0]); - if (player == null) { - commandSourceStack.getSender().sendMessage("Player not found"); - return; - } - giveItemToPlayer(player, item); - } - } +// /** +// * Gets the name of this command. +// * +// * @return the command name "give" +// */ +// @Override +// public String getName() { +// return "give"; +// } +// +// /** +// * Executes the give command with the provided arguments. +// * +// *

Command syntax: /give <player> <item> [count]

+// * +// *

Supported player selectors:

+// *
    +// *
  • @a - All players
  • +// *
  • @r - Random player
  • +// *
  • @s - Command sender (self)
  • +// *
  • player_name - Specific player by name
  • +// *
+// * +// *

Supported item formats:

+// *
    +// *
  • minecraft:item_name - Vanilla Minecraft items
  • +// *
  • plugin_name:item_name - Custom plugin items
  • +// *
+// * +// * @param commandSourceStack the source of the command execution +// * @param strings the command arguments [player, item, count] +// */ +// @Override +// public void execute(CommandSourceStack commandSourceStack, String[] strings) { +// if (strings.length < 2) { +// commandSourceStack.getSender().sendMessage("Usage: /give [count]"); +// return; +// } +// +// String itemName = strings[1]; +// int count = strings.length > 2 ? Integer.parseInt(strings[2]) : 1; +// +// // Find the item +// ItemStack item = null; +// if (itemName.startsWith("minecraft:")) { +// itemName = itemName.replace("minecraft:", ""); +// item = new ItemStack(Objects.requireNonNull(Material.getMaterial(itemName.toUpperCase())), count); +// } else if (itemName.startsWith(getInstance().getName().toLowerCase() + ":")) { +// String finalItemName = itemName.replace(getInstance().getName().toLowerCase() + ":", ""); +// item = customItems.stream() +// .filter(customItem -> customItem.getName().equalsIgnoreCase(finalItemName)) +// .findFirst() +// .map(customItem -> customItem.register().setAmount(count).create()) +// .orElse(null); +// } +// +// if (item == null) { +// commandSourceStack.getSender().sendMessage("Item not found"); +// return; +// } +// +// // Give the item to the player +// if (strings[0].equalsIgnoreCase("@a")) { +// for (Player player : Bukkit.getOnlinePlayers()) { +// giveItemToPlayer(player, item); +// } +// } else if (strings[0].equalsIgnoreCase("@r")) { +// List players = new ArrayList<>(Bukkit.getOnlinePlayers()); +// Player randomPlayer = players.get(new Random().nextInt(players.size())); +// giveItemToPlayer(randomPlayer, item); +// } else if (strings[0].equalsIgnoreCase("@s")) { +// Player player = (Player) commandSourceStack.getSender(); +// giveItemToPlayer(player, item); +// } else { +// Player player = Bukkit.getPlayer(strings[0]); +// if (player == null) { +// commandSourceStack.getSender().sendMessage("Player not found"); +// return; +// } +// giveItemToPlayer(player, item); +// } +// } + /** + * Gives an item to a specific player and sends a confirmation message. + * + *

This method adds the item to the player's inventory and sends + * a confirmation message showing the item name, quantity, and recipient.

+ * + * @param player the player to receive the item, must not be null + * @param item the item stack to give, must not be null + */ private void giveItemToPlayer(Player player, ItemStack item) { if (player != null) { player.getInventory().addItem(item); @@ -132,37 +213,99 @@ private void giveItemToPlayer(Player player, ItemStack item) { } } - @Override - public @NotNull Collection suggest(CommandSourceStack commandSourceStack, String[] args) { - return switch (args.length) { - case 0, 1 -> { - List players = new ArrayList<>(); - players.addAll(Bukkit.getOnlinePlayers().stream().map(Player::getName).toList()); - players.addAll(Arrays.asList("@a", "@s", "@r")); - yield players; - } - case 2 -> { - List items = new ArrayList<>(); - items.addAll(Arrays.stream(Common.getAllItemsMaterials()) - .map(material -> "minecraft:" + material.name().toLowerCase()) - .toList()); - items.addAll(customItems.stream() - .map(item -> getInstance().getName().toLowerCase() + ":" + item.getName()) - .toList()); - if (!args[1].isEmpty()) { - yield items.stream() - .filter(item -> item.split(":")[1].toLowerCase().startsWith(args[1].toLowerCase())) - .toList(); - } - yield items; - } - case 3 -> Collections.singletonList(""); - default -> Collections.emptyList(); - }; - } + // WIP: Move to dev tool - @Override - public boolean canUse(CommandSender sender) { - return sender instanceof Player; - } +// /** +// * Provides intelligent tab completion suggestions for the give command. +// * +// *

This method dynamically generates context-aware suggestions based on the current +// * argument position and existing input, enhancing the user experience by providing +// * relevant completions for each stage of command construction.

+// * +// *

Tab Completion Behavior:

+// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// * +// *
Argument PositionSuggestions ProvidedDescription
1 (Player)Online player names + selectorsAll currently connected players, plus @a (all), @s (self), @r (random)
2 (Item)Available items with namespacesMinecraft items (minecraft:) and registered custom items (plugin:)
3 (Count)<count> placeholderVisual hint indicating numeric quantity expected
+// * +// *

Intelligent Filtering:

+// *

For item suggestions (argument 2), the method applies smart filtering when partial +// * input is detected, matching against the item name portion after the namespace prefix. +// * This allows for efficient item discovery through progressive typing.

+// * +// *

Performance Considerations:

+// *

The method efficiently streams and filters large collections of materials and +// * custom items, ensuring responsive tab completion even with extensive item registries.

+// * +// * @param commandSourceStack the command execution context providing sender information +// * @param args the array of command arguments currently being typed by the user +// * @return a collection of contextually relevant completion suggestions, never null +// * +// * @since 1.0.0 +// * @deprecated since 1.1.0, scheduled for removal - use new completion system +// * +// * @see #execute(CommandSourceStack, String[]) for command execution logic +// * @see CustomItem#getName() for custom item name retrieval +// */ +// @Deprecated(since = "1.1.0", forRemoval = true) +// @Override +// public @NotNull Collection suggest(CommandSourceStack commandSourceStack, String[] args) { +// return switch (args.length) { +// case 0, 1 -> { +// List players = new ArrayList<>(); +// players.addAll(Bukkit.getOnlinePlayers().stream().map(Player::getName).toList()); +// players.addAll(Arrays.asList("@a", "@s", "@r")); +// yield players; +// } +// case 2 -> { +// List items = new ArrayList<>(); +// items.addAll(Arrays.stream(Common.getAllItemsMaterials()) +// .map(material -> "minecraft:" + material.name().toLowerCase()) +// .toList()); +// items.addAll(customItems.stream() +// .map(item -> getInstance().getName().toLowerCase() + ":" + item.getName()) +// .toList()); +// if (!args[1].isEmpty()) { +// yield items.stream() +// .filter(item -> item.split(":")[1].toLowerCase().startsWith(args[1].toLowerCase())) +// .toList(); +// } +// yield items; +// } +// case 3 -> Collections.singletonList(""); +// default -> Collections.emptyList(); +// }; +// } +// +// /** +// * Determines if a command sender can use this command. +// * +// *

Currently, only players are allowed to use the give command. +// * Console and other command senders are not permitted.

+// * +// * @param sender the command sender to check permissions for +// * @return true if the sender is a player, false otherwise +// */ +// @Override +// public boolean canUse(CommandSender sender) { +// return sender instanceof Player; +// } } diff --git a/src/main/java/com/github/pinont/singularitylib/api/manager/FileManager.java b/src/main/java/com/github/pinont/singularitylib/api/manager/FileManager.java index 1da0143..2c881b6 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/manager/FileManager.java +++ b/src/main/java/com/github/pinont/singularitylib/api/manager/FileManager.java @@ -1,14 +1,14 @@ package com.github.pinont.singularitylib.api.manager; -import com.github.pinont.singularitylib.plugin.CorePlugin; +import com.github.pinont.plugin.CorePlugin; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; import java.util.List; import java.util.Objects; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendConsoleMessage; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendDebugMessage; +import static com.github.pinont.plugin.CorePlugin.sendConsoleMessage; +import static com.github.pinont.plugin.CorePlugin.sendDebugMessage; /** * Manages file operations for the plugin. diff --git a/src/main/java/com/github/pinont/singularitylib/api/manager/WorldManager.java b/src/main/java/com/github/pinont/singularitylib/api/manager/WorldManager.java index 7593b00..b2363fe 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/manager/WorldManager.java +++ b/src/main/java/com/github/pinont/singularitylib/api/manager/WorldManager.java @@ -6,7 +6,7 @@ import java.util.Objects; -import static com.github.pinont.singularitylib.plugin.CorePlugin.getInstance; +import static com.github.pinont.plugin.CorePlugin.getInstance; /** * Manager class for creating, loading, and managing worlds. diff --git a/src/main/java/com/github/pinont/singularitylib/api/runnable/Runner.java b/src/main/java/com/github/pinont/singularitylib/api/runnable/Runner.java new file mode 100644 index 0000000..725c8f0 --- /dev/null +++ b/src/main/java/com/github/pinont/singularitylib/api/runnable/Runner.java @@ -0,0 +1,5 @@ +package com.github.pinont.singularitylib.api.runnable; + +public interface Runner { + void run(); +} diff --git a/src/main/java/com/github/pinont/singularitylib/api/runnable/Scheduler.java b/src/main/java/com/github/pinont/singularitylib/api/runnable/Scheduler.java new file mode 100644 index 0000000..4821eb1 --- /dev/null +++ b/src/main/java/com/github/pinont/singularitylib/api/runnable/Scheduler.java @@ -0,0 +1,127 @@ +package com.github.pinont.singularitylib.api.runnable; + +import com.github.pinont.singularitylib.api.utils.Common; +import com.github.pinont.plugin.CorePlugin; +import io.papermc.paper.threadedregions.scheduler.*; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; + +import java.util.concurrent.TimeUnit; + +public class Scheduler { + + private final boolean isFolia = CorePlugin.isFolia(); + + private final Server server = Bukkit.getServer(); + + private Runner runner; + private BukkitScheduler bukkitScheduler; + private AsyncScheduler asyncScheduler; + + public ScheduledTask scheduledTask = null; + public BukkitTask bukkitTask = null; + + private GlobalRegionScheduler globalScheduler; + private RegionScheduler regionScheduler; + private EntityScheduler entityScheduler; + + enum RunnerType { + SYNC, + ASYNC, + GLOBAL, + REGION, + ENTITY + } + + public AsyncScheduler getAsyncScheduler() { + return asyncScheduler; + } + + public BukkitScheduler getBukkitScheduler() { + return bukkitScheduler; + } + + public GlobalRegionScheduler getGlobalScheduler() { + return globalScheduler; + } + + public RegionScheduler getRegionScheduler() { + return regionScheduler; + } + + public Object getTask() { + return isFolia ? scheduledTask : bukkitTask; + } + + public void runTaskAsync(Plugin plugin, Runner runner) { + this.runner = runner; + if (isFolia) { + asyncScheduler = server.getAsyncScheduler(); + scheduledTask = asyncScheduler.runNow(plugin, _ -> runner.run()); + } else { + bukkitScheduler = server.getScheduler(); + bukkitTask = bukkitScheduler.runTaskAsynchronously(plugin, runner::run); + } + } + + public void runTask(Plugin plugin, Runner runner) { + this.runner = runner; + if (isFolia) { + globalScheduler = server.getGlobalRegionScheduler(); + scheduledTask = globalScheduler.run(plugin, _ -> runner.run()); + } else { + bukkitScheduler = server.getScheduler(); + bukkitTask = bukkitScheduler.runTask(plugin, runner::run); + } + } + + public void runRepeatingTask(Plugin plugin, Location location, Runner runner, int delayTicks, long periodTicks, TimeUnit timeUnit) { + this.runner = runner; + if (isFolia) { + regionScheduler = server.getRegionScheduler(); + scheduledTask = regionScheduler.runAtFixedRate(plugin, location, _ -> runner.run(), delayTicks, periodTicks); + } else { + bukkitScheduler = server.getScheduler(); + bukkitTask = bukkitScheduler.runTaskTimer(plugin, runner::run, 0L, periodTicks * timeUnit.toMillis(periodTicks)); + } + } + + public void runRepeatingTask(Plugin plugin, World world, Runner runner, int delayTicks, long periodTicks, TimeUnit timeUnit) { + new Scheduler().runRepeatingTask(plugin, new Location(world, 0, 0, 0), runner,delayTicks, periodTicks, timeUnit); + } + + public void runRepeatingTaskAsync(Plugin plugin, Runner runner, int delayTicks, long periodTicks, TimeUnit timeUnit) { + if (isFolia) { + asyncScheduler = server.getAsyncScheduler(); + scheduledTask = asyncScheduler.runAtFixedRate(plugin, _ -> runner.run(), delayTicks, periodTicks, timeUnit); + } else { + bukkitScheduler = server.getScheduler(); + bukkitTask = bukkitScheduler.runTaskTimer(plugin, runner::run, delayTicks, Common.toTicks(periodTicks, timeUnit)); + } + } + + public void cancelTask() { + if (isFolia) { + if (scheduledTask != null) { + scheduledTask.cancel(); + } + } else { + if (bukkitTask != null) { + bukkitTask.cancel(); + } + } + } + + public void cancelAllTasks(Plugin plugin) { + if (isFolia) { + asyncScheduler.cancelTasks(plugin); + } else { + bukkitScheduler.cancelTasks(plugin); + } + } +} diff --git a/src/main/java/com/github/pinont/singularitylib/api/ui/Layout.java b/src/main/java/com/github/pinont/singularitylib/api/ui/Layout.java index d65b13a..8311f60 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/ui/Layout.java +++ b/src/main/java/com/github/pinont/singularitylib/api/ui/Layout.java @@ -1,21 +1,30 @@ package com.github.pinont.singularitylib.api.ui; -/** - * Interface for defining layouts in menus. - * A layout maps a character key to a button for menu positioning. - */ -public interface Layout { +public class Layout { + + private final char key; + private final Button button; + public Layout(char c, Button button) { + this.key = c; + this.button = button; + + } + /** * Gets the character key used to position this layout in a menu. * * @return the character key */ - char getKey(); + public char getKey() { + return key; + } /** * Gets the button associated with this layout. * * @return the button for this layout position */ - Button getButton(); + public Button getButton() { + return button; + } } diff --git a/src/main/java/com/github/pinont/singularitylib/api/ui/Menu.java b/src/main/java/com/github/pinont/singularitylib/api/ui/Menu.java index b2bf86b..67fb1c5 100644 --- a/src/main/java/com/github/pinont/singularitylib/api/ui/Menu.java +++ b/src/main/java/com/github/pinont/singularitylib/api/ui/Menu.java @@ -8,13 +8,14 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.plugin.Plugin; import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; -import static com.github.pinont.singularitylib.plugin.CorePlugin.getInstance; -import static com.github.pinont.singularitylib.plugin.CorePlugin.sendDebugMessage; +import static com.github.pinont.plugin.CorePlugin.getInstance; +import static com.github.pinont.plugin.CorePlugin.sendDebugMessage; /** * Class for creating and managing custom inventory menus. @@ -24,6 +25,7 @@ public class Menu { private final String title; private final int size; + private final Plugin plugin; private final ArrayList patternLayout = new ArrayList<>(); private final ArrayList layouts = new ArrayList<>(); private final ArrayList