From 72615572f901760bc68b10132b96d31467c1fb59 Mon Sep 17 00:00:00 2001 From: rfresh2 <89827146+rfresh2@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:47:48 -0800 Subject: [PATCH] detect and track online time through 2b2t queue skips --- src/main/java/com/zenith/Proxy.java | 21 +++++++++++++++++-- .../zenith/command/impl/ReconnectCommand.java | 2 +- .../zenith/discord/DiscordEventListener.java | 2 +- .../zenith/event/proxy/DisconnectEvent.java | 7 ++++--- .../zenith/event/proxy/QueueSkipEvent.java | 6 ++++++ .../com/zenith/module/impl/AutoReconnect.java | 2 +- .../zenith/network/client/ClientSession.java | 3 ++- .../handler/incoming/SystemChatHandler.java | 12 +++++++++-- 8 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/zenith/event/proxy/QueueSkipEvent.java diff --git a/src/main/java/com/zenith/Proxy.java b/src/main/java/com/zenith/Proxy.java index c8f1a1006..72c5f0b6e 100644 --- a/src/main/java/com/zenith/Proxy.java +++ b/src/main/java/com/zenith/Proxy.java @@ -87,9 +87,11 @@ public class Proxy { protected final AtomicReference currentPlayer = new AtomicReference<>(); protected final FastArrayList activeConnections = new FastArrayList<>(ServerSession.class); private boolean inQueue = false; + private boolean didQueueSkip = false; private int queuePosition = 0; @Setter @Nullable private Instant connectTime; private Instant disconnectTime = Instant.now(); + private OptionalLong prevOnlineSeconds = OptionalLong.empty(); private Optional isPrio = Optional.empty(); @Getter private final AtomicBoolean loggingIn = new AtomicBoolean(false); @Setter @NotNull private AutoUpdater autoUpdater = NoOpAutoUpdater.INSTANCE; @@ -119,6 +121,7 @@ public void initEventHandlers() { of(StartQueueEvent.class, this::handleStartQueueEvent), of(QueuePositionUpdateEvent.class, this::handleQueuePositionUpdateEvent), of(QueueCompleteEvent.class, this::handleQueueCompleteEvent), + of(QueueSkipEvent.class, this::handleQueueSkipEvent), of(PlayerOnlineEvent.class, this::handlePlayerOnlineEvent), of(PrioStatusEvent.class, this::handlePrioStatusEvent), of(PrivateMessageSendEvent.class, this::handlePrivateMessageSendEvent) @@ -638,7 +641,7 @@ public void twoB2tTimeLimitKickWarningTick() { || !isOnlineOn2b2tForAtLeastDuration(twoB2tTimeLimit.minusMinutes(10L)) ) return; final ServerSession playerConnection = this.currentPlayer.get(); - final Duration durationUntilKick = twoB2tTimeLimit.minus(Duration.ofSeconds(Proxy.getInstance().getOnlineTimeSeconds())); + final Duration durationUntilKick = twoB2tTimeLimit.minus(Duration.ofSeconds(Proxy.getInstance().getOnlineTimeSecondsWithQueueSkip())); if (durationUntilKick.isNegative()) return; // sanity check just in case 2b's plugin changes var actionBarPacket = new ClientboundSetActionBarTextPacket( ComponentSerializer.minimessage((durationUntilKick.toMinutes() <= 3 ? "" : "") + twoB2tTimeLimit.toHours() + "hr kick in: " + durationUntilKick.toMinutes() + "m")); @@ -673,14 +676,24 @@ public long getOnlineTimeSeconds() { : 0L; } + public long getOnlineTimeSecondsWithQueueSkip() { + return !inQueue && didQueueSkip && prevOnlineSeconds.isPresent() + ? getOnlineTimeSeconds() + prevOnlineSeconds.getAsLong() + : getOnlineTimeSeconds(); + } + public String getOnlineTimeString() { - return Queue.getEtaStringFromSeconds(getOnlineTimeSeconds()); + return Queue.getEtaStringFromSeconds(getOnlineTimeSecondsWithQueueSkip()); } public void handleDisconnectEvent(DisconnectEvent event) { CACHE.reset(CacheResetType.FULL); this.disconnectTime = Instant.now(); + this.prevOnlineSeconds = inQueue + ? OptionalLong.empty() + : OptionalLong.of(Duration.between(this.connectTime, this.disconnectTime).toSeconds()); this.inQueue = false; + this.didQueueSkip = false; this.queuePosition = 0; TPS.reset(); if (!DISCORD.isRunning() @@ -724,6 +737,10 @@ public void handleQueueCompleteEvent(QueueCompleteEvent event) { this.connectTime = Instant.now(); } + public void handleQueueSkipEvent(QueueSkipEvent event) { + this.didQueueSkip = true; + } + public void handlePlayerOnlineEvent(PlayerOnlineEvent event) { if (this.isPrio.isEmpty()) // assume we are prio if we skipped queuing diff --git a/src/main/java/com/zenith/command/impl/ReconnectCommand.java b/src/main/java/com/zenith/command/impl/ReconnectCommand.java index f0d3b5b62..48751d71e 100644 --- a/src/main/java/com/zenith/command/impl/ReconnectCommand.java +++ b/src/main/java/com/zenith/command/impl/ReconnectCommand.java @@ -30,7 +30,7 @@ public LiteralArgumentBuilder register() { EXECUTOR.execute(() -> { Proxy.getInstance().disconnect(SYSTEM_DISCONNECT); MODULE.get(AutoReconnect.class).cancelAutoReconnect(); - Proxy.getInstance().connect(); + MODULE.get(AutoReconnect.class).scheduleAutoReconnect(2); }); }); } diff --git a/src/main/java/com/zenith/discord/DiscordEventListener.java b/src/main/java/com/zenith/discord/DiscordEventListener.java index b0063c826..94817215d 100644 --- a/src/main/java/com/zenith/discord/DiscordEventListener.java +++ b/src/main/java/com/zenith/discord/DiscordEventListener.java @@ -124,7 +124,7 @@ public void handleDisconnectEvent(DisconnectEvent event) { .addField("Reason", event.reason(), false) .addField("Why?", category.getWikiURL(), false) .addField("Category", category.toString(), false) - .addField("Online Duration", formatDuration(event.onlineDuration()), false) + .addField("Online Duration", formatDuration(event.onlineDurationWithQueueSkip()), false) .errorColor(); if (Proxy.getInstance().isOn2b2t() && !Proxy.getInstance().isPrio() diff --git a/src/main/java/com/zenith/event/proxy/DisconnectEvent.java b/src/main/java/com/zenith/event/proxy/DisconnectEvent.java index 18ab7942c..a04148cfb 100644 --- a/src/main/java/com/zenith/event/proxy/DisconnectEvent.java +++ b/src/main/java/com/zenith/event/proxy/DisconnectEvent.java @@ -8,14 +8,15 @@ public record DisconnectEvent( String reason, boolean manualDisconnect, Duration onlineDuration, + Duration onlineDurationWithQueueSkip, boolean wasInQueue, int queuePosition ) { - public DisconnectEvent(String reason, final Duration onlineDuration, boolean wasInQueue, int queuePosition) { - this(reason, (Shared.MANUAL_DISCONNECT.equals(reason)), onlineDuration, wasInQueue, queuePosition); + public DisconnectEvent(String reason, final Duration onlineDuration, Duration onlineDurationWithQueueSkip, boolean wasInQueue, int queuePosition) { + this(reason, (Shared.MANUAL_DISCONNECT.equals(reason)), onlineDuration, onlineDurationWithQueueSkip, wasInQueue, queuePosition); } public DisconnectEvent(String reason) { - this(reason, Duration.ZERO, false, 0); + this(reason, Duration.ZERO, Duration.ZERO, false, 0); } } diff --git a/src/main/java/com/zenith/event/proxy/QueueSkipEvent.java b/src/main/java/com/zenith/event/proxy/QueueSkipEvent.java new file mode 100644 index 000000000..a72ca3dd1 --- /dev/null +++ b/src/main/java/com/zenith/event/proxy/QueueSkipEvent.java @@ -0,0 +1,6 @@ +package com.zenith.event.proxy; + +// note: this may be posted before StartQueueEvent +public record QueueSkipEvent() { + public static final QueueSkipEvent INSTANCE = new QueueSkipEvent(); +} diff --git a/src/main/java/com/zenith/module/impl/AutoReconnect.java b/src/main/java/com/zenith/module/impl/AutoReconnect.java index c72291f0b..02e798e9e 100644 --- a/src/main/java/com/zenith/module/impl/AutoReconnect.java +++ b/src/main/java/com/zenith/module/impl/AutoReconnect.java @@ -99,7 +99,7 @@ private void delayBeforeReconnect(int delaySeconds) { final int countdown = delaySeconds; EVENT_BUS.postAsync(new AutoReconnectEvent(countdown)); // random jitter to help prevent multiple clients from logging in at the same time - Wait.wait((((int) (Math.random() * 5))) % 10); + Wait.waitRandomMs(5000); for (int i = countdown; i > 0; i-=10) { info("Reconnecting in {}s", i); Wait.wait(10); diff --git a/src/main/java/com/zenith/network/client/ClientSession.java b/src/main/java/com/zenith/network/client/ClientSession.java index 61e5bd31a..4563e2a49 100644 --- a/src/main/java/com/zenith/network/client/ClientSession.java +++ b/src/main/java/com/zenith/network/client/ClientSession.java @@ -165,9 +165,10 @@ public void callDisconnected(Component reason, Throwable cause) { } CLIENT_LOG.info("Disconnected: {}", reasonStr); var onlineDuration = Duration.ofSeconds(Proxy.getInstance().getOnlineTimeSeconds()); + var onlineDurationWithQueueSkip = Duration.ofSeconds(Proxy.getInstance().getOnlineTimeSecondsWithQueueSkip()); // stop processing packets before we reset the client cache to avoid race conditions getClientEventLoop().shutdownGracefully(0L, 15L, TimeUnit.SECONDS).awaitUninterruptibly(); - EVENT_BUS.post(new DisconnectEvent(reasonStr, onlineDuration, Proxy.getInstance().isInQueue(), Proxy.getInstance().getQueuePosition())); + EVENT_BUS.post(new DisconnectEvent(reasonStr, onlineDuration, onlineDurationWithQueueSkip, Proxy.getInstance().isInQueue(), Proxy.getInstance().getQueuePosition())); } public EventLoop getClientEventLoop() { diff --git a/src/main/java/com/zenith/network/client/handler/incoming/SystemChatHandler.java b/src/main/java/com/zenith/network/client/handler/incoming/SystemChatHandler.java index d22e47d00..d85c3143d 100644 --- a/src/main/java/com/zenith/network/client/handler/incoming/SystemChatHandler.java +++ b/src/main/java/com/zenith/network/client/handler/incoming/SystemChatHandler.java @@ -2,6 +2,7 @@ import com.zenith.Proxy; import com.zenith.event.proxy.DeathMessageEvent; +import com.zenith.event.proxy.QueueSkipEvent; import com.zenith.event.proxy.SelfDeathMessageEvent; import com.zenith.event.proxy.ServerChatReceivedEvent; import com.zenith.feature.deathmessages.DeathMessageParseResult; @@ -11,6 +12,7 @@ import com.zenith.util.ComponentSerializer; import lombok.NonNull; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket; @@ -35,10 +37,10 @@ public boolean applyAsync(@NonNull ClientboundSystemChatPacket packet, @NonNull final Component component = packet.getContent(); final String messageString = ComponentSerializer.serializePlain(component); Optional deathMessage = Optional.empty(); - if (!messageString.startsWith("<") && Proxy.getInstance().isOn2b2t()) - deathMessage = parseDeathMessage2b2t(component, deathMessage, messageString); String senderName = null; String whisperTarget = null; + if (!messageString.startsWith("<") && Proxy.getInstance().isOn2b2t()) + deathMessage = parseDeathMessage2b2t(component, deathMessage, messageString); if (messageString.startsWith("<")) { senderName = extractSenderNameNormalChat(messageString); } else if (deathMessage.isEmpty()) { @@ -59,6 +61,12 @@ public boolean applyAsync(@NonNull ClientboundSystemChatPacket packet, @NonNull messageString, Optional.ofNullable(whisperTarget).flatMap(t -> CACHE.getTabListCache().getFromName(t)), deathMessage)); + if (Proxy.getInstance().isOn2b2t() + && "Reconnecting to server 2b2t.".equals(messageString) + && NamedTextColor.GOLD.equals(component.style().color())) { + CLIENT_LOG.info("Queue Skip Detected"); + EVENT_BUS.postAsync(QueueSkipEvent.INSTANCE); + } } catch (final Exception e) { CLIENT_LOG.error("Caught exception in ChatHandler. Packet: {}", packet, e); }