parent
7598aded28
commit
4503f15d6c
9 changed files with 254 additions and 0 deletions
10
pom.xml
10
pom.xml
|
@ -28,6 +28,10 @@
|
||||||
<id>spigot-repo</id>
|
<id>spigot-repo</id>
|
||||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>dmulloy2-repo</id>
|
||||||
|
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -43,5 +47,11 @@
|
||||||
<version>24.1.0</version>
|
<version>24.1.0</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.comphenix.protocol</groupId>
|
||||||
|
<artifactId>ProtocolLib</artifactId>
|
||||||
|
<version>5.3.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
8
reflections.txt
Normal file
8
reflections.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
usage of:
|
||||||
|
|
||||||
|
reflections:
|
||||||
|
- ping/F3NameListener net.minecraft.network.protocol.common.custom.BrandPayload
|
||||||
|
|
||||||
|
protocollib:
|
||||||
|
- ping/F3NameListener sending and intercepting CUSTOM_PAYLOADs to modify brand
|
||||||
|
- ping/PingChecker sending and intercepting COOKIE_RESPONSEs to measure ping
|
|
@ -3,6 +3,9 @@ package eu.m724.tweaks;
|
||||||
import eu.m724.tweaks.chat.ChatCommands;
|
import eu.m724.tweaks.chat.ChatCommands;
|
||||||
import eu.m724.tweaks.chat.ChatManager;
|
import eu.m724.tweaks.chat.ChatManager;
|
||||||
import eu.m724.tweaks.door.DoorListener;
|
import eu.m724.tweaks.door.DoorListener;
|
||||||
|
import eu.m724.tweaks.ping.F3NameListener;
|
||||||
|
import eu.m724.tweaks.ping.PingChecker;
|
||||||
|
import eu.m724.tweaks.ping.PingCommands;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -18,5 +21,9 @@ public class TweaksPlugin extends JavaPlugin {
|
||||||
Objects.requireNonNull(getCommand("chatmanage")).setExecutor(chatCommands);
|
Objects.requireNonNull(getCommand("chatmanage")).setExecutor(chatCommands);
|
||||||
|
|
||||||
getServer().getPluginManager().registerEvents(new DoorListener(), this);
|
getServer().getPluginManager().registerEvents(new DoorListener(), this);
|
||||||
|
|
||||||
|
new F3NameListener(this).init();
|
||||||
|
new PingChecker(this).init();
|
||||||
|
Objects.requireNonNull(getCommand("ping")).setExecutor(new PingCommands());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
90
src/main/java/eu/m724/tweaks/ping/F3NameListener.java
Normal file
90
src/main/java/eu/m724/tweaks/ping/F3NameListener.java
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package eu.m724.tweaks.ping;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
import com.comphenix.protocol.events.*;
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
public class F3NameListener {
|
||||||
|
private final Plugin plugin;
|
||||||
|
private final Class<?> brandPayloadClass;
|
||||||
|
private final Constructor<?> brandPayloadConstructor;
|
||||||
|
|
||||||
|
public F3NameListener(Plugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
try {
|
||||||
|
this.brandPayloadClass = Class.forName("net.minecraft.network.protocol.common.custom.BrandPayload");
|
||||||
|
this.brandPayloadConstructor = brandPayloadClass.getConstructor(String.class);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||||
|
throw new BrandPayloadReflectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
new BukkitRunnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
double mspt = PlayerPing.getMillisPerTick();
|
||||||
|
boolean showMspt = mspt > 50.05; // mspt is at least like 50.01 because of some measuring overheads (I think)
|
||||||
|
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||||
|
double ping = PlayerPing.getPingMillis(player);
|
||||||
|
String brand;
|
||||||
|
if (ping == 0) {
|
||||||
|
brand = "wait";
|
||||||
|
} else {
|
||||||
|
if (showMspt) {
|
||||||
|
brand = "%.2f mspt | %.2f ms".formatted(mspt, PlayerPing.getPingMillis(player));
|
||||||
|
} else {
|
||||||
|
brand = "%.2f ms".formatted(PlayerPing.getPingMillis(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changeBrand(player, brand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.runTaskTimerAsynchronously(plugin, 0, 200); // 10 sec
|
||||||
|
|
||||||
|
// this is just to make the server not send a brand on login, it doesn't ever run after login
|
||||||
|
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
|
||||||
|
plugin,
|
||||||
|
ListenerPriority.NORMAL,
|
||||||
|
PacketType.Configuration.Server.CUSTOM_PAYLOAD
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public void onPacketSending(PacketEvent event) {
|
||||||
|
try {
|
||||||
|
PacketContainer packet = event.getPacket();
|
||||||
|
Object brandPayload = brandPayloadConstructor.newInstance("wait");
|
||||||
|
InternalStructure structure = new InternalStructure(brandPayload, new StructureModifier<>(brandPayloadClass));
|
||||||
|
packet.getStructures().write(0, structure);
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new BrandPayloadReflectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeBrand(Player player, String brand) {
|
||||||
|
PacketContainer packet = new PacketContainer(PacketType.Play.Server.CUSTOM_PAYLOAD);
|
||||||
|
Object brandPayload;
|
||||||
|
try {
|
||||||
|
brandPayload = brandPayloadConstructor.newInstance(brand);
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new BrandPayloadReflectionException(e);
|
||||||
|
}
|
||||||
|
InternalStructure structure = new InternalStructure(brandPayload, new StructureModifier<>(brandPayloadClass));
|
||||||
|
packet.getStructures().write(0, structure);
|
||||||
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BrandPayloadReflectionException extends RuntimeException {
|
||||||
|
public BrandPayloadReflectionException(Exception e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/eu/m724/tweaks/ping/MsptChecker.java
Normal file
19
src/main/java/eu/m724/tweaks/ping/MsptChecker.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package eu.m724.tweaks.ping;
|
||||||
|
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
|
public class MsptChecker extends BukkitRunnable {
|
||||||
|
private long lastLoop = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
long now = System.nanoTime();
|
||||||
|
PlayerPing.nspt = (now - lastLoop) / 100;
|
||||||
|
lastLoop = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Plugin plugin) {
|
||||||
|
this.runTaskTimerAsynchronously(plugin, 0, 100); // 5 secs
|
||||||
|
}
|
||||||
|
}
|
63
src/main/java/eu/m724/tweaks/ping/PingChecker.java
Normal file
63
src/main/java/eu/m724/tweaks/ping/PingChecker.java
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package eu.m724.tweaks.ping;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
|
import com.comphenix.protocol.events.PacketAdapter;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.wrappers.MinecraftKey;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class PingChecker extends BukkitRunnable {
|
||||||
|
private final Plugin plugin;
|
||||||
|
|
||||||
|
// TODO concurrnet too?
|
||||||
|
// this is in nanoseconds
|
||||||
|
private final Map<Player, Long> pending = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public PingChecker(Plugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
|
||||||
|
plugin,
|
||||||
|
ListenerPriority.NORMAL,
|
||||||
|
PacketType.Play.Client.COOKIE_RESPONSE
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public void onPacketReceiving(PacketEvent event) {
|
||||||
|
// below line checks, whether the first (and sole) identifier field in the packet is minecraft:chk_ping
|
||||||
|
if (event.getPacket().getMinecraftKeys().read(0).getKey().equals("chk_ping")) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
|
long start = pending.remove(player);
|
||||||
|
PlayerPing.pings.put(player, System.nanoTime() - start);
|
||||||
|
// gotta cancel because the server will kick
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.runTaskTimerAsynchronously(plugin, 0, 200); // 10 secs
|
||||||
|
new MsptChecker().init(plugin); // TODO should this be here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
plugin.getServer().getOnlinePlayers().forEach(player -> {
|
||||||
|
if (pending.containsKey(player)) return;
|
||||||
|
pending.put(player, System.nanoTime()); // here or at the bottom? probably doesn't matter
|
||||||
|
|
||||||
|
PacketContainer packet = new PacketContainer(PacketType.Play.Server.COOKIE_REQUEST);
|
||||||
|
packet.getMinecraftKeys().write(0, new MinecraftKey("chk_ping"));
|
||||||
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/eu/m724/tweaks/ping/PingCommands.java
Normal file
24
src/main/java/eu/m724/tweaks/ping/PingCommands.java
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package eu.m724.tweaks.ping;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class PingCommands implements CommandExecutor {
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||||
|
if (command.getName().equals("ping")) {
|
||||||
|
Player player = (Player) sender;
|
||||||
|
BaseComponent[] component = new ComponentBuilder("Ping: ").color(ChatColor.GOLD)
|
||||||
|
.append("%.2fms".formatted(PlayerPing.getPingMillis(player))).color(ChatColor.AQUA)
|
||||||
|
.create();
|
||||||
|
player.spigot().sendMessage(component);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
30
src/main/java/eu/m724/tweaks/ping/PlayerPing.java
Normal file
30
src/main/java/eu/m724/tweaks/ping/PlayerPing.java
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package eu.m724.tweaks.ping;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class PlayerPing {
|
||||||
|
// TODO concurrnet?
|
||||||
|
// in nanos
|
||||||
|
static final Map<Player, Long> pings = new ConcurrentHashMap<>();
|
||||||
|
// nanos per tick
|
||||||
|
static long nspt = 0L;
|
||||||
|
|
||||||
|
public static long getPingNanos(Player player) {
|
||||||
|
return pings.getOrDefault(player, -1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getPingMillis(Player player) {
|
||||||
|
return getPingNanos(player) / 1000000.0; // a mil ns in ms
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getNanosPerTick() {
|
||||||
|
return nspt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getMillisPerTick() {
|
||||||
|
return nspt / 1000000.0; // a mil ns in ms
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ version: ${project.version}
|
||||||
|
|
||||||
main: eu.m724.tweaks.TweaksPlugin
|
main: eu.m724.tweaks.TweaksPlugin
|
||||||
api-version: 1.21.1
|
api-version: 1.21.1
|
||||||
|
depend: [ProtocolLib]
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
chat:
|
chat:
|
||||||
|
@ -12,4 +13,6 @@ commands:
|
||||||
chatmanage:
|
chatmanage:
|
||||||
description: Chatroom user management commands
|
description: Chatroom user management commands
|
||||||
aliases: [cm, crm]
|
aliases: [cm, crm]
|
||||||
|
ping:
|
||||||
|
description: Your ping
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue