From 296c4a3e36257b1ed5a99f971f625b84f98af79d Mon Sep 17 00:00:00 2001 From: Beanes Date: Sat, 20 Jun 2026 01:20:01 +0200 Subject: [PATCH 1/2] Track titles to restore them after hit cooldown --- .../protocol/v1_9to1_8/Protocol1_9To1_8.java | 2 + .../cooldown/TitleCooldownVisualization.java | 134 ++++++++++++++++++ .../rewriter/PlayerPacketRewriter1_9.java | 3 + 3 files changed, 139 insertions(+) diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/Protocol1_9To1_8.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/Protocol1_9To1_8.java index b4140ccbf..3f84c82b6 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/Protocol1_9To1_8.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/Protocol1_9To1_8.java @@ -19,6 +19,7 @@ import com.viaversion.viabackwards.api.BackwardsProtocol; import com.viaversion.viarewind.api.data.RewindMappingData; +import com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization; import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider; import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.BlockItemPacketRewriter1_9; import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.EntityPacketRewriter1_9; @@ -87,6 +88,7 @@ public void init(UserConnection connection) { connection.put(new LevitationStorage()); connection.put(new PlayerPositionTracker()); connection.put(new CooldownStorage()); + connection.put(new TitleCooldownVisualization.StoredTitle()); connection.put(new BlockPlaceDestroyTracker()); connection.put(new BossBarStorage(connection)); } diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/cooldown/TitleCooldownVisualization.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/cooldown/TitleCooldownVisualization.java index 23eba07b4..3fd10c031 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/cooldown/TitleCooldownVisualization.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/cooldown/TitleCooldownVisualization.java @@ -18,9 +18,11 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.cooldown; import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8; +import com.viaversion.viaversion.api.connection.StorableObject; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.type.Types; +import com.viaversion.viaversion.libs.gson.JsonElement; import com.viaversion.viaversion.libs.gson.JsonPrimitive; import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8; import java.util.function.Consumer; @@ -31,15 +33,44 @@ public class TitleCooldownVisualization implements CooldownVisualization { private static final int ACTION_SET_SUBTITLE = 1; private static final int ACTION_SET_TIMES_AND_DISPLAY = 2; private static final int ACTION_HIDE = 3; + private static final int ACTION_RESET = 4; private final UserConnection user; + private boolean visible; public TitleCooldownVisualization(UserConnection user) { this.user = user; } + public static void trackTitle(PacketWrapper wrapper) { + final StoredTitle title = wrapper.user().get(StoredTitle.class); + final int action = wrapper.passthrough(Types.VAR_INT); + if (action == ACTION_SET_TITLE) { + title.setTitle(wrapper.passthrough(Types.COMPONENT)); + } else if (action == ACTION_SET_SUBTITLE) { + title.setSubtitle(wrapper.passthrough(Types.COMPONENT)); + } else if (action == ACTION_SET_TIMES_AND_DISPLAY) { + final int fadeIn = wrapper.passthrough(Types.INT); + final int stay = wrapper.passthrough(Types.INT); + final int fadeOut = wrapper.passthrough(Types.INT); + title.setTimes(fadeIn, stay, fadeOut); + } else if (action == ACTION_HIDE) { + title.clear(); + } else if (action == ACTION_RESET) { + title.reset(); + } + } + @Override public void show(double progress) throws Exception { + final StoredTitle title = user.get(StoredTitle.class); + if (title.isVisible() && title.title() != null) { + if (visible) { + restoreTitle(title); + } + return; + } + final String text = CooldownVisualization.buildProgressText("˙", progress); sendTitlePacket(ACTION_SET_TITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive(""))); @@ -49,12 +80,38 @@ public void show(double progress) throws Exception { wrapper.write(Types.INT, 2); wrapper.write(Types.INT, 5); }); + visible = true; } @Override public void hide() throws Exception { + if (!visible) { + return; + } + + final StoredTitle title = user.get(StoredTitle.class); + if (title.isVisible() && title.title() != null) { + restoreTitle(title); + return; + } + sendTitlePacket(ACTION_HIDE, wrapper -> { }); + visible = false; + } + + private void restoreTitle(StoredTitle title) { + final int[] times = title.remainingTimes(); + sendTitlePacket(ACTION_SET_TIMES_AND_DISPLAY, wrapper -> { + wrapper.write(Types.INT, times[0]); + wrapper.write(Types.INT, times[1]); + wrapper.write(Types.INT, times[2]); + }); + if (title.subtitle() != null) { + sendTitlePacket(ACTION_SET_SUBTITLE, wrapper -> wrapper.write(Types.COMPONENT, title.subtitle())); + } + sendTitlePacket(ACTION_SET_TITLE, wrapper -> wrapper.write(Types.COMPONENT, title.title())); + visible = false; } private void sendTitlePacket(int action, Consumer writer) { @@ -64,4 +121,81 @@ private void sendTitlePacket(int action, Consumer writer) { title.scheduleSend(Protocol1_9To1_8.class); } + public static final class StoredTitle implements StorableObject { + private JsonElement title; + private JsonElement subtitle; + private int fadeIn = 10; + private int stay = 70; + private int fadeOut = 20; + private long visibleSince; + private long visibleUntil; + + public void setTitle(JsonElement title) { + final long now = System.currentTimeMillis(); + this.title = title; + visibleSince = now; + visibleUntil = now + titleLength(); + } + + public void setSubtitle(JsonElement subtitle) { + this.subtitle = subtitle; + } + + public void setTimes(int fadeIn, int stay, int fadeOut) { + final long now = System.currentTimeMillis(); + this.fadeIn = fadeIn; + this.stay = stay; + this.fadeOut = fadeOut; + if (isVisible()) { + visibleSince = now; + visibleUntil = now + titleLength(); + } + } + + public void clear() { + visibleSince = 0; + visibleUntil = 0; + } + + public void reset() { + clear(); + title = null; + subtitle = null; + fadeIn = 10; + stay = 70; + fadeOut = 20; + } + + public boolean isVisible() { + return visibleUntil > System.currentTimeMillis(); + } + + public JsonElement title() { + return title; + } + + public JsonElement subtitle() { + return subtitle; + } + + public int[] remainingTimes() { + final int fullLength = Math.max(0, fadeIn + stay + fadeOut); + final int elapsed = (int) Math.min(fullLength, Math.max(0, (System.currentTimeMillis() - visibleSince) / 50L)); + if (elapsed < fadeIn) { + return new int[]{fadeIn - elapsed, stay, fadeOut}; + } + + final int titleEnd = fadeIn + stay; + if (elapsed < titleEnd) { + return new int[]{0, titleEnd - elapsed, fadeOut}; + } + + return new int[]{0, 0, fullLength - elapsed}; + } + + private long titleLength() { + return Math.max(0, fadeIn + stay + fadeOut) * 50L; + } + } + } diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java index 2bc10570c..e5e43baa7 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java @@ -23,6 +23,7 @@ import com.viaversion.viarewind.ViaRewind; import com.viaversion.viarewind.api.type.RewindTypes; import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8; +import com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization; import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BlockPlaceDestroyTracker; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BossBarStorage; @@ -107,6 +108,8 @@ public void register() { protocol.cancelClientbound(ClientboundPackets1_9.COOLDOWN); + protocol.registerClientbound(ClientboundPackets1_9.SET_TITLES, TitleCooldownVisualization::trackTitle); + protocol.registerClientbound(ClientboundPackets1_9.CUSTOM_PAYLOAD, new PacketHandlers() { @Override public void register() { From a692d9df5c7bc2740998534a3ab62f54115f6faf Mon Sep 17 00:00:00 2001 From: Florian Reuth Date: Sat, 25 Jul 2026 21:04:54 +0200 Subject: [PATCH 2/2] Cleanup code --- .../protocol/v1_9to1_8/Protocol1_9To1_8.java | 4 +- .../cooldown/TitleCooldownVisualization.java | 191 +++++------------- .../rewriter/PlayerPacketRewriter1_9.java | 30 ++- .../protocol/v1_9to1_8/storage/LastTitle.java | 107 ++++++++++ 4 files changed, 186 insertions(+), 146 deletions(-) create mode 100644 common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/storage/LastTitle.java diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/Protocol1_9To1_8.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/Protocol1_9To1_8.java index 3ec6ab9b5..5c88d8eb3 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/Protocol1_9To1_8.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/Protocol1_9To1_8.java @@ -19,7 +19,6 @@ import com.viaversion.viabackwards.api.BackwardsProtocol; import com.viaversion.viarewind.api.data.RewindMappingData; -import com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization; import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider; import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.BlockItemPacketRewriter1_9; import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.EntityPacketRewriter1_9; @@ -31,6 +30,7 @@ import com.viaversion.viarewind.protocol.v1_9to1_8.storage.EntityTracker1_9; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LevitationStorage; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.PlayerPositionTracker; +import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.WindowTracker; import com.viaversion.viarewind.protocol.v1_9to1_8.task.CooldownIndicatorTask; import com.viaversion.viarewind.protocol.v1_9to1_8.task.LevitationUpdateTask; @@ -88,7 +88,7 @@ public void init(UserConnection connection) { connection.put(new LevitationStorage()); connection.put(new PlayerPositionTracker()); connection.put(new CooldownStorage()); - connection.put(new TitleCooldownVisualization.StoredTitle()); + connection.put(new LastTitle()); connection.put(new BlockPlaceDestroyTracker()); connection.put(new BossBarStorage(connection)); } diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/cooldown/TitleCooldownVisualization.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/cooldown/TitleCooldownVisualization.java index 3fd10c031..78efd9a00 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/cooldown/TitleCooldownVisualization.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/cooldown/TitleCooldownVisualization.java @@ -18,22 +18,20 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.cooldown; import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8; -import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.type.Types; -import com.viaversion.viaversion.libs.gson.JsonElement; import com.viaversion.viaversion.libs.gson.JsonPrimitive; import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8; -import java.util.function.Consumer; public class TitleCooldownVisualization implements CooldownVisualization { - private static final int ACTION_SET_TITLE = 0; - private static final int ACTION_SET_SUBTITLE = 1; - private static final int ACTION_SET_TIMES_AND_DISPLAY = 2; - private static final int ACTION_HIDE = 3; - private static final int ACTION_RESET = 4; + public static final int ACTION_SET_TITLE = 0; + public static final int ACTION_SET_SUBTITLE = 1; + public static final int ACTION_SET_TIMES_AND_DISPLAY = 2; + public static final int ACTION_HIDE = 3; + public static final int ACTION_RESET = 4; private final UserConnection user; private boolean visible; @@ -42,160 +40,73 @@ public TitleCooldownVisualization(UserConnection user) { this.user = user; } - public static void trackTitle(PacketWrapper wrapper) { - final StoredTitle title = wrapper.user().get(StoredTitle.class); - final int action = wrapper.passthrough(Types.VAR_INT); - if (action == ACTION_SET_TITLE) { - title.setTitle(wrapper.passthrough(Types.COMPONENT)); - } else if (action == ACTION_SET_SUBTITLE) { - title.setSubtitle(wrapper.passthrough(Types.COMPONENT)); - } else if (action == ACTION_SET_TIMES_AND_DISPLAY) { - final int fadeIn = wrapper.passthrough(Types.INT); - final int stay = wrapper.passthrough(Types.INT); - final int fadeOut = wrapper.passthrough(Types.INT); - title.setTimes(fadeIn, stay, fadeOut); - } else if (action == ACTION_HIDE) { - title.clear(); - } else if (action == ACTION_RESET) { - title.reset(); - } - } - @Override public void show(double progress) throws Exception { - final StoredTitle title = user.get(StoredTitle.class); - if (title.isVisible() && title.title() != null) { - if (visible) { - restoreTitle(title); - } + final LastTitle title = this.user.get(LastTitle.class); + if (this.visible && title.isVisible()) { + this.restoreTitle(title); return; } - final String text = CooldownVisualization.buildProgressText("˙", progress); - - sendTitlePacket(ACTION_SET_TITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive(""))); - sendTitlePacket(ACTION_SET_SUBTITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive(text))); - sendTitlePacket(ACTION_SET_TIMES_AND_DISPLAY, wrapper -> { - wrapper.write(Types.INT, 0); - wrapper.write(Types.INT, 2); - wrapper.write(Types.INT, 5); - }); - visible = true; + final PacketWrapper setTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user); + setTitle.write(Types.VAR_INT, ACTION_SET_TITLE); + setTitle.write(Types.COMPONENT, new JsonPrimitive("")); + setTitle.send(Protocol1_9To1_8.class); + + final PacketWrapper setSubtitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user); + setSubtitle.write(Types.VAR_INT, ACTION_SET_SUBTITLE); + setSubtitle.write(Types.COMPONENT, new JsonPrimitive(CooldownVisualization.buildProgressText("˙", progress))); + setSubtitle.send(Protocol1_9To1_8.class); + + final PacketWrapper setTitleTimes = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user); + setTitleTimes.write(Types.VAR_INT, ACTION_SET_TIMES_AND_DISPLAY); + setTitleTimes.write(Types.INT, 0); + setTitleTimes.write(Types.INT, 2); + setTitleTimes.write(Types.INT, 5); + setTitleTimes.send(Protocol1_9To1_8.class); + this.visible = true; } @Override public void hide() throws Exception { - if (!visible) { + if (!this.visible) { return; } - final StoredTitle title = user.get(StoredTitle.class); - if (title.isVisible() && title.title() != null) { - restoreTitle(title); + final LastTitle title = this.user.get(LastTitle.class); + if (title.isVisible()) { + this.restoreTitle(title); return; } - sendTitlePacket(ACTION_HIDE, wrapper -> { - }); - visible = false; + final PacketWrapper hideTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user); + hideTitle.write(Types.VAR_INT, ACTION_HIDE); + hideTitle.send(Protocol1_9To1_8.class); + this.visible = false; } - private void restoreTitle(StoredTitle title) { + private void restoreTitle(final LastTitle title) { + final PacketWrapper setTitleTimes = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user); + setTitleTimes.write(Types.VAR_INT, ACTION_SET_TIMES_AND_DISPLAY); final int[] times = title.remainingTimes(); - sendTitlePacket(ACTION_SET_TIMES_AND_DISPLAY, wrapper -> { - wrapper.write(Types.INT, times[0]); - wrapper.write(Types.INT, times[1]); - wrapper.write(Types.INT, times[2]); - }); - if (title.subtitle() != null) { - sendTitlePacket(ACTION_SET_SUBTITLE, wrapper -> wrapper.write(Types.COMPONENT, title.subtitle())); - } - sendTitlePacket(ACTION_SET_TITLE, wrapper -> wrapper.write(Types.COMPONENT, title.title())); - visible = false; - } - - private void sendTitlePacket(int action, Consumer writer) { - final PacketWrapper title = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user); - title.write(Types.VAR_INT, action); - writer.accept(title); - title.scheduleSend(Protocol1_9To1_8.class); - } - - public static final class StoredTitle implements StorableObject { - private JsonElement title; - private JsonElement subtitle; - private int fadeIn = 10; - private int stay = 70; - private int fadeOut = 20; - private long visibleSince; - private long visibleUntil; - - public void setTitle(JsonElement title) { - final long now = System.currentTimeMillis(); - this.title = title; - visibleSince = now; - visibleUntil = now + titleLength(); - } - - public void setSubtitle(JsonElement subtitle) { - this.subtitle = subtitle; - } - - public void setTimes(int fadeIn, int stay, int fadeOut) { - final long now = System.currentTimeMillis(); - this.fadeIn = fadeIn; - this.stay = stay; - this.fadeOut = fadeOut; - if (isVisible()) { - visibleSince = now; - visibleUntil = now + titleLength(); - } - } - - public void clear() { - visibleSince = 0; - visibleUntil = 0; - } - - public void reset() { - clear(); - title = null; - subtitle = null; - fadeIn = 10; - stay = 70; - fadeOut = 20; - } - - public boolean isVisible() { - return visibleUntil > System.currentTimeMillis(); - } + setTitleTimes.write(Types.INT, times[0]); + setTitleTimes.write(Types.INT, times[1]); + setTitleTimes.write(Types.INT, times[2]); + setTitleTimes.send(Protocol1_9To1_8.class); - public JsonElement title() { - return title; - } - - public JsonElement subtitle() { - return subtitle; + if (title.subtitle() != null) { + final PacketWrapper setSubtitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user); + setSubtitle.write(Types.VAR_INT, ACTION_SET_SUBTITLE); + setSubtitle.write(Types.COMPONENT, title.subtitle()); + setSubtitle.send(Protocol1_9To1_8.class); } - public int[] remainingTimes() { - final int fullLength = Math.max(0, fadeIn + stay + fadeOut); - final int elapsed = (int) Math.min(fullLength, Math.max(0, (System.currentTimeMillis() - visibleSince) / 50L)); - if (elapsed < fadeIn) { - return new int[]{fadeIn - elapsed, stay, fadeOut}; - } - - final int titleEnd = fadeIn + stay; - if (elapsed < titleEnd) { - return new int[]{0, titleEnd - elapsed, fadeOut}; - } + final PacketWrapper setTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user); + setTitle.write(Types.VAR_INT, ACTION_SET_TITLE); + setTitle.write(Types.COMPONENT, title.title()); + setTitle.send(Protocol1_9To1_8.class); - return new int[]{0, 0, fullLength - elapsed}; - } - - private long titleLength() { - return Math.max(0, fadeIn + stay + fadeOut) * 50L; - } + this.visible = false; } } diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java index b7626c0cf..e9c707ec7 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java @@ -21,14 +21,13 @@ import com.viaversion.nbt.tag.ListTag; import com.viaversion.nbt.tag.StringTag; import com.viaversion.viarewind.ViaRewind; -import com.viaversion.viarewind.api.type.RewindTypes; import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8; -import com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization; import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BlockPlaceDestroyTracker; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BossBarStorage; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CooldownStorage; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.EntityTracker1_9; +import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle; import com.viaversion.viarewind.protocol.v1_9to1_8.storage.PlayerPositionTracker; import com.viaversion.viarewind.utils.ChatUtil; import com.viaversion.viaversion.api.Via; @@ -44,7 +43,6 @@ import com.viaversion.viaversion.api.rewriter.RewriterBase; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.libs.gson.JsonElement; -import com.viaversion.viaversion.libs.gson.JsonParser; import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8; import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8; @@ -54,6 +52,12 @@ import java.util.List; import java.util.UUID; +import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_HIDE; +import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_RESET; +import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_SUBTITLE; +import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_TIMES_AND_DISPLAY; +import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_TITLE; + public class PlayerPacketRewriter1_9 extends RewriterBase { public PlayerPacketRewriter1_9(Protocol1_9To1_8 protocol) { @@ -108,7 +112,25 @@ public void register() { protocol.cancelClientbound(ClientboundPackets1_9.COOLDOWN); - protocol.registerClientbound(ClientboundPackets1_9.SET_TITLES, TitleCooldownVisualization::trackTitle); + protocol.registerClientbound(ClientboundPackets1_9.SET_TITLES, wrapper -> { + final int action = wrapper.passthrough(Types.VAR_INT); + if (action == ACTION_SET_TITLE) { + final JsonElement title = wrapper.passthrough(Types.COMPONENT); + wrapper.user().get(LastTitle.class).setTitle(title); + } else if (action == ACTION_SET_SUBTITLE) { + final JsonElement subtitle = wrapper.passthrough(Types.COMPONENT); + wrapper.user().get(LastTitle.class).setSubtitle(subtitle); + } else if (action == ACTION_SET_TIMES_AND_DISPLAY) { + final int fadeIn = wrapper.passthrough(Types.INT); + final int stay = wrapper.passthrough(Types.INT); + final int fadeOut = wrapper.passthrough(Types.INT); + wrapper.user().get(LastTitle.class).setTimes(fadeIn, stay, fadeOut); + } else if (action == ACTION_HIDE) { + wrapper.user().get(LastTitle.class).hide(); + } else if (action == ACTION_RESET) { + wrapper.user().get(LastTitle.class).reset(); + } + }); protocol.registerClientbound(ClientboundPackets1_9.CUSTOM_PAYLOAD, new PacketHandlers() { @Override diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/storage/LastTitle.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/storage/LastTitle.java new file mode 100644 index 000000000..03e6ac9f5 --- /dev/null +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/storage/LastTitle.java @@ -0,0 +1,107 @@ +/* + * This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind + * Copyright (C) 2018-2026 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.viarewind.protocol.v1_9to1_8.storage; + +import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.libs.gson.JsonElement; + +public final class LastTitle implements StorableObject { + + private JsonElement title; + private JsonElement subtitle; + + private long visibleSince; + private long visibleUntil; + + private int fadeIn; + private int stay; + private int fadeOut; + + public LastTitle() { + this.reset(); + } + + public void setTitle(final JsonElement title) { + this.title = title; + + final long now = System.currentTimeMillis(); + this.visibleSince = now; + this.visibleUntil = now + titleLength(); + } + + public void setSubtitle(final JsonElement subtitle) { + this.subtitle = subtitle; + } + + public void setTimes(final int fadeIn, final int stay, final int fadeOut) { + this.fadeIn = fadeIn; + this.stay = stay; + this.fadeOut = fadeOut; + + if (this.visibleUntil > System.currentTimeMillis()) { + final long now = System.currentTimeMillis(); + this.visibleSince = now; + this.visibleUntil = now + titleLength(); + } + } + + public void hide() { + this.visibleSince = 0; + this.visibleUntil = 0; + } + + public void reset() { + this.hide(); + + this.fadeIn = 10; + this.stay = 70; + this.fadeOut = 20; + } + + public boolean isVisible() { + return this.title != null && this.visibleUntil > System.currentTimeMillis(); + } + + public JsonElement title() { + return this.title; + } + + public JsonElement subtitle() { + return this.subtitle; + } + + public int[] remainingTimes() { + final int fullLength = Math.max(0, this.fadeIn + this.stay + this.fadeOut); + final int elapsed = (int) Math.min(fullLength, Math.max(0, (System.currentTimeMillis() - this.visibleSince) / 50L)); + if (elapsed < this.fadeIn) { + return new int[]{this.fadeIn - elapsed, this.stay, this.fadeOut}; + } + + final int titleEnd = this.fadeIn + this.stay; + if (elapsed < titleEnd) { + return new int[]{0, titleEnd - elapsed, this.fadeOut}; + } + + return new int[]{0, 0, fullLength - elapsed}; + } + + private long titleLength() { + return Math.max(0, this.fadeIn + this.stay + this.fadeOut) * 50L; + } + +}