commit
Some checks failed
/ deploy (push) Failing after 2m13s

This commit is contained in:
Minecon724 2024-11-23 17:51:03 +01:00
parent ddd100f493
commit 282bebdcdb
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
22 changed files with 706 additions and 192 deletions

View file

@ -25,7 +25,6 @@
</resource>
</resources>
<plugins>
<!-- this is to reduce jar size by 1 or 2 kb -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
@ -56,7 +55,7 @@
</execution>
</executions>
</plugin>
<!-- end of this -->
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>

View file

@ -1,8 +1,4 @@
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
modules that require nms / reflections / protocollib:
- MOTD
- brand
- worldborder

View file

@ -0,0 +1,88 @@
package eu.m724.tweaks;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin;
public record TweaksConfig(
boolean isProtocolLib,
boolean worldborderHide,
boolean brandEnabled,
String brandText,
boolean brandShowPing,
boolean brandShowMspt,
boolean doorEnabled,
boolean doorDoubleOpen,
boolean doorKnocking,
boolean motdEnabled,
String motdSet,
boolean chatEnabled,
boolean chatLocalEvents,
String chatDefaultName,
boolean compassEnabled,
int compassWidth,
int compassPrecision
) {
public static final int CONFIG_VERSION = 1;
private static TweaksConfig config;
public static TweaksConfig getConfig() {
return config;
}
public static TweaksConfig load(Plugin plugin) {
plugin.saveDefaultConfig();
FileConfiguration config = plugin.getConfig();
boolean isProtocolLib = plugin.getServer().getPluginManager().getPlugin("ProtocolLib") != null;
int configVersion = config.getInt("magic number dont modify this", 0);
RuntimeException exception = new RuntimeException("Config version is %d, expected %d".formatted(configVersion, CONFIG_VERSION));
if (configVersion == 0) {
throw exception;
} else if (configVersion > CONFIG_VERSION) {
throw new RuntimeException("Please follow update instructions", exception);
} else if (configVersion < CONFIG_VERSION) {
throw new RuntimeException("Did you downgrade the plugin? Remove config.yml and let the plugin re-create it", exception);
}
boolean hideWorldBorder = config.getBoolean("worldborder.hide");
boolean brandEnabled = config.getBoolean("brand.enabled");
String brandText = config.getString("brand.text");
boolean brandShowPing = config.getBoolean("brand.showPing");
boolean brandShowMspt = config.getBoolean("brand.showMspt");
boolean doorDoubleOpen = config.getBoolean("doors.doubleOpen");
boolean doorKnocking = config.getBoolean("doors.knocking");
boolean doorEnabled = doorDoubleOpen || doorKnocking;
String motdSet = config.getString("motd.set");
boolean motdEnabled = !(motdSet.equals("false") || motdSet.isBlank());
boolean chatEnabled = config.getBoolean("chat.enabled");
boolean chatLocalEvents = config.getBoolean("chat.localEvents");
String chatDefaultName = config.getString("chat.defaultName");
boolean compassEnabled = config.getBoolean("compass.enabled");
int compassWidth = config.getInt("compass.width");
int compassPrecision = config.getInt("compass.precision");
TweaksConfig.config = new TweaksConfig(
isProtocolLib,
hideWorldBorder,
brandEnabled, brandText, brandShowPing, brandShowMspt,
doorEnabled, doorDoubleOpen, doorKnocking,
motdEnabled, motdSet,
chatEnabled, chatLocalEvents, chatDefaultName,
compassEnabled, compassWidth, compassPrecision
);
return TweaksConfig.config;
}
}

View file

@ -2,12 +2,12 @@ package eu.m724.tweaks;
import eu.m724.tweaks.chat.ChatCommands;
import eu.m724.tweaks.chat.ChatManager;
import eu.m724.tweaks.door.DoorListener;
import eu.m724.tweaks.compass.CompassManager;
import eu.m724.tweaks.door.DoorManager;
import eu.m724.tweaks.motd.MotdListener;
import eu.m724.tweaks.ping.F3NameListener;
import eu.m724.tweaks.ping.PingChecker;
import eu.m724.tweaks.ping.PingCommands;
import eu.m724.tweaks.player.MusicPlayer;
import eu.m724.tweaks.worldborder.WorldBorderManager;
import org.bukkit.plugin.java.JavaPlugin;
@ -17,33 +17,48 @@ import java.util.Objects;
public class TweaksPlugin extends JavaPlugin {
@Override
public void onEnable() {
TweaksConfig config = TweaksConfig.load(this);
new CompassManager(this).init();
if (config.chatEnabled()) {
ChatManager chatManager = new ChatManager(this);
chatManager.init();
ChatCommands chatCommands = new ChatCommands(chatManager);
Objects.requireNonNull(getCommand("chat")).setExecutor(chatCommands);
Objects.requireNonNull(getCommand("chatmanage")).setExecutor(chatCommands);
}
getServer().getPluginManager().registerEvents(new DoorListener(), this);
if (config.doorEnabled()) {
new DoorManager().init(this);
}
if (config.brandEnabled()) {
new F3NameListener(this).init();
}
new PingChecker(this).init();
Objects.requireNonNull(getCommand("ping")).setExecutor(new PingCommands());
Objects.requireNonNull(getCommand("dkick")).setExecutor(new PingCommands());
if (getServer().getPluginManager().getPlugin("voicechat") != null) {
/*if (getServer().getPluginManager().getPlugin("voicechat") != null) {
new MusicPlayer(this).init();
} else {
getLogger().warning("To use voice extensions, install \"Simple Voice Chat\"");
}
}*/
if (config.motdEnabled()) {
try {
new MotdListener("example").init(this);
new MotdListener(this).init();
} catch (IOException e) {
getLogger().severe("Failed to initialize MOTD extension");
throw new RuntimeException(e);
}
}
if (config.worldborderHide()) {
new WorldBorderManager().init(this);
}
}
}

View file

@ -1,20 +1,24 @@
package eu.m724.tweaks.chat;
import eu.m724.tweaks.TweaksConfig;
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.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class ChatListener implements Listener {
private final ChatManager chatManager;
private final boolean localEvents;
public ChatListener(ChatManager chatManager) {
this.chatManager = chatManager;
this.localEvents = TweaksConfig.getConfig().chatLocalEvents();
}
@EventHandler
@ -27,6 +31,7 @@ public class ChatListener implements Listener {
.create();
player.spigot().sendMessage(component);
if (localEvents) {
chatRoom.broadcast(
new ComponentBuilder()
.append(ChatFormatUtils.chatRoomPrefixShort(chatRoom))
@ -38,12 +43,14 @@ public class ChatListener implements Listener {
// remove Minecraft join message
event.setJoinMessage(null);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
ChatRoom chatRoom = chatManager.removePlayer(player);
if (localEvents) {
chatRoom.broadcast(
new ComponentBuilder()
.append(ChatFormatUtils.chatRoomPrefixShort(chatRoom))
@ -55,6 +62,25 @@ public class ChatListener implements Listener {
// remove Minecraft quit message
event.setQuitMessage(null);
}
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent event) {
if (localEvents) {
Player player = event.getEntity();
ChatRoom chatRoom = chatManager.getPlayerChatRoom(player);
chatRoom.broadcast(
new ComponentBuilder()
.append(ChatFormatUtils.chatRoomPrefixShort(chatRoom))
.append(event.getDeathMessage())
.create()
);
// remove Minecraft death message
event.setDeathMessage(null);
}
}
@EventHandler
public void onAsyncPlayerChat(AsyncPlayerChatEvent event) {

View file

@ -1,5 +1,6 @@
package eu.m724.tweaks.chat;
import eu.m724.tweaks.TweaksConfig;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentBuilder;
import org.bukkit.NamespacedKey;
@ -15,6 +16,7 @@ import java.util.Map;
public class ChatManager {
private final Plugin plugin;
private final NamespacedKey chatRoomKey;
private final String defaultRoom;
private final Map<Player, ChatRoom> playerMap = new HashMap<>();
private final Map<String, ChatRoom> roomIdMap = new HashMap<>();
@ -22,10 +24,11 @@ public class ChatManager {
public ChatManager(Plugin plugin) {
this.plugin = plugin;
this.chatRoomKey = new NamespacedKey(plugin, "chatRoom");
this.defaultRoom = TweaksConfig.getConfig().chatDefaultName();
}
public void init() {
getById("global");
getById(defaultRoom);
plugin.getServer().getPluginManager().registerEvents(new ChatListener(this), plugin);
}
@ -53,10 +56,15 @@ public class ChatManager {
ChatRoom chatRoom = roomIdMap.get(id);
if (chatRoom == null) {
if (id.equals(defaultRoom)) {
return new ChatRoom(defaultRoom, null, null);
} else {
chatRoom = ChatRoomLoader.load(plugin, id);
}
roomIdMap.put(id, chatRoom);
}
return chatRoom;
}
@ -104,9 +112,9 @@ public class ChatManager {
if (chatRoom == null) {
String id = player.getPersistentDataContainer().get(chatRoomKey, PersistentDataType.STRING);
if (id == null) id = "global";
if (id == null) id = defaultRoom;
chatRoom = getById(id);
if (chatRoom == null) chatRoom = getById("global");
if (chatRoom == null) chatRoom = getById(defaultRoom);
chatRoom.players.add(player);
playerMap.put(player, chatRoom);
@ -157,7 +165,7 @@ public class ChatManager {
roomIdMap.remove(chatRoom.id);
ChatRoomLoader.getFile(plugin, chatRoom.id).delete();
chatRoom.players.forEach(player -> {
setPlayerChatRoom(getById("global"), player);
setPlayerChatRoom(getById(defaultRoom), player);
});
}

View file

@ -57,10 +57,6 @@ public class ChatRoomLoader {
* @return the chat room or null if no such chat room
*/
static ChatRoom load(Plugin plugin, String id) {
if (id.equals("global")) {
return new ChatRoom("global", null, null);
}
File chatRoomFile = getFile(plugin, id);
if (!chatRoomFile.exists()) return null;

View file

@ -0,0 +1,133 @@
package eu.m724.tweaks.compass;
import eu.m724.tweaks.TweaksConfig;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
import org.bukkit.inventory.meta.CompassMeta;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
// TODO dimension check
public class CompassListener implements Listener {
private final int precision = TweaksConfig.getConfig().compassPrecision(); // degrees every point
private final int width = TweaksConfig.getConfig().compassWidth(); // points left to right
private final Map<Integer, String> points = Map.of(
0, ChatColor.DARK_GRAY + "S",
90, ChatColor.DARK_GRAY + "W",
180, ChatColor.DARK_GRAY + "N",
270, ChatColor.DARK_GRAY + "E"
);
@EventHandler
public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) {
if (event.getMainHandItem().getType() == Material.COMPASS || event.getOffHandItem().getType() == Material.COMPASS) {
event.getPlayer().resetTitle();
event.getPlayer().spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent());
}
}
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
CompassPlayerPreferences preferences = CompassPlayerPreferences.get(player);
boolean mainHand = event.getPlayer().getInventory().getItemInMainHand().getType() == Material.COMPASS;
boolean offHand = event.getPlayer().getInventory().getItemInOffHand().getType() == Material.COMPASS;
boolean holding = mainHand || offHand;
if (holding) {
preferences.visible = true;
} else {
if (preferences.visible) {
preferences.visible = false;
event.getPlayer().resetTitle();
event.getPlayer().spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent());
}
return;
}
double yaw = wrapMod(event.getTo().getYaw(), 360.0);
String[] visiblePoints = new String[width];
Arrays.fill(visiblePoints, ChatColor.DARK_GRAY + "-");
Map<Integer, String> currentPoints = new LinkedHashMap<>(points);
currentPoints.putAll(preferences.customPoints);
//
// TODO optimzie, caluclate that when needed
if (player.getBedSpawnLocation() != null) {
if (player.getBedSpawnLocation().getWorld().equals(player.getWorld())) {
double respawnYaw = calculateYaw(player.getLocation(), player.getBedSpawnLocation());
System.out.println(respawnYaw);
currentPoints.put((int) respawnYaw, ChatColor.GREEN + "r");
}
}
if (mainHand) {
CompassMeta meta = (CompassMeta) player.getInventory().getItemInMainHand().getItemMeta();
if (meta.isLodestoneTracked()) {
double lodeYaw = calculateYaw(player.getLocation(), meta.getLodestone());
currentPoints.put((int) lodeYaw, ChatColor.GRAY + "L");
}
}
if (offHand) {
CompassMeta meta = (CompassMeta) player.getInventory().getItemInOffHand().getItemMeta();
if (meta.isLodestoneTracked()) {
double lodeYaw = calculateYaw(player.getLocation(), meta.getLodestone());
currentPoints.put((int) lodeYaw, ChatColor.GRAY + "L");
}
}
//
for (Map.Entry<Integer, String> entry : currentPoints.entrySet()) {
double distance = -wrapModRange(yaw - entry.getKey(), -180, 180);
int index = (int) (distance / precision + width / 2.0);
if (index >= 0 && index < width) {
visiblePoints[index] = entry.getValue();
}
}
ComponentBuilder builder = new ComponentBuilder();
for (String point : visiblePoints) {
builder.append(point + ' ');
}
if (mainHand) {
event.getPlayer().sendTitle("", builder.build().toLegacyText(), 0, 70, 20);
} else {
event.getPlayer().spigot().sendMessage(ChatMessageType.ACTION_BAR, builder.create());
}
}
private double wrapModRange(double value, double start, double stop) {
double range = stop - start;
double result = start + (value - start - Math.floor((value - start) / range) * range);
return result == stop ? start : result;
}
private double wrapMod(double value, double stop) {
double result = (value - Math.floor(value / stop) * stop);
return result == stop ? 0 : result;
}
private double calculateYaw(Location source, Location target) {
return wrapMod(Math.atan2(source.getX() - target.getX(), target.getZ() - source.getZ()) * (180 / Math.PI), 360);
}
}

View file

@ -0,0 +1,15 @@
package eu.m724.tweaks.compass;
import org.bukkit.plugin.Plugin;
public class CompassManager {
private final Plugin plugin;
public CompassManager(Plugin plugin) {
this.plugin = plugin;
}
public void init() {
plugin.getServer().getPluginManager().registerEvents(new CompassListener(), plugin);
}
}

View file

@ -0,0 +1,17 @@
package eu.m724.tweaks.compass;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
public class CompassPlayerPreferences {
static Map<Player, CompassPlayerPreferences> playerMap = new HashMap<>();
boolean visible = false;
Map<Integer, String> customPoints = new HashMap<>();
static CompassPlayerPreferences get(Player player) {
return CompassPlayerPreferences.playerMap.computeIfAbsent(player, (k) -> new CompassPlayerPreferences());
}
}

View file

@ -0,0 +1,57 @@
package eu.m724.tweaks.door;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.Door;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockDamageAbortEvent;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.RayTraceResult;
import java.util.concurrent.ThreadLocalRandom;
public class DoorKnockListener implements Listener {
@EventHandler
public void onBlockDamageAbort(BlockDamageAbortEvent event) {
Block block = event.getBlock();
if (!(block.getBlockData() instanceof Door)) return;
World world = block.getLocation().getWorld();
Player player = event.getPlayer();
// TODO maybe it would be faster to check just player and the door
RayTraceResult result = player.rayTraceBlocks(5);
if (result == null) return;
Location hitLocation = result.getHitPosition().toLocation(world);
double distance = player.getEyeLocation().distanceSquared(hitLocation);
if (distance > 12) return;
Sound sound = block.getType() == Material.IRON_DOOR ? Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR : Sound.ENTITY_ZOMBIE_ATTACK_WOODEN_DOOR;
float volume = player.isSneaking() ? 0.4f : 1f;
float pitch = player.getFallDistance() > 0 ? 1f : 1.5f;
if (player.hasPotionEffect(PotionEffectType.NAUSEA)) {
pitch = ThreadLocalRandom.current().nextFloat(0.5f, 0.7f);
}
PotionEffect fatigue = player.getPotionEffect(PotionEffectType.MINING_FATIGUE);
if (fatigue != null) {
int level = fatigue.getAmplifier();
volume /= level / 3f;
pitch /= level;
}
volume *= (float) ((10.0 - Math.min(distance - 2, 10.0)) / 10.0);
world.playSound(hitLocation, sound, volume, pitch);
//world.spawnParticle(Particle.BLOCK, hitLocation, (int) (10 * volume), door);
}
}

View file

@ -0,0 +1,18 @@
package eu.m724.tweaks.door;
import eu.m724.tweaks.TweaksConfig;
import org.bukkit.plugin.Plugin;
public class DoorManager {
public void init(Plugin plugin) {
if (TweaksConfig.getConfig().doorKnocking()) {
plugin.getServer().getPluginManager().registerEvents(new DoorKnockListener(), plugin);
}
if (TweaksConfig.getConfig().doorDoubleOpen()) {
plugin.getServer().getPluginManager().registerEvents(new DoorOpenListener(), plugin);
}
}
}

View file

@ -4,56 +4,12 @@ import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.Door;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.RayTraceResult;
import java.util.concurrent.ThreadLocalRandom;
public class DoorListener implements Listener {
@EventHandler
public void onBlockDamage(BlockDamageEvent event) {
Block block = event.getBlock();
if (!(block.getBlockData() instanceof Door)) return;
World world = block.getLocation().getWorld();
Player player = event.getPlayer();
// TODO maybe it would be faster to check just player and the door
RayTraceResult result = player.rayTraceBlocks(5);
if (result == null) return;
Location hitLocation = result.getHitPosition().toLocation(world);
double distance = player.getEyeLocation().distanceSquared(hitLocation);
if (distance > 12) return;
Sound sound = block.getType() == Material.IRON_DOOR ? Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR : Sound.ENTITY_ZOMBIE_ATTACK_WOODEN_DOOR;
float volume = player.isSneaking() ? 0.4f : 1f;
float pitch = player.getFallDistance() > 0 ? 1f : 1.5f;
if (player.hasPotionEffect(PotionEffectType.NAUSEA)) {
pitch = ThreadLocalRandom.current().nextFloat(0.5f, 0.7f);
}
PotionEffect fatigue = player.getPotionEffect(PotionEffectType.MINING_FATIGUE);
if (fatigue != null) {
int level = fatigue.getAmplifier();
volume /= level / 3f;
pitch /= level;
}
volume *= (float) ((10.0 - Math.min(distance - 2, 10.0)) / 10.0);
world.playSound(hitLocation, sound, volume, pitch);
//world.spawnParticle(Particle.BLOCK, hitLocation, (int) (10 * volume), door);
}
public class DoorOpenListener implements Listener {
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
@ -109,4 +65,5 @@ public class DoorListener implements Listener {
location.getBlock().setBlockData(nextDoor);
}
}
}

View file

@ -5,6 +5,7 @@ import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.reflect.StructureModifier;
import com.google.gson.JsonElement;
import eu.m724.tweaks.TweaksConfig;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import net.minecraft.SharedConstants;
@ -21,15 +22,18 @@ import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
public class MotdListener {
private final String motdSetName;
private final Plugin plugin;
private Component[] motds;
public MotdListener(String motdSetName) {
this.motdSetName = motdSetName;
public MotdListener(Plugin plugin) {
this.plugin = plugin;
}
public void init(Plugin plugin) throws IOException {
public void init() throws IOException {
// TODO adding more MOTD features would require checking whether to enable set
String motdSetName = TweaksConfig.getConfig().motdSet();
File motdSetsFile = new File(plugin.getDataFolder() + "/motd sets/" + motdSetName + ".txt");
// if the directory didn't exist create example motd sets
@ -46,8 +50,6 @@ public class MotdListener {
})
.toArray(Component[]::new);
plugin.getLogger().info("Loaded %d MOTDs".formatted(motds.length));
registerListener(plugin);
}

View file

@ -4,6 +4,7 @@ import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.reflect.StructureModifier;
import eu.m724.tweaks.TweaksConfig;
import net.minecraft.network.protocol.common.custom.BrandPayload;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@ -12,34 +13,35 @@ import org.bukkit.scheduler.BukkitRunnable;
public class F3NameListener {
private final Plugin plugin;
private boolean showPing, showMspt;
private String text;
public F3NameListener(Plugin plugin) {
this.plugin = plugin;
}
public void init() {
showPing = TweaksConfig.getConfig().brandShowPing();
showMspt = TweaksConfig.getConfig().brandShowMspt();
text = TweaksConfig.getConfig().brandText();
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)
if (showPing) {
for (Player player : plugin.getServer().getOnlinePlayers()) {
double ping = PlayerPing.getPingMillis(player);
String brand;
if (ping == 0) {
brand = "wait";
} else {
if (showMspt) {
brand = "%.2f mspt | Ping: %.2f ms".formatted(mspt, PlayerPing.getPingMillis(player));
} else {
brand = "Ping: %.2f ms".formatted(PlayerPing.getPingMillis(player));
String text = getBrandText(player);
changeBrandFor(player, text);
}
}
changeBrand(player, brand);
} else {
String text = getBrandText(null);
changeBrandAll(text);
}
}
}.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
// TODO is there much difference if we create a packet instead of modifying?
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
@ -50,7 +52,7 @@ public class F3NameListener {
PacketContainer packet = event.getPacket();
InternalStructure structure = new InternalStructure(
new BrandPayload("wait"),
new BrandPayload(text),
new StructureModifier<>(BrandPayload.class)
);
@ -59,7 +61,29 @@ public class F3NameListener {
});
}
private void changeBrand(Player player, String brand) {
/**
* Composes a brand name
* @param player player or null if server wide
* @return the brand name
*/
private String getBrandText(Player player) {
String _text = text;
if (showMspt) {
double mspt = PlayerPing.getMillisPerTick();
if (mspt > 50.05) { // mspt is at least like 50.01 because of some measuring overheads (I think)
_text += " | %.2f mspt".formatted(mspt);
}
}
if (showPing) {
_text += " | %.2f ms".formatted(PlayerPing.getPingMillis(player));
}
return _text;
}
private PacketContainer composeBrandPacket(String brand) {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.CUSTOM_PAYLOAD);
InternalStructure structure = new InternalStructure(
@ -68,6 +92,21 @@ public class F3NameListener {
);
packet.getStructures().write(0, structure);
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
return packet;
}
/**
* Changes server brand for all players on the server
*/
private void changeBrandAll(String brand) {
ProtocolLibrary.getProtocolManager().broadcastServerPacket(composeBrandPacket(brand));
}
/**
* Changes server brand for a player
*/
private void changeBrandFor(Player player, String brand) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, composeBrandPacket(brand));
}
}

View file

@ -1,68 +1,24 @@
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 eu.m724.tweaks.TweaksConfig;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PingChecker extends BukkitRunnable {
public class PingChecker {
private final Plugin plugin;
// 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
if (!PlayerPing.kickQueue.contains(player)) {
event.setCancelled(true);
if (TweaksConfig.getConfig().isProtocolLib()) {
plugin.getLogger().info("Using ProtocolLib for checking ping");
new ProtocolPingChecker(plugin).start();
} else {
PlayerPing.kickQueue.remove(player);
plugin.getLogger().info("Using Spigot for checking ping");
}
}
}
});
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 -> {
// dropped packets happen so timing out a request after 30 seconds
if (System.nanoTime() - pending.getOrDefault(player, 0L) < 30000000000L) 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);
});
}
}

View file

@ -1,5 +1,6 @@
package eu.m724.tweaks.ping;
import eu.m724.tweaks.TweaksConfig;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
@ -20,6 +21,11 @@ public class PingCommands implements CommandExecutor {
.create();
player.spigot().sendMessage(component);
} else if (command.getName().equals("dkick")) {
if (!TweaksConfig.getConfig().isProtocolLib()) {
sender.sendMessage("This feature is not available");
return true;
}
if (args.length == 0) {
sender.sendMessage("Include one or more player names");
} else {

View file

@ -1,5 +1,6 @@
package eu.m724.tweaks.ping;
import eu.m724.tweaks.TweaksConfig;
import org.bukkit.entity.Player;
import java.util.Map;
@ -7,16 +8,24 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class PlayerPing {
// in nanos
static final Map<Player, Long> pings = new ConcurrentHashMap<>();
// nanos per tick
static volatile long nspt = 0L;
// if protocol lib is installed we use our own checker
private static final boolean external = TweaksConfig.getConfig().isProtocolLib();
// TODO remove this feature because why
static final Set<Player> kickQueue = ConcurrentHashMap.newKeySet();
// current player latencies, in nanos
static final Map<Player, Long> pings = new ConcurrentHashMap<>();
// nanoseconds per tick
static volatile long nspt = 0L;
public static long getPingNanos(Player player) {
if (!external) {
return (long) player.getPing() * 1000000;
} else {
return pings.getOrDefault(player, -1L);
}
}
public static double getPingMillis(Player player) {
return getPingNanos(player) / 1000000.0; // a mil ns in ms

View file

@ -0,0 +1,66 @@
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 ProtocolPingChecker extends BukkitRunnable {
// this is in nanoseconds
private final Map<Player, Long> pending = new ConcurrentHashMap<>();
private final Plugin plugin;
ProtocolPingChecker(Plugin plugin) {
this.plugin = plugin;
}
public void start() {
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
if (!PlayerPing.kickQueue.contains(player)) {
event.setCancelled(true);
} else {
PlayerPing.kickQueue.remove(player);
}
}
}
});
this.runTaskTimerAsynchronously(plugin, 0, 200); // 10 secs
}
@Override
public void run() {
plugin.getServer().getOnlinePlayers().forEach(player -> {
// dropped packets happen so timing out a request after 30 seconds
if (System.nanoTime() - pending.getOrDefault(player, 0L) < 30000000000L) 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);
});
}
}

View file

@ -0,0 +1,28 @@
package eu.m724.tweaks.worldborder;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
// TODO I could do this with API but yeah I'm more comfortable with this
public record WorldBorderInfoPayload(int extensionRadius) implements CustomPacketPayload {
private static final Type<WorldBorderInfoPayload> TYPE =
new Type<>(ResourceLocation.tryBuild("tweaks724", "worldborder"));
public static final StreamCodec<FriendlyByteBuf, WorldBorderInfoPayload> STREAM_CODEC =
CustomPacketPayload.codec(WorldBorderInfoPayload::write, WorldBorderInfoPayload::new);
public Type<? extends CustomPacketPayload> type() {
return TYPE;
}
private WorldBorderInfoPayload(FriendlyByteBuf byteBuf) {
this(byteBuf.readVarInt());
}
private void write(FriendlyByteBuf byteBuf) {
byteBuf.writeVarInt(extensionRadius);
}
}

View file

@ -2,19 +2,21 @@ package eu.m724.tweaks.worldborder;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.reflect.StructureModifier;
import net.minecraft.SharedConstants;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundInitializeBorderPacket;
import net.minecraft.network.protocol.status.ServerStatus;
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 org.bukkit.plugin.Plugin;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import java.nio.ByteBuffer;
public class WorldBorderManager {
private static final int EXTENSION_RADIUS = 512;
public void init(Plugin plugin) {
plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "tweaks724:worldborder");
byte[] infoArray = ByteBuffer.allocate(4).putInt(EXTENSION_RADIUS).array();
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
@ -23,12 +25,43 @@ public class WorldBorderManager {
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
event.getPacket().getDoubles().write(2, 60000000.0);
event.getPacket().getDoubles().write(3, 60000000.0);
// old diameter
packet.getDoubles().write(2, packet.getDoubles().read(2) + EXTENSION_RADIUS * 2);
// new diameter
packet.getDoubles().write(3, packet.getDoubles().read(3) + EXTENSION_RADIUS * 2);
System.out.println(
packet
);
// border radius
// packet.getIntegers().write(0, packet.getIntegers().read(0) + EXTENSION_RADIUS);
// warning distance
packet.getIntegers().write(1, packet.getIntegers().read(1) + EXTENSION_RADIUS);
event.getPlayer().sendPluginMessage(plugin, "tweaks724:worldborder", infoArray);
}
});
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
PacketType.Play.Server.SET_BORDER_SIZE
) {
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
// diameter
packet.getDoubles().write(0, packet.getDoubles().read(0) + EXTENSION_RADIUS * 2);
}
});
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
PacketType.Play.Server.SET_BORDER_WARNING_DISTANCE
) {
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
// warning distance
packet.getIntegers().write(0, packet.getIntegers().read(0) + EXTENSION_RADIUS);
}
});
}

View file

@ -0,0 +1,50 @@
worldborder:
# Hides the world border at 30 mil
# Client side so it won't make the world bigger or anything
# Warning: Don't use /worldborder while this is on
hide: true
# "Server brand" (second line of F3)
brand:
enabled: true
# Custom text, "" to not use
text: "My server"
# Show a player's ping
showPing: true
# Show mspt (millis per tick) if server is lagging
showMspt: true
doors:
# Opening one door also opens the adjacent one
doubleOpen: true
# Make a sound on left-clicking
knocking: true
motd:
# Name of the set containing the MOTDs
# (random displayed every ping)
# "" or false to disable
set: "example"
chat:
# Enable chatrooms
enabled: true
# Whether event messages like join, quit, death (I think that's all needed)
# are only within the player's chatroom
localEvents: true
# Name of the default / global chatroom
defaultName: "global"
compass:
# Compass will be shown in a text form
enabled: true
# How much points (each point is separated with a space)
# I suggest making this uneven to make center... in center
width: 9
# How many degrees every point
precision: 10
# Finally, thank you for downloading Tweaks724, I hope you enjoy!
# Don't modify unless told to
magic number dont modify this: 1