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 eecf4da90..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 @@ -30,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; @@ -87,6 +88,7 @@ public void init(UserConnection connection) { connection.put(new LevitationStorage()); connection.put(new PlayerPositionTracker()); connection.put(new CooldownStorage()); + 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 23eba07b4..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,21 +18,23 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.cooldown; import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8; +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.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; + 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; public TitleCooldownVisualization(UserConnection user) { this.user = user; @@ -40,28 +42,71 @@ public TitleCooldownVisualization(UserConnection user) { @Override public void show(double progress) throws Exception { - 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); - }); + final LastTitle title = this.user.get(LastTitle.class); + if (this.visible && title.isVisible()) { + this.restoreTitle(title); + return; + } + + 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 { - sendTitlePacket(ACTION_HIDE, wrapper -> { - }); + if (!this.visible) { + return; + } + + final LastTitle title = this.user.get(LastTitle.class); + if (title.isVisible()) { + this.restoreTitle(title); + return; + } + + 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 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); + 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(); + setTitleTimes.write(Types.INT, times[0]); + setTitleTimes.write(Types.INT, times[1]); + setTitleTimes.write(Types.INT, times[2]); + setTitleTimes.send(Protocol1_9To1_8.class); + + 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); + } + + 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); + + 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 5657fc91e..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,13 +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.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; @@ -43,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; @@ -53,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) { @@ -107,6 +112,26 @@ public void register() { protocol.cancelClientbound(ClientboundPackets1_9.COOLDOWN); + 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 public void register() { 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; + } + +}