diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7829495 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,27 @@ +name: Build JAR + +on: + push: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'adopt' + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Get project version + run: echo "version=$(grep '^version' build.gradle | cut -d' ' -f3 | tr -d '\"')" >> $GITHUB_ENV + - name: Build with Gradle + run: ./gradlew build -x test + - name: Publish artifact + uses: actions/upload-artifact@v4 + with: + name: FoliaFlow-${{ env.version }}-${{ github.sha }} + path: build/libs/FoliaFlow-${{ env.version }}.jar diff --git a/.gitignore b/.gitignore index 3c37caf..cdb5be5 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,4 @@ run/ # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar +niri-rpc/ diff --git a/README.md b/README.md index 83cdbb4..e1bf6f5 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,51 @@ -# FoliaFlow -![sss1](https://user-images.githubusercontent.com/125941391/231678608-9762d384-dc3a-440c-a4eb-3340cb39a3e3.png) +# THIS IS NOT MY PLUGIN, I JUST EDITED ONE SINGLE LINE IN PLUGIN.YML, THE ORIGINAL IS AT: https://github.com/MidnightTale/FoliaFlow -FoliaFlow is a Minecraft plugin that allows players to using gravity block duplication glitch again in folia (paper soon) +FoliaFlow is a Minecraft plugin that allows players to using gravity block duplication glitch again in Paper and Folia ## How is work? -Whenever a falling sand entity comes close to or hits the end portal, a new falling sand entity is summoned with a velocity that shoots it straight into the portal. However, we encountered a problem when the entity got reset by the safe teleport paper patch - it lost its velocity. To fix this, we added the velocity back when the entity was in the end. Another issue we faced was that the entity would instantly turn into a block upon teleportation because it have block below. To solve this, we made the center block of the spawn platform a slab, ensuring that the entity stays in its falling state. +Whenever a falling sand entity comes close to or hits the end portal, a new falling sand entity is summoned with a velocity that shoots it straight into the portal. However, we encountered a problem when the entity got reset by the safe teleport Paper patch - it lose its velocity. To fix this, we added the velocity back when the entity was in the end. + +## ⚠️If you using folia +And type of concreate factory collector that use piston full block to stop falling blocks. You should change to slab to prevent block early break follow picture below +### Top View +![topviewff](https://user-images.githubusercontent.com/125941391/234716000-8dfe3f57-b9a4-4340-8d1a-4e63fe2cdb91.png) +### Side View +![sideviewff](https://user-images.githubusercontent.com/125941391/234716115-f793602c-17f9-4689-9740-7ff25013eed4.png) ## Installation To install FoliaFlow, follow these steps: -1. Download the plugin JAR file from the [releases page](https://github.com/Hynse/FoliaFlow/releases). +1. Download the plugin JAR file from the [Modrinth](https://modrinth.com/plugin/foliaflow). 2. Copy the JAR file to the `plugins` folder of your Minecraft server. -3. Restart your server to enable the plugin. +3. Restart your server to load the plugin. + +## Config flies (config.yml) +```yml +# Don't touch this file unless you know what you're doing +# Default values seems vanilla. +# If you find values that seems more vanilla please create a PR +# https://github.com/Hynse/FoliaFlow + +# Default value for horizontal_coefficient +# - Folia (1.499) +# - Paper (2) +horizontal_coefficient: 1.499 +# ----------------------------------------- + +vertical_coefficient: -1.7 +spawn_height: 0.08 + +# Default value for teleport_offset +# - Folia (0.5) +# - Paper (2.4) +teleport_offset: 0.5 +# ----------------------------------------- +``` + +## Permissions +`foliaflow.reload` for using command `/flowreload` to reload config flies ## Contributing diff --git a/build.gradle b/build.gradle index 425c4d0..722f80f 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'xyz.hynse' -version = "1.7" +version = "3.0" repositories { mavenCentral() @@ -14,7 +14,7 @@ repositories { } dependencies { - compileOnly "dev.folia:folia-api:1.19.4-R0.1-SNAPSHOT" + compileOnly "dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT" } def targetJavaVersion = 17 diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/xyz/hynse/foliaflow/FoliaFlow.java b/src/main/java/xyz/hynse/foliaflow/FoliaFlow.java index bcbc5c5..b1cbc6b 100644 --- a/src/main/java/xyz/hynse/foliaflow/FoliaFlow.java +++ b/src/main/java/xyz/hynse/foliaflow/FoliaFlow.java @@ -1,182 +1,73 @@ package xyz.hynse.foliaflow; -import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler; -import io.papermc.paper.threadedregions.scheduler.RegionScheduler; -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.block.data.type.Slab; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.BlockDisplay; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.FallingBlock; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; + +import xyz.hynse.foliaflow.command.ReloadCommand; +import xyz.hynse.foliaflow.util.CheckSafeTeleportation; +import xyz.hynse.foliaflow.watcher.PortalWatcher; import java.io.File; import java.io.IOException; -import java.util.*; -public class FoliaFlow extends JavaPlugin implements Listener { - private final double vh = 0.2; - private final double vt = 0.8; - private final Vector velocity1 = new Vector(0, vh, vt); - private final Vector velocity2 = new Vector(vt, vh, 0); - private final Vector velocity3 = new Vector(0, vh, -vt); - private final Vector velocity4 = new Vector(-vt, vh, 0); +import java.io.InputStream; +import java.io.InputStreamReader; - private final Vector[] velocities = {velocity1, velocity2, velocity3, velocity4}; - private int counter = 0; - private final Set movingBlocks = new HashSet<>(); - private final Map velocitiesMap = new HashMap<>(); +public class FoliaFlow extends JavaPlugin { + private boolean isFirstEnable = true; + //public boolean isUnSafeTeleport = false; + public static FoliaFlow instance; + public static double horizontalCoefficient; + public static double verticalCoefficient; + public static double spawnHeight; + public static double TeleportOffset; + //public static CheckSafeTeleportation checkSafeTeleportation; @Override public void onEnable() { - super.onEnable(); - RegionScheduler schedulerblock = getServer().getRegionScheduler(); - schedulerblock.runAtFixedRate(this, Objects.requireNonNull(Bukkit.getWorld("world_the_end")), 1, 1, (schedulerTask) -> { - Block block = Objects.requireNonNull(Bukkit.getWorld("world_the_end")).getBlockAt(100, 48, 0); - if (block.getType() == Material.OBSIDIAN) { - block.setType(Material.COBBLED_DEEPSLATE_SLAB); - Slab slab = (Slab) block.getBlockData(); - slab.setType(Slab.Type.BOTTOM); - block.setBlockData(slab); - - } - - }, 1L, 1L); - - GlobalRegionScheduler schedulervelocity = this.getServer().getGlobalRegionScheduler(); - - schedulervelocity.runAtFixedRate(this, (scheduledTask) -> { - for (World world : Bukkit.getWorlds()) { - for (Entity entity : world.getEntities()) { - if (entity.getType() == EntityType.FALLING_BLOCK && entity.getWorld().getEnvironment() == World.Environment.THE_END) { - Location centerLoc = new Location(entity.getWorld(), 100, 48.5, 0); - Location loc = entity.getLocation(); - if (loc.distance(centerLoc) <= 1) { - if (!velocitiesMap.containsKey(entity)) { - int index = counter % 4; - counter++; - Vector velocity = velocities[index]; - entity.setVelocity(velocity); - velocitiesMap.put(entity, velocity); - movingBlocks.add(entity.getLocation()); - } - } - } - } - } - }, 20L, 1L); - - - File dataFile = new File(getDataFolder(), "data.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(dataFile); - - boolean entitySpawned = config.getBoolean("entity_spawned", false); - - if (!entitySpawned) { - RegionScheduler schedulerdisplay = getServer().getRegionScheduler(); - String tag = "FoliaFlow_Display"; - - schedulerdisplay.run(this, Objects.requireNonNull(Bukkit.getWorld("world_the_end")), 1, 1, (schedulerTask) -> { - World endWorld = getServer().getWorld("world_the_end"); - assert endWorld != null; - Chunk chunk = endWorld.getChunkAt(0, 0); - if (!chunk.isLoaded()) { - chunk.load(); - getLogger().info("Loaded Chunk"); - } - - boolean displayExists = endWorld.getEntitiesByClass(BlockDisplay.class).stream() - .anyMatch(entity -> entity.getScoreboardTags().contains(tag)); - getLogger().info("Exist BlockDisplay" + "(" + tag + ")"); - - if (!displayExists) { - BlockDisplay display = (BlockDisplay) endWorld.spawnEntity(new Location(endWorld, 100.0005, 48, -0.0005), EntityType.BLOCK_DISPLAY); - display.setBlock(Bukkit.createBlockData(Material.OBSIDIAN)); - display.addScoreboardTag(tag); - getLogger().info("Setup BlockDisplay" + "(" + tag + ") " + display); - - config.set("entity_spawned", true); - try { - config.save(dataFile); - } catch (IOException e) { - getLogger().warning("Failed to save plugin data file"); - } - } - }); + instance = this; + register(); + if (isFirstEnable) { + isFirstEnable = false; + copyDefaultConfig(); + setInitialConfigValues(); } - - - getServer().getPluginManager().registerEvents(this, this); - } - @Override - public void onDisable() { - super.onDisable(); + reload(); } - - - - @EventHandler - public void onFallingBlockToBlock(EntityChangeBlockEvent e){ - if(e.getEntityType() == EntityType.FALLING_BLOCK){ - Entity entity = e.getEntity(); - Location loc = entity.getLocation(); - Vector vel = entity.getVelocity(); - Block movingTo = getBlockMovingTo(loc, vel); - try { - - if(movingTo != null && movingTo.getType() == Material.END_PORTAL){ - Location spawnLoc = movingTo.getLocation(); - spawnLoc.setX(spawnLoc.getX()+0.5); - spawnLoc.setY(spawnLoc.getY()-0.25); - spawnLoc.setZ(spawnLoc.getZ()+0.5); - - FallingBlock dummy = loc.getWorld().spawnFallingBlock(spawnLoc, ((FallingBlock) entity).getBlockData()); - dummy.setDropItem(true); - dummy.setHurtEntities(true); - dummy.setGravity(true); - Vector dummyVel = vel.clone(); - dummyVel.setY(-dummyVel.getY()); - dummyVel.multiply(new Vector(2, 1, 2)); - - dummyVel.add(new Vector(0, 1, 0)); - - dummy.setVelocity(dummyVel); - } - } catch (NullPointerException dummy) { - getServer().getLogger().info("onFallingBlockToBlock error (likely chunky it not load)"); - } + private void copyDefaultConfig() { + File configFile = new File(getDataFolder(), "config.yml"); + if (!configFile.exists()) { + saveResource("config.yml", false); } } - Block getBlockMovingTo(Location loc, Vector vel) { - double absMax = 0, max = 0; - char dir = ' '; - Block relative = null; - if (Math.abs(vel.getX()) > absMax) { - max = vel.getX(); - absMax = Math.abs(vel.getX()); - dir = 'x'; + private void setInitialConfigValues() { + File configFile = new File(getDataFolder(), "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + InputStream defaultConfigStream = getResource("config.yml"); + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defaultConfigStream)); + config.options().copyDefaults(true); + config.setDefaults(defaultConfig); + try { + config.save(configFile); + } catch (IOException e) { + e.printStackTrace(); } - if (Math.abs(vel.getY()) > absMax) { - max = vel.getY(); - absMax = Math.abs(vel.getY()); - dir = 'y'; - } - if (Math.abs(vel.getZ()) > absMax) { - max = vel.getZ(); - dir = 'z'; - } - switch (dir) { - case 'x' -> relative = loc.getBlock().getRelative((int) Math.signum(max), 0, 0); - case 'y' -> relative = loc.getBlock().getRelative(0, (int) Math.signum(max), 0); - case 'z' -> relative = loc.getBlock().getRelative(0, 0, (int) Math.signum(max)); - } - return relative; } -} \ No newline at end of file + + public void reload() { + saveDefaultConfig(); + reloadConfig(); + horizontalCoefficient = getConfig().getDouble("horizontal_coefficient"); + verticalCoefficient = getConfig().getDouble("vertical_coefficient"); + spawnHeight = getConfig().getDouble("spawn_height"); + TeleportOffset = getConfig().getDouble("teleport_offset"); + } + + private void register() { + getCommand("flowreload").setExecutor(new ReloadCommand()); +// checkSafeTeleportation = new CheckSafeTeleportation(); +// checkSafeTeleportation.updateSafeTeleportation(); + getServer().getPluginManager().registerEvents(new PortalWatcher(), this); + } +} diff --git a/src/main/java/xyz/hynse/foliaflow/command/ReloadCommand.java b/src/main/java/xyz/hynse/foliaflow/command/ReloadCommand.java new file mode 100644 index 0000000..a81bd5d --- /dev/null +++ b/src/main/java/xyz/hynse/foliaflow/command/ReloadCommand.java @@ -0,0 +1,23 @@ +package xyz.hynse.foliaflow.command; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +import xyz.hynse.foliaflow.FoliaFlow; + +public class ReloadCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("foliaflow.reload")) return true; + try { + FoliaFlow.instance.reload(); + sender.sendMessage(ChatColor.YELLOW + "FoliaFlow reloaded."); + } catch (Exception e) { + sender.sendMessage(ChatColor.RED + "Something went wrong reloading FoliaFlow, see the console for more."); + e.printStackTrace(); + } + return true; + } +} diff --git a/src/main/java/xyz/hynse/foliaflow/util/CheckSafeTeleportation.java b/src/main/java/xyz/hynse/foliaflow/util/CheckSafeTeleportation.java new file mode 100644 index 0000000..58940d3 --- /dev/null +++ b/src/main/java/xyz/hynse/foliaflow/util/CheckSafeTeleportation.java @@ -0,0 +1,49 @@ +package xyz.hynse.foliaflow.util; + +import org.yaml.snakeyaml.Yaml; +import xyz.hynse.foliaflow.FoliaFlow; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Map; + +import static org.bukkit.Bukkit.getServer; + +public class CheckSafeTeleportation { + + public boolean checkUnsafeEndPortalTeleportation() { + File configFile = new File(getServer().getWorldContainer(), "config/paper-global.yml"); + if (!configFile.exists()) { + FoliaFlow.instance.getLogger().warning("paper-global.yml not found!"); + return false; + } + + try (FileInputStream fis = new FileInputStream(configFile)) { + Yaml yaml = new Yaml(); + Map config = yaml.load(fis); + + Map unsupportedSettings = (Map) config.get("unsupported-settings"); + if (unsupportedSettings != null) { + Boolean setting = (Boolean) unsupportedSettings.get("allow-unsafe-end-portal-teleportation"); + return setting != null && setting; + } + } catch (IOException e) { + FoliaFlow.instance.getLogger().severe("Failed to read paper-global.yml: " + e.getMessage()); + } + + return false; + } + + +// public void updateSafeTeleportation() { +// boolean safeTeleportation = checkUnsafeEndPortalTeleportation(); +// if (safeTeleportation) { +// FoliaFlow.instance.isUnSafeTeleport = true; +// FoliaFlow.instance.getLogger().info("Unsafe End Portal Teleportation is true."); +// } else { +// FoliaFlow.instance.isUnSafeTeleport = false; +// FoliaFlow.instance.getLogger().info("Unsafe End Portal Teleportation is false."); +// } +// } +} diff --git a/src/main/java/xyz/hynse/foliaflow/util/SchedulerUtil.java b/src/main/java/xyz/hynse/foliaflow/util/SchedulerUtil.java new file mode 100644 index 0000000..5883390 --- /dev/null +++ b/src/main/java/xyz/hynse/foliaflow/util/SchedulerUtil.java @@ -0,0 +1,32 @@ +package xyz.hynse.foliaflow.util; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +public class SchedulerUtil { + + private static Boolean IS_FOLIA = null; + + private static boolean tryFolia() { + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + public static Boolean isFolia() { + if (IS_FOLIA == null) IS_FOLIA = tryFolia(); + return IS_FOLIA; + } + + public static void runLaterEntity(Entity entity, Plugin plugin, Runnable runnable, int ticks) { + if (isFolia()) { + entity.getScheduler().runDelayed(plugin, (task) -> runnable.run(), null, ticks); + } else { + Bukkit.getScheduler().runTaskLater(plugin, runnable, ticks); + } + } +} diff --git a/src/main/java/xyz/hynse/foliaflow/util/VelocityUtil.java b/src/main/java/xyz/hynse/foliaflow/util/VelocityUtil.java new file mode 100644 index 0000000..953fbee --- /dev/null +++ b/src/main/java/xyz/hynse/foliaflow/util/VelocityUtil.java @@ -0,0 +1,19 @@ +package xyz.hynse.foliaflow.util; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +public class VelocityUtil { + public static Block getBlockMovingTo(Location loc, Vector vel) { + double absX = Math.abs(vel.getX()); + double absY = Math.abs(vel.getY()); + double absZ = Math.abs(vel.getZ()); + if (absX > absY && absX > absZ) { + return loc.getBlock().getRelative((int) Math.signum(vel.getX()), 0, 0); + } else if (absY > absZ) { + return loc.getBlock().getRelative(0, (int) Math.signum(vel.getY()), 0); + } + return loc.getBlock().getRelative(0, 0, (int) Math.signum(vel.getZ())); + } +} diff --git a/src/main/java/xyz/hynse/foliaflow/watcher/PortalWatcher.java b/src/main/java/xyz/hynse/foliaflow/watcher/PortalWatcher.java new file mode 100644 index 0000000..581b07b --- /dev/null +++ b/src/main/java/xyz/hynse/foliaflow/watcher/PortalWatcher.java @@ -0,0 +1,73 @@ +package xyz.hynse.foliaflow.watcher; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.FallingBlock; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityPortalEnterEvent; +import org.bukkit.util.Vector; + +import xyz.hynse.foliaflow.FoliaFlow; +import xyz.hynse.foliaflow.util.SchedulerUtil; + +import static xyz.hynse.foliaflow.util.VelocityUtil.getBlockMovingTo; + +public class PortalWatcher implements Listener { + + private static final String DUPE_TAG = "foliaflow_dupe"; + + @EventHandler + public void onFallingBlockToBlock(EntityChangeBlockEvent e){ + if(e.getEntityType() != EntityType.FALLING_BLOCK) return; + Entity entity = e.getEntity(); + if(entity.getScoreboardTags().contains(DUPE_TAG)) return; + Location loc = entity.getLocation(); + Vector vel = entity.getVelocity(); + Block movingTo = getBlockMovingTo(loc, vel); + + if(movingTo.getType() == Material.END_PORTAL){ + spawnDupe(entity, movingTo, false); + } + } + + @EventHandler + public void onPortalEnter(EntityPortalEnterEvent e){ + if(e.getEntityType() != EntityType.FALLING_BLOCK) return; + Entity entity = e.getEntity(); + if(entity.getScoreboardTags().contains(DUPE_TAG)) return; + Block portal = e.getLocation().getBlock(); + + if(portal.getType() == Material.END_PORTAL && portal.getRelative(0, -1, 0).getType().name().endsWith("_FENCE")){ + spawnDupe(entity, portal, true); + } + } + + private void spawnDupe(Entity entity, Block portal, boolean passThrough){ + Location spawnLoc = portal.getLocation(); + spawnLoc.add(0.5, FoliaFlow.spawnHeight, 0.5); + + FallingBlock dummy = entity.getWorld().spawnFallingBlock(spawnLoc, ((FallingBlock) entity).getBlockData()); + dummy.addScoreboardTag(DUPE_TAG); + Vector dummyVel = entity.getVelocity().clone(); + dummyVel.multiply(FoliaFlow.horizontalCoefficient); + if(passThrough){ + dummyVel.setY(entity.getVelocity().getY()); + } else { + dummyVel.setY(entity.getVelocity().getY() * FoliaFlow.verticalCoefficient); + } + dummy.setVelocity(dummyVel); + + SchedulerUtil.runLaterEntity(dummy, FoliaFlow.instance, () -> { + // Portal teleportation is a bit below vanilla, so we teleport it above + dummy.teleportAsync(dummy.getLocation().add(0, FoliaFlow.TeleportOffset, 0)); + dummy.setVelocity(dummyVel); + }, + 2 + ); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..6c9c6cb --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,19 @@ +# Don't touch this file unless you know what you're doing +# Default values seems vanilla. +# If you find values that seems more vanilla please create a PR +# https://github.com/Hynse/FoliaFlow + +# Default value for horizontal_coefficient +# - Folia (1.499) +# - Paper (2) +horizontal_coefficient: 1.499 +# ----------------------------------------- + +vertical_coefficient: -1.7 +spawn_height: 0.08 + +# Default value for teleport_offset +# - Folia (0.5) +# - Paper (2.4) +teleport_offset: 0.5 +# ----------------------------------------- diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml deleted file mode 100644 index 19f50a9..0000000 --- a/src/main/resources/paper-plugin.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: FoliaFlow -version: '${version}' -main: xyz.hynse.foliaflow.FoliaFlow -api-version: 1.19 -folia-supported: true \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 19f50a9..fb90a09 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,10 @@ name: FoliaFlow version: '${version}' main: xyz.hynse.foliaflow.FoliaFlow -api-version: 1.19 -folia-supported: true \ No newline at end of file +api-version: 26.1.2 +folia-supported: true +commands: + flowreload: + description: Reload FoliaFlow + usage: / + permission: foliaflow.reload