Compare commits

..
Author SHA1 Message Date
Sem 548927f81f Tell the player they received the user manual when issuing help command 2026-10-24 18:22:22 +02:00
Sem a875ada104 Update spigot api version to latest version 2026-10-24 18:15:07 +02:00
Sem b236f50a09 Update readme with explanation on how to build 2026-10-24 17:56:00 +02:00
SemvdH 9e06c961d0 Add broadcasting messages when players do stuff with coordinates 2026-03-03 18:37:37 +01:00
SemvdH f53ea68c8a change help to book instead of text 2026-02-25 23:20:16 +01:00
SemvdH f780457092 Add removing of coordinates and add optional migration of coordinates database table to include autoincrement id property 2026-02-25 22:52:17 +01:00
SemvdH 09236a360b Add welcome message to show how to use the plugin 2026-02-05 20:27:44 +01:00
SemvdH ec00b18282 Add help command 2026-02-05 20:23:28 +01:00
SemvdH 69dc87b718 Finish map and coordinates system 2026-02-03 21:04:12 +01:00
SemvdH de87781ee1 Add command to get map to view al coordinates 2026-02-03 20:47:55 +01:00
SemvdH a07108e141 Add teleporting player 2026-02-03 18:08:48 +01:00
SemvdH 98fe1a696a First parts of displaying a GUI 2026-02-03 14:13:24 +01:00
SemvdH e1527dad4c Merge branch 'main' of ssh://gitty.interesting-corner.nl:2345/sem/mc-ic-server 2026-02-03 13:48:31 +01:00
SemvdH 2a38080e6b Add basics of GUI 2026-02-03 13:47:02 +01:00
sem 7c87de2a16 Add description of 2 current plugins 2025-11-14 10:32:16 +00:00
Sem 9cf37028a1 add getters and setters and returning single page inventory if amount of items is small enough 2025-11-14 11:30:00 +01:00
SemvdH c663d63341 remove empty line 2025-11-11 22:44:59 +01:00
SemvdH 397fe55744 Start of base holder for menu 2025-11-10 22:35:27 +01:00
33 changed files with 1035 additions and 205 deletions
+21 -1
View File
@@ -1,3 +1,23 @@
# mc-ic-server
Interesting Corner Spigot plugin for the Minecraft server
Interesting Corner Spigot plugins for the Minecraft server:
### IC-Core
Core plugin that handles the database and has base classes for creating a GUI.
### IC-Coords
Coordinates plugin to be able to save, load and teleport to different waypoints
# Building
To build the project, install the Maven for Java VSCode plugin. It will detect all the available projects and artifacts.
Install maven. Example in cachyOS:
```bash
sudo pacman -S maven
```
Then, run the install task for the `ic_plugins` project, or execute this command in the root of the repository:
```bash
mvn install -f "pom.xml"
```
The plugins jars will be in the _target_ folder of each plugin's folder
+4 -1
View File
@@ -60,7 +60,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.8-R0.1-SNAPSHOT</version>
<version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -111,6 +111,9 @@
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
@@ -2,11 +2,15 @@ package nl.interestingcorner.coordinates;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import nl.interestingcorner.coordinates.commands.CoordinatesCommand;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.coordinates.db.CoordinatesDatabaseManager;
import nl.interestingcorner.coordinates.gui.CoordinatesGUIListener;
import nl.interestingcorner.coordinates.listeners.MapUseListener;
import nl.interestingcorner.coordinates.listeners.PlayerJoinListener;
import nl.interestingcorner.core.MinecraftColor;
public class App extends JavaPlugin {
@@ -15,10 +19,11 @@ public class App extends JavaPlugin {
getLogger().info("Eyooo we boutta get lit!");
CoordinatesDatabaseManager.INSTANCE.initialize(this);
getLogger().info("Successfully initialized database");
getServer().getPluginManager().registerEvents(new CoordinatesGUIListener(), this);
registerCommands();
PluginManager pluginManager = getServer().getPluginManager();
pluginManager.registerEvents(new MapUseListener(), this);
pluginManager.registerEvents(new PlayerJoinListener(), this);
registerCommands();
}
@Override
@@ -27,15 +32,76 @@ public class App extends JavaPlugin {
}
private void registerCommands() {
registerSimgleCommand("ic-coords", new CoordinatesCommand());
registerSingleCommand("ic-coords", new CoordinatesCommand());
}
private void registerSimgleCommand(String name, CommandExecutor executor)
{
private void registerSingleCommand(String name, CommandExecutor executor) {
PluginCommand pc = this.getCommand(name);
if (pc != null)
{
if (pc != null) {
pc.setExecutor(executor);
}
}
@Deprecated
private void addOldCoordinates() {
CoordinatesDatabaseManager db = CoordinatesDatabaseManager.INSTANCE;
db.addCoordinate(new Coordinate(1, "The Tower", "Teleport to the Castle of Illusion",
new Coordinate.Position(2700, 65, 450), false, "the-old-world", MinecraftColor.GOLD));
db.addCoordinate(new Coordinate(12, "Temple of Tears", "Teleport to the temple in the sky",
new Coordinate.Position(2445, 218, -1902), false, "the-old-world", MinecraftColor.AQUA));
db.addCoordinate(new Coordinate(13, "Big village", "Teleport to a big ass village",
new Coordinate.Position(1828, 63, -845), false, "the-old-world", MinecraftColor.BLACK));
db.addCoordinate(new Coordinate(14, "Other village", "Teleport to yet another village",
new Coordinate.Position(1771, 64, -1626), false, "the-old-world", MinecraftColor.BLACK));
db.addCoordinate(new Coordinate(15, "Acacia village", "Teleport to acacia village",
new Coordinate.Position(2287, 63, -1492), false, "the-old-world", MinecraftColor.BLACK));
db.addCoordinate(new Coordinate(16, "Illager castle", "Teleport to an illager castle",
new Coordinate.Position(2694, 93, -1240), false, "the-old-world", MinecraftColor.GRAY));
db.addCoordinate(new Coordinate(17, "Huge honeycomb", "Hmmm honey", new Coordinate.Position(1650, 70, 637),
false, "the-old-world", MinecraftColor.BLACK));
db.addCoordinate(new Coordinate(18, "Mansion", "The big mansion next to The Tower",
new Coordinate.Position(2833, 66, 377), false, "the-old-world", MinecraftColor.DARK_AQUA));
db.addCoordinate(new Coordinate(19, "Village with labyrinth", "Big village with a labyrinth",
new Coordinate.Position(871, 66, 884), false, "the-old-world", MinecraftColor.BLACK));
db.addCoordinate(new Coordinate(20, "Ravine cave entrance", "Teleport to big cave entrance",
new Coordinate.Position(2080, 105, 1135), false, "the-old-world", MinecraftColor.BLACK));
db.addCoordinate(new Coordinate(21, "Skeleton spawner", "Get some XP made by Sienna",
new Coordinate.Position(2278, -9, 1484), false, "the-old-world", MinecraftColor.LIGHT_PURPLE));
db.addCoordinate(new Coordinate(22, "Nether portal", "Nether portal IN THE NETHER",
new Coordinate.Position(316, 81, 50), true, "the-old-world", MinecraftColor.RED));
db.addCoordinate(new Coordinate(25, "Home village", "The first home village",
new Coordinate.Position(2212, 69, 926), false, "the-old-world", MinecraftColor.GREEN));
db.addCoordinate(new Coordinate(26, "deep dark wool eiland", "ready to get your asshole ripped",
new Coordinate.Position(2626, -36, 1714), false, "the-old-world", MinecraftColor.DARK_BLUE));
db.addCoordinate(new Coordinate(27, "2nd Nether portal", "2nd nether portal IN THE NETHER",
new Coordinate.Position(264, 43, 106), true, "the-old-world", MinecraftColor.RED));
db.addCoordinate(new Coordinate(28, "Nether fortress", "Nether fortress IN THE NETHER",
new Coordinate.Position(85, 60, 423), true, "the-old-world", MinecraftColor.DARK_RED));
db.addCoordinate(new Coordinate(29, "Village hidden in the sand", "Also known as Sunagakure",
new Coordinate.Position(223, 75, 688), false, "the-old-world", MinecraftColor.YELLOW));
db.addCoordinate(new Coordinate(30, "deep dark", "deep down", new Coordinate.Position(2590, -17, -1690), false,
"the-old-world", MinecraftColor.DARK_BLUE));
db.addCoordinate(new Coordinate(31, "Mob farm", "mob farm maken voor xp en andere stuff enzo",
new Coordinate.Position(2471, 79, -440), false, "the-old-world", MinecraftColor.RED));
db.addCoordinate(new Coordinate(33, "Green cave", "NEW Green cave", new Coordinate.Position(10098, -10, 10076),
false, "the-old-world", MinecraftColor.GREEN));
db.addCoordinate(new Coordinate(34, "Castle", "Beautiful castle in plains biome",
new Coordinate.Position(9678, 90, 11222), false, "the-old-world", MinecraftColor.GOLD));
db.addCoordinate(new Coordinate(35, "RPG style city", "large village with a castle",
new Coordinate.Position(8904, 68, 11200), false, "the-old-world", MinecraftColor.AQUA));
db.addCoordinate(new Coordinate(36, "Abandoned castle", "Abandoned castle which we conquered",
new Coordinate.Position(9071, 74, 11022), false, "the-old-world", MinecraftColor.LIGHT_PURPLE));
db.addCoordinate(new Coordinate(37, "Portal", "New portal IN THE NETHER",
new Coordinate.Position(1136, 32, 1397), true, "the-old-world", MinecraftColor.RED));
db.addCoordinate(new Coordinate(38, "bee boss", "ga dood lol", new Coordinate.Position(3470, 64, 227), false,
"the-old-world", MinecraftColor.BLACK));
db.addCoordinate(new Coordinate(39, "bee boss 2.0", "ga dood neef", new Coordinate.Position(4045, 63, 130),
false, "the-old-world", MinecraftColor.BLACK));
db.addCoordinate(new Coordinate(40, "Ice biome", "RIP my boi titanic", new Coordinate.Position(4587, 63, 476),
false, "the-old-world", MinecraftColor.AQUA));
db.addCoordinate(new Coordinate(41, "Onderwater gang trap ofzo", "Neem water breathing of hoedje mee ofzo",
new Coordinate.Position(6156, 63, 2228), false, "the-old-world", MinecraftColor.GRAY));
db.addCoordinate(new Coordinate(42, "The hood", "da hood", new Coordinate.Position(8202, 70, 999580), false,
"the-old-world", MinecraftColor.GREEN));
}
}
@@ -13,6 +13,8 @@ import net.md_5.bungee.api.chat.TextComponent;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.coordinates.db.CoordinatesDatabaseManager;
import nl.interestingcorner.core.MinecraftColor;
import nl.interestingcorner.core.MinecraftFormattingCode;
import nl.interestingcorner.core.chat.ChatHelper;
public class AddCoordinateCommandHandler implements CoordinatesCommandHandler {
@@ -41,11 +43,6 @@ public class AddCoordinateCommandHandler implements CoordinatesCommandHandler {
return false;
}
sender.sendMessage("Args after parsing:");
sender.sendMessage("0:" + parsedArgs.get(0));
sender.sendMessage("1:" + parsedArgs.get(1));
sender.sendMessage("2:" + parsedArgs.get(2));
String name = parsedArgs.get(0);
String description = parsedArgs.get(1);
String color = parsedArgs.get(2);
@@ -66,8 +63,9 @@ public class AddCoordinateCommandHandler implements CoordinatesCommandHandler {
return false;
}
player.sendMessage(MinecraftColor.BLUE.toColorCode() + "Adding coordinate '" + name + "' at your current location...");
//TODO check if item doesnt already exist, coordinate with same values, create isEquals method for coordinate
return CoordinatesDatabaseManager.INSTANCE
boolean addSuccess = CoordinatesDatabaseManager.INSTANCE
.addCoordinate(name, // name
description, // description
new Coordinate.Position(playerLocation.getBlockX(), playerLocation.getBlockY(),
@@ -76,6 +74,30 @@ public class AddCoordinateCommandHandler implements CoordinatesCommandHandler {
playerWorld.getName(), // world
MinecraftColor.fromString(color) // color
);
if (!addSuccess) {
return false;
} else {
StringBuilder sb = new StringBuilder();
sb.append(MinecraftFormattingCode.ITALIC.toString())
.append(MinecraftFormattingCode.UNDERLINE.toString())
.append(MinecraftColor.LIGHT_PURPLE.toColorCode())
.append("[IC-Coords] ")
.append(MinecraftFormattingCode.RESET.toString())
.append(MinecraftColor.WHITE.toColorCode())
.append("Player ")
.append(MinecraftColor.AQUA.toColorCode())
.append(player.getName())
.append(MinecraftColor.WHITE.toColorCode())
.append(MinecraftFormattingCode.RESET.toString())
.append(" added new coordinate ")
.append(MinecraftColor.fromString(color).toColorCode())
.append(MinecraftFormattingCode.BOLD.toString())
.append(name)
.append(MinecraftFormattingCode.RESET.toString())
.append(MinecraftColor.WHITE.toColorCode())
.append(" to the map!");
ChatHelper.broadcastMessage(sb.toString());
}
}
return true;
@@ -84,12 +106,10 @@ public class AddCoordinateCommandHandler implements CoordinatesCommandHandler {
private List<String> removeFirstArg(String[] args) {
List<String> onlyArgs = new ArrayList<>();
sender.sendMessage("removeFirstArg: size of args is " + String.valueOf(args.length));
for (int i = 1; i < args.length; i++) {
onlyArgs.add(args[i]);
}
sender.sendMessage("removeFirstArg: size of onlyArgs is " + String.valueOf(onlyArgs.size()));
return onlyArgs;
}
@@ -14,14 +14,14 @@ public class CoordinatesCommand implements CommandExecutor{
sb.append(string);
sb.append(" ");
}
sender.sendMessage("Args length: " + String.valueOf(args.length));
sender.sendMessage(sb.toString());
// strategy design pattern
return switch (args[0]) {
case "get" -> new GetCoordinatesCommandHandler().handleCommand(sender, args);
case "add" -> new AddCoordinateCommandHandler().handleCommand(sender, args);
case "map" -> new GetMapCommandHandler().handleCommand(sender, args);
case "help" -> new HelpCommandHandler().handleCommand(sender, args);
case "remove" -> new RemoveCoordinateCommandHandler().handleCommand(sender, args);
default -> false;
};
}
@@ -5,42 +5,40 @@ import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import nl.interestingcorner.coordinates.db.CoordinatesDatabaseManager;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.coordinates.gui.CoordinatesGUI;
import nl.interestingcorner.coordinates.db.CoordinatesDatabaseManager;
import nl.interestingcorner.coordinates.gui.ShowCoordinatesGUI;
public class GetCoordinatesCommandHandler implements CoordinatesCommandHandler {
@Override
public boolean handleCommand(CommandSender sender, String[] args) {
if (sender instanceof Player player) {
List<Coordinate> coords;
if (args.length < 2) {
coords = CoordinatesDatabaseManager.INSTANCE.getAllCoordinates();
} else if (args[1].equalsIgnoreCase("world")) {
String world = player.getWorld().getName();
coords = CoordinatesDatabaseManager.INSTANCE.getAllCoordinates(world);
} else {
sender.sendMessage("Invalid argument: " + args[1]);
return false;
}
if (coords.isEmpty()) {
player.sendMessage("No coordinates found! Add some with the §3/ic-coords §5add §fcommand.");
return true;
try {
if (sender instanceof Player player) {
List<Coordinate> coords;
if (args.length < 2) {
coords = CoordinatesDatabaseManager.INSTANCE.getAllCoordinates();
} else if (args[1].equalsIgnoreCase("world")) {
String world = player.getWorld().getName();
coords = CoordinatesDatabaseManager.INSTANCE.getAllCoordinates(world);
} else {
sender.sendMessage("Invalid argument: " + args[1]);
return false;
}
if (coords.isEmpty()) {
player.sendMessage("No coordinates found! Add some with the §3/ic-coords §5add §fcommand.");
return true;
}
new ShowCoordinatesGUI().open(player, coords);
}
return true;
StringBuilder res = new StringBuilder("Coordinates: ");
for (Coordinate coordinate : coords) {
res.append(coordinate.toString());
}
player.sendMessage(res.toString());
CoordinatesGUI.open(player, coords);
} catch (Exception e) {
sender.sendMessage("An error occurred while getting coordinates: " + e.getMessage());
return false;
}
return true;
}
}
@@ -0,0 +1,55 @@
package nl.interestingcorner.coordinates.commands;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import nl.interestingcorner.core.MinecraftColor;
public class GetMapCommandHandler implements CoordinatesCommandHandler {
public static final String MAP_ITEM_NAME = MinecraftColor.LIGHT_PURPLE.toColorCode() + "Map of Coordinates";
public static final String MAP_ITEM_LORE = MinecraftColor.AQUA.toColorCode() + "This will take you anywhere!";
@Override
public boolean handleCommand(CommandSender sender, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage("This command can only be used by a player.");
return true;
}
// Create a custom map item
ItemStack map = new ItemStack(Material.FILLED_MAP);
ItemMeta meta = map.getItemMeta();
if (meta != null) {
meta.setDisplayName(MAP_ITEM_NAME);
List<String> lore = new ArrayList<>();
lore.add(MAP_ITEM_LORE);
meta.setLore(lore);
map.setItemMeta(meta);
}
// Give the item to the player
player.getInventory().addItem(map);
player.sendMessage(MinecraftColor.GREEN.toColorCode() + "You received the Coordinates map! Right-click to use it.");
return true;
}
/**
* Check if the given item is the special map item.
* @param item The item to check.
* @return true if it is the special map, false otherwise.
*/
public static boolean isSpecialMap(ItemStack item) {
if (item == null || item.getType() != Material.FILLED_MAP) return false;
ItemMeta meta = item.getItemMeta();
if (meta == null) return false;
return meta.hasDisplayName() && meta.getDisplayName().equals(MAP_ITEM_NAME) &&
meta.hasLore() && meta.getLore() != null && meta.getLore().contains(MAP_ITEM_LORE);
}
}
@@ -0,0 +1,66 @@
package nl.interestingcorner.coordinates.commands;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import nl.interestingcorner.core.MinecraftColor;
public class HelpCommandHandler implements CoordinatesCommandHandler {
@Override
public boolean handleCommand(CommandSender sender, String[] args) {
if (sender instanceof Player player) {
// Send the help book to the player
ItemStack helpBook = new ItemStack(Material.WRITTEN_BOOK);
BookMeta meta = (BookMeta) helpBook.getItemMeta();
meta.setTitle("IC-Coords User Manual");
meta.setAuthor("Sem");
// Table of contents
String toc = "IC-Coords Plugin Commands\n\n" +
"1. Map\n" +
"2. Get\n" +
"3. Add\n" +
"4. Remove\n" +
"5. Help\n\n" +
"Use the next pages for details.";
// Command explanations
String pageMap = MinecraftColor.LIGHT_PURPLE.toColorCode() + "1. Map\n\n" +
MinecraftColor.DARK_BLUE.toColorCode() + "/ic-coords " + MinecraftColor.GOLD.toColorCode() + "map\n" +
MinecraftColor.BLACK.toColorCode() + "Get a special map that you can use to teleport between coordinates. Click on each coordinate to teleport to it.";
String pageGet = MinecraftColor.LIGHT_PURPLE.toColorCode() + "2. Get\n\n" +
MinecraftColor.DARK_BLUE.toColorCode() + "/ic-coords " + MinecraftColor.GOLD.toColorCode() + "get [world]\n" +
MinecraftColor.BLACK.toColorCode() + "Show all coordinates. Add a world name to filter by world.\n" +
MinecraftColor.DARK_GREEN.toColorCode() + "Example: /ic-coords get world_nether";
String pageAdd = MinecraftColor.LIGHT_PURPLE.toColorCode() + "3. Add\n\n" +
MinecraftColor.DARK_BLUE.toColorCode() + "/ic-coords " + MinecraftColor.GOLD.toColorCode() + "add <name> <description> [color]\n" +
MinecraftColor.BLACK.toColorCode() + "Add a new coordinate at your current location with a name, description, and color.\n" +
"If no color is specified, the color will be white.\n" +
MinecraftColor.DARK_GREEN.toColorCode() + "Example: /ic-coords add Home \"My base\" red";
String pageRemove = MinecraftColor.LIGHT_PURPLE.toColorCode() + "4. Remove\n\n" +
MinecraftColor.DARK_BLUE.toColorCode() + "/ic-coords " + MinecraftColor.GOLD.toColorCode() + "remove\n" +
MinecraftColor.BLACK.toColorCode() + "Remove a coordinate. This will show a GUI with all coordinates and you can click on one to remove it.";
String pageHelp = MinecraftColor.LIGHT_PURPLE.toColorCode() + "5. Help\n\n" +
MinecraftColor.DARK_BLUE.toColorCode() + "/ic-coords " + MinecraftColor.GOLD.toColorCode() + "help\n" +
MinecraftColor.BLACK.toColorCode() + "Get this User Manual for the plugin";
meta.setPages(toc, pageMap, pageGet, pageAdd, pageRemove, pageHelp);
helpBook.setItemMeta(meta);
player.getInventory().addItem(helpBook);
player.sendMessage(MinecraftColor.GREEN.toColorCode() + "You have received the IC-Coords User Manual!");
return true;
} else {
sender.sendMessage("This command can only be used by a player.");
return false;
}
}
}
@@ -0,0 +1,39 @@
package nl.interestingcorner.coordinates.commands;
import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.coordinates.db.CoordinatesDatabaseManager;
import nl.interestingcorner.coordinates.gui.RemoveCoordinatesGUI;
public class RemoveCoordinateCommandHandler implements CoordinatesCommandHandler {
/**
* Removes a coordinate from the database. Usage: /ic-coords remove This
* command will show the GUI to select a coordinate and remove it when a
* coordinate is clicked.
*/
@Override
public boolean handleCommand(CommandSender sender, String[] args) {
if (sender instanceof Player player) {
List<Coordinate> coords;
String world = player.getWorld().getName();
coords = CoordinatesDatabaseManager.INSTANCE.getAllCoordinates(world);
if (coords.isEmpty()) {
player.sendMessage("No coordinates found! Add some with the §3/ic-coords §5add §fcommand.");
return true;
}
RemoveCoordinatesGUI gui = new RemoveCoordinatesGUI();
gui.open(player, coords);
return true;
}
return false;
}
}
@@ -2,6 +2,7 @@ package nl.interestingcorner.coordinates.db;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
@@ -87,7 +88,7 @@ public class Coordinate {
public static String createTableStatement = """
CREATE TABLE IF NOT EXISTS coordinates (
id INTEGER PRIMARY KEY,
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
description VARCHAR(255),
x INT NOT NULL,
@@ -136,6 +137,56 @@ public class Coordinate {
return itemStack;
}
/**
* Converts this Coordinate to a Bukkit Location object
* @return the Location object
*/
public Location toLocation() {
return new Location(
org.bukkit.Bukkit.getWorld(this.world),
this.position.x(),
this.position.y(),
this.position.z()
);
}
/**
* Creates a Coordinate object from an ItemStack
* @param item the ItemStack to convert
* @return the Coordinate object, or null if the ItemStack is not a valid Coordinate
*/
public static Coordinate fromItem(ItemStack item)
{
if (item == null || item.getItemMeta() == null) {
return null;
}
ItemMeta meta = item.getItemMeta();
if (meta == null) {
return null;
}
String name = meta.getDisplayName().replaceAll("§[0-9a-fk-or]", "");
List<String> lore = meta.getLore();
if (lore == null || lore.size() < 6) {
return null;
}
String description = lore.get(0).replaceAll("§[0-9a-fk-or]", "");
boolean nether = lore.get(1).contains("Nether Coordinate");
String xLine = lore.get(2);
String yLine = lore.get(3);
String zLine = lore.get(4);
String worldLine = lore.get(5);
int x = Integer.parseInt(xLine.substring(xLine.indexOf(":") + 2));
int y = Integer.parseInt(yLine.substring(yLine.indexOf(":") + 2));
int z = Integer.parseInt(zLine.substring(zLine.indexOf(":") + 2));
String world = worldLine.substring(worldLine.indexOf(":") + 2);
MinecraftColor color = MinecraftColor.fromMaterial(item.getType());
return new Coordinate(-1, name, description, new Position(x, y, z), nether, world, color);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
@@ -1,5 +1,6 @@
package nl.interestingcorner.coordinates.db;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -12,14 +13,17 @@ import org.bukkit.plugin.java.JavaPlugin;
import nl.interestingcorner.core.MinecraftColor;
import nl.interestingcorner.core.db.DatabaseManager;
import nl.interestingcorner.core.logging.Logger;
public enum CoordinatesDatabaseManager implements nl.interestingcorner.core.db.DatabaseInitializeListener{
public enum CoordinatesDatabaseManager implements nl.interestingcorner.core.db.DatabaseInitializeListener {
INSTANCE;
private Connection connection;
private JavaPlugin app;
private final String TAG = getClass().getSimpleName();
public void initialize(JavaPlugin app) {
this.app = app;
DatabaseManager.INSTANCE.registerDatabaseInitializeListener(this);
@@ -29,7 +33,7 @@ public enum CoordinatesDatabaseManager implements nl.interestingcorner.core.db.D
* Gets all coordinates from the database
*
* @return a list with all coordinates from the database. Empty if an error
* occurred.
* occurred.
*/
public List<Coordinate> getAllCoordinates() {
List<Coordinate> result = new ArrayList<>();
@@ -56,7 +60,7 @@ public enum CoordinatesDatabaseManager implements nl.interestingcorner.core.db.D
} catch (SQLException e) {
this.app.getLogger().log(Level.SEVERE, "Could not get coordinates for world {0}! {1}",
new Object[] { world, e.getMessage() });
new Object[]{world, e.getMessage()});
}
return result;
}
@@ -88,16 +92,38 @@ public enum CoordinatesDatabaseManager implements nl.interestingcorner.core.db.D
return coordinates;
}
/**
* Removes the given coordinate from the database.
*
* @param coordinate the coordinate to remove
* @return true if the coordinate was removed successfully, false if an
* error occurred.
*/
public boolean removeCoordinate(Coordinate coordinate) {
String sql = "DELETE FROM coordinates WHERE id = ?";
try {
PreparedStatement removeCoordinateStatement = this.connection.prepareStatement(sql);
removeCoordinateStatement.setInt(1, coordinate.id);
removeCoordinateStatement.executeUpdate();
} catch (SQLException e) {
this.app.getLogger().log(Level.SEVERE, "Error removing coordinate from database: {0}", e.getMessage());
return false;
}
Logger.INSTANCE.info(TAG, "Removed coordinate '" + coordinate.name + "' (id: " + coordinate.id + ") from database.");
return true;
}
/**
* Adds a new coordinate to the database with the given parameters.
*
* @param name the name of the coordinate
* @param name the name of the coordinate
* @param description a short description of the coordinate
* @param position the position of the coordinate where the player will
* spwan when they teleport to the coordinate
* @param nether if the coordinate is in the nether
* @param world the Multiverse world the coordinate belongs to
* @param color the color to display for the coordinate name
* @param position the position of the coordinate where the player will
* spwan when they teleport to the coordinate
* @param nether if the coordinate is in the nether
* @param world the Multiverse world the coordinate belongs to
* @param color the color to display for the coordinate name
* @return true if the command was added successfully, false otherwise
*/
public boolean addCoordinate(String name, String description, Coordinate.Position position, boolean nether,
@@ -119,6 +145,8 @@ public enum CoordinatesDatabaseManager implements nl.interestingcorner.core.db.D
this.app.getLogger().log(Level.SEVERE, "Error adding coordinate to database: {0}", e.getMessage());
return false;
}
Logger.INSTANCE.info(TAG, "Added coordinate '" + name + "' to database.");
return true;
}
@@ -127,6 +155,36 @@ public enum CoordinatesDatabaseManager implements nl.interestingcorner.core.db.D
coordinate.world, coordinate.color);
}
/**
* Tries to set the id of the given coordinate from the database. This is useful for when converting between ItemStacks and Coordinates, since the id is not stored in the ItemStack lore.
* NOTE: The coordinate is identified by all its parameters except for the id, so if there are multiple coordinates with the same parameters, this method may set the id of the wrong coordinate.
* @param coordinate the coordinate to set the id for
* @return true if the id was set successfully, false otherwise
*/
public boolean trySetIdFromDatabase(Coordinate coordinate) {
String sql = "SELECT id FROM coordinates WHERE name = ? AND description = ? AND x = ? AND y = ? AND z = ? AND nether = ? AND world = ?";
try {
PreparedStatement statement = this.connection.prepareStatement(sql);
statement.setString(1, coordinate.name);
statement.setString(2, coordinate.description);
statement.setInt(3, coordinate.position.x());
statement.setInt(4, coordinate.position.y());
statement.setInt(5, coordinate.position.z());
statement.setBoolean(6, coordinate.nether);
statement.setString(7, coordinate.world);
ResultSet rs = statement.executeQuery();
if (rs.next()) {
int id = rs.getInt("id");
coordinate.id = id;
return true;
}
} catch (SQLException e) {
this.app.getLogger().log(Level.SEVERE, "Error getting id for coordinate: {0}", e.getMessage());
}
return false;
}
/**
* initializes the tables for the database.
*
@@ -155,10 +213,55 @@ public enum CoordinatesDatabaseManager implements nl.interestingcorner.core.db.D
@Override
public void initializeDatabaseTables(Connection connection) {
this.connection = connection;
migrateCoordinatesTableIfNeeded();
if (!initializeTables()) {
this.app.getLogger().severe("Could not initialize coordinates database tables");
}
this.app.getLogger().info("Coordinates database tables initialized");
}
private void migrateCoordinatesTableIfNeeded() {
try {
boolean needsMigration;
try (PreparedStatement checkTable = this.connection.prepareStatement(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='coordinates';");
ResultSet rs = checkTable.executeQuery()) {
needsMigration = false;
if (rs.next()) {
String createSql = rs.getString("sql");
// Check if AUTOINCREMENT is missing
if (createSql != null && !createSql.contains("AUTOINCREMENT")) {
needsMigration = true;
}
}
}
if (needsMigration) {
// Backup database file before migration
File backup = DatabaseManager.INSTANCE.createBackupDatabase();
if (backup != null) {
this.app.getLogger().log(Level.INFO, "Database backup created before migration: {0}", backup.getName());
} else {
this.app.getLogger().log(Level.WARNING, "Failed to create database backup before migration.");
}
this.app.getLogger().log(Level.WARNING, "Migrating coordinates table to add AUTOINCREMENT...");
// Rename old table
this.connection.createStatement().executeUpdate("ALTER TABLE coordinates RENAME TO coordinates_old;");
try (PreparedStatement createTableStatement = this.connection.prepareStatement(Coordinate.createTableStatement)) {
createTableStatement.executeUpdate();
}
// Copy data (excluding id, so new ids are generated)
String copySql = "INSERT INTO coordinates (name, description, x, y, z, nether, color, world) "
+ "SELECT name, description, x, y, z, nether, color, world FROM coordinates_old;";
this.connection.createStatement().executeUpdate(copySql);
// Drop old table
this.connection.createStatement().executeUpdate("DROP TABLE coordinates_old;");
this.app.getLogger().log(Level.INFO, "Migration complete. New ids assigned.");
}
} catch (SQLException e) {
this.app.getLogger().log(Level.SEVERE, "Migration error: {0}", e.getMessage());
}
}
}
@@ -2,67 +2,18 @@ package nl.interestingcorner.coordinates.gui;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.core.MinecraftColor;
public class CoordinatesGUI {
public static void open(Player player, List<Coordinate> coordinates) {
int coordinatesAmount = coordinates.size();
int size;
if (coordinatesAmount <= 9) {
size = 9;
} else {
// TODO handle if size is more than 54 - 9, add pagination
/**
* if more than 45, add pages. Split by 45 items per page.
* use bottom row leftmost and rightmost item for navigation.
* use paper item for page number display in middle.
* use lore of navigation items clicked to handle showing next/previous page.
* use player.getOpeninventory() to get current inventory and update or close it.
*/
// round up to nearest multiple of 9
size = ((coordinatesAmount / 9) + 1) * 9;
player.sendMessage("Size: " + size);
}
// Create an inventory with 9 slots and a title
Inventory gui = Bukkit.createInventory(null, size, "Coordinates Menu");
for (Coordinate coordinate : coordinates) {
ItemStack item = createCoordinateItem(coordinate);
gui.addItem(item);
}
// Open the GUI for the player
player.openInventory(gui);
}
private static ItemStack createCoordinateItem(Coordinate coordinate) {
ItemStack itemStack = new ItemStack(coordinate.color.toMaterial());
ItemMeta meta = itemStack.getItemMeta();
if (meta == null) {
return itemStack;
}
meta.setDisplayName(coordinate.color.toColorCode() + coordinate.name);
meta.setLore(List.of(
MinecraftColor.WHITE.toColorCode() + coordinate.description,
coordinate.nether ? MinecraftColor.RED.toColorCode() + "Nether Coordinate" : "Overworld Coordinate",
"X: " + coordinate.position.x(),
"Y: " + coordinate.position.y(),
"Z: " + coordinate.position.z(),
"World: " + coordinate.world
));
itemStack.setItemMeta(meta);
return itemStack;
}
}
/**
* Class responsible for showing a GUI to the player with coordinates. The implementation decides what is done with those coordinates when the player clicks them
*/
public abstract class CoordinatesGUI {
/**
* Opens the GUI for the player with the given coordinates.
* @param player the player to show the GUI to
* @param coords the coordinates to show in the GUI
*/
public abstract void open(Player player, List<Coordinate> coords);
}
@@ -1,33 +0,0 @@
package nl.interestingcorner.coordinates.gui;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.entity.Player;
import org.bukkit.ChatColor;
public class CoordinatesGUIListener implements Listener{
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
// Check if this is our custom GUI
if (event.getView().getTitle().equals("Coordinates Menu")) {
event.setCancelled(true); // Prevent taking the item
if (event.getCurrentItem() == null) return;
Player player = (Player) event.getWhoClicked();
switch (event.getSlot()) {
case 0 -> {
player.sendMessage(ChatColor.GREEN + "You clicked Add Coordinate!");
player.closeInventory();
// Here you could open another GUI or run a command
}
// Add other cases for other slots if needed
}
}
}
}
@@ -0,0 +1,30 @@
package nl.interestingcorner.coordinates.gui;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.coordinates.gui.clicklisteners.RemoveItemClickListener;
import nl.interestingcorner.core.gui.GUI;
public class RemoveCoordinatesGUI extends CoordinatesGUI{
@Override
public void open(Player player, List<Coordinate> coords) {
List<ItemStack> items = new ArrayList<>();
for (Coordinate coord : coords) {
ItemStack item = coord.toItem();
items.add(item);
}
GUI gui = new GUI("Click coordinate to remove", GUI.DEFAULT_PAGE_SIZE);
gui.addItemClickListener(new RemoveItemClickListener());
gui.setItems(items);
gui.show(player);
}
}
@@ -0,0 +1,31 @@
package nl.interestingcorner.coordinates.gui;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.coordinates.gui.clicklisteners.TeleportItemClickListener;
import nl.interestingcorner.core.gui.GUI;
/**
* Class to show the available coordinates to a player so they can teleport to a coordinate by clicking it.
*/
public class ShowCoordinatesGUI extends CoordinatesGUI {
@Override
public void open(Player player, List<Coordinate> coords) {
List<ItemStack> items = new ArrayList<>();
for (Coordinate coord : coords) {
ItemStack item = coord.toItem();
items.add(item);
}
GUI gui = new GUI("Coordinates", GUI.DEFAULT_PAGE_SIZE);
gui.addItemClickListener(new TeleportItemClickListener());
gui.setItems(items);
gui.show(player);
}
}
@@ -0,0 +1,51 @@
package nl.interestingcorner.coordinates.gui.clicklisteners;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.coordinates.db.CoordinatesDatabaseManager;
import nl.interestingcorner.core.MinecraftColor;
import nl.interestingcorner.core.MinecraftFormattingCode;
import nl.interestingcorner.core.chat.ChatHelper;
import nl.interestingcorner.core.gui.GUI;
import nl.interestingcorner.core.gui.GUIItemClickListener;
public class RemoveItemClickListener implements GUIItemClickListener {
@Override
public void onItemClick(Player player, GUI gui, ItemStack item, int slot) {
Coordinate coordinate = Coordinate.fromItem(item);
if (!CoordinatesDatabaseManager.INSTANCE.trySetIdFromDatabase(coordinate)) {
player.sendMessage(MinecraftColor.RED + "Error: Could not find coordinate in database. Contact Sem about this issue!");
return;
}
CoordinatesDatabaseManager.INSTANCE.removeCoordinate(coordinate);
player.closeInventory();
player.sendMessage("Removed coordinate " + coordinate.name);
StringBuilder sb = new StringBuilder();
sb.append(MinecraftFormattingCode.ITALIC.toString())
.append(MinecraftFormattingCode.UNDERLINE.toString())
.append(MinecraftColor.LIGHT_PURPLE.toColorCode())
.append("[IC-Coords] ")
.append(MinecraftFormattingCode.RESET.toString())
.append(MinecraftColor.WHITE.toColorCode())
.append("Player ")
.append(MinecraftColor.AQUA.toColorCode())
.append(player.getName())
.append(MinecraftFormattingCode.RESET.toString())
.append(MinecraftColor.RED.toColorCode())
.append(" removed ")
.append(MinecraftColor.WHITE.toColorCode())
.append("coordinate ")
.append(coordinate.color.toColorCode())
.append(MinecraftFormattingCode.BOLD.toString())
.append(coordinate.name)
.append(MinecraftFormattingCode.RESET.toString())
.append(MinecraftColor.WHITE.toColorCode())
.append(" from the map!");
ChatHelper.broadcastMessage(sb.toString());
}
}
@@ -0,0 +1,53 @@
package nl.interestingcorner.coordinates.gui.clicklisteners;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import nl.interestingcorner.coordinates.db.Coordinate;
import nl.interestingcorner.core.MinecraftColor;
import nl.interestingcorner.core.MinecraftFormattingCode;
import nl.interestingcorner.core.chat.ChatHelper;
import nl.interestingcorner.core.gui.GUI;
import nl.interestingcorner.core.gui.GUIItemClickListener;
import nl.interestingcorner.core.logging.Logger;
public class TeleportItemClickListener implements GUIItemClickListener {
private final String TAG = this.getClass().getSimpleName();
@Override
public void onItemClick(Player player, GUI gui, ItemStack item, int slot) {
Logger.INSTANCE.info(TAG, "Item clicked in slot " + slot + " by player " + player.getName());
Coordinate coordinate = Coordinate.fromItem(item);
if (coordinate != null) {
Location loc = coordinate.toLocation();
Logger.INSTANCE.info(TAG, "Teleporting player " + player.getName() + " to coordinate " + coordinate.name);
player.sendMessage("Teleporting you to " + coordinate.name);
player.teleport(loc);
player.sendTitle(coordinate.name, coordinate.description, 10, 70, 20);
player.closeInventory();
StringBuilder sb = new StringBuilder();
sb.append(MinecraftFormattingCode.ITALIC.toString())
.append(MinecraftFormattingCode.UNDERLINE.toString())
.append(MinecraftColor.LIGHT_PURPLE.toColorCode())
.append("[IC-Coords] ")
.append(MinecraftFormattingCode.RESET.toString())
.append(MinecraftColor.WHITE.toColorCode())
.append("Player ")
.append(MinecraftColor.AQUA.toColorCode())
.append(player.getName())
.append(MinecraftFormattingCode.RESET.toString())
.append(MinecraftColor.WHITE.toColorCode())
.append(" yeeted themselves to ")
.append(coordinate.color.toColorCode())
.append(MinecraftFormattingCode.BOLD.toString())
.append(coordinate.name)
.append(MinecraftFormattingCode.RESET.toString())
.append(MinecraftColor.WHITE.toColorCode());
ChatHelper.broadcastMessage(sb.toString());
}
}
}
@@ -0,0 +1,25 @@
package nl.interestingcorner.coordinates.listeners;
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 nl.interestingcorner.coordinates.commands.GetMapCommandHandler;
public class MapUseListener implements Listener {
@EventHandler
public void onPlayerUseMap(PlayerInteractEvent event) {
Player player = event.getPlayer();
ItemStack item = event.getItem();
if (GetMapCommandHandler.isSpecialMap(item)) {
event.setCancelled(true);
// execute command to show coordinates of this world
player.performCommand("ic-coords get world");
}
}
}
@@ -0,0 +1,15 @@
package nl.interestingcorner.coordinates.listeners;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import nl.interestingcorner.core.MinecraftColor;
public class PlayerJoinListener implements Listener{
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
event.getPlayer().sendMessage(MinecraftColor.LIGHT_PURPLE.toColorCode() + "This server uses the IC-Coords plugin." + MinecraftColor.YELLOW.toColorCode() + " Use /ic-coords help to see the available commands.");
}
}
@@ -7,4 +7,4 @@ depend: [IC-core]
commands:
ic-coords:
description: Main command for the coordinates
usage: /ic-coords get | add
usage: /ic-coords get | add | remove | map | help. To add a command, use /ic-coords add "name" "description" <color>.
+4 -1
View File
@@ -54,7 +54,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.8-R0.1-SNAPSHOT</version>
<version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -111,6 +111,9 @@
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
@@ -3,6 +3,8 @@ package nl.interestingcorner.core;
import org.bukkit.plugin.java.JavaPlugin;
import nl.interestingcorner.core.db.DatabaseManager;
import nl.interestingcorner.core.gui.GUIInventoryClickListener;
import nl.interestingcorner.core.logging.Logger;
/**
* Core plugin class for the Interesting Corner plugin
@@ -11,6 +13,8 @@ public class App extends JavaPlugin {
@Override
public void onEnable() {
DatabaseManager.INSTANCE.initialize(this);
Logger.INSTANCE.initialize(this);
getServer().getPluginManager().registerEvents(GUIInventoryClickListener.INSTANCE, this);
getLogger().info("Core plugin loaded!");
}
@@ -124,6 +124,20 @@ public enum MinecraftColor {
return materialsMap.get(this);
}
/**
* Finds a MinecraftColor from a Material.
* @param material the material to find the color from
* @return the MinecraftColor corresponding to the material. White if not found.
*/
public static MinecraftColor fromMaterial(Material material) {
for (MinecraftColor color : MinecraftColor.values()) {
if (materialsMap.get(color) == material) {
return color;
}
}
return WHITE;
}
/**
* Converts this MinecraftColor to a corresponding Minecraft color code.
* @return
@@ -0,0 +1,21 @@
package nl.interestingcorner.core;
public enum MinecraftFormattingCode {
BOLD("§l"),
ITALIC("§o"),
UNDERLINE("§n"),
STRIKETHROUGH("§m"),
RESET("§r"),
OBFUSCATED("§k");
private final String code;
MinecraftFormattingCode(String code) {
this.code = code;
}
@Override
public String toString() {
return code;
}
}
@@ -0,0 +1,24 @@
package nl.interestingcorner.core.chat;
import org.bukkit.Server;
public class ChatHelper {
/**
* Sends a message to all online players on the server.
* @param message the message to send
*/
public static void sendMessageToEveryone(String message) {
Server server = org.bukkit.Bukkit.getServer();
server.getOnlinePlayers().forEach(player -> player.sendMessage(message));
}
/**
* Broadcasts a message to all players on the server, including those who are not online.
* @param message the message to send
*/
public static void broadcastMessage(String message) {
Server server = org.bukkit.Bukkit.getServer();
server.broadcastMessage(message);
}
}
@@ -28,7 +28,7 @@ public enum DatabaseManager {
if (this.initialized) {
return true;
}
this.app = app;
if (!setupDatabase()) {
@@ -58,6 +58,12 @@ public enum DatabaseManager {
return true;
}
/**
* Registers a listener that will be called when the database is
* initialized. This can be used to initialize the tables for the database.
*
* @param listener the listener to register
*/
public void registerDatabaseInitializeListener(DatabaseInitializeListener listener) {
this.databaseInitializeListeners.add(listener);
this.app.getLogger().log(Level.INFO, "Registered database initialize listener: {0}", listener.getClass().getName());
@@ -93,7 +99,7 @@ public enum DatabaseManager {
this.connection = DriverManager.getConnection(url);
} catch (SQLException e) {
this.app.getLogger().log(Level.SEVERE, "Could not connect to database file {0}: {1}",
new Object[] { dbFile, e.getMessage() });
new Object[]{dbFile, e.getMessage()});
}
this.app.getLogger().log(Level.INFO, "Connected to SQLite database: {0}", dbFile.getName());
@@ -102,6 +108,37 @@ public enum DatabaseManager {
return true;
}
/**
* Creates a backup of the current database file in the plugin data folder.
* The backup file will be named database_backup_TIMESTAMP.db
*
* @return the File object of the backup, or null if failed
*/
public File createBackupDatabase() {
if (this.app == null) {
return null;
}
File dbFile = new File(app.getDataFolder(), "database.db");
if (!dbFile.exists()) {
return null;
}
String backupName = "database_backup_" + System.currentTimeMillis() + ".db";
File backupFile = new File(app.getDataFolder(), backupName);
try (
java.io.FileInputStream in = new java.io.FileInputStream(dbFile); java.io.FileOutputStream out = new java.io.FileOutputStream(backupFile)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
this.app.getLogger().log(Level.INFO, "Database backup created: {0}", backupFile.getName());
return backupFile;
} catch (Exception e) {
this.app.getLogger().log(Level.SEVERE, "Failed to create database backup: {0}", e.getMessage());
return null;
}
}
/**
* initializes the tables for the database.
*/
@@ -1,14 +1,159 @@
package nl.interestingcorner.core.gui;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class GUI implements InventoryHolder {
public final class GUI implements InventoryHolder, Listener {
public static final int MAX_INVENTORY_SIZE = 54; // max size of inventory before pages are needed
public static final int DEFAULT_PAGE_SIZE = 45; // default items per page (excluding navigation row)
public static final int PREVIOUS_PAGE_SLOT = 45; // slot for previous page button
public static final int NEXT_PAGE_SLOT = 53; // slot for next page button
/**
* Inventory that will be displayed
*/
private Inventory inventory;
/**
* Full list of items that the GUI holds
*/
private List<ItemStack> items;
/**
* Current page being displayed
*/
private int currentPage;
/**
* Title of the GUI
*/
private final String title;
/**
* Number of items per page
*/
private final int pageSize;
/**
* Listener for item click events
*/
private final List<GUIItemClickListener> itemClickListeners;
public GUI(String title, int pageSize) {
this.title = title;
this.pageSize = Math.min(pageSize, MAX_INVENTORY_SIZE);
this.items = new ArrayList<>();
this.currentPage = 0;
this.inventory = null;
this.itemClickListeners = new ArrayList<>();
addItemClickListener(new NavigationItemClickListener());
}
public void setItems(List<ItemStack> items) {
this.items = new ArrayList<>(items);
this.currentPage = 0;
updateInventory();
}
public void addItemClickListener(GUIItemClickListener listener) {
this.itemClickListeners.add(listener);
}
public List<GUIItemClickListener> getItemClickListeners() {
return this.itemClickListeners;
}
public void addItem(ItemStack item) {
this.items.add(item);
updateInventory();
}
public void show(Player player) {
updateInventory();
player.openInventory(this.inventory);
}
public void nextPage() {
if ((currentPage + 1) * pageSize < items.size()) {
currentPage++;
updateInventory();
}
}
public void previousPage() {
if (currentPage > 0) {
currentPage--;
updateInventory();
}
}
public int getPagesAmount() {
return (int) Math.ceil((double) items.size() / pageSize);
}
private void updateInventory() {
boolean needsPagination = items.size() > DEFAULT_PAGE_SIZE;
int inventorySize = needsPagination ? MAX_INVENTORY_SIZE : pageSize;
this.inventory = Bukkit.createInventory(this, inventorySize, title);
int itemsPerPage = needsPagination ? DEFAULT_PAGE_SIZE : pageSize;
int start = currentPage * itemsPerPage;
int end = Math.min(start + itemsPerPage, items.size());
List<ItemStack> pageItems = items.subList(start, end);
for (int i = 0; i < pageItems.size(); i++) {
if (needsPagination && i >= DEFAULT_PAGE_SIZE) {
// Fill only the top rows (0-44) with items if paginating
break;
}
this.inventory.setItem(i, pageItems.get(i));
}
// Add navigation buttons if needed
if (needsPagination) {
if (currentPage > 0) {
// Slot 45: Previous Page
// use currentPage to show 1-based page number
ItemStack prevButton = createNavigationItem("Previous Page", (currentPage) + " / " + getPagesAmount());
this.inventory.setItem(PREVIOUS_PAGE_SLOT, prevButton);
}
if ((currentPage + 1) * pageSize < items.size()) {
// Slot 53: Next Page
// use currentPage + 2 to show 1-based page number
ItemStack nextButton = createNavigationItem("Next Page", (currentPage + 2) + " / " + getPagesAmount());
this.inventory.setItem(NEXT_PAGE_SLOT, nextButton);
}
}
}
private ItemStack createNavigationItem(String name, String description) {
ItemStack compass = new ItemStack(Material.COMPASS);
ItemMeta meta = compass.getItemMeta();
if (meta == null)
{
return compass;
}
meta.setDisplayName(name);
List<String> lore = new ArrayList<>();
lore.add(description);
meta.setLore(lore);
compass.setItemMeta(meta);
return compass;
}
@Override
public Inventory getInventory() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInventory'");
return this.inventory;
}
}
@@ -1,42 +0,0 @@
package nl.interestingcorner.core.gui;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
public class GUIBuilder {
public static final int MAX_INVENTORY_SIZE = 54; // max size of inventory before pages are needed
public static final int MAX_PAGE_INVENTORY_SIZE = 45; // max amount of items on a page
public Inventory buildInventory(List<ItemStack> items) {
/**
* if more than 45, add pages. Split by 45 items per page.
* use bottom row leftmost and rightmost item for navigation.
* use paper item for page number display in middle.
* use lore of navigation items clicked to handle showing next/previous page.
* use player.getOpeninventory() to get current inventory and update or close
* it.
*/
int itemsAmount = items.size();
int pagesAmount = getPagesAmount(itemsAmount);
Inventory gui = Bukkit.createInventory(null, MAX_INVENTORY_SIZE, "Coordinates Menu");
for (ItemStack item : items) {
gui.addItem(item);
}
return gui;
}
private static int getPagesAmount(int itemsAmount) {
if (itemsAmount > MAX_INVENTORY_SIZE) {
return ((itemsAmount / MAX_PAGE_INVENTORY_SIZE) + 1);
}
return 1;
}
}
@@ -0,0 +1,32 @@
package nl.interestingcorner.core.gui;
import java.util.List;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import nl.interestingcorner.core.logging.Logger;
public enum GUIInventoryClickListener implements Listener{
INSTANCE;
private final String TAG = this.getClass().getSimpleName();
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (event.getInventory().getHolder() instanceof GUI gui) {
Logger.INSTANCE.info(TAG, "Inventory click event detected");
List<GUIItemClickListener> itemClickListeners = gui.getItemClickListeners();
for (GUIItemClickListener itemClickListener : itemClickListeners)
{
event.setCancelled(true);
Logger.INSTANCE.info(TAG, "Sending click event to listener");
itemClickListener.onItemClick((Player) event.getWhoClicked(), gui, event.getCurrentItem(), event.getSlot());
}
}
}
}
@@ -0,0 +1,8 @@
package nl.interestingcorner.core.gui;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public interface GUIItemClickListener {
void onItemClick(Player player, GUI gui, ItemStack item, int slot);
}
@@ -0,0 +1,21 @@
package nl.interestingcorner.core.gui;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class NavigationItemClickListener implements GUIItemClickListener {
@Override
public void onItemClick(Player player, GUI gui, ItemStack item, int slot) {
if (slot == GUI.PREVIOUS_PAGE_SLOT) {
gui.previousPage();
player.closeInventory();
gui.show(player);
} else if (slot == GUI.NEXT_PAGE_SLOT) {
gui.nextPage();
player.closeInventory();
gui.show(player);
}
}
}
@@ -0,0 +1,19 @@
package nl.interestingcorner.core.logging;
import org.bukkit.plugin.java.JavaPlugin;
public enum Logger {
INSTANCE;
private JavaPlugin plugin;
public void initialize(JavaPlugin plugin) {
this.plugin = plugin;
}
public void info(String tag, String message) {
if (this.plugin != null) {
this.plugin.getLogger().info("[" + tag + "] " + message);
}
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
#!/bin/bash
mvn install -f "/home/sem/Development/Minecraft/mc-ic-server/pom.xml"
mvn install -f "pom.xml"
scp ic_plugin_core/target/ic_plugin_core-1.0-SNAPSHOT.jar sem@10.10.100.50:/home/sem/minecraft/plugins
scp ic_plugin_coordinates/target/ic_plugin_coordinates-1.0-SNAPSHOT.jar sem@10.10.100.50:/home/sem/minecraft/plugins
scp ic_plugin_coordinates/target/ic_plugin_coordinates-1.0-SNAPSHOT.jar sem@10.10.100.50:/home/sem/minecraft/plugins