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 +