From 50c4c0b2d8bb35bbe5de74681c9d19ac82692594 Mon Sep 17 00:00:00 2001 From: Luke <32024335+lukeeey@users.noreply.github.com> Date: Tue, 5 May 2020 14:10:27 +0100 Subject: [PATCH] Initial world border --- .../network/session/GeyserSession.java | 15 +++ .../java/world/JavaWorldBorderTranslator.java | 78 ++++++++++++ .../geysermc/connector/world/WorldBorder.java | 116 ++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaWorldBorderTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/world/WorldBorder.java 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 51cc0923..88fba8b0 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 @@ -66,6 +66,7 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.LocaleUtils; import org.geysermc.connector.utils.Toolbox; +import org.geysermc.connector.world.WorldBorder; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.EncryptionUtil; @@ -156,6 +157,9 @@ public class GeyserSession implements CommandSender { @Setter private int craftSlot = 0; + @Setter + private WorldBorder worldBorder; // Im just going to shove this here until i move some stuff around + public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { this.connector = connector; this.upstream = new UpstreamSession(bedrockServerSession); @@ -392,6 +396,17 @@ public class GeyserSession implements CommandSender { upstream.sendPacket(textPacket); } + public void sendActionBar(String text) { + SetTitlePacket setTitlePacket = new SetTitlePacket(); + setTitlePacket.setType(SetTitlePacket.Type.SET_ACTIONBAR_MESSAGE); + setTitlePacket.setText(text); + setTitlePacket.setFadeInTime(0); + setTitlePacket.setStayTime(0); + setTitlePacket.setFadeOutTime(0); + + upstream.sendPacket(setTitlePacket); + } + @Override public boolean isConsole() { return false; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaWorldBorderTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaWorldBorderTranslator.java new file mode 100644 index 00000000..6795e78f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaWorldBorderTranslator.java @@ -0,0 +1,78 @@ +/* + * 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.translators.java.world; + +import com.github.steveice10.mc.protocol.data.game.world.WorldBorderAction; +import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerWorldBorderPacket; +import com.nukkitx.math.vector.Vector2f; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; +import org.geysermc.connector.world.WorldBorder; + +@Translator(packet = ServerWorldBorderPacket.class) +public class JavaWorldBorderTranslator extends PacketTranslator { + + @Override + public void translate(ServerWorldBorderPacket packet, GeyserSession session) { + WorldBorder worldBorder = session.getWorldBorder(); + + if(packet.getAction() != WorldBorderAction.INITIALIZE && worldBorder == null) { + return; + } + + switch(packet.getAction()) { + case INITIALIZE: + // should be getCenterZ() + worldBorder = new WorldBorder(Vector2f.from(packet.getCenterX(), packet.getCenterY()), packet.getRadius(), packet.getOldRadius(), packet.getNewRadius(), + packet.getSpeed(), packet.getWarningTime(), packet.getWarningTime()); + + session.setWorldBorder(worldBorder); + break; + case SET_SIZE: + worldBorder.setRadius(packet.getRadius()); + break; + case LERP_SIZE: + worldBorder.setOldRadius(packet.getOldRadius()); + worldBorder.setNewRadius(packet.getNewRadius()); + worldBorder.setSpeed(packet.getSpeed()); + break; + case SET_CENTER: + // should be getCenterZ() + worldBorder.setCenter(Vector2f.from(packet.getCenterX(), packet.getCenterY())); + break; + case SET_WARNING_TIME: + worldBorder.setWarningTime(packet.getWarningTime()); + return; + case SET_WARNING_BLOCKS: + worldBorder.setWarningBlocks(packet.getWarningBlocks()); + return; + } + + worldBorder.update(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/world/WorldBorder.java b/connector/src/main/java/org/geysermc/connector/world/WorldBorder.java new file mode 100644 index 00000000..f7ea173b --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/world/WorldBorder.java @@ -0,0 +1,116 @@ +/* + * 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.world; + +import com.nukkitx.math.vector.Vector2f; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.packet.TextPacket; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.geysermc.common.ChatColor; +import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.PlayerEntity; +import org.geysermc.connector.network.session.GeyserSession; + +@Getter +@Setter +@RequiredArgsConstructor +public class WorldBorder { + private @NonNull Vector2f center; + private @NonNull double radius; + private @NonNull double oldRadius; + private @NonNull double newRadius; + private @NonNull long speed; + private @NonNull int warningTime; + private @NonNull int warningBlocks; + + private double minX; + private double minZ; + private double maxX; + private double maxZ; + + public void onTick(GeyserSession session) { + PlayerEntity player = session.getPlayerEntity(); + if(isNearEdge(player)) { + session.sendActionBar(ChatColor.BOLD + "" + ChatColor.RED + "You are near the world border (" + (int) getDistanceToEdge(player) + " blocks)"); + } + } + + /** + * Updates the min and max positions of the world border. + * This should be called every time there is a modifcation to either the center coordinates or the radius. + */ + public void update() { + this.minX = Math.max(center.getX() - newRadius / 2.0D, -newRadius); + this.minZ = Math.max(center.getY() - newRadius / 2.0D, -newRadius); + this.maxX = Math.min(center.getX() + newRadius / 2.0D, newRadius); + this.maxZ = Math.min(center.getY() + newRadius / 2.0D, newRadius); + } + + /** + * Checks if an entity is within the warning distance to the edge of the world border. + * https://wiki.vg/Protocol#World_Border + */ + public boolean isNearEdge(Entity entity) { + double distance = Math.max(Math.min(speed * 1000 * warningTime, Math.abs(newRadius - oldRadius)), warningBlocks); + + float entityDistance = (float) getDistanceToEdge(entity); + + if ((double) entityDistance < distance) { + return true; + } + return false; + } + + /** + * Checks if an entity is inside the world border. + * + * This method needs to be improved as it doesn't account for when the world border + * is currently changing size, it only accounts for the target size. + * + * Something similar to the method above should work. + */ + public boolean isInsideBorder(Entity entity) { + return entity.getPosition().getX() > minX && entity.getPosition().getX() < maxX && entity.getPosition().getZ() > minZ && entity.getPosition().getZ() < maxZ; + } + + /** + * Calculates how close the entity is to the edge of the world border. + */ + public double getDistanceToEdge(Entity entity) { + Vector3f pos = entity.getPosition(); + + double minPosZ = pos.getZ() - minZ; + double maxPosZ = maxZ - pos.getZ(); + double minPosX = pos.getX() - minX; + double maxPosX = maxX - pos.getX(); + + return Math.min(Math.min(Math.min(minPosX, maxPosX), minPosZ), maxPosZ); + } +} \ No newline at end of file