Initial commit: Folia compatibility and cleanup

This commit is contained in:
Professor Doom 2026-05-19 16:04:33 -05:00
commit ec4329fcc1
8 changed files with 271 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
target/
plans/
*.class
*.jar
.vscode/
.idea/
*.iml
.DS_Store

7
README.md Normal file
View file

@ -0,0 +1,7 @@
# EzBoner
this is really fucking hard to explain,
when you use bonemeal, it affects all nearby crops (up to 32) using the proper amount of bonemeal, super duper fast and easy
## Features
- Supports foila!
- **Connected Growth**: Look upwards

68
pom.xml Normal file
View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://www.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>boner</groupId>
<artifactId>EzBoner</artifactId>
<version>1.0</version>
<name>EzBoner</name>
<url>https://coldfiles.dev/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>dev.folia</groupId>
<artifactId>folia-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>plugin.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<copy file="${project.build.directory}/${project.build.finalName}.jar"
todir="/home/bunchy7s/programs/mcplugins/"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,26 @@
package boner;
import org.bukkit.plugin.java.JavaPlugin;
import boner.managers.PluginManager;
import boner.listeners.PlayerListener;
public class EzBoner extends JavaPlugin {
@Override
public void onEnable() {
// Initialize managers
PluginManager.getInstance().initialize();
// Register listeners
getServer().getPluginManager().registerEvents(new PlayerListener(), this);
getLogger().info("EzBoner has been enabled!");
}
@Override
public void onDisable() {
getLogger().info("EzBoner has been disabled!");
}
}

View file

@ -0,0 +1,74 @@
package boner.listeners;
import boner.EzBoner;
import boner.utils.Utils;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.block.data.Ageable;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
public class PlayerListener implements Listener {
private final EzBoner plugin = JavaPlugin.getPlugin(EzBoner.class);
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
Player player = event.getPlayer();
if (!player.isSneaking()) return;
ItemStack item = event.getItem();
if (item == null || item.getType() != Material.BONE_MEAL) return;
Block clickedBlock = event.getClickedBlock();
if (clickedBlock == null || !Utils.isBonemealable(clickedBlock)) return;
// Cancel the original event to handle it ourselves
event.setCancelled(true);
// Find connected crops (limit 32)
List<Block> targets = Utils.findConnectedCrops(clickedBlock, 32);
if (targets.isEmpty()) return;
// Folia-safe execution on the region of the clicked block
org.bukkit.Bukkit.getRegionScheduler().execute(plugin, clickedBlock.getLocation(), () -> {
int appliedCount = 0;
for (Block target : targets) {
if (Utils.isBonemealable(target)) {
// Apply bonemeal effect
if (target.getBlockData() instanceof Ageable ageable) {
ageable.setAge(ageable.getMaximumAge());
target.setBlockData(ageable);
} else {
// Fallback for saplings or other non-ageable but bonemealable blocks
target.applyBoneMeal(org.bukkit.block.BlockFace.UP);
}
appliedCount++;
}
}
if (appliedCount > 0) {
// Consume items from player
final int finalAppliedCount = appliedCount;
player.getScheduler().execute(plugin, () -> {
ItemStack handItem = player.getInventory().getItem(event.getHand());
if (handItem != null && handItem.getType() == Material.BONE_MEAL) {
handItem.setAmount(Math.max(0, handItem.getAmount() - finalAppliedCount));
}
}, null, 0);
}
});
}
}

View file

@ -0,0 +1,16 @@
package boner.managers;
public class PluginManager {
private static PluginManager instance;
public static PluginManager getInstance() {
if (instance == null) {
instance = new PluginManager();
}
return instance;
}
public void initialize() {
// Initialize your managers here
}
}

View file

@ -0,0 +1,66 @@
package boner.utils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.Ageable;
import org.bukkit.block.data.BlockData;
import java.util.*;
public class Utils {
public static Component colorize(String msg) {
if (msg.contains("<") && msg.contains(">")) {
return MiniMessage.miniMessage().deserialize(msg);
}
return LegacyComponentSerializer.legacyAmpersand().deserialize(msg);
}
public static String legacyColorize(String msg) {
return LegacyComponentSerializer.legacyAmpersand().serialize(
LegacyComponentSerializer.legacyAmpersand().deserialize(msg)
);
}
public static boolean isBonemealable(Block block) {
BlockData data = block.getBlockData();
if (data instanceof Ageable ageable) {
return ageable.getAge() < ageable.getMaximumAge();
}
// Support for saplings which might not be Ageable in some versions but are bonemealable
return block.getType().name().contains("SAPLING");
}
public static List<Block> findConnectedCrops(Block start, int limit) {
List<Block> found = new ArrayList<>();
Queue<Block> queue = new LinkedList<>();
Set<Block> visited = new HashSet<>();
if (isBonemealable(start)) {
queue.add(start);
visited.add(start);
}
while (!queue.isEmpty() && found.size() < limit) {
Block current = queue.poll();
found.add(current);
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
if (x == 0 && z == 0) continue;
Block neighbor = current.getRelative(x, 0, z);
if (!visited.contains(neighbor) && isBonemealable(neighbor)) {
visited.add(neighbor);
queue.add(neighbor);
}
}
}
}
return found;
}
}

View file

@ -0,0 +1,6 @@
main: boner.EzBoner
version: 1.0
name: EzBoner
author: bunchy7s
api-version: 1.21
folia-supported: true