mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-22 14:34:59 +01:00
Handle ClientboundTickingStatePacket correctly and fix Throwable Scales (#4850)
* Proper tick rate handling * Fix frozen variable getter * Fix formatting i think third attempt * Formatting fix attempt 5 fsdiofhsdioufhvuisdhviuo9ds * Fix stuff, also fixed the sizing of throwables as they were to big * Move update ticking state * Update core/src/main/java/org/geysermc/geyser/session/GeyserSession.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * Fixes for spaces and documentation * Missed a space * wait now ive fixed it * Fix languages * try again to fix languages * Fix Java doc comments for tickable interface * Fix javadoc comment in Geyser Session * fix comment * fix some tick rate stuffs * Fix build fail * fix some stuff * merge * test * Update languages * Update mappings * delete broken stuff * Fix cooldown * fix cooldowns * Update core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java Co-authored-by: chris <github@onechris.mozmail.com> * Update BoatEntity.java * Update GeyserSession.java * fix some stuff * Update CooldownUtils.java * fix some accidental formatting issues * Fix missing inport * Update GeyserSession.java * Update core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java Co-authored-by: chris <github@onechris.mozmail.com> * Update core/src/main/java/org/geysermc/geyser/entity/type/ThrowableItemEntity.java Co-authored-by: chris <github@onechris.mozmail.com> * Fix missing import --------- Co-authored-by: rtm516 <rtm516@users.noreply.github.com> Co-authored-by: chris <github@onechris.mozmail.com>
This commit is contained in:
parent
1fea22d980
commit
8b232d7900
13 changed files with 278 additions and 84 deletions
|
@ -38,6 +38,7 @@ import org.geysermc.geyser.entity.type.ChestBoatEntity;
|
|||
import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity;
|
||||
import org.geysermc.geyser.entity.type.DisplayBaseEntity;
|
||||
import org.geysermc.geyser.entity.type.EnderCrystalEntity;
|
||||
import org.geysermc.geyser.entity.type.EnderEyeEntity;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.EvokerFangsEntity;
|
||||
import org.geysermc.geyser.entity.type.ExpOrbEntity;
|
||||
|
@ -197,7 +198,7 @@ public final class EntityDefinitions {
|
|||
public static final EntityDefinition<EvokerFangsEntity> EVOKER_FANGS;
|
||||
public static final EntityDefinition<ThrowableItemEntity> EXPERIENCE_BOTTLE;
|
||||
public static final EntityDefinition<ExpOrbEntity> EXPERIENCE_ORB;
|
||||
public static final EntityDefinition<Entity> EYE_OF_ENDER;
|
||||
public static final EntityDefinition<EnderEyeEntity> EYE_OF_ENDER;
|
||||
public static final EntityDefinition<FallingBlockEntity> FALLING_BLOCK;
|
||||
public static final EntityDefinition<FireballEntity> FIREBALL;
|
||||
public static final EntityDefinition<FireworkEntity> FIREWORK_ROCKET;
|
||||
|
@ -345,7 +346,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")
|
||||
|
|
|
@ -189,7 +189,7 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
|||
session.sendDownstreamGamePacket(steerPacket);
|
||||
return;
|
||||
}
|
||||
doTick = !doTick; // Run every 100 ms
|
||||
doTick = !doTick; // Run every other tick
|
||||
if (!doTick || passengers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -25,12 +25,14 @@
|
|||
|
||||
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.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
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;
|
||||
|
||||
|
@ -39,7 +41,7 @@ import java.util.UUID;
|
|||
*/
|
||||
public class ThrowableItemEntity extends ThrowableEntity {
|
||||
/**
|
||||
* Number of ticks since the entity was spawned by the Java server
|
||||
* Number of draw ticks since the entity was spawned by the Java server
|
||||
*/
|
||||
private int age;
|
||||
private boolean invisible;
|
||||
|
@ -48,29 +50,38 @@ public class ThrowableItemEntity extends ThrowableEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
setFlag(EntityFlag.INVISIBLE, true);
|
||||
invisible = false;
|
||||
}
|
||||
|
||||
private void checkVisibility() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
age++;
|
||||
age = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
// Correct sizing
|
||||
dirtyMetadata.put(EntityDataTypes.SCALE, 0.5f);
|
||||
}
|
||||
|
||||
private void checkVisibility() {
|
||||
age++;
|
||||
|
||||
// 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().down(EntityDefinitions.PLAYER.offset());
|
||||
setInvisible(playerPos.distanceSquared(position.add(0, definition.offset(), 0)) < 12.25);
|
||||
} else {
|
||||
setInvisible(age < 2);
|
||||
}
|
||||
|
||||
if (invisible != getFlag(EntityFlag.INVISIBLE)) {
|
||||
setFlag(EntityFlag.INVISIBLE, invisible);
|
||||
updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTick() {
|
||||
checkVisibility();
|
||||
super.tick();
|
||||
super.drawTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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,8 +26,21 @@
|
|||
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
|
||||
* 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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -183,6 +183,7 @@ import org.geysermc.geyser.util.ChunkUtils;
|
|||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
import org.geysermc.geyser.util.LoginEncryptionUtils;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.geyser.util.MinecraftAuthLogger;
|
||||
import org.geysermc.mcprotocollib.auth.GameProfile;
|
||||
import org.geysermc.mcprotocollib.network.BuiltinFlags;
|
||||
|
@ -600,7 +601,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;
|
||||
|
||||
|
@ -644,7 +645,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
/**
|
||||
* Stores cookies sent by the Java server.
|
||||
*/
|
||||
@Setter @Getter
|
||||
@Setter
|
||||
private Map<String, byte[]> cookies = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
private final GeyserCameraData cameraData;
|
||||
|
@ -653,6 +654,16 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
|
||||
private MinecraftProtocol protocol;
|
||||
|
||||
private int nanosecondsPerTick = 50000000;
|
||||
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;
|
||||
|
||||
|
||||
public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop tickEventLoop) {
|
||||
this.geyser = geyser;
|
||||
this.upstream = new UpstreamSession(bedrockServerSession);
|
||||
|
@ -895,38 +906,38 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
}
|
||||
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);
|
||||
if (ex instanceof CompletionException ce
|
||||
&& ce.getCause() instanceof MinecraftRequestException mre
|
||||
&& mre.getResponse().getStatusCode() == 404) {
|
||||
// Player is trying to join with a Microsoft account that doesn't have Java Edition purchased
|
||||
disconnect(GeyserLocale.getPlayerLocaleString("geyser.network.remote.invalid_account", locale()));
|
||||
} else {
|
||||
disconnect(ex.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (ex != null) {
|
||||
geyser.getLogger().error("Failed to log in with Microsoft code!", ex);
|
||||
if (ex instanceof CompletionException ce
|
||||
&& ce.getCause() instanceof MinecraftRequestException mre
|
||||
&& mre.getResponse().getStatusCode() == 404) {
|
||||
// Player is trying to join with a Microsoft account that doesn't have Java Edition purchased
|
||||
disconnect(GeyserLocale.getPlayerLocaleString("geyser.network.remote.invalid_account", locale()));
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -947,14 +958,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE;
|
||||
|
||||
// Start ticking
|
||||
tickThread = tickEventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS);
|
||||
tickThread = tickEventLoop.scheduleAtFixedRate(this::tick, nanosecondsPerTick, nanosecondsPerTick, TimeUnit.NANOSECONDS);
|
||||
|
||||
TcpSession downstream;
|
||||
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, tickEventLoop);
|
||||
this.protocol, this.tickEventLoop);
|
||||
this.downstream = new DownstreamSession(downstream);
|
||||
} else {
|
||||
downstream = new TcpClientSession(this.remoteServer.address(), this.remoteServer.port(), "0.0.0.0", 0, this.protocol, null, tickEventLoop);
|
||||
|
@ -1077,11 +1088,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
|
||||
// Download and load the language for the player
|
||||
MinecraftLocale.downloadAndLoadLocale(locale);
|
||||
|
||||
// if (sentSpawnPacket && !GameProtocol.isPre1_21_2(GeyserSession.this)) {
|
||||
// // Possible form to close.
|
||||
// upstream.sendPacket(new ClientboundCloseFormPacket());
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1243,6 +1249,19 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
}, duration, timeUnit);
|
||||
}
|
||||
|
||||
public void updateTickingState(float tickRate, boolean frozen) {
|
||||
tickThread.cancel(false);
|
||||
|
||||
this.tickingFrozen = frozen;
|
||||
|
||||
tickRate = MathUtils.clamp(tickRate, 1.0f, 10000.0f);
|
||||
|
||||
millisecondsPerTick = 1000.0f / tickRate;
|
||||
|
||||
nanosecondsPerTick = MathUtils.ceil(1000000000.0f / tickRate);
|
||||
tickThread = tickEventLoop.scheduleAtFixedRate(this::tick, nanosecondsPerTick, nanosecondsPerTick, TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
private void executeRunnable(Runnable runnable) {
|
||||
try {
|
||||
runnable.run();
|
||||
|
@ -1251,10 +1270,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
} catch (Throwable e) {
|
||||
geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every 50 milliseconds - one Minecraft tick.
|
||||
* Called every Minecraft tick.
|
||||
*/
|
||||
protected void tick() {
|
||||
try {
|
||||
|
@ -1280,13 +1300,21 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
isInWorldBorderWarningArea = false;
|
||||
}
|
||||
|
||||
boolean gameShouldUpdate = !tickingFrozen || stepTicks > 0;
|
||||
if (stepTicks > 0) {
|
||||
--stepTicks;
|
||||
}
|
||||
|
||||
Entity vehicle = playerEntity.getVehicle();
|
||||
if (vehicle instanceof ClientVehicle clientVehicle && vehicle.isValid()) {
|
||||
clientVehicle.getVehicleComponent().tickVehicle();
|
||||
}
|
||||
|
||||
for (Tickable entity : entityCache.getTickableEntities()) {
|
||||
entity.tick();
|
||||
entity.drawTick();
|
||||
if (gameShouldUpdate) {
|
||||
entity.tick();
|
||||
}
|
||||
}
|
||||
|
||||
if (armAnimationTicks >= 0) {
|
||||
|
@ -1424,7 +1452,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
public void setClientData(BedrockClientData data) {
|
||||
this.clientData = data;
|
||||
this.inputCache.setInputMode(
|
||||
org.cloudburstmc.protocol.bedrock.data.InputMode.values()[data.getCurrentInputMode().ordinal()]);
|
||||
org.cloudburstmc.protocol.bedrock.data.InputMode.values()[data.getCurrentInputMode().ordinal()]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1844,7 +1872,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();
|
||||
|
@ -2110,7 +2138,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
|
||||
@Override
|
||||
public UUID javaUuid() {
|
||||
return playerEntity != null ? playerEntity.getUuid() : null ;
|
||||
return playerEntity != null ? playerEntity.getUuid() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -123,9 +123,13 @@ public final class WorldCache {
|
|||
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("");
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||
// (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.getNanosecondsPerTick(), TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
if (oldItem.getJavaId() != newItem.getJavaId()) {
|
||||
|
|
|
@ -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.updateTickingState(packet.getTickRate(), packet.isFrozen());
|
||||
}
|
||||
}
|
|
@ -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.ClientboundTickingStepPacket;
|
||||
|
||||
@Translator(packet = ClientboundTickingStepPacket.class)
|
||||
public class JavaTickingStepTranslator extends PacketTranslator<ClientboundTickingStepPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundTickingStepPacket packet) {
|
||||
session.setStepTicks(packet.getTickSteps());
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ public class JavaCooldownTranslator extends PacketTranslator<ClientboundCooldown
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,13 +40,15 @@ public class JavaSetTitlesAnimationTranslator extends PacketTranslator<Clientbou
|
|||
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);
|
||||
|
|
|
@ -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;
|
||||
|
@ -50,6 +51,7 @@ public class CooldownUtils {
|
|||
|
||||
/**
|
||||
* 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) {
|
||||
|
@ -57,7 +59,9 @@ public class CooldownUtils {
|
|||
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);
|
||||
|
@ -83,6 +87,7 @@ public class CooldownUtils {
|
|||
|
||||
/**
|
||||
* 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 +107,7 @@ public class CooldownUtils {
|
|||
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), (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);
|
||||
|
@ -115,8 +120,9 @@ public class CooldownUtils {
|
|||
|
||||
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 tickrateMultiplier = Math.max(session.getMillisecondsPerTick() / 50, 1.0);
|
||||
double cooldown = restrain(((double) time) * session.getAttackSpeed() / (tickrateMultiplier * 1000.0), 1.0);
|
||||
return cooldown < 1.0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,7 +134,8 @@ public class CooldownUtils {
|
|||
|
||||
private static String getTitle(GeyserSession session) {
|
||||
long time = System.currentTimeMillis() - session.getLastHitTime();
|
||||
double cooldown = restrain(((double) time) * session.getAttackSpeed() / 1000d, 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;
|
||||
|
|
Loading…
Reference in a new issue