9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-28 03:19:15 +00:00

WorldEdit support

This commit is contained in:
XiaoMoMi
2024-09-07 23:54:36 +08:00
parent 6ca6281507
commit d4a40f5b5c
15 changed files with 511 additions and 15 deletions

View File

@@ -13,12 +13,14 @@ repositories {
maven("https://nexus.betonquest.org/repository/betonquest/") // betonquest
maven("https://repo.dmulloy2.net/repository/public/") // betonquest needs packet wrapper?
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://repo.rapture.pw/repository/maven-releases/") // flow nbt
}
dependencies {
compileOnly(project(":common"))
compileOnly(project(":api"))
compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}")
compileOnly("com.flowpowered:flow-nbt:${rootProject.properties["flow_nbt_version"]}")
compileOnly("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") {
exclude(module = "adventure-bom")
exclude(module = "checker-qual")

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.bukkit.integration.worldedit;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.InternalRegistries;
import net.momirealms.customcrops.api.core.block.CustomCropsBlock;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;
import net.momirealms.customcrops.common.util.Key;
import java.util.Optional;
public class CustomCropsDelegateExtent extends AbstractDelegateExtent {
private CustomCropsWorld<?> world = null;
protected CustomCropsDelegateExtent(EditSessionEvent editSessionEvent) {
super(editSessionEvent.getExtent());
World weWorld = editSessionEvent.getWorld();
if (weWorld == null) return;
Optional<CustomCropsWorld<?>> optionalWorld = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(weWorld.getName());
optionalWorld.ifPresent(customCropsWorld -> this.world = customCropsWorld);
}
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 location, T block) throws WorldEditException {
if (world != null) {
BaseBlock baseBlock = block.toBaseBlock();
Pos3 pos3 = new Pos3(location.getBlockX(), location.getBlockY(), location.getBlockZ());
world.removeBlockState(pos3);
CompoundTag tag = baseBlock.getNbtData();
if (tag != null) {
String type = tag.getString("cc_type");
if (type != null) {
Key key = Key.key(type);
CustomCropsBlock customCropsBlock = InternalRegistries.BLOCK.get(key);
if (customCropsBlock != null) {
byte[] bytes = tag.getByteArray("cc_data");
if (bytes != null) {
CustomCropsBlockState state = CustomCropsBlockState.create(customCropsBlock, bytes);
world.addBlockState(pos3, state);
}
}
}
}
}
return super.setBlock(location, block);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.bukkit.integration.worldedit;
public class WorldEditHook {
private static WorldEditListener listener;
private static boolean registered;
public static void register() {
if (registered) return;
try {
if (listener == null) {
listener = new WorldEditListener();
}
listener.load();
registered= true;
} catch (Exception e) {
e.printStackTrace();
}
}
public static void unregister() {
if (!registered) return;
try {
if (listener != null) {
listener.unload();
registered = false;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.bukkit.integration.worldedit;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.session.SessionManager;
import com.sk89q.worldedit.util.eventbus.EventHandler;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.ConfigManager;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;
import net.momirealms.customcrops.api.util.PluginUtils;
import net.momirealms.customcrops.common.plugin.feature.Reloadable;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class WorldEditListener implements Reloadable, Listener {
private Constructor<?> baseBlockConstructor;
public WorldEditListener() {
try {
baseBlockConstructor = BaseBlock.class.getDeclaredConstructor(BlockState.class, CompoundTag.class);
baseBlockConstructor.setAccessible(true);
// if (PluginUtils.isEnabled("FastAsyncWorldEdit")) {
// BukkitCustomCropsPlugin.getInstance().getPluginLogger().info("FastAsyncWorldEdit detected. Don't forget to add `" + CustomCropsDelegateExtent.class.getCanonicalName() + "` to allowed-plugins.");
// }
} catch (ReflectiveOperationException e) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Not a supported WorldEdit version", e);
}
}
@Override
public void unload() {
WorldEdit.getInstance().getEventBus().unregister(this);
HandlerList.unregisterAll(this);
}
@Override
public void load() {
WorldEdit.getInstance().getEventBus().register(this);
Bukkit.getPluginManager().registerEvents(this, BukkitCustomCropsPlugin.getInstance().getBoostrap());
}
@Subscribe(priority = EventHandler.Priority.LATE)
public void onEditSession(EditSessionEvent event) {
if (!ConfigManager.worldeditSupport()) return;
if (event.getStage() != EditSession.Stage.BEFORE_CHANGE) {
return;
}
try {
event.setExtent(new CustomCropsDelegateExtent(event));
} catch (Exception e) {
BukkitCustomCropsPlugin.getInstance().debug(e.getMessage());
}
}
@org.bukkit.event.EventHandler(ignoreCancelled = true)
private void onCommand(PlayerCommandPreprocessEvent event) {
if (!ConfigManager.worldeditSupport()) return;
String string = event.getMessage();
if (string.startsWith("//copy")) {
Player player = event.getPlayer();
BukkitCustomCropsPlugin.getInstance().getScheduler().sync().runLater(() ->
updateCopyClipBoard(player), 1, player.getLocation());
}
if (string.startsWith("//cut")) {
updateCutClipBoard(event.getPlayer());
}
}
public void updateCutClipBoard(Player player) {
com.sk89q.worldedit.entity.Player actor = BukkitAdapter.adapt(player);
SessionManager manager = WorldEdit.getInstance().getSessionManager();
LocalSession localSession = manager.get(actor);
World world = actor.getWorld();
Region region;
try {
region = localSession.getRegionSelector(world).getRegion();
} catch (IncompleteRegionException e) {
return;
}
Optional<CustomCropsWorld<?>> optionalWorld = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(world.getName());
if (optionalWorld.isEmpty()) {
return;
}
CustomCropsWorld<?> customWorld = optionalWorld.get();
HashMap<BlockVector3, CustomCropsBlockState> tempMap = new HashMap<>();
BlockVector3 maxPoint = region.getMaximumPoint();
BlockVector3 minPoint = region.getMinimumPoint();
int minX = Math.min(minPoint.getBlockX(), maxPoint.getBlockX());
int minY = Math.min(minPoint.getBlockY(), maxPoint.getBlockY());
int minZ = Math.min(minPoint.getBlockZ(), maxPoint.getBlockZ());
int maxX = Math.max(maxPoint.getBlockX(), minPoint.getBlockX());
int maxY = Math.max(maxPoint.getBlockY(), minPoint.getBlockY());
int maxZ = Math.max(maxPoint.getBlockZ(), minPoint.getBlockZ());
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
Pos3 pos3 = new Pos3(x, y, z);
BlockVector3 vector3 = BlockVector3.at(x, y, z);
Optional<CustomCropsBlockState> optionalState = customWorld.getBlockState(pos3);
optionalState.ifPresent(customCropsBlockState -> tempMap.put(vector3, customCropsBlockState));
}
}
}
BukkitCustomCropsPlugin.getInstance().getScheduler().sync().runLater(() -> {
ClipboardHolder holder;
try {
holder = localSession.getClipboard();
} catch (EmptyClipboardException e) {
return;
}
Clipboard clipboard = holder.getClipboard();
for (Map.Entry<BlockVector3, CustomCropsBlockState> entry : tempMap.entrySet()) {
BaseBlock baseBlock = clipboard.getFullBlock(entry.getKey());
CustomCropsBlockState state = entry.getValue();
CompoundTag tag = baseBlock.getNbtData();
if (tag != null) {
Map<String, Tag> map = tag.getValue();
map.put("cc_type", new StringTag(state.type().type().asString()));
map.put("cc_data", new ByteArrayTag(state.getNBTDataAsBytes()));
} else {
try {
clipboard.setBlock(entry.getKey(), (BaseBlock) baseBlockConstructor.newInstance(
baseBlock.toImmutableState(),
new CompoundTag(
new HashMap<>(
Map.of(
"cc_type", new StringTag(state.type().type().asString()),
"cc_data", new ByteArrayTag(state.getNBTDataAsBytes())
)
)
)
));
} catch (WorldEditException | InvocationTargetException |
InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}, 1, player.getLocation());
}
public void updateCopyClipBoard(Player player) {
com.sk89q.worldedit.entity.Player actor = BukkitAdapter.adapt(player);
SessionManager manager = WorldEdit.getInstance().getSessionManager();
LocalSession localSession = manager.get(actor);
ClipboardHolder holder;
try {
holder = localSession.getClipboard();
} catch (EmptyClipboardException e) {
return;
}
Clipboard clipboard = holder.getClipboard();
Region region = clipboard.getRegion();
World world = region.getWorld();
if (world == null) return;
BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(world.getName())
.ifPresent(customWorld -> {
BukkitCustomCropsPlugin.getInstance().getScheduler().async().execute(() -> {
BlockVector3 maxPoint = region.getMaximumPoint();
BlockVector3 minPoint = region.getMinimumPoint();
int minX = Math.min(minPoint.getBlockX(), maxPoint.getBlockX());
int minY = Math.min(minPoint.getBlockY(), maxPoint.getBlockY());
int minZ = Math.min(minPoint.getBlockZ(), maxPoint.getBlockZ());
int maxX = Math.max(maxPoint.getBlockX(), minPoint.getBlockX());
int maxY = Math.max(maxPoint.getBlockY(), minPoint.getBlockY());
int maxZ = Math.max(maxPoint.getBlockZ(), minPoint.getBlockZ());
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
Pos3 pos3 = new Pos3(x, y, z);
BlockVector3 vector3 = BlockVector3.at(x, y, z);
Optional<CustomCropsBlockState> optionalState = customWorld.getBlockState(pos3);
if (optionalState.isPresent()) {
BaseBlock baseBlock = clipboard.getFullBlock(vector3);
CustomCropsBlockState state = optionalState.get();
CompoundTag tag = baseBlock.getNbtData();
if (tag != null) {
Map<String, Tag> map = tag.getValue();
map.put("cc_type", new StringTag(state.type().type().asString()));
map.put("cc_data", new ByteArrayTag(state.getNBTDataAsBytes()));
} else {
try {
clipboard.setBlock(vector3, (BaseBlock) baseBlockConstructor.newInstance(
baseBlock.toImmutableState(),
new CompoundTag(
new HashMap<>(
Map.of(
"cc_type", new StringTag(state.type().type().asString()),
"cc_data", new ByteArrayTag(state.getNBTDataAsBytes())
)
)
)
));
} catch (WorldEditException | InvocationTargetException |
InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
});
});
}
}