Initial RS-ItemMagnet repository

This commit is contained in:
BigPappa committed 2026-08-13 02:14:48 -05:00
commit a642ec43e7
47 files changed
+7344

No files matched your search

+36
View File
@@ -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
+21
View File
@@ -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.
+226
View File
@@ -0,0 +1,226 @@
<p align="center">
<h1 align="center">RS-ItemMagnet</h1>
<p align="center">
Automated Item Collection for Modern Minecraft Servers
</p>
<p align="center">
![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)
</p>
---
## 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
<img width="346" height="131" alt="27b8e481-1df0-465c-8fa1-2530636fa19f" src="https://github.com/user-attachments/assets/7446e87e-a77e-466f-8177-98c18b572011" />
>
### Filter Menu
<img width="349" height="267" alt="cde741b8-51ce-4915-9919-1442c2fb1020" src="https://github.com/user-attachments/assets/8169f346-91f3-403b-bc2e-0cb82c40c0d8" />
### Radius Configuration
<img width="349" height="131" alt="1f0d7b5f-5155-4af4-88f4-cd20e4ad9ca6" src="https://github.com/user-attachments/assets/9c2583a1-15b5-4dc5-a9f6-af627e16f790" />
---
# 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.
---
<p align="center">
Built by RSScripting
</p>
<p align="center">
Generate • Collect • Transport • Store
</p>
+21
View File
@@ -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.
+91
View File
@@ -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.
+42
View File
@@ -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.
+24
View File
@@ -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.
+21
View File
@@ -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.
+45
View File
@@ -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
+68
View File
@@ -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.
+52
View File
@@ -0,0 +1,52 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rsscripting</groupId>
<artifactId>RS-ItemMagnet</artifactId>
<version>2.0.1</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.6-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.50.3.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<finalName>${project.artifactId}-${project.version}</finalName>
</build>
</project>
@@ -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;
}
}
@@ -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;
}
}
@@ -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<String> onTabComplete(
@NotNull CommandSender sender,
@NotNull Command command,
@NotNull String label,
@NotNull String @NotNull [] args
) {
List<String> completions =
new ArrayList<>();
/*
|--------------------------------------------------------------------------
| FIRST ARGUMENT
|--------------------------------------------------------------------------
*/
if (args.length == 1) {
completions.add("reload");
completions.add("version");
}
return completions;
}
}
@@ -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
);
}
}
@@ -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
);
}
}
@@ -0,0 +1,15 @@
package com.rsscripting.itemmagnet.enums;
public enum MachineStatus {
RUNNING,
PAUSED,
NO_TARGET,
TARGET_MISSING,
TARGET_INVALID
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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<Integer, ItemStack> 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
);
}
}
@@ -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);
}
}
@@ -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."
);
}
}
@@ -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);
}
}
@@ -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
);
}
}
}
@@ -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
);
}
}
}
@@ -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<UUID, Block> 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();
}
}
@@ -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()
);
}
}
@@ -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;
}
}
}
}
@@ -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
);
}
}
@@ -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()
);
}
}
File diff suppressed because it is too large. Load diff
@@ -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;
}
}
@@ -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;
}
}
@@ -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<UUID, PendingConversion>
pendingConversions =
new HashMap<>();
private static final Map<UUID, PendingTargetAssignment>
pendingTargetAssignments =
new HashMap<>();
public static boolean containsFilterItem(
Block block,
ItemStack item
) {
Map<Integer, ItemStack> 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()
);
}
}
@@ -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<UUID, PreviewData>
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
) {}
}
@@ -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<Location, Integer> 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
);
}
}
}
@@ -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<Location, Integer> 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<Item> 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<Integer, ItemStack> 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
);
}
}
}
}
}
@@ -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
);
}
}
@@ -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("<version>")[1]
.split("</version>")[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;
}
}
@@ -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";
}
@@ -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";
}
@@ -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<String> 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;
}
}
@@ -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
)
)
);
}
}
@@ -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();
}
}
+9
View File
@@ -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.
+32
View File
@@ -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
+21
View File
@@ -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