Compare commits

..

No commits in common. "master" and "1.19.4-2" have entirely different histories.

15 changed files with 171 additions and 352 deletions

View file

@ -1,27 +0,0 @@
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

1
.gitignore vendored
View file

@ -116,4 +116,3 @@ run/
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar !gradle-wrapper.jar
niri-rpc/

View file

@ -1 +0,0 @@

View file

@ -1,51 +1,15 @@
# THIS IS NOT MY PLUGIN, I JUST EDITED ONE SINGLE LINE IN PLUGIN.YML, THE ORIGINAL IS AT: https://github.com/MidnightTale/FoliaFlow # FoliaFlow
![sss1](https://user-images.githubusercontent.com/125941391/231678608-9762d384-dc3a-440c-a4eb-3340cb39a3e3.png)
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 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)
FoliaFlow is a Minecraft plugin that allows players to using gravity block duplication glitch
## Installation ## Installation
To install FoliaFlow, follow these steps: To install FoliaFlow, follow these steps:
1. Download the plugin JAR file from the [Modrinth](https://modrinth.com/plugin/foliaflow). 1. Download the plugin JAR file from the [releases page](https://github.com/Hynse/FoliaFlow/releases).
2. Copy the JAR file to the `plugins` folder of your Minecraft server. 2. Copy the JAR file to the `plugins` folder of your Minecraft server.
3. Restart your server to load the plugin. 3. Restart your server to enable 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 ## Contributing

View file

@ -2,8 +2,10 @@ plugins {
id 'java' id 'java'
} }
def loader = 'folia'
def server = '1.19.4'
group = 'xyz.hynse' group = 'xyz.hynse'
version = "3.0" version = "1.4-${server}-${loader}"
repositories { repositories {
mavenCentral() mavenCentral()
@ -11,10 +13,14 @@ repositories {
name = "papermc-repo" name = "papermc-repo"
url = "https://repo.papermc.io/repository/maven-public/" url = "https://repo.papermc.io/repository/maven-public/"
} }
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/groups/public/"
}
} }
dependencies { dependencies {
compileOnly "dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT" compileOnly "dev.folia:folia-api:1.19.4-R0.1-SNAPSHOT"
} }
def targetJavaVersion = 17 def targetJavaVersion = 17

0
gradlew vendored Executable file → Normal file
View file

View file

@ -1,73 +1,171 @@
package xyz.hynse.foliaflow; package xyz.hynse.foliaflow;
import org.bukkit.configuration.file.YamlConfiguration; import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
import io.papermc.paper.threadedregions.scheduler.RegionScheduler;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.Slab;
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.plugin.java.JavaPlugin;
import org.bukkit.util.Vector;
import xyz.hynse.foliaflow.command.ReloadCommand; import java.util.*;
import xyz.hynse.foliaflow.util.CheckSafeTeleportation; import java.util.concurrent.TimeUnit;
import xyz.hynse.foliaflow.watcher.PortalWatcher;
import java.io.File; import static org.bukkit.Bukkit.getScheduler;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class FoliaFlow extends JavaPlugin { public class FoliaFlow extends JavaPlugin implements Listener {
private boolean isFirstEnable = true; private final double vh = 0.2;
//public boolean isUnSafeTeleport = false; private final double vt = 0.8;
public static FoliaFlow instance; private final Vector velocity1 = new Vector(0, vh, vt);
public static double horizontalCoefficient; private final Vector velocity2 = new Vector(vt, vh, 0);
public static double verticalCoefficient; private final Vector velocity3 = new Vector(0, vh, -vt);
public static double spawnHeight; private final Vector velocity4 = new Vector(-vt, vh, 0);
public static double TeleportOffset;
//public static CheckSafeTeleportation checkSafeTeleportation; private final Vector[] velocities = {velocity1, velocity2, velocity3, velocity4};
private int counter = 0;
private final Set<Location> movingBlocks = new HashSet<>();
private final Map<Entity, Vector> velocitiesMap = new HashMap<>(); // Create a map to store velocities
private ScheduledTask task;
private ScheduledTask blockktask;
@Override @Override
public void onEnable() { public void onEnable() {
instance = this; super.onEnable();
register();
if (isFirstEnable) {
isFirstEnable = false;
copyDefaultConfig();
setInitialConfigValues();
}
reload();
}
private void copyDefaultConfig() { // Get the region scheduler for the server
File configFile = new File(getDataFolder(), "config.yml");
if (!configFile.exists()) {
saveResource("config.yml", false);
}
}
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 { try {
config.save(configFile); RegionScheduler schedulerblock = getServer().getRegionScheduler();
} catch (IOException e) {
// Schedule a repeating task to run every tick using runAtFixedRate() method
blockktask = 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);
} catch (NullPointerException block) {
getServer().getLogger().info("Region Scheduler erorr (likly chunky it not load)");
}
AsyncScheduler scheduler = getServer().getAsyncScheduler();
task = scheduler.runAtFixedRate(this, (scheduledTask) -> getScheduler().runTask(this, () -> {
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) {
// Set the initial velocity of the falling block only if it doesn't have a velocity stored
if (!velocitiesMap.containsKey(entity)) {
try {
int index = counter % 4;
counter++;
Vector velocity = velocities[index];
entity.setVelocity(velocity);
velocitiesMap.put(entity, velocity); // Store the velocity in the map
movingBlocks.add(entity.getLocation()); // Add the location to the set
} catch (NullPointerException e) {
// Handle the exception here, for example:
e.printStackTrace(); e.printStackTrace();
getServer().getLogger().info("An error occurred: " + e.getMessage());
}
}
}
}
}
}
}), 0L, 1L, TimeUnit.MILLISECONDS);
getServer().getPluginManager().registerEvents(this, this);
getServer().getConsoleSender().sendMessage("");
getServer().getConsoleSender().sendMessage(ChatColor.GREEN + " ______________ ");
getServer().getConsoleSender().sendMessage(ChatColor.GREEN + " / ____/ ____/ /___ _ __");
getServer().getConsoleSender().sendMessage(ChatColor.GREEN + " / /_ / /_ / / __ \\ | /| / /");
getServer().getConsoleSender().sendMessage(ChatColor.GREEN + " / __/ / __/ / / /_/ / |/ |/ / ");
getServer().getConsoleSender().sendMessage(ChatColor.GREEN + "/_/ /_/ /_/\\____/|__/|__/ ");
getServer().getConsoleSender().sendMessage("");
getServer().getConsoleSender().sendMessage(ChatColor.YELLOW + "Plugin started successfully!");
getServer().getConsoleSender().sendMessage("");
}
@Override
public void onDisable() {
task.cancel();
blockktask.cancel();
super.onDisable();
debug();
}
@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 erorr (likly chunky it not load)");
}
} }
} }
public void reload() { Block getBlockMovingTo(Location loc, Vector vel) {
saveDefaultConfig(); double absMax = 0, max = 0;
reloadConfig(); char dir = ' ';
horizontalCoefficient = getConfig().getDouble("horizontal_coefficient"); Block relative = null;
verticalCoefficient = getConfig().getDouble("vertical_coefficient"); if (Math.abs(vel.getX()) > absMax) {
spawnHeight = getConfig().getDouble("spawn_height"); max = vel.getX();
TeleportOffset = getConfig().getDouble("teleport_offset"); absMax = Math.abs(vel.getX());
dir = 'x';
}
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));
}
//debug("Moving falling block from location " + loc.toString() + " to location " + dir);
return relative;
} }
private void register() { private void debug() {
getCommand("flowreload").setExecutor(new ReloadCommand()); getServer().getConsoleSender().sendMessage(ChatColor.AQUA + "[FoliaFlow] " + "Plugin stopped successfully!");
// checkSafeTeleportation = new CheckSafeTeleportation();
// checkSafeTeleportation.updateSafeTeleportation();
getServer().getPluginManager().registerEvents(new PortalWatcher(), this);
} }
} }

View file

@ -1,23 +0,0 @@
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;
}
}

View file

@ -1,49 +0,0 @@
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<String, Object> config = yaml.load(fis);
Map<String, Object> unsupportedSettings = (Map<String, Object>) 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.");
// }
// }
}

View file

@ -1,32 +0,0 @@
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);
}
}
}

View file

@ -1,19 +0,0 @@
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()));
}
}

View file

@ -1,73 +0,0 @@
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
);
}
}

View file

@ -1,19 +0,0 @@
# 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
# -----------------------------------------

View file

@ -0,0 +1,5 @@
name: FoliaFlow
version: '${version}'
main: xyz.hynse.foliaflow.FoliaFlow
api-version: 1.19
folia-supported: true

View file

@ -1,10 +0,0 @@
name: FoliaFlow
version: '${version}'
main: xyz.hynse.foliaflow.FoliaFlow
api-version: 26.1.2
folia-supported: true
commands:
flowreload:
description: Reload FoliaFlow
usage: /<command>
permission: foliaflow.reload