From 3d957e35b801f9a9ec0c6748046467affc25843c Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 12 Jul 2024 20:31:02 +0800 Subject: [PATCH 01/26] Proper tick rate handling --- .../geyser/entity/type/BoatEntity.java | 2 +- .../geyser/entity/type/LivingEntity.java | 1 + .../entity/type/ThrowableItemEntity.java | 8 +- .../geyser/session/GeyserSession.java | 98 +++++++++++-------- .../java/JavaTickingStateTranslator.java | 40 ++++++++ 5 files changed, 103 insertions(+), 46 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStateTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java index 47ae6777a65..f535255030e 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java @@ -187,7 +187,7 @@ public InteractionResult interact(Hand hand) { @Override public void tick() { // Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing - doTick = !doTick; // Run every 100 ms + doTick = !doTick; // Run every other tick if (!doTick || passengers.isEmpty()) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index 499084555c4..6ecb1ce5aa1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -422,3 +422,4 @@ protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttrib return type.getAttribute((float) AttributeUtils.calculateValue(javaAttribute)); } } + diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java index 55334010f14..4724c84dc6a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java @@ -25,12 +25,12 @@ package org.geysermc.geyser.entity.type; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import java.util.UUID; @@ -48,6 +48,10 @@ public ThrowableItemEntity(GeyserSession session, int entityId, long geyserId, U super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); setFlag(EntityFlag.INVISIBLE, true); invisible = false; + if (session.isFrozen()) { + age = 4; + checkVisibility(); + } } private void checkVisibility() { diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 25dd21662e9..9818b521178 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -550,7 +550,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { /** * Stores cookies sent by the Java server. */ - @Setter @Getter + @Setter + @Getter private Map cookies = new Object2ObjectOpenHashMap<>(); private final GeyserCameraData cameraData; @@ -559,6 +560,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private MinecraftProtocol protocol; + private float tickRate = 20.0f; + + private boolean frozen = false; + public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop) { this.geyser = geyser; this.upstream = new UpstreamSession(bedrockServerSession); @@ -656,7 +661,7 @@ public void connect() { // Default move speed // Bedrock clients move very fast by default until they get an attribute packet correcting the speed attributesPacket.setAttributes(Collections.singletonList( - GeyserAttributeType.MOVEMENT_SPEED.getAttribute())); + GeyserAttributeType.MOVEMENT_SPEED.getAttribute())); upstream.sendPacket(attributesPacket); GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket(); @@ -759,7 +764,7 @@ public void authenticateWithMicrosoftCode(boolean offlineAccess) { sendUpstreamPacket(packet); final PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getOrCreateTask( - getAuthData().xuid() + getAuthData().xuid() ); task.setOnline(true); task.resetTimer(); @@ -800,13 +805,13 @@ public boolean onMicrosoftLoginComplete(PendingMicrosoftAuthentication.Authentic GameProfile selectedProfile = service.getSelectedProfile(); if (selectedProfile == null) { disconnect(GeyserLocale.getPlayerLocaleString( - "geyser.network.remote.invalid_account", - clientData.getLanguageCode() + "geyser.network.remote.invalid_account", + clientData.getLanguageCode() )); } else { this.protocol = new MinecraftProtocol( - selectedProfile, - service.getAccessToken() + selectedProfile, + service.getAccessToken() ); try { connectDownstream(); @@ -831,7 +836,7 @@ private void connectDownstream() { GeyserImpl.getInstance().eventBus().fire(loginEvent); if (loginEvent.isCancelled()) { String disconnectReason = loginEvent.disconnectReason() == null ? - BedrockDisconnectReasons.DISCONNECTED : loginEvent.disconnectReason(); + BedrockDisconnectReasons.DISCONNECTED : loginEvent.disconnectReason(); disconnect(disconnectReason); return; } @@ -841,7 +846,7 @@ private void connectDownstream() { boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE; // Start ticking - tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS); + tickThread = eventLoop.scheduleAtFixedRate(this::tick, Math.round(1000 / tickRate), Math.round(1000 / tickRate), TimeUnit.MILLISECONDS); this.protocol.setUseDefaultListeners(false); @@ -849,8 +854,8 @@ private void connectDownstream() { if (geyser.getBootstrap().getSocketAddress() != null) { // We're going to connect through the JVM and not through TCP downstream = new LocalSession(this.remoteServer.address(), this.remoteServer.port(), - geyser.getBootstrap().getSocketAddress(), upstream.getAddress().getAddress().getHostAddress(), - this.protocol, this.protocol.createHelper()); + geyser.getBootstrap().getSocketAddress(), upstream.getAddress().getAddress().getHostAddress(), + this.protocol, this.protocol.createHelper()); this.downstream = new DownstreamSession(downstream); } else { downstream = new TcpClientSession(this.remoteServer.address(), this.remoteServer.port(), this.protocol); @@ -917,16 +922,16 @@ public void packetSending(PacketSendingEvent event) { } encryptedData = cipher.encryptFromString(BedrockData.of( - clientData.getGameVersion(), - authData.name(), - authData.xuid(), - clientData.getDeviceOs().ordinal(), - clientData.getLanguageCode(), - clientData.getUiProfile().ordinal(), - clientData.getCurrentInputMode().ordinal(), - bedrockAddress, - skinUploader.getId(), - skinUploader.getVerifyCode() + clientData.getGameVersion(), + authData.name(), + authData.xuid(), + clientData.getDeviceOs().ordinal(), + clientData.getLanguageCode(), + clientData.getUiProfile().ordinal(), + clientData.getCurrentInputMode().ordinal(), + bedrockAddress, + skinUploader.getId(), + skinUploader.getVerifyCode() ).toString()); } catch (Exception e) { geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); @@ -960,11 +965,11 @@ public void connected(ConnectedEvent event) { if (downstream instanceof LocalSession) { // Connected directly to the server geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.connect_internal", - authData.name(), protocol.getProfile().getName())); + authData.name(), protocol.getProfile().getName())); } else { // Connected to an IP address geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.connect", - authData.name(), protocol.getProfile().getName(), remoteServer.address())); + authData.name(), protocol.getProfile().getName(), remoteServer.address())); } UUID uuid = protocol.getProfile().getId(); @@ -1004,10 +1009,10 @@ public void disconnected(DisconnectedEvent event) { disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale()); // Explain that they may be looking for Floodgate. geyser.getLogger().warning(GeyserLocale.getLocaleStringLog( - geyser.getPlatformType() == PlatformType.STANDALONE ? - "geyser.network.remote.floodgate_explanation_standalone" - : "geyser.network.remote.floodgate_explanation_plugin", - Constants.FLOODGATE_DOWNLOAD_LOCATION + geyser.getPlatformType() == PlatformType.STANDALONE ? + "geyser.network.remote.floodgate_explanation_standalone" + : "geyser.network.remote.floodgate_explanation_plugin", + Constants.FLOODGATE_DOWNLOAD_LOCATION )); } else { // Likely that Floodgate is not configured correctly. @@ -1070,6 +1075,13 @@ public void packetError(PacketErrorEvent event) { downstream.connect(false, loginEvent.transferring()); } + public void updateTickData(float tickRate, boolean frozen) { + tickThread.cancel(true); + this.tickRate = tickRate; + this.frozen = frozen; + tickThread = eventLoop.scheduleAtFixedRate(this::tick, Math.round(1000 / getTickRate()), Math.round(1000 / getTickRate()), TimeUnit.MILLISECONDS); + } + public void disconnect(String reason) { if (!closed) { loggedIn = false; @@ -1159,7 +1171,7 @@ public ScheduledFuture scheduleInEventLoop(Runnable runnable, long duration, } /** - * Called every 50 milliseconds - one Minecraft tick. + * Called every Minecraft tick - 1000/tickRate milliseconds. */ protected void tick() { try { @@ -1171,7 +1183,7 @@ protected void tick() { // A null return value cancels the packet if (position != null) { ServerboundMovePlayerPosPacket packet = new ServerboundMovePlayerPosPacket(playerEntity.isOnGround(), - position.getX(), position.getY(), position.getZ()); + position.getX(), position.getY(), position.getZ()); sendDownstreamGamePacket(packet); } lastMovementTimestamp = System.currentTimeMillis(); @@ -1197,11 +1209,11 @@ protected void tick() { isInWorldBorderWarningArea = false; } - - for (Tickable entity : entityCache.getTickableEntities()) { - entity.tick(); + if (!isFrozen()) { + for (Tickable entity : entityCache.getTickableEntities()) { + entity.tick(); + } } - if (armAnimationTicks >= 0) { // As of 1.18.2 Java Edition, it appears that the swing time is dynamically updated depending on the // player's effect status, but the animation can cut short if the duration suddenly decreases @@ -1316,7 +1328,7 @@ public void setGameMode(GameMode newGamemode) { */ public void useItem(Hand hand) { sendDownstreamGamePacket(new ServerboundUseItemPacket( - hand, worldCache.nextPredictionSequence(), playerEntity.getYaw(), playerEntity.getPitch())); + hand, worldCache.nextPredictionSequence(), playerEntity.getYaw(), playerEntity.getPitch())); } /** @@ -1365,7 +1377,7 @@ public void armSwingPending() { private boolean disableBlocking() { if (playerEntity.getFlag(EntityFlag.BLOCKING)) { ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, - Vector3i.ZERO, Direction.DOWN, 0); + Vector3i.ZERO, Direction.DOWN, 0); sendDownstreamGamePacket(releaseItemPacket); playerEntity.setFlag(EntityFlag.BLOCKING, false); return true; @@ -1375,7 +1387,7 @@ private boolean disableBlocking() { public void requestOffhandSwap() { ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO, - Direction.DOWN, 0); + Direction.DOWN, 0); sendDownstreamGamePacket(swapHandsPacket); } @@ -1584,7 +1596,7 @@ public void confirmTeleport(Vector3d position) { unconfirmedTeleport.resetUnconfirmedFor(); geyser.getLogger().debug("Resending teleport " + unconfirmedTeleport.getTeleportConfirmId()); getPlayerEntity().moveAbsolute(Vector3f.from(unconfirmedTeleport.getX(), unconfirmedTeleport.getY(), unconfirmedTeleport.getZ()), - unconfirmedTeleport.getYaw(), unconfirmedTeleport.getPitch(), playerEntity.isOnGround(), true); + unconfirmedTeleport.getYaw(), unconfirmedTeleport.getPitch(), playerEntity.isOnGround(), true); } } @@ -1711,7 +1723,7 @@ public void setDaylightCycle(boolean doCycle) { * Send a gamerule value to the client * * @param gameRule The gamerule to send - * @param value The value of the gamerule + * @param value The value of the gamerule */ public void sendGameRule(String gameRule, Object value) { GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket(); @@ -1850,8 +1862,8 @@ private int getRenderDistance() { */ public void sendJavaClientSettings() { ServerboundClientInformationPacket clientSettingsPacket = new ServerboundClientInformationPacket(locale(), - getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS, - HandPreference.RIGHT_HAND, false, true); + getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS, + HandPreference.RIGHT_HAND, false, true); sendDownstreamPacket(clientSettingsPacket); } @@ -1904,8 +1916,8 @@ public float getEyeHeight() { return switch (pose) { case SNEAKING -> 1.27f; case SWIMMING, - FALL_FLYING, // Elytra - SPIN_ATTACK -> 0.4f; // Trident spin attack + FALL_FLYING, // Elytra + SPIN_ATTACK -> 0.4f; // Trident spin attack case SLEEPING -> 0.2f; default -> EntityDefinitions.PLAYER.offset(); }; @@ -1923,7 +1935,7 @@ public float getEyeHeight() { @Override public UUID javaUuid() { - return playerEntity != null ? playerEntity.getUuid() : null ; + return playerEntity != null ? playerEntity.getUuid() : null; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStateTranslator.java new file mode 100644 index 00000000000..4e547ef59f9 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStateTranslator.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 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.geyser.translator.protocol.java; + +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundTickingStatePacket; + +@Translator(packet = ClientboundTickingStatePacket.class) +public class JavaTickingStateTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundTickingStatePacket packet) { + session.updateTickData(packet.getTickRate(), packet.isFrozen()); + } +} From 91a1a12651d3e0049925dfa81199b22730f54da3 Mon Sep 17 00:00:00 2001 From: LetsGoAway <68365423+letsgoawaydev@users.noreply.github.com> Date: Sat, 13 Jul 2024 11:22:45 +0800 Subject: [PATCH 02/26] Fix frozen variable getter --- .../main/java/org/geysermc/geyser/session/GeyserSession.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 9818b521178..6c9c0de46f1 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1209,7 +1209,7 @@ protected void tick() { isInWorldBorderWarningArea = false; } - if (!isFrozen()) { + if (!frozen) { for (Tickable entity : entityCache.getTickableEntities()) { entity.tick(); } From 841869149c5d040341f9e200ee6c895696cbd201 Mon Sep 17 00:00:00 2001 From: Ethan Date: Sat, 13 Jul 2024 15:42:45 +0800 Subject: [PATCH 03/26] Fix formatting i think third attempt --- core/src/main/resources/languages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index afbf78bbe0b..60b20023a92 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit afbf78bbe0b39d0a076a42c228828c12f7f7da90 +Subproject commit 60b20023a92f084aba895ab0336e70fa7fb311fb From fc04909508761ea915455edc16acc56981325e65 Mon Sep 17 00:00:00 2001 From: Ethan Date: Sat, 13 Jul 2024 15:46:11 +0800 Subject: [PATCH 04/26] Formatting fix attempt 5 fsdiofhsdioufhvuisdhviuo9ds --- .../geyser/session/GeyserSession.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 6c9c0de46f1..7a979cde304 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -661,7 +661,7 @@ public void connect() { // Default move speed // Bedrock clients move very fast by default until they get an attribute packet correcting the speed attributesPacket.setAttributes(Collections.singletonList( - GeyserAttributeType.MOVEMENT_SPEED.getAttribute())); + GeyserAttributeType.MOVEMENT_SPEED.getAttribute())); upstream.sendPacket(attributesPacket); GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket(); @@ -764,7 +764,7 @@ public void authenticateWithMicrosoftCode(boolean offlineAccess) { sendUpstreamPacket(packet); final PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getOrCreateTask( - getAuthData().xuid() + getAuthData().xuid() ); task.setOnline(true); task.resetTimer(); @@ -805,13 +805,13 @@ public boolean onMicrosoftLoginComplete(PendingMicrosoftAuthentication.Authentic GameProfile selectedProfile = service.getSelectedProfile(); if (selectedProfile == null) { disconnect(GeyserLocale.getPlayerLocaleString( - "geyser.network.remote.invalid_account", - clientData.getLanguageCode() + "geyser.network.remote.invalid_account", + clientData.getLanguageCode() )); } else { this.protocol = new MinecraftProtocol( - selectedProfile, - service.getAccessToken() + selectedProfile, + service.getAccessToken() ); try { connectDownstream(); @@ -836,7 +836,7 @@ private void connectDownstream() { GeyserImpl.getInstance().eventBus().fire(loginEvent); if (loginEvent.isCancelled()) { String disconnectReason = loginEvent.disconnectReason() == null ? - BedrockDisconnectReasons.DISCONNECTED : loginEvent.disconnectReason(); + BedrockDisconnectReasons.DISCONNECTED : loginEvent.disconnectReason(); disconnect(disconnectReason); return; } @@ -854,8 +854,8 @@ private void connectDownstream() { if (geyser.getBootstrap().getSocketAddress() != null) { // We're going to connect through the JVM and not through TCP downstream = new LocalSession(this.remoteServer.address(), this.remoteServer.port(), - geyser.getBootstrap().getSocketAddress(), upstream.getAddress().getAddress().getHostAddress(), - this.protocol, this.protocol.createHelper()); + geyser.getBootstrap().getSocketAddress(), upstream.getAddress().getAddress().getHostAddress(), + this.protocol, this.protocol.createHelper()); this.downstream = new DownstreamSession(downstream); } else { downstream = new TcpClientSession(this.remoteServer.address(), this.remoteServer.port(), this.protocol); @@ -922,16 +922,16 @@ public void packetSending(PacketSendingEvent event) { } encryptedData = cipher.encryptFromString(BedrockData.of( - clientData.getGameVersion(), - authData.name(), - authData.xuid(), - clientData.getDeviceOs().ordinal(), - clientData.getLanguageCode(), - clientData.getUiProfile().ordinal(), - clientData.getCurrentInputMode().ordinal(), - bedrockAddress, - skinUploader.getId(), - skinUploader.getVerifyCode() + clientData.getGameVersion(), + authData.name(), + authData.xuid(), + clientData.getDeviceOs().ordinal(), + clientData.getLanguageCode(), + clientData.getUiProfile().ordinal(), + clientData.getCurrentInputMode().ordinal(), + bedrockAddress, + skinUploader.getId(), + skinUploader.getVerifyCode() ).toString()); } catch (Exception e) { geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); @@ -965,11 +965,11 @@ public void connected(ConnectedEvent event) { if (downstream instanceof LocalSession) { // Connected directly to the server geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.connect_internal", - authData.name(), protocol.getProfile().getName())); + authData.name(), protocol.getProfile().getName())); } else { // Connected to an IP address geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.connect", - authData.name(), protocol.getProfile().getName(), remoteServer.address())); + authData.name(), protocol.getProfile().getName(), remoteServer.address())); } UUID uuid = protocol.getProfile().getId(); @@ -1009,10 +1009,10 @@ public void disconnected(DisconnectedEvent event) { disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale()); // Explain that they may be looking for Floodgate. geyser.getLogger().warning(GeyserLocale.getLocaleStringLog( - geyser.getPlatformType() == PlatformType.STANDALONE ? - "geyser.network.remote.floodgate_explanation_standalone" - : "geyser.network.remote.floodgate_explanation_plugin", - Constants.FLOODGATE_DOWNLOAD_LOCATION + geyser.getPlatformType() == PlatformType.STANDALONE ? + "geyser.network.remote.floodgate_explanation_standalone" + : "geyser.network.remote.floodgate_explanation_plugin", + Constants.FLOODGATE_DOWNLOAD_LOCATION )); } else { // Likely that Floodgate is not configured correctly. @@ -1183,7 +1183,7 @@ protected void tick() { // A null return value cancels the packet if (position != null) { ServerboundMovePlayerPosPacket packet = new ServerboundMovePlayerPosPacket(playerEntity.isOnGround(), - position.getX(), position.getY(), position.getZ()); + position.getX(), position.getY(), position.getZ()); sendDownstreamGamePacket(packet); } lastMovementTimestamp = System.currentTimeMillis(); @@ -1328,7 +1328,7 @@ public void setGameMode(GameMode newGamemode) { */ public void useItem(Hand hand) { sendDownstreamGamePacket(new ServerboundUseItemPacket( - hand, worldCache.nextPredictionSequence(), playerEntity.getYaw(), playerEntity.getPitch())); + hand, worldCache.nextPredictionSequence(), playerEntity.getYaw(), playerEntity.getPitch())); } /** @@ -1377,7 +1377,7 @@ public void armSwingPending() { private boolean disableBlocking() { if (playerEntity.getFlag(EntityFlag.BLOCKING)) { ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, - Vector3i.ZERO, Direction.DOWN, 0); + Vector3i.ZERO, Direction.DOWN, 0); sendDownstreamGamePacket(releaseItemPacket); playerEntity.setFlag(EntityFlag.BLOCKING, false); return true; @@ -1387,7 +1387,7 @@ private boolean disableBlocking() { public void requestOffhandSwap() { ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO, - Direction.DOWN, 0); + Direction.DOWN, 0); sendDownstreamGamePacket(swapHandsPacket); } @@ -1596,7 +1596,7 @@ public void confirmTeleport(Vector3d position) { unconfirmedTeleport.resetUnconfirmedFor(); geyser.getLogger().debug("Resending teleport " + unconfirmedTeleport.getTeleportConfirmId()); getPlayerEntity().moveAbsolute(Vector3f.from(unconfirmedTeleport.getX(), unconfirmedTeleport.getY(), unconfirmedTeleport.getZ()), - unconfirmedTeleport.getYaw(), unconfirmedTeleport.getPitch(), playerEntity.isOnGround(), true); + unconfirmedTeleport.getYaw(), unconfirmedTeleport.getPitch(), playerEntity.isOnGround(), true); } } @@ -1862,8 +1862,8 @@ private int getRenderDistance() { */ public void sendJavaClientSettings() { ServerboundClientInformationPacket clientSettingsPacket = new ServerboundClientInformationPacket(locale(), - getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS, - HandPreference.RIGHT_HAND, false, true); + getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS, + HandPreference.RIGHT_HAND, false, true); sendDownstreamPacket(clientSettingsPacket); } @@ -1916,8 +1916,8 @@ public float getEyeHeight() { return switch (pose) { case SNEAKING -> 1.27f; case SWIMMING, - FALL_FLYING, // Elytra - SPIN_ATTACK -> 0.4f; // Trident spin attack + FALL_FLYING, // Elytra + SPIN_ATTACK -> 0.4f; // Trident spin attack case SLEEPING -> 0.2f; default -> EntityDefinitions.PLAYER.offset(); }; From f83e2fb093f421ea3e202eb40c5a6f1d7dd0d8fa Mon Sep 17 00:00:00 2001 From: Ethan Date: Sat, 13 Jul 2024 18:01:19 +0800 Subject: [PATCH 05/26] Fix stuff, also fixed the sizing of throwables as they were to big --- .../geyser/entity/EntityDefinitions.java | 4 +- .../geyser/entity/type/EnderEyeEntity.java | 48 +++++++++++++++++++ .../geyser/entity/type/ThrowableEntity.java | 2 +- .../entity/type/ThrowableItemEntity.java | 38 ++++++--------- .../geysermc/geyser/entity/type/Tickable.java | 2 + .../geyser/session/GeyserSession.java | 30 +++++++----- .../java/JavaTickingStateTranslator.java | 2 +- .../java/JavaTickingStepTranslator.java | 40 ++++++++++++++++ 8 files changed, 126 insertions(+), 40 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/entity/type/EnderEyeEntity.java create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStepTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index 11b4a32d1f3..b2aa0399549 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -93,7 +93,7 @@ public final class EntityDefinitions { public static final EntityDefinition EVOKER_FANGS; public static final EntityDefinition EXPERIENCE_BOTTLE; public static final EntityDefinition EXPERIENCE_ORB; - public static final EntityDefinition EYE_OF_ENDER; + public static final EntityDefinition EYE_OF_ENDER; public static final EntityDefinition FALLING_BLOCK; public static final EntityDefinition FIREBALL; public static final EntityDefinition FIREWORK_ROCKET; @@ -250,7 +250,7 @@ public final class EntityDefinitions { .height(0.8f).width(0.5f) .identifier("minecraft:evocation_fang") .build(); - EYE_OF_ENDER = EntityDefinition.inherited(Entity::new, entityBase) + EYE_OF_ENDER = EntityDefinition.inherited(EnderEyeEntity::new, entityBase) .type(EntityType.EYE_OF_ENDER) .heightAndWidth(0.25f) .identifier("minecraft:eye_of_ender_signal") diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/EnderEyeEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/EnderEyeEntity.java new file mode 100644 index 00000000000..cc5a58f2125 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/type/EnderEyeEntity.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 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.geyser.entity.type; + + +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; +import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.EntityDefinitions; +import org.geysermc.geyser.session.GeyserSession; + +import java.util.UUID; + +public class EnderEyeEntity extends Entity { + public EnderEyeEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + } + + @Override + protected void initializeMetadata() { + super.initializeMetadata(); + // Correct sizing + dirtyMetadata.put(EntityDataTypes.SCALE, 0.5f); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java index 25bbdbd3c13..b09a9fcc5cc 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java @@ -130,7 +130,7 @@ protected float getGravity() { case SNOWBALL: case EGG: case ENDER_PEARL: - return 0.03f; + return 0.0325f; case LLAMA_SPIT: return 0.06f; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java index 4724c84dc6a..3707f18cbbd 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.entity.type; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; @@ -38,43 +39,34 @@ * Used as a class for any projectile entity that looks like an item */ public class ThrowableItemEntity extends ThrowableEntity { - /** - * Number of ticks since the entity was spawned by the Java server - */ - private int age; private boolean invisible; public ThrowableItemEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); setFlag(EntityFlag.INVISIBLE, true); invisible = false; - if (session.isFrozen()) { - age = 4; - checkVisibility(); - } } - + @Override + protected void initializeMetadata() { + super.initializeMetadata(); + // Correct sizing + dirtyMetadata.put(EntityDataTypes.SCALE, 0.5f); + } private void checkVisibility() { + Vector3f playerPos = session.getPlayerEntity().getPosition(); + // Prevent projectiles from blocking the player's screen + setInvisible(position.distanceSquared(playerPos) < 9); + if (invisible != getFlag(EntityFlag.INVISIBLE)) { - if (!invisible) { - Vector3f playerPos = session.getPlayerEntity().getPosition(); - // Prevent projectiles from blocking the player's screen - if (age >= 4 || position.distanceSquared(playerPos) > 16) { - setFlag(EntityFlag.INVISIBLE, false); - updateBedrockMetadata(); - } - } else { - setFlag(EntityFlag.INVISIBLE, true); - updateBedrockMetadata(); - } + setFlag(EntityFlag.INVISIBLE, invisible); + updateBedrockMetadata(); } - age++; } @Override - public void tick() { + public void drawTick() { checkVisibility(); - super.tick(); + super.drawTick(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java b/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java index 06bf45b3dea..ec5d335603d 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java @@ -29,5 +29,7 @@ * Implemented onto anything that should have code ran every Minecraft tick - 50 milliseconds. */ public interface Tickable { + default void drawTick() {}; void tick(); + } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 7a979cde304..c88e7bf48ce 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2024 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 @@ -560,9 +560,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private MinecraftProtocol protocol; - private float tickRate = 20.0f; - - private boolean frozen = false; + private boolean tickingFrozen = false; + @Setter + private int stepTicks = 0; + private boolean gameShouldUpdate = true; public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop) { this.geyser = geyser; @@ -846,7 +847,7 @@ private void connectDownstream() { boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE; // Start ticking - tickThread = eventLoop.scheduleAtFixedRate(this::tick, Math.round(1000 / tickRate), Math.round(1000 / tickRate), TimeUnit.MILLISECONDS); + tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS); this.protocol.setUseDefaultListeners(false); @@ -1075,11 +1076,10 @@ public void packetError(PacketErrorEvent event) { downstream.connect(false, loginEvent.transferring()); } - public void updateTickData(float tickRate, boolean frozen) { + public void updateTickingState(float tickRate, boolean frozen) { tickThread.cancel(true); - this.tickRate = tickRate; - this.frozen = frozen; - tickThread = eventLoop.scheduleAtFixedRate(this::tick, Math.round(1000 / getTickRate()), Math.round(1000 / getTickRate()), TimeUnit.MILLISECONDS); + this.tickingFrozen = frozen; + tickThread = eventLoop.scheduleAtFixedRate(this::tick, Math.round(1000 / tickRate), Math.round(1000 / tickRate), TimeUnit.MILLISECONDS); } public void disconnect(String reason) { @@ -1208,9 +1208,13 @@ protected void tick() { camera().removeFog("minecraft:fog_crimson_forest"); isInWorldBorderWarningArea = false; } - - if (!frozen) { - for (Tickable entity : entityCache.getTickableEntities()) { + gameShouldUpdate = !tickingFrozen || stepTicks > 0; + if (stepTicks > 0) { + --stepTicks; + } + for (Tickable entity : entityCache.getTickableEntities()) { + entity.drawTick(); + if (gameShouldUpdate) { entity.tick(); } } @@ -2070,4 +2074,4 @@ private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) packet.setSoftEnum(new CommandEnumData(name, Collections.singletonMap(enums, Collections.emptySet()), true)); sendUpstreamPacket(packet); } -} +} \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStateTranslator.java index 4e547ef59f9..85d4974cf8c 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStateTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaTickingStateTranslator.java @@ -35,6 +35,6 @@ public class JavaTickingStateTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundTickingStepPacket packet) { + session.setStepTicks(packet.getTickSteps()); + } +} From 4fd9cbe24925029171235f6077bf4fc4e492861a Mon Sep 17 00:00:00 2001 From: Ethan Date: Sat, 13 Jul 2024 18:04:00 +0800 Subject: [PATCH 06/26] Move update ticking state --- .../org/geysermc/geyser/session/GeyserSession.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index c88e7bf48ce..c69b93c628d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1076,12 +1076,6 @@ public void packetError(PacketErrorEvent event) { downstream.connect(false, loginEvent.transferring()); } - public void updateTickingState(float tickRate, boolean frozen) { - tickThread.cancel(true); - this.tickingFrozen = frozen; - tickThread = eventLoop.scheduleAtFixedRate(this::tick, Math.round(1000 / tickRate), Math.round(1000 / tickRate), TimeUnit.MILLISECONDS); - } - public void disconnect(String reason) { if (!closed) { loggedIn = false; @@ -1170,6 +1164,12 @@ public ScheduledFuture scheduleInEventLoop(Runnable runnable, long duration, }, duration, timeUnit); } + public void updateTickingState(float tickRate, boolean frozen) { + tickThread.cancel(true); + this.tickingFrozen = frozen; + tickThread = eventLoop.scheduleAtFixedRate(this::tick, Math.round(1000 / tickRate), Math.round(1000 / tickRate), TimeUnit.MILLISECONDS); + } + /** * Called every Minecraft tick - 1000/tickRate milliseconds. */ From 0eb91340693e66050098f99e96a7d80da2ce20a1 Mon Sep 17 00:00:00 2001 From: LetsGoAway <68365423+letsgoawaydev@users.noreply.github.com> Date: Thu, 18 Jul 2024 17:34:17 +0800 Subject: [PATCH 07/26] Update core/src/main/java/org/geysermc/geyser/session/GeyserSession.java Co-authored-by: rtm516 --- .../main/java/org/geysermc/geyser/session/GeyserSession.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 5e3afa4575e..f344df6a56a 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2024 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 From b8b05177b85f973a0962486587aab4f99ac7e505 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 24 Jul 2024 16:37:10 +0800 Subject: [PATCH 08/26] Fixes for spaces and documentation --- .../org/geysermc/geyser/entity/type/Tickable.java | 14 ++++++++++++-- .../org/geysermc/geyser/session/GeyserSession.java | 10 ++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java b/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java index ec5d335603d..5dda64ade06 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java @@ -29,7 +29,17 @@ * Implemented onto anything that should have code ran every Minecraft tick - 50 milliseconds. */ public interface Tickable { - default void drawTick() {}; - void tick(); + /* + * This function gets called every tick at all times, even when the server requests that the + * game should be frozen. This should be used for updating things that are always + * client side updated on Java, regardless of if the server is frozen or not. + */ + default void drawTick() { + } + /* + * This function gets called every game tick as long as the + * game tick loop isn't frozen. + */ + void tick(); } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index f344df6a56a..5e20e5e8171 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -561,6 +561,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private MinecraftProtocol protocol; private boolean tickingFrozen = false; + /* + * The amount of ticks requested by the server that the + * game should proceed with, even if game ticking is frozen. + */ @Setter private int stepTicks = 0; private boolean gameShouldUpdate = true; @@ -1208,6 +1212,7 @@ protected void tick() { camera().removeFog("minecraft:fog_crimson_forest"); isInWorldBorderWarningArea = false; } + gameShouldUpdate = !tickingFrozen || stepTicks > 0; if (stepTicks > 0) { --stepTicks; @@ -1218,6 +1223,7 @@ protected void tick() { entity.tick(); } } + if (armAnimationTicks >= 0) { // As of 1.18.2 Java Edition, it appears that the swing time is dynamically updated depending on the // player's effect status, but the animation can cut short if the duration suddenly decreases @@ -1643,7 +1649,7 @@ public void sendDownstreamLoginPacket(Packet packet) { /** * Send a packet to the remote server if in the specified state. * - * @param packet the java edition packet from MCProtocolLib + * @param packet the java edition packet from MCProtocolLib * @param intendedState the state the client should be in */ public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) { @@ -1727,7 +1733,7 @@ public void setDaylightCycle(boolean doCycle) { * Send a gamerule value to the client * * @param gameRule The gamerule to send - * @param value The value of the gamerule + * @param value The value of the gamerule */ public void sendGameRule(String gameRule, Object value) { GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket(); From 02c0469a9c175d65c7e7cacddfb6591d14b5ff92 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 24 Jul 2024 16:38:53 +0800 Subject: [PATCH 09/26] Missed a space --- .../org/geysermc/geyser/entity/type/ThrowableItemEntity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java index 3707f18cbbd..e936d7304b9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java @@ -49,6 +49,7 @@ public ThrowableItemEntity(GeyserSession session, int entityId, long geyserId, U @Override protected void initializeMetadata() { super.initializeMetadata(); + // Correct sizing dirtyMetadata.put(EntityDataTypes.SCALE, 0.5f); } From 611b010f59b345e0456abce3c3ff0e494e835955 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 24 Jul 2024 16:39:19 +0800 Subject: [PATCH 10/26] wait now ive fixed it --- .../org/geysermc/geyser/entity/type/ThrowableItemEntity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java index e936d7304b9..fb7aa8bffdb 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java @@ -46,13 +46,14 @@ public ThrowableItemEntity(GeyserSession session, int entityId, long geyserId, U setFlag(EntityFlag.INVISIBLE, true); invisible = false; } + @Override protected void initializeMetadata() { super.initializeMetadata(); - // Correct sizing dirtyMetadata.put(EntityDataTypes.SCALE, 0.5f); } + private void checkVisibility() { Vector3f playerPos = session.getPlayerEntity().getPosition(); // Prevent projectiles from blocking the player's screen From 4f345c03e7adccdee0341737441cc2d5c73882ed Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 24 Jul 2024 16:46:27 +0800 Subject: [PATCH 11/26] Fix languages --- core/src/main/resources/languages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 60b20023a92..5fafc70544d 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 60b20023a92f084aba895ab0336e70fa7fb311fb +Subproject commit 5fafc70544d9c61db8b193f63bcc3b994d9a210f From 046d287cb989b439d8408ff2828defb6c1259c0b Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 24 Jul 2024 17:02:07 +0800 Subject: [PATCH 12/26] try again to fix languages --- core/src/main/resources/languages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 5fafc70544d..afbf78bbe0b 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 5fafc70544d9c61db8b193f63bcc3b994d9a210f +Subproject commit afbf78bbe0b39d0a076a42c228828c12f7f7da90 From 2da55999a927ea6dfc601269eb0780ee1e5315a3 Mon Sep 17 00:00:00 2001 From: LetsGoAway <68365423+letsgoawaydev@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:51:08 +0800 Subject: [PATCH 13/26] Fix Java doc comments for tickable interface --- .../org/geysermc/geyser/entity/type/Tickable.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java b/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java index 5dda64ade06..f61ff355f66 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Tickable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2024 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 @@ -26,18 +26,19 @@ package org.geysermc.geyser.entity.type; /** - * Implemented onto anything that should have code ran every Minecraft tick - 50 milliseconds. + * Implemented onto anything that should have code ran every Minecraft tick. + * By default, the Java server runs at 20 TPS, 50 milliseconds for each tick. */ public interface Tickable { - /* - * This function gets called every tick at all times, even when the server requests that the - * game should be frozen. This should be used for updating things that are always + /** + * This function gets called every tick at all times, even when the server requests that + * the game should be frozen. This should be used for updating things that are always * client side updated on Java, regardless of if the server is frozen or not. */ default void drawTick() { } - /* + /** * This function gets called every game tick as long as the * game tick loop isn't frozen. */ From 09bfe4b60d6e46b3f45f47bf945761585407a845 Mon Sep 17 00:00:00 2001 From: LetsGoAway <68365423+letsgoawaydev@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:53:54 +0800 Subject: [PATCH 14/26] Fix javadoc comment in Geyser Session --- .../java/org/geysermc/geyser/session/GeyserSession.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 5e20e5e8171..63cf4997d93 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -561,9 +561,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private MinecraftProtocol protocol; private boolean tickingFrozen = false; - /* - * The amount of ticks requested by the server that the - * game should proceed with, even if game ticking is frozen. + /** + * The amount of ticks requested by the server that the game should proceed with, even if the game tick loop is frozen. */ @Setter private int stepTicks = 0; @@ -2080,4 +2079,4 @@ private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) packet.setSoftEnum(new CommandEnumData(name, Collections.singletonMap(enums, Collections.emptySet()), true)); sendUpstreamPacket(packet); } -} \ No newline at end of file +} From e86d11ced034510de4c03128582451c19a384cd6 Mon Sep 17 00:00:00 2001 From: LetsGoAway <68365423+letsgoawaydev@users.noreply.github.com> Date: Fri, 26 Jul 2024 10:40:31 +0800 Subject: [PATCH 15/26] fix comment --- .../main/java/org/geysermc/geyser/session/GeyserSession.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index c63c70d3a7f..c6f856b01c6 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1702,7 +1702,7 @@ public void sendDownstreamLoginPacket(Packet packet) { /** * Send a packet to the remote server if in the specified state. * - * @param packet the java edition packet from MCProtocolLib + * @param packet the java edition packet from MCProtocolLib * @param intendedState the state the client should be in */ public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) { From e348d3e562835a2ad252aca19eed47f10d5840c4 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 30 Jul 2024 00:05:42 +0800 Subject: [PATCH 16/26] fix some tick rate stuffs --- .../geyser/entity/type/ThrowableEntity.java | 2 +- .../entity/type/ThrowableItemEntity.java | 8 ++- .../geyser/session/GeyserSession.java | 54 ++++++++++--------- .../BedrockMobEquipmentTranslator.java | 2 +- .../geysermc/geyser/util/CooldownUtils.java | 11 ++-- 5 files changed, 42 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java index b09a9fcc5cc..25bbdbd3c13 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java @@ -130,7 +130,7 @@ protected float getGravity() { case SNOWBALL: case EGG: case ENDER_PEARL: - return 0.0325f; + return 0.03f; case LLAMA_SPIT: return 0.06f; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java index fb7aa8bffdb..2b2062ad16f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java @@ -40,11 +40,13 @@ */ public class ThrowableItemEntity extends ThrowableEntity { private boolean invisible; + private int age; public ThrowableItemEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); setFlag(EntityFlag.INVISIBLE, true); invisible = false; + age = 0; } @Override @@ -55,9 +57,11 @@ protected void initializeMetadata() { } private void checkVisibility() { - Vector3f playerPos = session.getPlayerEntity().getPosition(); + age++; + Vector3f playerPos = session.getPlayerEntity().getPosition().sub(0, session.getPlayerEntity().getDefinition().offset(),0); + // Prevent projectiles from blocking the player's screen - setInvisible(position.distanceSquared(playerPos) < 9); + setInvisible((age <= 2 && !session.isTickingFrozen()) || (playerPos.distanceSquared(position.add(0, definition.offset(), 0)) < 12.25)); if (invisible != getFlag(EntityFlag.INVISIBLE)) { setFlag(EntityFlag.INVISIBLE, invisible); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index a00b85a6792..30e42756f55 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -618,7 +618,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * Stores cookies sent by the Java server. */ @Setter - @Getter private Map cookies = new Object2ObjectOpenHashMap<>(); private final GeyserCameraData cameraData; @@ -627,6 +626,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private MinecraftProtocol protocol; + @Getter + private int millisecondsPerTick = 50; private boolean tickingFrozen = false; /** * The amount of ticks requested by the server that the game should proceed with, even if the game tick loop is frozen. @@ -857,31 +858,31 @@ public boolean onMicrosoftLoginComplete(PendingMicrosoftAuthentication.Authentic } task.cleanup(); // player is online -> remove pending authentication immediately return task.getAuthentication().handle((result, ex) -> { - if (ex != null) { - geyser.getLogger().error("Failed to log in with Microsoft code!", ex); - disconnect(ex.toString()); - return false; - } + if (ex != null) { + geyser.getLogger().error("Failed to log in with Microsoft code!", ex); + disconnect(ex.toString()); + return false; + } - StepMCProfile.MCProfile mcProfile = result.session().getMcProfile(); - StepMCToken.MCToken mcToken = mcProfile.getMcToken(); + StepMCProfile.MCProfile mcProfile = result.session().getMcProfile(); + StepMCToken.MCToken mcToken = mcProfile.getMcToken(); - this.protocol = new MinecraftProtocol( - new GameProfile(mcProfile.getId(), mcProfile.getName()), - mcToken.getAccessToken() - ); + this.protocol = new MinecraftProtocol( + new GameProfile(mcProfile.getId(), mcProfile.getName()), + mcToken.getAccessToken() + ); - try { - connectDownstream(); - } catch (Throwable t) { - t.printStackTrace(); - return false; - } + try { + connectDownstream(); + } catch (Throwable t) { + t.printStackTrace(); + return false; + } - // Save our auth chain for later use - geyser.saveAuthChain(bedrockUsername(), GSON.toJson(result.step().toJson(result.session()))); - return true; - }).getNow(false); + // Save our auth chain for later use + geyser.saveAuthChain(bedrockUsername(), GSON.toJson(result.step().toJson(result.session()))); + return true; + }).getNow(false); } /** @@ -902,7 +903,7 @@ private void connectDownstream() { boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE; // Start ticking - tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS); + tickThread = eventLoop.scheduleAtFixedRate(this::tick, millisecondsPerTick, millisecondsPerTick, TimeUnit.MILLISECONDS); this.protocol.setUseDefaultListeners(false); @@ -1220,9 +1221,10 @@ public ScheduledFuture scheduleInEventLoop(Runnable runnable, long duration, } public void updateTickingState(float tickRate, boolean frozen) { - tickThread.cancel(true); + tickThread.cancel(false); this.tickingFrozen = frozen; - tickThread = eventLoop.scheduleAtFixedRate(this::tick, Math.round(1000 / tickRate), Math.round(1000 / tickRate), TimeUnit.MILLISECONDS); + millisecondsPerTick = Math.round(1000.0f / MathUtils.clamp(tickRate, 1.0f, 10000.0f)); + tickThread = eventLoop.scheduleAtFixedRate(this::tick, millisecondsPerTick, millisecondsPerTick, TimeUnit.MILLISECONDS); } /** @@ -1784,7 +1786,7 @@ public void setDaylightCycle(boolean doCycle) { * Send a gamerule value to the client * * @param gameRule The gamerule to send - * @param value The value of the gamerule + * @param value The value of the gamerule */ public void sendGameRule(String gameRule, Object value) { GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java index 35ad942d041..d91e7e0fe80 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java @@ -66,7 +66,7 @@ public void translate(GeyserSession session, MobEquipmentPacket packet) { // (No need to send a release item packet - Java doesn't do this when swapping items) // Required to do it a tick later or else it doesn't register session.scheduleInEventLoop(() -> session.useItem(Hand.MAIN_HAND), - 50, TimeUnit.MILLISECONDS); + session.getMillisecondsPerTick(), TimeUnit.MILLISECONDS); } if (oldItem.getJavaId() != newItem.getJavaId()) { diff --git a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java index c020e96b2c6..071b24455ae 100644 --- a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java @@ -50,6 +50,7 @@ public static CooldownType getDefaultShowCooldown() { /** * Starts sending the fake cooldown to the Bedrock client. If the cooldown is not disabled, the sent type is the cooldownPreference in {@link PreferencesCache} + * * @param session GeyserSession */ public static void sendCooldown(GeyserSession session) { @@ -83,6 +84,7 @@ public static void sendCooldown(GeyserSession session) { /** * Keeps updating the cooldown until the bar is complete. + * * @param session GeyserSession * @param sessionPreference The type of cooldown the client prefers * @param lastHitTime The time of the last hit. Used to gauge how long the cooldown is taking. @@ -102,7 +104,7 @@ private static void computeCooldown(GeyserSession session, CooldownType sessionP session.sendUpstreamPacket(titlePacket); if (hasCooldown(session)) { session.scheduleInEventLoop(() -> - computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 + computeCooldown(session, sessionPreference, lastHitTime), session.getMillisecondsPerTick(), TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 } else { SetTitlePacket removeTitlePacket = new SetTitlePacket(); removeTitlePacket.setType(SetTitlePacket.Type.CLEAR); @@ -115,8 +117,8 @@ private static void computeCooldown(GeyserSession session, CooldownType sessionP private static boolean hasCooldown(GeyserSession session) { long time = System.currentTimeMillis() - session.getLastHitTime(); - double cooldown = restrain(((double) time) * session.getAttackSpeed() / 1000d, 1.5); - return cooldown < 1.1; + double cooldown = restrain(((double) time) * session.getAttackSpeed() / (session.getMillisecondsPerTick() * 20d), 1.5); + return cooldown < 1.0; } @@ -128,7 +130,7 @@ private static double restrain(double x, double max) { private static String getTitle(GeyserSession session) { long time = System.currentTimeMillis() - session.getLastHitTime(); - double cooldown = restrain(((double) time) * session.getAttackSpeed() / 1000d, 1); + double cooldown = restrain(((double) time) * session.getAttackSpeed() / (session.getMillisecondsPerTick() * 20d), 1); int darkGrey = (int) Math.floor(10d * cooldown); int grey = 10 - darkGrey; @@ -157,7 +159,6 @@ public enum CooldownType { * Convert the CooldownType string (from config) to the enum, DISABLED on fail * * @param name CooldownType string - * * @return The converted CooldownType */ public static CooldownType getByName(String name) { From 7af831fce0f10fa9906aa815914792fd6ef46674 Mon Sep 17 00:00:00 2001 From: LetsGoAway <68365423+letsgoawaydev@users.noreply.github.com> Date: Tue, 30 Jul 2024 11:48:19 +0800 Subject: [PATCH 17/26] Fix build fail --- .../src/main/java/org/geysermc/geyser/session/GeyserSession.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 30e42756f55..faa4fefa579 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -167,6 +167,7 @@ import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.EntityUtils; +import org.geysermc.geyser.util.MathUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MinecraftAuthLogger; import org.geysermc.mcprotocollib.auth.GameProfile; From 99e7385bdc0611727d7ce753dcc32052674d137e Mon Sep 17 00:00:00 2001 From: Ethan Date: Sat, 10 Aug 2024 13:18:30 +0800 Subject: [PATCH 18/26] fix some stuff --- .../entity/type/ThrowableItemEntity.java | 15 +++++++++++---- .../geyser/session/GeyserSession.java | 19 +++++++++++-------- .../BedrockMobEquipmentTranslator.java | 2 +- .../geysermc/geyser/util/CooldownUtils.java | 8 +++++--- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java index 2b2062ad16f..3a24b0b5752 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java @@ -39,14 +39,17 @@ * Used as a class for any projectile entity that looks like an item */ public class ThrowableItemEntity extends ThrowableEntity { - private boolean invisible; + /** + * Number of draw ticks since the entity was spawned by the Java server + */ private int age; + private boolean invisible; public ThrowableItemEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); setFlag(EntityFlag.INVISIBLE, true); invisible = false; - age = 0; + age = 1; } @Override @@ -58,10 +61,14 @@ protected void initializeMetadata() { private void checkVisibility() { age++; - Vector3f playerPos = session.getPlayerEntity().getPosition().sub(0, session.getPlayerEntity().getDefinition().offset(),0); // Prevent projectiles from blocking the player's screen - setInvisible((age <= 2 && !session.isTickingFrozen()) || (playerPos.distanceSquared(position.add(0, definition.offset(), 0)) < 12.25)); + if (session.isTickingFrozen()) { + Vector3f playerPos = session.getPlayerEntity().getPosition().sub(0, session.getPlayerEntity().getDefinition().offset(), 0); + setInvisible(playerPos.distanceSquared(position.add(0, definition.offset(), 0)) < 12.25); + } else { + setInvisible(age <= 2); + } if (invisible != getFlag(EntityFlag.INVISIBLE)) { setFlag(EntityFlag.INVISIBLE, invisible); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index faa4fefa579..ed1083d3eb1 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -587,7 +587,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private boolean advancedTooltips = false; /** - * The thread that will run every 50 milliseconds - one Minecraft tick. + * The thread that will run every game tick. */ private ScheduledFuture tickThread = null; @@ -628,14 +628,15 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private MinecraftProtocol protocol; @Getter - private int millisecondsPerTick = 50; + private int nanosecondsPerTick = 50000000; + @Getter + private float millisecondsPerTick = 50.0f; private boolean tickingFrozen = false; /** * The amount of ticks requested by the server that the game should proceed with, even if the game tick loop is frozen. */ @Setter private int stepTicks = 0; - private boolean gameShouldUpdate = true; public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop) { this.geyser = geyser; @@ -904,7 +905,7 @@ private void connectDownstream() { boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE; // Start ticking - tickThread = eventLoop.scheduleAtFixedRate(this::tick, millisecondsPerTick, millisecondsPerTick, TimeUnit.MILLISECONDS); + tickThread = eventLoop.scheduleAtFixedRate(this::tick, nanosecondsPerTick, nanosecondsPerTick, TimeUnit.NANOSECONDS); this.protocol.setUseDefaultListeners(false); @@ -1224,12 +1225,14 @@ public ScheduledFuture scheduleInEventLoop(Runnable runnable, long duration, public void updateTickingState(float tickRate, boolean frozen) { tickThread.cancel(false); this.tickingFrozen = frozen; - millisecondsPerTick = Math.round(1000.0f / MathUtils.clamp(tickRate, 1.0f, 10000.0f)); - tickThread = eventLoop.scheduleAtFixedRate(this::tick, millisecondsPerTick, millisecondsPerTick, TimeUnit.MILLISECONDS); + millisecondsPerTick = 1000.0f / tickRate; + + nanosecondsPerTick = MathUtils.ceil(1000000000.0f / MathUtils.clamp(tickRate, 1.0f, 10000.0f)); + tickThread = eventLoop.scheduleAtFixedRate(this::tick, nanosecondsPerTick, nanosecondsPerTick, TimeUnit.NANOSECONDS); } /** - * Called every Minecraft tick - 1000/tickRate milliseconds. + * Called every Minecraft tick. */ protected void tick() { try { @@ -1267,7 +1270,7 @@ protected void tick() { isInWorldBorderWarningArea = false; } - gameShouldUpdate = !tickingFrozen || stepTicks > 0; + boolean gameShouldUpdate = !tickingFrozen || stepTicks > 0; if (stepTicks > 0) { --stepTicks; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java index d91e7e0fe80..64681723eaa 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java @@ -66,7 +66,7 @@ public void translate(GeyserSession session, MobEquipmentPacket packet) { // (No need to send a release item packet - Java doesn't do this when swapping items) // Required to do it a tick later or else it doesn't register session.scheduleInEventLoop(() -> session.useItem(Hand.MAIN_HAND), - session.getMillisecondsPerTick(), TimeUnit.MILLISECONDS); + session.getNanosecondsPerTick(), TimeUnit.NANOSECONDS); } if (oldItem.getJavaId() != newItem.getJavaId()) { diff --git a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java index 071b24455ae..bfb3204f860 100644 --- a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java @@ -58,7 +58,8 @@ public static void sendCooldown(GeyserSession session) { CooldownType sessionPreference = session.getPreferencesCache().getCooldownPreference(); if (sessionPreference == CooldownType.DISABLED) return; - if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used + if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) + return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used // Set the times to stay a bit with no fade in nor out SetTitlePacket titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.TIMES); @@ -91,7 +92,8 @@ public static void sendCooldown(GeyserSession session) { */ private static void computeCooldown(GeyserSession session, CooldownType sessionPreference, long lastHitTime) { if (session.isClosed()) return; // Don't run scheduled tasks if the client left - if (lastHitTime != session.getLastHitTime()) return; // Means another cooldown has started so there's no need to continue this one + if (lastHitTime != session.getLastHitTime()) + return; // Means another cooldown has started so there's no need to continue this one SetTitlePacket titlePacket = new SetTitlePacket(); if (sessionPreference == CooldownType.ACTIONBAR) { titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); @@ -104,7 +106,7 @@ private static void computeCooldown(GeyserSession session, CooldownType sessionP session.sendUpstreamPacket(titlePacket); if (hasCooldown(session)) { session.scheduleInEventLoop(() -> - computeCooldown(session, sessionPreference, lastHitTime), session.getMillisecondsPerTick(), TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 + computeCooldown(session, sessionPreference, lastHitTime), session.getNanosecondsPerTick(), TimeUnit.NANOSECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 } else { SetTitlePacket removeTitlePacket = new SetTitlePacket(); removeTitlePacket.setType(SetTitlePacket.Type.CLEAR); From e5a2ae48604da9533f0f38747595849a3fe9619b Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 16 Aug 2024 21:14:56 +0800 Subject: [PATCH 19/26] merge --- core/src/main/resources/languages | 2 +- core/src/main/resources/mappings | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 7499daf712a..a943a1bb910 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 7499daf712ad6de70a07fba471b51b4ad92315c5 +Subproject commit a943a1bb910f58caa61f14bafacbc622bd48a694 diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 698fd2b108a..aaf53d6953c 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 698fd2b108a9e53f1e47b8cfdc122651b70d6059 +Subproject commit aaf53d6953c927e5ac1b87fd6627ffbfd4aa7cf5 From 807881fbdf0070b51964c06421a403065c869183 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 16 Aug 2024 21:25:44 +0800 Subject: [PATCH 20/26] test --- .../org/geysermc/geyser/session/GeyserSession.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index b79bced7283..3448ad4940c 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -66,6 +66,7 @@ import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting; import org.cloudburstmc.protocol.bedrock.data.GameRuleData; import org.cloudburstmc.protocol.bedrock.data.GameType; +import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.PlayerPermission; import org.cloudburstmc.protocol.bedrock.data.SoundEvent; import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType; @@ -85,6 +86,7 @@ import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket; import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket; +import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet; import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket; @@ -1250,9 +1252,21 @@ public ScheduledFuture scheduleInEventLoop(Runnable runnable, long duration, public void updateTickingState(float tickRate, boolean frozen) { tickThread.cancel(false); + this.tickingFrozen = frozen; + + LevelEventPacket pausePacket = new LevelEventPacket(); + pausePacket.setType(LevelEvent.GLOBAL_PAUSE); + pausePacket.setData(frozen ? 1 : 0); + pausePacket.setPosition(Vector3f.ZERO); + sendUpstreamPacket(pausePacket); + millisecondsPerTick = 1000.0f / tickRate; +// LevelEventPacket timestepPacket = new LevelEventPacket(); +// timestepPacket.setType(LevelEvent.SIM_TIME_SCALE); + + nanosecondsPerTick = MathUtils.ceil(1000000000.0f / MathUtils.clamp(tickRate, 1.0f, 10000.0f)); tickThread = eventLoop.scheduleAtFixedRate(this::tick, nanosecondsPerTick, nanosecondsPerTick, TimeUnit.NANOSECONDS); } From 3602fc2804f2a87481af40035c7ed2bf11184a7f Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 16 Aug 2024 21:29:40 +0800 Subject: [PATCH 21/26] Update languages --- core/src/main/resources/languages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index a943a1bb910..7499daf712a 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit a943a1bb910f58caa61f14bafacbc622bd48a694 +Subproject commit 7499daf712ad6de70a07fba471b51b4ad92315c5 From 21b87c8285ebeb34c25f25f7e578ef64ec577300 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 16 Aug 2024 21:40:31 +0800 Subject: [PATCH 22/26] Update mappings --- core/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index aaf53d6953c..698fd2b108a 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit aaf53d6953c927e5ac1b87fd6627ffbfd4aa7cf5 +Subproject commit 698fd2b108a9e53f1e47b8cfdc122651b70d6059 From 079c4e69d85936e58b665202716b14c3cab2d506 Mon Sep 17 00:00:00 2001 From: Ethan <68365423+letsgoawaydev@users.noreply.github.com> Date: Sat, 31 Aug 2024 16:24:01 +0800 Subject: [PATCH 23/26] delete broken stuff --- .../geyser/session/GeyserSession.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 3448ad4940c..42293490afc 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1255,18 +1255,8 @@ public void updateTickingState(float tickRate, boolean frozen) { this.tickingFrozen = frozen; - LevelEventPacket pausePacket = new LevelEventPacket(); - pausePacket.setType(LevelEvent.GLOBAL_PAUSE); - pausePacket.setData(frozen ? 1 : 0); - pausePacket.setPosition(Vector3f.ZERO); - sendUpstreamPacket(pausePacket); - millisecondsPerTick = 1000.0f / tickRate; -// LevelEventPacket timestepPacket = new LevelEventPacket(); -// timestepPacket.setType(LevelEvent.SIM_TIME_SCALE); - - nanosecondsPerTick = MathUtils.ceil(1000000000.0f / MathUtils.clamp(tickRate, 1.0f, 10000.0f)); tickThread = eventLoop.scheduleAtFixedRate(this::tick, nanosecondsPerTick, nanosecondsPerTick, TimeUnit.NANOSECONDS); } @@ -1275,6 +1265,21 @@ public void updateTickingState(float tickRate, boolean frozen) { * Called every Minecraft tick. */ protected void tick() { + LevelEventPacket pause = new LevelEventPacket(); + pause.setType(LevelEvent.GLOBAL_PAUSE); + pause.setData(0); + pause.setPosition(Vector3f.ZERO); + sendUpstreamPacket(pause); + LevelEventPacket timestepPacket = new LevelEventPacket(); + timestepPacket.setType(LevelEvent.S); + timestepPacket.setData(Math.round(millisecondsPerTick)); + timestepPacket.setPosition(Vector3f.from(millisecondsPerTick)); + sendUpstreamPacket(timestepPacket); + LevelEventPacket timescalePacket = new LevelEventPacket(); + timescalePacket.setType(LevelEvent.SIM_TIME_SCALE); + timescalePacket.setData(Math.round(millisecondsPerTick)); + timescalePacket.setPosition(Vector3f.from(millisecondsPerTick)); + sendUpstreamPacket(timescalePacket); try { pistonCache.tick(); // Check to see if the player's position needs updating - a position update should be sent once every 3 seconds From 90a01191cf56fb49e801d42b5939829818052126 Mon Sep 17 00:00:00 2001 From: Ethan <68365423+letsgoawaydev@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:25:40 +0800 Subject: [PATCH 24/26] Fix cooldown --- .../entity/type/ThrowableItemEntity.java | 5 +++-- .../geyser/session/GeyserSession.java | 19 +++---------------- .../geyser/session/cache/WorldCache.java | 10 +++++++--- .../JavaSetTitlesAnimationTranslator.java | 10 ++++++---- .../geysermc/geyser/util/CooldownUtils.java | 9 ++++++--- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java index 3a24b0b5752..7e6ae3d22ea 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java @@ -49,7 +49,7 @@ public ThrowableItemEntity(GeyserSession session, int entityId, long geyserId, U super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); setFlag(EntityFlag.INVISIBLE, true); invisible = false; - age = 1; + age = 0; } @Override @@ -64,10 +64,11 @@ private void checkVisibility() { // Prevent projectiles from blocking the player's screen if (session.isTickingFrozen()) { + // This may seem odd, but it matches java edition Vector3f playerPos = session.getPlayerEntity().getPosition().sub(0, session.getPlayerEntity().getDefinition().offset(), 0); setInvisible(playerPos.distanceSquared(position.add(0, definition.offset(), 0)) < 12.25); } else { - setInvisible(age <= 2); + setInvisible(age < 2); } if (invisible != getFlag(EntityFlag.INVISIBLE)) { diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 42293490afc..7bf381a6f02 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1255,9 +1255,11 @@ public void updateTickingState(float tickRate, boolean frozen) { this.tickingFrozen = frozen; + tickRate = MathUtils.clamp(tickRate, 1.0f, 10000.0f); + millisecondsPerTick = 1000.0f / tickRate; - nanosecondsPerTick = MathUtils.ceil(1000000000.0f / MathUtils.clamp(tickRate, 1.0f, 10000.0f)); + nanosecondsPerTick = MathUtils.ceil(1000000000.0f / tickRate); tickThread = eventLoop.scheduleAtFixedRate(this::tick, nanosecondsPerTick, nanosecondsPerTick, TimeUnit.NANOSECONDS); } @@ -1265,21 +1267,6 @@ public void updateTickingState(float tickRate, boolean frozen) { * Called every Minecraft tick. */ protected void tick() { - LevelEventPacket pause = new LevelEventPacket(); - pause.setType(LevelEvent.GLOBAL_PAUSE); - pause.setData(0); - pause.setPosition(Vector3f.ZERO); - sendUpstreamPacket(pause); - LevelEventPacket timestepPacket = new LevelEventPacket(); - timestepPacket.setType(LevelEvent.S); - timestepPacket.setData(Math.round(millisecondsPerTick)); - timestepPacket.setPosition(Vector3f.from(millisecondsPerTick)); - sendUpstreamPacket(timestepPacket); - LevelEventPacket timescalePacket = new LevelEventPacket(); - timescalePacket.setType(LevelEvent.SIM_TIME_SCALE); - timescalePacket.setData(Math.round(millisecondsPerTick)); - timescalePacket.setPosition(Vector3f.from(millisecondsPerTick)); - sendUpstreamPacket(timescalePacket); try { pistonCache.tick(); // Check to see if the player's position needs updating - a position update should be sent once every 3 seconds diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java index fb5137b05a9..f34ec49d8fd 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java @@ -118,9 +118,13 @@ private void forceSyncCorrectTitleTimes() { SetTitlePacket titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.TIMES); titlePacket.setText(""); - titlePacket.setFadeInTime(trueTitleFadeInTime); - titlePacket.setStayTime(trueTitleStayTime); - titlePacket.setFadeOutTime(trueTitleFadeOutTime); + + // We need a tick rate multiplier as otherwise the timings are incorrect on different tick rates because + // bedrock can only run at 20 TPS (50ms = 1 tick) + int tickrateMultiplier = Math.round(session.getMillisecondsPerTick()) / 50; + titlePacket.setFadeInTime(trueTitleFadeInTime * tickrateMultiplier); + titlePacket.setStayTime(trueTitleStayTime * tickrateMultiplier); + titlePacket.setFadeOutTime(trueTitleFadeOutTime * tickrateMultiplier); titlePacket.setPlatformOnlineId(""); titlePacket.setXuid(""); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/title/JavaSetTitlesAnimationTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/title/JavaSetTitlesAnimationTranslator.java index 4bc5ba0c512..d310690d5b8 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/title/JavaSetTitlesAnimationTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/title/JavaSetTitlesAnimationTranslator.java @@ -40,13 +40,15 @@ public void translate(GeyserSession session, ClientboundSetTitlesAnimationPacket int stayTime = packet.getStay(); int fadeOutTime = packet.getFadeOut(); session.getWorldCache().setTitleTimes(fadeInTime, stayTime, fadeOutTime); - + // We need a tick rate multiplier as otherwise the timings are incorrect on different tick rates because + // bedrock can only run at 20 TPS (50ms = 1 tick) + int tickrateMultiplier = Math.round(session.getMillisecondsPerTick()) / 50; SetTitlePacket titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.TIMES); titlePacket.setText(""); - titlePacket.setFadeInTime(fadeInTime); - titlePacket.setFadeOutTime(fadeOutTime); - titlePacket.setStayTime(stayTime); + titlePacket.setFadeInTime(fadeInTime * tickrateMultiplier); + titlePacket.setFadeOutTime(fadeOutTime * tickrateMultiplier); + titlePacket.setStayTime(stayTime * tickrateMultiplier); titlePacket.setXuid(""); titlePacket.setPlatformOnlineId(""); session.sendUpstreamPacket(titlePacket); diff --git a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java index bfb3204f860..72123a6e98c 100644 --- a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.util; import lombok.Getter; +import org.cloudburstmc.math.GenericMath; import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.PreferencesCache; @@ -106,7 +107,7 @@ private static void computeCooldown(GeyserSession session, CooldownType sessionP session.sendUpstreamPacket(titlePacket); if (hasCooldown(session)) { session.scheduleInEventLoop(() -> - computeCooldown(session, sessionPreference, lastHitTime), session.getNanosecondsPerTick(), TimeUnit.NANOSECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 + computeCooldown(session, sessionPreference, lastHitTime), (long) restrain(session.getMillisecondsPerTick(), 50), TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 } else { SetTitlePacket removeTitlePacket = new SetTitlePacket(); removeTitlePacket.setType(SetTitlePacket.Type.CLEAR); @@ -119,7 +120,8 @@ private static void computeCooldown(GeyserSession session, CooldownType sessionP private static boolean hasCooldown(GeyserSession session) { long time = System.currentTimeMillis() - session.getLastHitTime(); - double cooldown = restrain(((double) time) * session.getAttackSpeed() / (session.getMillisecondsPerTick() * 20d), 1.5); + double tickrateMultiplier = Math.max(session.getMillisecondsPerTick() / 50, 1.0); + double cooldown = restrain(((double) time) * session.getAttackSpeed() / (tickrateMultiplier * 1000.0), 1.0); return cooldown < 1.0; } @@ -132,7 +134,8 @@ private static double restrain(double x, double max) { private static String getTitle(GeyserSession session) { long time = System.currentTimeMillis() - session.getLastHitTime(); - double cooldown = restrain(((double) time) * session.getAttackSpeed() / (session.getMillisecondsPerTick() * 20d), 1); + double tickrateMultiplier = Math.max(session.getMillisecondsPerTick() / 50, 1.0); + double cooldown = restrain(((double) time) * session.getAttackSpeed() / (tickrateMultiplier * 1000.0), 1.0); int darkGrey = (int) Math.floor(10d * cooldown); int grey = 10 - darkGrey; From dcc3dce7b329b4e5debe54ec42d72cf64205f9d4 Mon Sep 17 00:00:00 2001 From: Ethan <68365423+letsgoawaydev@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:39:42 +0800 Subject: [PATCH 25/26] fix cooldowns --- .../translator/protocol/java/level/JavaCooldownTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java index 8e07a7d89af..a8a121e03b7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java @@ -54,7 +54,7 @@ public void translate(GeyserSession session, ClientboundCooldownPacket packet) { if (cooldownCategory != null) { PlayerStartItemCooldownPacket bedrockPacket = new PlayerStartItemCooldownPacket(); bedrockPacket.setItemCategory(cooldownCategory); - bedrockPacket.setCooldownDuration(packet.getCooldownTicks()); + bedrockPacket.setCooldownDuration(Math.round(packet.getCooldownTicks() * (session.getMillisecondsPerTick() / 50))); session.sendUpstreamPacket(bedrockPacket); } } From 4fc1d66e7857a321951c435f44f93d60d84667e8 Mon Sep 17 00:00:00 2001 From: LetsGoAway <68365423+letsgoawaydev@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:54:56 +0800 Subject: [PATCH 26/26] Update core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java Co-authored-by: chris --- core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java index 72123a6e98c..dd42c96bd4c 100644 --- a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java @@ -59,8 +59,9 @@ public static void sendCooldown(GeyserSession session) { CooldownType sessionPreference = session.getPreferencesCache().getCooldownPreference(); if (sessionPreference == CooldownType.DISABLED) return; - if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) + if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) { return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used + } // Set the times to stay a bit with no fade in nor out SetTitlePacket titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.TIMES);