diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 046b4e9..0a7e3e8 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -23,11 +23,11 @@ jobs: uses: actions/setup-java@v4 with: distribution: temurin - java-version: "21" + java-version: "25" cache: maven - name: Build with Maven - run: mvn -B -ntp clean verify + run: mvn clean package - name: Select plugin JAR id: jar diff --git a/.github/workflows/modrinth-publish.yml b/.github/workflows/modrinth-publish.yml new file mode 100644 index 0000000..ffba576 --- /dev/null +++ b/.github/workflows/modrinth-publish.yml @@ -0,0 +1,74 @@ +name: Publish to Modrinth (Alpha) + +on: + push: + branches: [ main ] + workflow_dispatch: + +env: + JAVA_VERSION: '25' + +jobs: + build-and-publish: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK ${{ env.JAVA_VERSION }} + uses: actions/setup-java@v4 + with: + java-version: ${{ env.JAVA_VERSION }} + distribution: temurin + + - name: Build with Maven + run: mvn -B clean package -q + + - name: Prepare artifact + id: prepare + run: | + VERSION=$(grep -m1 '' pom.xml | sed 's/.*\(.*\)<\/version>.*/\1/') + SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) + ARTIFACT_NAME="SkyeBlock-${VERSION}-${SHORT_SHA}.jar" + cp target/SkyeBlock-*.jar "target/${ARTIFACT_NAME}" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT" + echo "artifact=${ARTIFACT_NAME}" >> "$GITHUB_OUTPUT" + echo "Built: ${ARTIFACT_NAME}" + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.prepare.outputs.artifact }} + path: target/${{ steps.prepare.outputs.artifact }} + + - name: Upload to Modrinth + run: | + VERSION="${{ steps.prepare.outputs.version }}" + SHORT_SHA="${{ steps.prepare.outputs.short_sha }}" + FILE="target/${{ steps.prepare.outputs.artifact }}" + + jq -n \ + --arg version "$VERSION" \ + --arg name "skyeblock.${VERSION}-alpha-${SHORT_SHA}" \ + --arg project "${{ secrets.MODRINTH_PROJECT_ID }}" \ + '{ + version_number: $version, + name: $name, + version_type: "alpha", + game_versions: ["26.2"], + loaders: ["paper", "spigot"], + project_id: $project, + featured: false, + file_parts: ["file"], + dependencies: [ + { project_id: "Vebnzrzj", dependency_type: "optional" }, + { project_id: "DKY9btbd", dependency_type: "optional" }, + { project_id: "1u6JkXh5", dependency_type: "optional" } + ] + }' > /tmp/modrinth-data.json + + curl -X POST "https://api.modrinth.com/v2/version" \ + -H "Authorization: ${{ secrets.MODRINTH_TOKEN }}" \ + -F "data=@/tmp/modrinth-data.json" \ + -F "file=@${FILE}"; echo diff --git a/.gitignore b/.gitignore index 9137096..52bcc0a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties .mvn/wrapper/maven-wrapper.jar +target/*/* +target/maven-status/* + # IDE Files .idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f57251..a02f2f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,33 @@ -# Changelog +# SkyeBlock Changelog -All notable changes to this project will be documented in this file. +## 3.3.3 (2026-07-13) -## [3.2.0-1.21.10] - 2025-12-26 -- Build now targets Java 21 for 1.21.x dependency compatibility. -- CI/CD: GitHub Actions builds on pushes/PRs, publishes releases on `v*` tags, and maintains a rolling `main-latest` prerelease with auto-generated release notes. -- Packaging: resource filtering adjusted so `.schem` files are included correctly. -- Visitor protection: fixed ProtocolLib packet handling for block/container interaction restrictions. -- Docs/config: added/expanded permission documentation and configuration migration support. +### MC 26.2 & ASP 4.0.0 Support +- Updated Paper API to `26.2.build.60-beta` +- Updated `plugin.yml` api-version to `26.2` +- Integrated Advanced Slime World Manager (ASWM) API 4.0.0 + - Two-stage world loading: `readWorld`/`createEmptyWorld` (async) -> `loadWorld` (sync) + - `FileLoader` shading (package `com.infernalsuite.asp.loaders`) + - `LegacySWMLoaderWrapper` for backward compatibility +- Java 21 bytecode target (compatible with Java 25 runtime on MC 26.2) -## [1.21.10 up2] -- Enforce container/workstation access via per-island permissions and visitor settings (blocks external plugins like sell wands on others' chests). -- Permission-aware inventory open/interaction handling integrated with IslandPermissionManager. +### Schematic Bundling +- Schematics are now bundled inside the plugin JAR under `resources/schematics/` +- `SchematicManager` auto-extracts bundled schematics to the data folder on startup +- 16 bundled schematics: advanced, bare_bones, campsite, cozy_grove, desert, fishermans_paradise, igloo, inverted, mineshaft, nether_generic, nether_jail, olympus, orchid, sandy_isle, vanilla, wilson -## [1.21.10-dev] - 2025-12-20 -- Updated for Minecraft 1.21.10 API compatibility and opened the 1.21.10-dev line. -- Lowered Java target to 17 for wider host compatibility. -- Added gamerule access checks and new permission bundles (`skyeblock.player`, `skyeblock.settings.gamerules`), with GUI filtering. -- Added `/mobspawning` command registration to plugin.yml. -- Marked WorldGuard and ProtocolLib as soft dependencies; added DEPENDENCIES.md for setup guidance. -- Added new schematic files for multiple environments. -- Allowed admins to bypass visiting-disabled checks when appropriate. -- Build/Maven updates to align with 1.21.10 APIs. +### H2 Database Storage (replaces YAML) +- Added embedded H2 `2.3.232` database (`skyeblock.db` in plugin data folder) +- New `DatabaseManager` class handles all H2 connection, schema, and CRUD operations +- **Island data** (`islands`, `island_visitor_permissions`, `island_coop_members`, `island_pending_invites`, `island_votes`, `island_custom_permissions`) — replaces `island-data.yml` +- **Island settings** (`island_settings`) — replaces `island-settings.yml` +- **Player data** (`player_data`) — replaces `player-data.yml` +- Transactional saves with rollback on failure +- `MERGE INTO` for upserts on single-row updates +- Old YAML files are now ignored; data is written to `skyeblock.db` -## [1.21.4] -- Earlier 1.21.4 work predates this changelog; see git history for details. +### Changes +- `/is` now teleports to your island instead of opening settings (use `/is settings` for settings) +- Rewrote README with full feature, command, and permission reference +- Added GitHub Actions workflow for Modrinth alpha publishing +- Version bumped to 3.3.3 diff --git a/README.md b/README.md index d8870c9..ebde8c0 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,168 @@ # SkyeBlock -Skyblock plugin for Paper/Spigot (1.21.10 tested) with isolated island worlds, GUI-driven settings, and granular permissions. Dual command style (`/island` and `/sb …`) keeps legacy players happy while giving admins structured subcommands. +A skyblock island plugin for Minecraft 26.2 (Chaos Cubed) on Paper. Each player gets an isolated island world with persistent storage backed by an embedded H2 database. Supports Advanced Slime World Manager for efficient world management. -## Highlights -- Island per player with optional SlimeWorldManager/ASWM storage; hub + spawn helpers. -- GUI settings for islands (gamerules, visiting, deletion guard) with live apply. -- Multiple starter templates (classic, desert, nether) plus drop-in schematics. -- LuckPerms-friendly permissions, split into player base, safe gamerules, and full admin bypass. -- Nether island support with biome control and safe teleports. +## Features + +- Isolated island worlds: each island lives in its own world for full separation +- H2 embedded database storage for islands, settings, and player data (no YAML) +- 16 bundled schematic templates extracted from the plugin JAR on first run +- Advanced Slime World Manager (ASWM) 4.0.0 integration for world loading +- GUI-driven settings for gamerules, visiting, and island management +- Per-island gamerule overrides with permission-based access control +- Coop system with role-based access (Visitor, Member, Admin, Co-Owner) +- Voting system with 7-day vote tracking +- Warp system with configurable warp points +- Resource world support (Nether, End) +- Visitor protection with granular permission control +- Nether island auto-creation with biome control +- Island deletion cooldown and try-limit system ## Requirements -- Paper/Spigot 1.21.x -- Java 17+ -- Optional: SlimeWorldManager/AdvancedSlimeWorldManager, WorldEdit (dep), LuckPerms. -## Install / Build +- Minecraft 26.2 (Chaos Cubed) server +- Paper or Spigot (api-version 26.2) +- Java 21 or newer +- WorldEdit (required dependency) +- WorldGuard, ProtocolLib (optional, for full functionality) +- Advanced Slime World Manager (optional, for efficient world storage) +- LuckPerms (optional, for permission management) + +## Installation + +1. Build the plugin: ```bash +cd SkyeBlock mvn clean package -cp target/skyeblock-*.jar /path/to/server/plugins/ ``` -Start the server once to generate configs in `plugins/SkyeBlock/`. - -## Core Commands -- `/island create ` – create an island (classic/desert/nether) -- `/island home` – go to your island -- `/island settings` – open settings GUI (gamerules/visiting/delete) -- `/visit ` – visit another island -- `/hub` and `/spawn` – quick travel -- `/sb island …` – subcommand equivalents; `/sb` shows help -- Admin: `/sba …`, `/delete [player]`, `/island list`, `/island status` - -## Permissions (key groups) -- `skyeblock.player` – default player bundle (island basics, hub/spawn, warp) **no gamerules**. -- `skyeblock.settings.gamerules` – safe/common gamerules (daylight/weather cycle, keepinventory, mobgriefing/spawn, fire/fall/fire/drown damage, insomnia, immediate respawn, natural regen, announce advancements, elytra check). -- `skyeblock.gamerule.*` – all gamerules; `skyeblock.gamerules.adminbypass` overrides checks. -- `skyeblock.*` – everything (op). - -Individual gamerule nodes mirror Bukkit names (e.g., `skyeblock.gamerule.dodaylightcycle`, `skyeblock.gamerule.doweathercycle`, `skyeblock.gamerule.randomtickspeed`). - -## Gamerule & Settings GUI -- Access via `/island settings` → main menu → Gamerule Settings. -- Only gamerules you have permission for are shown. -- Boolean rules toggle; numeric rules adjust via clicks/input; applied instantly to the island world. +2. Copy `target/SkyeBlock-*.jar` to your server's `plugins/` directory. +3. Start the server once to generate configuration files in `plugins/SkyeBlock/`. +4. Configure `config.yml` to match your server setup. +5. Restart the server. + +## Commands + +### Player Commands + +| Command | Description | +|---------|-------------| +| `/island` or `/is` | Teleport to your island (or open creation GUI if you have none) | +| `/island create [type]` | Create a new island with the given template | +| `/island tp` | Teleport to your island | +| `/island settings` | Open the island settings GUI | +| `/island edit ` | Customize your island name, description, or icon | +| `/island set ` | Set custom teleport locations | +| `/island coop ` | Manage coop members | +| `/island visit [player]` | Visit another player's island or open the browser | +| `/island lock` | Lock your island (coop members only) | +| `/island unlock` | Unlock your island | +| `/island vote ` | Vote for another player's island | +| `/island types` | List available island templates | +| `/island help` | Show the help message | +| `/visit [player]` | Visit another player's island | +| `/hub` | Teleport to the hub world | +| `/spawn` | Teleport to the spawn world | +| `/warp [name]` | Open the warp GUI or teleport to a specific warp | +| `/mobspawning` | Check mob spawning status | + +### Admin Commands + +| Command | Description | +|---------|-------------| +| `/sba ` | Admin command with full access | +| `/island list` | List all islands on the server | +| `/island status` | Show server status information | +| `/island delete` | Delete your island (with confirmation GUI) | +| `/islandpermissions [player]` | Manage per-player island permissions | +| `/warpadmin ` | Manage warps and resource worlds | +| `/convertislands ` | Convert old island data format | +| `/mobspawning ` | Manage global mob spawning settings | + +## Permissions + +### Default Player Permissions (`skyeblock.player`) + +Grants access to basic island commands, visiting, coop, voting, warps, and hub/spawn. Does not include gamerule access. + +### Gamerule Permissions + +- `skyeblock.settings.gamerules` - Access to common safe gamerules (daylight cycle, weather cycle, keep inventory, mob griefing, mob spawning, fire tick, fall damage, fire damage, drowning damage, insomnia, immediate respawn, natural regeneration, announce advancements, elytra movement check) +- `skyeblock.gamerule.*` - Access to all gamerules including advanced ones (random tick speed, spawn radius, entity cramming, command chain length, sleeping percentage, etc.) +- `skyeblock.gamerule.` - Individual gamerule access (e.g., `skyeblock.gamerule.randomtickspeed`) +- `skyeblock.gamerules.adminbypass` - Bypass all gamerule permission checks + +### Admin Permissions + +- `skyeblock.admin` - Full admin access to all commands +- `skyeblock.admin.convert` - Island data conversion +- `skyeblock.admin.warp` - Warp management +- `skyeblock.admin.mobspawning` - Global mob spawning management + +### Island-Specific Permissions + +- `skyeblock.island` - Basic island commands +- `skyeblock.island.visit` - Visit other islands +- `skyeblock.island.vote` - Vote for islands +- `skyeblock.island.edit` - Edit island title, description, icon +- `skyeblock.island.coop` - Manage coop members +- `skyeblock.island.lock` - Lock/unlock islands +- `skyeblock.island.set` - Set custom home/visit locations +- `skyeblock.island.delete` - Delete your own island +- `skyeblock.island.permissions` - Manage per-player island permissions + +### Warp Permissions + +- `skyeblock.warp` - Access to warp GUI +- `skyeblock.warp.spawn`, `skyeblock.warp.nether`, `skyeblock.warp.end`, `skyeblock.warp.pvp`, `skyeblock.warp.shop` - Individual warp access ## Configuration -Generated files live in `plugins/SkyeBlock/`: -- `config.yml` – world names, hub, nether toggle/biome, distances. -- `island-settings.yml` – per-island gamerule values. -- `warps.yml` – warp definitions. -- `schematics/` – bundled island templates; add your own `.schem` files here. + +All configuration files are in `plugins/SkyeBlock/`: + +### config.yml + +- `hub` - Hub world settings (world name, spawn coordinates, teleport-on-join) +- `world` - World settings (nether auto-creation, border size) +- `resource-worlds` - Nether and End resource world configuration +- `island` - Island settings (teleport on join, creation cooldown, template mappings, visiting toggle) +- `messages` - All plugin messages in MiniMessage format +- `schematics` - Schematic template path + +### Database + +Island data, settings, and player data are stored in `plugins/SkyeBlock/skyeblock.db` (H2 embedded database). The database is created automatically on first startup. + +### Warps + +Warp definitions are stored in `plugins/SkyeBlock/warps.yml`. ## Island Templates -- Built-in: classic, desert, nether (see `src/main/resources/schematics/`). -- To add: drop a `.schem` into `plugins/SkyeBlock/schematics/`, then reference it via your island type config. -## Development Notes -- Language: Java 17; build: Maven. -- Main entry: `skyeblock.nobleskye.dev.skyeblock.SkyeBlockPlugin`. -- Primary configs/resourceds under `src/main/resources/`. +16 bundled templates are extracted from the plugin JAR on first startup: -## Contributing / Support -PRs and issues welcome. For perms: start players with `skyeblock.player`, grant `skyeblock.settings.gamerules` for safe tweaks, reserve `skyeblock.gamerule.*`/`adminbypass` for staff. +**Easy:** vanilla, bare_bones, campsite, cozy_grove, mineshaft +**Medium:** desert, fishermans_paradise, inverted, orchid +**Hard:** advanced, igloo, nether_jail, olympus, sandy_isle, wilson +**Nether:** nether_generic +To add custom templates, place `.schem` files (WorldEdit format) in `plugins/SkyeBlock/schematics/` and add a mapping entry in `config.yml` under `island.templates`. -## Server Owners -Ensure dependencies (WorldGuard, ProtocolLib) are installed for full functionality. Adjust permissions via your chosen manager (e.g., LuckPerms) to suit your server's needs. -For autoupdating you can use a plugin like -[PluginUpdater](https://modrinth.com/plugin/plugin-updater) which you can find more info [here](https://github.com/OakLoaf/PluginUpdater/wiki/PluginUpdater-Plugin#configuring-pluginupdater) +## Building + +```bash +mvn clean package +``` + +The built JAR includes all dependencies (H2 database, ASWM file loader) shaded in. + +## Development + +- Language: Java 21 (bytecode target 21) +- Build system: Maven +- Main class: `skyeblock.nobleskye.dev.skyeblock.SkyeBlockPlugin` +- Database: H2 2.3.232 (embedded, file-based) +- Source structure: `src/main/java/` for code, `src/main/resources/` for configs and schematics ## License -Licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0). See LICENSE file for details. + +Licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0). diff --git a/pom.xml b/pom.xml index 199cf91..0a6cbe6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,11 +6,11 @@ skyeblock.nobleskye.dev skyeblock - 3.2.0-1.21.10 + 3.3.3 jar SkyeBlock - SkyeBlock plugin for Minecraft 1.21.10 + SkyeBlock plugin for Minecraft 26.2 (Chaos Cubed) 21 @@ -43,6 +43,10 @@ jitpack.io https://jitpack.io/ + + is-snapshots + https://repo.infernalsuite.com/repository/maven-snapshots/ + @@ -50,7 +54,7 @@ io.papermc.paper paper-api - 1.21.3-R0.1-SNAPSHOT + 26.2.build.60-beta provided @@ -85,6 +89,28 @@ 5.4 provided + + + + com.infernalsuite.asp + api + 4.0.0-SNAPSHOT + provided + + + + + com.infernalsuite.asp + file-loader + 4.0.0-SNAPSHOT + + + + + com.h2database + h2 + 2.3.232 + @@ -128,6 +154,15 @@ false + + + + + + com.infernalsuite.asp.loaders + skyeblock.nobleskye.dev.skyeblock.libs.aswm.loaders + + diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/SkyeBlockPlugin.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/SkyeBlockPlugin.java index 5145f04..48d2bbe 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/SkyeBlockPlugin.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/SkyeBlockPlugin.java @@ -8,6 +8,7 @@ import skyeblock.nobleskye.dev.skyeblock.listeners.PlayerLocationListener; import skyeblock.nobleskye.dev.skyeblock.listeners.VisitorPacketListener; import skyeblock.nobleskye.dev.skyeblock.managers.CustomSchematicManager; +import skyeblock.nobleskye.dev.skyeblock.managers.DatabaseManager; import skyeblock.nobleskye.dev.skyeblock.managers.IslandManager; import skyeblock.nobleskye.dev.skyeblock.managers.SchematicManager; import skyeblock.nobleskye.dev.skyeblock.managers.WorldManager; @@ -41,6 +42,7 @@ public class SkyeBlockPlugin extends JavaPlugin { private SchematicManager schematicManager; private CustomSchematicManager customSchematicManager; private WorldManager worldManager; + private DatabaseManager databaseManager; private IslandSettingsManager islandSettingsManager; private IslandPermissionManager permissionManager; private ResourceWorldManager resourceWorldManager; @@ -76,14 +78,16 @@ public void onEnable() { // Initialize managers this.worldManager = new WorldManager(this); + this.databaseManager = new DatabaseManager(this); + this.databaseManager.open(); this.schematicManager = new SchematicManager(this); this.customSchematicManager = new CustomSchematicManager(this); this.islandManager = new IslandManager(this); - this.islandSettingsManager = new IslandSettingsManager(this); + this.islandSettingsManager = new IslandSettingsManager(this, databaseManager); this.permissionManager = new IslandPermissionManager(this); this.resourceWorldManager = new ResourceWorldManager(this); this.warpManager = new WarpManager(this); - this.playerDataManager = new PlayerDataManager(this); + this.playerDataManager = new PlayerDataManager(this, databaseManager); // Initialize GUIs this.islandSettingsGUI = new IslandSettingsGUI(this); @@ -127,6 +131,11 @@ public void onDisable() { playerDataManager.savePlayerData(); } + // Close database connection + if (databaseManager != null) { + databaseManager.close(); + } + getComponentLogger().info(Component.text("SkyeBlock plugin disabled!", NamedTextColor.RED)); } @@ -280,6 +289,10 @@ public WorldManager getWorldManager() { return worldManager; } + public DatabaseManager getDatabaseManager() { + return databaseManager; + } + public IslandSettingsManager getIslandSettingsManager() { return islandSettingsManager; } diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/IslandCommand.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/IslandCommand.java index c25d523..5ef1c95 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/IslandCommand.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/IslandCommand.java @@ -46,8 +46,8 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (args.length == 0) { // Check if player has an island first if (plugin.getIslandManager().hasIsland(player.getUniqueId())) { - // If they have an island, open settings instead of teleporting - handleSettingsCommand(player); + // Teleport to island + handleTeleportCommand(player); } else { // If they don't have an island, open the creation GUI if (player.hasPermission("skyeblock.island")) { diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI.java index cadfc65..8a62e9e 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI.java @@ -74,7 +74,7 @@ public void openDeleteConfirmation(Player player, UUID targetPlayerUUID) { private void createConfirmationInventory(Player player, DeleteSession session) { Inventory inventory = Bukkit.createInventory(this, INVENTORY_SIZE, - Component.text("DELETE ISLAND - CLICK " + (REQUIRED_CLICKS - session.clickCount) + " MORE TIMES") + Component.text("DELETE ISLAND - CLICK 3 TIMES") .color(NamedTextColor.DARK_RED) .decoration(TextDecoration.BOLD, true)); diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/DatabaseManager.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/DatabaseManager.java new file mode 100644 index 0000000..43caf27 --- /dev/null +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/DatabaseManager.java @@ -0,0 +1,870 @@ +package skyeblock.nobleskye.dev.skyeblock.managers; + +import skyeblock.nobleskye.dev.skyeblock.SkyeBlockPlugin; +import skyeblock.nobleskye.dev.skyeblock.models.Island; +import skyeblock.nobleskye.dev.skyeblock.permissions.IslandPermission; +import org.bukkit.Bukkit; +import org.bukkit.GameRule; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.IOException; +import java.sql.*; +import java.util.*; +import java.util.logging.Level; + +/** + * Manages H2 embedded database for island data, settings, and player data storage. + * Modeled after LuckPerms' approach: single file-based H2 database in the plugin data folder. + */ +public class DatabaseManager { + private final SkyeBlockPlugin plugin; + private Connection connection; + private final File dbFile; + + public DatabaseManager(SkyeBlockPlugin plugin) { + this.plugin = plugin; + this.dbFile = new File(plugin.getDataFolder(), "skyeblock"); + } + + /** + * Open the H2 connection and create tables if needed + */ + public void open() { + try { + plugin.getDataFolder().mkdirs(); + Class.forName("org.h2.Driver"); + String url = "jdbc:h2:file:" + dbFile.getAbsolutePath() + ";MODE=MySQL;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE"; + connection = DriverManager.getConnection(url, "sa", ""); + plugin.getLogger().info("H2 database opened: " + dbFile.getName()); + createTables(); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Failed to open H2 database", e); + } + } + + /** + * Close the H2 connection + */ + public void close() { + try { + if (connection != null && !connection.isClosed()) { + connection.close(); + plugin.getLogger().info("H2 database closed"); + } + } catch (SQLException e) { + plugin.getLogger().log(Level.WARNING, "Failed to close H2 database", e); + } + } + + private void createTables() throws SQLException { + try (Statement stmt = connection.createStatement()) { + // Islands table - core island data + stmt.executeUpdate(""" + CREATE TABLE IF NOT EXISTS islands ( + owner_uuid VARCHAR(36) PRIMARY KEY, + island_id VARCHAR(128), + island_type VARCHAR(64), + display_title VARCHAR(256), + display_description VARCHAR(512), + display_icon TEXT, + locked BOOLEAN DEFAULT FALSE, + adventure_mode_for_visitors BOOLEAN DEFAULT TRUE, + last_online_time BIGINT DEFAULT 0, + location_world VARCHAR(128), + location_x DOUBLE DEFAULT 0, + location_y DOUBLE DEFAULT 0, + location_z DOUBLE DEFAULT 0, + location_yaw FLOAT DEFAULT 0, + location_pitch FLOAT DEFAULT 0, + home_world VARCHAR(128), + home_x DOUBLE, + home_y DOUBLE, + home_z DOUBLE, + home_yaw FLOAT, + home_pitch FLOAT, + visit_world VARCHAR(128), + visit_x DOUBLE, + visit_y DOUBLE, + visit_z DOUBLE, + visit_yaw FLOAT, + visit_pitch FLOAT + ) + """); + + // Visitor permissions table + stmt.executeUpdate(""" + CREATE TABLE IF NOT EXISTS island_visitor_permissions ( + owner_uuid VARCHAR(36) PRIMARY KEY, + can_break_blocks BOOLEAN DEFAULT FALSE, + can_place_blocks BOOLEAN DEFAULT FALSE, + can_open_containers BOOLEAN DEFAULT FALSE, + can_pickup_items BOOLEAN DEFAULT FALSE, + can_drop_items BOOLEAN DEFAULT FALSE, + can_interact_entities BOOLEAN DEFAULT FALSE, + can_use_pvp BOOLEAN DEFAULT FALSE, + can_use_redstone BOOLEAN DEFAULT FALSE + ) + """); + + // Coop members table + stmt.executeUpdate(""" + CREATE TABLE IF NOT EXISTS island_coop_members ( + owner_uuid VARCHAR(36) NOT NULL, + coop_uuid VARCHAR(36) NOT NULL, + role VARCHAR(32) NOT NULL DEFAULT 'MEMBER', + PRIMARY KEY (owner_uuid, coop_uuid) + ) + """); + + // Pending invites table + stmt.executeUpdate(""" + CREATE TABLE IF NOT EXISTS island_pending_invites ( + owner_uuid VARCHAR(36) NOT NULL, + invite_uuid VARCHAR(36) NOT NULL, + PRIMARY KEY (owner_uuid, invite_uuid) + ) + """); + + // Votes table + stmt.executeUpdate(""" + CREATE TABLE IF NOT EXISTS island_votes ( + owner_uuid VARCHAR(36) NOT NULL, + voter_uuid VARCHAR(36) NOT NULL, + vote_time BIGINT NOT NULL, + PRIMARY KEY (owner_uuid, voter_uuid) + ) + """); + + // Custom permissions table + stmt.executeUpdate(""" + CREATE TABLE IF NOT EXISTS island_custom_permissions ( + owner_uuid VARCHAR(36) NOT NULL, + player_uuid VARCHAR(36) NOT NULL, + permission_node VARCHAR(128) NOT NULL, + PRIMARY KEY (owner_uuid, player_uuid, permission_node) + ) + """); + + // Island settings (gamerules) table + stmt.executeUpdate(""" + CREATE TABLE IF NOT EXISTS island_settings ( + island_id VARCHAR(128) NOT NULL, + game_rule VARCHAR(128) NOT NULL, + value VARCHAR(256), + value_type VARCHAR(16) NOT NULL DEFAULT 'BOOLEAN', + PRIMARY KEY (island_id, game_rule) + ) + """); + + // Player data table + stmt.executeUpdate(""" + CREATE TABLE IF NOT EXISTS player_data ( + player_uuid VARCHAR(36) PRIMARY KEY, + last_world VARCHAR(128), + last_x DOUBLE, + last_y DOUBLE, + last_z DOUBLE, + last_yaw FLOAT, + last_pitch FLOAT + ) + """); + + plugin.getLogger().info("H2 database tables initialized"); + } + } + + // ==================== Island Data Operations ==================== + + /** + * Save all islands to the database (transactional) + */ + public void saveAllIslands(Map islands) { + try { + connection.setAutoCommit(false); + + // Clear all existing data + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate("DELETE FROM island_custom_permissions"); + stmt.executeUpdate("DELETE FROM island_votes"); + stmt.executeUpdate("DELETE FROM island_pending_invites"); + stmt.executeUpdate("DELETE FROM island_coop_members"); + stmt.executeUpdate("DELETE FROM island_visitor_permissions"); + stmt.executeUpdate("DELETE FROM islands"); + } + + // Insert all islands + for (Island island : islands.values()) { + insertIsland(island); + } + + connection.commit(); + connection.setAutoCommit(true); + plugin.getLogger().info("Saved " + islands.size() + " islands to H2 database"); + } catch (SQLException e) { + rollback(); + plugin.getLogger().log(Level.SEVERE, "Failed to save islands to H2 database", e); + } + } + + /** + * Load all islands from the database + */ + public Map loadAllIslands() { + Map islands = new HashMap<>(); + if (connection == null) return islands; + + try { + // Load all islands from the islands table + try (PreparedStatement ps = connection.prepareStatement("SELECT * FROM islands"); + ResultSet rs = ps.executeQuery()) { + + while (rs.next()) { + try { + UUID ownerUUID = UUID.fromString(rs.getString("owner_uuid")); + String islandId = rs.getString("island_id"); + String islandType = rs.getString("island_type"); + + // Load location + String worldName = rs.getString("location_world"); + if (worldName == null) { + plugin.getLogger().warning("Skipping island " + islandId + " - no world specified"); + continue; + } + + World world = getOrCreateWorld(worldName, islandId); + if (world == null) { + plugin.getLogger().warning("Skipping island " + islandId + " - world " + worldName + " could not be loaded"); + continue; + } + + double x = rs.getDouble("location_x"); + double y = rs.getDouble("location_y"); + double z = rs.getDouble("location_z"); + float yaw = rs.getFloat("location_yaw"); + float pitch = rs.getFloat("location_pitch"); + Location location = new Location(world, x, y, z, yaw, pitch); + + Island island = new Island(ownerUUID, islandType, location); + + // Set basic properties + String title = rs.getString("display_title"); + if (title != null) island.setDisplayTitle(title); + String desc = rs.getString("display_description"); + if (desc != null) island.setDisplayDescription(desc); + island.setLocked(rs.getBoolean("locked")); + island.setAdventureModeForVisitors(rs.getBoolean("adventure_mode_for_visitors")); + + // Load display icon + String iconJson = rs.getString("display_icon"); + if (iconJson != null && !iconJson.isEmpty()) { + island.setDisplayIcon(deserializeItemStack(iconJson)); + } + + // Load last online time + long lastOnline = rs.getLong("last_online_time"); + if (lastOnline > 0) { + try { + java.lang.reflect.Field field = Island.class.getDeclaredField("lastOnlineTime"); + field.setAccessible(true); + field.set(island, lastOnline); + } catch (Exception e) { + island.updateLastOnlineTime(); + } + } + + // Load home location + String homeWorldName = rs.getString("home_world"); + if (homeWorldName != null) { + World homeWorld = getOrCreateWorld(homeWorldName, islandId); + if (homeWorld == null) homeWorld = world; + island.setHomeLocation(new Location(homeWorld, + rs.getDouble("home_x"), rs.getDouble("home_y"), rs.getDouble("home_z"), + rs.getFloat("home_yaw"), rs.getFloat("home_pitch"))); + } + + // Load visit location + String visitWorldName = rs.getString("visit_world"); + if (visitWorldName != null) { + World visitWorld = getOrCreateWorld(visitWorldName, islandId); + if (visitWorld == null) visitWorld = world; + island.setVisitLocation(new Location(visitWorld, + rs.getDouble("visit_x"), rs.getDouble("visit_y"), rs.getDouble("visit_z"), + rs.getFloat("visit_yaw"), rs.getFloat("visit_pitch"))); + } + + // Load visitor permissions + loadVisitorPermissions(ownerUUID, island); + + // Load coop members + loadCoopMembers(ownerUUID, island); + + // Load pending invites + loadPendingInvites(ownerUUID, island); + + // Load votes + loadVotes(ownerUUID, island); + + // Load custom permissions + loadCustomPermissions(ownerUUID, island); + + islands.put(ownerUUID, island); + } catch (Exception e) { + plugin.getLogger().warning("Failed to load island from H2: " + e.getMessage()); + } + } + } + + plugin.getLogger().info("Loaded " + islands.size() + " islands from H2 database"); + } catch (SQLException e) { + plugin.getLogger().log(Level.SEVERE, "Failed to load islands from H2 database", e); + } + + return islands; + } + + /** + * Save a single island to the database + */ + public void saveIsland(Island island) { + try { + connection.setAutoCommit(false); + deleteIslandData(island.getOwnerUUID()); + insertIsland(island); + connection.commit(); + connection.setAutoCommit(true); + } catch (SQLException e) { + rollback(); + plugin.getLogger().log(Level.SEVERE, "Failed to save island to H2: " + island.getIslandId(), e); + } + } + + /** + * Delete an island from the database + */ + public void deleteIsland(UUID ownerUUID) { + try { + connection.setAutoCommit(false); + deleteIslandData(ownerUUID); + connection.commit(); + connection.setAutoCommit(true); + plugin.getLogger().info("Deleted island data for " + ownerUUID + " from H2"); + } catch (SQLException e) { + rollback(); + plugin.getLogger().log(Level.SEVERE, "Failed to delete island from H2: " + ownerUUID, e); + } + } + + private void deleteIslandData(UUID ownerUUID) throws SQLException { + String uuid = ownerUUID.toString(); + try (PreparedStatement ps1 = connection.prepareStatement("DELETE FROM island_custom_permissions WHERE owner_uuid = ?"); + PreparedStatement ps2 = connection.prepareStatement("DELETE FROM island_votes WHERE owner_uuid = ?"); + PreparedStatement ps3 = connection.prepareStatement("DELETE FROM island_pending_invites WHERE owner_uuid = ?"); + PreparedStatement ps4 = connection.prepareStatement("DELETE FROM island_coop_members WHERE owner_uuid = ?"); + PreparedStatement ps5 = connection.prepareStatement("DELETE FROM island_visitor_permissions WHERE owner_uuid = ?"); + PreparedStatement ps6 = connection.prepareStatement("DELETE FROM islands WHERE owner_uuid = ?")) { + ps1.setString(1, uuid); ps1.executeUpdate(); + ps2.setString(1, uuid); ps2.executeUpdate(); + ps3.setString(1, uuid); ps3.executeUpdate(); + ps4.setString(1, uuid); ps4.executeUpdate(); + ps5.setString(1, uuid); ps5.executeUpdate(); + ps6.setString(1, uuid); ps6.executeUpdate(); + } + + // Also delete settings for this island's islandId + // We need to find the islandId first from the islands table before deletion + // This is handled in the caller + } + + private void insertIsland(Island island) throws SQLException { + String uuid = island.getOwnerUUID().toString(); + + // Insert main island data + try (PreparedStatement ps = connection.prepareStatement( + "INSERT INTO islands (owner_uuid, island_id, island_type, display_title, display_description, display_icon, " + + "locked, adventure_mode_for_visitors, last_online_time, " + + "location_world, location_x, location_y, location_z, location_yaw, location_pitch, " + + "home_world, home_x, home_y, home_z, home_yaw, home_pitch, " + + "visit_world, visit_x, visit_y, visit_z, visit_yaw, visit_pitch) " + + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")) { + + ps.setString(1, uuid); + ps.setString(2, island.getIslandId()); + ps.setString(3, island.getIslandType()); + ps.setString(4, island.getDisplayTitle()); + ps.setString(5, island.getDisplayDescription()); + ps.setString(6, island.getDisplayIcon() != null ? serializeItemStack(island.getDisplayIcon()) : null); + ps.setBoolean(7, island.isLocked()); + ps.setBoolean(8, island.isAdventureModeForVisitors()); + ps.setLong(9, island.getLastOnlineTime()); + + // Location + Location loc = island.getLocation(); + if (loc != null && loc.getWorld() != null) { + ps.setString(10, loc.getWorld().getName()); + ps.setDouble(11, loc.getX()); + ps.setDouble(12, loc.getY()); + ps.setDouble(13, loc.getZ()); + ps.setFloat(14, loc.getYaw()); + ps.setFloat(15, loc.getPitch()); + } + + // Home location + Location home = island.getHomeLocation(); + if (home != null && home.getWorld() != null && home != loc) { + ps.setString(16, home.getWorld().getName()); + ps.setDouble(17, home.getX()); + ps.setDouble(18, home.getY()); + ps.setDouble(19, home.getZ()); + ps.setFloat(20, home.getYaw()); + ps.setFloat(21, home.getPitch()); + } + + // Visit location + Location visit = island.getVisitLocation(); + if (visit != null && visit.getWorld() != null && visit != home) { + ps.setString(22, visit.getWorld().getName()); + ps.setDouble(23, visit.getX()); + ps.setDouble(24, visit.getY()); + ps.setDouble(25, visit.getZ()); + ps.setFloat(26, visit.getYaw()); + ps.setFloat(27, visit.getPitch()); + } + + ps.executeUpdate(); + } + + // Insert visitor permissions + try (PreparedStatement ps = connection.prepareStatement( + "INSERT INTO island_visitor_permissions " + + "(owner_uuid, can_break_blocks, can_place_blocks, can_open_containers, can_pickup_items, " + + "can_drop_items, can_interact_entities, can_use_pvp, can_use_redstone) " + + "VALUES (?,?,?,?,?,?,?,?,?)")) { + ps.setString(1, uuid); + ps.setBoolean(2, island.canVisitorBreakBlocks()); + ps.setBoolean(3, island.canVisitorPlaceBlocks()); + ps.setBoolean(4, island.canVisitorOpenContainers()); + ps.setBoolean(5, island.canVisitorPickupItems()); + ps.setBoolean(6, island.canVisitorDropItems()); + ps.setBoolean(7, island.canVisitorInteractWithEntities()); + ps.setBoolean(8, island.canVisitorUsePvp()); + ps.setBoolean(9, island.canVisitorUseRedstone()); + ps.executeUpdate(); + } + + // Insert coop members + for (Map.Entry entry : island.getCoopMembers().entrySet()) { + try (PreparedStatement ps = connection.prepareStatement( + "INSERT INTO island_coop_members (owner_uuid, coop_uuid, role) VALUES (?,?,?)")) { + ps.setString(1, uuid); + ps.setString(2, entry.getKey().toString()); + ps.setString(3, entry.getValue().name()); + ps.executeUpdate(); + } + } + + // Insert pending invites + for (UUID invite : island.getPendingInvites()) { + try (PreparedStatement ps = connection.prepareStatement( + "INSERT INTO island_pending_invites (owner_uuid, invite_uuid) VALUES (?,?)")) { + ps.setString(1, uuid); + ps.setString(2, invite.toString()); + ps.executeUpdate(); + } + } + + // Insert votes (only recent ones - last 7 days) + long sevenDaysAgo = System.currentTimeMillis() - (7 * 24 * 60 * 60 * 1000L); + for (Map.Entry entry : island.getVotes().entrySet()) { + if (entry.getValue() > sevenDaysAgo) { + try (PreparedStatement ps = connection.prepareStatement( + "INSERT INTO island_votes (owner_uuid, voter_uuid, vote_time) VALUES (?,?,?)")) { + ps.setString(1, uuid); + ps.setString(2, entry.getKey().toString()); + ps.setLong(3, entry.getValue()); + ps.executeUpdate(); + } + } + } + + // Insert custom permissions + for (Map.Entry> entry : island.getCustomPlayerPermissions().entrySet()) { + for (IslandPermission perm : entry.getValue()) { + try (PreparedStatement ps = connection.prepareStatement( + "INSERT INTO island_custom_permissions (owner_uuid, player_uuid, permission_node) VALUES (?,?,?)")) { + ps.setString(1, uuid); + ps.setString(2, entry.getKey().toString()); + ps.setString(3, perm.getNode()); + ps.executeUpdate(); + } + } + } + } + + private void loadVisitorPermissions(UUID ownerUUID, Island island) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement( + "SELECT * FROM island_visitor_permissions WHERE owner_uuid = ?")) { + ps.setString(1, ownerUUID.toString()); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + island.setVisitorCanBreakBlocks(rs.getBoolean("can_break_blocks")); + island.setVisitorCanPlaceBlocks(rs.getBoolean("can_place_blocks")); + island.setVisitorCanOpenContainers(rs.getBoolean("can_open_containers")); + island.setVisitorCanPickupItems(rs.getBoolean("can_pickup_items")); + island.setVisitorCanDropItems(rs.getBoolean("can_drop_items")); + island.setVisitorCanInteractWithEntities(rs.getBoolean("can_interact_entities")); + island.setVisitorCanUsePvp(rs.getBoolean("can_use_pvp")); + island.setVisitorCanUseRedstone(rs.getBoolean("can_use_redstone")); + } + } + } + } + + private void loadCoopMembers(UUID ownerUUID, Island island) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement( + "SELECT coop_uuid, role FROM island_coop_members WHERE owner_uuid = ?")) { + ps.setString(1, ownerUUID.toString()); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + UUID coopUuid = UUID.fromString(rs.getString("coop_uuid")); + Island.CoopRole role = Island.CoopRole.valueOf(rs.getString("role")); + island.addCoopMember(coopUuid, role); + } + } + } + } + + private void loadPendingInvites(UUID ownerUUID, Island island) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement( + "SELECT invite_uuid FROM island_pending_invites WHERE owner_uuid = ?")) { + ps.setString(1, ownerUUID.toString()); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + island.addPendingInvite(UUID.fromString(rs.getString("invite_uuid"))); + } + } + } + } + + private void loadVotes(UUID ownerUUID, Island island) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement( + "SELECT voter_uuid, vote_time FROM island_votes WHERE owner_uuid = ?")) { + ps.setString(1, ownerUUID.toString()); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + island.getVotes().put( + UUID.fromString(rs.getString("voter_uuid")), + rs.getLong("vote_time") + ); + } + } + } + } + + private void loadCustomPermissions(UUID ownerUUID, Island island) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement( + "SELECT player_uuid, permission_node FROM island_custom_permissions WHERE owner_uuid = ?")) { + ps.setString(1, ownerUUID.toString()); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + UUID playerUuid = UUID.fromString(rs.getString("player_uuid")); + String node = rs.getString("permission_node"); + for (IslandPermission perm : IslandPermission.values()) { + if (perm.getNode().equals(node)) { + island.addCustomPermission(playerUuid, perm); + break; + } + } + } + } + } + } + + // ==================== Island Settings Operations ==================== + + /** + * Load all island settings from the database + */ + public Map, Object>> loadAllIslandSettings() { + Map, Object>> allSettings = new HashMap<>(); + if (connection == null) return allSettings; + + try (PreparedStatement ps = connection.prepareStatement("SELECT DISTINCT island_id FROM island_settings"); + ResultSet rs = ps.executeQuery()) { + + List islandIds = new ArrayList<>(); + while (rs.next()) { + islandIds.add(rs.getString("island_id")); + } + + for (String islandId : islandIds) { + allSettings.put(islandId, loadIslandSettings(islandId)); + } + } catch (SQLException e) { + plugin.getLogger().log(Level.SEVERE, "Failed to load island settings from H2", e); + } + + return allSettings; + } + + /** + * Load settings for a specific island + */ + public Map, Object> loadIslandSettings(String islandId) { + Map, Object> settings = new HashMap<>(); + + try (PreparedStatement ps = connection.prepareStatement( + "SELECT game_rule, value, value_type FROM island_settings WHERE island_id = ?")) { + ps.setString(1, islandId); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + String ruleName = rs.getString("game_rule"); + String valueStr = rs.getString("value"); + String valueType = rs.getString("value_type"); + + GameRule gameRule = GameRule.getByName(ruleName); + if (gameRule == null) continue; + + if ("BOOLEAN".equals(valueType) && gameRule.getType() == Boolean.class) { + settings.put(gameRule, Boolean.parseBoolean(valueStr)); + } else if ("INTEGER".equals(valueType) && gameRule.getType() == Integer.class) { + settings.put(gameRule, Integer.parseInt(valueStr)); + } + } + } + } catch (SQLException e) { + plugin.getLogger().log(Level.WARNING, "Failed to load settings for island " + islandId, e); + } + + return settings; + } + + /** + * Save all island settings to the database + */ + public void saveAllIslandSettings(Map, Object>> allSettings) { + try { + connection.setAutoCommit(false); + + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate("DELETE FROM island_settings"); + } + + for (Map.Entry, Object>> entry : allSettings.entrySet()) { + saveIslandSettings(entry.getKey(), entry.getValue()); + } + + connection.commit(); + connection.setAutoCommit(true); + } catch (SQLException e) { + rollback(); + plugin.getLogger().log(Level.SEVERE, "Failed to save island settings to H2", e); + } + } + + /** + * Save settings for a specific island + */ + public void saveIslandSettings(String islandId, Map, Object> settings) { + try (PreparedStatement ps = connection.prepareStatement( + "MERGE INTO island_settings (island_id, game_rule, value, value_type) KEY (island_id, game_rule) VALUES (?,?,?,?)")) { + + for (Map.Entry, Object> entry : settings.entrySet()) { + GameRule gameRule = entry.getKey(); + Object value = entry.getValue(); + + ps.setString(1, islandId); + ps.setString(2, gameRule.getName()); + + if (gameRule.getType() == Boolean.class) { + ps.setString(3, String.valueOf(value)); + ps.setString(4, "BOOLEAN"); + } else if (gameRule.getType() == Integer.class) { + ps.setString(3, String.valueOf(value)); + ps.setString(4, "INTEGER"); + } + ps.addBatch(); + } + ps.executeBatch(); + } catch (SQLException e) { + plugin.getLogger().log(Level.WARNING, "Failed to save settings for island " + islandId, e); + } + } + + /** + * Delete settings for a specific island + */ + public void deleteIslandSettings(String islandId) { + try (PreparedStatement ps = connection.prepareStatement("DELETE FROM island_settings WHERE island_id = ?")) { + ps.setString(1, islandId); + ps.executeUpdate(); + } catch (SQLException e) { + plugin.getLogger().log(Level.WARNING, "Failed to delete settings for island " + islandId, e); + } + } + + // ==================== Player Data Operations ==================== + + /** + * Load all player data from the database + */ + public Map loadAllPlayerData() { + Map lastLocations = new HashMap<>(); + if (connection == null) return lastLocations; + + try (PreparedStatement ps = connection.prepareStatement("SELECT * FROM player_data"); + ResultSet rs = ps.executeQuery()) { + + while (rs.next()) { + try { + UUID playerUUID = UUID.fromString(rs.getString("player_uuid")); + String worldName = rs.getString("last_world"); + if (worldName == null) continue; + + World world = Bukkit.getWorld(worldName); + if (world == null) continue; + + Location loc = new Location(world, + rs.getDouble("last_x"), rs.getDouble("last_y"), rs.getDouble("last_z"), + rs.getFloat("last_yaw"), rs.getFloat("last_pitch")); + lastLocations.put(playerUUID, loc); + } catch (Exception e) { + plugin.getLogger().warning("Failed to load player data row: " + e.getMessage()); + } + } + } catch (SQLException e) { + plugin.getLogger().log(Level.SEVERE, "Failed to load player data from H2", e); + } + + return lastLocations; + } + + /** + * Save all player data to the database + */ + public void saveAllPlayerData(Map lastLocations) { + try { + connection.setAutoCommit(false); + + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate("DELETE FROM player_data"); + } + + for (Map.Entry entry : lastLocations.entrySet()) { + Location loc = entry.getValue(); + if (loc == null || loc.getWorld() == null) continue; + + try (PreparedStatement ps = connection.prepareStatement( + "INSERT INTO player_data (player_uuid, last_world, last_x, last_y, last_z, last_yaw, last_pitch) " + + "VALUES (?,?,?,?,?,?,?)")) { + ps.setString(1, entry.getKey().toString()); + ps.setString(2, loc.getWorld().getName()); + ps.setDouble(3, loc.getX()); + ps.setDouble(4, loc.getY()); + ps.setDouble(5, loc.getZ()); + ps.setFloat(6, (float) loc.getYaw()); + ps.setFloat(7, (float) loc.getPitch()); + ps.executeUpdate(); + } + } + + connection.commit(); + connection.setAutoCommit(true); + plugin.getLogger().info("Saved player data for " + lastLocations.size() + " players to H2"); + } catch (SQLException e) { + rollback(); + plugin.getLogger().log(Level.SEVERE, "Failed to save player data to H2", e); + } + } + + /** + * Save a single player's data + */ + public void savePlayerData(UUID playerUUID, Location location) { + if (location == null || location.getWorld() == null) return; + + try (PreparedStatement ps = connection.prepareStatement( + "MERGE INTO player_data (player_uuid, last_world, last_x, last_y, last_z, last_yaw, last_pitch) " + + "KEY (player_uuid) VALUES (?,?,?,?,?,?,?)")) { + ps.setString(1, playerUUID.toString()); + ps.setString(2, location.getWorld().getName()); + ps.setDouble(3, location.getX()); + ps.setDouble(4, location.getY()); + ps.setDouble(5, location.getZ()); + ps.setFloat(6, (float) location.getYaw()); + ps.setFloat(7, (float) location.getPitch()); + ps.executeUpdate(); + } catch (SQLException e) { + plugin.getLogger().log(Level.WARNING, "Failed to save player data for " + playerUUID, e); + } + } + + /** + * Delete a player's data + */ + public void deletePlayerData(UUID playerUUID) { + try (PreparedStatement ps = connection.prepareStatement("DELETE FROM player_data WHERE player_uuid = ?")) { + ps.setString(1, playerUUID.toString()); + ps.executeUpdate(); + } catch (SQLException e) { + plugin.getLogger().log(Level.WARNING, "Failed to delete player data for " + playerUUID, e); + } + } + + // ==================== Utility Methods ==================== + + private World getOrCreateWorld(String worldName, String islandId) { + World world = Bukkit.getWorld(worldName); + if (world == null && plugin.getWorldManager() != null) { + world = plugin.getWorldManager().getOrLoadIslandWorld(islandId); + if (world != null && world.getName().equals(plugin.getWorldManager().getSkyBlockWorld().getName())) { + return null; + } + } + return world; + } + + @SuppressWarnings("unchecked") + private String serializeItemStack(ItemStack item) { + if (item == null) return null; + Map serialized = item.serialize(); + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : serialized.entrySet()) { + if (sb.length() > 0) sb.append(";"); + sb.append(entry.getKey()).append("=").append(entry.getValue()); + } + return sb.toString(); + } + + private ItemStack deserializeItemStack(String data) { + if (data == null || data.isEmpty()) return null; + try { + Map map = new HashMap<>(); + String[] pairs = data.split(";"); + for (String pair : pairs) { + int eq = pair.indexOf('='); + if (eq > 0) { + map.put(pair.substring(0, eq), pair.substring(eq + 1)); + } + } + return ItemStack.deserialize(map); + } catch (Exception e) { + plugin.getLogger().warning("Failed to deserialize ItemStack: " + e.getMessage()); + return null; + } + } + + private void rollback() { + try { + if (connection != null && !connection.isClosed()) { + connection.rollback(); + connection.setAutoCommit(true); + } + } catch (SQLException e) { + plugin.getLogger().log(Level.WARNING, "Failed to rollback transaction", e); + } + } +} diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandDataManager.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandDataManager.java index e2f8334..1ed8cc9 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandDataManager.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandDataManager.java @@ -2,523 +2,55 @@ import skyeblock.nobleskye.dev.skyeblock.SkyeBlockPlugin; import skyeblock.nobleskye.dev.skyeblock.models.Island; -import skyeblock.nobleskye.dev.skyeblock.permissions.IslandPermission; -import org.bukkit.Location; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.inventory.ItemStack; -import java.io.File; -import java.io.IOException; import java.util.*; /** - * Manages persistent storage of island data + * Manages persistent storage of island data via H2 database */ public class IslandDataManager { private final SkyeBlockPlugin plugin; - private File dataFile; - private FileConfiguration dataConfig; - - public IslandDataManager(SkyeBlockPlugin plugin) { + private final DatabaseManager db; + + public IslandDataManager(SkyeBlockPlugin plugin, DatabaseManager db) { this.plugin = plugin; - loadDataFile(); - } - - private void loadDataFile() { - dataFile = new File(plugin.getDataFolder(), "island-data.yml"); - if (!dataFile.exists()) { - try { - dataFile.createNewFile(); - } catch (IOException e) { - plugin.getLogger().severe("Failed to create island data file: " + e.getMessage()); - } - } - dataConfig = YamlConfiguration.loadConfiguration(dataFile); + this.db = db; } - + /** * Save all island data to persistent storage */ public void saveAllIslands(Map islands) { - // Clear existing data - dataConfig.set("islands", null); - - for (Map.Entry entry : islands.entrySet()) { - UUID ownerUUID = entry.getKey(); - Island island = entry.getValue(); - - String basePath = "islands." + ownerUUID.toString(); - - // Basic island data - dataConfig.set(basePath + ".island-id", island.getIslandId()); - dataConfig.set(basePath + ".island-type", island.getIslandType()); - dataConfig.set(basePath + ".display-title", island.getDisplayTitle()); - dataConfig.set(basePath + ".display-description", island.getDisplayDescription()); - dataConfig.set(basePath + ".locked", island.isLocked()); - dataConfig.set(basePath + ".adventure-mode-for-visitors", island.isAdventureModeForVisitors()); - dataConfig.set(basePath + ".last-online-time", island.getLastOnlineTime()); - - // Save granular visitor permissions - dataConfig.set(basePath + ".visitor-permissions.can-break-blocks", island.canVisitorBreakBlocks()); - dataConfig.set(basePath + ".visitor-permissions.can-place-blocks", island.canVisitorPlaceBlocks()); - dataConfig.set(basePath + ".visitor-permissions.can-open-containers", island.canVisitorOpenContainers()); - dataConfig.set(basePath + ".visitor-permissions.can-pickup-items", island.canVisitorPickupItems()); - dataConfig.set(basePath + ".visitor-permissions.can-drop-items", island.canVisitorDropItems()); - dataConfig.set(basePath + ".visitor-permissions.can-interact-entities", island.canVisitorInteractWithEntities()); - dataConfig.set(basePath + ".visitor-permissions.can-use-pvp", island.canVisitorUsePvp()); - dataConfig.set(basePath + ".visitor-permissions.can-use-redstone", island.canVisitorUseRedstone()); - - // Save location - Location loc = island.getLocation(); - if (loc != null) { - dataConfig.set(basePath + ".location.world", loc.getWorld().getName()); - dataConfig.set(basePath + ".location.x", loc.getX()); - dataConfig.set(basePath + ".location.y", loc.getY()); - dataConfig.set(basePath + ".location.z", loc.getZ()); - dataConfig.set(basePath + ".location.yaw", loc.getYaw()); - dataConfig.set(basePath + ".location.pitch", loc.getPitch()); - } - - // Save home location - Location homeLoc = island.getHomeLocation(); - if (homeLoc != null) { - dataConfig.set(basePath + ".home-location.world", homeLoc.getWorld().getName()); - dataConfig.set(basePath + ".home-location.x", homeLoc.getX()); - dataConfig.set(basePath + ".home-location.y", homeLoc.getY()); - dataConfig.set(basePath + ".home-location.z", homeLoc.getZ()); - dataConfig.set(basePath + ".home-location.yaw", homeLoc.getYaw()); - dataConfig.set(basePath + ".home-location.pitch", homeLoc.getPitch()); - } - - // Save visit location - Location visitLoc = island.getVisitLocation(); - if (visitLoc != null) { - dataConfig.set(basePath + ".visit-location.world", visitLoc.getWorld().getName()); - dataConfig.set(basePath + ".visit-location.x", visitLoc.getX()); - dataConfig.set(basePath + ".visit-location.y", visitLoc.getY()); - dataConfig.set(basePath + ".visit-location.z", visitLoc.getZ()); - dataConfig.set(basePath + ".visit-location.yaw", visitLoc.getYaw()); - dataConfig.set(basePath + ".visit-location.pitch", visitLoc.getPitch()); - } - - // Save display icon - ItemStack icon = island.getDisplayIcon(); - if (icon != null) { - dataConfig.set(basePath + ".display-icon", icon); - } - - // Save coop members - Map coopMembers = island.getCoopMembers(); - if (!coopMembers.isEmpty()) { - for (Map.Entry coopEntry : coopMembers.entrySet()) { - String coopPath = basePath + ".coop-members." + coopEntry.getKey().toString(); - dataConfig.set(coopPath, coopEntry.getValue().name()); - } - } - - // Save pending invites - Set pendingInvites = island.getPendingInvites(); - if (!pendingInvites.isEmpty()) { - List inviteList = new ArrayList<>(); - for (UUID invite : pendingInvites) { - inviteList.add(invite.toString()); - } - dataConfig.set(basePath + ".pending-invites", inviteList); - } - - // Save votes (only recent ones) - for (Map.Entry voteEntry : island.getVotes().entrySet()) { - // Only save votes from the last 7 days - long sevenDaysAgo = System.currentTimeMillis() - (7 * 24 * 60 * 60 * 1000L); - if (voteEntry.getValue() > sevenDaysAgo) { - String votePath = basePath + ".votes." + voteEntry.getKey().toString(); - dataConfig.set(votePath, voteEntry.getValue()); - } - } - - // Save custom permissions - Map> customPermissions = island.getCustomPlayerPermissions(); - if (!customPermissions.isEmpty()) { - for (Map.Entry> permEntry : customPermissions.entrySet()) { - String permPath = basePath + ".custom-permissions." + permEntry.getKey().toString(); - List permissionNodes = new ArrayList<>(); - for (IslandPermission perm : permEntry.getValue()) { - permissionNodes.add(perm.getNode()); - } - dataConfig.set(permPath, permissionNodes); - } - } - } - - try { - dataConfig.save(dataFile); - plugin.getLogger().info("Saved " + islands.size() + " islands to persistent storage"); - } catch (IOException e) { - plugin.getLogger().severe("Failed to save island data: " + e.getMessage()); - } + db.saveAllIslands(islands); } - + /** * Load all island data from persistent storage */ public Map loadAllIslands() { - Map islands = new HashMap<>(); - - if (!dataConfig.contains("islands")) { - return islands; - } - - for (String uuidString : dataConfig.getConfigurationSection("islands").getKeys(false)) { - try { - UUID ownerUUID = UUID.fromString(uuidString); - String basePath = "islands." + uuidString; - - // Load basic data - String islandId = dataConfig.getString(basePath + ".island-id"); - String islandType = dataConfig.getString(basePath + ".island-type"); - - // Load location - String worldName = dataConfig.getString(basePath + ".location.world"); - if (worldName == null) { - plugin.getLogger().warning("Skipping island " + islandId + " - no world specified"); - continue; - } - - // Check if world exists or can be loaded - org.bukkit.World world = plugin.getServer().getWorld(worldName); - if (world == null) { - // Try to load the world through WorldManager - world = plugin.getWorldManager().getOrLoadIslandWorld(islandId); - if (world == null || world.getName().equals(plugin.getWorldManager().getSkyBlockWorld().getName())) { - plugin.getLogger().warning("Skipping island " + islandId + " - world " + worldName + " could not be loaded"); - continue; - } - } - - double x = dataConfig.getDouble(basePath + ".location.x"); - double y = dataConfig.getDouble(basePath + ".location.y"); - double z = dataConfig.getDouble(basePath + ".location.z"); - float yaw = (float) dataConfig.getDouble(basePath + ".location.yaw"); - float pitch = (float) dataConfig.getDouble(basePath + ".location.pitch"); - - Location location = new Location(world, x, y, z, yaw, pitch); - - // Create island object - Island island = new Island(ownerUUID, islandType, location); - - // Load optional data - if (dataConfig.contains(basePath + ".display-title")) { - island.setDisplayTitle(dataConfig.getString(basePath + ".display-title")); - } - if (dataConfig.contains(basePath + ".display-description")) { - island.setDisplayDescription(dataConfig.getString(basePath + ".display-description")); - } - if (dataConfig.contains(basePath + ".locked")) { - island.setLocked(dataConfig.getBoolean(basePath + ".locked")); - } - if (dataConfig.contains(basePath + ".adventure-mode-for-visitors")) { - island.setAdventureModeForVisitors(dataConfig.getBoolean(basePath + ".adventure-mode-for-visitors")); - } - - // Load granular visitor permissions - if (dataConfig.contains(basePath + ".visitor-permissions.can-break-blocks")) { - island.setVisitorCanBreakBlocks(dataConfig.getBoolean(basePath + ".visitor-permissions.can-break-blocks")); - } - if (dataConfig.contains(basePath + ".visitor-permissions.can-place-blocks")) { - island.setVisitorCanPlaceBlocks(dataConfig.getBoolean(basePath + ".visitor-permissions.can-place-blocks")); - } - if (dataConfig.contains(basePath + ".visitor-permissions.can-open-containers")) { - island.setVisitorCanOpenContainers(dataConfig.getBoolean(basePath + ".visitor-permissions.can-open-containers")); - } - if (dataConfig.contains(basePath + ".visitor-permissions.can-pickup-items")) { - island.setVisitorCanPickupItems(dataConfig.getBoolean(basePath + ".visitor-permissions.can-pickup-items")); - } - if (dataConfig.contains(basePath + ".visitor-permissions.can-drop-items")) { - island.setVisitorCanDropItems(dataConfig.getBoolean(basePath + ".visitor-permissions.can-drop-items")); - } - if (dataConfig.contains(basePath + ".visitor-permissions.can-interact-entities")) { - island.setVisitorCanInteractWithEntities(dataConfig.getBoolean(basePath + ".visitor-permissions.can-interact-entities")); - } - if (dataConfig.contains(basePath + ".visitor-permissions.can-use-pvp")) { - island.setVisitorCanUsePvp(dataConfig.getBoolean(basePath + ".visitor-permissions.can-use-pvp")); - } - if (dataConfig.contains(basePath + ".visitor-permissions.can-use-redstone")) { - island.setVisitorCanUseRedstone(dataConfig.getBoolean(basePath + ".visitor-permissions.can-use-redstone")); - } - if (dataConfig.contains(basePath + ".last-online-time")) { - // Use reflection to set the last online time (as there's no setter) - try { - java.lang.reflect.Field field = Island.class.getDeclaredField("lastOnlineTime"); - field.setAccessible(true); - field.set(island, dataConfig.getLong(basePath + ".last-online-time")); - } catch (Exception e) { - // Fallback - update to current time - island.updateLastOnlineTime(); - } - } - - // Load home location - if (dataConfig.contains(basePath + ".home-location.world")) { - String homeWorldName = dataConfig.getString(basePath + ".home-location.world"); - org.bukkit.World homeWorld = plugin.getServer().getWorld(homeWorldName); - - // If world not found, try to load it through WorldManager - if (homeWorld == null) { - homeWorld = plugin.getWorldManager().getIslandWorld(islandId); - } - - if (homeWorld != null) { - double hx = dataConfig.getDouble(basePath + ".home-location.x"); - double hy = dataConfig.getDouble(basePath + ".home-location.y"); - double hz = dataConfig.getDouble(basePath + ".home-location.z"); - float hyaw = (float) dataConfig.getDouble(basePath + ".home-location.yaw"); - float hpitch = (float) dataConfig.getDouble(basePath + ".home-location.pitch"); - - island.setHomeLocation(new Location(homeWorld, hx, hy, hz, hyaw, hpitch)); - } else { - plugin.getLogger().warning("Could not load home location for island " + islandId + " - world " + homeWorldName + " not found"); - } - } - - // Load visit location - if (dataConfig.contains(basePath + ".visit-location.world")) { - String visitWorldName = dataConfig.getString(basePath + ".visit-location.world"); - org.bukkit.World visitWorld = plugin.getServer().getWorld(visitWorldName); - - // If world not found, try to load it through WorldManager - if (visitWorld == null) { - visitWorld = plugin.getWorldManager().getIslandWorld(islandId); - } - - if (visitWorld != null) { - double vx = dataConfig.getDouble(basePath + ".visit-location.x"); - double vy = dataConfig.getDouble(basePath + ".visit-location.y"); - double vz = dataConfig.getDouble(basePath + ".visit-location.z"); - float vyaw = (float) dataConfig.getDouble(basePath + ".visit-location.yaw"); - float vpitch = (float) dataConfig.getDouble(basePath + ".visit-location.pitch"); - - island.setVisitLocation(new Location(visitWorld, vx, vy, vz, vyaw, vpitch)); - } else { - plugin.getLogger().warning("Could not load visit location for island " + islandId + " - world " + visitWorldName + " not found"); - } - } - - // Load display icon - if (dataConfig.contains(basePath + ".display-icon")) { - ItemStack icon = dataConfig.getItemStack(basePath + ".display-icon"); - island.setDisplayIcon(icon); - } - - // Load coop members - if (dataConfig.contains(basePath + ".coop-members")) { - for (String coopUuidString : dataConfig.getConfigurationSection(basePath + ".coop-members").getKeys(false)) { - try { - UUID coopUuid = UUID.fromString(coopUuidString); - String roleName = dataConfig.getString(basePath + ".coop-members." + coopUuidString); - Island.CoopRole role = Island.CoopRole.valueOf(roleName); - island.addCoopMember(coopUuid, role); - } catch (Exception e) { - plugin.getLogger().warning("Failed to load coop member " + coopUuidString + " for island " + islandId); - } - } - } - - // Load pending invites - if (dataConfig.contains(basePath + ".pending-invites")) { - List inviteStrings = dataConfig.getStringList(basePath + ".pending-invites"); - for (String inviteString : inviteStrings) { - try { - UUID inviteUuid = UUID.fromString(inviteString); - island.addPendingInvite(inviteUuid); - } catch (Exception e) { - plugin.getLogger().warning("Failed to load pending invite " + inviteString + " for island " + islandId); - } - } - } - - // Load votes - if (dataConfig.contains(basePath + ".votes")) { - for (String voteUuidString : dataConfig.getConfigurationSection(basePath + ".votes").getKeys(false)) { - try { - UUID voteUuid = UUID.fromString(voteUuidString); - long voteTime = dataConfig.getLong(basePath + ".votes." + voteUuidString); - // Directly add to votes map to preserve timestamp - island.getVotes().put(voteUuid, voteTime); - } catch (Exception e) { - plugin.getLogger().warning("Failed to load vote " + voteUuidString + " for island " + islandId); - } - } - } - - // Load custom permissions - if (dataConfig.contains(basePath + ".custom-permissions")) { - for (String permUuidString : dataConfig.getConfigurationSection(basePath + ".custom-permissions").getKeys(false)) { - try { - UUID playerUuid = UUID.fromString(permUuidString); - List permissionNodes = dataConfig.getStringList(basePath + ".custom-permissions." + permUuidString); - - for (String node : permissionNodes) { - for (IslandPermission perm : IslandPermission.values()) { - if (perm.getNode().equals(node)) { - island.addCustomPermission(playerUuid, perm); - break; - } - } - } - } catch (Exception e) { - plugin.getLogger().warning("Failed to load custom permissions for player " + permUuidString + " on island " + islandId); - } - } - } - - islands.put(ownerUUID, island); - - } catch (Exception e) { - plugin.getLogger().warning("Failed to load island for UUID " + uuidString + ": " + e.getMessage()); - } - } - - plugin.getLogger().info("Loaded " + islands.size() + " islands from persistent storage"); - return islands; + return db.loadAllIslands(); } - + /** * Save a single island to persistent storage */ public void saveIsland(Island island) { - Map singleIslandMap = new HashMap<>(); - singleIslandMap.put(island.getOwnerUUID(), island); - - // Load existing data first - Map allIslands = loadAllIslands(); - allIslands.put(island.getOwnerUUID(), island); - - // Save all data - saveAllIslands(allIslands); + db.saveIsland(island); } - + /** * Delete an island from persistent storage */ public void deleteIsland(UUID ownerUUID) { - if (dataConfig.contains("islands." + ownerUUID.toString())) { - dataConfig.set("islands." + ownerUUID.toString(), null); - try { - dataConfig.save(dataFile); - plugin.getLogger().info("Deleted island data for player " + ownerUUID); - } catch (IOException e) { - plugin.getLogger().severe("Failed to delete island data: " + e.getMessage()); - } - } + // Get island ID before deleting so we can clean up settings too + db.deleteIsland(ownerUUID); } - + /** * Load a specific island for a player */ public Island loadIsland(UUID playerUUID) { - if (!dataConfig.contains("islands." + playerUUID.toString())) { - return null; - } - - try { - String basePath = "islands." + playerUUID.toString(); - - // Load basic data - String islandId = dataConfig.getString(basePath + ".island-id"); - String islandType = dataConfig.getString(basePath + ".island-type"); - - if (islandId == null) { - plugin.getLogger().warning("Island data for " + playerUUID + " has no island-id"); - return null; - } - - // Load location - String worldName = dataConfig.getString(basePath + ".location.world"); - if (worldName == null) { - plugin.getLogger().warning("Island " + islandId + " has no world specified"); - return null; - } - - // Try to get or load the world - org.bukkit.World world = plugin.getServer().getWorld(worldName); - if (world == null) { - // Try to load the world through WorldManager - world = plugin.getWorldManager().getIslandWorld(islandId); - if (world == null) { - plugin.getLogger().warning("Could not load world " + worldName + " for island " + islandId); - // Continue anyway - the world might be loaded later - world = plugin.getWorldManager().getSkyBlockWorld(); // Use main world as fallback - } - } - - double x = dataConfig.getDouble(basePath + ".location.x"); - double y = dataConfig.getDouble(basePath + ".location.y"); - double z = dataConfig.getDouble(basePath + ".location.z"); - float yaw = (float) dataConfig.getDouble(basePath + ".location.yaw"); - float pitch = (float) dataConfig.getDouble(basePath + ".location.pitch"); - - Location location = new Location(world, x, y, z, yaw, pitch); - - // Create island object - Island island = new Island(playerUUID, islandType != null ? islandType : "vanilla", location); - - // Load additional properties - if (dataConfig.contains(basePath + ".locked")) { - island.setLocked(dataConfig.getBoolean(basePath + ".locked")); - } - - // Load home location if it exists - if (dataConfig.contains(basePath + ".home-location")) { - String homeWorldName = dataConfig.getString(basePath + ".home-location.world"); - org.bukkit.World homeWorld = plugin.getServer().getWorld(homeWorldName != null ? homeWorldName : worldName); - if (homeWorld != null) { - double homeX = dataConfig.getDouble(basePath + ".home-location.x"); - double homeY = dataConfig.getDouble(basePath + ".home-location.y"); - double homeZ = dataConfig.getDouble(basePath + ".home-location.z"); - float homeYaw = (float) dataConfig.getDouble(basePath + ".home-location.yaw"); - float homePitch = (float) dataConfig.getDouble(basePath + ".home-location.pitch"); - - island.setHomeLocation(new Location(homeWorld, homeX, homeY, homeZ, homeYaw, homePitch)); - } - } - - // Load visit location if it exists - if (dataConfig.contains(basePath + ".visit-location")) { - String visitWorldName = dataConfig.getString(basePath + ".visit-location.world"); - org.bukkit.World visitWorld = plugin.getServer().getWorld(visitWorldName != null ? visitWorldName : worldName); - if (visitWorld != null) { - double visitX = dataConfig.getDouble(basePath + ".visit-location.x"); - double visitY = dataConfig.getDouble(basePath + ".visit-location.y"); - double visitZ = dataConfig.getDouble(basePath + ".visit-location.z"); - float visitYaw = (float) dataConfig.getDouble(basePath + ".visit-location.yaw"); - float visitPitch = (float) dataConfig.getDouble(basePath + ".visit-location.pitch"); - - island.setVisitLocation(new Location(visitWorld, visitX, visitY, visitZ, visitYaw, visitPitch)); - } - } - - // Load coop players (using the correct method name) - if (dataConfig.contains(basePath + ".coop-players")) { - List coopPlayerStrings = dataConfig.getStringList(basePath + ".coop-players"); - for (String coopPlayerString : coopPlayerStrings) { - try { - UUID coopUUID = UUID.fromString(coopPlayerString); - // Add as coop member with default role - island.addCoopMember(coopUUID, Island.CoopRole.MEMBER); - } catch (IllegalArgumentException e) { - plugin.getLogger().warning("Invalid coop player UUID: " + coopPlayerString); - } - } - } - - plugin.getLogger().info("Successfully loaded island " + islandId + " for player " + playerUUID); - return island; - - } catch (Exception e) { - plugin.getLogger().severe("Error loading island for player " + playerUUID + ": " + e.getMessage()); - e.printStackTrace(); - return null; - } + Map singleMap = db.loadAllIslands(); + return singleMap.get(playerUUID); } } diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandManager.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandManager.java index 9fba523..a68072d 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandManager.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandManager.java @@ -33,7 +33,7 @@ public IslandManager(SkyeBlockPlugin plugin) { */ public void initialize() { plugin.getLogger().info("Initializing Island Manager..."); - this.dataManager = new IslandDataManager(plugin); + this.dataManager = new IslandDataManager(plugin, plugin.getDatabaseManager()); loadAllIslands(); plugin.getLogger().info("Island Manager initialization complete. Players can now be teleported to their islands immediately."); } diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandSettingsManager.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandSettingsManager.java index d63276d..c0e2588 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandSettingsManager.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandSettingsManager.java @@ -4,32 +4,27 @@ import org.bukkit.GameRule; import org.bukkit.World; import org.bukkit.entity.Player; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import java.io.File; -import java.io.IOException; import java.util.*; public class IslandSettingsManager { private final SkyeBlockPlugin plugin; + private final DatabaseManager db; private final Map, Object>> islandSettings; - private File settingsFile; - private FileConfiguration settingsConfig; - + // Supported gamerules with their default values private final Map, Object> defaultGameRules; - - public IslandSettingsManager(SkyeBlockPlugin plugin) { + + public IslandSettingsManager(SkyeBlockPlugin plugin, DatabaseManager db) { this.plugin = plugin; + this.db = db; this.islandSettings = new HashMap<>(); this.defaultGameRules = new HashMap<>(); - + initializeDefaultGameRules(); - loadSettingsFile(); loadIslandSettings(); } - + @SuppressWarnings("unchecked") private void initializeDefaultGameRules() { // Boolean gamerules @@ -58,7 +53,7 @@ private void initializeDefaultGameRules() { defaultGameRules.put(GameRule.DO_TRADER_SPAWNING, true); defaultGameRules.put(GameRule.FORGIVE_DEAD_PLAYERS, true); defaultGameRules.put(GameRule.UNIVERSAL_ANGER, false); - + // Integer gamerules defaultGameRules.put(GameRule.RANDOM_TICK_SPEED, 3); defaultGameRules.put(GameRule.SPAWN_RADIUS, 10); @@ -66,92 +61,56 @@ private void initializeDefaultGameRules() { defaultGameRules.put(GameRule.MAX_COMMAND_CHAIN_LENGTH, 65536); defaultGameRules.put(GameRule.PLAYERS_SLEEPING_PERCENTAGE, 100); } - - private void loadSettingsFile() { - settingsFile = new File(plugin.getDataFolder(), "island-settings.yml"); - if (!settingsFile.exists()) { - plugin.saveResource("island-settings.yml", false); - } - settingsConfig = YamlConfiguration.loadConfiguration(settingsFile); - } - + private void loadIslandSettings() { - if (settingsConfig.contains("islands")) { - for (String islandId : settingsConfig.getConfigurationSection("islands").getKeys(false)) { - Map, Object> settings = new HashMap<>(); - - for (GameRule gameRule : defaultGameRules.keySet()) { - String ruleName = gameRule.getName(); - String path = "islands." + islandId + "." + ruleName; - - if (settingsConfig.contains(path)) { - if (gameRule.getType() == Boolean.class) { - settings.put(gameRule, settingsConfig.getBoolean(path)); - } else if (gameRule.getType() == Integer.class) { - settings.put(gameRule, settingsConfig.getInt(path)); - } - } else { - settings.put(gameRule, defaultGameRules.get(gameRule)); - } - } - - islandSettings.put(islandId, settings); - } + Map, Object>> loaded = db.loadAllIslandSettings(); + + // Merge with defaults for any missing gamerules + for (Map.Entry, Object>> entry : loaded.entrySet()) { + Map, Object> settings = new HashMap<>(defaultGameRules); + settings.putAll(entry.getValue()); + islandSettings.put(entry.getKey(), settings); } + + plugin.getLogger().info("Loaded island settings for " + islandSettings.size() + " islands from H2"); } - + public void saveSettings() { - try { - // Clear existing settings - settingsConfig.set("islands", null); - - // Save all island settings - for (String islandId : islandSettings.keySet()) { - Map, Object> settings = islandSettings.get(islandId); - for (GameRule gameRule : settings.keySet()) { - String path = "islands." + islandId + "." + gameRule.getName(); - settingsConfig.set(path, settings.get(gameRule)); - } - } - - settingsConfig.save(settingsFile); - } catch (IOException e) { - plugin.getLogger().severe("Failed to save island settings: " + e.getMessage()); - } + db.saveAllIslandSettings(islandSettings); } - + public Map, Object> getIslandSettings(String islandId) { return islandSettings.getOrDefault(islandId, new HashMap<>(defaultGameRules)); } - + @SuppressWarnings("unchecked") public void setGameRule(String islandId, GameRule gameRule, T value) { islandSettings.computeIfAbsent(islandId, k -> new HashMap<>(defaultGameRules)) .put(gameRule, value); - + // Apply to the actual world if it exists World world = plugin.getWorldManager().getIslandWorld(islandId); if (world != null && !world.equals(plugin.getWorldManager().getSkyBlockWorld())) { world.setGameRule(gameRule, value); } - + saveSettings(); } - + @SuppressWarnings("unchecked") public T getGameRule(String islandId, GameRule gameRule) { Map, Object> settings = getIslandSettings(islandId); return (T) settings.getOrDefault(gameRule, defaultGameRules.get(gameRule)); } - + @SuppressWarnings("unchecked") public void applySettingsToWorld(String islandId, World world) { Map, Object> settings = getIslandSettings(islandId); - + for (Map.Entry, Object> entry : settings.entrySet()) { GameRule gameRule = entry.getKey(); Object value = entry.getValue(); - + try { if (gameRule.getType() == Boolean.class) { world.setGameRule((GameRule) gameRule, (Boolean) value); @@ -159,58 +118,52 @@ public void applySettingsToWorld(String islandId, World world) { world.setGameRule((GameRule) gameRule, (Integer) value); } } catch (Exception e) { - plugin.getLogger().warning("Failed to apply gamerule " + gameRule.getName() + + plugin.getLogger().warning("Failed to apply gamerule " + gameRule.getName() + " to world " + world.getName() + ": " + e.getMessage()); } } } - + public void createDefaultSettings(String islandId) { if (!islandSettings.containsKey(islandId)) { - islandSettings.put(islandId, new HashMap<>(defaultGameRules)); - saveSettings(); + Map, Object> defaults = new HashMap<>(defaultGameRules); + islandSettings.put(islandId, defaults); + db.saveIslandSettings(islandId, defaults); } } - + public void deleteIslandSettings(String islandId) { islandSettings.remove(islandId); - saveSettings(); + db.deleteIslandSettings(islandId); } - + public List> getAvailableGameRules(Player player) { List> available = new ArrayList<>(); - - // Check if player has admin bypass + if (player.hasPermission("skyeblock.gamerules.adminbypass")) { return new ArrayList<>(defaultGameRules.keySet()); } - - // Only show gamerules if player explicitly has permission + for (GameRule gameRule : defaultGameRules.keySet()) { String permissionNode = "skyeblock.gamerule." + gameRule.getName().toLowerCase(); - - // Only show the gamerule if the player explicitly has the permission if (player.hasPermission(permissionNode)) { available.add(gameRule); } } - + return available; } - + public Map, Object> getDefaultGameRules() { return new HashMap<>(defaultGameRules); } - + public boolean hasPermissionForGameRule(Player player, GameRule gameRule) { - // Check admin bypass first if (player.hasPermission("skyeblock.gamerules.adminbypass")) { return true; } - + String permissionNode = "skyeblock.gamerule." + gameRule.getName().toLowerCase(); - - // Only allow access if player explicitly has the permission return player.hasPermission(permissionNode); } } diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/PlayerDataManager.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/PlayerDataManager.java index bec2620..011bae9 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/PlayerDataManager.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/PlayerDataManager.java @@ -2,120 +2,49 @@ import skyeblock.nobleskye.dev.skyeblock.SkyeBlockPlugin; import org.bukkit.Location; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; -import java.io.File; -import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** - * Manages player data such as last locations and preferences + * Manages player data such as last locations and preferences via H2 database */ public class PlayerDataManager { private final SkyeBlockPlugin plugin; - private File dataFile; - private FileConfiguration dataConfig; + private final DatabaseManager db; private final Map lastLocations; - - public PlayerDataManager(SkyeBlockPlugin plugin) { + + public PlayerDataManager(SkyeBlockPlugin plugin, DatabaseManager db) { this.plugin = plugin; + this.db = db; this.lastLocations = new HashMap<>(); - loadDataFile(); loadPlayerData(); } - - private void loadDataFile() { - dataFile = new File(plugin.getDataFolder(), "player-data.yml"); - if (!dataFile.exists()) { - try { - dataFile.createNewFile(); - } catch (IOException e) { - plugin.getLogger().severe("Failed to create player data file: " + e.getMessage()); - } - } - dataConfig = YamlConfiguration.loadConfiguration(dataFile); - } - + /** * Load all player data from persistent storage */ private void loadPlayerData() { - if (!dataConfig.contains("players")) { - return; - } - - for (String uuidString : dataConfig.getConfigurationSection("players").getKeys(false)) { - try { - UUID playerUUID = UUID.fromString(uuidString); - String basePath = "players." + uuidString; - - // Load last location - if (dataConfig.contains(basePath + ".last-location.world")) { - String worldName = dataConfig.getString(basePath + ".last-location.world"); - org.bukkit.World world = plugin.getServer().getWorld(worldName); - - if (world != null) { - double x = dataConfig.getDouble(basePath + ".last-location.x"); - double y = dataConfig.getDouble(basePath + ".last-location.y"); - double z = dataConfig.getDouble(basePath + ".last-location.z"); - float yaw = (float) dataConfig.getDouble(basePath + ".last-location.yaw"); - float pitch = (float) dataConfig.getDouble(basePath + ".last-location.pitch"); - - Location lastLocation = new Location(world, x, y, z, yaw, pitch); - lastLocations.put(playerUUID, lastLocation); - } - } - } catch (Exception e) { - plugin.getLogger().warning("Failed to load player data for " + uuidString + ": " + e.getMessage()); - } - } - - plugin.getLogger().info("Loaded player data for " + lastLocations.size() + " players"); + lastLocations.putAll(db.loadAllPlayerData()); + plugin.getLogger().info("Loaded player data for " + lastLocations.size() + " players from H2"); } - + /** * Save all player data to persistent storage */ public void savePlayerData() { - // Clear existing player data - dataConfig.set("players", null); - - for (Map.Entry entry : lastLocations.entrySet()) { - UUID playerUUID = entry.getKey(); - Location location = entry.getValue(); - - String basePath = "players." + playerUUID.toString(); - - // Save last location - if (location != null && location.getWorld() != null) { - dataConfig.set(basePath + ".last-location.world", location.getWorld().getName()); - dataConfig.set(basePath + ".last-location.x", location.getX()); - dataConfig.set(basePath + ".last-location.y", location.getY()); - dataConfig.set(basePath + ".last-location.z", location.getZ()); - dataConfig.set(basePath + ".last-location.yaw", location.getYaw()); - dataConfig.set(basePath + ".last-location.pitch", location.getPitch()); - } - } - - try { - dataConfig.save(dataFile); - plugin.getLogger().info("Saved player data for " + lastLocations.size() + " players"); - } catch (IOException e) { - plugin.getLogger().severe("Failed to save player data: " + e.getMessage()); - } + db.saveAllPlayerData(lastLocations); } - + /** * Update a player's last location */ public void updateLastLocation(Player player) { updateLastLocation(player.getUniqueId(), player.getLocation()); } - + /** * Update a player's last location */ @@ -124,42 +53,43 @@ public void updateLastLocation(UUID playerUUID, Location location) { lastLocations.put(playerUUID, location.clone()); } } - + /** * Get a player's last location */ public Location getLastLocation(UUID playerUUID) { return lastLocations.get(playerUUID); } - + /** * Get a player's last location */ public Location getLastLocation(Player player) { return getLastLocation(player.getUniqueId()); } - + /** * Check if a player has a stored last location */ public boolean hasLastLocation(UUID playerUUID) { return lastLocations.containsKey(playerUUID); } - + /** * Check if a player has a stored last location */ public boolean hasLastLocation(Player player) { return hasLastLocation(player.getUniqueId()); } - + /** * Remove a player's last location data */ public void removePlayerData(UUID playerUUID) { lastLocations.remove(playerUUID); + db.deletePlayerData(playerUUID); } - + /** * Remove a player's last location data */ diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/SchematicManager.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/SchematicManager.java index c0b2118..575b8b0 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/SchematicManager.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/SchematicManager.java @@ -17,12 +17,20 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.logging.Level; public class SchematicManager { private final SkyeBlockPlugin plugin; private final File schematicFolder; + private static final String[] BUNDLED_SCHEMATICS = { + "advanced", "bare_bones", "campsite", "cozy_grove", "desert", + "fishermans_paradise", "igloo", "inverted", "mineshaft", + "nether_generic", "nether_jail", "olympus", "orchid", + "sandy_isle", "vanilla", "wilson" + }; + public SchematicManager(SkyeBlockPlugin plugin) { this.plugin = plugin; @@ -43,10 +51,32 @@ public SchematicManager(SkyeBlockPlugin plugin) { schematicFolder.mkdirs(); } + // Extract bundled schematics from resources if missing + extractBundledSchematics(); + // Create default schematic files if they don't exist createDefaultSchematics(); } + private void extractBundledSchematics() { + int extracted = 0; + for (String name : BUNDLED_SCHEMATICS) { + File target = new File(schematicFolder, name + ".schem"); + if (target.exists()) continue; + + try (InputStream is = plugin.getResource("schematics/" + name + ".schem")) { + if (is == null) continue; + java.nio.file.Files.copy(is, target.toPath()); + extracted++; + } catch (IOException e) { + plugin.getLogger().warning("Failed to extract schematic: " + name + ".schem"); + } + } + if (extracted > 0) { + plugin.getLogger().info("Extracted " + extracted + " bundled schematics to " + schematicFolder.getAbsolutePath()); + } + } + public boolean pasteSchematic(String schematicName, Location location) { plugin.getLogger().info("Attempting to paste schematic: " + schematicName + " at " + location); diff --git a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/WorldManager.java b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/WorldManager.java index 178c8a9..2df8cf9 100644 --- a/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/WorldManager.java +++ b/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/WorldManager.java @@ -1,6 +1,14 @@ package skyeblock.nobleskye.dev.skyeblock.managers; import skyeblock.nobleskye.dev.skyeblock.SkyeBlockPlugin; +import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.loaders.file.FileLoader; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.WorldCreator; @@ -8,124 +16,96 @@ import org.bukkit.plugin.Plugin; import java.io.File; -import java.util.HashMap; -import java.util.Map; +import java.io.IOException; public class WorldManager { private final SkyeBlockPlugin plugin; private World skyBlockWorld; private World skyBlockNetherWorld; - private Plugin slimePlugin; - private Object slimeLoader; - private final Map islandWorlds; + private AdvancedSlimePaperAPI aspAPI; + private SlimeLoader slimeLoader; private boolean slimeWorldEnabled = false; + private boolean aspApiAvailable = false; public WorldManager(SkyeBlockPlugin plugin) { this.plugin = plugin; - this.islandWorlds = new HashMap<>(); } public void initializeWorld() { - // Check for Advanced Slime World Manager (ASWM) checkForASWM(); - - // Initialize hub world instead of skyblock world createHubWorld(); - - // Note: No longer auto-creating nether/end worlds here - // Resource worlds are managed by ResourceWorldManager } private void checkForASWM() { try { - // Check for built-in ASWM (ASP servers) - if (checkForBuiltInASWM()) { + if (checkForASP() || checkForLegacySWM()) { return; } - - // Check for ASWM plugin - if (checkForASWMPlugin()) { - return; - } - - // Check for legacy SWM plugin - if (checkForLegacySWM()) { - return; - } - - plugin.getLogger().info("⚠ No SlimeWorldManager found. Islands will use standard Bukkit worlds."); - plugin.getLogger().info("⚠ For better performance, install ASWM plugin or use an ASP server with built-in ASWM."); - + plugin.getLogger().info("No SlimeWorldManager found. Islands will use standard Bukkit worlds."); } catch (Exception e) { plugin.getLogger().warning("Unexpected error during SlimeWorldManager detection: " + e.getMessage()); - e.printStackTrace(); } slimeWorldEnabled = false; } - - private boolean checkForBuiltInASWM() { + + private boolean checkForASP() { try { - plugin.getLogger().info("Checking for built-in ASWM (ASP server)..."); - - // Try to access ASWM API directly using the new package structure - Class slimePluginClass = Class.forName("com.infernalsuite.aswm.SlimePlugin"); - plugin.getLogger().info("Found built-in ASWM API class: " + slimePluginClass.getName()); - - // Try to get the plugin instance - Object aswmInstance = slimePluginClass.getMethod("getInstance").invoke(null); - if (aswmInstance != null) { - plugin.getLogger().info("Successfully got built-in ASWM instance"); - - // Try to get the file loader using the new API - Class loaderManagerClass = Class.forName("com.infernalsuite.aswm.api.AdvancedSlimeWorldManagerAPI"); - Object apiInstance = loaderManagerClass.getMethod("instance").invoke(null); - - if (apiInstance != null) { - Object loader = loaderManagerClass.getMethod("getLoader", String.class).invoke(apiInstance, "file"); + plugin.getLogger().info("Checking for AdvancedSlimePaper API (ASP 4.0.0)..."); + aspAPI = AdvancedSlimePaperAPI.instance(); + if (aspAPI != null) { + plugin.getLogger().info("Found ASP API instance"); + File worldsDir = new File(plugin.getDataFolder(), "slime_worlds"); + slimeLoader = new FileLoader(worldsDir); + aspApiAvailable = true; + slimeWorldEnabled = true; + plugin.getLogger().info("Advanced Slime World Manager (ASP 4.0.0) initialized"); + return true; + } + } catch (NoClassDefFoundError e) { + plugin.getLogger().info("ASP API not available"); + } catch (Exception e) { + plugin.getLogger().info("Failed to initialize ASP API: " + e.getMessage()); + } + return false; + } + + private boolean checkForLegacySWM() { + try { + plugin.getLogger().info("Checking for legacy SlimeWorldManager plugin..."); + Plugin swmPlugin = plugin.getServer().getPluginManager().getPlugin("SlimeWorldManager"); + if (swmPlugin != null && swmPlugin.isEnabled()) { + Class apiClass = Class.forName("com.grinderwolf.swm.api.SlimePlugin"); + Object api = apiClass.getMethod("getInstance").invoke(null); + if (api != null) { + Object loader = apiClass.getMethod("getLoader", String.class).invoke(api, "file"); if (loader != null) { - slimePlugin = (Plugin) aswmInstance; - slimeLoader = loader; + aspAPI = null; + slimeLoader = new LegacySWMLoaderWrapper(loader, loader.getClass()); slimeWorldEnabled = true; - plugin.getLogger().info("✓ Built-in Advanced Slime World Manager API integration initialized successfully!"); - plugin.getLogger().info("✓ Built-in ASWM (ASP server) detected and configured - islands will use slime worlds"); + plugin.getLogger().info("Legacy SWM plugin detected and configured"); return true; - } else { - plugin.getLogger().warning("Built-in ASWM API getLoader returned null"); } - } else { - plugin.getLogger().warning("Built-in ASWM API instance returned null"); } } } catch (ClassNotFoundException e) { - plugin.getLogger().info("Built-in ASWM API not found (not an ASP server or old version)"); + plugin.getLogger().info("Legacy SWM plugin API classes not found"); } catch (Exception e) { - plugin.getLogger().info("Failed to initialize built-in ASWM API: " + e.getMessage()); - plugin.getLogger().info("This is normal if you're not using an ASP server with built-in ASWM"); + plugin.getLogger().warning("Failed to initialize legacy SWM: " + e.getMessage()); } - return false; } private void createHubWorld() { - // Get hub world name from config, default to "hub" String hubWorldName = plugin.getConfig().getString("hub.world", "hub"); - - // Check if hub world already exists skyBlockWorld = Bukkit.getWorld(hubWorldName); - if (skyBlockWorld == null) { plugin.getLogger().info("Creating void hub world: " + hubWorldName); - - // Create new hub world as void WorldCreator creator = new WorldCreator(hubWorldName); creator.type(WorldType.FLAT); creator.generateStructures(false); creator.generator(new VoidWorldGenerator()); - skyBlockWorld = creator.createWorld(); - if (skyBlockWorld != null) { - // Set world properties - use config settings for mob spawning boolean allowMonsters = plugin.getConfig().getBoolean("world.spawning.allow-monsters", true); boolean allowAnimals = plugin.getConfig().getBoolean("world.spawning.allow-animals", true); skyBlockWorld.setSpawnFlags(allowAnimals, allowMonsters); @@ -133,295 +113,199 @@ private void createHubWorld() { skyBlockWorld.setStorm(false); skyBlockWorld.setThundering(false); skyBlockWorld.setWeatherDuration(Integer.MAX_VALUE); - - // Set difficulty from config String difficultyStr = plugin.getConfig().getString("world.spawning.difficulty", "normal"); try { org.bukkit.Difficulty difficulty = org.bukkit.Difficulty.valueOf(difficultyStr.toUpperCase()); skyBlockWorld.setDifficulty(difficulty); } catch (IllegalArgumentException e) { - plugin.getLogger().warning("Invalid difficulty setting: " + difficultyStr + ", using NORMAL"); skyBlockWorld.setDifficulty(org.bukkit.Difficulty.NORMAL); } - - // Set spawn location from config double spawnX = plugin.getConfig().getDouble("hub.spawn.x", 0); double spawnY = plugin.getConfig().getDouble("hub.spawn.y", 100); double spawnZ = plugin.getConfig().getDouble("hub.spawn.z", 0); - skyBlockWorld.setSpawnLocation((int) spawnX, (int) spawnY, (int) spawnZ); - - plugin.getLogger().info("✓ Created void hub world: " + hubWorldName); - plugin.getLogger().info("✓ Hub spawn set to: " + spawnX + ", " + spawnY + ", " + spawnZ); + plugin.getLogger().info("Created void hub world: " + hubWorldName); } else { - plugin.getLogger().severe("✗ Failed to create hub world!"); + plugin.getLogger().severe("Failed to create hub world!"); } } else { - plugin.getLogger().info("✓ Loaded existing hub world: " + hubWorldName); + plugin.getLogger().info("Loaded existing hub world: " + hubWorldName); } } public World getSkyBlockWorld() { return skyBlockWorld; } - + public World getSkyBlockNetherWorld() { return skyBlockNetherWorld; } - + public boolean hasNetherWorld() { return skyBlockNetherWorld != null; } public World createIslandWorld(String islandId) { - // Priority 1: Use ASWM/SlimeWorldManager if available and enabled - if (slimeWorldEnabled && slimePlugin != null && slimeLoader != null) { - plugin.getLogger().info("ASWM is available - attempting to create slime world for island: " + islandId); + if (slimeWorldEnabled && slimeLoader != null) { try { World slimeWorld = createSlimeWorld(islandId); if (slimeWorld != null) { - plugin.getLogger().info("✓ Successfully created ASWM world for island: " + islandId + " (World: " + slimeWorld.getName() + ")"); return slimeWorld; - } else { - plugin.getLogger().warning("✗ ASWM world creation returned null for island: " + islandId + " - falling back to standard world"); } } catch (Exception e) { - plugin.getLogger().warning("✗ Failed to create ASWM world for island " + islandId + ": " + e.getMessage()); - plugin.getLogger().warning("Exception details: " + e.getClass().getSimpleName()); + plugin.getLogger().warning("Failed to create slime world for " + islandId + ": " + e.getMessage()); if (plugin.getConfig().getBoolean("debug", false)) { e.printStackTrace(); } plugin.getLogger().info("Falling back to standard Bukkit world creation..."); } - } else { - // Log why ASWM isn't being used - if (!slimeWorldEnabled) { - plugin.getLogger().info("ASWM not enabled - creating standard world for island: " + islandId); - } else if (slimePlugin == null) { - plugin.getLogger().info("ASWM plugin not found - creating standard world for island: " + islandId); - } else if (slimeLoader == null) { - plugin.getLogger().info("ASWM loader not available - creating standard world for island: " + islandId); - } } + return createStandardWorld(islandId); + } - // Fallback: Create standard Bukkit world - plugin.getLogger().info("Creating standard Bukkit world for island: " + islandId); - World standardWorld = createStandardWorld(islandId); - if (standardWorld != null) { - plugin.getLogger().info("✓ Successfully created standard world for island: " + islandId + " (World: " + standardWorld.getName() + ")"); - } else { - plugin.getLogger().severe("✗ Failed to create any world for island: " + islandId); + private World createSlimeWorld(String islandId) { + boolean isNetherIsland = islandId.contains("nether"); + String worldName = isNetherIsland + ? "skyeblock_nether_" + islandId + : "skyeblock_islands_" + islandId; + + if (aspApiAvailable) { + return createASPWorld(worldName, islandId, isNetherIsland); } - return standardWorld; + return createLegacySWMWorld(worldName, islandId, isNetherIsland); } - private World createSlimeWorld(String islandId) { + private World createASPWorld(String worldName, String islandId, boolean isNetherIsland) { try { - Class slimePluginClass = slimePlugin.getClass(); - - // Check if this is ASWM (plugin or built-in) or old SWM - boolean isASWM = slimePlugin.getName().equals("AdvancedSlimeWorldManager") || - slimePluginClass.getName().contains("com.infernalsuite.aswm"); - - if (isASWM) { - plugin.getLogger().info("Using ASWM API to create world for island: " + islandId); - World world = createASWMWorld(islandId, slimePluginClass); - if (world != null) { - plugin.getLogger().info("✓ ASWM world creation successful for: " + islandId); - return world; - } else { - plugin.getLogger().warning("✗ ASWM world creation returned null for: " + islandId); - } + SlimePropertyMap properties = new SlimePropertyMap(); + properties.setValue(SlimeProperties.SPAWN_X, 0); + properties.setValue(SlimeProperties.SPAWN_Y, 100); + properties.setValue(SlimeProperties.SPAWN_Z, 0); + String difficultyStr = plugin.getConfig().getString("world.spawning.difficulty", "normal"); + boolean allowMonsters = plugin.getConfig().getBoolean("world.spawning.allow-monsters", true); + boolean allowAnimals = plugin.getConfig().getBoolean("world.spawning.allow-animals", true); + properties.setValue(SlimeProperties.DIFFICULTY, difficultyStr.toLowerCase()); + properties.setValue(SlimeProperties.ALLOW_MONSTERS, allowMonsters); + properties.setValue(SlimeProperties.ALLOW_ANIMALS, allowAnimals); + properties.setValue(SlimeProperties.PVP, false); + if (isNetherIsland) { + properties.setValue(SlimeProperties.ENVIRONMENT, "nether"); } else { - plugin.getLogger().info("Using legacy SWM API to create world for island: " + islandId); - World world = createSWMWorld(islandId, slimePluginClass); - if (world != null) { - plugin.getLogger().info("✓ Legacy SWM world creation successful for: " + islandId); - return world; - } else { - plugin.getLogger().warning("✗ Legacy SWM world creation returned null for: " + islandId); - } + properties.setValue(SlimeProperties.ENVIRONMENT, "normal"); + } + properties.setValue(SlimeProperties.WORLD_TYPE, "flat"); + properties.setValue(SlimeProperties.DEFAULT_BIOME, "minecraft:plains"); + + boolean worldExists = false; + try { + worldExists = slimeLoader.worldExists(worldName); + } catch (IOException e) { + plugin.getLogger().warning("Error checking if world exists: " + e.getMessage()); + } + + SlimeWorld slimeWorld; + if (worldExists) { + slimeWorld = aspAPI.readWorld(slimeLoader, worldName, false, properties); + } else { + slimeWorld = aspAPI.createEmptyWorld(worldName, false, properties, slimeLoader); + aspAPI.saveWorld(slimeWorld); + } + + SlimeWorldInstance instance = aspAPI.loadWorld(slimeWorld, true); + World bukkitWorld = instance.getBukkitWorld(); + + if (bukkitWorld != null) { + bukkitWorld.setTime(6000); + bukkitWorld.setStorm(false); + bukkitWorld.setThundering(false); + bukkitWorld.setWeatherDuration(Integer.MAX_VALUE); + setupWorldBorder(bukkitWorld); + plugin.getLogger().info("ASP world created/loaded: " + worldName); + return bukkitWorld; } } catch (Exception e) { - plugin.getLogger().warning("Exception in createSlimeWorld for " + islandId + ": " + e.getMessage()); - plugin.getLogger().warning("Exception type: " + e.getClass().getSimpleName()); + plugin.getLogger().warning("ASP world creation failed for " + worldName + ": " + e.getMessage()); if (plugin.getConfig().getBoolean("debug", false)) { e.printStackTrace(); } } - - return null; - } - - private World createASWMWorld(String islandId, Class slimePluginClass) throws Exception { - // ASWM API implementation - determine world type from island ID - boolean isNetherIsland = islandId.contains("nether"); - - // Use structured naming for SlimeWorlds to reflect folder organization - String worldName; - if (isNetherIsland) { - worldName = "skyeblock_nether_" + islandId; - } else { - worldName = "skyeblock_islands_" + islandId; - } - - // Create properties using reflection for ASWM - Class slimePropertyMapClass = Class.forName("com.infernalsuite.aswm.api.world.properties.SlimePropertyMap"); - Object properties = slimePropertyMapClass.getConstructor().newInstance(); - - Class slimePropertiesClass = Class.forName("com.infernalsuite.aswm.api.world.properties.SlimeProperties"); - - // Set properties for ASWM - Object spawnX = slimePropertiesClass.getField("SPAWN_X").get(null); - Object spawnY = slimePropertiesClass.getField("SPAWN_Y").get(null); - Object spawnZ = slimePropertiesClass.getField("SPAWN_Z").get(null); - Object difficulty = slimePropertiesClass.getField("DIFFICULTY").get(null); - Object allowMonsters = slimePropertiesClass.getField("ALLOW_MONSTERS").get(null); - Object allowAnimals = slimePropertiesClass.getField("ALLOW_ANIMALS").get(null); - Object pvp = slimePropertiesClass.getField("PVP").get(null); - - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, spawnX, 0); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, spawnY, 100); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, spawnZ, 0); - - // Use config settings for mob spawning and difficulty - String difficultyStr = plugin.getConfig().getString("world.spawning.difficulty", "normal"); - boolean allowMonstersConfig = plugin.getConfig().getBoolean("world.spawning.allow-monsters", true); - boolean allowAnimalsConfig = plugin.getConfig().getBoolean("world.spawning.allow-animals", true); - - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, difficulty, difficultyStr.toLowerCase()); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, allowMonsters, allowMonstersConfig); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, allowAnimals, allowAnimalsConfig); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, pvp, false); - - // Create SlimeWorld for ASWM using the structured world name - Object slimeWorld = slimePluginClass.getMethod("createEmptyWorld", Object.class, String.class, boolean.class, Object.class) - .invoke(slimePlugin, slimeLoader, worldName, false, properties); - - // Generate the world in Bukkit - slimePluginClass.getMethod("generateWorld", Object.class).invoke(slimePlugin, slimeWorld); - - // Store reference using original islandId for tracking - islandWorlds.put(islandId, slimeWorld); - - World bukkitWorld = Bukkit.getWorld(worldName); - if (bukkitWorld != null) { - bukkitWorld.setTime(6000); - bukkitWorld.setStorm(false); - bukkitWorld.setThundering(false); - bukkitWorld.setWeatherDuration(Integer.MAX_VALUE); - - // Set world border from config - setupWorldBorder(bukkitWorld); - - plugin.getLogger().info("✓ ASWM world created successfully: " + worldName + " for island " + islandId); - return bukkitWorld; - } else { - plugin.getLogger().warning("✗ ASWM world generation failed - Bukkit world is null for: " + worldName); - } - return null; } - private World createSWMWorld(String islandId, Class slimePluginClass) throws Exception { - // Old SWM API implementation - determine world type from island ID - boolean isNetherIsland = islandId.contains("nether"); - - // Use structured naming for SlimeWorlds to reflect folder organization - String worldName; - if (isNetherIsland) { - worldName = "skyeblock_nether_" + islandId; - } else { - worldName = "skyeblock_islands_" + islandId; - } - - // Create properties using reflection for old SWM - Class slimePropertyMapClass = Class.forName("com.grinderwolf.swm.api.world.properties.SlimePropertyMap"); - Object properties = slimePropertyMapClass.getConstructor().newInstance(); - - Class slimePropertiesClass = Class.forName("com.grinderwolf.swm.api.world.properties.SlimeProperties"); - - // Set properties for old SWM - Object spawnX = slimePropertiesClass.getField("SPAWN_X").get(null); - Object spawnY = slimePropertiesClass.getField("SPAWN_Y").get(null); - Object spawnZ = slimePropertiesClass.getField("SPAWN_Z").get(null); - Object difficulty = slimePropertiesClass.getField("DIFFICULTY").get(null); - Object allowMonsters = slimePropertiesClass.getField("ALLOW_MONSTERS").get(null); - Object allowAnimals = slimePropertiesClass.getField("ALLOW_ANIMALS").get(null); - Object pvp = slimePropertiesClass.getField("PVP").get(null); - - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, spawnX, 0); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, spawnY, 100); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, spawnZ, 0); - - // Use config settings for mob spawning and difficulty - String difficultyStr = plugin.getConfig().getString("world.spawning.difficulty", "normal"); - boolean allowMonstersConfig = plugin.getConfig().getBoolean("world.spawning.allow-monsters", true); - boolean allowAnimalsConfig = plugin.getConfig().getBoolean("world.spawning.allow-animals", true); - - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, difficulty, difficultyStr.toLowerCase()); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, allowMonsters, allowMonstersConfig); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, allowAnimals, allowAnimalsConfig); - slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke(properties, pvp, false); - - // Create SlimeWorld for old SWM using the structured world name - Object slimeWorld = slimePluginClass.getMethod("createEmptyWorld", Object.class, String.class, boolean.class, Object.class) + private World createLegacySWMWorld(String worldName, String islandId, boolean isNetherIsland) { + try { + Class loaderClass = slimeLoader.getClass(); + String difficultyStr = plugin.getConfig().getString("world.spawning.difficulty", "normal"); + boolean allowMonsters = plugin.getConfig().getBoolean("world.spawning.allow-monsters", true); + boolean allowAnimals = plugin.getConfig().getBoolean("world.spawning.allow-animals", true); + + Class slimePropertyMapClass = Class.forName("com.grinderwolf.swm.api.world.properties.SlimePropertyMap"); + Object properties = slimePropertyMapClass.getConstructor().newInstance(); + Class slimePropertiesClass = Class.forName("com.grinderwolf.swm.api.world.properties.SlimeProperties"); + + slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke( + properties, slimePropertiesClass.getField("SPAWN_X").get(null), 0); + slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke( + properties, slimePropertiesClass.getField("SPAWN_Y").get(null), 100); + slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke( + properties, slimePropertiesClass.getField("SPAWN_Z").get(null), 0); + slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke( + properties, slimePropertiesClass.getField("DIFFICULTY").get(null), difficultyStr.toLowerCase()); + slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke( + properties, slimePropertiesClass.getField("ALLOW_MONSTERS").get(null), allowMonsters); + slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke( + properties, slimePropertiesClass.getField("ALLOW_ANIMALS").get(null), allowAnimals); + slimePropertyMapClass.getMethod("setValue", Object.class, Object.class).invoke( + properties, slimePropertiesClass.getField("PVP").get(null), false); + + Class slimePluginClass = Class.forName("com.grinderwolf.swm.api.SlimePlugin"); + Object slimePlugin = slimePluginClass.getMethod("getInstance").invoke(null); + + Object slimeWorld = slimePluginClass.getMethod("createEmptyWorld", Object.class, String.class, boolean.class, Object.class) .invoke(slimePlugin, slimeLoader, worldName, false, properties); - - // Generate the world in Bukkit - slimePluginClass.getMethod("generateWorld", Object.class).invoke(slimePlugin, slimeWorld); - - // Store reference using original islandId for tracking - islandWorlds.put(islandId, slimeWorld); - - World bukkitWorld = Bukkit.getWorld(worldName); - if (bukkitWorld != null) { - bukkitWorld.setTime(6000); - bukkitWorld.setStorm(false); - bukkitWorld.setThundering(false); - bukkitWorld.setWeatherDuration(Integer.MAX_VALUE); - - // Set world border from config - setupWorldBorder(bukkitWorld); - - plugin.getLogger().info("✓ Legacy SWM world created successfully: " + worldName + " for island " + islandId); - return bukkitWorld; - } else { - plugin.getLogger().warning("✗ Legacy SWM world generation failed - Bukkit world is null for: " + worldName); + slimePluginClass.getMethod("generateWorld", Object.class).invoke(slimePlugin, slimeWorld); + + World bukkitWorld = Bukkit.getWorld(worldName); + if (bukkitWorld != null) { + bukkitWorld.setTime(6000); + bukkitWorld.setStorm(false); + bukkitWorld.setThundering(false); + bukkitWorld.setWeatherDuration(Integer.MAX_VALUE); + setupWorldBorder(bukkitWorld); + plugin.getLogger().info("Legacy SWM world created: " + worldName); + return bukkitWorld; + } + } catch (Exception e) { + plugin.getLogger().warning("Legacy SWM world creation failed: " + e.getMessage()); + if (plugin.getConfig().getBoolean("debug", false)) { + e.printStackTrace(); + } } - return null; } private World createStandardWorld(String islandId) { try { - // Determine if this is a nether island based on id format boolean isNetherIsland = islandId.contains("nether"); - - // Create world in the appropriate directory structure - // root/skyeblock/islands/ for normal islands - // root/skyeblock/nether/ for nether islands String worldPath; if (isNetherIsland) { worldPath = "skyeblock/nether/" + islandId; } else { worldPath = "skyeblock/islands/" + islandId; } - + WorldCreator creator = new WorldCreator(worldPath); creator.type(WorldType.FLAT); creator.generateStructures(false); - - // Set nether environment for nether islands + if (isNetherIsland) { creator.environment(World.Environment.NETHER); } - + creator.generator(new VoidWorldGenerator()); - + World world = creator.createWorld(); if (world != null) { - // Set world properties - use config settings for mob spawning boolean allowMonsters = plugin.getConfig().getBoolean("world.spawning.allow-monsters", true); boolean allowAnimals = plugin.getConfig().getBoolean("world.spawning.allow-animals", true); world.setSpawnFlags(allowAnimals, allowMonsters); @@ -429,121 +313,82 @@ private World createStandardWorld(String islandId) { world.setStorm(false); world.setThundering(false); world.setWeatherDuration(Integer.MAX_VALUE); - - // Set difficulty from config + String difficultyStr = plugin.getConfig().getString("world.spawning.difficulty", "normal"); try { org.bukkit.Difficulty difficulty = org.bukkit.Difficulty.valueOf(difficultyStr.toUpperCase()); world.setDifficulty(difficulty); } catch (IllegalArgumentException e) { - plugin.getLogger().warning("Invalid difficulty setting: " + difficultyStr + ", using NORMAL"); world.setDifficulty(org.bukkit.Difficulty.NORMAL); } - - // Set world border from config + setupWorldBorder(world); - - plugin.getLogger().info("✓ Standard Bukkit world created successfully: " + worldPath); + plugin.getLogger().info("Standard Bukkit world created: " + worldPath); return world; } else { - plugin.getLogger().warning("✗ Standard Bukkit world creation failed for: " + worldPath); + plugin.getLogger().warning("Standard Bukkit world creation failed for: " + worldPath); } } catch (Exception e) { plugin.getLogger().severe("Failed to create standard world " + islandId + ": " + e.getMessage()); } - - // Ultimate fallback to main world - plugin.getLogger().warning("Falling back to main world for island: " + islandId); + return skyBlockWorld; } public boolean deleteIslandWorld(String islandId) { - // If it's the main world, don't delete it if (islandId.equals(skyBlockWorld.getName())) { - plugin.getLogger().info("Cannot delete main world, skipping deletion for: " + islandId); return true; } - // Determine if this is a nether island boolean isNetherIsland = islandId.contains("nether"); - - // Check for standard world names, new directory structure, and slime world names - String oldStandardWorldPath = "islands/" + islandId; - String newStandardWorldPath = isNetherIsland ? - "skyeblock/nether/" + islandId : - "skyeblock/islands/" + islandId; + String newSlimeWorldName = isNetherIsland + ? "skyeblock_nether_" + islandId + : "skyeblock_islands_" + islandId; String oldSlimeWorldName = "islands_" + islandId; - String newSlimeWorldName = isNetherIsland ? - "skyeblock_nether_" + islandId : - "skyeblock_islands_" + islandId; - - // Try to find and unload the world (could be either name format) + World bukkitWorld = Bukkit.getWorld(islandId); - if (bukkitWorld == null) { - bukkitWorld = Bukkit.getWorld(oldStandardWorldPath); - } - if (bukkitWorld == null) { - bukkitWorld = Bukkit.getWorld(newStandardWorldPath); - } - if (bukkitWorld == null) { - bukkitWorld = Bukkit.getWorld(oldSlimeWorldName); - } - if (bukkitWorld == null) { - bukkitWorld = Bukkit.getWorld(newSlimeWorldName); - } - - if (bukkitWorld != null) { - // Teleport any players out of the world first - bukkitWorld.getPlayers().forEach(player -> { - teleportToSpawn(player); - }); + if (bukkitWorld == null) bukkitWorld = Bukkit.getWorld("islands/" + islandId); + if (bukkitWorld == null) bukkitWorld = Bukkit.getWorld("skyeblock/islands/" + islandId); + if (bukkitWorld == null) bukkitWorld = Bukkit.getWorld("skyeblock/nether/" + islandId); + if (bukkitWorld == null) bukkitWorld = Bukkit.getWorld(oldSlimeWorldName); + if (bukkitWorld == null) bukkitWorld = Bukkit.getWorld(newSlimeWorldName); - // Unload the world + if (bukkitWorld != null) { + bukkitWorld.getPlayers().forEach(this::teleportToSpawn); if (!Bukkit.unloadWorld(bukkitWorld, false)) { plugin.getLogger().warning("Failed to unload world: " + bukkitWorld.getName()); } } - // Try SlimeWorld deletion if available - if (slimeWorldEnabled && slimePlugin != null && slimeLoader != null) { + if (slimeWorldEnabled && slimeLoader != null) { try { - // Use reflection to check if world exists and delete it - Class slimeLoaderClass = slimeLoader.getClass(); - - // Try both old and new naming conventions for slime worlds - boolean existsOld = (Boolean) slimeLoaderClass.getMethod("worldExists", String.class).invoke(slimeLoader, oldSlimeWorldName); - boolean existsNew = (Boolean) slimeLoaderClass.getMethod("worldExists", String.class).invoke(slimeLoader, newSlimeWorldName); - boolean existsPlain = (Boolean) slimeLoaderClass.getMethod("worldExists", String.class).invoke(slimeLoader, islandId); - - if (existsNew) { - slimeLoaderClass.getMethod("deleteWorld", String.class).invoke(slimeLoader, newSlimeWorldName); - plugin.getLogger().info("Deleted SlimeWorld: " + newSlimeWorldName); - } else if (existsOld) { - slimeLoaderClass.getMethod("deleteWorld", String.class).invoke(slimeLoader, oldSlimeWorldName); - plugin.getLogger().info("Deleted SlimeWorld: " + oldSlimeWorldName); - } else if (existsPlain) { - slimeLoaderClass.getMethod("deleteWorld", String.class).invoke(slimeLoader, islandId); - plugin.getLogger().info("Deleted SlimeWorld: " + islandId); + if (slimeLoader.worldExists(newSlimeWorldName)) { + slimeLoader.deleteWorld(newSlimeWorldName); + plugin.getLogger().info("Deleted slime world: " + newSlimeWorldName); + } else if (slimeLoader.worldExists(oldSlimeWorldName)) { + slimeLoader.deleteWorld(oldSlimeWorldName); + plugin.getLogger().info("Deleted slime world: " + oldSlimeWorldName); } } catch (Exception e) { - plugin.getLogger().warning("Failed to delete SlimeWorld " + islandId + ": " + e.getMessage()); + plugin.getLogger().warning("Failed to delete slime world " + islandId + ": " + e.getMessage()); } } else { - // Delete standard world folder (check both old and new directory structures) + String newStandardWorldPath = isNetherIsland + ? "skyeblock/nether/" + islandId + : "skyeblock/islands/" + islandId; + String oldStandardWorldPath = "islands/" + islandId; + try { - // Try new directory structure first File worldFolder = new File(Bukkit.getWorldContainer(), newStandardWorldPath); if (worldFolder.exists()) { deleteDirectory(worldFolder); plugin.getLogger().info("Deleted world folder: " + newStandardWorldPath); } else { - // Try old directory structure worldFolder = new File(Bukkit.getWorldContainer(), oldStandardWorldPath); if (worldFolder.exists()) { deleteDirectory(worldFolder); plugin.getLogger().info("Deleted world folder: " + oldStandardWorldPath); } else { - // Fallback to original naming convention worldFolder = new File(Bukkit.getWorldContainer(), islandId); if (worldFolder.exists()) { deleteDirectory(worldFolder); @@ -556,9 +401,6 @@ public boolean deleteIslandWorld(String islandId) { } } - // Remove from our tracking - islandWorlds.remove(islandId); - return true; } @@ -577,23 +419,12 @@ private void deleteDirectory(File directory) { } public World getIslandWorld(String islandId) { - // Try multiple world name formats World world = Bukkit.getWorld(islandId); - - // Check old path format - if (world == null) { - world = Bukkit.getWorld("islands/" + islandId); - } - - // Check old SlimeWorld format - if (world == null) { - world = Bukkit.getWorld("islands_" + islandId); - } - - // Determine if nether or overworld based on id + if (world == null) world = Bukkit.getWorld("islands/" + islandId); + if (world == null) world = Bukkit.getWorld("islands_" + islandId); + boolean isNetherIsland = islandId.contains("nether"); - - // Check new standard world structure + if (world == null) { if (isNetherIsland) { world = Bukkit.getWorld("skyeblock/nether/" + islandId); @@ -601,8 +432,7 @@ public World getIslandWorld(String islandId) { world = Bukkit.getWorld("skyeblock/islands/" + islandId); } } - - // Check new SlimeWorld structure + if (world == null) { if (isNetherIsland) { world = Bukkit.getWorld("skyeblock_nether_" + islandId); @@ -610,49 +440,32 @@ public World getIslandWorld(String islandId) { world = Bukkit.getWorld("skyeblock_islands_" + islandId); } } - - // Apply island settings to the world if found and it's not the main world - // This ensures gamerules are always correct when getting an island world + if (world != null && !world.equals(skyBlockWorld)) { plugin.getIslandSettingsManager().applySettingsToWorld(islandId, world); - plugin.getLogger().fine("Reapplied gamerules to island world: " + islandId); } - + return world != null ? world : skyBlockWorld; } - /** - * Gets an island world, loading it if necessary - * This method ensures that island worlds are available even after server restart - */ public World getOrLoadIslandWorld(String islandId) { - // First try to get the world if it's already loaded World world = getIslandWorld(islandId); - - // If we got the default skyblock world back, it means the island world wasn't found + if (world == skyBlockWorld) { plugin.getLogger().info("Island world not loaded for " + islandId + " - attempting to load/create it"); - - // Try to load/create the island world World loadedWorld = createIslandWorld(islandId); if (loadedWorld != null && !loadedWorld.equals(skyBlockWorld)) { - plugin.getLogger().info("Successfully loaded island world for " + islandId + ": " + loadedWorld.getName()); - // Apply current mob spawning settings to the newly loaded world applyMobSpawningSettings(loadedWorld); - // Apply island-specific gamerules from settings plugin.getIslandSettingsManager().applySettingsToWorld(islandId, loadedWorld); - plugin.getLogger().info("Applied gamerules to newly loaded world: " + islandId); return loadedWorld; } else { plugin.getLogger().warning("Failed to load island world for " + islandId + " - using default world"); } } else if (world != null && !world.equals(skyBlockWorld)) { - // Apply current mob spawning settings to existing loaded world applyMobSpawningSettings(world); - // Reapply island-specific gamerules to ensure they're correct plugin.getIslandSettingsManager().applySettingsToWorld(islandId, world); } - + return world; } @@ -661,50 +474,20 @@ public boolean isSlimeWorldEnabled() { } public String getSlimeWorldManagerType() { - if (!slimeWorldEnabled || slimePlugin == null) { - return "None"; - } - - // Check if this is built-in ASWM (ASP server) - try { - if (slimePlugin.getClass().getName().contains("com.infernalsuite.aswm")) { - return "ASWM (Built-in)"; - } - } catch (Exception e) { - // Ignore, fall through to plugin name check - } - - // Check plugin name for plugin-based managers - if (slimePlugin.getName().equals("AdvancedSlimeWorldManager")) { - return "ASWM"; - } else if (slimePlugin.getName().equals("SlimeWorldManager")) { - return "SWM"; - } - - // Fallback - try to identify by class name - String className = slimePlugin.getClass().getName(); - if (className.contains("aswm") || className.contains("AdvancedSlimeWorldManager")) { - return "ASWM"; - } else if (className.contains("swm") || className.contains("SlimeWorldManager")) { - return "SWM"; - } - - return "Unknown"; + if (!slimeWorldEnabled) return "None"; + if (aspApiAvailable) return "ASWM (ASP 4.0.0)"; + return "SWM (Legacy)"; } - /** - * Get detailed status information about the world manager for debugging - */ public String getDetailedStatus() { StringBuilder status = new StringBuilder(); - status.append("§6=== WorldManager Status ===\n"); - status.append("§7ASWM Enabled: §").append(slimeWorldEnabled ? "a✓ Yes" : "c✗ No").append("\n"); - status.append("§7Plugin Found: §").append(slimePlugin != null ? "a✓ " + slimePlugin.getName() : "c✗ None").append("\n"); - status.append("§7Loader Available: §").append(slimeLoader != null ? "a✓ Yes" : "c✗ No").append("\n"); - status.append("§7Manager Type: §b").append(getSlimeWorldManagerType()).append("\n"); - status.append("§7Main World: §e").append(skyBlockWorld != null ? skyBlockWorld.getName() : "null").append("\n"); - status.append("§7Nether World: §c").append(skyBlockNetherWorld != null ? skyBlockNetherWorld.getName() : "disabled").append("\n"); - status.append("§7Tracked Islands: §d").append(islandWorlds.size()).append(" worlds"); + status.append("=== WorldManager Status ===\n"); + status.append("ASWM Enabled: ").append(slimeWorldEnabled ? "Yes" : "No").append("\n"); + status.append("ASP API Available: ").append(aspApiAvailable ? "Yes" : "No").append("\n"); + status.append("Loader Available: ").append(slimeLoader != null ? "Yes" : "No").append("\n"); + status.append("Manager Type: ").append(getSlimeWorldManagerType()).append("\n"); + status.append("Main World: ").append(skyBlockWorld != null ? skyBlockWorld.getName() : "null").append("\n"); + status.append("Nether World: ").append(skyBlockNetherWorld != null ? skyBlockNetherWorld.getName() : "disabled").append("\n"); return status.toString(); } @@ -732,161 +515,112 @@ public void teleportToSpawn(org.bukkit.entity.Player player) { player.teleport(hubLocation); plugin.sendMessage(player, "teleported-to-hub"); } - - /** - * Set up world border for an island world based on config settings - */ + private void setupWorldBorder(World world) { try { boolean borderEnabled = plugin.getConfig().getBoolean("world.border.enabled", true); - if (!borderEnabled) { - plugin.getLogger().info("World border disabled in config for world: " + world.getName()); - return; - } - + if (!borderEnabled) return; + double borderSize = plugin.getConfig().getDouble("world.border.size", 10000); - - // Get world border org.bukkit.WorldBorder worldBorder = world.getWorldBorder(); - - // Set border center to world spawn (0,0) worldBorder.setCenter(0, 0); - - // Set border size worldBorder.setSize(borderSize); - - // Optional: Set damage and warning settings - worldBorder.setDamageAmount(0.2); // Damage per second when outside border - worldBorder.setDamageBuffer(5.0); // Distance past border before damage starts - worldBorder.setWarningDistance(5); // Distance from border to start warning - worldBorder.setWarningTime(15); // Seconds of warning when border is moving - - plugin.getLogger().info("World border set for " + world.getName() + " - Size: " + borderSize + " blocks"); - + worldBorder.setDamageAmount(0.2); + worldBorder.setDamageBuffer(5.0); + worldBorder.setWarningDistance(5); + worldBorder.setWarningTime(15); } catch (Exception e) { plugin.getLogger().warning("Failed to set world border for " + world.getName() + ": " + e.getMessage()); } } - - private boolean checkForASWMPlugin() { - try { - plugin.getLogger().info("Checking for ASWM plugin..."); - - // Check if ASWM plugin is loaded - Plugin aswmPlugin = plugin.getServer().getPluginManager().getPlugin("AdvancedSlimeWorldManager"); - if (aswmPlugin != null && aswmPlugin.isEnabled()) { - plugin.getLogger().info("Found AdvancedSlimeWorldManager plugin, attempting to initialize..."); - - // Try to get the API - Class apiClass = Class.forName("com.infernalsuite.aswm.api.AdvancedSlimeWorldManagerAPI"); - Object api = apiClass.getMethod("instance").invoke(null); - - if (api != null) { - Object loader = apiClass.getMethod("getLoader", String.class).invoke(api, "file"); - if (loader != null) { - slimePlugin = aswmPlugin; - slimeLoader = loader; - slimeWorldEnabled = true; - plugin.getLogger().info("✓ Advanced Slime World Manager integration initialized successfully!"); - plugin.getLogger().info("✓ ASWM plugin detected and configured - islands will use slime worlds"); - return true; - } else { - plugin.getLogger().warning("ASWM plugin getLoader returned null"); - } - } else { - plugin.getLogger().warning("ASWM plugin API instance returned null"); - } - } - } catch (ClassNotFoundException e) { - plugin.getLogger().info("ASWM plugin API classes not found"); - } catch (Exception e) { - plugin.getLogger().warning("Failed to initialize ASWM plugin integration: " + e.getMessage()); - } - - return false; - } - - private boolean checkForLegacySWM() { - try { - plugin.getLogger().info("Checking for legacy SlimeWorldManager plugin..."); - - // Check if legacy SWM plugin is loaded - Plugin swmPlugin = plugin.getServer().getPluginManager().getPlugin("SlimeWorldManager"); - if (swmPlugin != null && swmPlugin.isEnabled()) { - plugin.getLogger().info("Found SlimeWorldManager plugin, attempting to initialize..."); - - // Try to get the API - Class apiClass = Class.forName("com.grinderwolf.swm.api.SlimePlugin"); - Object api = apiClass.getMethod("getInstance").invoke(null); - - if (api != null) { - Object loader = apiClass.getMethod("getLoader", String.class).invoke(api, "file"); - if (loader != null) { - slimePlugin = swmPlugin; - slimeLoader = loader; - slimeWorldEnabled = true; - plugin.getLogger().info("✓ SlimeWorldManager integration initialized successfully!"); - plugin.getLogger().info("✓ Legacy SWM plugin detected and configured - islands will use slime worlds"); - return true; - } else { - plugin.getLogger().warning("Legacy SWM plugin getLoader returned null"); - } - } else { - plugin.getLogger().warning("Legacy SWM plugin API instance returned null"); - } - } - } catch (ClassNotFoundException e) { - plugin.getLogger().info("Legacy SWM plugin API classes not found"); - } catch (Exception e) { - plugin.getLogger().warning("Failed to initialize legacy SWM plugin integration: " + e.getMessage()); - } - - return false; - } - /** - * Apply mob spawning settings from config to an existing world - */ private void applyMobSpawningSettings(World world) { if (world == null) return; - - // Set spawn flags from config boolean allowMonsters = plugin.getConfig().getBoolean("world.spawning.allow-monsters", true); boolean allowAnimals = plugin.getConfig().getBoolean("world.spawning.allow-animals", true); world.setSpawnFlags(allowAnimals, allowMonsters); - - // Set difficulty from config String difficultyStr = plugin.getConfig().getString("world.spawning.difficulty", "normal"); try { org.bukkit.Difficulty difficulty = org.bukkit.Difficulty.valueOf(difficultyStr.toUpperCase()); world.setDifficulty(difficulty); } catch (IllegalArgumentException e) { - plugin.getLogger().warning("Invalid difficulty setting: " + difficultyStr + ", using NORMAL"); world.setDifficulty(org.bukkit.Difficulty.NORMAL); } - - plugin.getLogger().info("Applied mob spawning settings to world " + world.getName() + - " - Monsters: " + allowMonsters + ", Animals: " + allowAnimals + - ", Difficulty: " + world.getDifficulty()); } - - /** - * Update mob spawning settings for all loaded island worlds - * This can be used after changing config settings to apply them to existing worlds - */ + + private void safeSetTime(World world, long time) { + try { + if (world.getEnvironment() == World.Environment.NORMAL) { + world.setTime(time); + } + } catch (Exception ignored) { + } + } + public void updateMobSpawningForAllIslands() { plugin.getLogger().info("Updating mob spawning settings for all loaded island worlds..."); int updatedWorlds = 0; - for (World world : Bukkit.getWorlds()) { - // Check if this is an island world (not the main skyblock world or other worlds) - if (world.getName().startsWith("skyeblock_islands_") || - world.getName().contains("island-")) { + if (world.getName().startsWith("skyeblock_islands_") || world.getName().contains("island-")) { applyMobSpawningSettings(world); updatedWorlds++; } } - plugin.getLogger().info("Updated mob spawning settings for " + updatedWorlds + " island worlds"); } + + private static class LegacySWMLoaderWrapper implements SlimeLoader { + private final Object delegate; + private final Class delegateClass; + + LegacySWMLoaderWrapper(Object delegate, Class delegateClass) { + this.delegate = delegate; + this.delegateClass = delegateClass; + } + + @Override + public byte[] readWorld(String worldName) throws UnknownWorldException, IOException { + try { + return (byte[]) delegateClass.getMethod("readWorld", String.class).invoke(delegate, worldName); + } catch (Exception e) { + throw new IOException("Failed to read world", e); + } + } + + @Override + public boolean worldExists(String worldName) throws IOException { + try { + return (Boolean) delegateClass.getMethod("worldExists", String.class).invoke(delegate, worldName); + } catch (Exception e) { + throw new IOException("Failed to check world existence", e); + } + } + + @Override + public java.util.List listWorlds() throws IOException { + try { + return (java.util.List) delegateClass.getMethod("listWorlds").invoke(delegate); + } catch (Exception e) { + throw new IOException("Failed to list worlds", e); + } + } + + @Override + public void saveWorld(String worldName, byte[] serializedWorld) throws IOException { + try { + delegateClass.getMethod("saveWorld", String.class, byte[].class).invoke(delegate, worldName, serializedWorld); + } catch (Exception e) { + throw new IOException("Failed to save world", e); + } + } + + @Override + public void deleteWorld(String worldName) throws UnknownWorldException, IOException { + try { + delegateClass.getMethod("deleteWorld", String.class).invoke(delegate, worldName); + } catch (Exception e) { + throw new IOException("Failed to delete world", e); + } + } + } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 4fa17cd..6b90fbe 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,9 +1,9 @@ name: SkyeBlock version: '${project.version}' main: skyeblock.nobleskye.dev.skyeblock.SkyeBlockPlugin -api-version: '1.21' +api-version: '26.2' authors: [SkyeNetwork] -description: SkyeBlock plugin for Minecraft 1.21.10 - creating and managing skyeblock islands +description: SkyeBlock plugin for Minecraft 26.2 (Chaos Cubed) - creating and managing skyeblock islands website: https://skyemc.net/opensource modrinth-project-id: "UAeWLb2c" depend: [WorldEdit] diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index dca9b16..ccda7ba 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -3,10 +3,11 @@ skyeblock/nobleskye/dev/skyeblock/commands/PermissionCommand.class skyeblock/nobleskye/dev/skyeblock/SkyeBlockPlugin.class skyeblock/nobleskye/dev/skyeblock/permissions/IslandPermissionManager.class skyeblock/nobleskye/dev/skyeblock/permissions/IslandPermission$PermissionCategory.class -skyeblock/nobleskye/dev/skyeblock/commands/CreateIslandCommand.class skyeblock/nobleskye/dev/skyeblock/managers/CustomSchematicManager.class +skyeblock/nobleskye/dev/skyeblock/commands/CreateIslandCommand.class skyeblock/nobleskye/dev/skyeblock/gui/IslandCreationGUI.class skyeblock/nobleskye/dev/skyeblock/listeners/VisitorPacketListener.class +skyeblock/nobleskye/dev/skyeblock/managers/WorldManager$LegacySWMLoaderWrapper.class skyeblock/nobleskye/dev/skyeblock/managers/WorldManager.class skyeblock/nobleskye/dev/skyeblock/gui/IslandVisitGUI.class skyeblock/nobleskye/dev/skyeblock/gui/IslandCreationGUI$IslandType.class @@ -15,8 +16,8 @@ skyeblock/nobleskye/dev/skyeblock/listeners/PlayerJoinListener.class skyeblock/nobleskye/dev/skyeblock/commands/MobSpawningCommand.class skyeblock/nobleskye/dev/skyeblock/managers/VoidWorldGenerator.class skyeblock/nobleskye/dev/skyeblock/permissions/IslandPermission$1.class -skyeblock/nobleskye/dev/skyeblock/commands/DeleteCommand.class skyeblock/nobleskye/dev/skyeblock/models/Island$CoopRole.class +skyeblock/nobleskye/dev/skyeblock/commands/DeleteCommand.class skyeblock/nobleskye/dev/skyeblock/commands/SpawnCommand.class skyeblock/nobleskye/dev/skyeblock/commands/WarpAdminCommand.class skyeblock/nobleskye/dev/skyeblock/managers/ResourceWorldManager.class @@ -24,25 +25,26 @@ skyeblock/nobleskye/dev/skyeblock/gui/IslandSettingsGUI.class skyeblock/nobleskye/dev/skyeblock/commands/ConvertIslandsCommand.class skyeblock/nobleskye/dev/skyeblock/gui/WarpGUI.class skyeblock/nobleskye/dev/skyeblock/listeners/PlayerLocationListener.class -skyeblock/nobleskye/dev/skyeblock/commands/IslandCommand.class skyeblock/nobleskye/dev/skyeblock/managers/CustomSchematicManager$IslandSchematic.class +skyeblock/nobleskye/dev/skyeblock/commands/IslandCommand.class skyeblock/nobleskye/dev/skyeblock/managers/ResourceWorldManager$1.class skyeblock/nobleskye/dev/skyeblock/commands/SkyeBlockCommand.class skyeblock/nobleskye/dev/skyeblock/managers/WarpManager.class skyeblock/nobleskye/dev/skyeblock/listeners/VisitorProtectionListener.class skyeblock/nobleskye/dev/skyeblock/commands/WarpCommand.class -skyeblock/nobleskye/dev/skyeblock/commands/HubCommand.class -skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI.class skyeblock/nobleskye/dev/skyeblock/managers/IslandDataManager.class +skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI.class +skyeblock/nobleskye/dev/skyeblock/commands/HubCommand.class skyeblock/nobleskye/dev/skyeblock/managers/ResourceWorldManager$ResourceWorld.class -skyeblock/nobleskye/dev/skyeblock/gui/MainSettingsGUI.class skyeblock/nobleskye/dev/skyeblock/managers/SchematicManager.class +skyeblock/nobleskye/dev/skyeblock/gui/MainSettingsGUI.class skyeblock/nobleskye/dev/skyeblock/models/Island.class -skyeblock/nobleskye/dev/skyeblock/gui/VisitingSettingsGUI.class skyeblock/nobleskye/dev/skyeblock/managers/PlayerDataManager.class +skyeblock/nobleskye/dev/skyeblock/gui/VisitingSettingsGUI.class +skyeblock/nobleskye/dev/skyeblock/managers/DatabaseManager.class skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI$DeleteSession.class -skyeblock/nobleskye/dev/skyeblock/gui/PermissionManagementGUI.class skyeblock/nobleskye/dev/skyeblock/permissions/IslandPermission.class +skyeblock/nobleskye/dev/skyeblock/gui/PermissionManagementGUI.class skyeblock/nobleskye/dev/skyeblock/managers/IslandManager.class skyeblock/nobleskye/dev/skyeblock/managers/IslandSettingsManager.class skyeblock/nobleskye/dev/skyeblock/gui/IslandCreationGUI$Difficulty.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index 519e80c..5271783 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -1,40 +1,41 @@ -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/SkyeBlockPlugin.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/ConvertIslandsCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/CreateIslandCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/DeleteCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/HubCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/IslandCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/MobSpawningCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/PermissionCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/SkyeBlockAdminCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/SkyeBlockCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/SpawnCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/VisitCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/WarpAdminCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/WarpCommand.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/IslandCreationGUI.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/IslandSettingsGUI.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/IslandVisitGUI.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/MainSettingsGUI.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/PermissionManagementGUI.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/VisitingSettingsGUI.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/WarpGUI.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/listeners/PlayerJoinListener.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/listeners/PlayerLocationListener.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/listeners/VisitorPacketListener.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/listeners/VisitorProtectionListener.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/CustomSchematicManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandDataManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandSettingsManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/PlayerDataManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/ResourceWorldManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/SchematicManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/VoidWorldGenerator.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/WarpManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/WorldManager.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/models/Island.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/models/Warp.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/permissions/IslandPermission.java -/mnt/sda4/SkyeNetwork/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/permissions/IslandPermissionManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/SkyeBlockPlugin.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/ConvertIslandsCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/CreateIslandCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/DeleteCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/HubCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/IslandCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/MobSpawningCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/PermissionCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/SkyeBlockAdminCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/SkyeBlockCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/SpawnCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/VisitCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/WarpAdminCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/commands/WarpCommand.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/DeleteConfirmationGUI.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/IslandCreationGUI.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/IslandSettingsGUI.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/IslandVisitGUI.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/MainSettingsGUI.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/PermissionManagementGUI.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/VisitingSettingsGUI.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/gui/WarpGUI.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/listeners/PlayerJoinListener.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/listeners/PlayerLocationListener.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/listeners/VisitorPacketListener.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/listeners/VisitorProtectionListener.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/CustomSchematicManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/DatabaseManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandDataManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/IslandSettingsManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/PlayerDataManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/ResourceWorldManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/SchematicManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/VoidWorldGenerator.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/WarpManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/managers/WorldManager.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/models/Island.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/models/Warp.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/permissions/IslandPermission.java +/home/skylar/Documents/SkyeNetwork/SkyeBlock/SkyeBlock/src/main/java/skyeblock/nobleskye/dev/skyeblock/permissions/IslandPermissionManager.java