diff --git a/connector/src/main/java/org/geysermc/connector/entity/player/SkullPlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/player/SkullPlayerEntity.java index 60b7c70e3..61b5af38e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/player/SkullPlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/player/SkullPlayerEntity.java @@ -34,7 +34,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; import lombok.Getter; -import lombok.Setter; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -43,17 +42,16 @@ import org.geysermc.connector.network.session.GeyserSession; * custom player skulls in Bedrock. */ public class SkullPlayerEntity extends PlayerEntity { - /** * Stores the block state that the skull is associated with. Used to determine if the block in the skull's position * has changed */ @Getter - @Setter - private int blockState; + private final int blockState; - public SkullPlayerEntity(GameProfile gameProfile, long geyserId, Vector3f position, Vector3f rotation) { + public SkullPlayerEntity(GameProfile gameProfile, long geyserId, Vector3f position, Vector3f rotation, int blockState) { super(gameProfile, 0, geyserId, position, Vector3f.ZERO, rotation); + this.blockState = blockState; setPlayerList(false); //Set bounding box to almost nothing so the skull is able to be broken and not cause entity to cast a shadow diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index aa3d33ca8..2034c5c7e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -883,7 +883,7 @@ public class GeyserSession implements CommandSender { try { runnable.run(); } catch (Throwable e) { - e.printStackTrace(); + connector.getLogger().error("Error thrown in " + getName() + "'s event loop!", e); } }); } @@ -896,7 +896,7 @@ public class GeyserSession implements CommandSender { try { runnable.run(); } catch (Throwable e) { - e.printStackTrace(); + connector.getLogger().error("Error thrown in " + getName() + "'s event loop!", e); } }, duration, timeUnit); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java index 491f54381..c5551651b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java @@ -123,36 +123,45 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements Vector3i blockPosition = Vector3i.from(posX, posY, posZ); Vector3f entityPosition = Vector3f.from(x, y, z); Vector3f entityRotation = Vector3f.from(rotation, 0, rotation); - long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); getProfile(tag).whenComplete((gameProfile, throwable) -> { if (gameProfile == null) { - session.getConnector().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition.toString() + " " + tag.toString()); + session.getConnector().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition + " " + tag); return; } - SkullPlayerEntity existingSkull = session.getSkullCache().get(blockPosition); - if (existingSkull != null) { - // Ensure that two skulls can't spawn on the same point - existingSkull.despawnEntity(session, blockPosition); - } - - SkullPlayerEntity player = new SkullPlayerEntity(gameProfile, geyserId, entityPosition, entityRotation); - player.setBlockState(blockState); - - // Cache entity - session.getSkullCache().put(blockPosition, player); - - // Only send to session if we are initialized, otherwise it will happen then. - if (session.getUpstream().isInitialized()) { - player.spawnEntity(session); - - SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.scheduleInEventLoop(() -> { - // Delay to minimize split-second "player" pop-in - player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false); - player.updateBedrockMetadata(session); - }, 250, TimeUnit.MILLISECONDS))); + if (session.getEventLoop().inEventLoop()) { + spawnPlayer(session, gameProfile, blockPosition, entityPosition, entityRotation, blockState); + } else { + session.executeInEventLoop(() -> spawnPlayer(session, gameProfile, blockPosition, entityPosition, entityRotation, blockState)); } }); } + + private static void spawnPlayer(GeyserSession session, GameProfile profile, Vector3i blockPosition, + Vector3f entityPosition, Vector3f entityRotation, int blockState) { + long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); + + SkullPlayerEntity existingSkull = session.getSkullCache().get(blockPosition); + if (existingSkull != null) { + // Ensure that two skulls can't spawn on the same point + existingSkull.despawnEntity(session, blockPosition); + } + + SkullPlayerEntity player = new SkullPlayerEntity(profile, geyserId, entityPosition, entityRotation, blockState); + + // Cache entity + session.getSkullCache().put(blockPosition, player); + + // Only send to session if we are initialized, otherwise it will happen then. + if (session.getUpstream().isInitialized()) { + player.spawnEntity(session); + + SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.scheduleInEventLoop(() -> { + // Delay to minimize split-second "player" pop-in + player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false); + player.updateBedrockMetadata(session); + }, 250, TimeUnit.MILLISECONDS))); + } + } }