Ensure skull operations are done on the player thread

This commit is contained in:
Camotoy 2021-08-21 19:38:13 -04:00
parent ab540b1951
commit a1d167d5f1
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
3 changed files with 37 additions and 30 deletions

View file

@ -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

View file

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

View file

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