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);
}
}
}
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;
}
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);
}
}
});
}
}
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);
}
}
});
}
}
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);
}
Aggregations