From 7734a9b407582b79f7aeed31ae159237a9236386 Mon Sep 17 00:00:00 2001 From: Koopa <115321970+KoopaCode@users.noreply.github.com> Date: Sat, 1 Feb 2025 12:47:48 -0500 Subject: [PATCH] 0.0.1-ALPHA --- .github/workflows/build.yml | 90 ++++++++++ .gitignore | 28 ++- pom.xml | 74 ++++++++ .../koopalabs/ultrastack/StackListener.java | 164 ++++++++++++++++++ .../com/koopalabs/ultrastack/UltraStack.java | 61 +++++++ .../ultrastack/UltraStackCommand.java | 34 ++++ src/main/resources/config.yml | 57 ++++++ src/main/resources/plugin.yml | 18 ++ 8 files changed, 523 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 pom.xml create mode 100644 src/main/java/com/koopalabs/ultrastack/StackListener.java create mode 100644 src/main/java/com/koopalabs/ultrastack/UltraStack.java create mode 100644 src/main/java/com/koopalabs/ultrastack/UltraStackCommand.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..1e15122 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,90 @@ +name: Build +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + release: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + + - name: Get Project Info + run: | + echo "PLUGIN_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV + echo "PLUGIN_NAME=$(mvn help:evaluate -Dexpression=project.name -q -DforceStdout)" >> $GITHUB_ENV + echo "PLUGIN_FILE=$(mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout)" >> $GITHUB_ENV + + - name: Build with Maven + run: mvn -B package --file pom.xml + + - name: Debug Directory + run: ls -la target/ + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PLUGIN_FILE }}-${{ env.PLUGIN_VERSION }} + path: target/${{ env.PLUGIN_FILE }}-${{ env.PLUGIN_VERSION }}.jar + retention-days: 30 + + - name: Send Build Notification + if: always() + uses: sarisia/actions-status-discord@v1 + with: + webhook: ${{ secrets.DISCORD_WEBHOOK }} + title: "${{ job.status == 'success' && '✅ Build Success!' || '❌ Build Failed!' }}" + description: | + **${{ env.PLUGIN_NAME }} v${{ env.PLUGIN_VERSION }}** + By ${{ github.actor }} • ${{ github.sha }} + ${{ github.repository }} + color: ${{ job.status == 'success' && '0x00ff00' || '0xff0000' }} + username: "🏺Artifact Build's" + avatar_url: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" + + release: + needs: build + if: github.event_name == 'release' + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Download Artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.PLUGIN_FILE }}-${{ env.PLUGIN_VERSION }} + + - name: Upload Release + uses: softprops/action-gh-release@v2 + with: + files: ${{ env.PLUGIN_FILE }}-${{ env.PLUGIN_VERSION }}.jar + fail_on_unmatched_files: true + + - name: Send Release Notification + if: always() + uses: sarisia/actions-status-discord@v1 + with: + webhook: ${{ secrets.DISCORD_WEBHOOK }} + title: "${{ job.status == 'success' && '🎉 Release Published!' || '❌ Release Failed!' }}" + description: | + **${{ env.PLUGIN_NAME }} v${{ env.PLUGIN_VERSION }}** + ${{ job.status == 'success' && '➜ https://github.com/${{ github.repository }}/releases/latest' || '' }} + color: ${{ job.status == 'success' && '0x00ff00' || '0xff0000' }} + username: "🏺Artifact Build's" + avatar_url: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2f43530..a2cddc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +# Maven target/ pom.xml.tag pom.xml.releaseBackup @@ -10,8 +11,29 @@ buildNumber.properties # https://github.com/takari/maven-wrapper#usage-without-binary-jar .mvn/wrapper/maven-wrapper.jar -# Eclipse m2e generated files -# Eclipse Core +# IDE files +.idea/ +*.iml +.settings/ .project -# JDT-specific (Eclipse Java Development Tools) .classpath +.vscode/ + +# Compiled files +*.class +*.jar +*.war +*.ear + +# Logs +*.log +logs/ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..9668f77 --- /dev/null +++ b/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + com.koopalabs + ultrastack + 0.0.1-ALPHA + jar + + UltraStack + Configurable item stacking plugin with multi-world support + discord.gg/KmHGjaHWct + + + 1.8 + UTF-8 + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + + org.spigotmc + spigot-api + 1.21.4-R0.1-SNAPSHOT + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + false + + + + + + + + src/main/resources + true + + + + \ No newline at end of file diff --git a/src/main/java/com/koopalabs/ultrastack/StackListener.java b/src/main/java/com/koopalabs/ultrastack/StackListener.java new file mode 100644 index 0000000..b90307f --- /dev/null +++ b/src/main/java/com/koopalabs/ultrastack/StackListener.java @@ -0,0 +1,164 @@ +package com.koopalabs.ultrastack; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.ItemSpawnEvent; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.entity.Player; +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.entity.Item; +import org.bukkit.entity.Entity; +import java.util.List; + +public class StackListener implements Listener { + private final UltraStack plugin; + + public StackListener(UltraStack plugin) { + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItemSpawn(ItemSpawnEvent event) { + if (!plugin.getConfig().getBoolean("performance.update-on-spawn")) return; + if (!plugin.isWorldEnabled(event.getLocation().getWorld())) return; + + Item newItem = event.getEntity(); + ItemStack stack = newItem.getItemStack(); + if (stack == null || stack.getType() == Material.AIR) return; + + int maxStack = plugin.getMaxStackSize(stack.getType()); + + // Get all nearby items first + List nearbyItems = new java.util.ArrayList<>(); + for (Entity entity : newItem.getNearbyEntities(2, 2, 2)) { + if (entity instanceof Item && entity != newItem) { + Item item = (Item) entity; + if (item.getItemStack().isSimilar(stack)) { + nearbyItems.add(item); + } + } + } + + // If we found similar items, merge them all + if (!nearbyItems.isEmpty()) { + int totalAmount = stack.getAmount(); + for (Item item : nearbyItems) { + totalAmount += item.getItemStack().getAmount(); + item.remove(); // Remove the old items + } + + // Create a new stack with the total amount + if (totalAmount > maxStack) { + // If total exceeds max, create minimum number of full stacks + while (totalAmount > maxStack) { + ItemStack newStack = stack.clone(); + newStack.setAmount(maxStack); + newItem.getWorld().dropItem(newItem.getLocation(), newStack); + totalAmount -= maxStack; + } + + // Drop remaining items if any + if (totalAmount > 0) { + stack.setAmount(totalAmount); + newItem.setItemStack(stack); + } else { + event.setCancelled(true); + } + } else { + // If total is within max, just set the amount + stack.setAmount(totalAmount); + newItem.setItemStack(stack); + } + } else { + // No nearby items, just ensure proper stack size + if (stack.getAmount() > maxStack) { + int amount = stack.getAmount(); + stack.setAmount(maxStack); + + // Create additional stacks if needed + amount -= maxStack; + while (amount > 0) { + int nextAmount = Math.min(amount, maxStack); + ItemStack nextStack = stack.clone(); + nextStack.setAmount(nextAmount); + newItem.getWorld().dropItem(newItem.getLocation(), nextStack); + amount -= nextAmount; + } + } + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onInventoryClick(InventoryClickEvent event) { + if (!plugin.getConfig().getBoolean("performance.update-on-inventory-click")) return; + if (!plugin.isWorldEnabled(event.getWhoClicked().getWorld())) return; + if (!(event.getWhoClicked() instanceof Player)) return; + + ItemStack current = event.getCurrentItem(); + ItemStack cursor = event.getCursor(); + + // Only handle clicks with items + if (current == null || current.getType() == Material.AIR) return; + + int maxStack = plugin.getMaxStackSize(current.getType()); + if (maxStack <= current.getType().getMaxStackSize()) return; + + // Handle merging items + if (cursor != null && cursor.getType() != Material.AIR && + cursor.isSimilar(current) && + event.getAction() == InventoryAction.PLACE_ALL) { + + int totalAmount = cursor.getAmount() + current.getAmount(); + if (totalAmount <= maxStack) { + event.setCancelled(true); + current.setAmount(totalAmount); + cursor.setAmount(0); + } else if (current.getAmount() < maxStack) { + event.setCancelled(true); + current.setAmount(maxStack); + cursor.setAmount(totalAmount - maxStack); + } + } + + // Ensure stack size doesn't exceed our max + if (current.getAmount() > maxStack) { + current.setAmount(maxStack); + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onEntityPickupItem(EntityPickupItemEvent event) { + if (!plugin.getConfig().getBoolean("performance.update-on-pickup")) return; + if (!plugin.isWorldEnabled(event.getEntity().getWorld())) return; + if (!(event.getEntity() instanceof Player)) return; + + Player player = (Player) event.getEntity(); + ItemStack pickupItem = event.getItem().getItemStack(); + if (pickupItem == null || pickupItem.getType() == Material.AIR) return; + + int maxStack = plugin.getMaxStackSize(pickupItem.getType()); + if (maxStack <= pickupItem.getType().getMaxStackSize()) return; + + // Try to merge with existing stacks first + PlayerInventory inv = player.getInventory(); + for (ItemStack invItem : inv.getStorageContents()) { + if (invItem != null && invItem.isSimilar(pickupItem) && invItem.getAmount() < maxStack) { + int space = maxStack - invItem.getAmount(); + if (pickupItem.getAmount() <= space) { + invItem.setAmount(invItem.getAmount() + pickupItem.getAmount()); + event.getItem().remove(); + event.setCancelled(true); + return; + } + } + } + + // If we can't merge, just ensure the stack size is valid + pickupItem.setAmount(Math.min(maxStack, pickupItem.getAmount())); + } +} \ No newline at end of file diff --git a/src/main/java/com/koopalabs/ultrastack/UltraStack.java b/src/main/java/com/koopalabs/ultrastack/UltraStack.java new file mode 100644 index 0000000..24aceea --- /dev/null +++ b/src/main/java/com/koopalabs/ultrastack/UltraStack.java @@ -0,0 +1,61 @@ +package com.koopalabs.ultrastack; + +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; + +public class UltraStack extends JavaPlugin implements Listener { + private static UltraStack instance; + private FileConfiguration config; + + @Override + public void onEnable() { + instance = this; + saveDefaultConfig(); + loadConfig(); + + // Register events + getServer().getPluginManager().registerEvents(new StackListener(this), this); + + // Register commands + getCommand("ultrastack").setExecutor(new UltraStackCommand(this)); + } + + public void loadConfig() { + reloadConfig(); + config = getConfig(); + } + + public boolean isWorldEnabled(World world) { + if (!config.getBoolean("enabled")) return false; + + if (config.getStringList("worlds.disabled-worlds").contains(world.getName())) { + return false; + } + + java.util.List enabledWorlds = config.getStringList("worlds.enabled-worlds"); + return enabledWorlds.contains("all") || enabledWorlds.contains(world.getName()); + } + + public int getMaxStackSize(Material material) { + String mode = config.getString("stack-settings.mode", "all"); + + if (mode.equalsIgnoreCase("all")) { + return config.getInt("stack-settings.default-stack-size", 64000); + } + + String path = "stack-settings.items." + material.name(); + if (config.contains(path) && config.getBoolean(path + ".enabled")) { + return config.getInt(path + ".max-stack", 64); + } + + return material.getMaxStackSize(); + } + + public static UltraStack getInstance() { + return instance; + } +} \ No newline at end of file diff --git a/src/main/java/com/koopalabs/ultrastack/UltraStackCommand.java b/src/main/java/com/koopalabs/ultrastack/UltraStackCommand.java new file mode 100644 index 0000000..ece82f6 --- /dev/null +++ b/src/main/java/com/koopalabs/ultrastack/UltraStackCommand.java @@ -0,0 +1,34 @@ +package com.koopalabs.ultrastack; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.ChatColor; + +public class UltraStackCommand implements CommandExecutor { + private final UltraStack plugin; + + public UltraStackCommand(UltraStack plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("ultrastack.admin")) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + plugin.getConfig().getString("messages.prefix") + " " + + plugin.getConfig().getString("messages.no-permission"))); + return true; + } + + if (args.length > 0 && args[0].equalsIgnoreCase("reload")) { + plugin.loadConfig(); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + plugin.getConfig().getString("messages.prefix") + " " + + plugin.getConfig().getString("messages.reload"))); + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..2821998 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,57 @@ +# UltraStack Configuration +# Created by KoopaLabs +# Discord: discord.gg/KmHGjaHWct + +# Enable or disable the plugin globally +enabled: true + +# World Settings +worlds: + # Set to 'all' to enable in all worlds + # Or list specific worlds where the plugin should work + enabled-worlds: + - 'all' + # Or specify individual worlds: + # enabled-worlds: + # - 'world' + # - 'world_nether' + + # List worlds where the plugin should be disabled + disabled-worlds: + - 'example_world' + +# Stack Settings +stack-settings: + # Mode can be 'all' or 'specific' + # 'all' - affects all stackable items + # 'specific' - only affects items in the items list + mode: 'all' + + # Default stack size when mode is 'all' + # Set to -1 for infinite stacking + default-stack-size: 64000 + + # Specific item settings (used when mode is 'specific') + items: + DIAMOND: + enabled: true + max-stack: 512 + STONE: + enabled: true + max-stack: 999 + # Add more items as needed + +# Performance Settings +performance: + # Update stack size on item spawn + update-on-spawn: true + # Update stack size on item pickup + update-on-pickup: true + # Update stack size on inventory click + update-on-inventory-click: true + +# Messages +messages: + prefix: '&8[&bUltraStack&8]' + reload: '&aConfiguration reloaded successfully!' + no-permission: '&cYou don''t have permission to use this command!' \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..0a371b3 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,18 @@ +name: UltraStack +version: 0.0.1-ALPHA +main: com.koopalabs.ultrastack.UltraStack +api-version: '1.21' +description: Configurable item stacking plugin with multi-world support +author: KoopaLabs +website: discord.gg/KmHGjaHWct + +commands: + ultrastack: + description: Main command for UltraStack plugin + usage: / reload + permission: ultrastack.admin + +permissions: + ultrastack.admin: + description: Allows access to UltraStack admin commands + default: op \ No newline at end of file