diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java new file mode 100644 index 00000000..abb9016a --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.network.session.cache; + +import com.github.steveice10.mc.protocol.data.message.Message; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.EntityData; +import com.nukkitx.protocol.bedrock.packet.AddEntityPacket; +import com.nukkitx.protocol.bedrock.packet.BossEventPacket; +import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket; +import lombok.AllArgsConstructor; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.MessageUtils; + +@AllArgsConstructor +public class BossBar { + + private GeyserSession session; + + private long entityId; + private Message title; + private float health; + private int color; + private int overlay; + private int darkenSky; + + public void addBossBar() { + addBossEntity(); + updateBossBar(); + } + + public void updateBossBar() { + BossEventPacket bossEventPacket = new BossEventPacket(); + bossEventPacket.setBossUniqueEntityId(entityId); + bossEventPacket.setAction(BossEventPacket.Action.SHOW); + bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getClientData().getLanguageCode())); + bossEventPacket.setHealthPercentage(health); + bossEventPacket.setColor(color); //ignored by client + bossEventPacket.setOverlay(overlay); + bossEventPacket.setDarkenSky(darkenSky); + + session.getUpstream().sendPacket(bossEventPacket); + } + + public void updateTitle(Message title) { + this.title = title; + BossEventPacket bossEventPacket = new BossEventPacket(); + bossEventPacket.setBossUniqueEntityId(entityId); + bossEventPacket.setAction(BossEventPacket.Action.TITLE); + bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getClientData().getLanguageCode())); + + session.getUpstream().sendPacket(bossEventPacket); + } + + public void updateHealth(float health) { + this.health = health; + BossEventPacket bossEventPacket = new BossEventPacket(); + bossEventPacket.setBossUniqueEntityId(entityId); + bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE); + bossEventPacket.setHealthPercentage(health); + + session.getUpstream().sendPacket(bossEventPacket); + } + + public void removeBossBar() { + BossEventPacket bossEventPacket = new BossEventPacket(); + bossEventPacket.setBossUniqueEntityId(entityId); + bossEventPacket.setAction(BossEventPacket.Action.HIDE); + + session.getUpstream().sendPacket(bossEventPacket); + removeBossEntity(); + } + + /** + * Bedrock still needs an entity to display the BossBar.
+ * Just like 1.8 but it doesn't care about which entity + */ + private void addBossEntity() { + AddEntityPacket addEntityPacket = new AddEntityPacket(); + addEntityPacket.setUniqueEntityId(entityId); + addEntityPacket.setRuntimeEntityId(entityId); + addEntityPacket.setIdentifier("minecraft:creeper"); + addEntityPacket.setEntityType(33); + addEntityPacket.setPosition(session.getPlayerEntity().getPosition()); + addEntityPacket.setRotation(Vector3f.ZERO); + addEntityPacket.setMotion(Vector3f.ZERO); + addEntityPacket.getMetadata().put(EntityData.SCALE, 0.01F); // scale = 0 doesn't work? + + session.getUpstream().sendPacket(addEntityPacket); + } + + private void removeBossEntity() { + RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); + removeEntityPacket.setUniqueEntityId(entityId); + + session.getUpstream().sendPacket(removeEntityPacket); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java index f32ee2a5..f0b394fd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java @@ -48,7 +48,7 @@ public class EntityCache { private Long2ObjectMap entities = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); private Long2LongMap entityIdTranslations = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); private Map playerEntities = Collections.synchronizedMap(new HashMap<>()); - private Object2LongMap bossbars = new Object2LongOpenHashMap<>(); + private Map bossBars = Collections.synchronizedMap(new HashMap<>()); @Getter private AtomicLong nextEntityId = new AtomicLong(2L); @@ -116,24 +116,30 @@ public class EntityCache { playerEntities.remove(uuid); } - public long addBossBar(UUID uuid) { - long entityId = getNextEntityId().incrementAndGet(); - bossbars.put(uuid, entityId); - return entityId; + public void addBossBar(UUID uuid, BossBar bossBar) { + bossBars.put(uuid, bossBar); + bossBar.addBossBar(); } - public long getBossBar(UUID uuid) { - return bossbars.containsKey(uuid) ? bossbars.get(uuid) : -1; + public BossBar getBossBar(UUID uuid) { + return bossBars.get(uuid); } - public long removeBossBar(UUID uuid) { - return bossbars.remove(uuid); + public void removeBossBar(UUID uuid) { + BossBar bossBar = bossBars.remove(uuid); + if (bossBar != null) { + bossBar.removeBossBar(); + } + } + + public void updateBossBars() { + bossBars.values().forEach(BossBar::updateBossBar); } public void clear() { entities = null; entityIdTranslations = null; playerEntities = null; - bossbars = null; + bossBars = null; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java index 206f42d1..7ab71389 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java @@ -123,6 +123,7 @@ public class BedrockActionTranslator extends PacketTranslator { @Override public void translate(ServerBossBarPacket packet, GeyserSession session) { - BossEventPacket bossEventPacket = new BossEventPacket(); - bossEventPacket.setBossUniqueEntityId(session.getEntityCache().getBossBar(packet.getUuid())); - + BossBar bossBar = session.getEntityCache().getBossBar(packet.getUuid()); switch (packet.getAction()) { case ADD: - long entityId = session.getEntityCache().addBossBar(packet.getUuid()); - addBossEntity(session, entityId); - - bossEventPacket.setAction(BossEventPacket.Action.SHOW); - bossEventPacket.setBossUniqueEntityId(entityId); - bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), session.getClientData().getLanguageCode())); - bossEventPacket.setHealthPercentage(packet.getHealth()); - bossEventPacket.setColor(0); //ignored by client - bossEventPacket.setOverlay(1); - bossEventPacket.setDarkenSky(0); + long entityId = session.getEntityCache().getNextEntityId().incrementAndGet(); + bossBar = new BossBar(session, entityId, packet.getTitle(), packet.getHealth(), 0, 1, 0); + session.getEntityCache().addBossBar(packet.getUuid(), bossBar); break; case UPDATE_TITLE: - bossEventPacket.setAction(BossEventPacket.Action.TITLE); - bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), session.getClientData().getLanguageCode())); + if (bossBar != null) bossBar.updateTitle(packet.getTitle()); break; case UPDATE_HEALTH: - bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE); - bossEventPacket.setHealthPercentage(packet.getHealth()); + if (bossBar != null) bossBar.updateHealth(packet.getHealth()); break; case REMOVE: - bossEventPacket.setAction(BossEventPacket.Action.HIDE); - removeBossEntity(session, session.getEntityCache().removeBossBar(packet.getUuid())); + session.getEntityCache().removeBossBar(packet.getUuid()); break; case UPDATE_STYLE: case UPDATE_FLAGS: //todo return; } - - session.getUpstream().sendPacket(bossEventPacket); - } - - /** - * Bedrock still needs an entity to display the BossBar.
- * Just like 1.8 but it doesn't care about which entity - */ - private void addBossEntity(GeyserSession session, long entityId) { - AddEntityPacket addEntityPacket = new AddEntityPacket(); - addEntityPacket.setUniqueEntityId(entityId); - addEntityPacket.setRuntimeEntityId(entityId); - addEntityPacket.setIdentifier("minecraft:creeper"); - addEntityPacket.setEntityType(33); - addEntityPacket.setPosition(session.getPlayerEntity().getPosition()); - addEntityPacket.setRotation(Vector3f.ZERO); - addEntityPacket.setMotion(Vector3f.ZERO); - addEntityPacket.getMetadata().put(EntityData.SCALE, 0.01F); // scale = 0 doesn't work? - - session.getUpstream().sendPacket(addEntityPacket); - } - - private void removeBossEntity(GeyserSession session, long entityId) { - RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); - removeEntityPacket.setUniqueEntityId(entityId); - - session.getUpstream().sendPacket(removeEntityPacket); } }