Search in sources :

Example 11 with BlockSnapshot

use of org.spongepowered.api.block.BlockSnapshot in project SpongeCommon by SpongePowered.

the class TrackingUtil method randomTickBlock.

public static void randomTickBlock(PhaseTracker phaseTracker, IMixinWorldServer mixinWorld, Block block, BlockPos pos, IBlockState state, Random random) {
    final WorldServer minecraftWorld = mixinWorld.asMinecraftWorld();
    try (@SuppressWarnings("unused") StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
        Sponge.getCauseStackManager().pushCause(minecraftWorld);
        if (ShouldFire.TICK_BLOCK_EVENT) {
            final BlockSnapshot currentTickBlock = mixinWorld.createSpongeBlockSnapshot(state, state, pos, BlockChangeFlags.NONE);
            final TickBlockEvent event = SpongeEventFactory.createTickBlockEventRandom(Sponge.getCauseStackManager().getCurrentCause(), currentTickBlock);
            SpongeImpl.postEvent(event);
            if (event.isCancelled()) {
                return;
            }
        }
        final LocatableBlock locatable = LocatableBlock.builder().location(new Location<>(mixinWorld.asSpongeWorld(), pos.getX(), pos.getY(), pos.getZ())).state((BlockState) state).build();
        Sponge.getCauseStackManager().pushCause(locatable);
        IPhaseState<BlockTickContext> phase = ((IMixinBlock) block).requiresBlockCapture() ? TickPhase.Tick.RANDOM_BLOCK : TickPhase.Tick.NO_CAPTURE_BLOCK;
        final BlockTickContext phaseContext = phase.createPhaseContext().source(locatable);
        checkAndAssignBlockTickConfig(block, minecraftWorld, phaseContext);
        // We have to associate any notifiers in case of scheduled block updates from other sources
        final PhaseData current = phaseTracker.getCurrentPhaseData();
        final IPhaseState<?> currentState = current.state;
        ((IPhaseState) currentState).appendNotifierPreBlockTick(mixinWorld, pos, current.context, phaseContext);
        // Now actually switch to the new phase
        try (PhaseContext<?> context = phaseContext.buildAndSwitch()) {
            block.randomTick(minecraftWorld, pos, state, random);
        } catch (Exception | NoClassDefFoundError e) {
            phaseTracker.printExceptionFromPhase(e, phaseContext);
        }
    }
}
Also used : SpongeBlockSnapshot(org.spongepowered.common.block.SpongeBlockSnapshot) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) WorldServer(net.minecraft.world.WorldServer) TickBlockEvent(org.spongepowered.api.event.block.TickBlockEvent) BlockState(org.spongepowered.api.block.BlockState) IBlockState(net.minecraft.block.state.IBlockState) BlockTickContext(org.spongepowered.common.event.tracking.phase.tick.BlockTickContext) StackFrame(org.spongepowered.api.event.CauseStackManager.StackFrame) LocatableBlock(org.spongepowered.api.world.LocatableBlock)

Example 12 with BlockSnapshot

use of org.spongepowered.api.block.BlockSnapshot 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;
}
Also used : IMixinEntity(org.spongepowered.common.interfaces.entity.IMixinEntity) TileEntity(org.spongepowered.api.block.tileentity.TileEntity) IMixinTileEntity(org.spongepowered.common.interfaces.block.tile.IMixinTileEntity) Entity(org.spongepowered.api.entity.Entity) SpongeBlockChangeFlag(org.spongepowered.common.world.SpongeBlockChangeFlag) IBlockState(net.minecraft.block.state.IBlockState) IMixinLocation(org.spongepowered.common.interfaces.world.IMixinLocation) SpongeBlockSnapshot(org.spongepowered.common.block.SpongeBlockSnapshot) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) WorldServer(net.minecraft.world.WorldServer) World(org.spongepowered.api.world.World) SpongeBlockSnapshot(org.spongepowered.common.block.SpongeBlockSnapshot) BlockPos(net.minecraft.util.math.BlockPos) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) EntityItem(net.minecraft.entity.item.EntityItem)

Example 13 with BlockSnapshot

use of org.spongepowered.api.block.BlockSnapshot in project SpongeCommon by SpongePowered.

the class BlockDropItemsPhaseState method unwind.

@SuppressWarnings("unchecked")
@Override
public void unwind(GeneralizedContext phaseContext) {
    final BlockSnapshot blockSnapshot = phaseContext.getSource(BlockSnapshot.class).orElseThrow(TrackingUtil.throwWithContext("Could not find a block dropping items!", phaseContext));
    try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
        Sponge.getCauseStackManager().pushCause(blockSnapshot);
        Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.DROPPED_ITEM);
        if (phaseContext.getNotifier().isPresent()) {
            Sponge.getCauseStackManager().addContext(EventContextKeys.NOTIFIER, phaseContext.getNotifier().get());
        }
        if (phaseContext.getOwner().isPresent()) {
            Sponge.getCauseStackManager().addContext(EventContextKeys.OWNER, phaseContext.getOwner().get());
        }
        phaseContext.getCapturedItemsSupplier().acceptAndClearIfNotEmpty(items -> {
            final ArrayList<Entity> entities = new ArrayList<>();
            for (EntityItem item : items) {
                entities.add(EntityUtil.fromNative(item));
            }
            final DropItemEvent.Destruct event = SpongeEventFactory.createDropItemEventDestruct(Sponge.getCauseStackManager().getCurrentCause(), entities);
            SpongeImpl.postEvent(event);
            if (!event.isCancelled()) {
                for (Entity entity : event.getEntities()) {
                    EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                }
            }
        });
        phaseContext.getCapturedEntitySupplier().acceptAndClearIfNotEmpty(entities -> {
            final SpawnEntityEvent event = SpongeEventFactory.createSpawnEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), entities);
            SpongeImpl.postEvent(event);
            if (!event.isCancelled()) {
                for (Entity entity : event.getEntities()) {
                    EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                }
            }
            entities.clear();
        });
        final Location<World> worldLocation = blockSnapshot.getLocation().get();
        final IMixinWorldServer mixinWorld = ((IMixinWorldServer) worldLocation.getExtent());
        Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, SpawnTypes.BLOCK_SPAWNING);
        phaseContext.getCapturedBlockSupplier().acceptAndClearIfNotEmpty(blocks -> TrackingUtil.processBlockCaptures(blocks, this, phaseContext));
        phaseContext.getCapturedItemStackSupplier().acceptAndClearIfNotEmpty(drops -> {
            final List<EntityItem> items = drops.stream().map(drop -> drop.create(mixinWorld.asMinecraftWorld())).collect(Collectors.toList());
            final List<Entity> entities = (List<Entity>) (List<?>) items;
            if (!entities.isEmpty()) {
                DropItemEvent.Custom event = SpongeEventFactory.createDropItemEventCustom(Sponge.getCauseStackManager().getCurrentCause(), entities);
                SpongeImpl.postEvent(event);
                if (!event.isCancelled()) {
                    for (Entity droppedItem : event.getEntities()) {
                        mixinWorld.forceSpawnEntity(droppedItem);
                    }
                }
            }
            drops.clear();
        });
        phaseContext.getBlockDropSupplier().acceptAndClearIfNotEmpty(drops -> {
            for (BlockPos key : drops.asMap().keySet()) {
                final List<ItemDropData> values = drops.get(key);
                if (!values.isEmpty()) {
                    TrackingUtil.spawnItemDataForBlockDrops(values, blockSnapshot, phaseContext, this);
                }
            }
        });
    }
}
Also used : EntityItem(net.minecraft.entity.item.EntityItem) SpongeImpl(org.spongepowered.common.SpongeImpl) Location(org.spongepowered.api.world.Location) EventContextKeys(org.spongepowered.api.event.cause.EventContextKeys) SpongeEventFactory(org.spongepowered.api.event.SpongeEventFactory) Sponge(org.spongepowered.api.Sponge) BlockPos(net.minecraft.util.math.BlockPos) SpawnEntityEvent(org.spongepowered.api.event.entity.SpawnEntityEvent) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) InternalSpawnTypes(org.spongepowered.common.registry.type.event.InternalSpawnTypes) Entity(org.spongepowered.api.entity.Entity) Collectors(java.util.stream.Collectors) EntityUtil(org.spongepowered.common.entity.EntityUtil) ArrayList(java.util.ArrayList) SpawnTypes(org.spongepowered.api.event.cause.entity.spawn.SpawnTypes) List(java.util.List) TrackingUtil(org.spongepowered.common.event.tracking.TrackingUtil) GeneralizedContext(org.spongepowered.common.event.tracking.context.GeneralizedContext) World(org.spongepowered.api.world.World) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) CauseStackManager(org.spongepowered.api.event.CauseStackManager) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) Entity(org.spongepowered.api.entity.Entity) DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) ArrayList(java.util.ArrayList) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) World(org.spongepowered.api.world.World) SpawnEntityEvent(org.spongepowered.api.event.entity.SpawnEntityEvent) CauseStackManager(org.spongepowered.api.event.CauseStackManager) ArrayList(java.util.ArrayList) List(java.util.List) BlockPos(net.minecraft.util.math.BlockPos) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) EntityItem(net.minecraft.entity.item.EntityItem)

Example 14 with BlockSnapshot

use of org.spongepowered.api.block.BlockSnapshot in project SpongeCommon by SpongePowered.

the class DispensePhaseState method unwind.

@Override
public void unwind(GeneralizedContext phaseContext) {
    final BlockSnapshot blockSnapshot = phaseContext.getSource(BlockSnapshot.class).orElseThrow(TrackingUtil.throwWithContext("Could not find a block dispensing items!", phaseContext));
    phaseContext.getCapturedBlockSupplier().acceptAndClearIfNotEmpty(blockSnapshots -> TrackingUtil.processBlockCaptures(blockSnapshots, this, phaseContext));
    try (StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
        Sponge.getCauseStackManager().pushCause(blockSnapshot);
        Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.DISPENSE);
        phaseContext.addNotifierAndOwnerToCauseStack();
        phaseContext.getCapturedItemsSupplier().acceptAndClearIfNotEmpty(items -> {
            final ArrayList<Entity> entities = new ArrayList<>();
            for (EntityItem item : items) {
                entities.add(EntityUtil.fromNative(item));
            }
            final DropItemEvent.Dispense event = SpongeEventFactory.createDropItemEventDispense(Sponge.getCauseStackManager().getCurrentCause(), entities);
            SpongeImpl.postEvent(event);
            if (!event.isCancelled()) {
                for (Entity entity : event.getEntities()) {
                    EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                }
            }
        });
        phaseContext.getCapturedEntitySupplier().acceptAndClearIfNotEmpty(entities -> {
            final SpawnEntityEvent event = SpongeEventFactory.createSpawnEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), entities);
            SpongeImpl.postEvent(event);
            final User user = phaseContext.getNotifier().orElseGet(() -> phaseContext.getOwner().orElse(null));
            if (!event.isCancelled()) {
                for (Entity entity : event.getEntities()) {
                    if (user != null) {
                        EntityUtil.toMixin(entity).setCreator(user.getUniqueId());
                    }
                    EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                }
            }
        });
    }
}
Also used : Entity(org.spongepowered.api.entity.Entity) DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) User(org.spongepowered.api.entity.living.player.User) StackFrame(org.spongepowered.api.event.CauseStackManager.StackFrame) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) ArrayList(java.util.ArrayList) SpawnEntityEvent(org.spongepowered.api.event.entity.SpawnEntityEvent) EntityItem(net.minecraft.entity.item.EntityItem)

Example 15 with BlockSnapshot

use of org.spongepowered.api.block.BlockSnapshot in project SpongeCommon by SpongePowered.

the class GeneralPhase method processBlockTransactionListsPost.

/**
 *  @param snapshotsToProcess
 * @param unwindingState
 * @param unwinding
 */
@SuppressWarnings({ "unchecked" })
public static void processBlockTransactionListsPost(PhaseContext<?> postContext, List<BlockSnapshot> snapshotsToProcess, IPhaseState<?> unwindingState, PhaseContext<?> unwinding) {
    final List<Transaction<BlockSnapshot>> invalidTransactions = new ArrayList<>();
    ImmutableList<Transaction<BlockSnapshot>>[] transactionArrays = new ImmutableList[TrackingUtil.EVENT_COUNT];
    ImmutableList.Builder<Transaction<BlockSnapshot>>[] transactionBuilders = new ImmutableList.Builder[TrackingUtil.EVENT_COUNT];
    for (int i = 0; i < TrackingUtil.EVENT_COUNT; i++) {
        transactionBuilders[i] = new ImmutableList.Builder<>();
    }
    final List<ChangeBlockEvent> blockEvents = new ArrayList<>();
    for (BlockSnapshot snapshot : snapshotsToProcess) {
        // This processes each snapshot to assign them to the correct event in the next area, with the
        // correct builder array entry.
        TrackingUtil.TRANSACTION_PROCESSOR.apply(transactionBuilders).accept(TrackingUtil.TRANSACTION_CREATION.apply(snapshot));
    }
    for (int i = 0; i < TrackingUtil.EVENT_COUNT; i++) {
        // Build each event array
        transactionArrays[i] = transactionBuilders[i].build();
    }
    // Clear captured snapshots after processing them
    postContext.getCapturedBlocksOrEmptyList().clear();
    final ChangeBlockEvent[] mainEvents = new ChangeBlockEvent[BlockChange.values().length];
    // This likely needs to delegate to the phase in the event we don't use the source object as the main object causing the block changes
    // case in point for WorldTick event listeners since the players are captured non-deterministically
    // Creates the block events accordingly to the transaction arrays
    TrackingUtil.iterateChangeBlockEvents(transactionArrays, blockEvents, mainEvents);
    // We create the post event and of course post it in the method, regardless whether any transactions are invalidated or not
    final ChangeBlockEvent.Post postEvent = TrackingUtil.throwMultiEventsAndCreatePost(transactionArrays, blockEvents, mainEvents);
    if (postEvent == null) {
        // Means that we have had no actual block changes apparently?
        return;
    }
    // transactions of the preceeding block events)
    for (ChangeBlockEvent blockEvent : blockEvents) {
        // Need to only check if the event is cancelled, If it is, restore
        if (blockEvent.isCancelled()) {
            // Don't restore the transactions just yet, since we're just marking them as invalid for now
            for (Transaction<BlockSnapshot> transaction : Lists.reverse(blockEvent.getTransactions())) {
                transaction.setValid(false);
            }
        }
    }
    // Finally check the post event
    if (postEvent.isCancelled()) {
        // Of course, if post is cancelled, just mark all transactions as invalid.
        for (Transaction<BlockSnapshot> transaction : postEvent.getTransactions()) {
            transaction.setValid(false);
        }
    }
    // Because after, we will restore all the invalid transactions in reverse order.
    for (Transaction<BlockSnapshot> transaction : postEvent.getTransactions()) {
        if (!transaction.isValid()) {
            invalidTransactions.add(transaction);
        }
    }
    if (!invalidTransactions.isEmpty()) {
        // or the events were cancelled), again in reverse order of which they were received.
        for (Transaction<BlockSnapshot> transaction : Lists.reverse(invalidTransactions)) {
            transaction.getOriginal().restore(true, BlockChangeFlags.NONE);
            if (unwindingState.tracksBlockSpecificDrops()) {
                // Cancel any block drops or harvests for the block change.
                // This prevents unnecessary spawns.
                final Location<World> location = transaction.getOriginal().getLocation().orElse(null);
                if (location != null) {
                    // Cancel any block drops performed, avoids any item drops, regardless
                    final BlockPos pos = ((IMixinLocation) (Object) location).getBlockPos();
                    postContext.getBlockDropSupplier().removeAllIfNotEmpty(pos);
                }
            }
        }
        invalidTransactions.clear();
    }
    performPostBlockAdditions(postContext, postEvent.getTransactions(), unwindingState, unwinding);
}
Also used : IMixinLocation(org.spongepowered.common.interfaces.world.IMixinLocation) ImmutableList(com.google.common.collect.ImmutableList) ArrayList(java.util.ArrayList) SpongeBlockSnapshot(org.spongepowered.common.block.SpongeBlockSnapshot) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) World(org.spongepowered.api.world.World) ChangeBlockEvent(org.spongepowered.api.event.block.ChangeBlockEvent) Transaction(org.spongepowered.api.data.Transaction) BlockPos(net.minecraft.util.math.BlockPos)

Aggregations

BlockSnapshot (org.spongepowered.api.block.BlockSnapshot)144 World (org.spongepowered.api.world.World)64 Listener (org.spongepowered.api.event.Listener)61 Location (org.spongepowered.api.world.Location)27 BlockState (org.spongepowered.api.block.BlockState)21 BlockPos (net.minecraft.util.math.BlockPos)20 Entity (org.spongepowered.api.entity.Entity)20 CauseStackManager (org.spongepowered.api.event.CauseStackManager)19 Region (br.net.fabiozumbi12.RedProtect.Sponge.Region)18 IBlockState (net.minecraft.block.state.IBlockState)18 Transaction (org.spongepowered.api.data.Transaction)17 BlockType (org.spongepowered.api.block.BlockType)15 Direction (org.spongepowered.api.util.Direction)15 SpongeBlockSnapshot (org.spongepowered.common.block.SpongeBlockSnapshot)14 Vector3d (com.flowpowered.math.vector.Vector3d)13 ArrayList (java.util.ArrayList)13 ChangeBlockEvent (org.spongepowered.api.event.block.ChangeBlockEvent)13 Player (org.spongepowered.api.entity.living.player.Player)12 User (org.spongepowered.api.entity.living.player.User)11 LocatableBlock (org.spongepowered.api.world.LocatableBlock)11