diff --git a/pom.xml b/pom.xml
index 6e9b8fb..4e3a88a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.github.pinontsingularitylib
- 1.0.0
+ 1.3.0-SNAPSHOTjarSingularityLib
@@ -52,6 +52,9 @@
23
+ 1.21.8-R0.1-SNAPSHOT
+ 4.87.0
+ ${paperapi.version}UTF-8UTF-8${java.version}
@@ -74,6 +77,10 @@
sonatypehttps://oss.sonatype.org/content/groups/public/
+
+ jitpack.io
+ https://jitpack.io
+
@@ -130,6 +137,40 @@
maven-deploy-plugin3.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.paperpaper-api
- 1.21.8-R0.1-SNAPSHOT
+ ${paperapi.version}
+ provided
+
+
+ dev.folia
+ folia-api
+ ${paperapi.version}provided
@@ -165,7 +212,13 @@
org.mockbukkit.mockbukkitmockbukkit-v1.21
- 4.77.0
+ ${mockbukkit.version}
+ test
+
+
+ io.papermc.paper
+ paper-api
+ ${paper.version.from.mockbukkit}test
@@ -174,5 +227,11 @@
2.0.16test
+
+ 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 Position
+// *
Suggestions Provided
+// *
Description
+// *
+// *
+// *
1 (Player)
+// *
Online player names + selectors
+// *
All currently connected players, plus @a (all), @s (self), @r (random)
+// *
+// *
+// *
2 (Item)
+// *
Available items with namespaces
+// *
Minecraft items (minecraft:) and registered custom items (plugin:)
+// *
+// *
+// *
3 (Count)
+// *
<count> placeholder
+// *
Visual 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