From f22d286ea1aa45292b4d74f0980db4dffbb392bf Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 9 Sep 2021 21:27:38 -0400 Subject: [PATCH] Add world border translation (#1839) As Bedrock does not have world border support, this PR translates what the Java server sends us for a world border into particles and fog, while also preventing the Bedrock client from moving outside of the world border. Co-authored-by: Luke <32024335+lukeeey@users.noreply.github.com> Co-authored-by: ofunny --- .../geysermc/connector/entity/BoatEntity.java | 7 + .../network/ConnectorServerEventHandler.java | 4 +- .../network/session/GeyserSession.java | 27 ++ .../network/session/cache/WorldBorder.java | 291 ++++++++++++++++++ ...BedrockInventoryTransactionTranslator.java | 11 + .../BedrockMoveEntityAbsoluteTranslator.java | 18 ++ .../player/BedrockMovePlayerTranslator.java | 4 + .../JavaInitializeBorderTranslator.java | 51 +++ .../border/JavaSetBorderCenterPacket.java | 45 +++ .../JavaSetBorderLerpSizeTranslator.java | 47 +++ .../border/JavaSetBorderSizeTranslator.java | 46 +++ .../JavaSetBorderWarningDelayTranslator.java | 44 +++ ...avaSetBorderWarningDistanceTranslator.java | 44 +++ .../connector/utils/LanguageUtils.java | 37 ++- 14 files changed, 661 insertions(+), 15 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/session/cache/WorldBorder.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaInitializeBorderTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderCenterPacket.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderLerpSizeTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderSizeTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderWarningDelayTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderWarningDistanceTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java index bc7ad2330..4cc9fea0e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java @@ -81,6 +81,13 @@ public class BoatEntity extends Entity { session.sendUpstreamPacket(moveEntityPacket); } + /** + * Move the boat without making the adjustments needed to translate from Java + */ + public void moveAbsoluteWithoutAdjustments(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + super.moveAbsolute(session, position, Vector3f.from(rotation.getX(), 0, rotation.getX()), isOnGround, teleported); + } + @Override public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { super.moveRelative(session, relX, relY, relZ, Vector3f.from(rotation.getX(), 0, rotation.getX()), isOnGround); diff --git a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java index 35db999ce..94dd0b744 100644 --- a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java @@ -88,7 +88,9 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { @Override public BedrockPong onQuery(InetSocketAddress inetSocketAddress) { - connector.getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.pinged", inetSocketAddress)); + if (connector.getConfig().isDebugMode()) { + connector.getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.pinged", inetSocketAddress)); + } GeyserConfiguration config = connector.getConfig(); 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 707fc9717..cfc877716 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 @@ -154,6 +154,12 @@ public class GeyserSession implements CommandSender { private final Int2ObjectMap teleportMap = new Int2ObjectOpenHashMap<>(); + private final WorldBorder worldBorder; + /** + * Whether simulated fog has been sent to the client or not. + */ + private boolean isInWorldBorderWarningArea = false; + private final PlayerInventory playerInventory; @Setter private Inventory openInventory; @@ -452,6 +458,8 @@ public class GeyserSession implements CommandSender { this.tagCache = new TagCache(); this.worldCache = new WorldCache(this); + this.worldBorder = new WorldBorder(this); + this.collisionManager = new CollisionManager(this); this.playerEntity = new SessionPlayerEntity(this); @@ -928,6 +936,25 @@ public class GeyserSession implements CommandSender { lastMovementTimestamp = System.currentTimeMillis(); } + if (worldBorder.isResizing()) { + worldBorder.resize(); + } + + if (!worldBorder.isWithinWarningBoundaries()) { + // Show particles representing where the world border is + worldBorder.drawWall(); + // Set the mood + if (!isInWorldBorderWarningArea) { + isInWorldBorderWarningArea = true; + WorldBorder.sendFog(this, "minecraft:fog_crimson_forest"); + } + } else if (isInWorldBorderWarningArea) { + // Clear fog as we are outside the world border now + WorldBorder.removeFog(this); + isInWorldBorderWarningArea = false; + } + + for (Tickable entity : entityCache.getTickableEntities()) { entity.tick(this); } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldBorder.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldBorder.java new file mode 100644 index 000000000..d3f8362d7 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldBorder.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2019-2021 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.nukkitx.math.GenericMath; +import com.nukkitx.math.vector.Vector2f; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.LevelEventType; +import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; +import com.nukkitx.protocol.bedrock.packet.PlayerFogPacket; +import lombok.Getter; +import lombok.Setter; +import org.geysermc.connector.entity.player.PlayerEntity; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +import javax.annotation.Nonnull; +import java.util.Collections; + +public class WorldBorder { + private static final double DEFAULT_WORLD_BORDER_SIZE = 5.9999968E7D; + + @Getter @Setter + private @Nonnull Vector2f center = Vector2f.ZERO; + /** + * The diameter in blocks of the world border before it got changed or similar to newDiameter if not changed. + */ + @Getter @Setter + private double oldDiameter = DEFAULT_WORLD_BORDER_SIZE; + /** + * The diameter in blocks of the new world border. + */ + @Getter @Setter + private double newDiameter = DEFAULT_WORLD_BORDER_SIZE; + /** + * The speed to apply an expansion/shrinking of the world border. + * When a client joins they get the actual border oldDiameter and the time left to reach the newDiameter. + */ + @Getter @Setter + private long speed = 0; + /** + * The time in seconds before a shrinking world border would hit a not moving player. + * Creates the same visual warning effect as warningBlocks. + */ + @Getter @Setter + private int warningDelay = 15; + /** + * Block length before you reach the border to show warning particles. + */ + @Getter @Setter + private int warningBlocks = 5; + + @Getter + private boolean resizing; + private double currentDiameter; + + /* + * Boundaries of the actual world border. + * (The will get updated on expanding or shrinking) + */ + private double minX = 0.0D; + private double minZ = 0.0D; + private double maxX = 0.0D; + private double maxZ = 0.0D; + + /* + * The boundaries for the for the warning visuals. + */ + private double warningMaxX = 0.0D; + private double warningMaxZ = 0.0D; + private double warningMinX = 0.0D; + private double warningMinZ = 0.0D; + + /** + * To track when to send wall particle packets. + */ + private int currentWallTick; + + /** + * If the world border is resizing, this variable saves how many ticks have progressed in the resizing + */ + private long lastUpdatedWorldBorderTime = 0; + + private final GeyserSession session; + + public WorldBorder(GeyserSession session) { + this.session = session; + } + + /** + * @return true as long the entity is within the world limits. + */ + public boolean isInsideBorderBoundaries() { + Vector3f entityPosition = session.getPlayerEntity().getPosition(); + return entityPosition.getX() > minX && entityPosition.getX() < maxX && entityPosition.getZ() > minZ && entityPosition.getZ() < maxZ; + } + + /** + * Confirms that the entity is within world border boundaries when they move. + * Otherwise, if {@code adjustPosition} is true, this function will push the player back. + * + * @return if this player was indeed against the world border. Will return false if no world border was defined for us. + */ + public boolean isPassingIntoBorderBoundaries(Vector3f newPosition, boolean adjustPosition) { + boolean isInWorldBorder = isPassingIntoBorderBoundaries(newPosition); + if (isInWorldBorder && adjustPosition) { + PlayerEntity playerEntity = session.getPlayerEntity(); + // Move the player back, but allow gravity to take place + // Teleported = true makes going back better, but disconnects the player from their mounted entity + playerEntity.moveAbsolute(session, + Vector3f.from(playerEntity.getPosition().getX(), (newPosition.getY() - EntityType.PLAYER.getOffset()), playerEntity.getPosition().getZ()), + playerEntity.getRotation(), playerEntity.isOnGround(), session.getRidingVehicleEntity() == null); + } + return isInWorldBorder; + } + + public boolean isPassingIntoBorderBoundaries(Vector3f newEntityPosition) { + int entityX = GenericMath.floor(newEntityPosition.getX()); + int entityZ = GenericMath.floor(newEntityPosition.getZ()); + Vector3f currentEntityPosition = session.getPlayerEntity().getPosition(); + // Make sure we can't move out of the world border, but if we're out of the world border, we can move in + return (entityX == (int) minX && currentEntityPosition.getX() > newEntityPosition.getX()) || + (entityX == (int) maxX && currentEntityPosition.getX() < newEntityPosition.getX()) || + (entityZ == (int) minZ && currentEntityPosition.getZ() > newEntityPosition.getZ()) || + (entityZ == (int) maxZ && currentEntityPosition.getZ() < newEntityPosition.getZ()); + } + + /** + * Same as {@link #isInsideBorderBoundaries()} but using the warning boundaries. + * + * @return true as long the entity is within the world limits and not in the warning zone at the edge to the border. + */ + public boolean isWithinWarningBoundaries() { + Vector3f entityPosition = session.getPlayerEntity().getPosition(); + return entityPosition.getX() > warningMinX && entityPosition.getX() < warningMaxX && entityPosition.getZ() > warningMinZ && entityPosition.getZ() < warningMaxZ; + } + + /** + * Updates the world border's minimum and maximum properties + */ + public void update() { + /* + * Setting the correct boundary of our world border's square. + */ + double radius; + if (resizing) { + radius = this.currentDiameter / 2.0D; + } else { + radius = this.newDiameter / 2.0D; + } + this.minX = center.getX() - radius; + this.minZ = center.getY() - radius; // Mapping 2D vector to 3D coordinates >> Y becomes Z + this.maxX = center.getX() + radius; + this.maxZ = center.getY() + radius; // Mapping 2D vector to 3D coordinates >> Y becomes Z + + /* + * Caching the warning boundaries. + */ + this.warningMinX = this.minX + this.warningBlocks; + this.warningMinZ = this.minZ + this.warningBlocks; + this.warningMaxX = this.maxX - this.warningBlocks; + this.warningMaxZ = this.maxZ - this.warningBlocks; + } + + public void resize() { + if (this.lastUpdatedWorldBorderTime >= this.speed) { + // Diameter has now updated to the new diameter + this.resizing = false; + this.lastUpdatedWorldBorderTime = 0; + } else if (resizing) { + this.currentDiameter = this.oldDiameter + ((double) this.lastUpdatedWorldBorderTime / (double) this.speed) * (this.newDiameter - this.oldDiameter); + this.lastUpdatedWorldBorderTime += 50; + } + update(); + } + + public void setResizing(boolean resizing) { + this.resizing = resizing; + if (!resizing) { + this.lastUpdatedWorldBorderTime = 0; + } + } + + private static final LevelEventType WORLD_BORDER_PARTICLE = LevelEventType.PARTICLE_DENY_BLOCK; + + /** + * Draws a wall of particles where the world border resides + */ + public void drawWall() { + if (currentWallTick++ != 20) { + // Only draw a wall once every second + return; + } + currentWallTick = 0; + Vector3f entityPosition = session.getPlayerEntity().getPosition(); + float particlePosX = entityPosition.getX(); + float particlePosY = entityPosition.getY(); + float particlePosZ = entityPosition.getZ(); + + if (entityPosition.getX() > warningMaxX) { + drawWall(Vector3f.from(maxX, particlePosY, particlePosZ), true); + } + if (entityPosition.getX() < warningMinX) { + drawWall(Vector3f.from(minX, particlePosY, particlePosZ), true); + } + if (entityPosition.getZ() > warningMaxZ) { + drawWall(Vector3f.from(particlePosX, particlePosY, maxZ), false); + } + if (entityPosition.getZ() < warningMinZ) { + drawWall(Vector3f.from(particlePosX, particlePosY, minZ), false); + } + } + + private void drawWall(Vector3f position, boolean drawWallX) { + int initialY = (int) (position.getY() - EntityType.PLAYER.getOffset() - 1); + for (int y = initialY; y < (initialY + 5); y++) { + if (drawWallX) { + float x = position.getX(); + for (int z = (int) position.getZ() - 3; z < ((int) position.getZ() + 3); z++) { + if (z < minZ) { + continue; + } + if (z > maxZ) { + break; + } + + sendWorldBorderParticle(x, y, z); + } + } else { + float z = position.getZ(); + for (int x = (int) position.getX() - 3; x < ((int) position.getX() + 3); x++) { + if (x < minX) { + continue; + } + if (x > maxX) { + break; + } + + sendWorldBorderParticle(x, y, z); + } + } + } + } + + private void sendWorldBorderParticle(float x, float y, float z) { + LevelEventPacket effectPacket = new LevelEventPacket(); + effectPacket.setPosition(Vector3f.from(x, y, z)); + effectPacket.setType(WORLD_BORDER_PARTICLE); + session.getUpstream().sendPacket(effectPacket); + } + + /** + * Send the following fog IDs to the client + */ + public static void sendFog(GeyserSession session, String... fogNameSpaces) { + PlayerFogPacket packet = new PlayerFogPacket(); + Collections.addAll(packet.getFogStack(), fogNameSpaces); + session.sendUpstreamPacket(packet); + } + + /** + * Clear any additional fog sent to the client + */ + public static void removeFog(GeyserSession session) { + session.sendUpstreamPacket(new PlayerFogPacket()); + } + +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index 1f909806b..2de4e4cad 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -144,6 +144,12 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ServerInitializeBorderPacket packet) { + WorldBorder worldBorder = session.getWorldBorder(); + worldBorder.setCenter(Vector2f.from(packet.getNewCenterX(), packet.getNewCenterZ())); + worldBorder.setOldDiameter(packet.getOldSize()); + worldBorder.setNewDiameter(packet.getNewSize()); + worldBorder.setSpeed(packet.getLerpTime()); + worldBorder.setWarningDelay(packet.getWarningTime()); + worldBorder.setWarningBlocks(packet.getWarningBlocks()); + worldBorder.setResizing(packet.getLerpTime() > 0); + + worldBorder.update(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderCenterPacket.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderCenterPacket.java new file mode 100644 index 000000000..034c7cf6d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderCenterPacket.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2021 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.translators.java.world.border; + +import com.github.steveice10.mc.protocol.packet.ingame.server.world.border.ServerSetBorderCenterPacket; +import com.nukkitx.math.vector.Vector2f; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.WorldBorder; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +@Translator(packet = ServerSetBorderCenterPacket.class) +public class JavaSetBorderCenterPacket extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ServerSetBorderCenterPacket packet) { + WorldBorder worldBorder = session.getWorldBorder(); + worldBorder.setCenter(Vector2f.from(packet.getNewCenterX(), packet.getNewCenterZ())); + + worldBorder.update(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderLerpSizeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderLerpSizeTranslator.java new file mode 100644 index 000000000..b54a67b31 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderLerpSizeTranslator.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2021 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.translators.java.world.border; + +import com.github.steveice10.mc.protocol.packet.ingame.server.world.border.ServerSetBorderLerpSizePacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.WorldBorder; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +@Translator(packet = ServerSetBorderLerpSizePacket.class) +public class JavaSetBorderLerpSizeTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ServerSetBorderLerpSizePacket packet) { + WorldBorder worldBorder = session.getWorldBorder(); + worldBorder.setOldDiameter(packet.getOldSize()); + worldBorder.setNewDiameter(packet.getNewSize()); + worldBorder.setSpeed(packet.getLerpTime()); + worldBorder.setResizing(true); + + worldBorder.update(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderSizeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderSizeTranslator.java new file mode 100644 index 000000000..3b90019ad --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderSizeTranslator.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2021 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.translators.java.world.border; + +import com.github.steveice10.mc.protocol.packet.ingame.server.world.border.ServerSetBorderSizePacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.WorldBorder; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +@Translator(packet = ServerSetBorderSizePacket.class) +public class JavaSetBorderSizeTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ServerSetBorderSizePacket packet) { + WorldBorder worldBorder = session.getWorldBorder(); + worldBorder.setOldDiameter(packet.getSize()); + worldBorder.setNewDiameter(packet.getSize()); + worldBorder.setResizing(false); + + worldBorder.update(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderWarningDelayTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderWarningDelayTranslator.java new file mode 100644 index 000000000..b3b718b30 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderWarningDelayTranslator.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2021 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.translators.java.world.border; + +import com.github.steveice10.mc.protocol.packet.ingame.server.world.border.ServerSetBorderWarningDelayPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.WorldBorder; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +@Translator(packet = ServerSetBorderWarningDelayPacket.class) +public class JavaSetBorderWarningDelayTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ServerSetBorderWarningDelayPacket packet) { + WorldBorder worldBorder = session.getWorldBorder(); + worldBorder.setWarningDelay(packet.getWarningDelay()); + + worldBorder.update(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderWarningDistanceTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderWarningDistanceTranslator.java new file mode 100644 index 000000000..21d1d9209 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/border/JavaSetBorderWarningDistanceTranslator.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2021 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.translators.java.world.border; + +import com.github.steveice10.mc.protocol.packet.ingame.server.world.border.ServerSetBorderWarningDistancePacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.WorldBorder; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +@Translator(packet = ServerSetBorderWarningDistancePacket.class) +public class JavaSetBorderWarningDistanceTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ServerSetBorderWarningDistancePacket packet) { + WorldBorder worldBorder = session.getWorldBorder(); + worldBorder.setWarningBlocks(packet.getWarningBlocks()); + + worldBorder.update(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java index 5f5c1f17f..340a5e58c 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java @@ -117,20 +117,26 @@ public class LanguageUtils { if (formatString == null) { properties = LOCALE_MAPPINGS.get(getDefaultLocale()); formatString = properties.getProperty(key); + + // Try and get the key from en_US (this should only ever happen in development) + if (formatString == null) { + properties = LOCALE_MAPPINGS.get("en_US"); + formatString = properties.getProperty(key); + + // Final fallback + if (formatString == null) { + return key; + } + } } - // Try and get the key from en_US (this should only ever happen in development) - if (formatString == null) { - properties = LOCALE_MAPPINGS.get("en_US"); - formatString = properties.getProperty(key); + String message = formatString.replace("&", "\u00a7"); + if (values == null || values.length == 0) { + // Nothing to replace + return message; } - // Final fallback - if (formatString == null) { - return key; - } - - return MessageFormat.format(formatString.replace("'", "''").replace("&", "\u00a7"), values); + return MessageFormat.format(message.replace("'", "''"), values); } /** @@ -140,12 +146,15 @@ public class LanguageUtils { * @return The formatted locale */ public static String formatLocale(String locale) { - try { - String[] parts = locale.toLowerCase().split("_"); - return parts[0] + "_" + parts[1].toUpperCase(); - } catch (Exception e) { + // Currently, all valid Geyser locales follow the same pattern of ll_CC, where ll is the language and + // CC is the country + if (locale.length() != 5 || locale.indexOf('_') != 2) { + // Invalid locale return locale; } + String language = locale.substring(0, 2); + String country = locale.substring(3); + return language.toLowerCase(Locale.ENGLISH) + "_" + country.toUpperCase(Locale.ENGLISH); } /**