7373import net .minecraft .world .level .block .state .BlockState ;
7474import net .minecraft .world .level .chunk .ChunkAccess ;
7575import net .minecraft .world .level .portal .DimensionTransition ;
76+ import net .minecraft .world .level .portal .DimensionTransition .PostDimensionTransition ;
7677import net .minecraft .world .phys .AABB ;
7778import net .minecraft .world .phys .Vec3 ;
7879import net .neoforged .neoforge .common .NeoForge ;
@@ -88,6 +89,11 @@ public class TileEntityTeleporter extends TileEntityMekanism implements IChunkLo
8889 private static final TeleportInfo NO_FRAME = new TeleportInfo ((byte ) 2 , null , Collections .emptyList ());
8990 private static final TeleportInfo NO_LINK = new TeleportInfo ((byte ) 3 , null , Collections .emptyList ());
9091 private static final TeleportInfo NOT_ENOUGH_ENERGY = new TeleportInfo ((byte ) 4 , null , Collections .emptyList ());
92+ private static final PostDimensionTransition AWARD_ADVANCEMENT = entity -> {
93+ if (entity instanceof ServerPlayer player ) {
94+ MekanismCriteriaTriggers .TELEPORT .value ().trigger (player );
95+ }
96+ };
9197
9298 public final Set <UUID > didTeleport = new ObjectOpenHashSet <>();
9399 private final Predicate <Entity > SAME_DIMENSION_TARGET = entity -> canTeleportEntity (entity , null );
@@ -134,43 +140,37 @@ protected IInventorySlotHolder getInitialInventory(IContentsListener listener) {
134140 }
135141
136142 private boolean canTeleportEntity (Entity entity , @ Nullable Level destinationLevel ) {
137- if (entity .isSpectator () || entity .isPassenger ( ) || entity instanceof PartEntity || entity .getType ().is (Tags .EntityTypes .TELEPORTING_NOT_SUPPORTED )) {
143+ if (entity .isSpectator () || ! entity .canUsePortal ( false ) || entity instanceof PartEntity || entity .getType ().is (Tags .EntityTypes .TELEPORTING_NOT_SUPPORTED )) {
138144 return false ;
139145 } else if (destinationLevel != null && !entity .canChangeDimensions (entity .level (), destinationLevel )) {
140146 return false ;
141147 }
142148 return !didTeleport .contains (entity .getUUID ());
143149 }
144150
145- public static void alignPlayer (ServerPlayer player , MekanismTeleportEvent .Teleporter event , TileEntityTeleporter teleporter ) {
146- alignPlayer (player , BlockPos .containing (event .getTarget ()), teleporter );
147- }
148-
149- private static void alignPlayer (ServerPlayer player , BlockPos target , TileEntityTeleporter teleporter ) {
151+ private static float alignPlayer (ServerPlayer player , BlockPos target , TileEntityTeleporter teleporter ) {
150152 Direction side = null ;
151153 if (teleporter .frameDirection != null && teleporter .frameDirection .getAxis ().isHorizontal ()) {
152154 //If the frame is horizontal always face towards the other portion of the frame
153155 side = teleporter .frameDirection ;
154156 } else {
155157 BlockPos .MutableBlockPos mutable = new BlockPos .MutableBlockPos ();
158+ Level level = teleporter .getWorldNN ();
156159 for (Direction iterSide : EnumUtils .HORIZONTAL_DIRECTIONS ) {
157160 mutable .setWithOffset (target , iterSide );
158- if (player . level () .isEmptyBlock (mutable )) {
161+ if (level .isEmptyBlock (mutable )) {
159162 side = iterSide ;
160163 break ;
161164 }
162165 }
163166 }
164- float yaw = player .getYRot ();
165- if (side != null ) {
166- switch (side ) {
167- case NORTH -> yaw = 180 ;
168- case SOUTH -> yaw = 0 ;
169- case WEST -> yaw = 90 ;
170- case EAST -> yaw = 270 ;
171- }
172- }
173- player .connection .teleport (player .getX (), player .getY (), player .getZ (), yaw , player .getXRot ());
167+ return switch (side ) {
168+ case NORTH -> 180 ;
169+ case SOUTH -> 0 ;
170+ case WEST -> 90 ;
171+ case EAST -> 270 ;
172+ case null , default -> player .getYRot ();
173+ };
174174 }
175175
176176 @ Override
@@ -264,7 +264,7 @@ private TeleportInfo canTeleport(@Nullable TeleporterFrequency frequency) {
264264 return NO_LINK ;
265265 }
266266 targetWorld = server .getLevel (closestCoords .dimension ());
267- if (targetWorld == null ) {//In theory should not happen
267+ if (targetWorld == null || ! server . isLevelEnabled ( targetWorld ) ) {//In theory should not happen
268268 return NO_LINK ;
269269 }
270270 }
@@ -337,11 +337,9 @@ private void teleport(TeleporterFrequency frequency, TeleportInfo teleportInfo)
337337 double oldX = entity .getX ();
338338 double oldY = entity .getY ();
339339 double oldZ = entity .getZ ();
340- Entity teleportedEntity = teleportEntityTo (entity , teleWorld , event , true );
341- if (teleportedEntity instanceof ServerPlayer player ) {
342- alignPlayer (player , event , teleporter );
343- MekanismCriteriaTriggers .TELEPORT .value ().trigger (player );
344- }
340+ Entity teleportedEntity = teleportEntityTo (entity , teleWorld , teleporter , event , true , AWARD_ADVANCEMENT );
341+ //Note: The below logic isn't part of a PostDimensionTransition as the transition applies to all entities and passengers,
342+ // and we want the below logic to only happen once
345343 for (GlobalPos coords : activeCoords ) {
346344 Level world = level .dimension () == coords .dimension () ? level : currentServer .getLevel (coords .dimension ());
347345 TileEntityTeleporter tile = WorldUtils .getTileEntity (TileEntityTeleporter .class , world , coords .pos ());
@@ -351,13 +349,7 @@ private void teleport(TeleporterFrequency frequency, TeleportInfo teleportInfo)
351349 }
352350 energyContainer .extract (energyCost , Action .EXECUTE , AutomationType .INTERNAL );
353351 if (teleportedEntity != null ) {
354- SoundEvent sound = switch (teleportedEntity ) {
355- case Player player -> SoundEvents .PLAYER_TELEPORT ;
356- case Fox fox -> SoundEvents .FOX_TELEPORT ;
357- case Shulker shulker -> SoundEvents .SHULKER_TELEPORT ;
358- //Fall back to enderman teleporting sound
359- default -> SoundEvents .ENDERMAN_TELEPORT ;
360- };
352+ SoundEvent sound = getTeleportSound (teleportedEntity );
361353 if (level != teleportedEntity .level () || teleportedEntity .distanceToSqr (oldX , oldY , oldZ ) >= 25 ) {
362354 //If the entity teleported over 5 blocks, play the sound at both the destination and the source
363355 level .playSound (null , oldX , oldY , oldZ , sound , entity .getSoundSource ());
@@ -369,6 +361,16 @@ private void teleport(TeleporterFrequency frequency, TeleportInfo teleportInfo)
369361 }
370362 }
371363
364+ private static SoundEvent getTeleportSound (Entity entity ) {
365+ return switch (entity ) {
366+ case Player player -> SoundEvents .PLAYER_TELEPORT ;
367+ case Fox fox -> SoundEvents .FOX_TELEPORT ;
368+ case Shulker shulker -> SoundEvents .SHULKER_TELEPORT ;
369+ //Fall back to enderman teleporting sound
370+ default -> SoundEvents .ENDERMAN_TELEPORT ;
371+ };
372+ }
373+
372374 private void markTeleported (TileEntityTeleporter teleporter , Entity entity , boolean sameDimension , Level destinationWorld ) {
373375 if (sameDimension || entity .canChangeDimensions (entity .level (), destinationWorld )) {
374376 //Only mark the entity as teleported if it will teleport, it is in the same dimension or is able to change dimensions
@@ -381,11 +383,23 @@ private void markTeleported(TileEntityTeleporter teleporter, Entity entity, bool
381383 }
382384
383385 @ Nullable
384- public static Entity teleportEntityTo (Entity entity , Level targetWorld , MekanismTeleportEvent .Teleporter event , boolean persistMovement ) {
386+ public static Entity teleportEntityTo (Entity entity , Level targetWorld , TileEntityTeleporter target , MekanismTeleportEvent .Teleporter event , boolean persistMovement ,
387+ PostDimensionTransition transition ) {
385388 Vec3 destination = event .getTarget ();
389+ float yRot = entity .getYRot ();
390+ if (entity instanceof ServerPlayer player ) {
391+ //Align players with the output teleporter
392+ yRot = alignPlayer (player , BlockPos .containing (destination ), target );
393+ }
386394 if (!event .isTransDimensional ()) {
387395 Vec3 deltaMovement = entity .getDeltaMovement ();
388- entity .teleportTo (destination .x , destination .y , destination .z );
396+ if (entity instanceof ServerPlayer player ) {
397+ //Note: We can't use the normal teleportTo method for server players as they override it to not actually sync rotation
398+ // to the client
399+ player .connection .teleport (destination .x , destination .y , destination .z , yRot , entity .getXRot ());
400+ } else {
401+ entity .teleportTo (destination .x , destination .y , destination .z );
402+ }
389403 if (!entity .getPassengers ().isEmpty ()) {
390404 //Force re-apply any passengers so that players don't get "stuck" outside what they may be riding
391405 ((ServerChunkCache ) entity .level ().getChunkSource ()).broadcast (entity , new ClientboundSetPassengersPacket (entity ));
@@ -404,41 +418,17 @@ public static Entity teleportEntityTo(Entity entity, Level targetWorld, Mekanism
404418 //Force sync the delta movement to the client so that they don't stop moving due to the teleport and movement being client sided
405419 PacketDistributor .sendToPlayer (player , new PacketSetDeltaMovement (deltaMovement ));
406420 }
407- return entity ;
408- }
409- //Note: We grab the passengers here instead of in placeEntity as changeDimension starts by removing any passengers
410- List <Entity > passengers = entity .getPassengers ();
411- Level fromWorld = entity .level ();
412- ServerLevel serverLevel = (ServerLevel ) targetWorld ;
413- return entity .changeDimension (new DimensionTransition (serverLevel , destination , entity .getDeltaMovement (), entity .getYRot (), entity .getXRot (), target -> {
414- //Teleport all passengers to the other dimension and then make them start riding the entity again
415- for (Entity passenger : passengers ) {
416- teleportPassenger (fromWorld , serverLevel , destination , target , passenger );
421+ //Handle transition logic even though we didn't change dimensions
422+ if (transition != DimensionTransition .DO_NOTHING ) {
423+ for (Entity passenger : entity .getIndirectPassengers ()) {
424+ transition .onTransition (passenger );
425+ }
426+ transition .onTransition (entity );
417427 }
418- }));
419- }
420-
421- private static void teleportPassenger (Level fromWorld , ServerLevel destWorld , Vec3 destination , Entity repositionedEntity , Entity passenger ) {
422- if (!passenger .canChangeDimensions (fromWorld , destWorld )) {
423- //If the passenger can't change dimensions just let it peacefully stay after dismounting rather than trying to teleport it
424- return ;
428+ return entity ;
425429 }
426- //Note: We grab the passengers here instead of in placeEntity as changeDimension starts by removing any passengers
427- List <Entity > passengers = passenger .getPassengers ();
428- passenger .changeDimension (new DimensionTransition (destWorld , destination , passenger .getDeltaMovement (), passenger .getYRot (), passenger .getXRot (), target -> {
429- //TODO - 1.21: Test if this logic for making the entity not take damage works or if the post transition thing, is well past teleport and past damage
430- boolean invulnerable = target .isInvulnerable ();
431- //Make the entity invulnerable so that when we teleport it, it doesn't take damage
432- // we revert this state to the previous state after teleporting
433- target .setInvulnerable (true );
434- //Force our passenger to start riding the new entity again
435- target .startRiding (repositionedEntity , true );
436- //Teleport "nested" passengers
437- for (Entity p : passengers ) {
438- teleportPassenger (fromWorld , destWorld , destination , target , p );
439- }
440- target .setInvulnerable (invulnerable );
441- }));
430+ //player.connection.teleport(player.getX(), player.getY(), player.getZ(), yaw, player.getXRot());
431+ return entity .changeDimension (new DimensionTransition ((ServerLevel ) targetWorld , destination , entity .getDeltaMovement (), yRot , entity .getXRot (), transition ));
442432 }
443433
444434 private List <Entity > getToTeleport (boolean sameDimension , Level destinationLevel ) {
0 commit comments