Base Plugin Backend

This a copy of my current version of Lockage IT DOES NOT work expect bugs
This commit is contained in:
Koopa 2025-02-01 01:07:21 -05:00
parent 002e1864d0
commit 07c4cf6fac
11 changed files with 716 additions and 24 deletions

25
.gitignore vendored
View File

@ -1,24 +1 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
/target

19
plugin.yml Normal file
View File

@ -0,0 +1,19 @@
name: Lockage
version: '1.0.0'
main: com.koopalabs.lockage.Lockage
api-version: '1.20'
author: Koopa
description: A chest locking system using tripwire hooks as keys
commands:
lockage:
description: Shows plugin help and admin commands
usage: /<command> [reload]
permissions:
lockage.use:
description: Allows players to use basic locking features
default: true
lockage.admin:
description: Allows access to admin commands
default: op

87
pom.xml Normal file
View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.koopalabs</groupId>
<artifactId>lockage</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>Lockage</name>
<description>A chest locking system using tripwire hooks as keys</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.42.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>${project.name}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<minimizeJar>true</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>plugin.yml</include>
<include>config.yml</include>
<include>messages.yml</include>
</includes>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,54 @@
package com.koopalabs.lockage;
import com.koopalabs.lockage.commands.LockageCommand;
import com.koopalabs.lockage.database.DatabaseManager;
import com.koopalabs.lockage.listeners.ChestInteractionListener;
import com.koopalabs.lockage.managers.LockManager;
import org.bukkit.plugin.java.JavaPlugin;
public class Lockage extends JavaPlugin {
private static Lockage instance;
private DatabaseManager databaseManager;
private LockManager lockManager;
@Override
public void onEnable() {
instance = this;
// Save default config
saveDefaultConfig();
// Initialize managers
this.databaseManager = new DatabaseManager(this);
this.databaseManager.initialize();
this.lockManager = new LockManager(this);
// Register listeners
getServer().getPluginManager().registerEvents(new ChestInteractionListener(this), this);
// Register commands
getCommand("lockage").setExecutor(new LockageCommand(this));
getLogger().info("Lockage has been enabled!");
}
@Override
public void onDisable() {
if (databaseManager != null) {
databaseManager.close();
}
getLogger().info("Lockage has been disabled!");
}
public static Lockage getInstance() {
return instance;
}
public DatabaseManager getDatabaseManager() {
return databaseManager;
}
public LockManager getLockManager() {
return lockManager;
}
}

View File

@ -0,0 +1,59 @@
package com.koopalabs.lockage.commands;
import com.koopalabs.lockage.Lockage;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class LockageCommand implements CommandExecutor {
private final Lockage plugin;
public LockageCommand(Lockage plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("§cThis command can only be used by players!");
return true;
}
Player player = (Player) sender;
if (args.length > 0 && args[0].equalsIgnoreCase("reload")) {
if (!player.hasPermission("lockage.admin")) {
player.sendMessage(plugin.getConfig().getString("messages.no-permission").replace("&", "§"));
return true;
}
plugin.reloadConfig();
player.sendMessage("§aConfiguration reloaded!");
return true;
}
// Show help message by default
sendHelpMessage(player);
return true;
}
private void sendHelpMessage(Player player) {
player.sendMessage(ChatColor.GOLD + "=== Lockage Help ===");
player.sendMessage(ChatColor.YELLOW + "How to use:");
player.sendMessage(ChatColor.GRAY + "1. Get a tripwire hook");
player.sendMessage(ChatColor.GRAY + "2. Right-click an unlocked chest with the tripwire hook to lock it");
player.sendMessage(ChatColor.GRAY + "3. The hook will transform into a key for that chest");
player.sendMessage(ChatColor.GRAY + "4. Right-click the chest with the key to unlock it");
player.sendMessage("");
player.sendMessage(ChatColor.YELLOW + "Commands:");
if (player.hasPermission("lockage.admin")) {
player.sendMessage(ChatColor.GRAY + "/lockage reload §f- Reload the configuration");
}
player.sendMessage("");
player.sendMessage(ChatColor.YELLOW + "Tips:");
player.sendMessage(ChatColor.GRAY + "• Each key is unique to its chest");
player.sendMessage(ChatColor.GRAY + "• Keep your keys safe!");
player.sendMessage(ChatColor.GRAY + "• Only the key that locked a chest can unlock it");
}
}

View File

@ -0,0 +1,227 @@
package com.koopalabs.lockage.database;
import com.koopalabs.lockage.Lockage;
import org.bukkit.Location;
import java.io.File;
import java.sql.*;
import java.util.UUID;
public class DatabaseManager {
private final Lockage plugin;
private Connection connection;
public DatabaseManager(Lockage plugin) {
this.plugin = plugin;
}
public void initialize() {
File dataFolder = new File(plugin.getDataFolder(), "database.db");
boolean needsInit = !dataFolder.exists();
try {
if (needsInit) {
dataFolder.getParentFile().mkdirs();
dataFolder.createNewFile();
}
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + dataFolder);
// Create or verify tables
verifyDatabase();
if (needsInit) {
plugin.getLogger().info("Database created successfully!");
}
} catch (Exception e) {
plugin.getLogger().severe("Failed to initialize database: " + e.getMessage());
plugin.getLogger().severe("Attempting to repair database...");
repairDatabase();
}
}
private void verifyDatabase() {
try (Statement stmt = connection.createStatement()) {
// Check if tables exist and have correct structure
DatabaseMetaData meta = connection.getMetaData();
boolean hasLockedChests = false;
boolean hasKeys = false;
ResultSet tables = meta.getTables(null, null, "locked_chests", null);
hasLockedChests = tables.next();
tables.close();
tables = meta.getTables(null, null, "keys", null);
hasKeys = tables.next();
tables.close();
if (!hasLockedChests || !hasKeys) {
createTables();
}
// Verify data integrity
cleanupInvalidEntries();
} catch (SQLException e) {
plugin.getLogger().severe("Database verification failed: " + e.getMessage());
repairDatabase();
}
}
private void cleanupInvalidEntries() {
try {
// Remove entries with invalid worlds
String sql = "DELETE FROM locked_chests WHERE world NOT IN (SELECT name FROM worlds)";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
int removed = pstmt.executeUpdate();
if (removed > 0) {
plugin.getLogger().warning("Removed " + removed + " chest locks from invalid worlds");
}
}
// Remove orphaned keys
sql = "DELETE FROM keys WHERE key_id NOT IN (SELECT key_id FROM locked_chests)";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
int removed = pstmt.executeUpdate();
if (removed > 0) {
plugin.getLogger().warning("Removed " + removed + " orphaned keys");
}
}
} catch (SQLException e) {
plugin.getLogger().severe("Failed to clean up invalid entries: " + e.getMessage());
}
}
private void repairDatabase() {
try {
// Close existing connection
if (connection != null && !connection.isClosed()) {
connection.close();
}
// Backup existing database if it exists
File dbFile = new File(plugin.getDataFolder(), "database.db");
if (dbFile.exists()) {
File backupFile = new File(plugin.getDataFolder(), "database.db.backup");
if (backupFile.exists()) {
backupFile.delete();
}
dbFile.renameTo(backupFile);
plugin.getLogger().warning("Created database backup: database.db.backup");
}
// Create new database
dbFile.createNewFile();
connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile);
createTables();
plugin.getLogger().info("Database repaired successfully!");
} catch (Exception e) {
plugin.getLogger().severe("Failed to repair database: " + e.getMessage());
plugin.getLogger().severe("Please contact an administrator!");
}
}
private void createTables() {
try (Statement stmt = connection.createStatement()) {
// Table for locked chests
stmt.execute("CREATE TABLE IF NOT EXISTS locked_chests (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"world TEXT NOT NULL," +
"x INTEGER NOT NULL," +
"y INTEGER NOT NULL," +
"z INTEGER NOT NULL," +
"owner UUID NOT NULL," +
"key_id TEXT NOT NULL," +
"created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
")");
// Table for keys
stmt.execute("CREATE TABLE IF NOT EXISTS keys (" +
"key_id TEXT PRIMARY KEY," +
"owner UUID NOT NULL," +
"created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
")");
// Create indices for better performance
stmt.execute("CREATE INDEX IF NOT EXISTS idx_chest_location ON locked_chests(world, x, y, z)");
stmt.execute("CREATE INDEX IF NOT EXISTS idx_chest_key ON locked_chests(key_id)");
// Create worlds table to track valid worlds
stmt.execute("CREATE TABLE IF NOT EXISTS worlds (" +
"name TEXT PRIMARY KEY," +
"last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
")");
} catch (SQLException e) {
plugin.getLogger().severe("Failed to create tables: " + e.getMessage());
}
}
public void close() {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
plugin.getLogger().severe("Error closing database connection: " + e.getMessage());
}
}
public void addLockedChest(Location location, UUID owner, String keyId) {
String sql = "INSERT INTO locked_chests (world, x, y, z, owner, key_id) VALUES (?, ?, ?, ?, ?, ?)";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, location.getWorld().getName());
pstmt.setInt(2, location.getBlockX());
pstmt.setInt(3, location.getBlockY());
pstmt.setInt(4, location.getBlockZ());
pstmt.setString(5, owner.toString());
pstmt.setString(6, keyId);
pstmt.executeUpdate();
} catch (SQLException e) {
plugin.getLogger().severe("Failed to add locked chest: " + e.getMessage());
}
}
public boolean isChestLocked(Location location) {
String sql = "SELECT * FROM locked_chests WHERE world = ? AND x = ? AND y = ? AND z = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, location.getWorld().getName());
pstmt.setInt(2, location.getBlockX());
pstmt.setInt(3, location.getBlockY());
pstmt.setInt(4, location.getBlockZ());
ResultSet rs = pstmt.executeQuery();
return rs.next();
} catch (SQLException e) {
plugin.getLogger().severe("Failed to check locked chest: " + e.getMessage());
return false;
}
}
public boolean isKeyValidForChest(Location location, String keyId) {
String sql = "SELECT * FROM locked_chests WHERE world = ? AND x = ? AND y = ? AND z = ? AND key_id = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, location.getWorld().getName());
pstmt.setInt(2, location.getBlockX());
pstmt.setInt(3, location.getBlockY());
pstmt.setInt(4, location.getBlockZ());
pstmt.setString(5, keyId);
ResultSet rs = pstmt.executeQuery();
return rs.next();
} catch (SQLException e) {
plugin.getLogger().severe("Failed to check key validity: " + e.getMessage());
return false;
}
}
public void removeLock(Location location) {
String sql = "DELETE FROM locked_chests WHERE world = ? AND x = ? AND y = ? AND z = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, location.getWorld().getName());
pstmt.setInt(2, location.getBlockX());
pstmt.setInt(3, location.getBlockY());
pstmt.setInt(4, location.getBlockZ());
pstmt.executeUpdate();
} catch (SQLException e) {
plugin.getLogger().severe("Failed to remove lock: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,88 @@
package com.koopalabs.lockage.listeners;
import com.koopalabs.lockage.Lockage;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
public class ChestInteractionListener implements Listener {
private final Lockage plugin;
public ChestInteractionListener(Lockage plugin) {
this.plugin = plugin;
}
@EventHandler
public void onChestInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
Block block = event.getClickedBlock();
if (block == null || block.getType() != Material.CHEST) {
return;
}
ItemStack item = player.getInventory().getItemInMainHand();
boolean hasItem = item != null && !item.getType().equals(Material.AIR);
// Always cancel the event first to prevent chest opening
event.setCancelled(true);
// Check if chest is locked
if (plugin.getDatabaseManager().isChestLocked(block.getLocation())) {
// If they have a valid key, let them unlock
if (hasItem && item.getType() == Material.TRIPWIRE_HOOK) {
if (plugin.getLockManager().canUnlock(block.getLocation(), item)) {
plugin.getLockManager().unlockChest(block.getLocation());
player.sendMessage(plugin.getConfig().getString("messages.chest-unlocked").replace("&", "§"));
player.playSound(block.getLocation(), Sound.BLOCK_CHEST_OPEN, 1.0f, 1.0f);
player.playSound(block.getLocation(), Sound.BLOCK_CHAIN_BREAK, 0.5f, 1.0f);
event.setCancelled(false); // Allow chest to open
} else {
player.sendMessage(plugin.getConfig().getString("messages.invalid-key").replace("&", "§"));
player.playSound(block.getLocation(), Sound.BLOCK_CHEST_LOCKED, 1.0f, 0.5f);
}
} else {
player.sendMessage(plugin.getConfig().getString("messages.chest-protected").replace("&", "§"));
player.playSound(block.getLocation(), Sound.BLOCK_CHEST_LOCKED, 1.0f, 0.5f);
}
return;
}
// Chest is unlocked - check if they're trying to lock it with a tripwire hook
if (event.getAction() == Action.RIGHT_CLICK_BLOCK &&
hasItem && item.getType() == Material.TRIPWIRE_HOOK) {
// Remove one tripwire hook
if (item.getAmount() > 1) {
item.setAmount(item.getAmount() - 1);
} else {
player.getInventory().setItemInMainHand(null);
}
// Create and give the key
ItemStack key = plugin.getLockManager().createKeyForChest(player, block.getLocation());
player.getInventory().addItem(key);
player.sendMessage(plugin.getConfig().getString("messages.chest-locked").replace("&", "§"));
player.playSound(block.getLocation(), Sound.BLOCK_CHEST_CLOSE, 1.0f, 1.0f);
player.playSound(block.getLocation(), Sound.BLOCK_CHAIN_PLACE, 0.5f, 1.0f);
} else {
// Allow normal chest interaction if not trying to lock
event.setCancelled(false);
}
}
@EventHandler
public void onChestPlace(BlockPlaceEvent event) {
if (event.getBlock().getType() == Material.CHEST) {
// Chests are unlocked by default
return;
}
}
}

View File

@ -0,0 +1,95 @@
package com.koopalabs.lockage.managers;
import com.koopalabs.lockage.Lockage;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Arrays;
import java.util.UUID;
public class LockManager {
private final Lockage plugin;
public LockManager(Lockage plugin) {
this.plugin = plugin;
}
public ItemStack createKeyForChest(Player player, Location chestLocation) {
try {
ItemStack key = new ItemStack(Material.TRIPWIRE_HOOK);
ItemMeta meta = key.getItemMeta();
String keyId = UUID.randomUUID().toString().substring(0, 8);
meta.setDisplayName("§6Chest Key");
meta.setLore(Arrays.asList(
"§7Owner: " + player.getName(),
"§7Key ID: " + keyId,
"§7Location: " + formatLocation(chestLocation)
));
key.setItemMeta(meta);
// Try to lock the chest
try {
plugin.getDatabaseManager().addLockedChest(chestLocation, player.getUniqueId(), keyId);
} catch (Exception e) {
player.sendMessage("§cFailed to lock chest! Please contact an administrator.");
plugin.getLogger().severe("Failed to create key: " + e.getMessage());
return null;
}
return key;
} catch (Exception e) {
player.sendMessage("§cAn error occurred while creating the key!");
plugin.getLogger().severe("Failed to create key item: " + e.getMessage());
return null;
}
}
private String formatLocation(Location loc) {
return loc.getBlockX() + ", " + loc.getBlockY() + ", " + loc.getBlockZ();
}
public boolean isValidKey(ItemStack key) {
if (key == null || key.getType() != Material.TRIPWIRE_HOOK) {
return false;
}
if (!key.hasItemMeta() || !key.getItemMeta().hasLore()) {
return false;
}
return getKeyId(key) != null;
}
private String getKeyId(ItemStack key) {
if (key != null && key.hasItemMeta() && key.getItemMeta().hasLore()) {
for (String lore : key.getItemMeta().getLore()) {
if (lore.startsWith("§7Key ID: ")) {
return lore.substring(9);
}
}
}
return null;
}
public boolean canUnlock(Location location, ItemStack key) {
try {
if (!isValidKey(key)) {
return false;
}
String keyId = getKeyId(key);
return plugin.getDatabaseManager().isKeyValidForChest(location, keyId);
} catch (Exception e) {
plugin.getLogger().severe("Error checking key validity: " + e.getMessage());
return false;
}
}
public void unlockChest(Location location) {
plugin.getDatabaseManager().removeLock(location);
}
}

View File

@ -0,0 +1,29 @@
# Lockage Configuration
# Messages
messages:
prefix: "&8[&6Lockage&8] &7"
chest-locked: "&aChest locked successfully! Right-click with this key to unlock it."
chest-unlocked: "&aChest unlocked! Right-click with a tripwire hook to lock it again."
chest-protected: "&cThis chest is locked! You need the correct key to open it."
no-permission: "&cYou don't have permission to do this!"
invalid-key: "&cThis key doesn't work with this chest!"
# Settings
settings:
# Should players be able to break locked chests?
allow-break-locked-chests: false
# Should locked chests be protected from explosions?
protect-from-explosions: true
# Should double chests be locked together?
lock-double-chests: true
# Database settings
database:
# Database file name
filename: "database.db"
# How often to auto-save (in minutes)
auto-save: 5

View File

@ -0,0 +1,29 @@
# Lockage Messages
# You can customize all plugin messages here
general:
prefix: "&8[&6Lockage&8] &7"
reload: "&aConfiguration reloaded successfully!"
no-permission: "&cYou don't have permission to do this!"
keys:
created: "&aNew key created for chest!"
copied: "&aKey copied successfully!"
invalid: "&cThis is not a valid key!"
max-reached: "&cYou've reached the maximum number of keys for this chest!"
chests:
locked: "&aChest locked successfully!"
unlocked: "&aChest unlocked successfully!"
already-locked: "&cThis chest is already locked!"
protected: "&cThis chest is locked! You need the correct key to open it."
break-protected: "&cYou cannot break a locked chest!"
commands:
help:
- "&6=== Lockage Help ==="
- "&7/lock - Lock a chest with a key"
- "&7/unlock - Unlock a chest with a key"
- "&7/createkey - Create a copy of an existing key"
- "&7/lockage reload - Reload the configuration"
invalid-usage: "&cInvalid command usage! Type /lockage help for help."

View File

@ -0,0 +1,28 @@
name: Lockage
version: '1.0.0'
main: com.koopalabs.lockage.Lockage
api-version: '1.20'
author: Koopa
description: A chest locking system using tripwire hooks as keys
commands:
lock:
description: Lock a chest with a key
usage: /<command>
unlock:
description: Unlock a chest with a key
usage: /<command>
createkey:
description: Create a copy of an existing key
usage: /<command>
lockage:
description: Admin commands for Lockage
usage: /<command> [reload|help]
permissions:
lockage.use:
description: Allows players to use basic locking features
default: true
lockage.admin:
description: Allows access to admin commands
default: op