Cherry-pick WNA, minor changes. 1.16 VERY WIP

First noticed incident of operations ruining ChunkSections. Do not build and use this unless you're testing.

Rushed some of the changes, gotta sleep. Would be nice to get a review of this one from @mattbdev and @dordsor21
This commit is contained in:
Octavia Togami
2020-03-22 21:02:04 -07:00
committed by MattBDev
parent 4604aa5920
commit d232dc28e3
31 changed files with 1170 additions and 345 deletions

View File

@ -25,6 +25,7 @@ import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.metadata.EntityProperties;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.forge.internal.NBTConverter;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.NullWorld;

View File

@ -208,7 +208,7 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform {
}
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
SideEffect.CONNECTIONS,
SideEffect.VALIDATION,
SideEffect.ENTITY_AI,
SideEffect.LIGHTING,
SideEffect.NEIGHBORS

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.forge;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@ -34,6 +32,9 @@ import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.forge.internal.ForgeWorldNativeAccess;
import com.sk89q.worldedit.forge.internal.NBTConverter;
import com.sk89q.worldedit.forge.internal.TileEntityUtils;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.util.BiomeMath;
@ -105,6 +106,7 @@ import net.minecraft.world.storage.SaveHandler;
import net.minecraft.world.storage.WorldInfo;
import net.minecraftforge.common.DimensionManager;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
@ -119,7 +121,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An adapter to Minecraft worlds for WorldEdit.
@ -127,13 +129,13 @@ import javax.annotation.Nullable;
public class ForgeWorld extends AbstractWorld {
private static final Random random = new Random();
private static final int UPDATE = 1, NOTIFY = 2;
private static final net.minecraft.block.BlockState JUNGLE_LOG = Blocks.JUNGLE_LOG.getDefaultState();
private static final net.minecraft.block.BlockState JUNGLE_LEAF = Blocks.JUNGLE_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE);
private static final net.minecraft.block.BlockState JUNGLE_SHRUB = Blocks.OAK_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE);
private final WeakReference<World> worldRef;
private final ForgeWorldNativeAccess nativeAccess;
/**
* Construct a new world.
@ -143,6 +145,7 @@ public class ForgeWorld extends AbstractWorld {
ForgeWorld(World world) {
checkNotNull(world);
this.worldRef = new WeakReference<>(world);
this.nativeAccess = new ForgeWorldNativeAccess(worldRef);
}
/**
@ -194,101 +197,14 @@ public class ForgeWorld extends AbstractWorld {
return null;
}
/**
* This is a heavily modified function stripped from MC to apply worldedit-modifications.
*
* @see World#markAndNotifyBlock
*/
public void markAndNotifyBlock(World world, BlockPos pos, @Nullable Chunk chunk, net.minecraft.block.BlockState blockstate,
net.minecraft.block.BlockState newState, SideEffectSet sideEffectSet) {
Block block = newState.getBlock();
net.minecraft.block.BlockState blockstate1 = world.getBlockState(pos);
if (blockstate1 == newState) {
if (blockstate != blockstate1) {
world.markBlockRangeForRenderUpdate(pos, blockstate, blockstate1);
}
// Remove redundant branches
if (world.isRemote || chunk == null || chunk.getLocationType().isAtLeast(ChunkHolder.LocationType.TICKING)) {
if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) {
world.notifyBlockUpdate(pos, blockstate, newState, UPDATE | NOTIFY);
} else {
// If we want to skip entity AI, just call the chunk dirty flag.
((ServerChunkProvider) world.getChunkProvider()).markBlockChanged(pos);
}
}
if (!world.isRemote && sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) {
world.notifyNeighbors(pos, blockstate.getBlock());
if (newState.hasComparatorInputOverride()) {
world.updateComparatorOutputLevel(pos, block);
}
}
// Make connection updates optional
if (sideEffectSet.shouldApply(SideEffect.CONNECTIONS)) {
blockstate.updateDiagonalNeighbors(world, pos, 2);
newState.updateNeighbors(world, pos, 2);
newState.updateDiagonalNeighbors(world, pos, 2);
}
// This is disabled for other platforms, but keep it for mods.
world.onBlockStateChange(pos, blockstate, blockstate1);
}
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException {
checkNotNull(position);
checkNotNull(block);
World world = getWorldChecked();
int x = position.getBlockX();
int y = position.getBlockY();
int z = position.getBlockZ();
// First set the block
Chunk chunk = world.getChunk(x >> 4, z >> 4);
BlockPos pos = new BlockPos(x, y, z);
net.minecraft.block.BlockState old = chunk.getBlockState(pos);
OptionalInt stateId = BlockStateIdAccess.getBlockStateId(block.toImmutableState());
net.minecraft.block.BlockState newState = stateId.isPresent() ? Block.getStateById(stateId.getAsInt()) : ForgeAdapter.adapt(block.toImmutableState());
net.minecraft.block.BlockState successState = chunk.setBlockState(pos, newState, false);
boolean successful = successState != null;
// Create the TileEntity
if (successful || old == newState) {
if (block instanceof BaseBlock) {
CompoundTag tag = ((BaseBlock) block).getNbtData();
if (tag != null) {
CompoundNBT nativeTag = NBTConverter.toNative(tag);
nativeTag.putString("id", ((BaseBlock) block).getNbtId());
TileEntityUtils.setTileEntity(world, position, nativeTag);
successful = true; // update if TE changed as well
}
}
}
if (successful) {
if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) {
world.getChunkProvider().getLightManager().checkBlock(pos);
}
markAndNotifyBlock(world, pos, chunk, old, newState, sideEffects);
}
return successful;
return nativeAccess.setBlock(position, block, sideEffects);
}
@Override
public Set<SideEffect> applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException {
BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ());
net.minecraft.block.BlockState oldData = ForgeAdapter.adapt(previousType);
net.minecraft.block.BlockState newData = getWorld().getBlockState(pos);
if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) {
getWorld().getChunkProvider().getLightManager().checkBlock(pos);
}
markAndNotifyBlock(getWorld(), pos, null, oldData, newData, sideEffectSet); // Update
nativeAccess.applySideEffects(position, previousType, sideEffectSet);
return Sets.intersection(ForgeWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply());
}

View File

@ -0,0 +1,152 @@
/*
* 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 Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.forge.internal;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.forge.ForgeAdapter;
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 net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.server.ChunkHolder;
import net.minecraft.world.server.ServerChunkProvider;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.Objects;
public class ForgeWorldNativeAccess implements WorldNativeAccess<Chunk, BlockState, BlockPos> {
private static final int UPDATE = 1, NOTIFY = 2;
private final WeakReference<World> world;
private SideEffectSet sideEffectSet;
public ForgeWorldNativeAccess(WeakReference<World> world) {
this.world = world;
}
private World getWorld() {
return Objects.requireNonNull(world.get(), "The reference to the world was lost");
}
@Override
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public Chunk getChunk(int x, int z) {
return getWorld().getChunk(x, z);
}
@Override
public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) {
int stateId = BlockStateIdAccess.getBlockStateId(state);
return BlockStateIdAccess.isValidInternalId(stateId)
? Block.getStateById(stateId)
: ForgeAdapter.adapt(state);
}
@Override
public BlockState getBlockState(Chunk chunk, BlockPos position) {
return chunk.getBlockState(position);
}
@Nullable
@Override
public BlockState setBlockState(Chunk chunk, BlockPos position, BlockState state) {
return chunk.setBlockState(position, state, false);
}
@Override
public BlockState getValidBlockForPosition(BlockState block, BlockPos position) {
return Block.getValidBlockForPosition(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().getChunkProvider().getLightManager().checkBlock(position);
}
@Override
public boolean updateTileEntity(BlockPos position, CompoundTag tag) {
CompoundNBT nativeTag = NBTConverter.toNative(tag);
return TileEntityUtils.setTileEntity(getWorld(), position, nativeTag);
}
@Override
public void notifyBlockUpdate(BlockPos position, BlockState oldState, BlockState newState) {
getWorld().notifyBlockUpdate(position, oldState, newState, UPDATE | NOTIFY);
}
@Override
public boolean isChunkTicking(Chunk chunk) {
return chunk.getLocationType().isAtLeast(ChunkHolder.LocationType.TICKING);
}
@Override
public void markBlockChanged(BlockPos position) {
((ServerChunkProvider) getWorld().getChunkProvider()).markBlockChanged(position);
}
@Override
public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) {
World world = getWorld();
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
world.notifyNeighbors(pos, oldState.getBlock());
} else {
// Manually update each side
Block block = oldState.getBlock();
world.neighborChanged(pos.west(), block, pos);
world.neighborChanged(pos.east(), block, pos);
world.neighborChanged(pos.down(), block, pos);
world.neighborChanged(pos.up(), block, pos);
world.neighborChanged(pos.north(), block, pos);
world.neighborChanged(pos.south(), block, pos);
}
if (newState.hasComparatorInputOverride()) {
world.updateComparatorOutputLevel(pos, newState.getBlock());
}
}
@Override
public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState) {
World world = getWorld();
oldState.updateDiagonalNeighbors(world, pos, NOTIFY);
newState.updateNeighbors(world, pos, NOTIFY);
newState.updateDiagonalNeighbors(world, pos, NOTIFY);
}
@Override
public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) {
getWorld().onBlockStateChange(pos, oldState, newState);
}
}

View File

@ -24,13 +24,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.math.BlockVector3;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.IntNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
/**
* Utility methods for setting tile entities in the world.
*/
@ -39,39 +36,21 @@ public final class TileEntityUtils {
private TileEntityUtils() {
}
/**
* Update the given tag compound with position information.
*
* @param tag the tag
* @param position the position
*/
private static void updateForSet(CompoundNBT tag, BlockVector3 position) {
checkNotNull(tag);
checkNotNull(position);
tag.put("x", new IntNBT(position.getBlockX()));
tag.put("y", new IntNBT(position.getBlockY()));
tag.put("z", new IntNBT(position.getBlockZ()));
}
/**
* Set a tile entity at the given location using the tile entity ID from
* the tag.
*
* @param world the world
* @param position the position
* @param tag the tag for the tile entity (may be null to do nothing)
* @param tag the tag for the tile entity
*/
static boolean setTileEntity(World world, BlockVector3 position, @Nullable CompoundNBT tag) {
if (tag != null) {
updateForSet(tag, position);
TileEntity tileEntity = TileEntity.create(tag);
if (tileEntity == null) {
return false;
}
world.setTileEntity(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()), tileEntity);
return true;
static boolean setTileEntity(World world, BlockPos position, CompoundNBT tag) {
TileEntity tileEntity = TileEntity.create(tag);
if (tileEntity == null) {
return false;
}
world.setTileEntity(new BlockPos(position.getX(), position.getY(), position.getZ()), tileEntity);
return true;
}
public static CompoundNBT copyNbtData(TileEntity tile) {