Better handling of fake cooldown

Because of Bedrock limitations, if a player has text background opacity enabled, they'll see an empty section where the title is usually displayed as the fake cooldown is shown. This commit minimizes the time that is shown by clearing the text as soon as possible. Reference issue: https://github.com/GeyserMC/Geyser/issues/1710

This commit also removes starting the fake cooldown process if the client switches to an inventory slot with the same Java ID.
This commit is contained in:
Camotoy 2022-03-25 20:22:39 -04:00
parent c610e98f4c
commit f639be6362
No known key found for this signature in database
GPG Key ID: 7EEFB66FE798081F
6 changed files with 105 additions and 23 deletions

View File

@ -26,24 +26,36 @@
package org.geysermc.geyser.session.cache;
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.scoreboard.Scoreboard;
import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession;
import org.geysermc.geyser.session.GeyserSession;
@Getter
public class WorldCache {
public final class WorldCache {
private final GeyserSession session;
@Getter
private final ScoreboardSession scoreboardSession;
@Getter
private Scoreboard scoreboard;
@Getter
@Setter
private Difficulty difficulty = Difficulty.EASY;
/**
* Whether our cooldown changed the title time, and the true title times need to be re-sent.
*/
private boolean titleTimesNeedReset = false;
private int trueTitleFadeInTime;
private int trueTitleStayTime;
private int trueTitleFadeOutTime;
public WorldCache(GeyserSession session) {
this.session = session;
this.scoreboard = new Scoreboard(session);
scoreboardSession = new ScoreboardSession(session);
resetTitleTimes(false);
}
public void removeScoreboard() {
@ -58,4 +70,53 @@ public class WorldCache {
int pps = scoreboardSession.getPacketsPerSecond();
return Math.max(pps, pendingPps);
}
public void markTitleTimesAsIncorrect() {
titleTimesNeedReset = true;
}
/**
* Store the true active title times.
*/
public void setTitleTimes(int fadeInTime, int stayTime, int fadeOutTime) {
trueTitleFadeInTime = fadeInTime;
trueTitleStayTime = stayTime;
trueTitleFadeOutTime = fadeOutTime;
}
/**
* If needed, ensure that the Bedrock client will use the correct timings for titles.
*/
public void synchronizeCorrectTitleTimes() {
if (titleTimesNeedReset) {
forceSyncCorrectTitleTimes();
titleTimesNeedReset = false;
}
}
private void forceSyncCorrectTitleTimes() {
SetTitlePacket titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.TIMES);
titlePacket.setText("");
titlePacket.setFadeInTime(trueTitleFadeInTime);
titlePacket.setStayTime(trueTitleStayTime);
titlePacket.setFadeOutTime(trueTitleFadeOutTime);
titlePacket.setPlatformOnlineId("");
titlePacket.setXuid("");
session.sendUpstreamPacket(titlePacket);
}
/**
* Reset the true active title times to the (Java Edition 1.18.2) defaults.
*/
public void resetTitleTimes(boolean clientSync) {
trueTitleFadeInTime = 10;
trueTitleStayTime = 70;
trueTitleFadeOutTime = 20;
if (clientSync) {
forceSyncCorrectTitleTimes();
}
}
}

View File

@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@ -42,8 +43,9 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
@Override
public void translate(GeyserSession session, MobEquipmentPacket packet) {
if (!session.isSpawned() || packet.getHotbarSlot() > 8 ||
packet.getContainerId() != ContainerId.INVENTORY || session.getPlayerInventory().getHeldItemSlot() == packet.getHotbarSlot()) {
int newSlot = packet.getHotbarSlot();
if (!session.isSpawned() || newSlot > 8 || packet.getContainerId() != ContainerId.INVENTORY
|| session.getPlayerInventory().getHeldItemSlot() == newSlot) {
// For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention
return;
}
@ -51,12 +53,15 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
// Send book update before switching hotbar slot
session.getBookEditCache().checkForSend();
session.getPlayerInventory().setHeldItemSlot(packet.getHotbarSlot());
GeyserItemStack oldItem = session.getPlayerInventory().getItemInHand();
session.getPlayerInventory().setHeldItemSlot(newSlot);
ServerboundSetCarriedItemPacket setCarriedItemPacket = new ServerboundSetCarriedItemPacket(packet.getHotbarSlot());
ServerboundSetCarriedItemPacket setCarriedItemPacket = new ServerboundSetCarriedItemPacket(newSlot);
session.sendDownstreamPacket(setCarriedItemPacket);
if (session.isSneaking() && session.getPlayerInventory().getItemInHand().getJavaId() == session.getItemMappings().getStoredItems().shield().getJavaId()) {
GeyserItemStack newItem = session.getPlayerInventory().getItemInHand();
if (session.isSneaking() && newItem.getJavaId() == session.getItemMappings().getStoredItems().shield().getJavaId()) {
// Activate shield since we are already sneaking
// (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
@ -64,8 +69,10 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
50, TimeUnit.MILLISECONDS);
}
// Java sends a cooldown indicator whenever you switch an item
CooldownUtils.sendCooldown(session);
if (oldItem.getJavaId() != newItem.getJavaId()) {
// Java sends a cooldown indicator whenever you switch to a new item type
CooldownUtils.sendCooldown(session);
}
// Update the interactive tag, if an entity is present
if (session.getMouseoverEntity() != null) {

View File

@ -37,11 +37,14 @@ public class JavaClearTitlesTranslator extends PacketTranslator<ClientboundClear
@Override
public void translate(GeyserSession session, ClientboundClearTitlesPacket packet) {
SetTitlePacket titlePacket = new SetTitlePacket();
// TODO handle packet.isResetTimes()
titlePacket.setType(SetTitlePacket.Type.CLEAR);
titlePacket.setText("");
titlePacket.setXuid("");
titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket);
if (packet.isResetTimes()) {
session.getWorldCache().resetTitleTimes(true);
}
}
}

View File

@ -38,6 +38,8 @@ public class JavaSetTitleTextTranslator extends PacketTranslator<ClientboundSetT
@Override
public void translate(GeyserSession session, ClientboundSetTitleTextPacket packet) {
session.getWorldCache().synchronizeCorrectTitleTimes();
String text;
if (packet.getText() == null || Component.empty().equals(packet.getText())) { // This can happen, see https://github.com/KyoriPowered/adventure/issues/447
text = " ";

View File

@ -36,12 +36,17 @@ public class JavaSetTitlesAnimationTranslator extends PacketTranslator<Clientbou
@Override
public void translate(GeyserSession session, ClientboundSetTitlesAnimationPacket packet) {
int fadeInTime = packet.getFadeIn();
int stayTime = packet.getStay();
int fadeOutTime = packet.getFadeOut();
session.getWorldCache().setTitleTimes(fadeInTime, stayTime, fadeOutTime);
SetTitlePacket titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.TIMES);
titlePacket.setText("");
titlePacket.setFadeInTime(packet.getFadeIn());
titlePacket.setFadeOutTime(packet.getFadeOut());
titlePacket.setStayTime(packet.getStay());
titlePacket.setFadeInTime(fadeInTime);
titlePacket.setFadeOutTime(fadeOutTime);
titlePacket.setStayTime(stayTime);
titlePacket.setXuid("");
titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket);

View File

@ -57,8 +57,19 @@ public class CooldownUtils {
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
// Needs to be sent or no subtitle packet is recognized by the client
// Set the times to stay a bit with no fade in nor out
SetTitlePacket titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.TIMES);
titlePacket.setStayTime(1000);
titlePacket.setText("");
titlePacket.setXuid("");
titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket);
session.getWorldCache().markTitleTimesAsIncorrect();
// Needs to be sent or no subtitle packet is recognized by the client
titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.TITLE);
titlePacket.setText(" ");
titlePacket.setXuid("");
@ -85,9 +96,6 @@ public class CooldownUtils {
titlePacket.setType(SetTitlePacket.Type.SUBTITLE);
}
titlePacket.setText(getTitle(session));
titlePacket.setFadeInTime(0);
titlePacket.setFadeOutTime(5);
titlePacket.setStayTime(2);
titlePacket.setXuid("");
titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket);
@ -96,11 +104,7 @@ public class CooldownUtils {
computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50
} else {
SetTitlePacket removeTitlePacket = new SetTitlePacket();
if (sessionPreference == CooldownType.ACTIONBAR) {
removeTitlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
} else {
removeTitlePacket.setType(SetTitlePacket.Type.SUBTITLE);
}
removeTitlePacket.setType(SetTitlePacket.Type.CLEAR);
removeTitlePacket.setText(" ");
removeTitlePacket.setXuid("");
removeTitlePacket.setPlatformOnlineId("");