Skip to content

Commit

Permalink
Add jitter calculations
Browse files Browse the repository at this point in the history
  • Loading branch information
Axionize committed Oct 16, 2024
1 parent d69814d commit 573aee8
Show file tree
Hide file tree
Showing 15 changed files with 104 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package me.caseload.knockbacksync.listener.bukkit;

import me.caseload.knockbacksync.listener.PlayerJoinQuitListener;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.player.BukkitPlayer;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import me.caseload.knockbacksync.command.subcommand.ToggleOffGroundSubcommand;
import me.caseload.knockbacksync.command.subcommand.ToggleSubCommand;
import me.caseload.knockbacksync.manager.ConfigManager;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.permission.PermissionChecker;
import me.caseload.knockbacksync.util.ChatUtil;
Expand Down Expand Up @@ -59,7 +59,7 @@ public static LiteralArgumentBuilder<CommandSourceStack> build() {
if (playerData.getPing() == null) {
return Component.literal("Pong not received. Your estimated ping is " + playerData.getPlatformPlayer().getPing() + "ms.");
} else {
return Component.literal("Your last ping packet took " + playerData.getPing() + "ms.");
return Component.literal("Your last ping packet took " + playerData.getPing() + "ms. Jitter: " + playerData.getJitter() + "ms.");
}
}, false);
} else {
Expand All @@ -76,11 +76,9 @@ public static LiteralArgumentBuilder<CommandSourceStack> build() {
if (playerData.getPing() == null) {
return Component.literal("Pong not received. " + target.getDisplayName().getString() + "’s estimated ping is " + playerData.getEstimatedPing() + "ms.");
} else {
return Component.literal(target.getDisplayName().getString() + "’s last ping packet took " + playerData.getPing() + "ms.");
return Component.literal(target.getDisplayName().getString() + "’s last ping packet took " + playerData.getPing() + "ms. Jitter: " + playerData.getJitter() + "ms.");
}
}, false);
return 1;
})
)
)
.then(Commands.literal("reload")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.caseload.knockbacksync.KnockbackSyncBase;
import me.caseload.knockbacksync.manager.ConfigManager;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.permission.PermissionChecker;
import me.caseload.knockbacksync.util.ChatUtil;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package me.caseload.knockbacksync.listener;

import me.caseload.knockbacksync.KnockbackSyncBase;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.player.PlatformPlayer;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package me.caseload.knockbacksync.listener;

import me.caseload.knockbacksync.KnockbackSyncBase;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.player.PlatformPlayer;
import me.caseload.knockbacksync.util.ChatUtil;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.github.retrooper.packetevents.util.Vector3d;
import me.caseload.knockbacksync.KnockbackSyncBase;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.player.PlatformPlayer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.util.MathUtil;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPong;
import me.caseload.knockbacksync.KnockbackSyncBase;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;

import java.util.UUID;

public class PingReceiveListener extends PacketListenerAbstract {

@Override
Expand All @@ -31,6 +29,10 @@ public void onPacketReceive(PacketReceiveEvent event) {
long ping = System.currentTimeMillis() - sendTime;
playerData.setPreviousPing(playerData.getPing());
playerData.setPing(ping);

playerData.getJitterCalculator().addPing(ping, id);
double jitter = playerData.getJitterCalculator().calculateJitter();
playerData.setJitter(jitter);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import me.caseload.knockbacksync.ConfigWrapper;
import me.caseload.knockbacksync.KnockbackSyncBase;
import me.caseload.knockbacksync.Platform;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.runnable.PingRunnable;
import me.caseload.knockbacksync.scheduler.AbstractTaskHandle;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.caseload.knockbacksync.manager;

import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.util.FloodgateUtil;
import me.caseload.knockbacksync.util.GeyserUtil;
import org.jetbrains.annotations.NotNull;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package me.caseload.knockbacksync.player;

import lombok.Getter;

import java.util.*;
import java.util.stream.Collectors;

public class JitterCalculator {
private final int SAMPLE_SIZE = 15;
private final Queue<Long> pings = new LinkedList<>();
@Getter
private long sequenceNumber = 0;

public void addPing(long pingTime, long receivedSequence) {
pings.offer(pingTime);
if (pings.size() > SAMPLE_SIZE) {
pings.poll();
}

// Check for out-of-order packets
if (receivedSequence < sequenceNumber) {
// Handle out-of-order packet
}
sequenceNumber = receivedSequence;
}

public double calculateJitter() {
if (pings.size() < 2) return 0;

List<Long> sortedPings = new ArrayList<>(pings);
Collections.sort(sortedPings);

// Calculate IQR
int q1Index = sortedPings.size() / 4;
int q3Index = q1Index * 3;
long q1 = sortedPings.get(q1Index);
long q3 = sortedPings.get(q3Index);
long iqr = q3 - q1;

// Filter outliers
double lowerBound = q1 - 1.5 * iqr;
double upperBound = q3 + 1.5 * iqr;
List<Long> filteredPings = sortedPings.stream()
.filter(p -> p >= lowerBound && p <= upperBound)
.toList();

// Calculate standard deviation
double mean = filteredPings.stream().mapToLong(Long::longValue).average().orElse(0);
double variance = filteredPings.stream()
.mapToDouble(p -> Math.pow(p - mean, 2))
.average().orElse(0);
double stdDev = Math.sqrt(variance);

// Calculate mean jitter
double meanJitter = 0;
Long prevPing = null;
for (Long ping : filteredPings) {
if (prevPing != null) {
meanJitter += Math.abs(ping - prevPing);
}
prevPing = ping;
}
meanJitter /= (filteredPings.size() - 1);

// You can return different jitter metrics based on your needs
return stdDev; // or meanJitter, or both
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package me.caseload.knockbacksync.manager;
package me.caseload.knockbacksync.player;

import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.player.PlayerManager;
Expand All @@ -8,13 +8,10 @@
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPing;
import io.netty.channel.Channel;
import lombok.Getter;
import lombok.Setter;
import me.caseload.knockbacksync.KnockbackSyncBase;
//import me.caseload.knockbacksync.player.BukkitPlayer;
//import me.caseload.knockbacksync.player.FabricPlayer;
import me.caseload.knockbacksync.player.PlatformPlayer;
import me.caseload.knockbacksync.manager.CombatManager;
import me.caseload.knockbacksync.world.PlatformWorld;
import me.caseload.knockbacksync.scheduler.AbstractTaskHandle;
import me.caseload.knockbacksync.util.MathUtil;
Expand All @@ -24,15 +21,19 @@
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

@Getter
public class PlayerData {


@Getter
private JitterCalculator jitterCalculator;
@Getter @Setter
private double jitter;

private static final int PLUGIN_IDENTIFIER = 0x80000000; // Bit 31 set to 1 (negative)
private static final int ID_MASK = 0x7FFF; // 15-bit mask
private final AtomicInteger pingIdCounter = new AtomicInteger(0);
Expand Down Expand Up @@ -111,24 +112,6 @@ public PlayerData(PlatformPlayer platformPlayer) {
PING_OFFSET = KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().getInt("ping_offset", 25);
}

// public PlayerData(Player player) {
// this.uuid = player.getUniqueId();
// this.user = PacketEvents.getAPI().getPlayerManager().getUser(player);
// this.platformPlayer = new BukkitPlayer(player);
// PING_OFFSET = KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().getInt("ping_offset", 25);
// }

// public PlayerData(ServerPlayer player) {
// this.uuid = player.getUUID();
// ping listener doesn't work beause I can't get the user
// this exists temporarily until I fix packetevents
// Channel channel = (Channel) PacketEvents.getAPI().getProtocolManager().getChannel(uuid);
// this.user = PacketEvents.getAPI().getProtocolManager().getUser(channel);
// this.user = null; // PacketEvents.getAPI().getPlayerManager().getUser(player);
// this.platformPlayer = new FabricPlayer(player);
// PING_OFFSET = KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().getInt("ping_offset", 25);
// }

/**
* Calculates the player's ping with compensation for lag spikes.
* A hardcoded offset is applied for several reasons,
Expand Down Expand Up @@ -156,14 +139,14 @@ public boolean isPingIdOurs(int id) {
// We use the lower 15 bits for the counter, giving us 32,767 unique negative IDs before wrapping.
// The isPingIdOurs method checks if the ID is negative and if the upper 17 bits match our identifier.
// This should avoid conflicts with GrimAC which uses negative short range and other plugins which are mostly positive ranged
public void sendPing() {
if (user != null) {
int packetId = generatePingId();
timeline.put(packetId, System.currentTimeMillis());
WrapperPlayServerPing packet = new WrapperPlayServerPing(packetId);
user.sendPacket(packet);
}
public void sendPing() {
if (user != null) {
int packetId = generatePingId();
timeline.put(packetId, System.currentTimeMillis());
WrapperPlayServerPing packet = new WrapperPlayServerPing(packetId);
user.sendPacket(packet);
}
}

/**
* Determines if the Player is on the ground clientside, but not serverside
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import me.caseload.knockbacksync.KnockbackSyncBase;
import me.caseload.knockbacksync.manager.CombatManager;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;

import java.util.UUID;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package me.caseload.knockbacksync.stats.custom;

import me.caseload.knockbacksync.KnockbackSyncBase;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.player.PlatformPlayer;
import me.caseload.knockbacksync.world.PlatformServer;
import me.caseload.knockbacksync.stats.AdvancedPie;

import java.util.HashMap;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package me.caseload.knockbacksync.listener.fabric;

import me.caseload.knockbacksync.listener.PlayerJoinQuitListener;
import me.caseload.knockbacksync.manager.PlayerData;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.player.FabricPlayer;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;

Expand Down

0 comments on commit 573aee8

Please sign in to comment.