diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..1e306ba --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,33 @@ +name: Java CI with Gradle + +on: + push: + branches: [ "master", "main" ] + pull_request: + branches: [ "master", "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: StoneDamager-Build + path: build/libs/*.jar diff --git a/build.gradle b/build.gradle index 578a94b..aede55b 100644 --- a/build.gradle +++ b/build.gradle @@ -19,29 +19,40 @@ ext { } repositories { + // Maven Defaults mavenCentral() mavenLocal() + + // SpigotMC Repos maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' - - // As of Gradle 5.1, you can limit this to only those - // dependencies you expect from it content { includeGroup 'org.bukkit' includeGroup 'org.spigotmc' } } + + // Sonatype Repos maven { url = 'https://oss.sonatype.org/content/repositories/snapshots' } maven { url = 'https://oss.sonatype.org/content/repositories/central' } maven { name = "sonatype" url = "https://oss.sonatype.org/content/groups/public/" } + + // CodeMC Streamline Essentials Repo + maven { url "https://repo.codemc.org/repository/streamline-essentials/" } + + // JitPack maven { url "https://jitpack.io" } + + // PlaceholderAPI Repo maven { name = "placeholderapi" url = "https://repo.extendedclip.com/content/repositories/placeholderapi/" } + + // Papermc Repositories maven { url 'https://repo.papermc.io/repository/maven-snapshots/' } maven { url 'https://repo.papermc.io/repository/maven-public/' } } @@ -95,4 +106,4 @@ artifacts { wrapper { gradleVersion = '8.9' distributionType = Wrapper.DistributionType.ALL -} \ No newline at end of file +} diff --git a/dependencies.gradle b/dependencies.gradle index cf0046b..e0210c2 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -4,30 +4,31 @@ ext { SHADOW = [ ] ANNO = [ - 'com.github.streamline-essentials:BukkitOfUtils:master-SNAPSHOT', + // Lombok is handled by the plugin, but keeping standard dependencies here if needed + 'gg.drak:BukkitOfUtils:1.16.0', + + // StreamlineCore Dependencies (API & BAPI) + 'gg.drak:StreamlineCore-API:2.5.5.6', + 'gg.drak:StreamlineCore-BAPI:2.5.5.6', + + // Folia API for Folia compatibility 'dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT', - 'net.kyori:adventure-api:4.16.0', - 'net.kyori:adventure-text-serializer-gson:4.16.0', - 'net.kyori:adventure-text-serializer-legacy:4.16.0', - 'net.kyori:adventure-text-serializer-plain:4.16.0', - 'net.kyori:adventure-text-minimessage:4.16.0', - 'net.kyori:adventure-text-serializer-ansi:4.16.0', - 'net.kyori:ansi:1.0.3', ] COMP_ONLY = [ - 'com.github.streamline-essentials:BukkitOfUtils:master-SNAPSHOT', + // Lombok is handled by the plugin, but keeping standard dependencies here if needed + 'gg.drak:BukkitOfUtils:1.16.0', + + // StreamlineCore Dependencies (API & BAPI) + 'gg.drak:StreamlineCore-API:2.5.5.6', + 'gg.drak:StreamlineCore-BAPI:2.5.5.6', + + // Folia API for Folia compatibility 'dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT', - 'net.kyori:adventure-api:4.16.0', - 'net.kyori:adventure-text-serializer-gson:4.16.0', - 'net.kyori:adventure-text-serializer-legacy:4.16.0', - 'net.kyori:adventure-text-serializer-plain:4.16.0', - 'net.kyori:adventure-text-minimessage:4.16.0', - 'net.kyori:adventure-text-serializer-ansi:4.16.0', - 'net.kyori:ansi:1.0.3', ] FILES = [ ] OTHER_PLUGINS = [ - 'me.clip:placeholderapi:2.11.5', + // PlaceholderAPI for Placeholder support + 'me.clip:placeholderapi:2.11.7', ] -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index 5dcd030..79820f0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ name = StoneDamager group = host.plas -version = 1.9.0 +version = 1.11.0 plugin.main = default \ No newline at end of file diff --git a/src/main/java/host/plas/stonedamager/StoneDamager.java b/src/main/java/host/plas/stonedamager/StoneDamager.java index 5764881..8e29968 100644 --- a/src/main/java/host/plas/stonedamager/StoneDamager.java +++ b/src/main/java/host/plas/stonedamager/StoneDamager.java @@ -5,7 +5,7 @@ import host.plas.stonedamager.utils.DamageHandler; import lombok.Getter; import lombok.Setter; -import host.plas.stonedamager.config.DamagerConfig; +import host.plas.stonedamager.config.MainConfig; import host.plas.stonedamager.runnables.TickTicker; @Getter @Setter @@ -13,7 +13,7 @@ public final class StoneDamager extends BetterPlugin { @Getter @Setter private static StoneDamager instance; @Getter @Setter - private static DamagerConfig damagerConfig; + private static MainConfig mainConfig; @Getter @Setter private static TickTicker tickTicker; @@ -27,7 +27,7 @@ public void onBaseEnabled() { // Plugin startup logic instance = this; - damagerConfig = new DamagerConfig(); + mainConfig = new MainConfig(); tickTicker = new TickTicker(); diff --git a/src/main/java/host/plas/stonedamager/commands/ReloadCMD.java b/src/main/java/host/plas/stonedamager/commands/ReloadCMD.java index ecd1938..3f1bc06 100644 --- a/src/main/java/host/plas/stonedamager/commands/ReloadCMD.java +++ b/src/main/java/host/plas/stonedamager/commands/ReloadCMD.java @@ -17,7 +17,7 @@ public boolean command(CommandContext ctx) { CompletableFuture.runAsync(() -> { ctx.sendMessage("&eReloading &cconfigurations&8..."); - StoneDamager.getDamagerConfig().onReload(); + StoneDamager.getMainConfig().onReload(); ctx.sendMessage("&eReloaded &cconfigurations&8!"); }); diff --git a/src/main/java/host/plas/stonedamager/config/DamagerConfig.java b/src/main/java/host/plas/stonedamager/config/MainConfig.java similarity index 88% rename from src/main/java/host/plas/stonedamager/config/DamagerConfig.java rename to src/main/java/host/plas/stonedamager/config/MainConfig.java index 8779763..4a40ef4 100644 --- a/src/main/java/host/plas/stonedamager/config/DamagerConfig.java +++ b/src/main/java/host/plas/stonedamager/config/MainConfig.java @@ -1,17 +1,17 @@ package host.plas.stonedamager.config; +import gg.drak.thebase.storage.resources.flat.simple.SimpleConfiguration; import host.plas.bou.configs.bits.ConfigurableWhitelist; import host.plas.stonedamager.StoneDamager; import host.plas.stonedamager.data.DamagableSelection; import host.plas.stonedamager.utils.DamageHandler; -import tv.quaint.storage.resources.flat.simple.SimpleConfiguration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentSkipListSet; -public class DamagerConfig extends SimpleConfiguration { - public DamagerConfig() { +public class MainConfig extends SimpleConfiguration { + public MainConfig() { super("config.yml", StoneDamager.getInstance(), true); } @@ -26,7 +26,8 @@ public void onReload() { StoneDamager.getInstance().logInfo("&fLoaded &a" + getSelections().size() + " &fdamager selections."); - isStoneCutterPatchEnabled(); + isTryFindPlayer(); + getFindPlayerRadius(); String configVersion = getConfigVersion(); if (! configVersion.equals("1.0")) { @@ -101,9 +102,15 @@ public String getConfigVersion() { return getResource().getOrDefault("config-version", "null"); } - public boolean isStoneCutterPatchEnabled() { + public boolean isTryFindPlayer() { reloadResource(); - return getOrSetDefault("stonecutter-patch", true); + return getOrSetDefault("settings.try-find-player.enabled", true); + } + + public double getFindPlayerRadius() { + reloadResource(); + + return getOrSetDefault("settings.try-find-player.radius", 30d); } } diff --git a/src/main/java/host/plas/stonedamager/data/DamagableSelection.java b/src/main/java/host/plas/stonedamager/data/DamagableSelection.java index 93e1359..06f2dc5 100644 --- a/src/main/java/host/plas/stonedamager/data/DamagableSelection.java +++ b/src/main/java/host/plas/stonedamager/data/DamagableSelection.java @@ -1,14 +1,13 @@ package host.plas.stonedamager.data; +import gg.drak.thebase.objects.Identifiable; import host.plas.bou.configs.bits.ConfigurableWhitelist; -import host.plas.stonedamager.patch.StoneCutterPatch; import lombok.Getter; import lombok.Setter; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; -import tv.quaint.objects.Identifiable; import java.util.concurrent.atomic.AtomicBoolean; @@ -45,10 +44,6 @@ public boolean checkMaterial(String material) { } public boolean checkMaterial(Block block) { - if (isMaterialsContainsSC()) { - if (StoneCutterPatch.isStoneCutter(block)) return ! materials.isBlacklist(); - } - return checkMaterial(block.getType().name()); } @@ -63,14 +58,14 @@ public boolean checkEntity(String entity) { public boolean checkPermissions(Entity player) { if (! (player instanceof Player)) return true; - if (includePermission.isBlank() || includePermission.isEmpty()) { - if (excludePermission.isBlank() || excludePermission.isEmpty()) { + if (isEmptyPermission(includePermission)) { + if (isEmptyPermission(excludePermission)) { return true; } else { return ! player.hasPermission(excludePermission); } } else { - if (excludePermission.isBlank() || excludePermission.isEmpty()) { + if (isEmptyPermission(excludePermission)) { return player.hasPermission(includePermission); } else { return player.hasPermission(includePermission) && ! player.hasPermission(excludePermission); @@ -90,4 +85,11 @@ public boolean check(Entity entity) { return checkAll(entity, block); } + + public static boolean isEmptyPermission(String permission) { + return permission == null || permission.isBlank() || + permission.equalsIgnoreCase("none") || permission.equalsIgnoreCase("null") || + permission.equalsIgnoreCase("disable") || permission.equalsIgnoreCase("disabled") || + permission.equalsIgnoreCase("off"); + } } diff --git a/src/main/java/host/plas/stonedamager/events/ScheduledDamageEvent.java b/src/main/java/host/plas/stonedamager/events/ScheduledDamageEvent.java index c2ac26e..e9c435d 100644 --- a/src/main/java/host/plas/stonedamager/events/ScheduledDamageEvent.java +++ b/src/main/java/host/plas/stonedamager/events/ScheduledDamageEvent.java @@ -1,14 +1,19 @@ package host.plas.stonedamager.events; +import host.plas.stonedamager.StoneDamager; import host.plas.stonedamager.data.DamagableSelection; +import host.plas.stonedamager.objects.DistanceComparator; import lombok.Getter; import lombok.Setter; +import org.bukkit.Location; import org.bukkit.entity.LivingEntity; -import tv.quaint.events.components.BaseEvent; +import org.bukkit.entity.Player; + +import java.util.Optional; @Setter @Getter -public class ScheduledDamageEvent extends BaseEvent { +public class ScheduledDamageEvent extends StoneDamagerEvent { private LivingEntity entity; private DamagableSelection damagableSelection; @@ -16,4 +21,41 @@ public ScheduledDamageEvent(LivingEntity entity, DamagableSelection damagableSel this.entity = entity; this.damagableSelection = damagableSelection; } + + public Optional getClosestPlayer() { + boolean tryFind = StoneDamager.getMainConfig().isTryFindPlayer(); + if (! tryFind) return Optional.empty(); + + double radius = StoneDamager.getMainConfig().getFindPlayerRadius(); + return entity.getNearbyEntities(radius, radius, radius).stream() + .filter(e -> e instanceof Player) + .map(e -> (Player) e) + .min(new DistanceComparator(getEntityLocation())); + } + + public Location getEntityLocation() { + return entity.getLocation(); + } + + /** + * Damages the entity by the specified amount. + * + * Call this method only in synchronous context. + * @param damage the amount of damage to deal + */ + public void damageEntity(double damage) { + getClosestPlayer().ifPresentOrElse( + p -> getEntity().damage(damage, p), + () -> getEntity().damage(damage) + ); + } + + /** + * Damages the entity by the amount specified in the DamagableSelection. + * + * Call this method only in synchronous context. + */ + public void damageEntity() { + damageEntity(getDamagableSelection().getDamageAmount()); + } } diff --git a/src/main/java/host/plas/stonedamager/events/StoneDamagerEvent.java b/src/main/java/host/plas/stonedamager/events/StoneDamagerEvent.java new file mode 100644 index 0000000..08df968 --- /dev/null +++ b/src/main/java/host/plas/stonedamager/events/StoneDamagerEvent.java @@ -0,0 +1,14 @@ +package host.plas.stonedamager.events; + +import gg.drak.thebase.events.components.BaseEvent; +import host.plas.stonedamager.StoneDamager; + +public class StoneDamagerEvent extends BaseEvent { + public StoneDamagerEvent() { + super(); + } + + public StoneDamager getPlugin() { + return StoneDamager.getInstance(); + } +} diff --git a/src/main/java/host/plas/stonedamager/objects/DistanceComparator.java b/src/main/java/host/plas/stonedamager/objects/DistanceComparator.java new file mode 100644 index 0000000..91f8e7b --- /dev/null +++ b/src/main/java/host/plas/stonedamager/objects/DistanceComparator.java @@ -0,0 +1,28 @@ +package host.plas.stonedamager.objects; + +import lombok.Getter; +import lombok.Setter; +import org.bukkit.Location; +import org.bukkit.entity.Entity; + +import java.util.Comparator; + +@Getter @Setter +public class DistanceComparator implements Comparator { + private final Location referenceLocation; + + public DistanceComparator(Location referenceLocation) { + this.referenceLocation = referenceLocation; + } + + @Override + public int compare(Entity o1, Entity o2) { + Location l1 = o1.getLocation(); + Location l2 = o2.getLocation(); + + double d1 = l1.distanceSquared(referenceLocation); + double d2 = l2.distanceSquared(referenceLocation); + + return Double.compare(d1, d2); + } +} diff --git a/src/main/java/host/plas/stonedamager/patch/StoneCutterPatch.java b/src/main/java/host/plas/stonedamager/patch/StoneCutterPatch.java deleted file mode 100644 index 45e0808..0000000 --- a/src/main/java/host/plas/stonedamager/patch/StoneCutterPatch.java +++ /dev/null @@ -1,21 +0,0 @@ -package host.plas.stonedamager.patch; - -import host.plas.stonedamager.StoneDamager; -import org.bukkit.Material; -import org.bukkit.block.Block; - -public class StoneCutterPatch { - public static boolean isStoneCutter(Block block) { - if (block.getType() == Material.STONECUTTER) return true; - - if (! StoneDamager.getDamagerConfig().isStoneCutterPatchEnabled()) return false; - - if (block.getType() != Material.AIR) return false; - - block.setType(Material.STONECUTTER); - boolean isStoneCutter = block.getType() == Material.AIR; - block.setType(Material.AIR); - - return isStoneCutter; - } -} diff --git a/src/main/java/host/plas/stonedamager/utils/DamageHandler.java b/src/main/java/host/plas/stonedamager/utils/DamageHandler.java index 9d0f0c3..e6c522c 100644 --- a/src/main/java/host/plas/stonedamager/utils/DamageHandler.java +++ b/src/main/java/host/plas/stonedamager/utils/DamageHandler.java @@ -10,6 +10,7 @@ import lombok.Setter; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; import java.util.Optional; import java.util.concurrent.ConcurrentSkipListMap; @@ -40,24 +41,24 @@ public static void clearTickables() { } public static Optional getTickable(String identifier) { - return tickMap.keySet().stream().filter(damagableSelection -> damagableSelection.getIdentifier().equals(identifier)).findFirst(); + return tickMap.keySet().stream().filter(d -> d.getIdentifier().equals(identifier)).findFirst(); } public static long getTicksLeft(String identifier) { - return getTickable(identifier).map(damagableSelection -> tickMap.get(damagableSelection)).orElse(1L); + return getTickable(identifier).map(d -> tickMap.get(d)).orElse(1L); } public static void tickTicksLeft(String identifier) { - getTickable(identifier).ifPresent(damagableSelection -> { - long ticks = tickMap.get(damagableSelection); + getTickable(identifier).ifPresent(d -> { + long ticks = tickMap.get(d); ticks -= 1; - tickMap.put(damagableSelection, ticks); + tickMap.put(d, ticks); }); } public static void resetTicksLeft(String identifier) { - getTickable(identifier).ifPresent(damagableSelection -> { - tickMap.put(damagableSelection, damagableSelection.getTicksPerDamage()); + getTickable(identifier).ifPresent(d -> { + tickMap.put(d, d.getTicksPerDamage()); }); } @@ -70,13 +71,9 @@ public static void fire(LivingEntity entity, DamagableSelection damagableSelecti fireInSync(event); } else { if (ClassHelper.isFolia()) { - TaskManager.getScheduler().runTask(entity, () -> { - fireInSync(event); - }); + TaskManager.getScheduler().runTask(entity, () -> fireInSync(event)); } else { - TaskManager.getScheduler().runTask(() -> { - fireInSync(event); - }); + TaskManager.getScheduler().runTask(() -> fireInSync(event)); } } } catch (Throwable e) { @@ -84,11 +81,27 @@ public static void fire(LivingEntity entity, DamagableSelection damagableSelecti } } + // New helper: find nearest player within range + private static Player getNearestPlayer(LivingEntity entity, double range) { + Player nearest = null; + double nearestDist = Double.MAX_VALUE; + + for (Player p : entity.getWorld().getPlayers()) { + if (!p.isOnline() || p.isDead()) continue; + + double dist = p.getLocation().distance(entity.getLocation()); + if (dist <= range && dist < nearestDist) { + nearestDist = dist; + nearest = p; + } + } + + return nearest; + } + public static void fireInSync(ScheduledDamageEvent event) { try { - double damage = event.getDamagableSelection().getDamageAmount(); - - event.getEntity().damage(damage); + event.damageEntity(); } catch (Throwable e) { StoneDamager.getInstance().logWarningWithInfo("Error while firing damage event in sync.", e); } @@ -97,7 +110,7 @@ public static void fireInSync(ScheduledDamageEvent event) { public static void tick() { try { getTickMap().forEach((damagableSelection, ticks) -> { - if (! damagableSelection.isEnabled()) return; + if (!damagableSelection.isEnabled()) return; if (ticks > 0) { tickTicksLeft(damagableSelection.getIdentifier()); @@ -125,9 +138,9 @@ public static void tick() { public static Runnable getDamageTask(DamagableSelection damagableSelection, Entity entity) { return () -> { - if (! damagableSelection.isEnabled()) return; + if (!damagableSelection.isEnabled()) return; - if (! (entity instanceof LivingEntity)) return; + if (!(entity instanceof LivingEntity)) return; LivingEntity livingEntity = (LivingEntity) entity; if (damagableSelection.check(livingEntity)) { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b2245f4..74a7ee4 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -33,7 +33,8 @@ damagers: # Find a full list at: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/entity/EntityType.html list: [] # Empty list. # If the list above is a blacklist. - # If this is false, then it is a whitelist. + # true = do not damage these entities. + # false = only damage these entities. is-blacklist: true # Worlds config. worlds: @@ -41,7 +42,8 @@ damagers: list: - "lobby" # If the list above is a blacklist. - # If this is false, then it is a whitelist. + # true = do not damage in these worlds. + # false = only damage in these worlds. is-blacklist: true # Materials that count as a damager for this selection. materials: @@ -49,7 +51,8 @@ damagers: list: - "STONECUTTER" # If the list above is a blacklist. - # If this is false, then it is a whitelist. + # true = do not count these materials. + # false = only count these materials. is-blacklist: false glass: @@ -75,9 +78,18 @@ damagers: - "GLASS" is-blacklist: false -# For some reason, stonecutters are marked as air... -# This is a patch to fix that. -stonecutter-patch: true +# Settings for the damager system. +settings: + # Try to find a player to count as the source of damage. + # This is to allow for experience and loot drops. + try-find-player: + # If this feature is enabled. + # true = Yes. Use this feature. + # false = No. Do not use this feature. + enabled: true + # Maximum distance to search for a player (in blocks). + # Can be a decimal value. + radius: 30.0 # DO NOT TOUCH THIS. config-version: "1.0" \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c5af21a..6149372 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -8,7 +8,7 @@ description: A plugin to simply make it so that stone cutters damage entities. website: https://site.plasmere.net folia-supported: true -api-version: 1.21 +api-version: 1.14 commands: reloadstonedamager: