mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Proper tick rate handling
This commit is contained in:
parent
2a6025f3fc
commit
3d957e35b8
5 changed files with 103 additions and 46 deletions
|
|
@ -187,7 +187,7 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
|||
@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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -422,3 +422,4 @@ public class LivingEntity extends Entity {
|
|||
return type.getAttribute((float) AttributeUtils.calculateValue(javaAttribute));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 class ThrowableItemEntity extends ThrowableEntity {
|
|||
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() {
|
||||
|
|
|
|||
|
|
@ -550,7 +550,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
/**
|
||||
* Stores cookies sent by the Java server.
|
||||
*/
|
||||
@Setter @Getter
|
||||
@Setter
|
||||
@Getter
|
||||
private Map<String, byte[]> 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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
// 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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
sendUpstreamPacket(packet);
|
||||
|
||||
final PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getOrCreateTask(
|
||||
getAuthData().xuid()
|
||||
getAuthData().xuid()
|
||||
);
|
||||
task.setOnline(true);
|
||||
task.resetTimer();
|
||||
|
|
@ -800,13 +805,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
}
|
||||
|
||||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
}
|
||||
|
||||
/**
|
||||
* Called every 50 milliseconds - one Minecraft tick.
|
||||
* Called every Minecraft tick - 1000/tickRate milliseconds.
|
||||
*/
|
||||
protected void tick() {
|
||||
try {
|
||||
|
|
@ -1171,7 +1183,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
// 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 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
*/
|
||||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
|
||||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
* 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 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
*/
|
||||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
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 class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
|
||||
@Override
|
||||
public UUID javaUuid() {
|
||||
return playerEntity != null ? playerEntity.getUuid() : null ;
|
||||
return playerEntity != null ? playerEntity.getUuid() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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<ClientboundTickingStatePacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundTickingStatePacket packet) {
|
||||
session.updateTickData(packet.getTickRate(), packet.isFrozen());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue