From a642ec43e797f332234c00912639448ff16f6dc2 Mon Sep 17 00:00:00 2001 From: Big Pappa Date: Thu, 13 Aug 2026 02:14:48 -0500 Subject: [PATCH] Initial RS-ItemMagnet repository --- .gitignore | 36 + LICENSE | 21 + README.md | 226 ++ docs/commands.md | 21 + docs/configuration.md | 91 + docs/index.md | 42 + docs/installation.md | 24 + docs/permissions.md | 21 + docs/troubleshooting.md | 45 + docs/usage.md | 68 + pom.xml | 52 + .../rsscripting/itemmagnet/RSItemMagnet.java | 228 ++ .../itemmagnet/commands/RSPluginCommand.java | 102 + .../itemmagnet/commands/RSTabCompleter.java | 46 + .../itemmagnet/config/ConfigManager.java | 247 +++ .../itemmagnet/database/DatabaseManager.java | 169 ++ .../itemmagnet/enums/MachineStatus.java | 15 + .../itemmagnet/gui/RSAdminMenu.java | 140 ++ .../itemmagnet/gui/RSConversionMenu.java | 66 + .../itemmagnet/gui/RSFilterMenu.java | 227 ++ .../itemmagnet/gui/RSMainMenu.java | 285 +++ .../itemmagnet/gui/RSMenuHolder.java | 57 + .../itemmagnet/gui/RSRadiusMenu.java | 183 ++ .../itemmagnet/listeners/RSAdminListener.java | 128 ++ .../listeners/RSBlockBreakListener.java | 28 + .../listeners/RSConvertListener.java | 470 +++++ .../listeners/RSFilterListener.java | 297 +++ .../itemmagnet/listeners/RSMenuListener.java | 388 ++++ .../listeners/RSRadiusListener.java | 158 ++ .../RSUpdateNotificationListener.java | 42 + .../itemmagnet/managers/MachineManager.java | 1825 +++++++++++++++++ .../managers/PendingConversion.java | 36 + .../managers/PendingTargetAssignment.java | 34 + .../managers/RSConversionManager.java | 157 ++ .../managers/RSRadiusPreviewManager.java | 66 + .../itemmagnet/tasks/RSErrorTask.java | 62 + .../itemmagnet/tasks/RSItemMagnetTask.java | 255 +++ .../itemmagnet/tasks/RSRadiusPreviewTask.java | 235 +++ .../itemmagnet/utils/GitHubUpdateChecker.java | 273 +++ .../itemmagnet/utils/RSConstants.java | 83 + .../rsscripting/itemmagnet/utils/RSKeys.java | 20 + .../itemmagnet/utils/RSMenuUtils.java | 122 ++ .../itemmagnet/utils/RSMessageUtils.java | 171 ++ .../itemmagnet/utils/VersionUtils.java | 20 + src/main/resources/Notes | 9 + src/main/resources/config.yml | 32 + src/main/resources/plugin.yml | 21 + 47 files changed, 7344 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 docs/commands.md create mode 100644 docs/configuration.md create mode 100644 docs/index.md create mode 100644 docs/installation.md create mode 100644 docs/permissions.md create mode 100644 docs/troubleshooting.md create mode 100644 docs/usage.md create mode 100644 pom.xml create mode 100644 src/main/java/com/rsscripting/itemmagnet/RSItemMagnet.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/commands/RSPluginCommand.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/commands/RSTabCompleter.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/config/ConfigManager.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/database/DatabaseManager.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/enums/MachineStatus.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/gui/RSAdminMenu.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/gui/RSConversionMenu.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/gui/RSFilterMenu.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/gui/RSMainMenu.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/gui/RSMenuHolder.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/gui/RSRadiusMenu.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/listeners/RSAdminListener.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/listeners/RSBlockBreakListener.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/listeners/RSConvertListener.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/listeners/RSFilterListener.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/listeners/RSMenuListener.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/listeners/RSRadiusListener.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/listeners/RSUpdateNotificationListener.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/managers/MachineManager.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/managers/PendingConversion.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/managers/PendingTargetAssignment.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/managers/RSConversionManager.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/managers/RSRadiusPreviewManager.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/tasks/RSErrorTask.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/tasks/RSItemMagnetTask.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/tasks/RSRadiusPreviewTask.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/utils/GitHubUpdateChecker.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/utils/RSConstants.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/utils/RSKeys.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/utils/RSMenuUtils.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/utils/RSMessageUtils.java create mode 100644 src/main/java/com/rsscripting/itemmagnet/utils/VersionUtils.java create mode 100644 src/main/resources/Notes create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c7ef23f --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +.kotlin + +### IntelliJ IDEA ### +.idea/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..730017e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 RS-Scripting + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..90bcfbd --- /dev/null +++ b/README.md @@ -0,0 +1,226 @@ +

+

RS-ItemMagnet

+

+ Automated Item Collection for Modern Minecraft Servers +

+ +

+ +![Minecraft](https://img.shields.io/badge/Minecraft-1.21%2B-green) +![Platform](https://img.shields.io/badge/Platform-Paper-orange) +![Storage](https://img.shields.io/badge/Storage-SQLite-blue) +![Status](https://img.shields.io/badge/Status-Stable-success) +![Development](https://img.shields.io/badge/Development-Active-brightgreen) +![RS Ecosystem](https://img.shields.io/badge/RS-Ecosystem-purple) + +

+ +--- + +## Overview + +**RS-ItemMagnet** is an automated item collection plugin designed for Minecraft servers that want reliable resource logistics without the complexity of traditional hopper-heavy collection systems. + +Players place and configure ItemMagnets to automatically attract nearby dropped items and route them into a designated container while server owners maintain full control through configurable settings, filters, radius controls, permissions, and administrative tools. + +Built from the ground up with performance, scalability, and long-term server operation in mind. + +--- + +## Why RS-ItemMagnet? + +Traditional collection systems often create: + +* Excessive hopper usage +* Server lag +* Large item build-ups +* Complicated item transport systems +* Difficult maintenance and troubleshooting + +RS-ItemMagnet replaces those systems with a clean, configurable, database-backed solution that is easy for players to use and easy for administrators to manage. + +--- + +## Documentation + +- [Documentation](docs/index.md) +- [Installation](docs/installation.md) +- [Usage](docs/usage.md) +- [Configuration](docs/configuration.md) +- [Commands](docs/commands.md) +- [Permissions](docs/permissions.md) +- [Troubleshooting](docs/troubleshooting.md) + +# Features + +### Automated Item Collection + +Automatically attract nearby dropped items. + +### Dynamic GUI Menus + +Simple and intuitive inventory-based interfaces. + +### Advanced Item Filtering + +Control exactly which items are accepted by each ItemMagnet. + +### Radius Controls + +Configure collection range to fit your automation needs. + +### Target Containers + +Assign and reassign collection containers at any time. + +### Capacity-Aware Collection + +Avoid unnecessary item acquisition when storage becomes unavailable. + +### SQLite Storage + +Reliable persistent data storage. + +### Chunk-Safe Design + +Built to behave correctly across chunk loads and unloads. + +### Performance Focused + +Designed to minimize server impact while maximizing functionality. + +### Administrative Tools + +Powerful management tools for staff and server operators. + +--- + +# RS Ecosystem + +RS-ItemMagnet is the collection and routing component of the growing **RS Ecosystem**. + +```text +┌─────────────┐ +│ RS-MobStand │ +└──────┬──────┘ + │ Generate + ▼ +┌──────────────┐ +│ RS-ItemMagnet│ +└──────┬───────┘ + │ Collect + ▼ +┌───────────┐ +│ RS-Auger │ +└─────┬─────┘ + │ Transport + ▼ +┌─────────────┐ +│ RS-Warehouse│ +└─────────────┘ + Store +``` + +Together these plugins create a complete automated resource collection and logistics system. + +--- + +# Screenshots + +### Main Menu + +27b8e481-1df0-465c-8fa1-2530636fa19f +> + +### Filter Menu + +cde741b8-51ce-4915-9919-1442c2fb1020 + +### Radius Configuration + +1f0d7b5f-5155-4af4-88f4-cd20e4ad9ca6 + +--- + +# Permissions + +| Permission | Description | +| --------------------- | ------------------------- | +| `rs.itemmagnet.use` | Allows use of ItemMagnets | +| `rs.itemmagnet.admin` | Administrative access | + +--- + +# Installation + +1. Download the latest release. +2. Place the jar into your server's `plugins` folder. +3. Restart the server. +4. Configure settings as desired. +5. Enjoy automated item collection. + +--- + +# Performance + +RS-ItemMagnet was designed with long-term server operation in mind. + +Features include: + +* SQLite persistence +* Chunk-safe processing +* Efficient task management +* Capacity-aware acquisition +* Optimized item handling +* Minimal server overhead + +--- + +# Development Status + +| Component | Status | +| -------------------- | ---------- | +| Core System | ✅ Complete | +| GUI System | ✅ Complete | +| SQLite Storage | ✅ Complete | +| Filter System | ✅ Complete | +| Administrative Tools | ✅ Complete | +| Optimization | ✅ Complete | +| Public Release | ✅ Public | + +--- + +# Contributors + +### Big Pappa + +Project Creator +Founder of RSScripting + +### Atlas + +Architecture, Design & Development Support + +--- + +# Support + +If you encounter a bug or have a feature request, please open an issue in the project repository. + +--- + +# License + +MIT License + +See the LICENSE file for details. + +--- + +

+ Built by RSScripting +

+ +

+ Generate • Collect • Transport • Store +

diff --git a/docs/commands.md b/docs/commands.md new file mode 100644 index 0000000..dcc3ccb --- /dev/null +++ b/docs/commands.md @@ -0,0 +1,21 @@ +# Commands + +The main plugin command is: + +```text +/rsim +``` + +## `/rsim` + +Displays the running RS-ItemMagnet plugin information. + +## `/rsim version` + +Displays the installed RS-ItemMagnet version. + +## `/rsim reload` + +Reloads the RS-ItemMagnet configuration. + +This command requires the admin permission. diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..3653d55 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,91 @@ +# Configuration + +The configuration file is located at: + +`plugins/RS-ItemMagnet/config.yml` + +## Current Settings + +```yaml +# Default scan radius for RS-ItemMagnet to scan for dropped items. +default-radius: 10 + +# Maximum radius players may set their RS-ItemMagnets to. +# 0 = Unlimited. +max-radius: 25 + +# Seconds allowed to select a target Container. +conversion-timeout: 30 + +# Distance in blocks ABOVE and BELOW the magnet +# that items may be collected from. +# +# 0 = Unlimited (legacy behavior) +# >0 = Limit vertical collection distance. +item-y-distance: 0 + +# Whether OP players bypass permissions checks +admin-bypass: true + +# Enable debug logging. +debug-mode: false +``` + +## `default-radius` + +Sets the starting collection radius for newly created ItemMagnets. + +Default: + +`10` + +## `max-radius` + +Sets the maximum radius players may configure on an ItemMagnet. + +`0` means unlimited. + +Default: + +`25` + +## `conversion-timeout` + +Sets how many seconds a player has to select a target container during conversion. + +Default: + +`30` + +## `item-y-distance` + +Controls the vertical collection distance above and below the magnet. + +- `0` = unlimited vertical distance, preserving the legacy behavior. +- `1` = collect up to 1 block above and 1 block below. +- `2` = collect up to 2 blocks above and 2 blocks below. +- Higher values increase the allowed vertical distance accordingly. + +Default: + +`0` + +## `admin-bypass` + +When enabled, OP players can bypass permission checks used by the plugin. + +Default: + +`true` + +## `debug-mode` + +Enables additional debug logging for troubleshooting. + +Default: + +`false` + +## Configuration Migration + +RS-ItemMagnet checks for missing configuration settings when it starts. New settings can be added to an existing configuration without replacing the server owner's existing values. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b02c971 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,42 @@ +# RS-ItemMagnet Documentation + +RS-ItemMagnet is an automated item collection plugin for Paper Minecraft servers. + +## Documentation + +- [Installation](installation.md) +- [Usage](usage.md) +- [Configuration](configuration.md) +- [Commands](commands.md) +- [Permissions](permissions.md) +- [Troubleshooting](troubleshooting.md) + +## What RS-ItemMagnet Does + +An ItemMagnet converts a lodestone into an automated item collector. It scans for dropped items within its configured radius and deposits accepted items into a configured hopper, chest, or barrel. + +ItemMagnets are configured through inventory menus and store their machine data persistently using SQLite. + +## Core Features + +- Configurable collection radius +- Automatic item collection +- Target hopper, chest, or barrel assignment +- Target reassignment after conversion +- Item filtering +- Pause and resume controls +- Radius preview using a temporary particle outline +- Vertical collection-distance control +- SQLite persistence +- Administrative controls +- OP update-available notification + +## Requirements + +- Paper 1.21+ +- Java version supported by the Paper version being used +- A server with permission support + +## License + +RS-ItemMagnet is released under the MIT License. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..718ee32 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,24 @@ +# Installation + +## Requirements + +RS-ItemMagnet is built for Paper Minecraft servers using the 1.21 API line. + +## Install + +1. Download the latest RS-ItemMagnet release. +2. Place the plugin JAR in the server's `plugins` folder. +3. Start or restart the server. +4. Confirm that RS-ItemMagnet enables successfully in the server console. +5. Review `plugins/RS-ItemMagnet/config.yml` and adjust settings if needed. + +The plugin creates and initializes its SQLite database automatically. + +## Updating + +1. Stop the server. +2. Replace the existing RS-ItemMagnet JAR with the new version. +3. Start the server. +4. Allow the plugin to perform any configuration migrations required by the new version. + +When a new configuration setting is introduced, RS-ItemMagnet is designed to add missing settings without replacing the server owner's existing configuration values. diff --git a/docs/permissions.md b/docs/permissions.md new file mode 100644 index 0000000..984960b --- /dev/null +++ b/docs/permissions.md @@ -0,0 +1,21 @@ +# Permissions + +## `rsitemagnet.admin` + +Provides administrative access to RS-ItemMagnet features. + +Default: + +`op` + +## `rsitemagnet.bypass` + +Allows the player to bypass ItemMagnet ownership protection. + +Default: + +`op` + +## OP Bypass + +The `admin-bypass` configuration setting controls whether OP players receive the plugin's configured administrative bypass behavior. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..1c87412 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,45 @@ +# Troubleshooting + +## ItemMagnet will not collect items + +Check the following: + +1. Confirm the machine is running rather than paused. +2. Confirm a target container is assigned. +3. Confirm the target is a hopper, chest, or barrel. +4. Confirm the filter configuration is allowing the item. +5. Confirm the item is within the configured collection radius. +6. Check the server console for RS-ItemMagnet errors. + +## Target container is missing or invalid + +Open the ItemMagnet main menu and use **Target Container** to assign a new hopper, chest, or barrel. + +## Radius is not what I expected + +Open the Radius menu and check the current radius. The server configuration also controls the default and maximum radius. + +If vertical collection is restricted, check `item-y-distance` in `config.yml`. + +## Configuration changes are not taking effect + +Use: + +```text +/rsim reload +``` + +or restart the server. + +If a new setting was introduced by an update, check the server console for configuration migration messages. + +## Need more help? + +When reporting a problem, include: + +- RS-ItemMagnet version +- Paper version +- Minecraft version +- Relevant `config.yml` settings +- Server console errors +- Steps required to reproduce the problem diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..d3ff8de --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,68 @@ +# Usage + +## Creating an ItemMagnet + +An ItemMagnet is based on a lodestone. + +The current conversion process requires the player to use the plugin's required conversion item on the lodestone and then select a target container. + +Supported target containers are: + +- Hopper +- Chest +- Barrel + +## Main Menu + +Interacting with an ItemMagnet opens its main configuration menu. + +The current menu provides controls for: + +- **Convert Back** — restores the lodestone from the ItemMagnet. +- **Running / Paused** — pauses or resumes the machine. +- **Radius** — opens the radius configuration menu. +- **Show Radius** — closes the menu and displays a temporary particle outline of the collection radius to the player. +- **Target Container** — assigns or changes the container used for deposits. +- **Filter** — configures item filtering. +- **Admin** — opens administrative controls when permitted. + +## Radius + +The radius controls how far the ItemMagnet searches for dropped items. + +The current scanner uses the configured radius on the X, Y, and Z axes. The optional `item-y-distance` configuration setting can limit the vertical collection distance above and below the magnet. + +The radius menu provides: + +- `-10` +- `-5` +- `-1` +- `+1` +- `+5` +- `+10` + +The configured maximum radius is enforced when players increase the radius. + +## Radius Preview + +Select **Show Radius** from the main menu to display the collection boundary temporarily. + +The preview is visible only to the player who requested it and shows the collection area without creating a permanent world effect. + +## Target Containers + +An ItemMagnet can deposit into a hopper, chest, or barrel. + +The target can be assigned during conversion and can later be changed from the main menu. + +While selecting a target, normal interaction with non-container blocks remains available. Selecting a valid hopper, chest, or barrel assigns it as the target. + +## Filters + +The filter menu allows the ItemMagnet to control which item types it accepts. + +Filter modes and individual filter entries are managed through the ItemMagnet inventory menus. + +## Pause and Resume + +The main menu displays the current machine state. A running ItemMagnet can be paused and resumed without converting the machine back into a normal lodestone. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a389c8d --- /dev/null +++ b/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.rsscripting + RS-ItemMagnet + 2.0.1 + + + 21 + 21 + UTF-8 + + + + + papermc-repo + https://repo.papermc.io/repository/maven-public/ + + + + + + + io.papermc.paper + paper-api + 1.21.6-R0.1-SNAPSHOT + provided + + + + org.xerial + sqlite-jdbc + 3.50.3.0 + + + + + + + + src/main/resources + true + + + ${project.artifactId}-${project.version} + + + \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/RSItemMagnet.java b/src/main/java/com/rsscripting/itemmagnet/RSItemMagnet.java new file mode 100644 index 0000000..1351f41 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/RSItemMagnet.java @@ -0,0 +1,228 @@ +package com.rsscripting.itemmagnet; + +import com.rsscripting.itemmagnet.commands.RSPluginCommand; +import com.rsscripting.itemmagnet.commands.RSTabCompleter; +import com.rsscripting.itemmagnet.config.ConfigManager; +import com.rsscripting.itemmagnet.database.DatabaseManager; +import com.rsscripting.itemmagnet.listeners.*; +import com.rsscripting.itemmagnet.tasks.RSErrorTask; +import com.rsscripting.itemmagnet.tasks.RSRadiusPreviewTask; +import com.rsscripting.itemmagnet.utils.GitHubUpdateChecker; +import com.rsscripting.itemmagnet.utils.RSConstants; +import com.rsscripting.itemmagnet.tasks.RSItemMagnetTask; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.java.JavaPlugin; + +public class RSItemMagnet + extends JavaPlugin { + + private static RSItemMagnet instance; + + @Override + public void onEnable() { + + saveDefaultConfig(); + + + instance = this; + + /* + |-------------------------------------------------------------------------- + | CONFIG + |-------------------------------------------------------------------------- + */ + + ConfigManager.setup(); + + /* + |-------------------------------------------------------------------------- + | Database + |-------------------------------------------------------------------------- + */ + + DatabaseManager.initialize( + this + ); + + /* + |-------------------------------------------------------------------------- + | LISTENERS + |-------------------------------------------------------------------------- + */ + + /* Convert */ + RSConvertListener + conversionListener = + new RSConvertListener(); + + getServer().getPluginManager().registerEvents( + + conversionListener, + + this + + ); + + /* Main Menu */ + getServer().getPluginManager().registerEvents( + + new RSMenuListener( + conversionListener + ), + + this + + ); + + /* Radius */ + RSRadiusListener radiusListener = + new RSRadiusListener( + conversionListener + ); + + getServer() + .getPluginManager() + .registerEvents( + radiusListener, + this + ); + + /* Admin */ + RSAdminListener adminListener = + new RSAdminListener( + conversionListener + ); + + getServer() + .getPluginManager() + .registerEvents( + adminListener, + this + ); + + /* Filter */ + RSFilterListener filterListener = + new RSFilterListener( + conversionListener + ); + + getServer() + .getPluginManager() + .registerEvents( + filterListener, + this + ); + + getServer() + .getPluginManager() + .registerEvents( + new RSBlockBreakListener(), + this + ); + + getServer() + .getPluginManager() + .registerEvents( + new RSUpdateNotificationListener(), + this + ); + + /* + |-------------------------------------------------------------------------- + | COMMANDS + |-------------------------------------------------------------------------- + */ + + PluginCommand command = + getCommand( + RSConstants.COMMAND_ALIAS + ); + + if (command != null) { + + command.setExecutor( + new RSPluginCommand() + ); + + command.setTabCompleter( + new RSTabCompleter() + ); + + } + + /* + |-------------------------------------------------------------------------- + | UPDATE CHECKER + |-------------------------------------------------------------------------- + */ + + GitHubUpdateChecker.checkForUpdates(); + + /* + |-------------------------------------------------------------------------- + | ENABLE MESSAGE + |-------------------------------------------------------------------------- + */ + + getLogger().info( + RSConstants.PLUGIN_NAME + + " Enabled" + ); + + new RSItemMagnetTask().runTaskTimer( + + this, + + 20L, + + 20L + + ); + + new RSRadiusPreviewTask() + + .runTaskTimer( + + this, + + 0L, + + 10L + + ); + + new RSErrorTask().runTaskTimer( + + this, + + 100L, + + 100L + + ); + + } + + @Override + public void onDisable() { + + getLogger().info( + RSConstants.PLUGIN_NAME + + " Disabled" + ); + + } + + /* + |-------------------------------------------------------------------------- + | GET INSTANCE + |-------------------------------------------------------------------------- + */ + + public static RSItemMagnet getInstance() { + + return instance; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/commands/RSPluginCommand.java b/src/main/java/com/rsscripting/itemmagnet/commands/RSPluginCommand.java new file mode 100644 index 0000000..d63c730 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/commands/RSPluginCommand.java @@ -0,0 +1,102 @@ +package com.rsscripting.itemmagnet.commands; + +import com.rsscripting.itemmagnet.config.ConfigManager; +import com.rsscripting.itemmagnet.utils.RSConstants; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +public class RSPluginCommand + implements CommandExecutor { + + + + @Override + public boolean onCommand( + + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String @NotNull [] args + + ) { + + /* + |-------------------------------------------------------------------------- + | BASE COMMAND + |-------------------------------------------------------------------------- + */ + + if (args.length == 0) { + + sender.sendMessage( + RSConstants.PREFIX + + "Running " + + RSConstants.PLUGIN_NAME + ); + + return true; + + } + + /* + |-------------------------------------------------------------------------- + | RELOAD COMMAND + |-------------------------------------------------------------------------- + */ + + if (args[0].equalsIgnoreCase( + "reload" + )) { + + if (!sender.hasPermission( + RSConstants.ADMIN_PERMISSION + )) { + + sender.sendMessage( + RSConstants.PREFIX + + "No permission." + ); + + return true; + + } + + ConfigManager.reload(); + + sender.sendMessage( + RSConstants.PREFIX + + "Configuration reloaded." + ); + + return true; + + } + + /* + |-------------------------------------------------------------------------- + | VERSION COMMAND + |-------------------------------------------------------------------------- + */ + + if (args[0].equalsIgnoreCase( + "version" + )) { + + sender.sendMessage( + RSConstants.PREFIX + + RSConstants.PLUGIN_NAME + + " v" + + RSConstants.VERSION + ); + + return true; + + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/commands/RSTabCompleter.java b/src/main/java/com/rsscripting/itemmagnet/commands/RSTabCompleter.java new file mode 100644 index 0000000..cd9b9a1 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/commands/RSTabCompleter.java @@ -0,0 +1,46 @@ +package com.rsscripting.itemmagnet.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class RSTabCompleter + implements TabCompleter { + + @Override + public List onTabComplete( + + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String @NotNull [] args + + + ) { + + List completions = + new ArrayList<>(); + + /* + |-------------------------------------------------------------------------- + | FIRST ARGUMENT + |-------------------------------------------------------------------------- + */ + + if (args.length == 1) { + + completions.add("reload"); + + completions.add("version"); + + } + + return completions; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/config/ConfigManager.java b/src/main/java/com/rsscripting/itemmagnet/config/ConfigManager.java new file mode 100644 index 0000000..bbbc93b --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/config/ConfigManager.java @@ -0,0 +1,247 @@ +package com.rsscripting.itemmagnet.config; + +import com.rsscripting.itemmagnet.RSItemMagnet; +import org.bukkit.configuration.file.FileConfiguration; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ConfigManager { + + private static FileConfiguration config; + + /* + |-------------------------------------------------------------------------- + | SETUP + |-------------------------------------------------------------------------- + */ + + public static void setup() { + + RSItemMagnet.getInstance() + .saveDefaultConfig(); + + config = + RSItemMagnet.getInstance() + .getConfig(); + + /* + |-------------------------------------------------------------------------- + | CONFIG UPDATES + |-------------------------------------------------------------------------- + */ + boolean updated = false; + + if (!config.isSet("item-y-distance")) { + + addConfigSetting( + "item-y-distance", + "# Distance in blocks ABOVE and BELOW the magnet\n" + + "# that items may be collected from.\n" + + "#\n" + + "# 0 = Unlimited (legacy behavior)\n" + + "item-y-distance: 0\n" + ); + + updated = true; + + RSItemMagnet.getInstance() + .getLogger() + .info( + "Added missing config setting: item-y-distance" + ); + + } + + if (!config.isSet("max-target-distance")) { + + addConfigSetting( + "max-target-distance", + "# Maximum distance in blocks a target container may be from the ItemMagnet.\n" + + "# 0 = Unlimited.\n" + + "max-target-distance: 30\n" + ); + + updated = true; + + RSItemMagnet.getInstance() + .getLogger() + .info( + "Added missing config setting: max-target-distance" + ); + + } + + if (updated) { + + RSItemMagnet.getInstance() + .reloadConfig(); + + config = + RSItemMagnet.getInstance() + .getConfig(); + + RSItemMagnet.getInstance() + .getLogger() + .info( + "Config updated with missing settings and comments." + ); + + } + + } + + private static void addConfigSetting( + String path, + String documentedSetting + ) { + + Path configPath = + RSItemMagnet.getInstance() + .getDataFolder() + .toPath() + .resolve("config.yml"); + + try { + + String content = + Files.readString( + configPath, + StandardCharsets.UTF_8 + ); + + String newline = + content.contains("\r\n") + ? "\r\n" + : "\n"; + + documentedSetting = + documentedSetting.replace("\n", newline); + + String marker = + "# Whether OP players bypass permissions checks"; + + int markerIndex = + content.indexOf(marker); + + if (markerIndex >= 0) { + + content = + content.substring(0, markerIndex) + + documentedSetting + + newline + + content.substring(markerIndex); + + } else { + + if (!content.endsWith("\n") + && !content.endsWith("\r")) { + + content += newline; + + } + + content += newline + documentedSetting; + + } + + Files.writeString( + configPath, + content, + StandardCharsets.UTF_8 + ); + + } catch (IOException exception) { + + RSItemMagnet.getInstance() + .getLogger() + .warning( + "Failed to add config setting '" + + path + + "' with comments: " + + exception.getMessage() + ); + + config.set( + path, + path.equals("max-target-distance") + ? 30 + : 0 + ); + + } + + } + + /* + |-------------------------------------------------------------------------- + | RELOAD + |-------------------------------------------------------------------------- + */ + + public static void reload() { + + RSItemMagnet.getInstance() + .reloadConfig(); + + config = + RSItemMagnet.getInstance() + .getConfig(); + + } + + /* + |-------------------------------------------------------------------------- + | GET CONFIG + |-------------------------------------------------------------------------- + */ + + public static FileConfiguration get() { + + return config; + + } + + /* + |-------------------------------------------------------------------------- + | SETTINGS + |-------------------------------------------------------------------------- + */ + + public static int getDefaultRadius() { + + return config.getInt( + "default-radius" + ); + + } + + public static int getConversionTimeout() { + + return config.getInt( + "conversion-timeout" + ); + + } + + public static int getItemYDistance() { + + return config.getInt( + "item-y-distance", + 0 + ); + + } + + public static int getMaxTargetDistance() { + + return config.getInt( + "max-target-distance", + 30 + ); + + } + +} diff --git a/src/main/java/com/rsscripting/itemmagnet/database/DatabaseManager.java b/src/main/java/com/rsscripting/itemmagnet/database/DatabaseManager.java new file mode 100644 index 0000000..6c56529 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/database/DatabaseManager.java @@ -0,0 +1,169 @@ +package com.rsscripting.itemmagnet.database; + +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.util.logging.Level; + +public class DatabaseManager { + + private static JavaPlugin plugin; + + private static String databaseUrl; + + public static void initialize( + JavaPlugin pluginInstance + ) { + + plugin = pluginInstance; + + createDatabase(); + + createTables(); + + } + + private static void createDatabase() { + + try { + + File databaseFile = + new File( + plugin.getDataFolder(), + "database.db" + ); + + if (!plugin.getDataFolder().exists()) { + + if (!plugin.getDataFolder().mkdirs()) { + + plugin.getLogger().severe( + "Failed to create plugin data folder." + ); + + return; + + } + + } + + databaseUrl = + "jdbc:sqlite:" + + databaseFile.getAbsolutePath(); + + Connection connection = + DriverManager.getConnection( + databaseUrl + ); + + connection.close(); + + plugin.getLogger().info( + "SQLite database initialized." + ); + + } + + catch (Exception e) { + + plugin.getLogger().log( + Level.SEVERE, + "Failed to initialize database.", + e + ); + + } + + } + + private static void createTables() { + + try ( + + Connection connection = + DriverManager.getConnection( + databaseUrl + ) + + ) { + + connection.createStatement().execute( + """ + CREATE TABLE IF NOT EXISTS machines ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + world TEXT NOT NULL, + x INTEGER NOT NULL, + y INTEGER NOT NULL, + z INTEGER NOT NULL, + owner_uuid TEXT, + owner_name TEXT, + machine_type TEXT, + target_world TEXT, + target_x INTEGER, + target_y INTEGER, + target_z INTEGER, + radius INTEGER DEFAULT 5, + paused INTEGER DEFAULT 0, + status TEXT DEFAULT 'RUNNING', + filter_mode TEXT DEFAULT 'ALLOW_ALL', + created_at TEXT + ) + """ + ); + + connection.createStatement().execute( + """ + CREATE TABLE IF NOT EXISTS machine_filters ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + machine_id INTEGER NOT NULL, + page INTEGER NOT NULL, + slot INTEGER NOT NULL, + item_data TEXT NOT NULL + ) + """ + ); + + plugin.getLogger().info( + "Database tables initialized." + ); + + try { + + connection.createStatement().execute( + """ + ALTER TABLE machines + ADD COLUMN status TEXT DEFAULT 'RUNNING' + """ + ); + + } + catch (Exception ignored) { + + } + + } + + catch (Exception e) { + + plugin.getLogger().log( + Level.SEVERE, + "Failed to create tables.", + e + ); + + } + + } + public static Connection getConnection() + throws Exception { + + return DriverManager.getConnection( + databaseUrl + ); + + } + + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/enums/MachineStatus.java b/src/main/java/com/rsscripting/itemmagnet/enums/MachineStatus.java new file mode 100644 index 0000000..29b7f5b --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/enums/MachineStatus.java @@ -0,0 +1,15 @@ +package com.rsscripting.itemmagnet.enums; + +public enum MachineStatus { + + RUNNING, + + PAUSED, + + NO_TARGET, + + TARGET_MISSING, + + TARGET_INVALID + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/gui/RSAdminMenu.java b/src/main/java/com/rsscripting/itemmagnet/gui/RSAdminMenu.java new file mode 100644 index 0000000..84513cd --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/gui/RSAdminMenu.java @@ -0,0 +1,140 @@ +package com.rsscripting.itemmagnet.gui; + + +import com.rsscripting.itemmagnet.utils.RSMenuUtils; +import com.rsscripting.itemmagnet.utils.VersionUtils; +import com.rsscripting.itemmagnet.utils.GitHubUpdateChecker; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import net.kyori.adventure.text.Component; + +import java.util.List; + +public class RSAdminMenu { + + public static final String MENU_TITLE = "Admin"; + + public static void open( + Player player + ) { + + Inventory menu = + Bukkit.createInventory( + new RSMenuHolder( + "ADMIN" + ), + 18, + Component.text( + MENU_TITLE + ).color( + NamedTextColor.GOLD + ) + ); + + + /* + |-------------------------------------------------------------------------- + | FILLER + |-------------------------------------------------------------------------- + */ + + ItemStack filler = + RSMenuUtils.createFillerPane(); + + for (int i = 0; i < menu.getSize(); i++) { + + menu.setItem(i, filler); + + } + + /* +|-------------------------------------------------------------------------- +| BACK +|-------------------------------------------------------------------------- +*/ + + menu.setItem( + 4, + RSMenuUtils.createMenuItem( + + Material.OAK_DOOR, + + "§eBack" + + ) + ); + + /* + |-------------------------------------------------------------------------- + | RELOAD + |-------------------------------------------------------------------------- + */ + + menu.setItem( + 12, + RSMenuUtils.createMenuItem( + + Material.ENCHANTED_BOOK, + + "§cReload Plugin" + + ) + ); + + /* + |-------------------------------------------------------------------------- + | VERSION + |-------------------------------------------------------------------------- + */ + + menu.setItem( + 14, + RSMenuUtils.createMenuItem( + + Material.BOOK, + + "§ePlugin Version", + + List.of( + + "§7Current Version: §f" + + VersionUtils.getVersion(), + + "§7Latest Version: §f" + + ( + GitHubUpdateChecker.getLatestVersion() + == null + ? "Unknown" + : GitHubUpdateChecker.getLatestVersion() + ), + + "", + + ( + GitHubUpdateChecker.getLatestVersion() == null + || GitHubUpdateChecker.getLatestVersion() + .equalsIgnoreCase("Unknown") + ) + ? "§cStatus: Unable To Check" + : ( + !VersionUtils.getVersion().equalsIgnoreCase( + GitHubUpdateChecker.getLatestVersion() + ) + ? "§aStatus: Update Available" + : "§aStatus: Up To Date" + ) + + ) + + ) + ); + + player.openInventory(menu); + + } + +} diff --git a/src/main/java/com/rsscripting/itemmagnet/gui/RSConversionMenu.java b/src/main/java/com/rsscripting/itemmagnet/gui/RSConversionMenu.java new file mode 100644 index 0000000..dcacd04 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/gui/RSConversionMenu.java @@ -0,0 +1,66 @@ +package com.rsscripting.itemmagnet.gui; + +import com.rsscripting.itemmagnet.utils.RSMenuUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class RSConversionMenu { + + public static final String MENU_TITLE = "Activate Item Magnet"; + + public static void open( + Player player + ) { + + Inventory menu = + Bukkit.createInventory( + new RSMenuHolder( + "CONVERT" + ), + 9, + Component.text( + MENU_TITLE + ).color( + NamedTextColor.BLUE + ) + ); + + /* + |-------------------------------------------------------------------------- + | FILLER + |-------------------------------------------------------------------------- + */ + + ItemStack filler = + RSMenuUtils.createFillerPane(); + + for (int i = 0; i < menu.getSize(); i++) { + + menu.setItem(i, filler); + + } + + /* + |-------------------------------------------------------------------------- + | CONVERT BUTTON + |-------------------------------------------------------------------------- + */ + + menu.setItem( + 4, + RSMenuUtils.createMenuItem( + Material.CRAFTING_TABLE, + "§aConvert" + ) + ); + + player.openInventory(menu); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/gui/RSFilterMenu.java b/src/main/java/com/rsscripting/itemmagnet/gui/RSFilterMenu.java new file mode 100644 index 0000000..31337ea --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/gui/RSFilterMenu.java @@ -0,0 +1,227 @@ +package com.rsscripting.itemmagnet.gui; + +import com.rsscripting.itemmagnet.managers.MachineManager; +import com.rsscripting.itemmagnet.utils.RSKeys; +import com.rsscripting.itemmagnet.utils.RSMenuUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; + +public class RSFilterMenu { + + public static final String MENU_TITLE = + "Filter"; + + public static void open( + Player player, + Block block + ) { + + open( + player, + block, + 1 + ); + + } + + public static void open( + Player player, + Block block, + int page + ) { + + RSMenuHolder holder = + new RSMenuHolder( + "FILTER" + ); + + holder.setPage( + page + ); + + Inventory menu = + Bukkit.createInventory( + holder, + 54, + Component.text( + MENU_TITLE + + " [" + + holder.getPage() + + "]" + ).color( + NamedTextColor.BLUE + ) + ); + + /* + |-------------------------------------------------------------------------- + | TOP ROW FILLER + |-------------------------------------------------------------------------- + */ + + ItemStack filler = + RSMenuUtils.createFillerPane(); + + for (int i = 0; i < 9; i++) { + + menu.setItem( + i, + filler + ); + + } + +/* +|-------------------------------------------------------------------------- +| FILTER MODE +|-------------------------------------------------------------------------- +*/ + + String filterMode = + MachineManager.getFilterMode( + block + ); + + Material filterMaterial; + + String filterName; + + if (filterMode.equals( + RSKeys.FILTER_WHITELIST + )) { + + filterMaterial = + Material.WHITE_WOOL; + + filterName = + "§aWhitelist"; + + } + + else if (filterMode.equals( + RSKeys.FILTER_BLACKLIST + )) { + + filterMaterial = + Material.BLACK_WOOL; + + filterName = + "§cBlacklist"; + + } + + else { + + filterMaterial = + Material.LIME_WOOL; + + filterName = + "§aAllow All"; + + } + + menu.setItem( + 3, + RSMenuUtils.createMenuItem( + filterMaterial, + filterName + ) + ); + + /* + |-------------------------------------------------------------------------- + | PREVIOUS PAGE + |-------------------------------------------------------------------------- + */ + + if (holder.getPage() > 1) { + + menu.setItem( + 0, + RSMenuUtils.createMenuItem( + Material.ARROW, + "§ePrevious Page (" + holder.getPage() + ")" + ) + ); + + } + + /* + |-------------------------------------------------------------------------- + | FILTER ITEMS + |-------------------------------------------------------------------------- + */ + + Map filterItems = + MachineManager.getFilterItems( + block, + holder.getPage() + ); + + for (Integer slot : filterItems.keySet()) { + + menu.setItem( + slot, + filterItems.get( + slot + ) + ); + + } + + /* + |-------------------------------------------------------------------------- + | NEXT PAGE + |-------------------------------------------------------------------------- + */ + + int highestPage = + MachineManager.getHighestFilterPage( + block + ); + + if (holder.getPage() + <= highestPage) { + + menu.setItem( + 8, + RSMenuUtils.createMenuItem( + Material.ARROW, + "§eNext Page (" + holder.getPage() + ")" + ) + ); + + } + + /* + |-------------------------------------------------------------------------- + | BACK + |-------------------------------------------------------------------------- + */ + + menu.setItem( + 5, + RSMenuUtils.createMenuItem( + + Material.OAK_DOOR, + + "§eBack" + + ) + ); + + player.openInventory( + menu + ); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/gui/RSMainMenu.java b/src/main/java/com/rsscripting/itemmagnet/gui/RSMainMenu.java new file mode 100644 index 0000000..7dd1e0d --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/gui/RSMainMenu.java @@ -0,0 +1,285 @@ +package com.rsscripting.itemmagnet.gui; + +import com.rsscripting.itemmagnet.managers.MachineManager; +import com.rsscripting.itemmagnet.utils.RSConstants; +import com.rsscripting.itemmagnet.utils.RSMenuUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class RSMainMenu { + + public static final String MENU_TITLE = "Main"; + + public static void open( + Player player, + Block block + ) { + + Inventory menu = + Bukkit.createInventory( + new RSMenuHolder( + "MAIN" + ), + 18, + Component.text( + MENU_TITLE + ).color( + NamedTextColor.BLUE + ) + ); + + /* + |-------------------------------------------------------------------------- + | FILLER + |-------------------------------------------------------------------------- + */ + + ItemStack filler = + RSMenuUtils.createFillerPane(); + + for (int i = 0; i < menu.getSize(); i++) { + + menu.setItem(i, filler); + + } + + /* + |-------------------------------------------------------------------------- + | CONVERT BACK + |-------------------------------------------------------------------------- + */ + + menu.setItem( + 2, + RSMenuUtils.createMenuItem( + Material.COMPOSTER, + "§6Convert Back" + ) + ); + +/* +|-------------------------------------------------------------------------- +| PAUSE / RESUME +|-------------------------------------------------------------------------- +*/ + + boolean paused = + MachineManager.isPaused( + block + ); + + String status = + MachineManager.getStoredStatus( + block + ); + + String displayStatus; + + switch (status) { + + case "RUNNING" -> displayStatus = + "Running"; + + case "PAUSED" -> displayStatus = + "Paused"; + + case "ERROR_TARGET_MISSING" -> displayStatus = + "Target Container Missing"; + + case "ERROR_TARGET_INVALID" -> displayStatus = + "Invalid Target Container"; + + case "ERROR_NO_TARGET" -> displayStatus = + "No Target Container Assigned"; + + default -> displayStatus = + status; + + } + + menu.setItem( + 6, + RSMenuUtils.createMenuItem( + + paused + ? Material.RED_DYE + : Material.GREEN_DYE, + + paused + ? "§cPaused" + : "§aRunning", + + List.of( + "§7Status: §f" + + displayStatus + ) + + ) + ); + + /* + |-------------------------------------------------------------------------- + | RADIUS + |-------------------------------------------------------------------------- + */ + + menu.setItem( + 10, + RSMenuUtils.createMenuItem( + + Material.COMPASS, + + "§bRadius", + + List.of( + "§7Current Radius: §f" + + MachineManager.getRadius(block) + ) + + ) + ); + + /* +|-------------------------------------------------------------------------- +| Show Radius +|-------------------------------------------------------------------------- +*/ + + menu.setItem( + + 12, + + RSMenuUtils.createMenuItem( + + Material.ENDER_EYE, + + "§bShow Radius", + + List.of( + + "§7Displays a particle outline", + + "§7of the ItemMagnet radius.", + + "", + + "§eClick to preview." + + ) + + ) + + ); + + +/* +|-------------------------------------------------------------------------- +| TARGET CONTAINER +|-------------------------------------------------------------------------- +*/ + + int[] targetContainer = + MachineManager.getTargetContainer( + block + ); + + if (targetContainer == null) { + + menu.setItem( + 14, + RSMenuUtils.createMenuItem( + + Material.TARGET, + + "§eTarget Container", + + List.of( + "§cNo target Container assigned.", + "", + "§7Click to assign", + "§7a target Container." + ) + + ) + ); + + } else { + + menu.setItem( + 14, + RSMenuUtils.createMenuItem( + + Material.TARGET, + + "§eTarget Container", + + List.of( + "§7Current Container:", + " " + targetContainer[0], + " " + targetContainer[1], + " " + targetContainer[2], + "", + "§7Click to assign", + "§7a new target Container." + ) + + ) + ); + + } + + /* + |-------------------------------------------------------------------------- + | FILTER + |-------------------------------------------------------------------------- + */ + + menu.setItem( + 16, + RSMenuUtils.createMenuItem( + + Material.HOPPER, + + "§eFilter", + + List.of( + "§7Mode: §f" + + MachineManager.getFilterMode(block) + ) + + ) + ); + + /* + |-------------------------------------------------------------------------- + | ADMIN + |-------------------------------------------------------------------------- + */ + + if (player.hasPermission( + RSConstants.ADMIN_PERMISSION + )) { + + menu.setItem( + 4, + RSMenuUtils.createMenuItem( + Material.COMMAND_BLOCK, + "§cAdmin" + ) + ); + + } + + player.openInventory(menu); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/gui/RSMenuHolder.java b/src/main/java/com/rsscripting/itemmagnet/gui/RSMenuHolder.java new file mode 100644 index 0000000..70204cc --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/gui/RSMenuHolder.java @@ -0,0 +1,57 @@ +package com.rsscripting.itemmagnet.gui; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +public class RSMenuHolder + implements InventoryHolder { + + private final String menuId; + + private int page = 1; + + public RSMenuHolder( + String menuId + ) { + + this.menuId = menuId; + + } + + public String getMenuId() { + + return menuId; + + } + + public int getPage() { + + return page; + + } + + public void setPage( + int page + ) { + + if (page < 1) { + + page = 1; + + } + + this.page = page; + + } + + @Override + public @NotNull Inventory getInventory() { + + throw new UnsupportedOperationException( + "RSMenuHolder does not store an inventory." + ); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/gui/RSRadiusMenu.java b/src/main/java/com/rsscripting/itemmagnet/gui/RSRadiusMenu.java new file mode 100644 index 0000000..643bef8 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/gui/RSRadiusMenu.java @@ -0,0 +1,183 @@ +package com.rsscripting.itemmagnet.gui; + +import com.rsscripting.itemmagnet.managers.MachineManager; +import com.rsscripting.itemmagnet.utils.RSMenuUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class RSRadiusMenu { + + + public static void open( + Player player, + Block block + ) { + + Inventory menu = + Bukkit.createInventory( + new RSMenuHolder( + "RADIUS" + ), + 18, + Component.text( + "Radius: " + + MachineManager.getRadius( + block + ) + ).color(NamedTextColor.BLUE) + ); + + /* + |-------------------------------------------------------------------------- + | FILLER + |-------------------------------------------------------------------------- + */ + + ItemStack filler = + RSMenuUtils.createFillerPane(); + + for (int i = 0; i < menu.getSize(); i++) { + + menu.setItem(i, filler); + + } + + /* +|-------------------------------------------------------------------------- +| BACK +|-------------------------------------------------------------------------- +*/ + + menu.setItem( + 4, + RSMenuUtils.createMenuItem( + + Material.OAK_DOOR, + + "§eBack" + + ) + ); + + /* + |-------------------------------------------------------------------------- + | CURRENT RADIUS + |-------------------------------------------------------------------------- + */ + + menu.setItem( + 13, + RSMenuUtils.createMenuItem( + + Material.COMPASS, + + "§bCurrent Radius", + + List.of( + "§7Radius: §f" + + MachineManager.getRadius( + block + ) + ) + + ) + ); + + /* + |-------------------------------------------------------------------------- + | NEGATIVE BUTTONS + |-------------------------------------------------------------------------- + */ + + ItemStack minusTen = + RSMenuUtils.createMenuItem( + Material.RED_WOOL, + "§c-10" + ); + + minusTen.setAmount(10); + + menu.setItem( + 10, + minusTen + ); + + ItemStack minusFive = + RSMenuUtils.createMenuItem( + Material.RED_WOOL, + "§c-5" + ); + + minusFive.setAmount(5); + + menu.setItem( + 11, + minusFive + ); + + menu.setItem( + 12, + RSMenuUtils.createMenuItem( + Material.RED_WOOL, + "§c-1" + ) + ); + + /* + |-------------------------------------------------------------------------- + | POSITIVE BUTTONS + |-------------------------------------------------------------------------- + */ + + ItemStack plusOne = + RSMenuUtils.createMenuItem( + Material.GREEN_WOOL, + "§a+1" + ); + + plusOne.setAmount(1); + + menu.setItem( + 14, + plusOne + ); + + ItemStack plusFive = + RSMenuUtils.createMenuItem( + Material.GREEN_WOOL, + "§a+5" + ); + + plusFive.setAmount(5); + + menu.setItem( + 15, + plusFive + ); + + ItemStack plusTen = + RSMenuUtils.createMenuItem( + Material.GREEN_WOOL, + "§a+10" + ); + + plusTen.setAmount(10); + + menu.setItem( + 16, + plusTen + ); + + player.openInventory(menu); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/listeners/RSAdminListener.java b/src/main/java/com/rsscripting/itemmagnet/listeners/RSAdminListener.java new file mode 100644 index 0000000..240f8c9 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/listeners/RSAdminListener.java @@ -0,0 +1,128 @@ +package com.rsscripting.itemmagnet.listeners; + +import com.rsscripting.itemmagnet.config.ConfigManager; +import com.rsscripting.itemmagnet.gui.RSAdminMenu; +import com.rsscripting.itemmagnet.gui.RSMenuHolder; +import com.rsscripting.itemmagnet.utils.RSConstants; +import com.rsscripting.itemmagnet.gui.RSMainMenu; +import org.bukkit.block.Block; +import com.rsscripting.itemmagnet.utils.RSMessageUtils; +import org.bukkit.entity.Player; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; + +public class RSAdminListener + implements Listener { + + private final RSConvertListener + conversionListener; + + public RSAdminListener( + RSConvertListener conversionListener + ) { + + this.conversionListener = + conversionListener; + + } + + @EventHandler + public void onInventoryClick( + InventoryClickEvent event + ) { + + if (!(event.getWhoClicked() + instanceof Player player)) { + return; + } + + if (!(event.getInventory() + .getHolder() + instanceof RSMenuHolder holder)) { + return; + } + + if (!holder.getMenuId() + .equals( + "ADMIN" + )) { + return; + } + + event.setCancelled(true); + + /* + |-------------------------------------------------------------------------- + | SECURITY + |-------------------------------------------------------------------------- + */ + + if (!player.isOp() + || + + !player.hasPermission( + RSConstants.ADMIN_PERMISSION + )) { + + player.closeInventory(); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | BACK + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 4) { + + Block block = + conversionListener.getSelectedBlock( + player + ); + + if (block == null) { + + player.closeInventory(); + + return; + + } + + RSMainMenu.open( + player, + block + ); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | RELOAD + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 12) { + + ConfigManager.reload(); + + RSMessageUtils.success( + player, + "Configuration reloaded." + ); + + RSAdminMenu.open( + player + ); + + } + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/listeners/RSBlockBreakListener.java b/src/main/java/com/rsscripting/itemmagnet/listeners/RSBlockBreakListener.java new file mode 100644 index 0000000..6bd509a --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/listeners/RSBlockBreakListener.java @@ -0,0 +1,28 @@ +package com.rsscripting.itemmagnet.listeners; + +import com.rsscripting.itemmagnet.managers.MachineManager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; + +public class RSBlockBreakListener + implements Listener { + + @EventHandler + public void onBlockBreak( + BlockBreakEvent event + ) { + + if (MachineManager.machineExists( + event.getBlock() + )) { + + event.setCancelled( + true + ); + + } + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/listeners/RSConvertListener.java b/src/main/java/com/rsscripting/itemmagnet/listeners/RSConvertListener.java new file mode 100644 index 0000000..63e12f7 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/listeners/RSConvertListener.java @@ -0,0 +1,470 @@ +package com.rsscripting.itemmagnet.listeners; + +import com.rsscripting.itemmagnet.gui.RSConversionMenu; +import com.rsscripting.itemmagnet.gui.RSMainMenu; +import com.rsscripting.itemmagnet.managers.MachineManager; +import com.rsscripting.itemmagnet.managers.PendingTargetAssignment; +import com.rsscripting.itemmagnet.utils.RSConstants; +import com.rsscripting.itemmagnet.utils.RSMessageUtils; +import com.rsscripting.itemmagnet.config.ConfigManager; +import com.rsscripting.itemmagnet.managers.PendingConversion; +import com.rsscripting.itemmagnet.managers.RSConversionManager; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class RSConvertListener + implements Listener { + + private final Map selectedBlocks = + new HashMap<>(); + + @EventHandler + public void onPlayerInteractBlock( + PlayerInteractEvent event + ) { + + Player player = + event.getPlayer(); + + Block block = + event.getClickedBlock(); + + if (block == null) { + return; + } + + /* +|-------------------------------------------------------------------------- +| PENDING TARGET ASSIGNMENT +|-------------------------------------------------------------------------- +*/ + + PendingTargetAssignment pendingAssignment = + RSConversionManager + .getPendingTargetAssignment( + player + ); + + if (pendingAssignment != null) { + + if (pendingAssignment.isExpired()) { + + RSConversionManager.removePendingTargetAssignment( + player + ); + + player.sendMessage( + RSConstants.PREFIX + + "Target assignment expired." + ); + + return; + + } + + Block targetAssignmentMachine = + pendingAssignment.getMachine(); + + if (targetAssignmentMachine != null) { + + Material type = + block.getType(); + + if ( + type != Material.HOPPER + && + type != Material.CHEST + && + type != Material.BARREL + ) { + + RSMessageUtils.error( + player, + "Please select a Hopper, Chest, or Barrel." + ); + + return; + + } + + event.setCancelled(true); + + if (!targetAssignmentMachine.getWorld().equals( + block.getWorld() + )) { + + RSMessageUtils.error( + player, + "Target container must be in the same world." + ); + + return; + + } + + int maxDistance = + ConfigManager.getMaxTargetDistance(); + + if ( + maxDistance > 0 + && + targetAssignmentMachine + .getLocation() + .distance( + block.getLocation() + ) > maxDistance + ) { + + RSMessageUtils.error( + player, + "Target container must be within " + + maxDistance + + " blocks." + ); + + return; + + } + + MachineManager.setTargetContainer( + targetAssignmentMachine, + block + ); + + RSConversionManager.removePendingTargetAssignment( + player + ); + + RSMessageUtils.success( + player, + "Target container updated." + ); + + return; + + } + } + +/* +|-------------------------------------------------------------------------- +| PENDING Conversion CONTAINER SELECTION +|-------------------------------------------------------------------------- +*/ + + PendingConversion pendingConversion = + RSConversionManager.getPendingConversion( + player + ); + + if (pendingConversion != null) { + + if (pendingConversion.isExpired()) { + + RSConversionManager.removePendingConversion( + player + ); + + RSMessageUtils.error( + player, + "Conversion timed out." + ); + + return; + + } + + Material type = + block.getType(); + + if ( + type != Material.HOPPER + && + type != Material.CHEST + && + type != Material.BARREL + ) { + + RSMessageUtils.error( + player, + "Please select a Hopper, Chest, or Barrel." + ); + + return; + + } + + event.setCancelled(true); + + Block lodestone = + pendingConversion.getLodestone(); + + int maxDistance = + ConfigManager.getMaxTargetDistance(); + + if (!lodestone.getWorld().equals( + block.getWorld() + )) { + + RSMessageUtils.error( + player, + "Target container must be in the same world." + ); + + return; + + } + + if ( + maxDistance > 0 + && + lodestone.getLocation().distance( + block.getLocation() + ) > maxDistance + ) { + + RSMessageUtils.error( + + player, + + "Target container must be within " + + maxDistance + + " blocks." + + ); + + return; + + } + + MachineManager.insertMachine( + lodestone + ); + + MachineManager.setOwner( + lodestone, + player + ); + + MachineManager.setPaused( + lodestone, + false + ); + + MachineManager.setStatus( + lodestone, + "RUNNING" + ); + + MachineManager.setRadius( + lodestone, + ConfigManager.getDefaultRadius() + ); + + MachineManager.setFilterMode( + lodestone, + "ALLOW_ALL" + ); + + MachineManager.setTargetContainer( + lodestone, + block + ); + + RSConversionManager.removePendingConversion( + player + ); + + RSMessageUtils.success( + player, + "Target container assigned." + ); + + RSMainMenu.open( + player, + lodestone + ); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | REQUIRE SNEAKING + |-------------------------------------------------------------------------- + */ + + if (!player.isSneaking()) { + return; + } + + /* + |-------------------------------------------------------------------------- + | VALIDATE TARGET ENTITY + |-------------------------------------------------------------------------- + */ + + if (block.getType() != + RSConstants.REQUIRED_TARGET_BLOCK) { + return; + } + + /* + |-------------------------------------------------------------------------- + | VALIDATE HELD ITEM + |-------------------------------------------------------------------------- + */ + + ItemStack heldItem = + player.getInventory() + .getItemInMainHand(); + + if (heldItem.getType() != + RSConstants.REQUIRED_HELD_ITEM) { + return; + } + + /* + |-------------------------------------------------------------------------- + | CANCEL VANILLA ACTION + |-------------------------------------------------------------------------- + */ + + event.setCancelled(true); + + /* + |-------------------------------------------------------------------------- + | STORE SELECTED ENTITY + |-------------------------------------------------------------------------- + */ + + selectedBlocks.put( + player.getUniqueId(), + block + ); + + /* +|-------------------------------------------------------------------------- +| ALREADY CONVERTED +|-------------------------------------------------------------------------- +*/ + + if (MachineManager.machineExists( + block + )) { + + /* + |-------------------------------------------------------------------------- + | OWNER CHECK + |-------------------------------------------------------------------------- + */ + + if (!MachineManager.isOwner( + block, + player + ) + + && + + !player.hasPermission( + RSConstants.BYPASS_PERMISSION + ) + + ) { + + RSMessageUtils.error( + player, + "You are not the owner." + ); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | OPEN MAIN MENU + |-------------------------------------------------------------------------- + */ + + RSMainMenu.open( + player, + block + ); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | OPEN CONVERSION MENU + |-------------------------------------------------------------------------- + */ + + RSConversionMenu.open(player); + + } + + /* + |-------------------------------------------------------------------------- + | GET SELECTED ENTITY + |-------------------------------------------------------------------------- + */ + + public Block getSelectedBlock( + Player player + ) { + + return selectedBlocks.get( + player.getUniqueId() + ); + + } + /* + |-------------------------------------------------------------------------- + | Convert Back + |-------------------------------------------------------------------------- + */ + + public void convertBack( + Player player + ) { + + Block block = + selectedBlocks.get( + player.getUniqueId() + ); + + if (block == null) { + + player.closeInventory(); + + return; + + } + + MachineManager.deleteMachine( + block + ); + + RSMessageUtils.success( + player, + "Machine removed." + ); + + player.closeInventory(); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/listeners/RSFilterListener.java b/src/main/java/com/rsscripting/itemmagnet/listeners/RSFilterListener.java new file mode 100644 index 0000000..5ea84b8 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/listeners/RSFilterListener.java @@ -0,0 +1,297 @@ +package com.rsscripting.itemmagnet.listeners; + +import com.rsscripting.itemmagnet.gui.RSFilterMenu; +import com.rsscripting.itemmagnet.gui.RSMainMenu; +import com.rsscripting.itemmagnet.gui.RSMenuHolder; +import com.rsscripting.itemmagnet.managers.MachineManager; +import com.rsscripting.itemmagnet.managers.RSConversionManager; +import com.rsscripting.itemmagnet.utils.RSKeys; +import com.rsscripting.itemmagnet.utils.RSMessageUtils; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; + +public class RSFilterListener + implements Listener { + + + + + + private final RSConvertListener + conversionListener; + + public RSFilterListener( + RSConvertListener conversionListener + ) { + + this.conversionListener = + conversionListener; + + } + + @EventHandler + public void onInventoryClick( + InventoryClickEvent event + ) { + + if (!(event.getWhoClicked() + instanceof Player player)) { + return; + } + + if (!(event.getInventory() + .getHolder() + instanceof RSMenuHolder holder)) { + return; + } + + if (!holder.getMenuId() + .equals( + "FILTER" + )) { + return; + } + + Block block = + conversionListener.getSelectedBlock( + player + ); + + if (block == null) { + + player.closeInventory(); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | TOP ROW + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() < 9) { + + event.setCancelled(true); + + /* + |-------------------------------------------------------------------------- + | FILTER MODE + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 3) { + + String currentMode = + MachineManager.getFilterMode( + block + ); + + String nextMode; + + if (currentMode.equals( + RSKeys.FILTER_ALLOW_ALL + )) { + + nextMode = + RSKeys.FILTER_WHITELIST; + + } + + else if (currentMode.equals( + RSKeys.FILTER_WHITELIST + )) { + + nextMode = + RSKeys.FILTER_BLACKLIST; + + } + + else { + + nextMode = + RSKeys.FILTER_ALLOW_ALL; + + } + + MachineManager.setFilterMode( + block, + nextMode + ); + + RSFilterMenu.open( + player, + block + ); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | PREVIOUS PAGE + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 0 + && event.getCurrentItem() != null + && event.getCurrentItem().getType() + == Material.ARROW) { + + if (holder.getPage() > 1) { + + holder.setPage( + holder.getPage() - 1 + ); + + RSFilterMenu.open( + player, + block, + holder.getPage() + ); + + } + + return; + + } + + /* + |-------------------------------------------------------------------------- + | NEXT PAGE + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 8 + && event.getCurrentItem() != null + && event.getCurrentItem().getType() + == Material.ARROW) { + + int highestPage = + MachineManager.getHighestFilterPage( + block + ); + + if (holder.getPage() + <= highestPage) { + + holder.setPage( + holder.getPage() + 1 + ); + + RSFilterMenu.open( + player, + block, + holder.getPage() + ); + + } + + return; + + } + + /* + |-------------------------------------------------------------------------- + | BACK + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 5) { + + RSMainMenu.open( + player, + block + ); + + return; + + } + + return; + + } + + if (event.getRawSlot() >= event.getInventory().getSize()) { + return; + } + + /* + |-------------------------------------------------------------------------- + | FILTER STORAGE + |-------------------------------------------------------------------------- + */ + + event.setCancelled(true); + + if (event.getCursor().getType() + != Material.AIR) { + + ItemStack filterItem = + event.getCursor().clone(); + + filterItem.setAmount(1); + + if (RSConversionManager.containsFilterItem( + block, + filterItem + )) { + + RSMessageUtils.info( + player, + "That item is already in the filter." + ); + + return; + + } + + event.getInventory().setItem( + event.getRawSlot(), + filterItem + ); + + MachineManager.saveFilterItem( + block, + holder.getPage(), + event.getRawSlot(), + filterItem + ); + + RSFilterMenu.open( + player, + block, + holder.getPage() + ); + + return; + + } + + event.getInventory().setItem( + event.getRawSlot(), + null + ); + + MachineManager.removeFilterItem( + block, + holder.getPage(), + event.getRawSlot() + ); + + RSFilterMenu.open( + player, + block, + holder.getPage() + ); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/listeners/RSMenuListener.java b/src/main/java/com/rsscripting/itemmagnet/listeners/RSMenuListener.java new file mode 100644 index 0000000..9910eb8 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/listeners/RSMenuListener.java @@ -0,0 +1,388 @@ +package com.rsscripting.itemmagnet.listeners; + +import com.rsscripting.itemmagnet.config.ConfigManager; +import com.rsscripting.itemmagnet.enums.MachineStatus; +import com.rsscripting.itemmagnet.managers.MachineManager; +import com.rsscripting.itemmagnet.managers.RSConversionManager; +import com.rsscripting.itemmagnet.gui.*; +import com.rsscripting.itemmagnet.managers.RSRadiusPreviewManager; +import com.rsscripting.itemmagnet.utils.RSConstants; +import com.rsscripting.itemmagnet.utils.RSMessageUtils; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; + +public class RSMenuListener + implements Listener { + + private final RSConvertListener + conversionListener; + + public RSMenuListener( + RSConvertListener conversionListener + ) { + + this.conversionListener = + conversionListener; + + } + + @EventHandler + public void onInventoryClick( + InventoryClickEvent event + ) { + + Player player = + (Player) event.getWhoClicked(); + + /* + |-------------------------------------------------------------------------- + | CONVERSION MENU + |-------------------------------------------------------------------------- + */ + + if (event.getInventory() + .getHolder() + instanceof RSMenuHolder holder + && holder.getMenuId() + .equals( + "CONVERT" + )) { + + event.setCancelled(true); + + if (event.getCurrentItem() == null) { + return; + } + + if (event.getRawSlot() == 4) { + + Block block = + conversionListener.getSelectedBlock( + player + ); + + if (block == null){ + + player.closeInventory(); + + return; + + } + + RSConversionManager.startPendingConversion( + player, + block + ); + + RSMessageUtils.info( + + player, + + "Please select a Container within " + + ConfigManager.getDefaultRadius() + + " blocks to finish conversion." + + ); + + player.closeInventory(); + + } + + return; + + } + + /* + |-------------------------------------------------------------------------- + | MAIN MENU + |-------------------------------------------------------------------------- + */ + + if (event.getInventory() + .getHolder() + instanceof RSMenuHolder holder + && holder.getMenuId() + .equals( + "MAIN" + )) { + + event.setCancelled(true); + + if (event.getCurrentItem() == null) { + return; + } + + Block block = + conversionListener.getSelectedBlock( + player + ); + + if (block == null) { + + player.closeInventory(); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | CONVERT BACK + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 2) { + + conversionListener.convertBack( + player + ); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | ADMIN MENU + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 4) { + + if (!player.isOp() + || + + !player.hasPermission( + RSConstants.ADMIN_PERMISSION + )) { + + return; + + } + + RSAdminMenu.open( + player + ); + + } + + /* + |-------------------------------------------------------------------------- + | PAUSE / RESUME + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 6) { + + boolean paused = + MachineManager.isPaused( + block + ); + + if (paused) { + + MachineStatus detectedStatus = + MachineManager.getDetectedStatus( + block + ); + + if ( + detectedStatus + != MachineStatus.PAUSED + && + detectedStatus + != MachineStatus.RUNNING + ) { + + String line1; + String line2; + + switch (detectedStatus) { + + case NO_TARGET -> { + + line1 = + "No target container assigned."; + + line2 = + "Please assign a valid Hopper, Chest, or Barrel and press resume."; + + } + + case TARGET_MISSING -> { + + line1 = + "Target container is missing."; + + line2 = + "Assign a new container and press resume."; + + } + + case TARGET_INVALID -> { + + line1 = + "Target container is invalid."; + + line2 = + "Please assign a valid Hopper, Chest, or Barrel and press resume."; + + } + + default -> { + + line1 = + "Machine is not ready."; + + line2 = + ""; + + } + + } + + RSMessageUtils.error( + player, + line1 + ); + + if (!line2.isEmpty()) { + + RSMessageUtils.error( + player, + line2 + ); + + } + + return; + + } + + MachineManager.setPaused( + block, + false + ); + + MachineManager.setStatus( + block, + "RUNNING" + ); + + } + else { + + MachineManager.setPaused( + block, + true + ); + + MachineManager.setStatus( + block, + "PAUSED" + ); + + } + + RSMainMenu.open( + player, + block + ); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | RADIUS MENU + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 10) { + + RSRadiusMenu.open( + player, + block + ); + + return; + + } + + /* +|-------------------------------------------------------------------------- +| SHOW RADIUS +|-------------------------------------------------------------------------- +*/ + + if (event.getRawSlot() == 12) { + + RSRadiusPreviewManager.startPreview( + + player, + + block + + ); + + player.closeInventory(); + + return; + + } + + /* +|-------------------------------------------------------------------------- +| TARGET CONTAINER +|-------------------------------------------------------------------------- +*/ + + if (event.getRawSlot() == 14) { + + RSConversionManager.startPendingTargetAssignment( + player, + block + ); + + RSMessageUtils.info( + + player, + + "Please select a new target Container." + + ); + + player.closeInventory(); + + return; + + } + +/* +|-------------------------------------------------------------------------- +| FILTER +|-------------------------------------------------------------------------- +*/ + + if (event.getRawSlot() == 16) { + + RSFilterMenu.open( + player, + block + ); + + return; + + } + + + + } + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/listeners/RSRadiusListener.java b/src/main/java/com/rsscripting/itemmagnet/listeners/RSRadiusListener.java new file mode 100644 index 0000000..6dd2398 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/listeners/RSRadiusListener.java @@ -0,0 +1,158 @@ +package com.rsscripting.itemmagnet.listeners; + +import com.rsscripting.itemmagnet.config.ConfigManager; +import com.rsscripting.itemmagnet.gui.RSMainMenu; +import com.rsscripting.itemmagnet.gui.RSMenuHolder; +import com.rsscripting.itemmagnet.gui.RSRadiusMenu; +import com.rsscripting.itemmagnet.managers.MachineManager; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; + +public class RSRadiusListener + implements Listener { + + private final RSConvertListener + conversionListener; + + public RSRadiusListener( + RSConvertListener conversionListener + ) { + + this.conversionListener = + conversionListener; + + } + + @EventHandler + public void onInventoryClick( + InventoryClickEvent event + ) { + + if (!(event.getWhoClicked() + instanceof Player player)) { + return; + } + + if (!(event.getInventory() + .getHolder() + instanceof RSMenuHolder holder)) { + return; + } + + if (!holder.getMenuId() + .equals( + "RADIUS" + )) { + return; + } + + event.setCancelled(true); + + if (event.getCurrentItem() == null) { + return; + } + + Block block = + conversionListener.getSelectedBlock( + player + ); + + if (block == null) { + + player.closeInventory(); + + return; + + } + + /* + |-------------------------------------------------------------------------- + | BACK + |-------------------------------------------------------------------------- + */ + + if (event.getRawSlot() == 4) { + + RSMainMenu.open( + player, + block + ); + + return; + + } + + int radius = + MachineManager.getRadius( + block + ); + + int change; + + switch (event.getRawSlot()) { + + case 10: + change = -10; + break; + + case 11: + change = -5; + break; + + case 12: + change = -1; + break; + + case 14: + change = 1; + break; + + case 15: + change = 5; + break; + + case 16: + change = 10; + break; + + default: + return; + + } + + radius += change; + + if (radius < 1) { + radius = 1; + } + + int maxRadius = + (int) ConfigManager.get() + .getDouble( + "max-radius" + ); + if (maxRadius > 0 + && radius > maxRadius) { + + radius = maxRadius; + + } + + MachineManager.setRadius( + block, + radius + ); + + RSRadiusMenu.open( + player, + block + ); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/listeners/RSUpdateNotificationListener.java b/src/main/java/com/rsscripting/itemmagnet/listeners/RSUpdateNotificationListener.java new file mode 100644 index 0000000..f8987aa --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/listeners/RSUpdateNotificationListener.java @@ -0,0 +1,42 @@ +package com.rsscripting.itemmagnet.listeners; + +import com.rsscripting.itemmagnet.utils.GitHubUpdateChecker; +import com.rsscripting.itemmagnet.utils.RSConstants; +import com.rsscripting.itemmagnet.utils.VersionUtils; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class RSUpdateNotificationListener + implements Listener { + + @EventHandler + public void onPlayerJoin( + PlayerJoinEvent event + ) { + + if (!event.getPlayer().isOp()) { + + return; + + } + + if (!GitHubUpdateChecker.isUpdateAvailable()) { + + return; + + } + + event.getPlayer().sendMessage( + + RSConstants.PREFIX + + "Update Available! Current: " + + VersionUtils.getVersion() + + " Latest: " + + GitHubUpdateChecker.getLatestVersionFound() + + ); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/managers/MachineManager.java b/src/main/java/com/rsscripting/itemmagnet/managers/MachineManager.java new file mode 100644 index 0000000..efce2d5 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/managers/MachineManager.java @@ -0,0 +1,1825 @@ +package com.rsscripting.itemmagnet.managers; + +import com.rsscripting.itemmagnet.database.DatabaseManager; +import com.rsscripting.itemmagnet.enums.MachineStatus; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.UUID; +import java.util.Base64; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ByteArrayInputStream; +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.Material; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.inventory.ItemStack; +import org.bukkit.block.Block; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.util.io.BukkitObjectOutputStream; + +public class MachineManager { + + public static boolean machineExists( + Block block + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT id + FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + LIMIT 1 + """ + ) + + ) { + + statement.setString( + 1, + block.getWorld().getName() + ); + + statement.setInt( + 2, + block.getX() + ); + + statement.setInt( + 3, + block.getY() + ); + + statement.setInt( + 4, + block.getZ() + ); + + ResultSet result = + statement.executeQuery(); + + return result.next(); + + } + + catch (Exception e) { + + logError( + "Failed to check if machine exists.", + e + ); + + return false; + + } + + } + + public static void insertMachine( + Block block + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + INSERT INTO machines ( + world, + x, + y, + z, + created_at + ) + VALUES ( + ?, ?, ?, ?, datetime('now') + ) + """ + ) + + ) { + + statement.setString( + 1, + block.getWorld().getName() + ); + + statement.setInt( + 2, + block.getX() + ); + + statement.setInt( + 3, + block.getY() + ); + + statement.setInt( + 4, + block.getZ() + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to insert machine.", + e + ); + + } + + } + + public static void deleteMachine( + Block block + ) { + + int machineId = + getMachineId( + block + ); + + try ( + + Connection connection = + DatabaseManager.getConnection() + + ) { + + if (machineId != -1) { + + try ( + + PreparedStatement filterStatement = + connection.prepareStatement( + """ + DELETE FROM machine_filters + WHERE machine_id = ? + """ + ) + + ) { + + filterStatement.setInt( + 1, + machineId + ); + + filterStatement.executeUpdate(); + + } + + } + + try ( + + PreparedStatement machineStatement = + connection.prepareStatement( + """ + DELETE FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + """ + ) + + ) { + + machineStatement.setString( + 1, + block.getWorld().getName() + ); + + machineStatement.setInt( + 2, + block.getX() + ); + + machineStatement.setInt( + 3, + block.getY() + ); + + machineStatement.setInt( + 4, + block.getZ() + ); + + machineStatement.executeUpdate(); + + } + + } + + catch (Exception e) { + + logError( + "Failed to delete machine.", + e + ); + + } + + } + + public static void setOwner( + Block block, + Player player + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + UPDATE machines + SET owner_uuid = ?, + owner_name = ? + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + """ + ) + + ) { + + statement.setString( + 1, + player.getUniqueId().toString() + ); + + statement.setString( + 2, + player.getName() + ); + + statement.setString( + 3, + block.getWorld().getName() + ); + + statement.setInt( + 4, + block.getX() + ); + + statement.setInt( + 5, + block.getY() + ); + + statement.setInt( + 6, + block.getZ() + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to set machine owner.", + e + ); + + } + + } + + public static boolean isOwner( + Block block, + Player player + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT owner_uuid + FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + LIMIT 1 + """ + ) + + ) { + + statement.setString( + 1, + block.getWorld().getName() + ); + + statement.setInt( + 2, + block.getX() + ); + + statement.setInt( + 3, + block.getY() + ); + + statement.setInt( + 4, + block.getZ() + ); + + ResultSet result = + statement.executeQuery(); + + if (!result.next()) { + + return false; + + } + + String ownerUuid = + result.getString( + "owner_uuid" + ); + + if (ownerUuid == null) { + + return false; + + } + + return UUID.fromString( + ownerUuid + ).equals( + player.getUniqueId() + ); + + } + + catch (Exception e) { + + logError( + "Failed to check machine ownership.", + e + ); + + return false; + + } + + } + + public static void setRadius( + Block block, + int radius + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + UPDATE machines + SET radius = ? + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + """ + ) + + ) { + + statement.setInt( + 1, + radius + ); + + statement.setString( + 2, + block.getWorld().getName() + ); + + statement.setInt( + 3, + block.getX() + ); + + statement.setInt( + 4, + block.getY() + ); + + statement.setInt( + 5, + block.getZ() + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to set machine radius.", + e + ); + + } + + } + + public static int getRadius( + Block block + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT radius + FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + LIMIT 1 + """ + ) + + ) { + + statement.setString( + 1, + block.getWorld().getName() + ); + + statement.setInt( + 2, + block.getX() + ); + + statement.setInt( + 3, + block.getY() + ); + + statement.setInt( + 4, + block.getZ() + ); + + ResultSet result = + statement.executeQuery(); + + if (result.next()) { + + return result.getInt( + "radius" + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to get machine radius.", + e + ); + + } + + return 5; + + } + +/* +|-------------------------------------------------------------------------- +| TARGET CONTAINER +|-------------------------------------------------------------------------- +*/ + + public static void setTargetContainer( + Block machine, + Block container + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + UPDATE machines + SET target_world = ?, + target_x = ?, + target_y = ?, + target_z = ? + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + """ + ) + + ) { + + statement.setString( + 1, + container.getWorld().getName() + ); + + statement.setInt( + 2, + container.getX() + ); + + statement.setInt( + 3, + container.getY() + ); + + statement.setInt( + 4, + container.getZ() + ); + + statement.setString( + 5, + machine.getWorld().getName() + ); + + statement.setInt( + 6, + machine.getX() + ); + + statement.setInt( + 7, + machine.getY() + ); + + statement.setInt( + 8, + machine.getZ() + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to set target container.", + e + ); + + } + + } + + public static int[] getTargetContainer( + Block machine + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT target_x, + target_y, + target_z + FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + LIMIT 1 + """ + ) + + ) { + + statement.setString( + 1, + machine.getWorld().getName() + ); + + statement.setInt( + 2, + machine.getX() + ); + + statement.setInt( + 3, + machine.getY() + ); + + statement.setInt( + 4, + machine.getZ() + ); + + ResultSet result = + statement.executeQuery(); + + if (result.next()) { + + return new int[] { + + result.getInt( + "target_x" + ), + + result.getInt( + "target_y" + ), + + result.getInt( + "target_z" + ) + + }; + + } + + } + + catch (Exception e) { + + logError( + "Failed to get target container.", + e + ); + + } + + return null; + + } + + public static void setPaused( + Block block, + boolean paused + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + UPDATE machines + SET paused = ? + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + """ + ) + + ) { + + statement.setBoolean( + 1, + paused + ); + + statement.setString( + 2, + block.getWorld().getName() + ); + + statement.setInt( + 3, + block.getX() + ); + + statement.setInt( + 4, + block.getY() + ); + + statement.setInt( + 5, + block.getZ() + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to update machine status.", + e + ); + + } + + } + + public static void setStatus( + Block block, + String status + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + UPDATE machines + SET status = ? + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + """ + ) + + ) { + + statement.setString( + 1, + status + ); + + statement.setString( + 2, + block.getWorld().getName() + ); + + statement.setInt( + 3, + block.getX() + ); + + statement.setInt( + 4, + block.getY() + ); + + statement.setInt( + 5, + block.getZ() + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to set machine status.", + e + ); + + } + + } + + public static boolean isPaused( + Block block + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT paused + FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + LIMIT 1 + """ + ) + + ) { + + statement.setString( + 1, + block.getWorld().getName() + ); + + statement.setInt( + 2, + block.getX() + ); + + statement.setInt( + 3, + block.getY() + ); + + statement.setInt( + 4, + block.getZ() + ); + + ResultSet result = + statement.executeQuery(); + + if (result.next()) { + + return result.getBoolean( + "paused" + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to check machine pause state.", + e + ); + + } + + return false; + + } + + public static String getStoredStatus( + Block block + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT status + FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + LIMIT 1 + """ + ) + + ) { + + statement.setString( + 1, + block.getWorld().getName() + ); + + statement.setInt( + 2, + block.getX() + ); + + statement.setInt( + 3, + block.getY() + ); + + statement.setInt( + 4, + block.getZ() + ); + + ResultSet result = + statement.executeQuery(); + + if (result.next()) { + + return result.getString( + "status" + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to get stored machine status.", + e + ); + + } + + return "RUNNING"; + + } + + /* +|-------------------------------------------------------------------------- +| ALL MACHINES +|-------------------------------------------------------------------------- +*/ + + public static Map getAllMachines() { + + Map machines = + new HashMap<>(); + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT id, + world, + x, + y, + z + FROM machines + """ + ) + + ) { + + ResultSet result = + statement.executeQuery(); + + while (result.next()) { + + String worldName = + result.getString( + "world" + ); + + if (Bukkit.getWorld( + worldName + ) == null) { + + continue; + + } + + Location location = + new Location( + + Bukkit.getWorld( + worldName + ), + + result.getInt( + "x" + ), + + result.getInt( + "y" + ), + + result.getInt( + "z" + ) + + ); + + machines.put( + + location, + + result.getInt( + "id" + ) + + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to load machines.", + e + ); + + } + + return machines; + + } + + public static void setFilterMode( + Block block, + String filterMode + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + UPDATE machines + SET filter_mode = ? + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + """ + ) + + ) { + + statement.setString( + 1, + filterMode + ); + + statement.setString( + 2, + block.getWorld().getName() + ); + + statement.setInt( + 3, + block.getX() + ); + + statement.setInt( + 4, + block.getY() + ); + + statement.setInt( + 5, + block.getZ() + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to set filter mode.", + e + ); + + } + + } + + public static String getFilterMode( + Block block + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT filter_mode + FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + LIMIT 1 + """ + ) + + ) { + + statement.setString( + 1, + block.getWorld().getName() + ); + + statement.setInt( + 2, + block.getX() + ); + + statement.setInt( + 3, + block.getY() + ); + + statement.setInt( + 4, + block.getZ() + ); + + ResultSet result = + statement.executeQuery(); + + if (result.next()) { + + return result.getString( + "filter_mode" + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to get filter mode.", + e + ); + + } + + return "ALLOW_ALL"; + + } + + public static int getMachineId( + Block block + ) { + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT id + FROM machines + WHERE world = ? + AND x = ? + AND y = ? + AND z = ? + LIMIT 1 + """ + ) + + ) { + + statement.setString( + 1, + block.getWorld().getName() + ); + + statement.setInt( + 2, + block.getX() + ); + + statement.setInt( + 3, + block.getY() + ); + + statement.setInt( + 4, + block.getZ() + ); + + ResultSet result = + statement.executeQuery(); + + if (result.next()) { + + return result.getInt( + "id" + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to get machine id.", + e + ); + + } + + return -1; + + } + + public static void removeFilterItem( + Block block, + int page, + int slot + ) { + + int machineId = + getMachineId( + block + ); + + if (machineId == -1) { + + return; + + } + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + DELETE FROM machine_filters + WHERE machine_id = ? + AND page = ? + AND slot = ? + """ + ) + + ) { + + statement.setInt( + 1, + machineId + ); + + statement.setInt( + 2, + page + ); + + statement.setInt( + 3, + slot + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to remove filter item.", + e + ); + + } + + } + + public static String serializeItemStack( + ItemStack itemStack + ) { + + try { + + ByteArrayOutputStream outputStream = + new ByteArrayOutputStream(); + + BukkitObjectOutputStream dataOutput = + new BukkitObjectOutputStream( + outputStream + ); + + dataOutput.writeObject( + itemStack + ); + + dataOutput.close(); + + return Base64.getEncoder() + .encodeToString( + outputStream.toByteArray() + ); + + } + + catch (IOException e) { + + logError( + "Failed to serialize item stack.", + e + ); + + } + + return null; + + } + + public static void saveFilterItem( + Block block, + int page, + int slot, + ItemStack itemStack + ) { + + int machineId = + getMachineId( + block + ); + + if (machineId == -1) { + + return; + + } + + removeFilterItem( + block, + page, + slot + ); + + String serializedItem = + serializeItemStack( + itemStack + ); + + if (serializedItem == null) { + + return; + + } + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + INSERT INTO machine_filters + ( + machine_id, + page, + slot, + item_data + ) + VALUES + ( + ?, ?, ?, ? + ) + """ + ) + + ) { + + statement.setInt( + 1, + machineId + ); + + statement.setInt( + 2, + page + ); + + statement.setInt( + 3, + slot + ); + + statement.setString( + 4, + serializedItem + ); + + statement.executeUpdate(); + + } + + catch (Exception e) { + + logError( + "Failed to save filter item.", + e + ); + + } + + } + + public static ItemStack deserializeItemStack( + String serializedItem + ) { + + try { + + byte[] data = + Base64.getDecoder() + .decode( + serializedItem + ); + + BukkitObjectInputStream inputStream = + new BukkitObjectInputStream( + new ByteArrayInputStream( + data + ) + ); + + ItemStack itemStack = + (ItemStack) inputStream.readObject(); + + inputStream.close(); + + return itemStack; + + } + + catch (Exception e) { + + logError( + "Failed to deserialize item stack.", + e + ); + + } + + return null; + + } + + public static Map getFilterItems( + Block block, + int page + ) { + + Map items = + new HashMap<>(); + + int machineId = + getMachineId( + block + ); + + if (machineId == -1) { + + return items; + + } + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT slot, item_data + FROM machine_filters + WHERE machine_id = ? + AND page = ? + """ + ) + + ) { + + statement.setInt( + 1, + machineId + ); + + statement.setInt( + 2, + page + ); + + ResultSet result = + statement.executeQuery(); + + while (result.next()) { + + items.put( + + result.getInt( + "slot" + ), + + deserializeItemStack( + result.getString( + "item_data" + ) + ) + + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to load filter items.", + e + ); + + } + + return items; + + } + + public static Map getAllFilterItems( + Block block + ) { + + Map items = + new HashMap<>(); + + int machineId = + getMachineId( + block + ); + + if (machineId == -1) { + + return items; + + } + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT page, + slot, + item_data + FROM machine_filters + WHERE machine_id = ? + """ + ) + + ) { + + statement.setInt( + 1, + machineId + ); + + ResultSet result = + statement.executeQuery(); + + while (result.next()) { + + int page = + result.getInt( + "page" + ); + + int slot = + result.getInt( + "slot" + ); + + items.put( + + (page * 1000) + slot, + + deserializeItemStack( + result.getString( + "item_data" + ) + ) + + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to retrieve filter items.", + e + ); + + } + + return items; + + } + + public static int getHighestFilterPage( + Block block + ) { + + int machineId = + getMachineId( + block + ); + + if (machineId == -1) { + + return 1; + + } + + try ( + + Connection connection = + DatabaseManager.getConnection(); + + PreparedStatement statement = + connection.prepareStatement( + """ + SELECT MAX(page) + FROM machine_filters + WHERE machine_id = ? + """ + ) + + ) { + + statement.setInt( + 1, + machineId + ); + + ResultSet result = + statement.executeQuery(); + + if (result.next()) { + + int page = + result.getInt( + 1 + ); + + return Math.max( + page, + 1 + ); + + } + + } + + catch (Exception e) { + + logError( + "Failed to determine highest filter page.", + e + ); + + } + + return 1; + + } + + /* +|-------------------------------------------------------------------------- +| CONSOLE ERROR +|-------------------------------------------------------------------------- +*/ + + public static void logError( + String message, + Exception exception + ) { + + org.bukkit.Bukkit.getLogger().log( + + java.util.logging.Level.SEVERE, + + "[RS-ItemMagnet] " + message, + + exception + + ); + + } + + public static MachineStatus getDetectedStatus( + Block machineBlock + ) { + + int[] containerCoordinates = + getTargetContainer( + machineBlock + ); + + if (containerCoordinates == null) { + + return MachineStatus.NO_TARGET; + + } + + Block containerBlock = + machineBlock.getWorld() + .getBlockAt( + + containerCoordinates[0], + containerCoordinates[1], + containerCoordinates[2] + + ); + + if (containerBlock.getType() + == Material.AIR) { + + return MachineStatus.TARGET_MISSING; + + } + + Material type = + containerBlock.getType(); + + if ( + type != Material.HOPPER + && + type != Material.CHEST + && + type != Material.BARREL + ) { + + return MachineStatus.TARGET_INVALID; + + } + + return MachineStatus.RUNNING; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/managers/PendingConversion.java b/src/main/java/com/rsscripting/itemmagnet/managers/PendingConversion.java new file mode 100644 index 0000000..b1fbac4 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/managers/PendingConversion.java @@ -0,0 +1,36 @@ +package com.rsscripting.itemmagnet.managers; + +import org.bukkit.block.Block; + +public class PendingConversion { + + private final Block lodestone; + private final long expirationTime; + + public PendingConversion( + Block lodestone, + long expirationTime + ) { + + this.lodestone = + lodestone; + + this.expirationTime = + expirationTime; + + } + + public Block getLodestone() { + + return lodestone; + + } + + public boolean isExpired() { + + return System.currentTimeMillis() + > expirationTime; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/managers/PendingTargetAssignment.java b/src/main/java/com/rsscripting/itemmagnet/managers/PendingTargetAssignment.java new file mode 100644 index 0000000..f66d3a3 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/managers/PendingTargetAssignment.java @@ -0,0 +1,34 @@ +package com.rsscripting.itemmagnet.managers; + +import org.bukkit.block.Block; + +public class PendingTargetAssignment { + + private final Block machine; + + private final long expirationTime; + + public PendingTargetAssignment( + Block machine, + long expirationTime + ) { + + this.machine = machine; + this.expirationTime = expirationTime; + + } + + public Block getMachine() { + + return machine; + + } + + public boolean isExpired() { + + return System.currentTimeMillis() + > expirationTime; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/managers/RSConversionManager.java b/src/main/java/com/rsscripting/itemmagnet/managers/RSConversionManager.java new file mode 100644 index 0000000..dc81742 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/managers/RSConversionManager.java @@ -0,0 +1,157 @@ +package com.rsscripting.itemmagnet.managers; + +import com.rsscripting.itemmagnet.config.ConfigManager; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.block.Block; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class RSConversionManager { + + private static final Map + pendingConversions = + new HashMap<>(); + + private static final Map + pendingTargetAssignments = + new HashMap<>(); + + public static boolean containsFilterItem( + Block block, + ItemStack item + ) { + + Map filterItems = + MachineManager.getAllFilterItems( + block + ); + + for (ItemStack filterItem + : filterItems.values()) { + + if (filterItem == null) { + continue; + } + + if (filterItem.isSimilar( + item + )) { + + return true; + + } + + } + + return false; + + } + + /* +|-------------------------------------------------------------------------- +| PENDING CONVERSION +|-------------------------------------------------------------------------- +*/ + + public static void startPendingConversion( + Player player, + Block lodestone + ) { + + long timeoutMillis = + ConfigManager.getConversionTimeout() + * 1000L; + + pendingConversions.put( + + player.getUniqueId(), + + new PendingConversion( + + lodestone, + + System.currentTimeMillis() + + timeoutMillis + + ) + + ); + + } + + public static PendingConversion getPendingConversion( + Player player + ) { + + return pendingConversions.get( + player.getUniqueId() + ); + + } + + public static void removePendingConversion( + Player player + ) { + + pendingConversions.remove( + player.getUniqueId() + ); + + } + + /* +|-------------------------------------------------------------------------- +| PENDING TARGET ASSIGNMENT +|-------------------------------------------------------------------------- +*/ + + public static void startPendingTargetAssignment( + Player player, + Block lodestone + ) { + + long timeoutMillis = + ConfigManager.getConversionTimeout() + * 1000L; + + pendingTargetAssignments.put( + + player.getUniqueId(), + + new PendingTargetAssignment( + + lodestone, + + System.currentTimeMillis() + + timeoutMillis + + ) + + ); + + } + + public static PendingTargetAssignment getPendingTargetAssignment( + Player player + ) { + + return pendingTargetAssignments.get( + player.getUniqueId() + ); + + } + + public static void removePendingTargetAssignment( + Player player + ) { + + pendingTargetAssignments.remove( + player.getUniqueId() + ); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/managers/RSRadiusPreviewManager.java b/src/main/java/com/rsscripting/itemmagnet/managers/RSRadiusPreviewManager.java new file mode 100644 index 0000000..e19ed7e --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/managers/RSRadiusPreviewManager.java @@ -0,0 +1,66 @@ +package com.rsscripting.itemmagnet.managers; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class RSRadiusPreviewManager { + + private static final Map + previews = + new HashMap<>(); + + public static void startPreview( + Player player, + Block block + ) { + + previews.put( + + player.getUniqueId(), + + new PreviewData( + + block, + + System.currentTimeMillis() + + 30000 + + ) + + ); + + } + + public static PreviewData getPreview( + Player player + ) { + + return previews.get( + player.getUniqueId() + ); + + } + + public static void removePreview( + Player player + ) { + + previews.remove( + player.getUniqueId() + ); + + } + + public record PreviewData( + + Block block, + + long expireTime + + ) {} + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/tasks/RSErrorTask.java b/src/main/java/com/rsscripting/itemmagnet/tasks/RSErrorTask.java new file mode 100644 index 0000000..eab1b69 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/tasks/RSErrorTask.java @@ -0,0 +1,62 @@ +package com.rsscripting.itemmagnet.tasks; + +import com.rsscripting.itemmagnet.managers.MachineManager; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Map; + +public class RSErrorTask extends BukkitRunnable { + + @Override + public void run() { + + Map machines = + MachineManager.getAllMachines(); + + for ( + Location machineLocation + : machines.keySet() + ) { + + Block machineBlock = + machineLocation.getBlock(); + + String status = + MachineManager.getStoredStatus( + machineBlock + ); + + if ( + !status.startsWith( + "ERROR_" + ) + ) { + + continue; + + } + + machineLocation.getWorld() + .spawnParticle( + + Particle.ANGRY_VILLAGER, + + machineLocation.clone() + .add( + 0.5, + 0.75, + 0.5 + ), + + 1 + + ); + + } + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/tasks/RSItemMagnetTask.java b/src/main/java/com/rsscripting/itemmagnet/tasks/RSItemMagnetTask.java new file mode 100644 index 0000000..c7a4896 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/tasks/RSItemMagnetTask.java @@ -0,0 +1,255 @@ +package com.rsscripting.itemmagnet.tasks; + +import com.rsscripting.itemmagnet.config.ConfigManager; +import com.rsscripting.itemmagnet.enums.MachineStatus; +import com.rsscripting.itemmagnet.managers.MachineManager; +import com.rsscripting.itemmagnet.managers.RSConversionManager; +import org.bukkit.Location; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.entity.Item; +import org.bukkit.block.Block; +import org.bukkit.Particle; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class RSItemMagnetTask + extends BukkitRunnable { + + @Override + public void run() { + + Map machines = + MachineManager.getAllMachines(); + + for (Location machineLocation + : machines.keySet()) { + + if ( + MachineManager.isPaused( + machineLocation.getBlock() + ) + ) { + + continue; + + } + + int[] targetCoordinates = + MachineManager.getTargetContainer( + machineLocation.getBlock() + ); + + if (targetCoordinates == null) { + continue; + } + + if (!machineLocation.getWorld() + .isChunkLoaded( + targetCoordinates[0] >> 4, + targetCoordinates[2] >> 4 + )) { + + continue; + + } + + MachineStatus detectedStatus = + MachineManager.getDetectedStatus( + machineLocation.getBlock() + ); + + if ( + detectedStatus + == MachineStatus.TARGET_MISSING + ) { + + MachineManager.setPaused( + machineLocation.getBlock(), + true + ); + + MachineManager.setStatus( + machineLocation.getBlock(), + "ERROR_TARGET_MISSING" + ); + + continue; + + } + + if ( + detectedStatus + == MachineStatus.TARGET_INVALID + ) { + + MachineManager.setPaused( + machineLocation.getBlock(), + true + ); + + MachineManager.setStatus( + machineLocation.getBlock(), + "ERROR_TARGET_INVALID" + ); + + continue; + + } + + if ( + detectedStatus + == MachineStatus.NO_TARGET + ) { + + MachineManager.setPaused( + machineLocation.getBlock(), + true + ); + + MachineManager.setStatus( + machineLocation.getBlock(), + "ERROR_NO_TARGET" + ); + + continue; + + } + + int radius = + MachineManager.getRadius( + machineLocation.getBlock() + ); + + int yDistance = + ConfigManager.getItemYDistance(); + + Collection nearbyItems; + + if (yDistance <= 0) { + + nearbyItems = + machineLocation.getWorld() + .getNearbyEntitiesByType( + Item.class, + machineLocation, + radius + ); + + } + else { + + nearbyItems = + machineLocation.getWorld() + .getNearbyEntitiesByType( + Item.class, + machineLocation, + radius, + yDistance, + radius + ); + + } + + Location containerLocation = + new Location( + + machineLocation.getWorld(), + + targetCoordinates[0] + 0.5, + targetCoordinates[1] + 0.5, + targetCoordinates[2] + 0.5 + + ); + + for (Item item : nearbyItems) { + + if (item.getTicksLived() < 40) { + continue; + } + + String filterMode = + MachineManager.getFilterMode( + machineLocation.getBlock() + ); + + boolean inFilter = + RSConversionManager.containsFilterItem( + machineLocation.getBlock(), + item.getItemStack() + ); + + if (filterMode.equalsIgnoreCase( + "WHITELIST" + ) && !inFilter) { + + continue; + + } + + if (filterMode.equalsIgnoreCase( + "BLACKLIST" + ) && inFilter) { + + continue; + + } + + Block containerBlock = + containerLocation.getBlock(); + + if (!(containerBlock.getState() + instanceof InventoryHolder container)) { + + continue; + + } + + Inventory inventory = + container.getInventory(); + + ItemStack stack = + item.getItemStack(); + + HashMap leftovers = + inventory.addItem(stack); + + if (leftovers.isEmpty()) { + + item.getWorld().spawnParticle( + Particle.ELECTRIC_SPARK, + item.getLocation(), + 10, + 0.2, + 0.2, + 0.2, + 0.05 + ); + + item.remove(); + + } + else { + + ItemStack remaining = + leftovers.values() + .iterator() + .next(); + + item.setItemStack( + remaining + ); + + } + + } + + } + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/tasks/RSRadiusPreviewTask.java b/src/main/java/com/rsscripting/itemmagnet/tasks/RSRadiusPreviewTask.java new file mode 100644 index 0000000..17eeadd --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/tasks/RSRadiusPreviewTask.java @@ -0,0 +1,235 @@ +package com.rsscripting.itemmagnet.tasks; + +import com.rsscripting.itemmagnet.managers.MachineManager; +import com.rsscripting.itemmagnet.managers.RSRadiusPreviewManager; +import com.rsscripting.itemmagnet.config.ConfigManager; +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +public class RSRadiusPreviewTask + extends BukkitRunnable { + + @Override + public void run() { + + for (Player player + : org.bukkit.Bukkit.getOnlinePlayers()) { + + RSRadiusPreviewManager.PreviewData + preview = + RSRadiusPreviewManager + .getPreview( + player + ); + + if (preview == null) { + continue; + } + + if ( + System.currentTimeMillis() + >= preview.expireTime() + ) { + + RSRadiusPreviewManager + .removePreview( + player + ); + + continue; + + } + + Block block = + preview.block(); + + int radius = + MachineManager.getRadius( + block + ); + + Location center = + block.getLocation() + .clone() + .add( + 0.5, + 0.05, + 0.5 + ); + + int yDistance = + ConfigManager.getItemYDistance(); + + if (yDistance <= 0) { + + yDistance = + radius; + + } + + double middleY = + center.getY(); + + double topY = + center.getY() + + yDistance; + + double bottomY = + center.getY() + - yDistance; + + Particle.DustOptions dust = + new Particle.DustOptions( + Color.RED, + 1.5F + ); + + drawRing( + player, + center, + radius, + middleY, + dust + ); + + drawRing( + player, + center, + radius, + topY, + dust + ); + + drawRing( + player, + center, + radius, + bottomY, + dust + ); + } + + } + + private void drawRing( + + Player player, + + Location center, + + int radius, + + double y, + + Particle.DustOptions dust + + ) { + + double offsetY = + y - center.getY(); + + for ( + int x = -radius; + x <= radius; + x++ + ) { + + spawnParticle( + + player, + + center.clone().add( + x, + offsetY, + radius - 0.5 + ), + + dust + + ); + + spawnParticle( + + player, + + center.clone().add( + x, + offsetY, + -(radius - 0.5) + ), + + dust + + ); + + } + + for ( + int z = -radius; + z <= radius; + z++ + ) { + + spawnParticle( + + player, + + center.clone().add( + radius - 0.5, + offsetY, + z + ), + + dust + + ); + + spawnParticle( + + player, + + center.clone().add( + -radius, + offsetY, + z + ), + + dust + + ); + + } + + } + + private void spawnParticle( + Player player, + Location location, + Particle.DustOptions dust + ) { + + player.spawnParticle( + + Particle.DUST, + + location, + + 1, + + 0, + 0, + 0, + + 0, + + dust + + ); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/utils/GitHubUpdateChecker.java b/src/main/java/com/rsscripting/itemmagnet/utils/GitHubUpdateChecker.java new file mode 100644 index 0000000..95359eb --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/utils/GitHubUpdateChecker.java @@ -0,0 +1,273 @@ +package com.rsscripting.itemmagnet.utils; + +import com.rsscripting.itemmagnet.RSItemMagnet; +import org.bukkit.Bukkit; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URI; + +public class GitHubUpdateChecker { + + /* + |-------------------------------------------------------------------------- + | UPDATE STATUS + |-------------------------------------------------------------------------- + */ + + private static String latestVersion = null; + private static boolean updateAvailable = false; + + private static String updateStatus = + "Checking..."; + + /* + |-------------------------------------------------------------------------- + | CHECK FOR UPDATES + |-------------------------------------------------------------------------- + */ + + public static void checkForUpdates() { + + Bukkit.getScheduler().runTaskAsynchronously( + + RSItemMagnet.getInstance(), + + () -> { + + try { + + String apiURL = + "https://raw.githubusercontent.com/" + + RSConstants.GITHUB_USER + + "/" + + RSConstants.GITHUB_REPOSITORY + + "/master/pom.xml"; + + HttpURLConnection connection = + (HttpURLConnection) + URI.create(apiURL) + .toURL() + .openConnection(); + + connection.setRequestMethod("GET"); + + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + connection.getInputStream() + ) + ); + + StringBuilder response = + new StringBuilder(); + + String line; + + while ((line = reader.readLine()) != null) { + + response.append(line); + + } + + reader.close(); + + String pom = + response.toString(); + + latestVersion = + pom.split("")[1] + .split("")[0] + .trim(); + + String currentVersion = + VersionUtils.getVersion(); + + /* + |-------------------------------------------------------------------------- + | UPDATE AVAILABLE + |-------------------------------------------------------------------------- + */ + + if (latestVersion == null) { + + updateStatus = + "Unable To Check"; + + } + else if ( + compareVersions( + latestVersion, + currentVersion + ) > 0 + ) { + + updateAvailable = + true; + + updateStatus = + "Update Available"; + + RSItemMagnet.getInstance() + .getLogger() + .warning( + + "Update available! Current: " + + currentVersion + + " Latest: " + + latestVersion + + ); + + } + + /* + |-------------------------------------------------------------------------- + | UP TO DATE + |-------------------------------------------------------------------------- + */ + + else { + + updateAvailable = + false; + + updateStatus = + "Up To Date"; + + Bukkit.getConsoleSender().sendMessage( + + RSConstants.PREFIX + + "RS-ItemMagnet is up to date." + + ); + + } + + } + + catch (Exception exception) { + + latestVersion = null; + + updateStatus = + "Unable To Check"; + + Bukkit.getConsoleSender().sendMessage( + + RSConstants.PREFIX + + "Unable to check for updates." + + ); + + } + + } + + ); + + } + + /* + |-------------------------------------------------------------------------- + | GET LATEST VERSION + |-------------------------------------------------------------------------- + */ + + public static String getLatestVersion() { + + return latestVersion; + + } + + /* + |-------------------------------------------------------------------------- + | GET UPDATE STATUS + |-------------------------------------------------------------------------- + */ + + public static String getUpdateStatus() { + + return updateStatus; + + } + +/* +|-------------------------------------------------------------------------- +| UPDATE AVAILABLE +|-------------------------------------------------------------------------- +*/ + + public static boolean isUpdateAvailable() { + + return updateAvailable; + + } + +/* +|-------------------------------------------------------------------------- +| GET LATEST VERSION FOUND +|-------------------------------------------------------------------------- +*/ + + public static String getLatestVersionFound() { + + return latestVersion; + + } + + /* +|-------------------------------------------------------------------------- +| VERSION COMPARISON +|-------------------------------------------------------------------------- +*/ + + private static int compareVersions( + String version1, + String version2 + ) { + + String[] v1 = + version1.split("\\."); + + String[] v2 = + version2.split("\\."); + + int length = + Math.max( + v1.length, + v2.length + ); + + for (int i = 0; i < length; i++) { + + int num1 = + i < v1.length + ? Integer.parseInt(v1[i]) + : 0; + + int num2 = + i < v2.length + ? Integer.parseInt(v2[i]) + : 0; + + if (num1 < num2) { + + return -1; + + } + + if (num1 > num2) { + + return 1; + + } + + } + + return 0; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/utils/RSConstants.java b/src/main/java/com/rsscripting/itemmagnet/utils/RSConstants.java new file mode 100644 index 0000000..02487b3 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/utils/RSConstants.java @@ -0,0 +1,83 @@ +package com.rsscripting.itemmagnet.utils; + +import org.bukkit.Material; + +public class RSConstants { + + /* + |-------------------------------------------------------------------------- + | PLUGIN INFO + |-------------------------------------------------------------------------- + */ + + public static final String PLUGIN_NAME = + "RS-ItemMagnet"; + + public static final String PREFIX = + "§7[§cRS-ItemMagnet§7] §r"; + + public static final String COMMAND_ALIAS = + "rsitemmagnet"; + + /* + |-------------------------------------------------------------------------- + | GITHUB + |-------------------------------------------------------------------------- + */ + + public static final String GITHUB_USER = + "RS-Scripting"; + + public static final String GITHUB_REPOSITORY = + "RS-ItemMagnet"; + + /* + |-------------------------------------------------------------------------- + | PERMISSIONS + |-------------------------------------------------------------------------- + */ + + public static final String ADMIN_PERMISSION = + "rsitemmagnet.admin"; + + public static final String BYPASS_PERMISSION = + "rsitemmagnet.bypass"; + + /* + |-------------------------------------------------------------------------- + | REQUIRED CONVERSION ITEMS + |-------------------------------------------------------------------------- + */ + + public static final Material REQUIRED_HELD_ITEM = + Material.STICK; + + /* + |-------------------------------------------------------------------------- + | REQUIRED TARGET ENTITY + |-------------------------------------------------------------------------- + */ + + public static final Material + REQUIRED_TARGET_BLOCK = + Material.LODESTONE; + + /* + |-------------------------------------------------------------------------- + | MENU SETTINGS + |-------------------------------------------------------------------------- + */ + + public static final Material MENU_FILLER_MATERIAL = + Material.WHITE_STAINED_GLASS_PANE; + + /* + |-------------------------------------------------------------------------- + | Version + |-------------------------------------------------------------------------- + */ + + public static final String VERSION = + "1.0.0"; + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/utils/RSKeys.java b/src/main/java/com/rsscripting/itemmagnet/utils/RSKeys.java new file mode 100644 index 0000000..fe8cebe --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/utils/RSKeys.java @@ -0,0 +1,20 @@ +package com.rsscripting.itemmagnet.utils; + +public class RSKeys { + + /* + |-------------------------------------------------------------------------- + | FILTER MODES + |-------------------------------------------------------------------------- + */ + + public static final String FILTER_ALLOW_ALL = + "ALLOW_ALL"; + + public static final String FILTER_WHITELIST = + "WHITELIST"; + + public static final String FILTER_BLACKLIST = + "BLACKLIST"; + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/utils/RSMenuUtils.java b/src/main/java/com/rsscripting/itemmagnet/utils/RSMenuUtils.java new file mode 100644 index 0000000..09e4a8f --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/utils/RSMenuUtils.java @@ -0,0 +1,122 @@ +package com.rsscripting.itemmagnet.utils; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import net.kyori.adventure.text.Component; + +import java.util.List; + +public class RSMenuUtils { + + /* + |-------------------------------------------------------------------------- + | CREATE FILLER PANE + |-------------------------------------------------------------------------- + */ + + public static ItemStack createFillerPane() { + + ItemStack item = + new ItemStack( + RSConstants.MENU_FILLER_MATERIAL + ); + + ItemMeta meta = + item.getItemMeta(); + + if (meta != null) { + + meta.displayName( + Component.empty() + ); + + item.setItemMeta(meta); + + } + + return item; + + } + + /* + |-------------------------------------------------------------------------- + | CREATE MENU ITEM + |-------------------------------------------------------------------------- + */ + + public static ItemStack createMenuItem( + + Material material, + + String name + + ) { + + ItemStack item = + new ItemStack(material); + + ItemMeta meta = + item.getItemMeta(); + + if (meta != null) { + + meta.displayName( + Component.text( + name + ) + ); + + item.setItemMeta(meta); + + } + + return item; + + } + + /* + |-------------------------------------------------------------------------- + | CREATE MENU ITEM WITH LORE + |-------------------------------------------------------------------------- + */ + + public static ItemStack createMenuItem( + + Material material, + + String name, + + List lore + + ) { + + ItemStack item = + new ItemStack(material); + + ItemMeta meta = + item.getItemMeta(); + + if (meta != null) { + + meta.displayName( + Component.text( + name + ) + ); + + meta.lore( + lore.stream() + .map(Component::text) + .toList() + ); + + item.setItemMeta(meta); + + } + + return item; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/utils/RSMessageUtils.java b/src/main/java/com/rsscripting/itemmagnet/utils/RSMessageUtils.java new file mode 100644 index 0000000..463a93e --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/utils/RSMessageUtils.java @@ -0,0 +1,171 @@ +package com.rsscripting.itemmagnet.utils; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.entity.Player; + +public class RSMessageUtils { + + /* + |-------------------------------------------------------------------------- + | PREFIX + |-------------------------------------------------------------------------- + */ + + private static final Component PREFIX = + + Component.text("[") + .color(NamedTextColor.GRAY) + + .append( + Component.text( + "RS-ItemMagnet" + ).color( + NamedTextColor.RED + ) + ) + + .append( + Component.text("] ") + .color( + NamedTextColor.GRAY + ) + ); + + /* + |-------------------------------------------------------------------------- + | SUCCESS + |-------------------------------------------------------------------------- + */ + + public static void success( + Player player, + String message + ) { + + player.sendMessage( + + PREFIX.append( + + Component.text( + message + ).color( + NamedTextColor.GREEN + ) + + ) + + ); + + } + + /* + |-------------------------------------------------------------------------- + | WARNING + |-------------------------------------------------------------------------- + */ + @SuppressWarnings("unused") + public static void warning( + Player player, + String message + ) { + + player.sendMessage( + + PREFIX.append( + + Component.text( + message + ).color( + NamedTextColor.YELLOW + ) + + ) + + ); + + } + + /* + |-------------------------------------------------------------------------- + | ERROR + |-------------------------------------------------------------------------- + */ + + public static void error( + Player player, + String message + ) { + + player.sendMessage( + + PREFIX.append( + + Component.text( + message + ).color( + NamedTextColor.RED + ) + + ) + + ); + + } + + /* + |-------------------------------------------------------------------------- + | INFO + |-------------------------------------------------------------------------- + */ + + public static void info( + Player player, + String message + ) { + + player.sendMessage( + + PREFIX.append( + + Component.text( + message + ).color( + NamedTextColor.AQUA + ) + + ) + + ); + + } + + /* + |-------------------------------------------------------------------------- + | ADMIN + |-------------------------------------------------------------------------- + */ + + @SuppressWarnings("unused") + public static void admin( + Player player, + String message + ) { + + player.sendMessage( + + PREFIX.append( + + Component.text( + message + ).color( + NamedTextColor.GOLD + ) + + ) + + ); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/itemmagnet/utils/VersionUtils.java b/src/main/java/com/rsscripting/itemmagnet/utils/VersionUtils.java new file mode 100644 index 0000000..0113132 --- /dev/null +++ b/src/main/java/com/rsscripting/itemmagnet/utils/VersionUtils.java @@ -0,0 +1,20 @@ +package com.rsscripting.itemmagnet.utils; + +import com.rsscripting.itemmagnet.RSItemMagnet; + +public class VersionUtils { + + /* + |-------------------------------------------------------------------------- + | GET INSTALLED VERSION + |-------------------------------------------------------------------------- + */ + + public static String getVersion() { + + return RSItemMagnet.getInstance() + .getDescription() + .getVersion(); + } + +} \ No newline at end of file diff --git a/src/main/resources/Notes b/src/main/resources/Notes new file mode 100644 index 0000000..bca5275 --- /dev/null +++ b/src/main/resources/Notes @@ -0,0 +1,9 @@ +2.0.0 + FIXED + * Issue causing players to be unable to open gates, doors, etc when assigning destination + chests, hoppers, and barrels. + + ADDED + * Y distance setting to config allowing server admin to set vertical distance for magnets to scan. + Default: 0 (infinite) + * Trigger in magnet menu to show that magnets radius using a sparkle outline. \ 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..758c1f6 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,32 @@ +# +# ---------------------------------------- +# RS-ItemMagnet Configuration +# ---------------------------------------- +# + +# Default scan radius for RS-ItemMagnet to scan for dropped items. +default-radius: 10 + +# Maximum radius players may set their RS-ItemMagnets to. +# 0 = Unlimited. +max-radius: 25 + +# Seconds allowed to select a target Container. +conversion-timeout: 30 + +# Distance in blocks ABOVE and BELOW the magnet +# that items may be collected from. +# +# 0 = Unlimited (legacy behavior) +# >0 = Limit vertical collection distance. +item-y-distance: 0 + +# Maximum distance in blocks a target container may be from the ItemMagnet. +# 0 = Unlimited. +max-target-distance: 30 + +# Whether OP players bypass permissions checks +admin-bypass: true + +# Enable debug logging. +debug-mode: false \ 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..cc0b2a0 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,21 @@ +name: RS-ItemMagnet +version: ${project.version} +main: com.rsscripting.itemmagnet.RSItemMagnet +api-version: '1.21' + +author: RSScripting +description: RS Item magnet + +commands: + rsim: + description: Main plugin command + usage: /rsbt + +permissions: + rsim.admin: + description: Admin access + default: op + + rsim.bypass: + description: Bypass ownership protection + default: op \ No newline at end of file