use of org.spongepowered.common.interfaces.world.IMixinWorldServer in project SpongeCommon by SpongePowered.
the class MixinMinecraftServer method onUpdateTimeLightAndEntitiesHead.
// All chunk unload queuing needs to be processed BEFORE the future tasks are run as mods/plugins may have tasks that request chunks.
// This prevents a situation where a chunk is requested to load then unloads at end of tick.
@Inject(method = "updateTimeLightAndEntities", at = @At("HEAD"))
public void onUpdateTimeLightAndEntitiesHead(CallbackInfo ci) {
for (int i = 0; i < this.worlds.length; ++i) {
WorldServer worldServer = this.worlds[i];
// ChunkGC needs to be processed before a world tick in order to guarantee any chunk queued for unload
// can still be marked active and avoid unload if accessed during the same tick.
// Note: This injection must come before Forge's pre world tick event or it will cause issues with mods.
IMixinWorldServer spongeWorld = (IMixinWorldServer) worldServer;
if (spongeWorld.getChunkGCTickInterval() > 0) {
spongeWorld.doChunkGC();
}
// Moved from PlayerChunkMap to avoid chunks from unloading after being requested in same tick
if (worldServer.getPlayerChunkMap().players.isEmpty()) {
WorldProvider worldprovider = worldServer.provider;
if (!worldprovider.canRespawnHere()) {
worldServer.getChunkProvider().queueUnloadAll();
}
}
}
}
use of org.spongepowered.common.interfaces.world.IMixinWorldServer in project SpongeCommon by SpongePowered.
the class MixinPlayerList method initializeConnectionToPlayer.
public void initializeConnectionToPlayer(NetworkManager netManager, EntityPlayerMP playerIn, @Nullable NetHandlerPlayServer handler) {
GameProfile gameprofile = playerIn.getGameProfile();
PlayerProfileCache playerprofilecache = this.mcServer.getPlayerProfileCache();
GameProfile gameprofile1 = playerprofilecache.getProfileByUUID(gameprofile.getId());
String s = gameprofile1 == null ? gameprofile.getName() : gameprofile1.getName();
playerprofilecache.addEntry(gameprofile);
// Sponge start - save changes to offline User before reading player data
SpongeUser user = (SpongeUser) ((IMixinEntityPlayerMP) playerIn).getUserObject();
if (SpongeUser.dirtyUsers.contains(user)) {
user.save();
}
// Sponge end
NBTTagCompound nbttagcompound = this.readPlayerDataFromFile(playerIn);
WorldServer worldServer = this.mcServer.getWorld(playerIn.dimension);
int actualDimensionId = ((IMixinWorldServer) worldServer).getDimensionId();
BlockPos spawnPos = null;
// Join data
Optional<Instant> firstJoined = SpongePlayerDataHandler.getFirstJoined(playerIn.getUniqueID());
Instant lastJoined = Instant.now();
SpongePlayerDataHandler.setPlayerInfo(playerIn.getUniqueID(), firstJoined.orElse(lastJoined), lastJoined);
if (actualDimensionId != playerIn.dimension) {
SpongeImpl.getLogger().warn("Player [{}] has attempted to login to unloaded world [{}]. This is not safe so we have moved them to " + "the default world's spawn point.", playerIn.getName(), playerIn.dimension);
if (!firstJoined.isPresent()) {
spawnPos = SpongeImplHooks.getRandomizedSpawnPoint(worldServer);
} else {
spawnPos = worldServer.getSpawnPoint();
}
playerIn.dimension = actualDimensionId;
playerIn.setPosition(spawnPos.getX(), spawnPos.getY(), spawnPos.getZ());
}
// Sponge start - fire login event
@Nullable String kickReason = allowUserToConnect(netManager.getRemoteAddress(), gameprofile);
Text disconnectMessage;
if (kickReason != null) {
disconnectMessage = SpongeTexts.fromLegacy(kickReason);
} else {
disconnectMessage = Text.of("You are not allowed to log in to this server.");
}
Player player = (Player) playerIn;
Transform<World> fromTransform = player.getTransform().setExtent((World) worldServer);
Sponge.getCauseStackManager().pushCause(player);
ClientConnectionEvent.Login loginEvent = SpongeEventFactory.createClientConnectionEventLogin(Sponge.getCauseStackManager().getCurrentCause(), fromTransform, fromTransform, (RemoteConnection) netManager, new MessageEvent.MessageFormatter(disconnectMessage), (org.spongepowered.api.profile.GameProfile) gameprofile, player, false);
if (kickReason != null) {
loginEvent.setCancelled(true);
}
if (SpongeImpl.postEvent(loginEvent)) {
Sponge.getCauseStackManager().popCause();
disconnectClient(netManager, loginEvent.isMessageCancelled() ? Optional.empty() : Optional.of(loginEvent.getMessage()), gameprofile);
return;
}
Sponge.getCauseStackManager().popCause();
// Sponge end
worldServer = (WorldServer) loginEvent.getToTransform().getExtent();
double x = loginEvent.getToTransform().getPosition().getX();
double y = loginEvent.getToTransform().getPosition().getY();
double z = loginEvent.getToTransform().getPosition().getZ();
float pitch = (float) loginEvent.getToTransform().getPitch();
float yaw = (float) loginEvent.getToTransform().getYaw();
playerIn.dimension = ((IMixinWorldServer) worldServer).getDimensionId();
playerIn.setWorld(worldServer);
playerIn.interactionManager.setWorld((WorldServer) playerIn.world);
playerIn.setPositionAndRotation(x, y, z, yaw, pitch);
// make sure the chunk is loaded for login
worldServer.getChunkProvider().loadChunk(loginEvent.getToTransform().getLocation().getChunkPosition().getX(), loginEvent.getToTransform().getLocation().getChunkPosition().getZ());
// Sponge end
String s1 = "local";
if (netManager.getRemoteAddress() != null) {
s1 = netManager.getRemoteAddress().toString();
}
final WorldInfo worldinfo = worldServer.getWorldInfo();
final BlockPos spawnBlockPos = worldServer.getSpawnPoint();
this.setPlayerGameTypeBasedOnOther(playerIn, null, worldServer);
// Sponge start
if (handler == null) {
// Create the handler here (so the player's gets set)
handler = new NetHandlerPlayServer(this.mcServer, netManager, playerIn);
}
playerIn.connection = handler;
// Sponge end
// Support vanilla clients logging into custom dimensions
final int dimensionId = WorldManager.getClientDimensionId(playerIn, worldServer);
// Send dimension registration
WorldManager.sendDimensionRegistration(playerIn, worldServer.provider);
handler.sendPacket(new SPacketJoinGame(playerIn.getEntityId(), playerIn.interactionManager.getGameType(), worldinfo.isHardcoreModeEnabled(), dimensionId, worldServer.getDifficulty(), this.getMaxPlayers(), worldinfo.getTerrainType(), worldServer.getGameRules().getBoolean("reducedDebugInfo")));
handler.sendPacket(new SPacketCustomPayload("MC|Brand", (new PacketBuffer(Unpooled.buffer())).writeString(this.getServerInstance().getServerModName())));
handler.sendPacket(new SPacketServerDifficulty(worldinfo.getDifficulty(), worldinfo.isDifficultyLocked()));
handler.sendPacket(new SPacketSpawnPosition(spawnBlockPos));
handler.sendPacket(new SPacketPlayerAbilities(playerIn.capabilities));
handler.sendPacket(new SPacketHeldItemChange(playerIn.inventory.currentItem));
this.updatePermissionLevel(playerIn);
playerIn.getStatFile().markAllDirty();
playerIn.getRecipeBook().init(playerIn);
this.mcServer.refreshStatusNextTick();
handler.setPlayerLocation(x, y, z, yaw, pitch);
this.playerLoggedIn(playerIn);
// Sponge start - add world name to message
LOGGER.info(playerIn.getName() + "[" + s1 + "] logged in with entity id " + playerIn.getEntityId() + " in " + worldServer.getWorldInfo().getWorldName() + "(" + ((IMixinWorldServer) worldServer).getDimensionId() + ") at (" + playerIn.posX + ", " + playerIn.posY + ", " + playerIn.posZ + ")");
// Sponge end
this.updateTimeAndWeatherForPlayer(playerIn, worldServer);
// Sponge Start - Use the server's ResourcePack object
Optional<ResourcePack> pack = ((Server) this.mcServer).getDefaultResourcePack();
if (pack.isPresent()) {
((Player) playerIn).sendResourcePack(pack.get());
}
// Sponge End
// Sponge Start
//
// This sends the objective/score creation packets
// to the player, without attempting to remove them from their
// previous scoreboard (which is set in a field initializer).
// This allows #getWorldScoreboard to function
// as normal, without causing issues when it is initialized on the client.
((IMixinEntityPlayerMP) playerIn).initScoreboard();
for (PotionEffect potioneffect : playerIn.getActivePotionEffects()) {
handler.sendPacket(new SPacketEntityEffect(playerIn.getEntityId(), potioneffect));
}
if (nbttagcompound != null) {
if (nbttagcompound.hasKey("RootVehicle", 10)) {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompoundTag("RootVehicle");
Entity entity2 = AnvilChunkLoader.readWorldEntity(nbttagcompound1.getCompoundTag("Entity"), worldServer, true);
if (entity2 != null) {
UUID uuid = nbttagcompound1.getUniqueId("Attach");
if (entity2.getUniqueID().equals(uuid)) {
playerIn.startRiding(entity2, true);
} else {
for (Entity entity : entity2.getRecursivePassengers()) {
if (entity.getUniqueID().equals(uuid)) {
playerIn.startRiding(entity, true);
break;
}
}
}
if (!playerIn.isRiding()) {
LOGGER.warn("Couldn\'t reattach entity to player");
worldServer.removeEntityDangerously(entity2);
for (Entity entity3 : entity2.getRecursivePassengers()) {
worldServer.removeEntityDangerously(entity3);
}
}
}
} else if (nbttagcompound.hasKey("Riding", 10)) {
Entity entity1 = AnvilChunkLoader.readWorldEntity(nbttagcompound.getCompoundTag("Riding"), worldServer, true);
if (entity1 != null) {
playerIn.startRiding(entity1, true);
}
}
}
playerIn.addSelfToInternalCraftingInventory();
TextComponentTranslation chatcomponenttranslation;
if (!playerIn.getName().equalsIgnoreCase(s)) {
chatcomponenttranslation = new TextComponentTranslation("multiplayer.player.joined.renamed", playerIn.getDisplayName(), s);
} else {
chatcomponenttranslation = new TextComponentTranslation("multiplayer.player.joined", playerIn.getDisplayName());
}
chatcomponenttranslation.getStyle().setColor(TextFormatting.YELLOW);
// Fire PlayerJoinEvent
Text originalMessage = SpongeTexts.toText(chatcomponenttranslation);
MessageChannel originalChannel = player.getMessageChannel();
Sponge.getCauseStackManager().pushCause(player);
final ClientConnectionEvent.Join event = SpongeEventFactory.createClientConnectionEventJoin(Sponge.getCauseStackManager().getCurrentCause(), originalChannel, Optional.of(originalChannel), new MessageEvent.MessageFormatter(originalMessage), player, false);
SpongeImpl.postEvent(event);
Sponge.getCauseStackManager().popCause();
// Send to the channel
if (!event.isMessageCancelled()) {
event.getChannel().ifPresent(channel -> channel.send(player, event.getMessage()));
}
// Sponge end
}
use of org.spongepowered.common.interfaces.world.IMixinWorldServer in project SpongeCommon by SpongePowered.
the class MixinPlayerList method recreatePlayerEntity.
/**
* @author Zidane - June 13th, 2015
* @author simon816 - June 24th, 2015
* @author Zidane - March 29th, 2016
* @author gabizou - June 5th, 2016 - Update for teleportation changes to keep the same player.
*
* @reason - Direct respawning players to use Sponge events
* and process appropriately.
*
* @param playerIn The player being respawned/created
* @param targetDimension The target dimension
* @param conqueredEnd Whether the end was conquered
* @return The new player
*/
@Overwrite
public EntityPlayerMP recreatePlayerEntity(EntityPlayerMP playerIn, int targetDimension, boolean conqueredEnd) {
// UNLESS comming back from the end.
if (!conqueredEnd && targetDimension == 0) {
targetDimension = playerIn.dimension;
}
if (playerIn.isBeingRidden()) {
playerIn.removePassengers();
}
if (playerIn.isRiding()) {
playerIn.dismountRidingEntity();
}
final Player player = (Player) playerIn;
final Transform<World> fromTransform = player.getTransform();
WorldServer worldServer = this.mcServer.getWorld(targetDimension);
Transform<World> toTransform = new Transform<>(EntityUtil.getPlayerRespawnLocation(playerIn, worldServer), Vector3d.ZERO, Vector3d.ZERO);
targetDimension = ((IMixinWorldServer) toTransform.getExtent()).getDimensionId();
Location<World> location = toTransform.getLocation();
// If coming from end, fire a teleport event for plugins
if (conqueredEnd) {
// When leaving the end, players are never placed inside the teleporter but instead "respawned" in the target world
MoveEntityEvent.Teleport teleportEvent = EntityUtil.handleDisplaceEntityTeleportEvent(playerIn, location);
if (teleportEvent.isCancelled()) {
playerIn.queuedEndExit = false;
return playerIn;
}
toTransform = teleportEvent.getToTransform();
location = toTransform.getLocation();
}
// Keep players out of blocks
Vector3d tempPos = player.getLocation().getPosition();
playerIn.setPosition(location.getX(), location.getY(), location.getZ());
while (!((WorldServer) location.getExtent()).getCollisionBoxes(playerIn, playerIn.getEntityBoundingBox()).isEmpty()) {
playerIn.setPosition(playerIn.posX, playerIn.posY + 1.0D, playerIn.posZ);
location = location.add(0, 1, 0);
}
playerIn.setPosition(tempPos.getX(), tempPos.getY(), tempPos.getZ());
// ### PHASE 2 ### Remove player from current dimension
playerIn.getServerWorld().getEntityTracker().removePlayerFromTrackers(playerIn);
playerIn.getServerWorld().getEntityTracker().untrack(playerIn);
playerIn.getServerWorld().getPlayerChunkMap().removePlayer(playerIn);
this.playerEntityList.remove(playerIn);
this.mcServer.getWorld(playerIn.dimension).removeEntityDangerously(playerIn);
final BlockPos bedPos = SpongeImplHooks.getBedLocation(playerIn, targetDimension);
// ### PHASE 3 ### Reset player (if applicable)
// Recreate the player object in order to support Forge's PlayerEvent.Clone
PlayerInteractionManager playerinteractionmanager;
if (this.mcServer.isDemo()) {
playerinteractionmanager = new DemoPlayerInteractionManager(this.mcServer.getWorld(targetDimension));
} else {
playerinteractionmanager = new PlayerInteractionManager(this.mcServer.getWorld(targetDimension));
}
EntityPlayerMP newPlayer = new EntityPlayerMP(SpongeImpl.getServer(), worldServer, playerIn.getGameProfile(), playerinteractionmanager);
newPlayer.connection = playerIn.connection;
newPlayer.copyFrom(playerIn, conqueredEnd);
// set player dimension for RespawnPlayerEvent
newPlayer.dimension = targetDimension;
newPlayer.setEntityId(playerIn.getEntityId());
newPlayer.setCommandStats(playerIn);
newPlayer.setPrimaryHand(playerIn.getPrimaryHand());
// over to the new player
if (bedPos != null) {
newPlayer.setSpawnPoint(bedPos, playerIn.isSpawnForced());
}
for (String s : playerIn.getTags()) {
newPlayer.addTag(s);
}
this.setPlayerGameTypeBasedOnOther(newPlayer, playerIn, worldServer);
newPlayer.setSneaking(false);
// update to safe location
toTransform = toTransform.setLocation(location);
// ### PHASE 4 ### Fire event and set new location on the player
Sponge.getCauseStackManager().pushCause(newPlayer);
final RespawnPlayerEvent event = SpongeEventFactory.createRespawnPlayerEvent(Sponge.getCauseStackManager().getCurrentCause(), fromTransform, toTransform, (Player) playerIn, (Player) newPlayer, EntityUtil.tempIsBedSpawn, !conqueredEnd);
EntityUtil.tempIsBedSpawn = false;
SpongeImpl.postEvent(event);
Sponge.getCauseStackManager().popCause();
((IMixinEntity) player).setLocationAndAngles(event.getToTransform());
toTransform = event.getToTransform();
location = toTransform.getLocation();
if (!(location.getExtent() instanceof WorldServer)) {
SpongeImpl.getLogger().warn("Location set in PlayerRespawnEvent was invalid, using original location instead");
location = event.getFromTransform().getLocation();
}
worldServer = (WorldServer) location.getExtent();
final IMixinWorldServer mixinWorldServer = (IMixinWorldServer) worldServer;
// Set the dimension again in case a plugin changed the target world during RespawnPlayerEvent
newPlayer.dimension = mixinWorldServer.getDimensionId();
newPlayer.setWorld(worldServer);
newPlayer.interactionManager.setWorld(worldServer);
worldServer.getChunkProvider().loadChunk((int) location.getX() >> 4, (int) location.getZ() >> 4);
// ### PHASE 5 ### Respawn player in new world
// Support vanilla clients logging into custom dimensions
final int dimensionId = WorldManager.getClientDimensionId(newPlayer, worldServer);
// Send dimension registration
if (((IMixinEntityPlayerMP) newPlayer).usesCustomClient()) {
WorldManager.sendDimensionRegistration(newPlayer, worldServer.provider);
} else {
// Force vanilla client to refresh its chunk cache if same dimension type
if (fromTransform.getExtent() != worldServer && fromTransform.getExtent().getDimension().getType() == toTransform.getExtent().getDimension().getType()) {
newPlayer.connection.sendPacket(new SPacketRespawn((dimensionId >= 0 ? -1 : 0), worldServer.getDifficulty(), worldServer.getWorldInfo().getTerrainType(), newPlayer.interactionManager.getGameType()));
}
}
newPlayer.connection.sendPacket(new SPacketRespawn(dimensionId, worldServer.getDifficulty(), worldServer.getWorldInfo().getTerrainType(), newPlayer.interactionManager.getGameType()));
newPlayer.connection.setPlayerLocation(location.getX(), location.getY(), location.getZ(), (float) toTransform.getYaw(), (float) toTransform.getPitch());
final BlockPos spawnLocation = worldServer.getSpawnPoint();
newPlayer.connection.sendPacket(new SPacketSpawnPosition(spawnLocation));
newPlayer.connection.sendPacket(new SPacketSetExperience(newPlayer.experience, newPlayer.experienceTotal, newPlayer.experienceLevel));
this.updateTimeAndWeatherForPlayer(newPlayer, worldServer);
this.updatePermissionLevel(newPlayer);
worldServer.getPlayerChunkMap().addPlayer(newPlayer);
org.spongepowered.api.entity.Entity spongeEntity = (org.spongepowered.api.entity.Entity) newPlayer;
((org.spongepowered.api.world.World) worldServer).spawnEntity(spongeEntity);
this.playerEntityList.add(newPlayer);
this.uuidToPlayerMap.put(newPlayer.getUniqueID(), newPlayer);
newPlayer.addSelfToInternalCraftingInventory();
// Update reducedDebugInfo game rule
newPlayer.connection.sendPacket(new SPacketEntityStatus(newPlayer, worldServer.getGameRules().getBoolean(DefaultGameRules.REDUCED_DEBUG_INFO) ? (byte) 22 : 23));
for (PotionEffect potioneffect : newPlayer.getActivePotionEffects()) {
newPlayer.connection.sendPacket(new SPacketEntityEffect(newPlayer.getEntityId(), potioneffect));
}
((IMixinEntityPlayerMP) newPlayer).refreshScaledHealth();
return newPlayer;
}
use of org.spongepowered.common.interfaces.world.IMixinWorldServer 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.common.interfaces.world.IMixinWorldServer 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;
}
Aggregations