diff --git a/pom.xml b/pom.xml
index 59f0843..1785a65 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,10 @@
+
+ codemc-releases
+ https://repo.codemc.io/repository/maven-releases/
+
is-releases
https://repo.infernalsuite.com/repository/maven-releases/
@@ -103,6 +107,10 @@
jitpack.io
https://jitpack.io
+
+
+
@@ -143,5 +151,11 @@
MatrixColorAPI
v1.0.7
+
+ com.github.retrooper
+ packetevents-spigot
+ 2.11.2
+ provided
+
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java b/src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java
index ce8362f..0342f8d 100644
--- a/src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java
+++ b/src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java
@@ -1,21 +1,31 @@
package xyz.soukup.ecoCraftCore;
+import com.github.retrooper.packetevents.event.EventManager;
+import com.github.retrooper.packetevents.event.PacketListenerPriority;
+import com.infernalsuite.asp.api.AdvancedSlimePaperAPI;
+import com.infernalsuite.asp.api.world.SlimeWorldInstance;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.jdbc.JdbcConnectionSource;
+import com.j256.ormlite.stmt.UpdateBuilder;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;
+import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
+import xyz.soukup.ecoCraftCore.database.objects.*;
+import xyz.soukup.ecoCraftCore.islands.IslandCommand;
import xyz.soukup.ecoCraftCore.money.MoneyCommand;
import xyz.soukup.ecoCraftCore.player.PreparePlayer;
+import xyz.soukup.ecoCraftCore.player.TeleportRequestsHandler;
import xyz.soukup.ecoCraftCore.positionMarker.RulerCommand;
import xyz.soukup.ecoCraftCore.shop.ShopCommand;
-import xyz.soukup.ecoCraftCore.database.objects.Account;
-import xyz.soukup.ecoCraftCore.database.objects.Shop;
-import xyz.soukup.ecoCraftCore.database.objects.Transaction;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import xyz.soukup.ecoCraftCore.inventory.VirtualChest;
import xyz.soukup.ecoCraftCore.positionMarker.RulerMarking;
@@ -29,23 +39,38 @@ import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
+import com.github.retrooper.packetevents.PacketEvents;
+import xyz.soukup.ecoCraftCore.islands.FakeWater;
public final class EcoCraftCore extends JavaPlugin {
public static EcoCraftCore plugin;
public static ConnectionSource connectionSource;
+ public static FileConfiguration config;
@Override
public void onEnable() {
+
this.getLogger().info("plugin starting out");
+
plugin = this;
+ config = getConfig();
+
+ this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
+
+ PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
+ PacketEvents.getAPI().load();
+ PacketEvents.getAPI().init();
try {
+ saveDefaultConfig();
Messages.init();
prepareDatabase();
registerCommands();
registerEvents();
+ prepareSlimeWorldsSaver();
+
} catch (SQLException e) {
e.printStackTrace();
getLogger().severe("Failed to initialize database.");
@@ -63,13 +88,81 @@ public final class EcoCraftCore extends JavaPlugin {
public void onDisable() {
VirtualChest.saveCache();
+ saveSlimeWorlds();
+ adiosDatabase();
- try {
- if (connectionSource != null) {
- connectionSource.close();
+ PacketEvents.getAPI().terminate();
+
+ }
+
+ private void prepareSlimeWorldsSaver(){
+ Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> {
+ AdvancedSlimePaperAPI asp = AdvancedSlimePaperAPI.instance();
+
+ for (SlimeWorldInstance slimeWorldInstance : asp.getLoadedWorlds()){
+ try {
+ asp.saveWorld(slimeWorldInstance);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
- } catch (Exception e) {
- e.printStackTrace();
+ }, 12000L, 12000L);
+ }
+
+ private void prepareWorldInactivityUnloader(){
+ Bukkit.getScheduler().runTaskTimer(plugin, () -> {
+
+ long timeoutMillis = 30 * 60 * 1000;
+ long now = System.currentTimeMillis();
+
+ AdvancedSlimePaperAPI asp = AdvancedSlimePaperAPI.instance();
+ for (SlimeWorldInstance slimeWorldInstance : asp.getLoadedWorlds()) {
+
+ World world = slimeWorldInstance.getBukkitWorld();
+
+ if (!world.getPlayers().isEmpty()){
+ world.removeMetadata("last_empty_time", plugin);
+ continue;
+ }
+
+ if (!world.hasMetadata("last_empty_time")) {
+ world.setMetadata("last_empty_time", new FixedMetadataValue(plugin, now));
+ continue;
+ }
+
+ long emptySince = world.getMetadata("last_empty_time").get(0).asLong();
+
+
+ if ((now - emptySince) >= timeoutMillis) {
+ Bukkit.unloadWorld(world, true);
+ }
+
+
+
+ }
+
+ }, 1200L, 1200L);
+
+ }
+
+ private void saveSlimeWorlds(){
+ AdvancedSlimePaperAPI asp = AdvancedSlimePaperAPI.instance();
+
+ for (SlimeWorldInstance slimeWorldInstance : asp.getLoadedWorlds()){
+ try {
+ asp.saveWorld(slimeWorldInstance);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ try {
+ UpdateBuilder updateBuilder = DaoRegistry.getIslandDao().updateBuilder();
+ updateBuilder.where().eq("active_on", config.getString("server.name"));
+ updateBuilder.updateColumnValue("active_on", "");
+ updateBuilder.update();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
}
}
@@ -78,17 +171,40 @@ public final class EcoCraftCore extends JavaPlugin {
connectionSource = new JdbcConnectionSource(databaseUrl, "ecc", "ecc");
Logger.getLogger("com.j256.ormlite.table.TableUtils").setLevel(Level.OFF);
-
+ TableUtils.createTableIfNotExists(connectionSource, ActiveServer.class);
TableUtils.createTableIfNotExists(connectionSource, Transaction.class);
TableUtils.createTableIfNotExists(connectionSource, Shop.class);
TableUtils.createTableIfNotExists(connectionSource, VirtualChest.class);
TableUtils.createTableIfNotExists(connectionSource, Account.class);
+ TableUtils.createTableIfNotExists(connectionSource, Island.class);
+ TableUtils.createTableIfNotExists(connectionSource, TeleportRequest.class);
+ DaoRegistry.setActiveServerDao(DaoManager.createDao(connectionSource, ActiveServer.class));
DaoRegistry.setTransactionDao(DaoManager.createDao(connectionSource, Transaction.class));
+ DaoRegistry.setIslandDaoo(DaoManager.createDao(connectionSource, Island.class));
DaoRegistry.setShopDao(DaoManager.createDao(connectionSource, Shop.class));
DaoRegistry.setVirtualChestDao(DaoManager.createDao(connectionSource, VirtualChest.class));
DaoRegistry.setAccountDao(DaoManager.createDao(connectionSource, Account.class));
+ DaoRegistry.setTeleportRequestsDao(DaoManager.createDao(connectionSource, TeleportRequest.class));
+
+ ActiveServer activeServer = new ActiveServer(config.getString("server.name"));
+ activeServer.save();
+
+ }
+ private void adiosDatabase(){
+
+
+ try {
+ ActiveServer activeServer = new ActiveServer(config.getString("server.name"));
+ activeServer.delete();
+
+ if (connectionSource != null) {
+ connectionSource.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
private void registerCommands() {
@@ -97,19 +213,21 @@ public final class EcoCraftCore extends JavaPlugin {
lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(ShopCommand.createCommand().build()));
lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(RulerCommand.createCommand().build()));
lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(MoneyCommand.createCommand().build()));
+ lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(IslandCommand.createCommand().build()));
}
private void registerEvents(){
PluginManager pm = this.getServer().getPluginManager();
+ pm.registerEvents(new TeleportRequestsHandler(), this);
pm.registerEvents(new RulerMarking(), this);
pm.registerEvents(new VirtualChestLogic(), this);
pm.registerEvents(new ShopLogic(), this);
pm.registerEvents(new PreparePlayer(), this);
- }
-
-
+ EventManager events = PacketEvents.getAPI().getEventManager();
+ events.registerListener(new FakeWater(this), PacketListenerPriority.NORMAL);
+ }
}
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/database/DaoRegistry.java b/src/main/java/xyz/soukup/ecoCraftCore/database/DaoRegistry.java
index 46c9389..e5009df 100644
--- a/src/main/java/xyz/soukup/ecoCraftCore/database/DaoRegistry.java
+++ b/src/main/java/xyz/soukup/ecoCraftCore/database/DaoRegistry.java
@@ -10,7 +10,24 @@ public class DaoRegistry {
private static Dao virtualChestDao;
private static Dao accountDao;
private static Dao islandDao;
+ private static Dao teleportRequestsDao;
+ private static Dao activeServerDao;
+ public static Dao getTeleportRequestsDao() {
+ return teleportRequestsDao;
+ }
+
+ public static void setTeleportRequestsDao(Dao teleportRequestsDao) {
+ DaoRegistry.teleportRequestsDao = teleportRequestsDao;
+ }
+
+ public static Dao getActiveServerDao() {
+ return activeServerDao;
+ }
+
+ public static void setActiveServerDao(Dao activeServerDao) {
+ DaoRegistry.activeServerDao = activeServerDao;
+ }
public static Dao getShopDao() {
return shopDao;
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/database/objects/ActiveServer.java b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/ActiveServer.java
new file mode 100644
index 0000000..f02ce5b
--- /dev/null
+++ b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/ActiveServer.java
@@ -0,0 +1,34 @@
+package xyz.soukup.ecoCraftCore.database.objects;
+
+
+import com.j256.ormlite.field.DatabaseField;
+import com.j256.ormlite.table.DatabaseTable;
+import xyz.soukup.ecoCraftCore.database.DaoRegistry;
+
+import java.sql.SQLException;
+
+@DatabaseTable(tableName = "active_servers")
+public class ActiveServer {
+ @DatabaseField(unique = true, id = true)
+ private String name;
+
+ public ActiveServer(){
+
+ }
+
+ public ActiveServer(String name){
+ this.name = name;
+ }
+
+ public void save() throws SQLException {
+ DaoRegistry.getActiveServerDao().createIfNotExists(this);
+ }
+
+ public void delete() throws SQLException {
+ DaoRegistry.getActiveServerDao().delete(this);
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/database/objects/Island.java b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/Island.java
index edd47c5..002f8cf 100644
--- a/src/main/java/xyz/soukup/ecoCraftCore/database/objects/Island.java
+++ b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/Island.java
@@ -15,9 +15,8 @@ public class Island {
@DatabaseField(canBeNull = false, unique = true)
private String uuid;
- @DatabaseField(canBeNull = false, unique = true)
- private String name;
-
+ @DatabaseField(canBeNull = false)
+ private String type;
@DatabaseField(columnName = "display_name")
private String displayName;
@@ -37,12 +36,14 @@ public class Island {
@DatabaseField(defaultValue = "", columnName = "active_on")
private String activeOn;
+
+
public Island(){
}
- public Island(String name, String uuid, String displayName, String descritpion, String owner, String ownerType, byte[] data) {
- this.name = name;
+ public Island(String type, String uuid, String displayName, String descritpion, String owner, String ownerType, byte[] data) {
+ this.type = type;
this.uuid = uuid;
this.displayName = displayName;
this.descritpion = descritpion;
@@ -60,8 +61,8 @@ public class Island {
return id;
}
- public String getName() {
- return name;
+ public String getType() {
+ return type;
}
public String getDisplayName() {
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/database/objects/TeleportRequest.java b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/TeleportRequest.java
new file mode 100644
index 0000000..0dc48bf
--- /dev/null
+++ b/src/main/java/xyz/soukup/ecoCraftCore/database/objects/TeleportRequest.java
@@ -0,0 +1,96 @@
+package xyz.soukup.ecoCraftCore.database.objects;
+
+import com.j256.ormlite.field.DatabaseField;
+import com.j256.ormlite.table.DatabaseTable;
+import org.bukkit.Location;
+import xyz.soukup.ecoCraftCore.database.DaoRegistry;
+
+import java.sql.SQLException;
+
+@DatabaseTable(tableName = "teleport_requests")
+public class TeleportRequest {
+
+ @DatabaseField(unique = true, id = true)
+ private String player;
+
+ @DatabaseField(canBeNull = false)
+ private String server;
+
+ @DatabaseField(canBeNull = false)
+ private String world;
+
+ @DatabaseField
+ private Integer x;
+
+ @DatabaseField
+ private Integer y;
+
+ @DatabaseField
+ private Integer z;
+
+ @DatabaseField
+ private Float yaw;
+
+ @DatabaseField
+ private Float pitch;
+
+ public TeleportRequest() {
+ }
+
+ public TeleportRequest(String player, String server, String world) {
+ this.player = player;
+ this.server = server;
+ this.world = world;
+ }
+
+ public TeleportRequest(String player, String server, String world, Integer x, Integer y, Integer z, Float yaw, Float pitch) {
+ this.player = player;
+ this.server = server;
+ this.world = world;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.yaw = yaw;
+ this.pitch = pitch;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public String getWorld() {
+ return world;
+ }
+
+ public String getPlayer() {
+ return player;
+ }
+
+ public Integer getX() {
+ return x;
+ }
+
+ public Integer getY() {
+ return y;
+ }
+
+ public Integer getZ() {
+ return z;
+ }
+
+ public Float getYaw() {
+ return yaw;
+ }
+
+ public Float getPitch() {
+ return pitch;
+ }
+
+ public void save() throws SQLException {
+ DaoRegistry.getTeleportRequestsDao().createOrUpdate(this);
+ }
+
+ public void delete() throws SQLException {
+ DaoRegistry.getTeleportRequestsDao().delete(this);
+ }
+}
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandLoader.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/DatabaseIslandLoader.java
similarity index 98%
rename from src/main/java/xyz/soukup/ecoCraftCore/islands/IslandLoader.java
rename to src/main/java/xyz/soukup/ecoCraftCore/islands/DatabaseIslandLoader.java
index 1431f76..d6b0f26 100644
--- a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandLoader.java
+++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/DatabaseIslandLoader.java
@@ -10,7 +10,7 @@ import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;
-public class IslandLoader implements SlimeLoader {
+public class DatabaseIslandLoader implements SlimeLoader {
@Override
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/FakeWater.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/FakeWater.java
new file mode 100644
index 0000000..cd9fff6
--- /dev/null
+++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/FakeWater.java
@@ -0,0 +1,269 @@
+package xyz.soukup.ecoCraftCore.islands;
+
+import com.github.retrooper.packetevents.event.PacketListener;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
+import com.github.retrooper.packetevents.protocol.world.chunk.Column;
+import com.github.retrooper.packetevents.protocol.world.chunk.LightData;
+import com.github.retrooper.packetevents.protocol.world.chunk.TileEntity;
+import com.github.retrooper.packetevents.protocol.world.chunk.impl.v_1_18.Chunk_v1_18;
+import com.github.retrooper.packetevents.protocol.world.chunk.palette.DataPalette;
+import com.github.retrooper.packetevents.protocol.world.chunk.palette.PaletteType;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChunkData;
+import org.bukkit.Bukkit;
+import org.bukkit.NamespacedKey;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.BitSet;
+import java.util.Arrays;
+
+public class FakeWater implements PacketListener {
+ private static final int FALLBACK_WATER_STATE_ID_1_21_1 = 12015;
+
+ private final JavaPlugin plugin;
+ private final NamespacedKey keyX1, keyX2, keyZ1, keyZ2, keyType;
+
+ // Resolved once (reflection), cached afterwards
+ private volatile Integer cachedWaterStateId;
+
+ public FakeWater(JavaPlugin plugin) {
+ this.plugin = plugin;
+ this.keyX1 = new NamespacedKey(plugin, "borderx1");
+ this.keyX2 = new NamespacedKey(plugin, "borderx2");
+ this.keyZ1 = new NamespacedKey(plugin, "bordery1"); // Based on your spec
+ this.keyZ2 = new NamespacedKey(plugin, "borderx2"); // Based on your spec
+ this.keyType = new NamespacedKey(plugin, "island_type");
+ }
+
+ @Override
+ public void onPacketSend(PacketSendEvent event) {
+ if (event.getPacketType() != PacketType.Play.Server.CHUNK_DATA) return;
+
+ Player bukkitPlayer = Bukkit.getPlayer(event.getUser().getUUID());
+ if (bukkitPlayer == null) return;
+
+ World world = bukkitPlayer.getWorld();
+ PersistentDataContainer pdc = world.getPersistentDataContainer();
+
+ String type = pdc.get(keyType, PersistentDataType.STRING);
+ if (!"flat_grass".equals(type)) return;
+
+ Integer x1 = pdc.get(keyX1, PersistentDataType.INTEGER);
+ Integer x2 = pdc.get(keyX2, PersistentDataType.INTEGER);
+ Integer z1 = pdc.get(keyZ1, PersistentDataType.INTEGER);
+ Integer z2 = pdc.get(keyZ2, PersistentDataType.INTEGER);
+ if (x1 == null || x2 == null || z1 == null || z2 == null) return;
+
+ int minCX = Math.min(x1 >> 4, x2 >> 4) - 1;
+ int maxCX = Math.max(x1 >> 4, x2 >> 4) + 1;
+ int minCZ = Math.min(z1 >> 4, z2 >> 4) - 1;
+ int maxCZ = Math.max(z1 >> 4, z2 >> 4) + 1;
+
+ WrapperPlayServerChunkData wrapper = new WrapperPlayServerChunkData(event);
+
+ Column original = wrapper.getColumn();
+ if (original == null) return;
+
+ int chunkX = original.getX();
+ int chunkZ = original.getZ();
+ if (chunkX >= minCX && chunkX <= maxCX && chunkZ >= minCZ && chunkZ <= maxCZ) return;
+
+ Column fake = createFakeWaterColumn(world, original, resolveWaterStateId());
+ if (fake == null) return;
+
+ wrapper.setColumn(fake);
+
+ // Force full-bright lighting for fake chunks to avoid "goes dark after radius"
+ wrapper.setLightData(buildFullBrightLightData(world));
+ }
+
+ private static LightData buildFullBrightLightData(World world) {
+ int sections = (world.getMaxHeight() - world.getMinHeight()) >> 4;
+ if (sections <= 0) sections = 24;
+
+ // 1.17+ light data uses sectionCount + 2 (one below min, one above max)
+ int lightCount = sections + 2;
+
+ // Each light array entry is 16*16*16 nibbles = 4096 nibbles = 2048 bytes
+ byte[] fullSky = new byte[2048];
+ Arrays.fill(fullSky, (byte) 0xFF); // 0xF in every nibble => light level 15
+
+ byte[] noBlock = new byte[2048]; // default 0 => block light 0
+
+ // If your dimension can ever be no-skylight, handle it:
+ boolean hasSkyLight = world.getEnvironment() != World.Environment.NETHER
+ && world.getEnvironment() != World.Environment.THE_END; // adjust if you have custom dims
+
+ BitSet skyMask = new BitSet(lightCount);
+ BitSet blockMask = new BitSet(lightCount);
+ BitSet emptySkyMask = new BitSet(lightCount);
+ BitSet emptyBlockMask = new BitSet(lightCount);
+
+ byte[][] skyArray;
+ int skyCount;
+ if (hasSkyLight) {
+ skyMask.set(0, lightCount);
+ skyCount = lightCount;
+ skyArray = new byte[skyCount][];
+ for (int i = 0; i < skyCount; i++) {
+ skyArray[i] = fullSky;
+ }
+ } else {
+ // no skylight dimension
+ emptySkyMask.set(0, lightCount);
+ skyCount = 0;
+ skyArray = new byte[0][];
+ }
+
+ blockMask.set(0, lightCount);
+ int blockCount = lightCount;
+ byte[][] blockArray = new byte[blockCount][];
+ for (int i = 0; i < blockCount; i++) {
+ blockArray[i] = noBlock;
+ }
+
+ LightData ld = new LightData();
+ ld.setTrustEdges(true);
+ ld.setSkyLightMask(skyMask);
+ ld.setBlockLightMask(blockMask);
+ ld.setEmptySkyLightMask(emptySkyMask);
+ ld.setEmptyBlockLightMask(emptyBlockMask);
+ ld.setSkyLightCount(skyCount);
+ ld.setBlockLightCount(blockCount);
+ ld.setSkyLightArray(skyArray);
+ ld.setBlockLightArray(blockArray);
+ return ld;
+ }
+
+ private Column createFakeWaterColumn(World world, Column original, int waterStateId) {
+ int sections = (world.getMaxHeight() - world.getMinHeight()) >> 4;
+ if (sections <= 0) sections = 24;
+
+ BaseChunk[] originalChunks = original.getChunks();
+
+ // Find any existing biome palette from the original packet; we will reuse it.
+ DataPalette fallbackBiomePalette = null;
+ if (originalChunks != null) {
+ for (BaseChunk bc : originalChunks) {
+ if (bc instanceof Chunk_v1_18 c) {
+ fallbackBiomePalette = c.getBiomeData();
+ break;
+ }
+ }
+ }
+
+
+ BaseChunk[] chunks = new BaseChunk[sections];
+
+ for (int sectionIndex = 0; sectionIndex < sections; sectionIndex++) {
+ DataPalette biomePalette = fallbackBiomePalette;
+
+ if (originalChunks != null && sectionIndex < originalChunks.length && originalChunks[sectionIndex] instanceof Chunk_v1_18 c) {
+ // Prefer the matching section's biome palette if present
+ biomePalette = c.getBiomeData();
+ }
+
+ DataPalette blockPalette = PaletteType.CHUNK.create();
+
+ Chunk_v1_18 section = new Chunk_v1_18(0, blockPalette, biomePalette);
+ section.set(0, 0, 0, 0);
+
+ chunks[sectionIndex] = section;
+ }
+
+ // Section 0 == world Y -64..-49. Set only local y=0 and y=1 to water.
+ if (chunks[0] instanceof Chunk_v1_18 section0) {
+ for (int localY = 0; localY <= 1; localY++) {
+ for (int x = 0; x < 16; x++) {
+ for (int z = 0; z < 16; z++) {
+ section0.set(x, localY, z, waterStateId);
+ }
+ }
+ }
+ }
+
+ return new Column(
+ original.getX(),
+ original.getZ(),
+ true,
+ chunks,
+ new TileEntity[0],
+ original.getHeightMaps()
+ );
+ }
+
+ private int resolveWaterStateId() {
+ Integer cached = cachedWaterStateId;
+ if (cached != null) return cached;
+
+ int resolved = FALLBACK_WATER_STATE_ID_1_21_1;
+
+ try {
+ Object waterState = resolveNmsWaterBlockState();
+ if (waterState != null) {
+ Integer id = tryGetBlockStateIdViaBlockGetId(waterState);
+ if (id == null) {
+ id = tryGetBlockStateIdViaBuiltInRegistry(waterState);
+ }
+ if (id != null && id > 0) {
+ resolved = id;
+ }
+ }
+ } catch (ReflectiveOperationException ignored) {
+ // fallback
+ }
+
+ cachedWaterStateId = resolved;
+ plugin.getLogger().info("[FakeWater] Using water block-state id: " + resolved);
+ return resolved;
+ }
+
+ private static Object resolveNmsWaterBlockState() throws ReflectiveOperationException {
+ Class> blocksClass = Class.forName("net.minecraft.world.level.block.Blocks");
+ Field waterField = blocksClass.getField("WATER");
+ Object waterBlock = waterField.get(null);
+
+ Method defaultBlockState = waterBlock.getClass().getMethod("defaultBlockState");
+ return defaultBlockState.invoke(waterBlock);
+ }
+
+ private static Integer tryGetBlockStateIdViaBlockGetId(Object blockState) throws ReflectiveOperationException {
+ // Block.getId(BlockState) exists on many modern versions
+ Class> blockClass = Class.forName("net.minecraft.world.level.block.Block");
+ Class> blockStateClass = Class.forName("net.minecraft.world.level.block.state.BlockState");
+
+ try {
+ Method getId = blockClass.getMethod("getId", blockStateClass);
+ Object idObj = getId.invoke(null, blockState);
+ return (idObj instanceof Integer i) ? i : null;
+ } catch (NoSuchMethodException ignored) {
+ return null;
+ }
+ }
+
+ private static Integer tryGetBlockStateIdViaBuiltInRegistry(Object blockState) throws ReflectiveOperationException {
+ Class> blockStateClass = Class.forName("net.minecraft.world.level.block.state.BlockState");
+ Class> builtInRegistriesClass = Class.forName("net.minecraft.core.registries.BuiltInRegistries");
+
+ Field blockStateRegistryField = builtInRegistriesClass.getField("BLOCK_STATE");
+ Object blockStateRegistry = blockStateRegistryField.get(null);
+
+ Method getId = blockStateRegistry.getClass().getMethod("getId", Object.class);
+ try {
+ // Prefer exact signature if present: getId(BlockState)
+ Method exact = blockStateRegistry.getClass().getMethod("getId", blockStateClass);
+ Object idObj = exact.invoke(blockStateRegistry, blockState);
+ return (idObj instanceof Integer i) ? i : null;
+ } catch (NoSuchMethodException ignored) {
+ Object idObj = getId.invoke(blockStateRegistry, blockState);
+ return (idObj instanceof Integer i) ? i : null;
+ }
+ }
+}
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/FileIslandLoader.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/FileIslandLoader.java
new file mode 100644
index 0000000..e4ebff8
--- /dev/null
+++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/FileIslandLoader.java
@@ -0,0 +1,72 @@
+package xyz.soukup.ecoCraftCore.islands;
+
+import com.infernalsuite.asp.api.exceptions.UnknownWorldException;
+import com.infernalsuite.asp.api.loaders.SlimeLoader;
+import xyz.soukup.ecoCraftCore.EcoCraftCore;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class FileIslandLoader implements SlimeLoader {
+
+ private final Path storagePath;
+ private static final String EXTENSION = ".slime";
+
+ public FileIslandLoader() {
+ // Creates a directory named 'islands' inside your plugin folder
+ this.storagePath = EcoCraftCore.plugin.getDataFolder().toPath().resolve("island_templates");
+
+ if (!Files.exists(storagePath)) {
+ storagePath.toFile().mkdirs();
+ }
+ }
+
+ private Path getWorldPath(String worldName) {
+ return storagePath.resolve(worldName + EXTENSION);
+ }
+
+ @Override
+ public byte[] readWorld(String worldName) throws UnknownWorldException, IOException {
+ Path path = getWorldPath(worldName);
+ if (!Files.exists(path)) {
+ throw new UnknownWorldException(worldName);
+ }
+ return Files.readAllBytes(path);
+ }
+
+ @Override
+ public boolean worldExists(String worldName) throws IOException {
+ return Files.exists(getWorldPath(worldName));
+ }
+
+ @Override
+ public void saveWorld(String worldName, byte[] serializedWorld) throws IOException {
+ // Files.write will create or overwrite the file automatically
+ Files.write(getWorldPath(worldName), serializedWorld);
+ }
+
+ @Override
+ public void deleteWorld(String worldName) throws IOException {
+ Files.deleteIfExists(getWorldPath(worldName));
+ }
+
+ @Override
+ public List listWorlds() throws IOException {
+ File folder = storagePath.toFile();
+ File[] files = folder.listFiles((dir, name) -> name.endsWith(EXTENSION));
+
+ if (files == null) {
+ return Collections.emptyList();
+ }
+
+ return Arrays.stream(files)
+ .map(file -> file.getName().replace(EXTENSION, ""))
+ .collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandCommand.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandCommand.java
index 283b225..2c0cd2e 100644
--- a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandCommand.java
+++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandCommand.java
@@ -1,32 +1,228 @@
package xyz.soukup.ecoCraftCore.islands;
+import com.infernalsuite.asp.api.AdvancedSlimePaperAPI;
+import com.infernalsuite.asp.api.world.SlimeWorld;
+import com.j256.ormlite.stmt.QueryBuilder;
+import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import xyz.soukup.ecoCraftCore.database.DaoRegistry;
+import xyz.soukup.ecoCraftCore.database.objects.Island;
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.List;
+
+import static xyz.soukup.ecoCraftCore.EcoCraftCore.plugin;
public class IslandCommand {
- LiteralArgumentBuilder tp = Commands.literal("tp")
- .then(Commands.argument("uuid", StringArgumentType.word())
- .executes(IslandCommand::teleport));
+ public static LiteralArgumentBuilder createCommand() {
+
+ LiteralArgumentBuilder tp = Commands.literal("tp")
+ .then(Commands.argument("uuid", StringArgumentType.word())
+ .executes(IslandCommand::teleport)
+ .suggests(((context, builder) -> {
+ try {
+ QueryBuilder queryBuilder = DaoRegistry.getIslandDao().queryBuilder();
+ queryBuilder.selectColumns("uuid");
+ List islands = queryBuilder.query();
+ for (Island island : islands) {
+ builder.suggest(island.getUuid());
+ }
+ return builder.buildFuture();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ })));
+
+ LiteralArgumentBuilder create = Commands.literal("create")
+ .then(Commands.argument("type", StringArgumentType.word())
+ .then(Commands.argument("display_name", StringArgumentType.string())
+ .then(Commands.argument("description", StringArgumentType.greedyString())
+ .executes(IslandCommand::createWorld))));
+
+ LiteralArgumentBuilder load = Commands.literal("load")
+ .then(Commands.argument("uuid", StringArgumentType.word())
+ .suggests(((context, builder) -> {
+ try {
+ QueryBuilder queryBuilder = DaoRegistry.getIslandDao().queryBuilder();
+ queryBuilder.selectColumns("uuid");
+ List islands = queryBuilder.query();
+ for (Island island : islands) {
+ builder.suggest(island.getUuid());
+ }
+ return builder.buildFuture();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }))
+ .executes(IslandCommand::loadWorld));
+
+ LiteralArgumentBuilder loadTemplate = Commands.literal("loadTemplate")
+ .then(Commands.argument("name", StringArgumentType.word())
+ .suggests(((context, builder) -> {
+ IslandManager islandManager = new IslandManager();
+ FileIslandLoader fileLoader = islandManager.fileLoader;
+
+ try {
+ for (String world: fileLoader.listWorlds()){
+ builder.suggest(world);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return builder.buildFuture();
+ }))
+ .executes(IslandCommand::loadTemplate));
+
+ LiteralArgumentBuilder template = Commands.literal("template")
+ .then(Commands.argument("uuid", StringArgumentType.word())
+ .suggests((context, builder) -> {
+ try {
+ QueryBuilder queryBuilder = DaoRegistry.getIslandDao().queryBuilder();
+ queryBuilder.selectColumns("uuid");
+ List islands = queryBuilder.query();
+ for (Island island : islands) {
+ builder.suggest(island.getUuid());
+ }
+ return builder.buildFuture();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .then(Commands.argument("templateName", StringArgumentType.word())
+ .executes(IslandCommand::createTemplate)));
+
+ LiteralArgumentBuilder metadata = Commands.literal("metadata")
+ .then(Commands.argument("key", StringArgumentType.word())
+ .then(Commands.argument("value", IntegerArgumentType.integer())
+ .executes(IslandCommand::setMetadata)));
+
+ LiteralArgumentBuilder metadataString = Commands.literal("metadataString")
+ .then(Commands.argument("key", StringArgumentType.word())
+ .then(Commands.argument("value", StringArgumentType.string())
+ .executes(IslandCommand::setMetadataString)));
- LiteralArgumentBuilder create = Commands.literal("create")
- .then(Commands.argument("name", StringArgumentType.word())
- .then(Commands.argument("display_name", StringArgumentType.string())));
+ // NEW: Branch to read all metadata
+ LiteralArgumentBuilder listMetadata = Commands.literal("listMetadata")
+ .executes(IslandCommand::readAllMetadata);
+
+ return Commands.literal("island")
+ .then(tp)
+ .then(create)
+ .then(load)
+ .then(template)
+ .then(metadata)
+ .then(metadataString)
+ .then(listMetadata)
+ .then(loadTemplate);
+
+ }
private static int teleport(CommandContext context) {
+ IslandManager islandManager = new IslandManager();
+ try {
+ islandManager.teleport((Player) context.getSource().getSender(), context.getArgument("uuid", String.class));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
return 0;
}
private static int createWorld(CommandContext context) {
+ IslandManager islandManager = new IslandManager();
+ String type = context.getArgument("type", String.class);
+ String displayName = context.getArgument("display_name", String.class);
+ String description = context.getArgument("description", String.class);
+ islandManager.createIsland(type, displayName, description, "n", "n");
return 0;
}
private static int loadWorld(CommandContext context) {
+ IslandManager islandManager = new IslandManager();
+ islandManager.loadIsland(context.getArgument("uuid", String.class));
+ return 0;
+ }
+
+ private static int loadTemplate(CommandContext context) {
+ IslandManager islandManager = new IslandManager();
+ islandManager.loadIslandTemplate(context.getArgument("name", String.class));
return 0;
}
-}
+ private static int createTemplate(CommandContext context) {
+ IslandManager islandManager = new IslandManager();
+ String uuid = StringArgumentType.getString(context, "uuid");
+ String templateName = StringArgumentType.getString(context, "templateName");
+ context.getSource().getSender().sendMessage("§aCreating template '" + templateName + "' from world '" + uuid + "'...");
+ islandManager.createIslandTemplate(uuid, templateName);
+ context.getSource().getSender().sendMessage("§aTemplate created successfully!");
+ return 1;
+ }
+
+ private static int setMetadata(CommandContext context) {
+ if (!(context.getSource().getSender() instanceof Player player)) return 0;
+
+ String keyName = StringArgumentType.getString(context, "key");
+ Integer value = IntegerArgumentType.getInteger(context, "value");
+
+
+
+ player.getWorld().getPersistentDataContainer().set(new NamespacedKey(plugin, keyName), PersistentDataType.INTEGER, value);
+ return 1;
+ }
+
+ private static int setMetadataString(CommandContext context) {
+ if (!(context.getSource().getSender() instanceof Player player)) return 0;
+
+ String keyName = StringArgumentType.getString(context, "key");
+ String value = StringArgumentType.getString(context, "value");
+
+ player.getWorld().getPersistentDataContainer().set(new NamespacedKey(plugin, keyName), PersistentDataType.STRING, value);
+
+ return 1;
+ }
+
+ private static int readAllMetadata(CommandContext context) {
+ if (!(context.getSource().getSender() instanceof Player player)) {
+ context.getSource().getSender().sendMessage("§cOnly players can use this.");
+ return 0;
+ }
+
+
+ PersistentDataContainer pdc = player.getWorld().getPersistentDataContainer();
+ if (pdc.getKeys().isEmpty()) {
+ player.sendMessage("§eNo metadata found.");
+ return 1;
+ }
+
+ player.sendMessage("§6--- World Metadata ---");
+ for (NamespacedKey key : pdc.getKeys()) {
+ String val = "unknown";
+ // Logic to determine type for display
+ if (pdc.has(key, PersistentDataType.STRING)) val = pdc.get(key, PersistentDataType.STRING);
+ else if (pdc.has(key, PersistentDataType.INTEGER)) val = String.valueOf(pdc.get(key, PersistentDataType.INTEGER));
+
+ player.sendMessage("§b" + key.getKey() + "§7: §f" + val);
+ }
+ return 1;
+ }
+
+ private static void saveSlimeWorld(SlimeWorld world, Player player, String key, String val) {
+ try {
+ AdvancedSlimePaperAPI.instance().saveWorld(world);
+ player.sendMessage("§aMetadata set: §f" + key + " §7= §f" + val);
+ } catch (IOException e) {
+ player.sendMessage("§cFailed to save world metadata!");
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandManager.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandManager.java
index 939b960..aae1ff3 100644
--- a/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandManager.java
+++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/IslandManager.java
@@ -1,85 +1,253 @@
package xyz.soukup.ecoCraftCore.islands;
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
import com.infernalsuite.asp.api.AdvancedSlimePaperAPI;
+import com.infernalsuite.asp.api.exceptions.CorruptedWorldException;
+import com.infernalsuite.asp.api.exceptions.NewerFormatException;
+import com.infernalsuite.asp.api.exceptions.UnknownWorldException;
+import com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException;
import com.infernalsuite.asp.api.world.SlimeWorld;
import com.infernalsuite.asp.api.world.properties.SlimeProperties;
import com.infernalsuite.asp.api.world.properties.SlimePropertyMap;
+import com.j256.ormlite.dao.Dao;
+import com.j256.ormlite.dao.GenericRawResults;
+import com.j256.ormlite.stmt.QueryBuilder;
+import com.j256.ormlite.stmt.UpdateBuilder;
import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
import xyz.soukup.ecoCraftCore.database.objects.Island;
import xyz.soukup.ecoCraftCore.database.DaoRegistry;
+import xyz.soukup.ecoCraftCore.database.objects.TeleportRequest;
+import java.io.IOException;
import java.sql.SQLException;
+import java.util.Objects;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import static xyz.soukup.ecoCraftCore.EcoCraftCore.config;
import static xyz.soukup.ecoCraftCore.EcoCraftCore.plugin;
public class IslandManager {
private final AdvancedSlimePaperAPI asp = AdvancedSlimePaperAPI.instance();
- private final IslandLoader loader = new IslandLoader();
+ private final DatabaseIslandLoader databaseLoader = new DatabaseIslandLoader();
+ public final FileIslandLoader fileLoader = new FileIslandLoader();
+ private final Dao dao = DaoRegistry.getIslandDao();
+ public void createIslandTemplate(String uuid, String templateName){
+ try {
+ SlimeWorld slimeWorld = asp.getLoadedWorld(uuid).clone(templateName, fileLoader);
+ asp.saveWorld(slimeWorld);
+ } catch (WorldAlreadyExistsException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
- public void createIsland(String name, String displayName, String descritpion, String owner, String ownerType) {
+ public String createIsland(String type, String displayName, String descritpion, String owner, String ownerType) {
String uuid = UUID.randomUUID().toString();
-
- SlimePropertyMap props = new SlimePropertyMap();
- props.setValue(SlimeProperties.ENVIRONMENT, "overworld");
- props.setValue(SlimeProperties.WORLD_TYPE, "flat");
-
- // Create empty world in ASWM
try {
- // Note: createEmptyWorld is fast, so we can run some parts sync if needed,
- // but it's best to run the whole chain async.
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try {
- // Create the database entry first so the loader has a row to update
+ SlimePropertyMap props = new SlimePropertyMap();
+ props.setValue(SlimeProperties.ENVIRONMENT, "normal");
+ props.setValue(SlimeProperties.WORLD_TYPE, "flat");
+ props.setValue(SlimeProperties.ALLOW_ANIMALS, false);
+ props.setValue(SlimeProperties.ALLOW_MONSTERS, false);
+ props.setValue(SlimeProperties.SPAWN_X, 0);
+ props.setValue(SlimeProperties.SPAWN_Y, 2);
+ props.setValue(SlimeProperties.SPAWN_Z, 0);
- Island island = new Island(name, uuid, displayName, descritpion, owner, ownerType, null);
+
+ Island island = new Island(type, uuid, displayName, descritpion, owner, ownerType, null);
island.save();
- SlimeWorld slimeWorld = asp.createEmptyWorld(uuid, false, props, loader);
+
+ SlimeWorld slimeWorld;
+
+ slimeWorld = asp.createEmptyWorld(uuid, false, props, databaseLoader);
+
+ asp.saveWorld(slimeWorld);
- Bukkit.getScheduler().runTask(plugin, () -> {
- asp.loadWorld(slimeWorld, true);
- });
} catch (Exception e) { e.printStackTrace(); }
});
} catch (Exception e) { e.printStackTrace(); }
+
+ return uuid;
}
- // 2. Retrieve and Load existing island
- public int loadIsland(String uuid) {
- if (Bukkit.getWorld(uuid) != null) {
- return 0;
+
+ public void teleport(Player player, String uuid) throws Exception {
+ teleport(player, uuid, null, null, null, null, null);
+ }
+
+ public void teleport(Player player, String uuid, Integer x, Integer y, Integer z, Float yaw, Float pitch) throws Exception {
+ if (Bukkit.getWorld(uuid) != null){
+ teleportLocally(player, uuid, x, y, z, yaw, pitch);
}
- try {
- Island island = DaoRegistry.getIslandDao().queryBuilder()
- .selectColumns("active_on")
- .where()
- .eq("uuid", uuid)
- .queryForFirst();
-
- if (!island.getActiveOn().isEmpty()){
- return 1;
+ String whereIsActive = whereIsActive(uuid);
+ if (whereIsActive != null){
+ sendPlayerAway(player, whereIsActive, uuid, x, y, z, yaw, pitch);
+ }
+
+ QueryBuilder queryBuilder = dao.queryBuilder().setCountOf(true);
+ queryBuilder.where().eq("uuid", uuid);
+
+ if (dao.countOf(queryBuilder.prepare()) < 1){
+ return;
+ }
+
+ if (player.getVirtualHost() != null){
+ String emptiestServer = getEmptiestServer();
+ if (!Objects.equals(emptiestServer, config.getString("server.name"))){
+ sendPlayerAway(player, emptiestServer, uuid, x, y, z, yaw, pitch);
}
- } catch (SQLException e) {
- return 2;
+
}
+ teleportLocally(player, uuid, x, y, z, yaw, pitch);
+
+ }
+
+ public void teleportLocally(Player player, String uuid,
+ Integer x, Integer y, Integer z,
+ Float yaw, Float pitch) {
+
+ loadIsland(uuid).thenAccept(world -> {
+
+ Location location = world.getSpawnLocation();
+
+ if (x != null) {
+ location = new Location(world, x, y, z, yaw, pitch);
+ }
+
+ player.teleport(location);
+
+ }).exceptionally(ex -> {
+ ex.printStackTrace();
+ player.sendMessage("§cFailed to load island.");
+ return null;
+ });
+ }
+
+ private String whereIsActive(String uuid) throws SQLException {
+ Island island = dao.queryBuilder()
+ .selectColumns("active_on")
+ .where()
+ .eq("uuid", uuid)
+ .queryForFirst();
+
+ if (island == null){
+ return null;
+ }
+ if (island.getActiveOn() == null){
+ return null;
+ }
+
+ return island.getActiveOn();
+ }
+
+ private void sendPlayerAway(Player player, String server, String uuid, Integer x, Integer y, Integer z, Float yaw, Float pitch) throws SQLException {
+ TeleportRequest teleportRequest = new TeleportRequest(player.getName(), server, uuid, x, y, z, yaw, pitch);
+ teleportRequest.save();
+
+ ByteArrayDataOutput out = ByteStreams.newDataOutput();
+ out.writeUTF("Connect");
+ out.writeUTF(server);
+ player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
+ }
+
+ public void loadIslandTemplate(String name){
+ SlimeWorld slimeWorld = null;
+ try {
+ slimeWorld = asp.readWorld(fileLoader, name, false, new SlimePropertyMap());
+ asp.loadWorld(slimeWorld, true);
+
+ } catch (UnknownWorldException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (CorruptedWorldException e) {
+ throw new RuntimeException(e);
+ } catch (NewerFormatException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public CompletableFuture loadIsland(String uuid) {
+
+ CompletableFuture future = new CompletableFuture<>();
+
+ if (Bukkit.getWorld(uuid) != null) {
+ future.complete(Bukkit.getWorld(uuid));
+ return future;
+ }
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try {
- SlimeWorld slimeWorld = asp.readWorld(loader, uuid, false, new SlimePropertyMap());
+ SlimeWorld slimeWorld = asp.readWorld(databaseLoader, uuid, false, new SlimePropertyMap());
+
Bukkit.getScheduler().runTask(plugin, () -> {
- asp.loadWorld(slimeWorld, true);
+ try {
+
+ UpdateBuilder updateBuilder = dao.updateBuilder();
+ updateBuilder.where().eq("uuid", uuid);
+ updateBuilder.updateColumnValue("active_on", config.getString("server.name"));
+ updateBuilder.update();
+
+ asp.loadWorld(slimeWorld, true);
+
+ World world = Bukkit.getWorld(uuid);
+ if (world != null) {
+ future.complete(world);
+ } else {
+ future.completeExceptionally(
+ new IllegalStateException("World loaded but Bukkit returned null"));
+ }
+
+ } catch (Exception e) {
+ future.completeExceptionally(e);
+ }
});
- } catch (Exception e) { e.printStackTrace(); }
+
+ } catch (Exception e) {
+ future.completeExceptionally(e);
+ }
});
- return 0;
+
+ return future;
+ }
+
+ private String getEmptiestServer() throws Exception {
+ String query = "SELECT active_servers.name " +
+ "FROM active_servers " +
+ "LEFT JOIN islands ON active_servers.name = islands.active_on " +
+ "GROUP BY active_servers.name " +
+ "ORDER BY COUNT(islands.uuid) ASC " +
+ "LIMIT 1";
+
+ GenericRawResults rawResults =
+ DaoRegistry.getActiveServerDao().queryRaw(query);
+
+ String[] firstResult = rawResults.getFirstResult();
+ rawResults.close();
+
+ if (firstResult != null && firstResult.length > 0) {
+ String serverName = firstResult[0];
+ return firstResult[0];
+ }
+
+ return null;
}
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/UnloadWorld.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/UnloadIsland.java
similarity index 61%
rename from src/main/java/xyz/soukup/ecoCraftCore/islands/UnloadWorld.java
rename to src/main/java/xyz/soukup/ecoCraftCore/islands/UnloadIsland.java
index 63977a8..9a14b2f 100644
--- a/src/main/java/xyz/soukup/ecoCraftCore/islands/UnloadWorld.java
+++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/UnloadIsland.java
@@ -1,4 +1,4 @@
package xyz.soukup.ecoCraftCore.islands;
-public class UnloadWorld {
+public class UnloadIsland {
}
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/islands/generators/FlatGrass.java b/src/main/java/xyz/soukup/ecoCraftCore/islands/generators/FlatGrass.java
new file mode 100644
index 0000000..e8a20c1
--- /dev/null
+++ b/src/main/java/xyz/soukup/ecoCraftCore/islands/generators/FlatGrass.java
@@ -0,0 +1,44 @@
+package xyz.soukup.ecoCraftCore.islands.generators;
+
+import org.bukkit.Material;
+import org.bukkit.generator.ChunkGenerator;
+import org.bukkit.generator.WorldInfo;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Random;
+
+public class FlatGrass extends ChunkGenerator {
+
+
+ @Override
+ public void generateNoise(@NotNull WorldInfo worldInfo, @NotNull Random random, int chunkX, int chunkZ, @NotNull ChunkData chunkData) {
+
+ for (int x = 0; x < 16; x++) {
+ for (int z = 0; z < 16; z++) {
+ for (int y = worldInfo.getMinHeight(); y < 0; y++) {
+ chunkData.setBlock(x, y, z, Material.WATER);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void generateSurface(@NotNull WorldInfo worldInfo, @NotNull Random random, int chunkX, int chunkZ, @NotNull ChunkData chunkData) {
+ for (int x = 0; x < 16; x++) {
+ for (int z = 0; z < 16; z++) {
+
+ int worldX = (chunkX * 16) + x;
+ int worldZ = (chunkZ * 16) + z;
+
+ if (worldX >= -50 && worldX < 50 && worldZ >= -50 && worldZ < 50) {
+ chunkData.setBlock(x, 1, z, Material.STONE);
+ }
+ if (worldX >= -51 && worldX < 51 && worldZ >= -51 && worldZ < 51) {
+ chunkData.setBlock(x, 0, z, Material.STONE);
+ chunkData.setBlock(x, -1, z, Material.STONE);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/xyz/soukup/ecoCraftCore/player/TeleportRequestsHandler.java b/src/main/java/xyz/soukup/ecoCraftCore/player/TeleportRequestsHandler.java
new file mode 100644
index 0000000..99ac37b
--- /dev/null
+++ b/src/main/java/xyz/soukup/ecoCraftCore/player/TeleportRequestsHandler.java
@@ -0,0 +1,40 @@
+package xyz.soukup.ecoCraftCore.player;
+
+import com.j256.ormlite.stmt.QueryBuilder;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import xyz.soukup.ecoCraftCore.database.DaoRegistry;
+import xyz.soukup.ecoCraftCore.database.objects.TeleportRequest;
+import xyz.soukup.ecoCraftCore.islands.IslandManager;
+
+import java.sql.SQLException;
+
+public class TeleportRequestsHandler implements Listener {
+
+ @EventHandler
+ public void teleportRequestHandler(PlayerJoinEvent event){
+ try {
+ Player player = event.getPlayer();
+
+ QueryBuilder queryBuilder = DaoRegistry.getTeleportRequestsDao().queryBuilder();
+ queryBuilder.where().eq("player", player.getName());
+
+ TeleportRequest teleportRequest = queryBuilder.queryForFirst();
+
+ if (teleportRequest == null){
+ return;
+ }
+
+ IslandManager islandManager = new IslandManager();
+
+ islandManager.teleportLocally(player, teleportRequest.getWorld(), teleportRequest.getX(), teleportRequest.getY(), teleportRequest.getY(), teleportRequest.getYaw(), teleportRequest.getPitch());
+
+ teleportRequest.delete();
+
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 5868d64..2c14e67 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -2,3 +2,6 @@ name: EcoCraftCore
version: '1.0-SNAPSHOT'
main: xyz.soukup.ecoCraftCore.EcoCraftCore
api-version: '1.21'
+depend:
+ - packetevents
+