use of org.spongepowered.common.block.SpongeBlockSnapshot in project SpongeCommon by SpongePowered.
the class TrackingUtil method performBlockAdditions.
@SuppressWarnings("rawtypes")
public static boolean performBlockAdditions(List<Transaction<BlockSnapshot>> transactions, IPhaseState<?> phaseState, PhaseContext<?> phaseContext, boolean noCancelledTransactions) {
// We have to use a proxy so that our pending changes are notified such that any accessors from block
// classes do not fail on getting the incorrect block state from the IBlockAccess
// final SpongeProxyBlockAccess proxyBlockAccess = new SpongeProxyBlockAccess(transactions);
final CapturedMultiMapSupplier<BlockPos, ItemDropData> capturedBlockDrops = phaseContext.getBlockDropSupplier();
final CapturedMultiMapSupplier<BlockPos, EntityItem> capturedBlockItemEntityDrops = phaseContext.getBlockItemDropSupplier();
final CapturedMultiMapSupplier<BlockPos, net.minecraft.entity.Entity> capturedBlockEntitySpawns = phaseContext.getBlockEntitySpawnSupplier();
for (Transaction<BlockSnapshot> transaction : transactions) {
if (!transaction.isValid()) {
// Rememver that this value needs to be set to false to return because of the fact that
// a transaction was marked as invalid or cancelled. This is used primarily for
// things like portal creation, and if false, removes the portal from the cache
noCancelledTransactions = false;
// Don't use invalidated block transactions during notifications, these only need to be restored
continue;
}
// Handle custom replacements
if (transaction.getCustom().isPresent()) {
transaction.getFinal().restore(true, BlockChangeFlags.NONE);
}
final SpongeBlockSnapshot oldBlockSnapshot = (SpongeBlockSnapshot) transaction.getOriginal();
final SpongeBlockSnapshot newBlockSnapshot = (SpongeBlockSnapshot) transaction.getFinal();
final Location<World> worldLocation = oldBlockSnapshot.getLocation().get();
final IMixinWorldServer mixinWorldServer = (IMixinWorldServer) worldLocation.getExtent();
// Handle item drops captured
final BlockPos pos = ((IMixinLocation) (Object) oldBlockSnapshot.getLocation().get()).getBlockPos();
// This is for pre-merged items
capturedBlockDrops.acceptAndRemoveIfPresent(pos, items -> spawnItemDataForBlockDrops(items, oldBlockSnapshot, phaseContext, phaseState));
// And this is for un-pre-merged items, these will be EntityItems, not ItemDropDatas.
capturedBlockItemEntityDrops.acceptAndRemoveIfPresent(pos, items -> spawnItemEntitiesForBlockDrops(items, oldBlockSnapshot, phaseContext, phaseState));
// This is for entities actually spawned
capturedBlockEntitySpawns.acceptAndRemoveIfPresent(pos, items -> spawnEntitiesForBlock(items, oldBlockSnapshot, phaseContext, phaseState));
final WorldServer worldServer = mixinWorldServer.asMinecraftWorld();
SpongeHooks.logBlockAction(worldServer, oldBlockSnapshot.blockChange, transaction);
final SpongeBlockChangeFlag changeFlag = oldBlockSnapshot.getChangeFlag();
final IBlockState originalState = (IBlockState) oldBlockSnapshot.getState();
final IBlockState newState = (IBlockState) newBlockSnapshot.getState();
// We call onBlockAdded here for blocks without a TileEntity.
// MixinChunk#setBlockState will call onBlockAdded for blocks
// with a TileEntity or when capturing is not being done.
final PhaseTracker phaseTracker = PhaseTracker.getInstance();
if (!SpongeImplHooks.hasBlockTileEntity(newState.getBlock(), newState) && changeFlag.performBlockPhysics() && originalState.getBlock() != newState.getBlock()) {
newState.getBlock().onBlockAdded(worldServer, pos, newState);
final PhaseData peek = phaseTracker.getCurrentPhaseData();
if (peek.state == GeneralPhase.Post.UNWINDING) {
((IPhaseState) peek.state).unwind(peek.context);
}
}
// proxyBlockAccess.proceed();
((IPhaseState) phaseState).handleBlockChangeWithUser(oldBlockSnapshot.blockChange, transaction, phaseContext);
if (changeFlag.isNotifyClients()) {
// Always try to notify clients of the change.
worldServer.notifyBlockUpdate(pos, originalState, newState, changeFlag.getRawFlag());
}
if (changeFlag.updateNeighbors()) {
// Notify neighbors only if the change flag allowed it.
mixinWorldServer.spongeNotifyNeighborsPostBlockChange(pos, originalState, newState, changeFlag);
} else if (changeFlag.notifyObservers()) {
worldServer.updateObservingBlocksAt(pos, newState.getBlock());
}
final PhaseData peek = phaseTracker.getCurrentPhaseData();
if (peek.state == GeneralPhase.Post.UNWINDING) {
((IPhaseState) peek.state).unwind(peek.context);
}
}
return noCancelledTransactions;
}
use of org.spongepowered.common.block.SpongeBlockSnapshot in project SpongeCommon by SpongePowered.
the class SpongeItemStackBuilder method fromBlockSnapshot.
@Override
public ItemStack.Builder fromBlockSnapshot(BlockSnapshot blockSnapshot) {
checkNotNull(blockSnapshot, "The snapshot was null!");
reset();
final BlockType blockType = blockSnapshot.getState().getType();
final Optional<ItemType> itemType = blockType.getItem();
itemType(itemType.orElseThrow(() -> new IllegalArgumentException("ItemType not found for block type: " + blockType.getId())));
quantity(1);
if (blockSnapshot instanceof SpongeBlockSnapshot) {
final Block block = (Block) blockType;
this.damageValue = block.damageDropped((IBlockState) blockSnapshot.getState());
final Optional<NBTTagCompound> compound = ((SpongeBlockSnapshot) blockSnapshot).getCompound();
if (compound.isPresent()) {
this.compound = new NBTTagCompound();
this.compound.setTag(NbtDataUtil.BLOCK_ENTITY_TAG, compound.get());
}
// todo probably needs more testing, but this'll do donkey...
} else {
// TODO handle through the API specifically handling the rest of the data stuff
blockSnapshot.getContainers().forEach(this::itemData);
}
return this;
}
use of org.spongepowered.common.block.SpongeBlockSnapshot in project SpongeCommon by SpongePowered.
the class MixinWorldServer method createSpongeBlockSnapshot.
@Override
public SpongeBlockSnapshot createSpongeBlockSnapshot(IBlockState state, IBlockState extended, BlockPos pos, BlockChangeFlag updateFlag) {
this.builder.reset();
this.builder.blockState((BlockState) state).extendedState((BlockState) extended).worldId(this.getUniqueId()).position(VecHelper.toVector3i(pos));
Optional<UUID> creator = getCreator(pos.getX(), pos.getY(), pos.getZ());
Optional<UUID> notifier = getNotifier(pos.getX(), pos.getY(), pos.getZ());
if (creator.isPresent()) {
this.builder.creator(creator.get());
}
if (notifier.isPresent()) {
this.builder.notifier(notifier.get());
}
if (state.getBlock() instanceof ITileEntityProvider) {
// We MUST only check to see if a TE exists to avoid creating a new one.
final net.minecraft.tileentity.TileEntity te = this.getChunkFromBlockCoords(pos).getTileEntity(pos, net.minecraft.world.chunk.Chunk.EnumCreateEntityType.CHECK);
if (te != null) {
TileEntity tile = (TileEntity) te;
for (DataManipulator<?, ?> manipulator : ((IMixinCustomDataHolder) tile).getCustomManipulators()) {
this.builder.add(manipulator);
}
NBTTagCompound nbt = new NBTTagCompound();
// Some mods like OpenComputers assert if attempting to save robot while moving
try {
te.writeToNBT(nbt);
this.builder.unsafeNbt(nbt);
} catch (Throwable t) {
// ignore
}
}
}
return new SpongeBlockSnapshot(this.builder, (SpongeBlockChangeFlag) updateFlag);
}
use of org.spongepowered.common.block.SpongeBlockSnapshot in project SpongeCommon by SpongePowered.
the class TransactionSink method logScheduledUpdate.
@SuppressWarnings("ConstantConditions")
default void logScheduledUpdate(final ServerLevel serverWorld, final TickNextTickData<?> data) {
final WeakReference<ServerLevel> worldRef = new WeakReference<>(serverWorld);
final Supplier<ServerLevel> worldSupplier = () -> Objects.requireNonNull(worldRef.get(), "ServerWorld dereferenced");
@Nullable final BlockEntity tileEntity = serverWorld.getBlockEntity(data.pos);
final BlockState existing = serverWorld.getBlockState(data.pos);
final SpongeBlockSnapshot original = TrackingUtil.createPooledSnapshot(existing, data.pos, BlockChangeFlags.NONE, Constants.World.DEFAULT_BLOCK_CHANGE_LIMIT, tileEntity, worldSupplier, Optional::empty, Optional::empty);
original.blockChange = BlockChange.MODIFY;
final ScheduleUpdateTransaction transaction = new ScheduleUpdateTransaction(original, data);
this.logTransaction(transaction);
}
use of org.spongepowered.common.block.SpongeBlockSnapshot in project SpongeCommon by SpongePowered.
the class LevelChunkMixin_Tracker method bridge$createChunkPipeline.
/**
* Technically a full overwrite for {@link LevelChunk#setBlockState(BlockPos, BlockState, boolean)}
* and due to Sponge's hijacking of {@link ServerLevel#setBlock(BlockPos, BlockState, int)},
* it needs to be able to record transactions when necessary. This implementation allows for us to
* further specify the types of transactions and what proxies are needing to set up where.
*
* @param pos The position changing
* @param newState The new state
* @param currentState The current state - passed in from either chunk or world
* @param flag The sponge change flag, converted from an int to a proper struct
* @return The changed block state if not null
* @author gabizou - January 13th, 2020 - Minecraft 1.14.3
*/
@Override
@NonNull
public ChunkPipeline bridge$createChunkPipeline(final BlockPos pos, final BlockState newState, final BlockState currentState, final SpongeBlockChangeFlag flag, final int limit) {
final boolean isFake = ((LevelBridge) this.level).bridge$isFake();
if (isFake) {
throw new IllegalStateException("Cannot call ChunkBridge.bridge$buildChunkPipeline in non-Server managed worlds");
}
// int i = pos.getX() & 15;
final int xPos = pos.getX() & 15;
// int j = pos.getY();
final int yPos = pos.getY();
// int k = pos.getZ() & 15;
final int zPos = pos.getZ() & 15;
// Sponge - get the moving flag from our flag construct
LevelChunkSection chunksection = this.sections[yPos >> 4];
if (chunksection == LevelChunkMixin_Tracker.EMPTY_SECTION) {
if (newState.isAir()) {
return ChunkPipeline.nullReturn((LevelChunk) (Object) this, (ServerLevel) this.level);
}
chunksection = new LevelChunkSection(yPos >> 4 << 4);
this.sections[yPos >> 4] = chunksection;
}
// Sponge Start - Build out the BlockTransaction
final PhaseContext<@NonNull ?> context = PhaseTracker.getInstance().getPhaseContext();
@Nullable final BlockEntity existing = this.shadow$getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK);
// Build a transaction maybe?
final WeakReference<ServerLevel> ref = new WeakReference<>((ServerLevel) this.level);
final SpongeBlockSnapshot snapshot = TrackingUtil.createPooledSnapshot(currentState, pos, flag, limit, existing, () -> Objects.requireNonNull(ref.get(), "ServerWorld dereferenced"), Optional::empty, Optional::empty);
// Pulled up from below
final ChangeBlock transaction = context.createTransaction(snapshot, newState, flag);
snapshot.blockChange = context.associateBlockChangeWithSnapshot(newState, currentState);
if (((BlockStateBridge) snapshot.state()).bridge$hasTileEntity() && (snapshot.blockChange == BlockChange.BREAK || snapshot.blockChange == BlockChange.MODIFY)) {
transaction.queuedRemoval = existing;
}
final ChunkPipeline.Builder builder = ChunkPipeline.builder().kickOff(transaction).chunk((LevelChunk) (Object) this).chunkSection(chunksection).world((ServerLevel) this.level);
// Populate the effects
transaction.populateChunkEffects(builder);
return builder.build();
}
Aggregations