3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-12-25 18:40:05 +01:00
* Add the bundled data files

* feat: add 1.21.4 adapter

* Update the XTypes/XCategories files

Co-authored-by: Maddy Miller <mnmiller1@me.com>

* chore/fix: update isFullCube to actually check for shape not occlusion

* chore: remove forget packet

* chore: add block connection fixer to adapter

---------

Co-authored-by: Maddy Miller <mnmiller1@me.com>
Dieser Commit ist enthalten in:
Pierre Maurice Schwang 2024-12-17 21:32:17 +01:00 committet von GitHub
Ursprung 7427665c29
Commit 74bb6e30d2
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
35 geänderte Dateien mit 8905 neuen und 8 gelöschten Zeilen

Datei anzeigen

@ -83,7 +83,7 @@ allprojects {
} }
applyCommonConfiguration() applyCommonConfiguration()
val supportedVersions = listOf("1.20.4", "1.20.5", "1.20.6", "1.21", "1.21.1", "1.21.3") val supportedVersions = listOf("1.20.4", "1.20.5", "1.20.6", "1.21", "1.21.1", "1.21.3", "1.21.4")
tasks { tasks {
supportedVersions.forEach { supportedVersions.forEach {

Datei anzeigen

@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit"
include("worldedit-libs") include("worldedit-libs")
listOf("1_20_2", "1_20_4", "1_20_5", "1_21", "1_21_3").forEach { listOf("1_20_2", "1_20_4", "1_20_5", "1_21", "1_21_3", "1_21_4").forEach {
include("worldedit-bukkit:adapters:adapter-$it") include("worldedit-bukkit:adapters:adapter-$it")
} }

Datei anzeigen

@ -0,0 +1,17 @@
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
plugins {
java
}
applyPaperweightAdapterConfiguration()
repositories {
gradlePluginPortal()
}
dependencies {
// url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.21.4-R0.1-SNAPSHOT/
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.21.4-R0.1-20241207.191616-8")
compileOnly(libs.paperlib)
}

Datei anzeigen

@ -0,0 +1,87 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* 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
* (at your option) 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 com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_4;
import com.mojang.authlib.GameProfile;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ParticleStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.stats.Stat;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.world.phys.Vec3;
import java.util.OptionalInt;
import java.util.UUID;
class PaperweightFakePlayer extends ServerPlayer {
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]");
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
private static final ClientInformation FAKE_CLIENT_INFO = new ClientInformation(
"en_US", 16, ChatVisiblity.FULL, true, 0, HumanoidArm.LEFT, false, false, ParticleStatus.ALL
);
PaperweightFakePlayer(ServerLevel world) {
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE, FAKE_CLIENT_INFO);
}
@Override
public Vec3 position() {
return ORIGIN;
}
@Override
public void tick() {
}
@Override
public void die(DamageSource damagesource) {
}
@Override
public OptionalInt openMenu(MenuProvider factory) {
return OptionalInt.empty();
}
@Override
public void updateOptions(ClientInformation clientOptions) {
}
@Override
public void displayClientMessage(Component message, boolean actionBar) {
}
@Override
public void awardStat(Stat<?> stat, int amount) {
}
@Override
public void awardStat(Stat<?> stat) {
}
@Override
public void openTextEdit(SignBlockEntity sign, boolean front) {
}
}

Datei anzeigen

@ -0,0 +1,240 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* 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
* (at your option) 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 com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_4;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4.PaperweightFaweAdapter;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.block.BlockTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
public class PaperweightServerLevelDelegateProxy implements InvocationHandler {
private static final Logger LOGGER = LogManagerCompat.getLogger();
// FAWE start - extent not EditSession
private final Extent editSession;
//FAWE end
private final ServerLevel serverLevel;
//FAWE start - use FAWE adapter
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
//FAWE end
//FAWE start - force error if method not caught by this instance
private final boolean errorOnPassthrough;
//FAWE end
private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
this.editSession = editSession;
this.serverLevel = serverLevel;
//FAWE start
this.errorOnPassthrough = false;
//FAWE end
}
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter)
);
}
//FAWE start - force error if method not caught by this instance
private PaperweightServerLevelDelegateProxy(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
this.editSession = extent;
this.serverLevel = serverLevel;
this.errorOnPassthrough = errorOnPassthrough;
}
public static WorldGenLevel newInstance(Extent extent, ServerLevel serverLevel, boolean errorOnPassthrough) {
return (WorldGenLevel) Proxy.newProxyInstance(
serverLevel.getClass().getClassLoader(),
serverLevel.getClass().getInterfaces(),
new PaperweightServerLevelDelegateProxy(extent, serverLevel, errorOnPassthrough)
);
}
//FAWE end
@Nullable
private BlockEntity getBlockEntity(BlockPos blockPos) {
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
if (tileEntity == null) {
return null;
}
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
newEntity.loadWithComponents(
(CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
).getNbtReference().getValue()),
this.serverLevel.registryAccess()
);
return newEntity;
}
private BlockState getBlockState(BlockPos blockPos) {
return adapter.adapt(this.editSession.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
}
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), adapter.adapt(blockState));
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private boolean removeBlock(BlockPos blockPos, boolean bl) {
try {
return editSession.setBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ(), BlockTypes.AIR.getDefaultState());
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
}
}
private FluidState getFluidState(BlockPos pos) {
return getBlockState(pos).getFluidState();
}
private boolean isWaterAt(BlockPos pos) {
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//FAWE start - cannot use switch where method names are equal
String methodName = method.getName();
if (Refraction.pickName("getBlockState", "a_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockState
return getBlockState(blockPos);
}
}
if (Refraction.pickName("getBlockEntity", "c_").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
// getBlockEntity
return getBlockEntity(blockPos);
}
}
if ("a".equals(methodName) || "setBlock".equals(methodName) || "removeBlock".equals(methodName) || "destroyBlock".equals(
methodName)) {
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
// setBlock
return setBlock(blockPos, blockState);
} else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
// removeBlock (and also matches destroyBlock)
return removeBlock(blockPos, bl);
}
}
//FAWE start
if (Refraction.pickName("getFluidState", "b_").equals(methodName)) { //net.minecraft.world.level.BlockGetter
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return getFluidState(blockPos);
}
}
if (Refraction.pickName("isWaterAt", "z").equals(methodName)) { //net.minecraft.world.level.LevelReader
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
return isWaterAt(blockPos);
}
}
if (Refraction.pickName("getEntities", "a_").equals(methodName)) { //net.minecraft.world.level.EntityGetter
if (args.length == 2 && args[0] instanceof Entity && args[1] instanceof AABB) {
return new ArrayList<>();
}
}
// Specific passthroughs that we want to allow
// net.minecraft.world.level.BlockAndTintGetter
if (Refraction.pickName("getRawBrightness", "b").equals(methodName)) {
return method.invoke(this.serverLevel, args);
}
// net.minecraft.world.level.LevelHeightAccessor
if (Refraction.pickName("getMaxBuildHeight", "al").equals(methodName)) {
if (args.length == 0) {
return method.invoke(this.serverLevel, args);
}
}
// net.minecraft.world.level.SignalGetter
if (Refraction.pickName("hasNeighborSignal", "C").equals(methodName)) {
if (args.length == 1 && args[0] instanceof BlockPos) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getSignal", "c").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getControlInputSignal", "a").equals(methodName)) {
if (args.length == 3 && args[0] instanceof BlockPos && args[1] instanceof Direction && args[2] instanceof Boolean) {
return method.invoke(this.serverLevel, args);
}
}
if (Refraction.pickName("getDirectSignal", "a").equals(methodName)) {
if (args.length == 2 && args[0] instanceof BlockPos && args[1] instanceof Direction) {
return method.invoke(this.serverLevel, args);
}
}
//FAWE start - force error if method not caught by this instance
if (errorOnPassthrough) {
LOGGER.error(
"""
Attempted passthough of method {}.
Method argument types: {}
Method argument values: {}
""",
method.getName(),
Arrays.stream(args).map(a -> a.getClass().getName()).toList(),
Arrays.stream(args).map(Object::toString).toList()
);
throw new FaweException("Method required passthrough.");
}
//FAWE end
return method.invoke(this.serverLevel, args);
}
}

Datei anzeigen

@ -0,0 +1,192 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* 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
* (at your option) 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 com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_4;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.block.BlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.enginehub.linbus.tree.LinCompoundTag;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.Objects;
public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
private static final int UPDATE = 1;
private static final int NOTIFY = 2;
private final PaperweightAdapter adapter;
private final WeakReference<ServerLevel> world;
private SideEffectSet sideEffectSet;
public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> world) {
this.adapter = adapter;
this.world = world;
}
private ServerLevel getWorld() {
return Objects.requireNonNull(world.get(), "The reference to the world was lost");
}
@Override
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public LevelChunk getChunk(int x, int z) {
return getWorld().getChunk(x, z);
}
@Override
public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) {
int stateId = BlockStateIdAccess.getBlockStateId(state);
return BlockStateIdAccess.isValidInternalId(stateId)
? Block.stateById(stateId)
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
}
@Override
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) {
return chunk.getBlockState(position);
}
@Nullable
@Override
public net.minecraft.world.level.block.state.BlockState setBlockState(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState state) {
return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE));
}
@Override
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(net.minecraft.world.level.block.state.BlockState block, BlockPos position) {
return Block.updateFromNeighbourShapes(block, getWorld(), position);
}
@Override
public BlockPos getPosition(int x, int y, int z) {
return new BlockPos(x, y, z);
}
@Override
public void updateLightingForBlock(BlockPos position) {
getWorld().getChunkSource().getLightEngine().checkBlock(position);
}
@Override
public boolean updateTileEntity(BlockPos position, LinCompoundTag tag) {
// We will assume that the tile entity was created for us
BlockEntity tileEntity = getWorld().getBlockEntity(position);
if (tileEntity == null) {
return false;
}
Tag nativeTag = adapter.fromNativeLin(tag);
PaperweightAdapter.readTagIntoTileEntity((net.minecraft.nbt.CompoundTag) nativeTag, tileEntity);
return true;
}
@Override
public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY);
}
}
@Override
public boolean isChunkTicking(LevelChunk chunk) {
return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING);
}
@Override
public void markBlockChanged(LevelChunk chunk, BlockPos position) {
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
getWorld().getChunkSource().blockChanged(position);
}
}
@Override
public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
ServerLevel world = getWorld();
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
world.updateNeighborsAt(pos, oldState.getBlock());
} else {
// When we don't want events, manually run the physics without them.
Block block = oldState.getBlock();
fireNeighborChanged(pos, world, block, pos.west());
fireNeighborChanged(pos, world, block, pos.east());
fireNeighborChanged(pos, world, block, pos.below());
fireNeighborChanged(pos, world, block, pos.above());
fireNeighborChanged(pos, world, block, pos.north());
fireNeighborChanged(pos, world, block, pos.south());
}
if (newState.hasAnalogOutputSignal()) {
world.updateNeighbourForOutputSignal(pos, newState.getBlock());
}
}
@Override
public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
ServerLevel world = getWorld();
newState.onPlace(world, pos, oldState, false);
}
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
world.getBlockState(neighborPos).handleNeighborChanged(world, neighborPos, block, ExperimentalRedstoneUtils.initialOrientation(world, null, null), false);
}
@Override
public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) {
ServerLevel world = getWorld();
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
CraftWorld craftWorld = world.getWorld();
BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState));
world.getCraftServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
}
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
}
@Override
public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
getWorld().onBlockStateChange(pos, oldState, newState);
}
@Override
public void flush() {
}
}

Datei anzeigen

@ -0,0 +1,84 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* 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
* (at your option) 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 com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_4;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
/**
* Dedicated class to map all names that we use.
*
* <p>
* Overloads are split into multiple fields, as they <em>CAN</em> have different obfuscated names.
* </p>
*/
public final class StaticRefraction {
public static final String GET_CHUNK_FUTURE_MAIN_THREAD = Refraction.pickName(
"getChunkFutureMainThread", "c"
);
public static final String MAIN_THREAD_PROCESSOR = Refraction.pickName(
"mainThreadProcessor", "g"
);
public static final String NEXT_TICK_TIME = Refraction.pickName("nextTickTime", "e");
public static final String GET_BLOCK_STATE = Refraction.pickName("getBlockState", "a_");
/**
* {@code addFreshEntityWithPassengers(Entity entity)}.
*/
public static final String ADD_FRESH_ENTITY_WITH_PASSENGERS_ENTITY = Refraction.pickName(
"addFreshEntityWithPassengers", "a_"
);
/**
* {@code addFreshEntityWithPassengers(Entity entity, CreatureSpawnEvent.SpawnReason reason)}.
*/
public static final String ADD_FRESH_ENTITY_WITH_PASSENGERS_ENTITY_SPAWN_REASON =
Refraction.pickName("addFreshEntityWithPassengers", "a_");
/**
* {@code addFreshEntity(Entity entity)}.
*/
public static final String ADD_FRESH_ENTITY = Refraction.pickName("addFreshEntity", "b");
/**
* {@code getBlockEntity(BlockPos blockPos)}.
*/
public static final String GET_BLOCK_ENTITY = Refraction.pickName("getBlockEntity", "c_");
/**
* {@code setBlock(BlockPos blockPos, BlockState blockState, int flags)}.
*/
public static final String SET_BLOCK = Refraction.pickName("setBlock", "a");
/**
* {@code setBlock(BlockPos blockPos, BlockState blockState, int flags, int maxUpdateDepth)}.
*/
public static final String SET_BLOCK_MAX_UPDATE = Refraction.pickName("setBlock", "a");
public static final String REMOVE_BLOCK = Refraction.pickName("removeBlock", "a");
/**
* {@code destroyBlock(BlockPos blockPos, boolean drop)}.
*/
public static final String DESTROY_BLOCK = Refraction.pickName("destroyBlock", "b");
/**
* {@code destroyBlock(BlockPos blockPos, boolean drop, Entity breakingEntity)}.
*/
public static final String DESTROY_BLOCK_BREAKING_ENTITY = Refraction.pickName(
"destroyBlock", "a"
);
/**
* {@code destroyBlock(BlockPos blockPos, boolean drop, Entity breakingEntity, int maxUpdateDepth)}.
*/
public static final String DESTROY_BLOCK_BREAKING_ENTITY_MAX_UPDATE = Refraction.pickName(
"destroyBlock", "a"
);
}

Datei anzeigen

@ -0,0 +1,175 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.PushReaction;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import javax.annotation.Nullable;
public class PaperweightBlockMaterial implements BlockMaterial {
private final Block block;
private final BlockState blockState;
private final CraftBlockData craftBlockData;
private final org.bukkit.Material craftMaterial;
private final int opacity;
private final FaweCompoundTag tile;
public PaperweightBlockMaterial(Block block) {
this(block, block.defaultBlockState());
}
public PaperweightBlockMaterial(Block block, BlockState blockState) {
this.block = block;
this.blockState = blockState;
this.craftBlockData = CraftBlockData.fromData(blockState);
this.craftMaterial = craftBlockData.getMaterial();
opacity = blockState.getLightBlock();
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
BlockPos.ZERO,
blockState
);
tile = tileEntity == null
? null
: PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity);
}
public Block getBlock() {
return block;
}
public BlockState getState() {
return blockState;
}
public CraftBlockData getCraftBlockData() {
return craftBlockData;
}
@Override
public boolean isAir() {
return blockState.isAir();
}
@Override
public boolean isFullCube() {
return Block.isShapeFullBlock(blockState.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO));
}
@Override
public boolean isOpaque() {
return blockState.canOcclude();
}
@Override
public boolean isPowerSource() {
return blockState.isSignalSource();
}
@Override
public boolean isLiquid() {
return !blockState.getFluidState().is(Fluids.EMPTY);
}
@Override
public boolean isSolid() {
return blockState.isSolidRender();
}
@Override
public float getHardness() {
return craftBlockData.getState().destroySpeed;
}
@Override
public float getResistance() {
return block.getExplosionResistance();
}
@Override
public float getSlipperiness() {
return block.getFriction();
}
@Override
public int getLightValue() {
return blockState.getLightEmission();
}
@Override
public int getLightOpacity() {
return opacity;
}
@Override
public boolean isFragileWhenPushed() {
return blockState.getPistonPushReaction() == PushReaction.DESTROY;
}
@Override
public boolean isUnpushable() {
return blockState.getPistonPushReaction() == PushReaction.BLOCK;
}
@Override
public boolean isTicksRandomly() {
return blockState.isRandomlyTicking();
}
@SuppressWarnings("deprecation")
@Override
public boolean isMovementBlocker() {
return blockState.blocksMotion();
}
@Override
public boolean isBurnable() {
return craftMaterial.isBurnable();
}
@Override
public boolean isToolRequired() {
// Removed in 1.16.1, this is not present in higher versions
return false;
}
@Override
public boolean isReplacedDuringPlacement() {
return blockState.canBeReplaced();
}
@Override
public boolean isTranslucent() {
return !blockState.canOcclude();
}
@Override
public boolean hasContainer() {
return block instanceof EntityBlock;
}
@Override
public boolean isTile() {
return block instanceof EntityBlock;
}
@Override
public @Nullable FaweCompoundTag defaultTile() {
return tile;
}
@Override
public int getMapColor() {
// rgb field
return block.defaultMapColor().col;
}
}

Datei anzeigen

@ -0,0 +1,638 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.mojang.serialization.Codec;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_4.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.state.BooleanProperty;
import com.sk89q.worldedit.registry.state.DirectionalProperty;
import com.sk89q.worldedit.registry.state.EnumProperty;
import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.concurrency.LazyReference;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.RegenOptions;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import io.papermc.lib.PaperLib;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.WritableRegistry;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.chunk.LevelChunk;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.entity.Player;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinStringTag;
import org.enginehub.linbus.tree.LinTag;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static net.minecraft.core.registries.Registries.BIOME;
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
private static final Codec<DataComponentPatch> COMPONENTS_CODEC = DataComponentPatch.CODEC.optionalFieldOf(
"components", DataComponentPatch.EMPTY
).codec();
static {
try {
CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
} catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
}
}
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
super(new PaperweightAdapter());
}
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
return blockEntity -> FaweCompoundTag.of(
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))
);
}
public static Property<?> adaptProperty(net.minecraft.world.level.block.state.properties.Property<?> property) {
return switch (property) {
case net.minecraft.world.level.block.state.properties.BooleanProperty booleanProperty ->
new BooleanProperty(booleanProperty.getName(), ImmutableList.copyOf(booleanProperty.getPossibleValues()));
case net.minecraft.world.level.block.state.properties.IntegerProperty integerProperty ->
new IntegerProperty(integerProperty.getName(), ImmutableList.copyOf(integerProperty.getPossibleValues()));
case net.minecraft.world.level.block.state.properties.EnumProperty<?> enumProperty -> {
if (enumProperty.getValueClass() == net.minecraft.core.Direction.class) {
yield new DirectionalProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream()
.map(StringRepresentable::getSerializedName)
.map(s -> s.toUpperCase(Locale.ROOT))
.map(Direction::valueOf)
.toList()
);
}
yield new EnumProperty(enumProperty.getName(), enumProperty.getPossibleValues().stream()
.map(StringRepresentable::getSerializedName).collect(Collectors.toCollection(ArrayList::new)));
}
default -> throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + property.getClass().getSimpleName());
};
}
private static String getEntityId(Entity entity) {
return net.minecraft.world.entity.EntityType.getKey(entity.getType()).toString();
}
private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) {
entity.save(compoundTag);
}
@Override
public BukkitImplAdapter<net.minecraft.nbt.Tag> getParent() {
return parent;
}
private synchronized boolean init() {
if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) {
return false;
}
ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size
ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size
for (int i = 0; i < ibdToStateOrdinal.length; i++) {
BlockState blockState = BlockTypesCache.states[i];
PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial();
int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState());
char ordinal = blockState.getOrdinalChar();
ibdToStateOrdinal[id] = ordinal;
ordinalToIbdID[ordinal] = id;
}
Map<String, List<Property<?>>> properties = new HashMap<>();
try {
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
Object obj = field.get(null);
if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property<?> state)) {
continue;
}
Property<?> property = adaptProperty(state);
properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> {
if (v == null) {
v = new ArrayList<>(Collections.singletonList(property));
} else {
v.add(property);
}
return v;
});
}
} catch (IllegalAccessException e) {
LOGGER.error("failed to initialize block states", e);
} finally {
allBlockProperties = ImmutableMap.copyOf(properties);
}
initialised = true;
return true;
}
@Override
public BlockMaterial getMaterial(BlockType blockType) {
Block block = getBlock(blockType);
return new PaperweightBlockMaterial(block);
}
@Override
public synchronized BlockMaterial getMaterial(BlockState state) {
net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
return new PaperweightBlockMaterial(blockState.getBlock(), blockState);
}
public Block getBlock(BlockType blockType) {
return DedicatedServer.getServer().registryAccess().lookupOrThrow(Registries.BLOCK)
.getValue(ResourceLocation.fromNamespaceAndPath(blockType.getNamespace(), blockType.getResource()));
}
@Deprecated
@Override
public BlockState getBlock(Location location) {
Preconditions.checkNotNull(location);
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
BlockState state = adapt(blockData);
if (state == null) {
org.bukkit.block.Block bukkitBlock = location.getBlock();
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
}
return state;
}
@Override
public BaseBlock getFullBlock(final Location location) {
Preconditions.checkNotNull(location);
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
BlockState state = adapt(blockData);
if (state == null) {
org.bukkit.block.Block bukkitBlock = location.getBlock();
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
}
if (state.getBlockType().getMaterial().hasContainer()) {
// Read the NBT data
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
if (blockEntity != null) {
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess());
return state.toBaseBlock((LinCompoundTag) toNativeLin(tag));
}
}
return state.toBaseBlock();
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.HISTORY,
SideEffect.HEIGHTMAPS,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS
);
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SUPPORTED_SIDE_EFFECTS;
}
@Override
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
}
@Override
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
Preconditions.checkNotNull(entity);
CraftEntity craftEntity = ((CraftEntity) entity);
Entity mcEntity = craftEntity.getHandle();
String id = getEntityId(mcEntity);
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
Supplier<LinCompoundTag> saveTag = () -> {
final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag();
readEntityIntoTag(mcEntity, minecraftTag);
//add Id for AbstractChangeSet to work
final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag);
final Map<String, LinTag<?>> tags = NbtUtils.getLinCompoundTagValues(tag);
tags.put("Id", LinStringTag.of(id));
return LinCompoundTag.of(tags);
};
return new LazyBaseEntity(type, saveTag);
}
@Override
public Component getRichBlockName(BlockType blockType) {
return parent.getRichBlockName(blockType);
}
@Override
public Component getRichItemName(ItemType itemType) {
return parent.getRichItemName(itemType);
}
@Override
public Component getRichItemName(BaseItemStack itemStack) {
return parent.getRichItemName(itemStack);
}
@Override
public OptionalInt getInternalBlockStateId(BlockState state) {
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState();
return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState));
}
@Override
public BlockState adapt(BlockData blockData) {
CraftBlockData cbd = ((CraftBlockData) blockData);
net.minecraft.world.level.block.state.BlockState ibd = cbd.getState();
return adapt(ibd);
}
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
return BlockTypesCache.states[adaptToChar(blockState)];
}
public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) {
int id = Block.BLOCK_STATE_REGISTRY.getId(blockState);
if (initialised) {
return ibdToStateOrdinal[id];
}
synchronized (this) {
if (initialised) {
return ibdToStateOrdinal[id];
}
try {
init();
return ibdToStateOrdinal[id];
} catch (ArrayIndexOutOfBoundsException e1) {
LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!",
blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1
);
return BlockTypesCache.ReservedIDs.AIR;
}
}
}
public char ibdIDToOrdinal(int id) {
if (initialised) {
return ibdToStateOrdinal[id];
}
synchronized (this) {
if (initialised) {
return ibdToStateOrdinal[id];
}
init();
return ibdToStateOrdinal[id];
}
}
@Override
public char[] getIbdToStateOrdinal() {
if (initialised) {
return ibdToStateOrdinal;
}
synchronized (this) {
if (initialised) {
return ibdToStateOrdinal;
}
init();
return ibdToStateOrdinal;
}
}
public int ordinalToIbdID(char ordinal) {
if (initialised) {
return ordinalToIbdID[ordinal];
}
synchronized (this) {
if (initialised) {
return ordinalToIbdID[ordinal];
}
init();
return ordinalToIbdID[ordinal];
}
}
@Override
public int[] getOrdinalToIbdID() {
if (initialised) {
return ordinalToIbdID;
}
synchronized (this) {
if (initialised) {
return ordinalToIbdID;
}
init();
return ordinalToIbdID;
}
}
@Override
public <B extends BlockStateHolder<B>> BlockData adapt(B state) {
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
return material.getCraftBlockData();
}
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
return Block.stateById(getOrdinalToIbdID()[blockState.getOrdinal()]);
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
if (map != null && wasAccessibleSinceLastSave(map)) {
boolean flag = false;
// PlayerChunk.d players = map.players;
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
*/ Stream.empty();
ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
.forEach(entityPlayer -> {
synchronized (chunkPacket) {
ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket();
if (nmsPacket == null) {
nmsPacket = mapUtil.create(this, chunkPacket);
chunkPacket.setNativePacket(nmsPacket);
}
try {
FaweCache.INSTANCE.CHUNK_FLAG.get().set(true);
entityPlayer.connection.send(nmsPacket);
} finally {
FaweCache.INSTANCE.CHUNK_FLAG.get().set(false);
}
}
});
}
}
@Override
public Map<String, ? extends Property<?>> getProperties(BlockType blockType) {
return getParent().getProperties(blockType);
}
@Override
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) {
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
return blockState1.hasPostProcess(
getServerLevel(world),
new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())
);
}
@Override
public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess();
ItemStack stack = new ItemStack(
registryAccess.lookupOrThrow(Registries.ITEM).getValueOrThrow(ResourceKey.create(
Registries.ITEM, ResourceLocation.parse(baseItemStack.getType().id())
)),
baseItemStack.getAmount()
);
final CompoundTag nbt = (net.minecraft.nbt.CompoundTag) fromNativeLin(baseItemStack.getNbt());
if (nbt != null) {
final DataComponentPatch patch = COMPONENTS_CODEC
.parse(registryAccess.createSerializationContext(NbtOps.INSTANCE), nbt)
.getOrThrow();
stack.applyComponents(patch);
}
return CraftItemStack.asCraftMirror(stack);
}
@Override
protected void preCaptureStates(final ServerLevel serverLevel) {
serverLevel.captureTreeGeneration = true;
serverLevel.captureBlockStates = true;
}
@Override
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
return new ArrayList<>(serverLevel.capturedBlockStates.values());
}
@Override
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
serverLevel.captureBlockStates = false;
serverLevel.captureTreeGeneration = false;
serverLevel.capturedBlockStates.clear();
}
@Override
protected ServerLevel getServerLevel(final World world) {
return ((CraftWorld) world).getHandle();
}
@Override
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess();
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
final net.minecraft.nbt.Tag tag = COMPONENTS_CODEC.encodeStart(
registryAccess.createSerializationContext(NbtOps.INSTANCE),
nmsStack.getComponentsPatch()
).getOrThrow();
return new BaseItemStack(
BukkitAdapter.asItemType(itemStack.getType()),
LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag)),
itemStack.getAmount()
);
}
@Override
public Tag toNative(net.minecraft.nbt.Tag foreign) {
return parent.toNative(foreign);
}
@Override
public net.minecraft.nbt.Tag fromNative(Tag foreign) {
return parent.fromNative(foreign);
}
@Override
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
}
@Override
public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) {
return new PaperweightGetBlocks(world, chunkX, chunkZ);
}
@Override
public int getInternalBiomeId(BiomeType biomeType) {
final Registry<Biome> registry = MinecraftServer
.getServer()
.registryAccess()
.lookupOrThrow(BIOME);
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.id());
Biome biome = registry.getValue(resourceLocation);
return registry.getId(biome);
}
@Override
public Iterable<NamespacedKey> getRegisteredBiomes() {
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) ((CraftServer) Bukkit.getServer())
.getServer()
.registryAccess()
.lookupOrThrow(BIOME);
List<ResourceLocation> keys = biomeRegistry.stream()
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
List<NamespacedKey> namespacedKeys = new ArrayList<>();
for (ResourceLocation key : keys) {
try {
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
} catch (IllegalArgumentException e) {
LOGGER.error("Error converting biome key {}", key.toString(), e);
}
}
return namespacedKeys;
}
@Override
public RelighterFactory getRelighterFactory() {
if (PaperLib.isPaper()) {
return new PaperweightStarlightRelighterFactory();
} else {
return new NMSRelighterFactory();
}
}
@Override
public Map<String, List<Property<?>>> getAllProperties() {
if (initialised) {
return allBlockProperties;
}
synchronized (this) {
if (initialised) {
return allBlockProperties;
}
init();
return allBlockProperties;
}
}
@Override
public IBatchProcessor getTickingPostProcessor() {
return new PaperweightPostProcessor();
}
@Override
public PlacementStateProcessor getPlatformPlacementProcessor(Extent extent, BlockTypeMask mask, Region region) {
return new PaperweightPlacementStateProcessor(extent, mask, region);
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (PaperLib.isPaper()) { // Papers new chunk system has no related replacement - therefor we assume true.
return true;
}
try {
return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
} catch (IllegalAccessException | InvocationTargetException ignored) {
return false;
}
}
}

Datei anzeigen

@ -0,0 +1,143 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightFaweMutableBlockPlaceContext extends BlockPlaceContext {
private static final BlockHitResult DEFAULT_BLOCK_HIT = new BlockHitResult(
new Vec3(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
Direction.NORTH,
new BlockPos(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
false
);
private final ServerLevel level;
private BlockHitResult hitResult = null;
private Direction direction = null;
private BlockPos relativePos;
@SuppressWarnings("DataFlowIssue")
public PaperweightFaweMutableBlockPlaceContext(ServerLevel level) {
super(
level,
null,
null,
null,
DEFAULT_BLOCK_HIT
);
this.level = level;
this.replaceClicked = false;
}
public PaperweightFaweMutableBlockPlaceContext withSetting(BlockHitResult hitResult, Direction direction) {
this.hitResult = hitResult;
this.direction = direction;
this.relativePos = hitResult.getBlockPos().relative(hitResult.getDirection());
return this;
}
@Override
@Nonnull
public BlockPos getClickedPos() {
return this.relativePos;
}
@Override
@Nonnull
public Direction getClickedFace() {
return this.hitResult.getDirection();
}
@Override
@Nonnull
public Vec3 getClickLocation() {
return this.hitResult.getLocation();
}
@Override
public boolean isInside() {
return this.hitResult.isInside();
}
@Override
@SuppressWarnings("NullableProblems")
public ItemStack getItemInHand() {
return ItemStack.EMPTY;
}
@Nullable
@Override
public Player getPlayer() {
return null;
}
@Override
@SuppressWarnings("NullableProblems")
public InteractionHand getHand() {
return null;
}
@Override
@Nonnull
public Level getLevel() {
return this.level;
}
@Override
@Nonnull
public Direction getHorizontalDirection() {
return this.direction.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.direction;
}
@Override
public boolean isSecondaryUseActive() {
return false;
}
@Override
public float getRotation() {
return (float) (this.direction.get2DDataValue() * 90);
}
@Override
public boolean canPlace() {
return this.getLevel().getBlockState(this.getClickedPos()).canBeReplaced(this);
}
@Override
public boolean replacingClickedOnBlock() {
return false;
}
@Override
@Nonnull
public Direction getNearestLookingDirection() {
return direction;
}
@Override
@Nonnull
public Direction getNearestLookingVerticalDirection() {
return direction.getAxis() == Direction.Axis.Y ? Direction.UP : Direction.DOWN;
}
@Override
@Nonnull
public Direction[] getNearestLookingDirections() {
return new Direction[]{direction};
}
}

Datei anzeigen

@ -0,0 +1,294 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.TaskManager;
import com.fastasyncworldedit.core.util.task.RunnableVal;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.block.BlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.enginehub.linbus.tree.LinCompoundTag;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<LevelChunk,
net.minecraft.world.level.block.state.BlockState, BlockPos> {
private static final int UPDATE = 1;
private static final int NOTIFY = 2;
private static final Direction[] NEIGHBOUR_ORDER = {
Direction.EAST,
Direction.WEST,
Direction.DOWN,
Direction.UP,
Direction.NORTH,
Direction.SOUTH
};
private final PaperweightFaweAdapter paperweightFaweAdapter;
private final WeakReference<Level> level;
private final AtomicInteger lastTick;
private final Set<CachedChange> cachedChanges = new HashSet<>();
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
private SideEffectSet sideEffectSet;
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
this.paperweightFaweAdapter = paperweightFaweAdapter;
this.level = level;
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
}
private Level getLevel() {
return Objects.requireNonNull(level.get(), "The reference to the world was lost");
}
@Override
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public LevelChunk getChunk(int x, int z) {
return getLevel().getChunk(x, z);
}
@Override
public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) {
int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar());
return BlockStateIdAccess.isValidInternalId(stateId)
? Block.stateById(stateId)
: ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState();
}
@Override
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) {
return levelChunk.getBlockState(blockPos);
}
@Nullable
@Override
public synchronized net.minecraft.world.level.block.state.BlockState setBlockState(
LevelChunk levelChunk, BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState blockState
) {
int currentTick = MinecraftServer.currentTick;
if (Fawe.isMainThread()) {
return levelChunk.setBlockState(blockPos, blockState,
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
);
}
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
cachedChunksToSend.add(new IntPair(levelChunk.locX, levelChunk.locZ));
boolean nextTick = lastTick.get() > currentTick;
if (nextTick || cachedChanges.size() >= 1024) {
if (nextTick) {
lastTick.set(currentTick);
}
flushAsync(nextTick);
}
return blockState;
}
@Override
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
net.minecraft.world.level.block.state.BlockState blockState,
BlockPos blockPos
) {
return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos);
}
@Override
public BlockPos getPosition(int x, int y, int z) {
return new BlockPos(x, y, z);
}
@Override
public void updateLightingForBlock(BlockPos blockPos) {
getLevel().getChunkSource().getLightEngine().checkBlock(blockPos);
}
@Override
public boolean updateTileEntity(BlockPos blockPos, LinCompoundTag tag) {
// We will assume that the tile entity was created for us,
// though we do not do this on the other versions
BlockEntity blockEntity = getLevel().getBlockEntity(blockPos);
if (blockEntity == null) {
return false;
}
net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeLin(tag);
blockEntity.loadWithComponents((CompoundTag) nativeTag, DedicatedServer.getServer().registryAccess());
return true;
}
@Override
public void notifyBlockUpdate(
LevelChunk levelChunk, BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState oldState,
net.minecraft.world.level.block.state.BlockState newState
) {
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY);
}
}
@Override
public boolean isChunkTicking(LevelChunk levelChunk) {
return levelChunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING);
}
@Override
public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) {
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos);
}
}
@Override
public void notifyNeighbors(
BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState oldState,
net.minecraft.world.level.block.state.BlockState newState
) {
Level level = getLevel();
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
level.blockUpdated(blockPos, oldState.getBlock());
} else {
// When we don't want events, manually run the physics without them.
// Un-nest neighbour updating
for (Direction direction : NEIGHBOUR_ORDER) {
BlockPos shifted = blockPos.relative(direction);
level.getBlockState(shifted).handleNeighborChanged(level, shifted, oldState.getBlock(), ExperimentalRedstoneUtils.initialOrientation(level, null, null), false);
}
}
if (newState.hasAnalogOutputSignal()) {
level.updateNeighbourForOutputSignal(blockPos, newState.getBlock());
}
}
@Override
public void updateNeighbors(
BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState oldState,
net.minecraft.world.level.block.state.BlockState newState,
int recursionLimit
) {
Level level = getLevel();
// a == updateNeighbors
// b == updateDiagonalNeighbors
oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
CraftWorld craftWorld = level.getWorld();
if (craftWorld != null) {
BlockPhysicsEvent event = new BlockPhysicsEvent(
craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
CraftBlockData.fromData(newState)
);
level.getCraftServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
}
}
newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit);
newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
}
@Override
public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
Level world = getLevel();
newState.onPlace(world, pos, oldState, false);
}
@Override
public void onBlockStateChange(
BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState oldState,
net.minecraft.world.level.block.state.BlockState newState
) {
getLevel().onBlockStateChange(blockPos, oldState, newState);
}
private synchronized void flushAsync(final boolean sendChunks) {
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
cachedChanges.clear();
final Set<IntPair> toSend;
if (sendChunks) {
toSend = Set.copyOf(cachedChunksToSend);
cachedChunksToSend.clear();
} else {
toSend = Collections.emptySet();
}
RunnableVal<Object> runnableVal = new RunnableVal<>() {
@Override
public void run(Object value) {
changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
));
if (!sendChunks) {
return;
}
for (IntPair chunk : toSend) {
PaperweightPlatformAdapter.sendChunk(chunk, getLevel().getWorld().getHandle(), chunk.x(), chunk.z());
}
}
};
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
}
@Override
public synchronized void flush() {
RunnableVal<Object> runnableVal = new RunnableVal<>() {
@Override
public void run(Object value) {
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
));
for (IntPair chunk : cachedChunksToSend) {
PaperweightPlatformAdapter.sendChunk(chunk, getLevel().getWorld().getHandle(), chunk.x(), chunk.z());
}
}
};
if (Fawe.isMainThread()) {
runnableVal.run();
} else {
TaskManager.taskManager().sync(runnableVal);
}
cachedChanges.clear();
cachedChunksToSend.clear();
}
private record CachedChange(
LevelChunk levelChunk,
BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState blockState
) {
}
}

Datei anzeigen

@ -0,0 +1,276 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import net.minecraft.core.Holder;
import net.minecraft.nbt.Tag;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO;
import org.apache.logging.log4j.Logger;
import org.enginehub.linbus.tree.LinCompoundTag;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
public class PaperweightGetBlocks_Copy implements IChunkGet {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
private final Set<FaweCompoundTag> entities = new HashSet<>();
private final char[][] blocks;
private final int minHeight;
private final int maxHeight;
final ServerLevel serverLevel;
final LevelChunk levelChunk;
private Holder<Biome>[][] biomes = null;
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
this.levelChunk = levelChunk;
this.serverLevel = levelChunk.level;
this.minHeight = serverLevel.getMinY();
this.maxHeight = serverLevel.getMaxY() - 1; // Minecraft max limit is exclusive.
this.blocks = new char[getSectionCount()][];
}
protected void storeTile(BlockEntity blockEntity) {
@SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
tiles.put(
BlockVector3.at(
blockEntity.getBlockPos().getX(),
blockEntity.getBlockPos().getY(),
blockEntity.getBlockPos().getZ()
),
FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId(DedicatedServer
.getServer()
.registryAccess())))
);
}
protected void storeEntity(Entity entity) {
@SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
entity.save(compoundTag);
entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag)));
}
@Override
public Collection<FaweCompoundTag> entities() {
return this.entities;
}
@Override
public @Nullable FaweCompoundTag entity(final UUID uuid) {
for (FaweCompoundTag tag : entities) {
if (uuid.equals(NbtUtils.uuid(tag))) {
return tag;
}
}
return null;
}
@Override
public boolean isCreateCopy() {
return false;
}
@Override
public int setCreateCopy(boolean createCopy) {
return -1;
}
@Override
public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
}
@Override
public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
}
@Override
public int getMaxY() {
return maxHeight;
}
@Override
public int getMinY() {
return minHeight;
}
@Override
public int getMaxSectionPosition() {
return maxHeight >> 4;
}
@Override
public int getMinSectionPosition() {
return minHeight >> 4;
}
@Override
public BiomeType getBiomeType(int x, int y, int z) {
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()][(y & 12) << 2 | (z & 12) | (x & 12) >> 2];
return PaperweightPlatformAdapter.adapt(biome, serverLevel);
}
@Override
public void removeSectionLighting(int layer, boolean sky) {
}
@Override
public boolean trim(boolean aggressive, int layer) {
return false;
}
@Override
public IBlocks reset() {
return null;
}
@Override
public int getSectionCount() {
return serverLevel.getSectionsCount();
}
protected void storeSection(int layer, char[] data) {
blocks[layer] = data;
}
protected void storeBiomes(int layer, PalettedContainerRO<Holder<Biome>> biomeData) {
if (biomes == null) {
biomes = new Holder[getSectionCount()][];
}
if (biomes[layer] == null) {
biomes[layer] = new Holder[64];
}
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
if (PaperLib.isPaper()) {
for (int i = 0; i < 64; i++) {
biomes[layer][i] = palettedContainer.get(i); // Only public on paper
}
} else {
try {
for (int i = 0; i < 64; i++) {
biomes[layer][i] = (Holder<Biome>) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
} else {
LOGGER.error(
"Cannot correctly save biomes to history. Expected class type {} but got {}",
PalettedContainer.class.getSimpleName(),
biomeData.getClass().getSimpleName()
);
}
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
BlockState state = BlockTypesCache.states[get(x, y, z)];
return state.toBaseBlock((IBlocks) this, x, y, z);
}
@Override
public boolean hasSection(int layer) {
layer -= getMinSectionPosition();
return blocks[layer] != null;
}
@Override
public char[] load(int layer) {
layer -= getMinSectionPosition();
if (blocks[layer] == null) {
blocks[layer] = new char[4096];
Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR);
}
return blocks[layer];
}
@Override
public char[] loadIfPresent(int layer) {
layer -= getMinSectionPosition();
return blocks[layer];
}
@Override
public BlockState getBlock(int x, int y, int z) {
return BlockTypesCache.states[get(x, y, z)];
}
@Override
public Map<BlockVector3, FaweCompoundTag> tiles() {
return tiles;
}
@Override
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
return tiles.get(BlockVector3.at(x, y, z));
}
@Override
public int getSkyLight(int x, int y, int z) {
return 0;
}
@Override
public int getEmittedLight(int x, int y, int z) {
return 0;
}
@Override
public int[] getHeightMap(HeightMapType type) {
return new int[0];
}
@Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
return null;
}
public char get(int x, int y, int z) {
final int layer = (y >> 4) - getMinSectionPosition();
final int index = (y & 15) << 8 | z << 4 | x;
return blocks[layer][index];
}
@Override
public boolean trim(boolean aggressive) {
return false;
}
}

Datei anzeigen

@ -0,0 +1,136 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.enginehub.linbus.tree.LinCompoundTag;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PaperweightLevelProxy extends ServerLevel {
protected ServerLevel serverLevel;
private PaperweightPlacementStateProcessor processor;
private PaperweightFaweAdapter adapter;
@SuppressWarnings("DataFlowIssue")
private PaperweightLevelProxy() {
super(null, null, null, null, null, null, null, true, 0L, null, true, null, null, null, null);
throw new IllegalStateException("Cannot be instantiated");
}
public static PaperweightLevelProxy getInstance(ServerLevel serverLevel, PaperweightPlacementStateProcessor processor) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PaperweightLevelProxy newLevel;
try {
newLevel = (PaperweightLevelProxy) unsafe.allocateInstance(PaperweightLevelProxy.class);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
newLevel.processor = processor;
newLevel.adapter = ((PaperweightFaweAdapter) WorldEditPlugin.getInstance().getBukkitImplAdapter());
newLevel.serverLevel = serverLevel;
return newLevel;
}
@Nullable
@Override
public BlockEntity getBlockEntity(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return null;
}
LinCompoundTag tag = processor.getTileAt(blockPos.getX(), blockPos.getY(), blockPos.getZ());
if (tag == null) {
return null;
}
BlockState state = adapter.adapt(processor.getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
if (!(state.getBlock() instanceof EntityBlock entityBlock)) {
return null;
}
BlockEntity tileEntity = entityBlock.newBlockEntity(blockPos, state);
tileEntity.loadWithComponents((CompoundTag) adapter.fromNativeLin(tag), serverLevel.registryAccess());
return tileEntity;
}
@Override
@Nonnull
public BlockState getBlockState(@Nonnull BlockPos blockPos) {
if (blockPos.getX() == Integer.MAX_VALUE) {
return Blocks.AIR.defaultBlockState();
}
com.sk89q.worldedit.world.block.BlockState state = processor.getBlockStateAt(
blockPos.getX(),
blockPos.getY(),
blockPos.getZ()
);
return adapter.adapt(state);
}
@SuppressWarnings("unused")
@Override
@Nonnull
public FluidState getFluidState(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@SuppressWarnings("unused")
@Override
public boolean isWaterAt(@Nonnull BlockPos pos) {
if (pos.getX() == Integer.MAX_VALUE) {
return false;
}
return getBlockState(pos).getFluidState().is(FluidTags.WATER);
}
@Override
public int getHeight() {
return serverLevel.getHeight();
}
@Override
public int getMinY() {
return serverLevel.getMinY();
}
@Override
public int getMaxY() {
return serverLevel.getMaxY();
}
@Override
public boolean isInsideBuildHeight(int blockY) {
return serverLevel.isInsideBuildHeight(blockY);
}
@Override
public boolean isOutsideBuildHeight(BlockPos pos) {
return serverLevel.isOutsideBuildHeight(pos);
}
@Override
public boolean isOutsideBuildHeight(int blockY) {
return serverLevel.isOutsideBuildHeight(blockY);
}
@Override
public WorldBorder getWorldBorder() {
return serverLevel.getWorldBorder();
}
}

Datei anzeigen

@ -0,0 +1,34 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
//TODO un-very-break-this
public class PaperweightMapChunkUtil extends MapChunkUtil<ClientboundLevelChunkWithLightPacket> {
public PaperweightMapChunkUtil() throws NoSuchFieldException {
fieldX = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a"));
fieldZ = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("x", "b"));
fieldBitMask = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("z", "c"));
fieldHeightMap = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("heightmaps", "b"));
fieldChunkData = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("chunkData", "d"));
fieldBlockEntities = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("buffer", "c"));
fieldFull = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("blockEntitiesData", "d"));
fieldX.setAccessible(true);
fieldZ.setAccessible(true);
fieldBitMask.setAccessible(true);
fieldHeightMap.setAccessible(true);
fieldChunkData.setAccessible(true);
fieldBlockEntities.setAccessible(true);
fieldFull.setAccessible(true);
}
@Override
public ClientboundLevelChunkWithLightPacket createPacket() {
// TODO ??? return new ClientboundLevelChunkPacket();
throw new UnsupportedOperationException();
}
}

Datei anzeigen

@ -0,0 +1,111 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.core.extent.processor.PlacementStateProcessor;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.bukkit.craftbukkit.CraftWorld;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class PaperweightPlacementStateProcessor extends PlacementStateProcessor {
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance()
.getBukkitImplAdapter());
private final PaperweightFaweMutableBlockPlaceContext mutableBlockPlaceContext;
private final PaperweightLevelProxy proxyLevel;
public PaperweightPlacementStateProcessor(Extent extent, BlockTypeMask mask, Region region) {
super(extent, mask, region);
World world = ExtentTraverser.getWorldFromExtent(extent);
if (world == null) {
throw new UnsupportedOperationException(
"World is required for PlacementStateProcessor but none found in given extent.");
}
BukkitWorld bukkitWorld;
if (world instanceof WorldWrapper wrapper) {
bukkitWorld = (BukkitWorld) wrapper.getParent();
} else {
bukkitWorld = (BukkitWorld) world;
}
this.proxyLevel = PaperweightLevelProxy.getInstance(((CraftWorld) bukkitWorld.getWorld()).getHandle(), this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
private PaperweightPlacementStateProcessor(
Extent extent,
BlockTypeMask mask,
Map<SecondPass, Character> crossChunkSecondPasses,
ServerLevel serverLevel,
ThreadLocal<PlacementStateProcessor> threadProcessors,
Region region,
AtomicBoolean finished
) {
super(extent, mask, crossChunkSecondPasses, threadProcessors, region, finished);
this.proxyLevel = PaperweightLevelProxy.getInstance(serverLevel, this);
this.mutableBlockPlaceContext = new PaperweightFaweMutableBlockPlaceContext(proxyLevel);
}
@Override
protected char getStateAtFor(
int x,
int y,
int z,
BlockState state,
Vector3 clickPos,
Direction clickedFaceDirection,
BlockVector3 clickedBlock
) {
Block block = ((PaperweightBlockMaterial) state.getMaterial()).getBlock();
Vec3 pos = new Vec3(clickPos.x(), clickPos.y(), clickPos.z());
net.minecraft.core.Direction side = net.minecraft.core.Direction.valueOf(clickedFaceDirection.toString());
BlockPos blockPos = new BlockPos(clickedBlock.x(), clickedBlock.y(), clickedBlock.z());
net.minecraft.world.level.block.state.BlockState newState = block.getStateForPlacement(mutableBlockPlaceContext.withSetting(
new BlockHitResult(pos, side, blockPos, false),
side.getOpposite()
));
return newState == null ? BlockTypesCache.ReservedIDs.AIR : adapter.ibdIDToOrdinal(Block.BLOCK_STATE_REGISTRY.getId(
newState));
}
@Override
@Nullable
public Extent construct(Extent child) {
if (child == getExtent()) {
return this;
}
return new PaperweightPlacementStateProcessor(child, mask, region);
}
@Override
public PlacementStateProcessor fork() {
return new PaperweightPlacementStateProcessor(
extent,
mask,
postCompleteSecondPasses,
proxyLevel.serverLevel,
threadProcessors,
region,
finished
);
}
}

Datei anzeigen

@ -0,0 +1,720 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.TaskManager;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.IdMap;
import net.minecraft.core.Registry;
import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.BitStorage;
import net.minecraft.util.SimpleBitStorage;
import net.minecraft.util.ThreadingDetector;
import net.minecraft.util.Unit;
import net.minecraft.util.ZeroBitStorage;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.GlobalPalette;
import net.minecraft.world.level.chunk.HashMapPalette;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.LinearPalette;
import net.minecraft.world.level.chunk.Palette;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.SingleValuePalette;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftChunk;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import static java.lang.invoke.MethodType.methodType;
import static net.minecraft.core.registries.Registries.BIOME;
public final class PaperweightPlatformAdapter extends NMSAdapter {
public static final Field fieldData;
public static final Constructor<?> dataConstructor;
public static final Field fieldStorage;
public static final Field fieldPalette;
private static final Field fieldTickingFluidCount;
private static final Field fieldTickingBlockCount;
private static final Field fieldBiomes;
private static final MethodHandle methodGetVisibleChunk;
private static final Field fieldThreadingDetector;
private static final Field fieldLock;
private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity;
/*
* This is a workaround for the changes from https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1fddefce1cdce44010927b888432bf70c0e88cde#src/main/java/org/bukkit/craftbukkit/CraftChunk.java
* and is only needed to support 1.19.4 versions before *and* after this change.
*/
private static final MethodHandle CRAFT_CHUNK_GET_HANDLE;
private static final Field fieldRemove;
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
private static Field SERVER_LEVEL_ENTITY_MANAGER;
static final MethodHandle PALETTED_CONTAINER_GET;
static {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
fieldData.setAccessible(true);
Class<?> dataClazz = fieldData.getType();
dataConstructor = dataClazz.getDeclaredConstructors()[0];
dataConstructor.setAccessible(true);
fieldStorage = dataClazz.getDeclaredField(Refraction.pickName("storage", "b"));
fieldStorage.setAccessible(true);
fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c"));
fieldPalette.setAccessible(true);
fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g"));
fieldTickingFluidCount.setAccessible(true);
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f"));
fieldTickingBlockCount.setAccessible(true);
Field tmpFieldBiomes;
try {
// Seems it's sometimes biomes and sometimes "i". Idk this is just easier than having to try to deal with it
tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes"); // apparently unobf
} catch (NoSuchFieldException ignored) {
tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i"); // apparently obf
}
fieldBiomes = tmpFieldBiomes;
fieldBiomes.setAccessible(true);
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(
Refraction.pickName(
"getVisibleChunkIfPresent",
"b"
), long.class
);
getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
if (!PaperLib.isPaper()) {
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
fieldThreadingDetector.setAccessible(true);
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
fieldLock.setAccessible(true);
} else {
// in paper, the used methods are synchronized properly
fieldThreadingDetector = null;
fieldLock = null;
}
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
Refraction.pickName("removeGameEventListener", "a"),
BlockEntity.class,
ServerLevel.class
);
removeGameEventListener.setAccessible(true);
methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener);
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
Refraction.pickName(
"removeBlockEntityTicker",
"k"
), BlockPos.class
);
removeBlockEntityTicker.setAccessible(true);
methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker);
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
fieldRemove.setAccessible(true);
try {
Level.class.getDeclaredMethod("moonrise$getEntityLookup");
PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities");
PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true);
} catch (NoSuchMethodException ignored) {
// Non-Paper
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "N"));
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
}
Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod(
Refraction.pickName("get", "a"),
int.class
);
palettedContaienrGet.setAccessible(true);
PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet);
} catch (RuntimeException | Error e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
MethodHandle craftChunkGetHandle;
final MethodType type = methodType(LevelChunk.class);
try {
craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", type);
} catch (NoSuchMethodException | IllegalAccessException e) {
try {
final MethodType newType = methodType(ChunkAccess.class, ChunkStatus.class);
craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", newType);
craftChunkGetHandle = MethodHandles.insertArguments(craftChunkGetHandle, 1, ChunkStatus.FULL);
} catch (NoSuchMethodException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
CRAFT_CHUNK_GET_HANDLE = craftChunkGetHandle;
}
static boolean setSectionAtomic(
String worldName,
IntPair pair,
LevelChunkSection[] sections,
LevelChunkSection expected,
LevelChunkSection value,
int layer
) {
return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer);
}
// There is no point in having a functional semaphore for paper servers.
private static final ThreadLocal<DelegateSemaphore> SEMAPHORE_THREAD_LOCAL =
ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null));
static DelegateSemaphore applyLock(LevelChunkSection section) {
if (PaperLib.isPaper()) {
return SEMAPHORE_THREAD_LOCAL.get();
}
try {
synchronized (section) {
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
synchronized (currentThreadingDetector) {
Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore;
}
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
fieldLock.set(currentThreadingDetector, newLock);
return newLock;
}
}
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
if (!PaperLib.isPaper()) {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
if (nmsChunk != null) {
return nmsChunk;
}
if (Fawe.isMainThread()) {
return serverLevel.getChunk(chunkX, chunkZ);
}
} else {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk;
}
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk;
}
// Avoid "async" methods from the main thread.
if (Fawe.isMainThread()) {
return serverLevel.getChunk(chunkX, chunkZ);
}
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
try {
CraftChunk chunk;
try {
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
String world = serverLevel.getWorld().getName();
// We've already taken 10 seconds we can afford to wait a little here.
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
if (loaded) {
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
// Retry chunk load
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
} else {
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
}
}
addTicket(serverLevel, chunkX, chunkZ);
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
} catch (Throwable e) {
e.printStackTrace();
}
}
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
}
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
// Ensure chunk is definitely loaded before applying a ticket
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
.getChunkSource()
.addRegionTicket(ChunkHolderManager.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
}
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
try {
return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ));
} catch (Throwable thr) {
throw new RuntimeException(thr);
}
}
@SuppressWarnings("deprecation")
public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int chunkZ) {
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
if (chunkHolder == null) {
return;
}
LevelChunk levelChunk;
if (PaperLib.isPaper()) {
// getChunkAtIfLoadedImmediately is paper only
levelChunk = nmsWorld.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
} else {
levelChunk = chunkHolder.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null);
}
if (levelChunk == null) {
return;
}
StampLockHolder lockHolder = new StampLockHolder();
NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
if (lockHolder.chunkLock == null) {
return;
}
MinecraftServer.getServer().execute(() -> {
try {
ChunkPos pos = levelChunk.getPos();
ClientboundLevelChunkWithLightPacket packet;
if (PaperLib.isPaper()) {
packet = new ClientboundLevelChunkWithLightPacket(
levelChunk,
nmsWorld.getLightEngine(),
null,
null,
false // last false is to not bother with x-ray
);
} else {
// deprecated on paper - deprecation suppressed
packet = new ClientboundLevelChunkWithLightPacket(
levelChunk,
nmsWorld.getLightEngine(),
null,
null
);
}
nearbyPlayers(nmsWorld, pos).forEach(p -> p.connection.send(packet));
} finally {
NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
}
});
}
private static List<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false);
}
/*
NMS conversion
*/
public static LevelChunkSection newChunkSection(
final int layer,
final char[] blocks,
CachedBukkitAdapter adapter,
Registry<Biome> biomeRegistry,
@Nullable PalettedContainer<Holder<Biome>> biomes
) {
return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes);
}
public static LevelChunkSection newChunkSection(
final int layer,
final Function<Integer, char[]> get,
char[] set,
CachedBukkitAdapter adapter,
Registry<Biome> biomeRegistry,
@Nullable PalettedContainer<Holder<Biome>> biomes
) {
if (set == null) {
return newChunkSection(biomeRegistry, biomes);
}
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
try {
int num_palette;
if (get == null) {
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, null);
} else {
num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter, null);
}
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
if (bitsPerEntry > 0 && bitsPerEntry < 5) {
bitsPerEntry = 4;
} else if (bitsPerEntry > 8) {
bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1);
}
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
if (num_palette == 1) {
for (int i = 0; i < blockBitArrayEnd; i++) {
blockStates[i] = 0;
}
} else {
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntryNonZero, 4096, blockStates);
bitArray.fromRaw(blocksCopy);
}
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
final BitStorage nmsBits;
if (bitsPerEntry == 0) {
nmsBits = new ZeroBitStorage(4096);
} else {
nmsBits = new SimpleBitStorage(bitsPerEntry, 4096, bits);
}
List<net.minecraft.world.level.block.state.BlockState> palette;
if (bitsPerEntry < 9) {
palette = new ArrayList<>();
for (int i = 0; i < num_palette; i++) {
int ordinal = paletteToBlock[i];
blockToPalette[ordinal] = Integer.MAX_VALUE;
final BlockState state = BlockTypesCache.states[ordinal];
palette.add(((PaperweightBlockMaterial) state.getMaterial()).getState());
}
} else {
palette = List.of();
}
// Create palette with data
@SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot
final PalettedContainer<net.minecraft.world.level.block.state.BlockState> blockStatePalettedContainer =
new PalettedContainer<>(
Block.BLOCK_STATE_REGISTRY,
PalettedContainer.Strategy.SECTION_STATES,
PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry),
nmsBits,
palette
);
if (biomes == null) {
IdMap<Holder<Biome>> biomeHolderIdMap = biomeRegistry.asHolderIdMap();
biomes = new PalettedContainer<>(
biomeHolderIdMap,
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(
BiomeTypes.PLAINS)),
PalettedContainer.Strategy.SECTION_BIOMES
);
}
return new LevelChunkSection(blockStatePalettedContainer, biomes);
} catch (final Throwable e) {
throw e;
} finally {
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
Arrays.fill(paletteToBlock, Integer.MAX_VALUE);
Arrays.fill(blockStates, 0);
Arrays.fill(blocksCopy, 0);
}
}
@SuppressWarnings("deprecation") // Only deprecated in paper
private static LevelChunkSection newChunkSection(
Registry<Biome> biomeRegistry,
@Nullable PalettedContainer<Holder<Biome>> biomes
) {
if (biomes == null) {
return new LevelChunkSection(biomeRegistry);
}
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
Block.BLOCK_STATE_REGISTRY,
Blocks.AIR.defaultBlockState(),
PalettedContainer.Strategy.SECTION_STATES
);
return new LevelChunkSection(dataPaletteBlocks, biomes);
}
public static void setBiomesToChunkSection(LevelChunkSection section, PalettedContainer<Holder<Biome>> biomes) {
try {
fieldBiomes.set(section, biomes);
} catch (IllegalAccessException e) {
LOGGER.error("Could not set biomes to chunk section", e);
}
}
/**
* Create a new {@link PalettedContainer<Biome>}. Should only be used if no biome container existed beforehand.
*/
public static PalettedContainer<Holder<Biome>> getBiomePalettedContainer(
BiomeType[] biomes,
IdMap<Holder<Biome>> biomeRegistry
) {
if (biomes == null) {
return null;
}
BukkitImplAdapter<?> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
// Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length
Map<BiomeType, Holder<Biome>> palette = new HashMap<>();
for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) {
Holder<Biome> biome;
if (biomeType == null) {
biome = biomeRegistry.byId(adapter.getInternalBiomeId(BiomeTypes.PLAINS));
} else {
biome = biomeRegistry.byId(adapter.getInternalBiomeId(biomeType));
}
palette.put(biomeType, biome);
}
int biomeCount = palette.size();
int bitsPerEntry = MathMan.log2nlz(biomeCount - 1);
Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration(
new FakeIdMapBiome(biomeCount),
bitsPerEntry
);
if (bitsPerEntry > 3) {
bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1);
}
PalettedContainer<Holder<Biome>> biomePalettedContainer = new PalettedContainer<>(
biomeRegistry,
biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
PalettedContainer.Strategy.SECTION_BIOMES
);
final Palette<Holder<Biome>> biomePalette;
if (bitsPerEntry == 0) {
biomePalette = new SingleValuePalette<>(
biomePalettedContainer.registry,
biomePalettedContainer,
new ArrayList<>(palette.values()) // Must be modifiable
);
} else if (bitsPerEntry == 4) {
biomePalette = LinearPalette.create(
4,
biomePalettedContainer.registry,
biomePalettedContainer,
new ArrayList<>(palette.values()) // Must be modifiable
);
} else if (bitsPerEntry < 9) {
biomePalette = HashMapPalette.create(
bitsPerEntry,
biomePalettedContainer.registry,
biomePalettedContainer,
new ArrayList<>(palette.values()) // Must be modifiable
);
} else {
biomePalette = GlobalPalette.create(
bitsPerEntry,
biomePalettedContainer.registry,
biomePalettedContainer,
null // unused
);
}
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
final int arrayLength = MathMan.ceilZero(64f / blocksPerLong);
BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage(
bitsPerEntry,
64,
new long[arrayLength]
);
try {
Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette);
fieldData.set(biomePalettedContainer, data);
int index = 0;
for (int y = 0; y < 4; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 4; x++, index++) {
BiomeType biomeType = biomes[index];
if (biomeType == null) {
continue;
}
Holder<Biome> biome = biomeRegistry.byId(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(biomeType));
if (biome == null) {
continue;
}
biomePalettedContainer.set(x, y, z, biome);
}
}
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
return biomePalettedContainer;
}
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
fieldTickingFluidCount.setShort(section, (short) 0);
fieldTickingBlockCount.setShort(section, (short) 0);
}
public static BiomeType adapt(Holder<Biome> biome, LevelAccessor levelAccessor) {
final Registry<Biome> biomeRegistry = levelAccessor.registryAccess().lookupOrThrow(BIOME);
if (biomeRegistry.getKey(biome.value()) == null) {
return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN
: null;
}
return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString());
}
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
try {
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
if (blockEntity != null) {
if (!levelChunk.level.isClientSide) {
methodRemoveGameEventListener.invoke(levelChunk, beacon, levelChunk.level);
}
fieldRemove.set(beacon, true);
}
}
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
static List<Entity> getEntities(LevelChunk chunk) {
if (PaperLib.isPaper()) {
try {
//noinspection unchecked
return (List<Entity>) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level
.moonrise$getEntityLookup()
.getChunk(chunk.locX, chunk.locZ));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Failed to lookup entities [PAPER=true]", e);
}
}
try {
//noinspection unchecked
return ((PersistentEntitySectionManager<Entity>) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos());
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to lookup entities [PAPER=false]", e);
}
}
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
@Override
public int getId(final net.minecraft.world.level.block.state.BlockState entry) {
return 0;
}
@Nullable
@Override
public net.minecraft.world.level.block.state.BlockState byId(final int index) {
return null;
}
@Nonnull
@Override
public Iterator<net.minecraft.world.level.block.state.BlockState> iterator() {
return Collections.emptyIterator();
}
}
record FakeIdMapBiome(int size) implements IdMap<Biome> {
@Override
public int getId(final Biome entry) {
return 0;
}
@Nullable
@Override
public Biome byId(final int index) {
return null;
}
@Nonnull
@Override
public Iterator<Biome> iterator() {
return Collections.emptyIterator();
}
}
}

Datei anzeigen

@ -0,0 +1,175 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.registry.state.PropertyKey;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
import javax.annotation.Nullable;
public class PaperweightPostProcessor implements IBatchProcessor {
@Override
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
return set;
}
@SuppressWarnings("deprecation")
@Override
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
// The PostProcessor shouldn't be added, but just in case
if (!tickFluid) {
return;
}
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
layer:
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
char[] set = iChunkSet.loadIfPresent(layer);
if (set == null) {
// No edit means no need to process
continue;
}
char[] get = null;
for (int i = 0; i < 4096; i++) {
char ordinal = set[i];
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
boolean fromGet = false; // Used for liquids
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
if (get == null) {
get = getBlocks.load(layer);
}
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
// actually being set
if (get == null) {
continue layer;
}
fromGet = true;
ordinal = replacedOrdinal = get[i];
}
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
continue;
} else if (!fromGet) { // if fromGet, don't do the same again
if (get == null) {
get = getBlocks.load(layer);
}
replacedOrdinal = get[i];
}
boolean ticking = BlockTypesCache.ticking[ordinal];
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
boolean replacedWasLiquid = false;
BlockState replacedState = null;
if (!ticking) {
// If the block being replaced was not ticking, it cannot be a liquid
if (!replacedWasTicking) {
continue;
}
// If the block being replaced is not fluid, we do not need to worry
if (!(replacedWasLiquid =
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
continue;
}
}
BlockState state = BlockState.getFromOrdinal(ordinal);
boolean liquid = state.getMaterial().isLiquid();
int x = i & 15;
int y = (i >> 8) & 15;
int z = (i >> 4) & 15;
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
if (liquid || replacedWasLiquid) {
if (liquid) {
addFluid(getBlocks.serverLevel, state, position);
continue;
}
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
// being ticked anyway. We only need it to be "hit" once.
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
continue;
}
addFluid(getBlocks.serverLevel, replacedState, position);
}
}
}
}
@Nullable
@Override
public Extent construct(final Extent child) {
throw new UnsupportedOperationException("Processing only");
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
if (set == null || get == null) {
return false;
}
char ordinal;
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
if (x > 0 && set[i - 1] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
return true;
}
}
if (x < 15 && set[i + 1] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
return true;
}
}
if (z > 0 && set[i - 16] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
return true;
}
}
if (z < 15 && set[i + 16] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
return true;
}
}
if (y > 0 && set[i - 256] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
return true;
}
}
if (y < 15 && set[i + 256] != reserved) {
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
}
return false;
}
@SuppressWarnings("deprecation")
private boolean isFluid(char ordinal) {
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
}
@SuppressWarnings("deprecation")
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
Fluid type;
if (replacedState.getBlockType() == BlockTypes.LAVA) {
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
} else {
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
}
serverLevel.scheduleTick(
position,
type,
type.getTickDelay(serverLevel)
);
}
}

Datei anzeigen

@ -0,0 +1,80 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.status.ChunkPyramid;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkPyramid.LOADING_PYRAMID
.getStepTo(ChunkStatus.FULL)
.getAccumulatedRadiusOf(ChunkStatus.LIGHT);
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
super(serverLevel, queue);
}
@Override
protected ChunkPos createChunkPos(final long chunkKey) {
return new ChunkPos(chunkKey);
}
@Override
protected long asLong(final int chunkX, final int chunkZ) {
return ChunkPos.asLong(chunkX, chunkZ);
}
@Override
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
FAWE_TICKET,
chunkPos,
LIGHT_LEVEL,
Unit.INSTANCE
));
}
protected void invokeRelight(
Set<ChunkPos> coords,
Consumer<ChunkPos> chunkCallback,
IntConsumer processCallback
) {
try {
serverLevel.getChunkSource().getLightEngine().starlight$serverRelightChunks(coords, chunkCallback, processCallback);
} catch (Exception e) {
LOGGER.error("Error occurred on relighting", e);
}
}
/*
* Allow the server to unload the chunks again.
* Also, if chunk packets are sent delayed, we need to do that here
*/
protected void postProcessChunks(Set<ChunkPos> coords) {
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
for (ChunkPos pos : coords) {
int x = pos.x;
int z = pos.z;
if (delay) { // we still need to send the block changes of that chunk
PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
}
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
}
}
}

Datei anzeigen

@ -0,0 +1,25 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4;
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftWorld;
import javax.annotation.Nonnull;
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
@Override
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
org.bukkit.World w = Bukkit.getWorld(world.getName());
if (w == null) {
return NullRelighter.INSTANCE;
}
return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue);
}
}

Datei anzeigen

@ -0,0 +1,298 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4.regen;
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.io.file.SafeFiles;
import com.sk89q.worldedit.world.RegenOptions;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.ProgressListener;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.generator.BiomeProvider;
import javax.annotation.Nonnull;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.Map;
import java.util.OptionalLong;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import static net.minecraft.core.registries.Registries.BIOME;
public class PaperweightRegen extends Regenerator {
private static final Field serverWorldsField;
private static final Field paperConfigField;
private static final Field generatorSettingBaseSupplierField;
static {
try {
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
serverWorldsField.setAccessible(true);
Field tmpPaperConfigField;
try { //only present on paper
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
tmpPaperConfigField.setAccessible(true);
} catch (Exception e) {
tmpPaperConfigField = null;
}
paperConfigField = tmpPaperConfigField;
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
"settings", "e"));
generatorSettingBaseSupplierField.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//runtime
private ServerLevel originalServerWorld;
private ServerLevel freshWorld;
private LevelStorageSource.LevelStorageAccess session;
private Path tempDir;
public PaperweightRegen(
World originalBukkitWorld,
Region region,
Extent target,
RegenOptions options
) {
super(originalBukkitWorld, region, target, options);
}
@Override
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
while (shouldKeepTicking.getAsBoolean()) {
if (!this.freshWorld.getChunkSource().pollTask()) {
return;
}
}
}
@Override
protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
seed = options.getSeed().orElse(originalServerWorld.getSeed());
return true;
}
@Override
protected boolean initNewWorld() throws Exception {
//world folder
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
//prepare for world init (see upstream implementation for reference)
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
WorldOptions originalOpts = originalWorldData.worldGenOptions();
WorldOptions newOpts = options.getSeed().isPresent()
? originalOpts.withSeed(OptionalLong.of(seed))
: originalOpts;
LevelSettings newWorldSettings = new LevelSettings(
"faweregentempworld",
originalWorldData.settings.gameType(),
originalWorldData.settings.hardcore(),
originalWorldData.settings.difficulty(),
originalWorldData.settings.allowCommands(),
originalWorldData.settings.gameRules(),
originalWorldData.settings.getDataConfiguration()
);
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
originalWorldData.isFlatWorld()
? PrimaryLevelData.SpecialWorldProperty.FLAT
: originalWorldData.isDebugWorld()
? PrimaryLevelData.SpecialWorldProperty.DEBUG
: PrimaryLevelData.SpecialWorldProperty.NONE;
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
BiomeProvider biomeProvider = getBiomeProvider();
//init world
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
server,
server.executor,
session,
newWorldData,
originalServerWorld.dimension(),
new LevelStem(
originalServerWorld.dimensionTypeRegistration(),
originalServerWorld.getChunkSource().getGenerator()
),
new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(),
seed,
ImmutableList.of(),
false,
originalServerWorld.getRandomSequences(),
environment,
generator,
biomeProvider
) {
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
.lookupOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
) : null;
@Override
public @Nonnull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
if (options.hasBiomeType()) {
return singleBiome;
}
return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ);
}
@Override
public void save(
final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled
) {
// noop, spigot
}
@Override
public void save(
final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled,
final boolean close
) {
// noop, paper
}
}).get();
freshWorld.noSave = true;
removeWorldFromWorldsMap();
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
if (paperConfigField != null) {
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
}
return true;
}
@Override
protected void cleanup() {
try {
session.close();
} catch (Exception ignored) {
}
//shutdown chunk provider
try {
Fawe.instance().getQueueHandler().sync(() -> {
try {
freshWorld.getChunkSource().getDataStorage().cache.clear();
freshWorld.getChunkSource().close(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} catch (Exception ignored) {
}
//remove world from server
try {
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
} catch (Exception ignored) {
}
//delete directory
try {
SafeFiles.tryHardToDeleteDir(tempDir);
} catch (Exception ignored) {
}
}
@Override
protected IChunkCache<IChunkGet> initSourceQueueCache() {
return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
}
//util
@SuppressWarnings("unchecked")
private void removeWorldFromWorldsMap() {
try {
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
map.remove("faweregentempworld");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
return switch (env) {
case NETHER -> LevelStem.NETHER;
case THE_END -> LevelStem.END;
default -> LevelStem.OVERWORLD;
};
}
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
private RegenNoOpWorldLoadListener() {
}
@Override
public void updateSpawnPos(@Nonnull ChunkPos spawnPos) {
}
@Override
public void onStatusChange(
final @Nonnull ChunkPos pos,
@org.jetbrains.annotations.Nullable final net.minecraft.world.level.chunk.status.ChunkStatus status
) {
}
@Override
public void start() {
}
@Override
public void stop() {
}
}
}

Datei anzeigen

@ -252,7 +252,7 @@ publishMods {
// We publish the reobfJar twice to ensure that the modrinth download menu picks the right jar for the platform regardless // We publish the reobfJar twice to ensure that the modrinth download menu picks the right jar for the platform regardless
// of minecraft version. // of minecraft version.
val mojmapPaperVersions = listOf("1.20.6", "1.21.1", "1.21.3") val mojmapPaperVersions = listOf("1.20.6", "1.21.1", "1.21.3", "1.21.4")
val spigotMappedPaperVersions = listOf("1.20.2", "1.20.4") val spigotMappedPaperVersions = listOf("1.20.2", "1.20.4")
// Mark reobfJar as spigot only for 1.20.5+ // Mark reobfJar as spigot only for 1.20.5+

Datei anzeigen

@ -2,7 +2,7 @@ name: FastAsyncWorldEdit
main: com.sk89q.worldedit.bukkit.WorldEditPlugin main: com.sk89q.worldedit.bukkit.WorldEditPlugin
version: "${internalVersion}" version: "${internalVersion}"
load: STARTUP load: STARTUP
api-version: 1.15 api-version: 1.20
softdepend: [ Vault ] softdepend: [ Vault ]
provides: [ WorldEdit ] provides: [ WorldEdit ]
website: https://www.spigotmc.org/resources/13932/ website: https://www.spigotmc.org/resources/13932/

Datei anzeigen

@ -94,4 +94,8 @@ public final class Constants {
*/ */
public static final int DATA_VERSION_MC_1_21_3 = 4082; public static final int DATA_VERSION_MC_1_21_3 = 4082;
/**
* The DataVersion for Minecraft 1.21.4
*/
public static final int DATA_VERSION_MC_1_21_4 = 4189;
} }

Datei anzeigen

@ -179,6 +179,8 @@ public final class BiomeTypes {
@Nullable @Nullable
public static final BiomeType OLD_GROWTH_SPRUCE_TAIGA = get("minecraft:old_growth_spruce_taiga"); public static final BiomeType OLD_GROWTH_SPRUCE_TAIGA = get("minecraft:old_growth_spruce_taiga");
@Nullable @Nullable
public static final BiomeType PALE_GARDEN = get("minecraft:pale_garden");
@Nullable
public static final BiomeType PLAINS = get("minecraft:plains"); public static final BiomeType PLAINS = get("minecraft:plains");
@Nullable @Nullable
public static final BiomeType RIVER = get("minecraft:river"); public static final BiomeType RIVER = get("minecraft:river");

Datei anzeigen

@ -45,6 +45,7 @@ public final class BlockCategories {
public static final BlockCategory BASE_STONE_OVERWORLD = get("minecraft:base_stone_overworld"); public static final BlockCategory BASE_STONE_OVERWORLD = get("minecraft:base_stone_overworld");
public static final BlockCategory BEACON_BASE_BLOCKS = get("minecraft:beacon_base_blocks"); public static final BlockCategory BEACON_BASE_BLOCKS = get("minecraft:beacon_base_blocks");
public static final BlockCategory BEDS = get("minecraft:beds"); public static final BlockCategory BEDS = get("minecraft:beds");
public static final BlockCategory BEE_ATTRACTIVE = get("minecraft:bee_attractive");
public static final BlockCategory BEE_GROWABLES = get("minecraft:bee_growables"); public static final BlockCategory BEE_GROWABLES = get("minecraft:bee_growables");
public static final BlockCategory BEEHIVES = get("minecraft:beehives"); public static final BlockCategory BEEHIVES = get("minecraft:beehives");
public static final BlockCategory BIG_DRIPLEAF_PLACEABLE = get("minecraft:big_dripleaf_placeable"); public static final BlockCategory BIG_DRIPLEAF_PLACEABLE = get("minecraft:big_dripleaf_placeable");
@ -146,6 +147,7 @@ public final class BlockCategories {
public static final BlockCategory OCCLUDES_VIBRATION_SIGNALS = get("minecraft:occludes_vibration_signals"); public static final BlockCategory OCCLUDES_VIBRATION_SIGNALS = get("minecraft:occludes_vibration_signals");
public static final BlockCategory OVERWORLD_CARVER_REPLACEABLES = get("minecraft:overworld_carver_replaceables"); public static final BlockCategory OVERWORLD_CARVER_REPLACEABLES = get("minecraft:overworld_carver_replaceables");
public static final BlockCategory OVERWORLD_NATURAL_LOGS = get("minecraft:overworld_natural_logs"); public static final BlockCategory OVERWORLD_NATURAL_LOGS = get("minecraft:overworld_natural_logs");
public static final BlockCategory PALE_OAK_LOGS = get("minecraft:pale_oak_logs");
public static final BlockCategory PARROTS_SPAWNABLE_ON = get("minecraft:parrots_spawnable_on"); public static final BlockCategory PARROTS_SPAWNABLE_ON = get("minecraft:parrots_spawnable_on");
public static final BlockCategory PIGLIN_REPELLENTS = get("minecraft:piglin_repellents"); public static final BlockCategory PIGLIN_REPELLENTS = get("minecraft:piglin_repellents");
public static final BlockCategory PLANKS = get("minecraft:planks"); public static final BlockCategory PLANKS = get("minecraft:planks");
@ -186,7 +188,7 @@ public final class BlockCategories {
public static final BlockCategory STONE_PRESSURE_PLATES = get("minecraft:stone_pressure_plates"); public static final BlockCategory STONE_PRESSURE_PLATES = get("minecraft:stone_pressure_plates");
public static final BlockCategory STRIDER_WARM_BLOCKS = get("minecraft:strider_warm_blocks"); public static final BlockCategory STRIDER_WARM_BLOCKS = get("minecraft:strider_warm_blocks");
public static final BlockCategory SWORD_EFFICIENT = get("minecraft:sword_efficient"); public static final BlockCategory SWORD_EFFICIENT = get("minecraft:sword_efficient");
public static final BlockCategory TALL_FLOWERS = get("minecraft:tall_flowers"); @Deprecated public static final BlockCategory TALL_FLOWERS = get("minecraft:tall_flowers");
public static final BlockCategory TERRACOTTA = get("minecraft:terracotta"); public static final BlockCategory TERRACOTTA = get("minecraft:terracotta");
public static final BlockCategory TRAIL_RUINS_REPLACEABLE = get("minecraft:trail_ruins_replaceable"); public static final BlockCategory TRAIL_RUINS_REPLACEABLE = get("minecraft:trail_ruins_replaceable");
public static final BlockCategory TRAPDOORS = get("minecraft:trapdoors"); public static final BlockCategory TRAPDOORS = get("minecraft:trapdoors");

Datei anzeigen

@ -427,6 +427,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType CHISELED_RED_SANDSTONE = init(); public static final BlockType CHISELED_RED_SANDSTONE = init();
@Nullable @Nullable
public static final BlockType CHISELED_RESIN_BRICKS = init();
@Nullable
public static final BlockType CHISELED_SANDSTONE = init(); public static final BlockType CHISELED_SANDSTONE = init();
@Nullable @Nullable
public static final BlockType CHISELED_STONE_BRICKS = init(); public static final BlockType CHISELED_STONE_BRICKS = init();
@ -441,6 +443,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType CLAY = init(); public static final BlockType CLAY = init();
@Nullable @Nullable
public static final BlockType CLOSED_EYEBLOSSOM = init();
@Nullable
public static final BlockType COAL_BLOCK = init(); public static final BlockType COAL_BLOCK = init();
@Nullable @Nullable
public static final BlockType COAL_ORE = init(); public static final BlockType COAL_ORE = init();
@ -1294,6 +1298,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType OCHRE_FROGLIGHT = init(); public static final BlockType OCHRE_FROGLIGHT = init();
@Nullable @Nullable
public static final BlockType OPEN_EYEBLOSSOM = init();
@Nullable
public static final BlockType ORANGE_BANNER = init(); public static final BlockType ORANGE_BANNER = init();
@Nullable @Nullable
public static final BlockType ORANGE_BED = init(); public static final BlockType ORANGE_BED = init();
@ -1489,6 +1495,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType POTTED_CHERRY_SAPLING = init(); public static final BlockType POTTED_CHERRY_SAPLING = init();
@Nullable @Nullable
public static final BlockType POTTED_CLOSED_EYEBLOSSOM = init();
@Nullable
public static final BlockType POTTED_CORNFLOWER = init(); public static final BlockType POTTED_CORNFLOWER = init();
@Nullable @Nullable
public static final BlockType POTTED_CRIMSON_FUNGUS = init(); public static final BlockType POTTED_CRIMSON_FUNGUS = init();
@ -1516,6 +1524,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType POTTED_OAK_SAPLING = init(); public static final BlockType POTTED_OAK_SAPLING = init();
@Nullable @Nullable
public static final BlockType POTTED_OPEN_EYEBLOSSOM = init();
@Nullable
public static final BlockType POTTED_ORANGE_TULIP = init(); public static final BlockType POTTED_ORANGE_TULIP = init();
@Nullable @Nullable
public static final BlockType POTTED_OXEYE_DAISY = init(); public static final BlockType POTTED_OXEYE_DAISY = init();
@ -1688,6 +1698,18 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType REPEATING_COMMAND_BLOCK = init(); public static final BlockType REPEATING_COMMAND_BLOCK = init();
@Nullable @Nullable
public static final BlockType RESIN_BLOCK = init();
@Nullable
public static final BlockType RESIN_BRICK_SLAB = init();
@Nullable
public static final BlockType RESIN_BRICK_STAIRS = init();
@Nullable
public static final BlockType RESIN_BRICK_WALL = init();
@Nullable
public static final BlockType RESIN_BRICKS = init();
@Nullable
public static final BlockType RESIN_CLUMP = init();
@Nullable
public static final BlockType RESPAWN_ANCHOR = init(); public static final BlockType RESPAWN_ANCHOR = init();
@Nullable @Nullable
public static final BlockType ROOTED_DIRT = init(); public static final BlockType ROOTED_DIRT = init();

Datei anzeigen

@ -77,6 +77,10 @@ public final class EntityTypes {
@Nullable @Nullable
public static final EntityType COW = get("minecraft:cow"); public static final EntityType COW = get("minecraft:cow");
@Nullable @Nullable
public static final EntityType CREAKING = get("minecraft:creaking");
@Deprecated @Nullable
public static final EntityType CREAKING_TRANSIENT = get("minecraft:creaking_transient");
@Nullable
public static final EntityType CREEPER = get("minecraft:creeper"); public static final EntityType CREEPER = get("minecraft:creeper");
@Nullable @Nullable
public static final EntityType DOLPHIN = get("minecraft:dolphin"); public static final EntityType DOLPHIN = get("minecraft:dolphin");

Datei anzeigen

@ -69,6 +69,7 @@ public final class ItemCategories {
public static final ItemCategory DIAMOND_ORES = get("minecraft:diamond_ores"); public static final ItemCategory DIAMOND_ORES = get("minecraft:diamond_ores");
public static final ItemCategory DIRT = get("minecraft:dirt"); public static final ItemCategory DIRT = get("minecraft:dirt");
public static final ItemCategory DOORS = get("minecraft:doors"); public static final ItemCategory DOORS = get("minecraft:doors");
public static final ItemCategory DROWNED_PREFERRED_WEAPONS = get("minecraft:drowned_preferred_weapons");
public static final ItemCategory DYEABLE = get("minecraft:dyeable"); public static final ItemCategory DYEABLE = get("minecraft:dyeable");
public static final ItemCategory EMERALD_ORES = get("minecraft:emerald_ores"); public static final ItemCategory EMERALD_ORES = get("minecraft:emerald_ores");
public static final ItemCategory ENCHANTABLE_ARMOR = get("minecraft:enchantable/armor"); public static final ItemCategory ENCHANTABLE_ARMOR = get("minecraft:enchantable/armor");
@ -92,7 +93,7 @@ public final class ItemCategories {
public static final ItemCategory FENCE_GATES = get("minecraft:fence_gates"); public static final ItemCategory FENCE_GATES = get("minecraft:fence_gates");
public static final ItemCategory FENCES = get("minecraft:fences"); public static final ItemCategory FENCES = get("minecraft:fences");
public static final ItemCategory FISHES = get("minecraft:fishes"); public static final ItemCategory FISHES = get("minecraft:fishes");
public static final ItemCategory FLOWERS = get("minecraft:flowers"); @Deprecated public static final ItemCategory FLOWERS = get("minecraft:flowers");
public static final ItemCategory FOOT_ARMOR = get("minecraft:foot_armor"); public static final ItemCategory FOOT_ARMOR = get("minecraft:foot_armor");
public static final ItemCategory FOX_FOOD = get("minecraft:fox_food"); public static final ItemCategory FOX_FOOD = get("minecraft:fox_food");
public static final ItemCategory FREEZE_IMMUNE_WEARABLES = get("minecraft:freeze_immune_wearables"); public static final ItemCategory FREEZE_IMMUNE_WEARABLES = get("minecraft:freeze_immune_wearables");
@ -127,6 +128,7 @@ public final class ItemCategories {
@Deprecated public static final ItemCategory OCCLUDES_VIBRATION_SIGNALS = get("minecraft:occludes_vibration_signals"); @Deprecated public static final ItemCategory OCCLUDES_VIBRATION_SIGNALS = get("minecraft:occludes_vibration_signals");
public static final ItemCategory OCELOT_FOOD = get("minecraft:ocelot_food"); public static final ItemCategory OCELOT_FOOD = get("minecraft:ocelot_food");
@Deprecated public static final ItemCategory OVERWORLD_NATURAL_LOGS = get("minecraft:overworld_natural_logs"); @Deprecated public static final ItemCategory OVERWORLD_NATURAL_LOGS = get("minecraft:overworld_natural_logs");
public static final ItemCategory PALE_OAK_LOGS = get("minecraft:pale_oak_logs");
public static final ItemCategory PANDA_FOOD = get("minecraft:panda_food"); public static final ItemCategory PANDA_FOOD = get("minecraft:panda_food");
public static final ItemCategory PARROT_FOOD = get("minecraft:parrot_food"); public static final ItemCategory PARROT_FOOD = get("minecraft:parrot_food");
public static final ItemCategory PARROT_POISONOUS_FOOD = get("minecraft:parrot_poisonous_food"); public static final ItemCategory PARROT_POISONOUS_FOOD = get("minecraft:parrot_poisonous_food");
@ -134,7 +136,10 @@ public final class ItemCategories {
public static final ItemCategory PIG_FOOD = get("minecraft:pig_food"); public static final ItemCategory PIG_FOOD = get("minecraft:pig_food");
public static final ItemCategory PIGLIN_FOOD = get("minecraft:piglin_food"); public static final ItemCategory PIGLIN_FOOD = get("minecraft:piglin_food");
public static final ItemCategory PIGLIN_LOVED = get("minecraft:piglin_loved"); public static final ItemCategory PIGLIN_LOVED = get("minecraft:piglin_loved");
public static final ItemCategory PIGLIN_PREFERRED_WEAPONS = get("minecraft:piglin_preferred_weapons");
public static final ItemCategory PIGLIN_REPELLENTS = get("minecraft:piglin_repellents"); public static final ItemCategory PIGLIN_REPELLENTS = get("minecraft:piglin_repellents");
public static final ItemCategory PIGLIN_SAFE_ARMOR = get("minecraft:piglin_safe_armor");
public static final ItemCategory PILLAGER_PREFERRED_WEAPONS = get("minecraft:pillager_preferred_weapons");
public static final ItemCategory PLANKS = get("minecraft:planks"); public static final ItemCategory PLANKS = get("minecraft:planks");
public static final ItemCategory RABBIT_FOOD = get("minecraft:rabbit_food"); public static final ItemCategory RABBIT_FOOD = get("minecraft:rabbit_food");
public static final ItemCategory RAILS = get("minecraft:rails"); public static final ItemCategory RAILS = get("minecraft:rails");
@ -144,6 +149,7 @@ public final class ItemCategories {
public static final ItemCategory SHEEP_FOOD = get("minecraft:sheep_food"); public static final ItemCategory SHEEP_FOOD = get("minecraft:sheep_food");
public static final ItemCategory SHOVELS = get("minecraft:shovels"); public static final ItemCategory SHOVELS = get("minecraft:shovels");
public static final ItemCategory SIGNS = get("minecraft:signs"); public static final ItemCategory SIGNS = get("minecraft:signs");
public static final ItemCategory SKELETON_PREFERRED_WEAPONS = get("minecraft:skeleton_preferred_weapons");
public static final ItemCategory SKULLS = get("minecraft:skulls"); public static final ItemCategory SKULLS = get("minecraft:skulls");
public static final ItemCategory SLABS = get("minecraft:slabs"); public static final ItemCategory SLABS = get("minecraft:slabs");
public static final ItemCategory SMALL_FLOWERS = get("minecraft:small_flowers"); public static final ItemCategory SMALL_FLOWERS = get("minecraft:small_flowers");
@ -159,12 +165,12 @@ public final class ItemCategories {
public static final ItemCategory STRIDER_FOOD = get("minecraft:strider_food"); public static final ItemCategory STRIDER_FOOD = get("minecraft:strider_food");
public static final ItemCategory STRIDER_TEMPT_ITEMS = get("minecraft:strider_tempt_items"); public static final ItemCategory STRIDER_TEMPT_ITEMS = get("minecraft:strider_tempt_items");
public static final ItemCategory SWORDS = get("minecraft:swords"); public static final ItemCategory SWORDS = get("minecraft:swords");
public static final ItemCategory TALL_FLOWERS = get("minecraft:tall_flowers"); @Deprecated public static final ItemCategory TALL_FLOWERS = get("minecraft:tall_flowers");
public static final ItemCategory TERRACOTTA = get("minecraft:terracotta"); public static final ItemCategory TERRACOTTA = get("minecraft:terracotta");
@Deprecated public static final ItemCategory TOOLS = get("minecraft:tools"); @Deprecated public static final ItemCategory TOOLS = get("minecraft:tools");
public static final ItemCategory TRAPDOORS = get("minecraft:trapdoors"); public static final ItemCategory TRAPDOORS = get("minecraft:trapdoors");
public static final ItemCategory TRIM_MATERIALS = get("minecraft:trim_materials"); public static final ItemCategory TRIM_MATERIALS = get("minecraft:trim_materials");
public static final ItemCategory TRIM_TEMPLATES = get("minecraft:trim_templates"); @Deprecated public static final ItemCategory TRIM_TEMPLATES = get("minecraft:trim_templates");
public static final ItemCategory TRIMMABLE_ARMOR = get("minecraft:trimmable_armor"); public static final ItemCategory TRIMMABLE_ARMOR = get("minecraft:trimmable_armor");
public static final ItemCategory TURTLE_FOOD = get("minecraft:turtle_food"); public static final ItemCategory TURTLE_FOOD = get("minecraft:turtle_food");
public static final ItemCategory VILLAGER_PLANTABLE_SEEDS = get("minecraft:villager_plantable_seeds"); public static final ItemCategory VILLAGER_PLANTABLE_SEEDS = get("minecraft:villager_plantable_seeds");

Datei anzeigen

@ -492,6 +492,8 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType CHISELED_RED_SANDSTONE = init(); public static final ItemType CHISELED_RED_SANDSTONE = init();
@Nullable @Nullable
public static final ItemType CHISELED_RESIN_BRICKS = init();
@Nullable
public static final ItemType CHISELED_SANDSTONE = init(); public static final ItemType CHISELED_SANDSTONE = init();
@Nullable @Nullable
public static final ItemType CHISELED_STONE_BRICKS = init(); public static final ItemType CHISELED_STONE_BRICKS = init();
@ -512,6 +514,8 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType CLOCK = init(); public static final ItemType CLOCK = init();
@Nullable @Nullable
public static final ItemType CLOSED_EYEBLOSSOM = init();
@Nullable
public static final ItemType COAL = init(); public static final ItemType COAL = init();
@Nullable @Nullable
public static final ItemType COAL_BLOCK = init(); public static final ItemType COAL_BLOCK = init();
@ -1696,6 +1700,8 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType OMINOUS_TRIAL_KEY = init(); public static final ItemType OMINOUS_TRIAL_KEY = init();
@Nullable @Nullable
public static final ItemType OPEN_EYEBLOSSOM = init();
@Nullable
public static final ItemType ORANGE_BANNER = init(); public static final ItemType ORANGE_BANNER = init();
@Nullable @Nullable
public static final ItemType ORANGE_BED = init(); public static final ItemType ORANGE_BED = init();
@ -2070,6 +2076,20 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType REPEATING_COMMAND_BLOCK = init(); public static final ItemType REPEATING_COMMAND_BLOCK = init();
@Nullable @Nullable
public static final ItemType RESIN_BLOCK = init();
@Nullable
public static final ItemType RESIN_BRICK = init();
@Nullable
public static final ItemType RESIN_BRICK_SLAB = init();
@Nullable
public static final ItemType RESIN_BRICK_STAIRS = init();
@Nullable
public static final ItemType RESIN_BRICK_WALL = init();
@Nullable
public static final ItemType RESIN_BRICKS = init();
@Nullable
public static final ItemType RESIN_CLUMP = init();
@Nullable
public static final ItemType RESPAWN_ANCHOR = init(); public static final ItemType RESPAWN_ANCHOR = init();
@Nullable @Nullable
public static final ItemType RIB_ARMOR_TRIM_SMITHING_TEMPLATE = init(); public static final ItemType RIB_ARMOR_TRIM_SMITHING_TEMPLATE = init();

Datei anzeigen

@ -46,6 +46,7 @@ public class BundledRegistries implements Registries {
versionMap.put(Range.atLeast(Constants.DATA_VERSION_MC_1_20), "120"); versionMap.put(Range.atLeast(Constants.DATA_VERSION_MC_1_20), "120");
versionMap.put(Range.atLeast(Constants.DATA_VERSION_MC_1_21), "121"); versionMap.put(Range.atLeast(Constants.DATA_VERSION_MC_1_21), "121");
versionMap.put(Range.atLeast(Constants.DATA_VERSION_MC_1_21_3), "1213"); versionMap.put(Range.atLeast(Constants.DATA_VERSION_MC_1_21_3), "1213");
versionMap.put(Range.atLeast(Constants.DATA_VERSION_MC_1_21_4), "1214");
VERSION_MAP = ImmutableRangeMap.copyOf(versionMap); VERSION_MAP = ImmutableRangeMap.copyOf(versionMap);
} }

Dateidiff unterdrückt, weil mindestens eine Zeile zu lang ist

Dateidiff unterdrückt, weil mindestens eine Zeile zu lang ist