From c478a1c382006e78f4d1a6a11f4fc7916143c47f Mon Sep 17 00:00:00 2001
From: balugaq <129668183+balugaq@users.noreply.github.com>
Date: Sun, 10 Nov 2024 22:55:31 +0800
Subject: [PATCH] Resolves #170 #171
---
pom.xml | 2 +-
.../utils/ReflectionUtil.java | 16 ++
.../networksexpansion/utils/WorldUtils.java | 56 +++++++
.../networks/commands/NetworksMain.java | 137 ++++++++++++++++--
4 files changed, 196 insertions(+), 15 deletions(-)
create mode 100644 src/main/java/com/ytdd9527/networksexpansion/utils/WorldUtils.java
diff --git a/pom.xml b/pom.xml
index 63b977d0..7eb910fa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
4.0.0
com.ytdd9527.networksexpansion
NetworksExpansion
- 2.1-Alpha-5
+ 2.1-Alpha-6
diff --git a/src/main/java/com/ytdd9527/networksexpansion/utils/ReflectionUtil.java b/src/main/java/com/ytdd9527/networksexpansion/utils/ReflectionUtil.java
index c6d09217..6657798f 100644
--- a/src/main/java/com/ytdd9527/networksexpansion/utils/ReflectionUtil.java
+++ b/src/main/java/com/ytdd9527/networksexpansion/utils/ReflectionUtil.java
@@ -1,5 +1,6 @@
package com.ytdd9527.networksexpansion.utils;
+import io.github.thebusybiscuit.slimefun4.libraries.dough.collections.Pair;
import lombok.experimental.UtilityClass;
import java.lang.reflect.Field;
@@ -68,4 +69,19 @@ public static T getProperty(Object o, Class clazz, String fieldName) t
return null;
}
+
+ public static Pair> getDeclaredFieldsRecursively(Class> clazz, String fieldName){
+ try{
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return new Pair<>(field, clazz);
+ }catch (Throwable e){
+ clazz = clazz.getSuperclass();
+ if(clazz == null){
+ return null;
+ } else {
+ return getDeclaredFieldsRecursively(clazz, fieldName);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/ytdd9527/networksexpansion/utils/WorldUtils.java b/src/main/java/com/ytdd9527/networksexpansion/utils/WorldUtils.java
new file mode 100644
index 00000000..823eb558
--- /dev/null
+++ b/src/main/java/com/ytdd9527/networksexpansion/utils/WorldUtils.java
@@ -0,0 +1,56 @@
+package com.ytdd9527.networksexpansion.utils;
+
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockState;
+
+import java.lang.reflect.Field;
+
+public class WorldUtils {
+ protected static Class> CraftBlockStateClass;
+ protected static Field IBlockDataField;
+ protected static Field BlockPositionField;
+ protected static Field WorldField;
+ protected static Field WeakWorldField;
+ protected static boolean invokeBlockStateSuccess = false;
+ static {
+ try {
+ World sampleWorld= Bukkit.getWorlds().get(0);
+ BlockState blockstate=sampleWorld.getBlockAt(0, 0, 0).getState();
+ var result= ReflectionUtil.getDeclaredFieldsRecursively(blockstate.getClass(),"data");
+ IBlockDataField = result.getFirstValue();
+ IBlockDataField.setAccessible(true);
+ CraftBlockStateClass = result.getSecondValue();
+ BlockPositionField = ReflectionUtil.getDeclaredFieldsRecursively(CraftBlockStateClass,"position").getFirstValue();
+ BlockPositionField.setAccessible(true);
+ WorldField = ReflectionUtil.getDeclaredFieldsRecursively(CraftBlockStateClass,"world").getFirstValue();
+ WorldField.setAccessible(true);
+ WeakWorldField = ReflectionUtil.getDeclaredFieldsRecursively(CraftBlockStateClass,"weakWorld").getFirstValue();
+ WeakWorldField.setAccessible(true);
+ invokeBlockStateSuccess = true;
+ } catch (Throwable ignored) {
+
+ }
+ }
+ public static boolean copyBlockState(BlockState stateToSet, Block toBlock){
+ if (!invokeBlockStateSuccess) {
+ return false;
+ }
+
+ BlockState originalState = toBlock.getState();
+ if (!CraftBlockStateClass.isInstance(originalState) ||!CraftBlockStateClass.isInstance(stateToSet)) {
+ return false;
+ }
+
+ try {
+ BlockPositionField.set(stateToSet, BlockPositionField.get(originalState));
+ WorldField.set(stateToSet, WorldField.get(originalState));
+ WeakWorldField.set(stateToSet, WeakWorldField.get(originalState));
+ stateToSet.update(true, false);
+ return true;
+ } catch (Throwable ignored) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/github/sefiraat/networks/commands/NetworksMain.java b/src/main/java/io/github/sefiraat/networks/commands/NetworksMain.java
index 3abb660b..6a5a34de 100644
--- a/src/main/java/io/github/sefiraat/networks/commands/NetworksMain.java
+++ b/src/main/java/io/github/sefiraat/networks/commands/NetworksMain.java
@@ -8,6 +8,7 @@
import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils;
import com.ytdd9527.networksexpansion.core.items.unusable.AbstractBlueprint;
import com.ytdd9527.networksexpansion.implementation.machines.unit.NetworksDrawer;
+import com.ytdd9527.networksexpansion.utils.WorldUtils;
import io.github.bakedlibs.dough.collections.Pair;
import io.github.bakedlibs.dough.skins.PlayerHead;
import io.github.bakedlibs.dough.skins.PlayerSkin;
@@ -30,12 +31,14 @@
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
+import org.bukkit.Bukkit;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
+import org.bukkit.block.BlockState;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -55,33 +58,33 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
-@SuppressWarnings("deprecation")
+@SuppressWarnings({"deprecation", "unused"})
public class NetworksMain implements TabExecutor {
- private static final Map> SELECTED_POS = new HashMap<>();
-
+ private static final Map> SELECTED_POS = new HashMap<>();
public static Location getPos1(Player p) {
- if (SELECTED_POS.get(p.getName()) == null) {
+ if (SELECTED_POS.get(p.getUniqueId()) == null) {
return null;
}
- return SELECTED_POS.get(p.getName()).getFirstValue();
+ return SELECTED_POS.get(p.getUniqueId()).getFirstValue();
}
public static Location getPos2(Player p) {
- if (SELECTED_POS.get(p.getName()) == null) {
+ if (SELECTED_POS.get(p.getUniqueId()) == null) {
return null;
}
- return SELECTED_POS.get(p.getName()).getSecondValue();
+ return SELECTED_POS.get(p.getUniqueId()).getSecondValue();
}
public static void setPos1(Player p, Location pos) {
- SELECTED_POS.put(p.getName(), new Pair<>(pos, getPos2(p)));
+ SELECTED_POS.put(p.getUniqueId(), new Pair<>(pos, getPos2(p)));
}
public static void setPos2(Player p, Location pos) {
- SELECTED_POS.put(p.getName(), new Pair<>(getPos1(p), pos));
+ SELECTED_POS.put(p.getUniqueId(), new Pair<>(getPos1(p), pos));
}
public static String locationToString(Location l) {
@@ -353,6 +356,104 @@ public static void worldeditPos2(Player player, Location location) {
}
}
+ public static void worldeditClone(Player player) {
+ worldeditClone(player, false);
+ }
+ public static void worldeditClone(Player player, boolean overrideData) {
+ if (getPos1(player) == null || getPos2(player) == null) {
+ player.sendMessage(Networks.getLocalizationService().getString("messages.commands.worldedit.must-select-area"));
+ return;
+ }
+
+ if (!Objects.equals(getPos1(player).getWorld().getUID(), getPos2(player).getWorld().getUID())) {
+ player.sendMessage(Networks.getLocalizationService().getString("messages.commands.worldedit.must-select-same-world"));
+ return;
+ }
+
+ player.sendMessage(String.format(Networks.getLocalizationService().getString("messages.commands.worldedit.pasting-block"), locationToString(getPos1(player)), locationToString(getPos2(player))));
+ final long currentMillSeconds = System.currentTimeMillis();
+
+ final AtomicInteger count = new AtomicInteger();
+ final Location playerLocation = player.getLocation();
+ final ItemStack itemInHand = player.getItemInHand();
+
+ final Location pos1 = getPos1(player);
+ final int dx = playerLocation.getBlockX() - pos1.getBlockX();
+ final int dy = playerLocation.getBlockY() - pos1.getBlockY();
+ final int dz = playerLocation.getBlockZ() - pos1.getBlockZ();
+
+ Bukkit.getScheduler().runTask(Networks.getInstance(), () -> {
+ doWorldEdit(getPos1(player), getPos2(player), (fromLocation -> {
+ final Block fromBlock = fromLocation.getBlock();
+ final Block toBlock = playerLocation.getWorld().getBlockAt(fromLocation.getBlockX() + dx, fromLocation.getBlockY() + dy, fromLocation.getBlockZ() + dz);
+ final SlimefunItem slimefunItem = StorageCacheUtils.getSfItem(fromLocation);
+ final Location toLocation = toBlock.getLocation();
+
+ // Block Data
+ WorldUtils.copyBlockState(fromBlock.getState(), toBlock);
+
+ // Count means successful pasting block data. Not including Slimefun data.
+ count.addAndGet(1);
+
+ // Slimefun Data
+ if (slimefunItem == null) {
+ return;
+ }
+
+ // Call Handler
+ slimefunItem.callItemHandler(BlockPlaceHandler.class, handler -> handler.onPlayerPlace(
+ new BlockPlaceEvent(
+ toBlock,
+ toBlock.getState(),
+ toBlock.getRelative(BlockFace.SOUTH),
+ itemInHand,
+ player,
+ true
+ )
+ ));
+
+ SlimefunBlockData fromSlimefunBlockData = Slimefun.getDatabaseManager().getBlockDataController().getBlockData(fromLocation);
+ if (overrideData) {
+ Slimefun.getDatabaseManager().getBlockDataController().removeBlock(toLocation);
+ }
+
+ if (StorageCacheUtils.hasBlock(toLocation)) {
+ return;
+ }
+
+ // Slimefun Block
+ Slimefun.getDatabaseManager().getBlockDataController().createBlock(toLocation, slimefunItem.getId());
+ SlimefunBlockData toSlimefunBlockData = Slimefun.getDatabaseManager().getBlockDataController().getBlockData(toLocation);
+
+ // SlimefunBlockData
+ if (fromSlimefunBlockData == null || toSlimefunBlockData == null) {
+ return;
+ }
+
+ Map data = fromSlimefunBlockData.getAllData();
+ for (String key : data.keySet()) {
+ toSlimefunBlockData.setData(key, data.get(key));
+ }
+
+ // BlockMenu
+ final BlockMenu fromMenu = fromSlimefunBlockData.getBlockMenu();
+ final BlockMenu toMenu = toSlimefunBlockData.getBlockMenu();
+
+ if (fromMenu == null || toMenu == null) {
+ return;
+ }
+
+ ItemStack[] contents = fromMenu.getContents();
+ for (int i = 0; i < contents.length; i++) {
+ if (contents[i] != null) {
+ toMenu.getInventory().setItem(i, contents[i].clone());
+ }
+ }
+ }));
+ player.sendMessage(String.format(Networks.getLocalizationService().getString("messages.commands.worldedit.paste-done"), count, System.currentTimeMillis() - currentMillSeconds));
+ });
+ }
+
public static void worldeditPaste(Player player, String sfid) {
worldeditPaste(player, sfid, false, false);
}
@@ -415,9 +516,9 @@ public static void worldeditPaste(Player player, String sfid, boolean overrideDa
}
skin = skin0;
isHead = isHead0;
- // java's operation ↑
+
doWorldEdit(getPos1(player), getPos2(player), (location -> {
- final Block targetBlock = getPos1(player).getWorld().getBlockAt(location);
+ final Block targetBlock = location.getBlock();
sfItem.callItemHandler(BlockPlaceHandler.class, h -> h.onPlayerPlace(
new BlockPlaceEvent(
targetBlock,
@@ -923,6 +1024,14 @@ public boolean onCommand(@Nonnull CommandSender sender, @Nonnull Command command
}
}
+ case "clone" -> {
+ if (args.length <= 2) {
+ worldeditClone(player);
+ } else if (args.length <= 3) {
+ worldeditClone(player, "override".equalsIgnoreCase(args[2]));
+ }
+ }
+
case "paste" -> {
if (args.length <= 2) {
player.sendMessage(getErrorMessage(ErrorType.MISSING_REQUIRED_ARGUMENT, "sfId"));
@@ -932,13 +1041,13 @@ public boolean onCommand(@Nonnull CommandSender sender, @Nonnull Command command
boolean force = false;
switch (args.length) {
case 5 -> {
- if (args[3].toLowerCase(Locale.ROOT).equals("override")) {
+ if ("override".equalsIgnoreCase(args[3])) {
overrideData = true;
}
force = Boolean.parseBoolean(args[4]);
}
case 4 -> {
- if (args[3].toLowerCase(Locale.ROOT).equals("override")) {
+ if ("override".equalsIgnoreCase(args[3])) {
overrideData = true;
}
}
@@ -1166,7 +1275,7 @@ public void fixBlueprint(Player player, String before) {
case "fillquantum", "addstorageitem", "reducestorageitem", "setquantum" -> List.of("");
case "fixblueprint" -> List.of("");
case "setcontainerid" -> List.of("");
- case "worldedit" -> List.of("pos1", "pos2", "paste", "clear", "blockmenu", "blockinfo");
+ case "worldedit" -> List.of("pos1", "pos2", "paste", "clear", "clone", "blockmenu", "blockinfo");
default -> List.of();
};
}