wip
All checks were successful
/ deploy (push) Successful in 1m24s

This commit is contained in:
Minecon724 2024-11-18 16:52:57 +01:00
parent 4503f15d6c
commit 621c006590
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
15 changed files with 233 additions and 51 deletions

19
README.md Normal file
View file

@ -0,0 +1,19 @@
# tweaks724
Dependencies: ProtocolLib
### Caveats
Disable "secure chat"
### Chatrooms
- `/c` to see info about current chatroom
- `/c <name>` to join a chatroom
- `/cm create <name>` to create a chatroom
- `/cm set* <name>` to change a setting (for owners)
### Commands
- `/dkick` - discreet kick. The player will be kicked with a cryptic error message, and those smarter will think it's a plugin bug.[^1] Brilliant!
- `/ping` - checks ping. Ping is checked using a more accurate[^2] method
[^1]: or, better, client bug, if they don't trust their client
[^2]: not

36
pom.xml
View file

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>eu.m724</groupId>
<artifactId>mutils</artifactId>
<artifactId>tweaks</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
@ -21,6 +21,40 @@
<filtering>true</filtering>
</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>
<version>3.6.0</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<minimizeJar>true</minimizeJar>
<artifactSet>
<includes>
<include>eu.m724:tweaks</include>
</includes>
</artifactSet>
<filters>
<filter>
<artifact>*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- end of this -->
</plugins>
</build>
<repositories>

View file

@ -25,5 +25,6 @@ public class TweaksPlugin extends JavaPlugin {
new F3NameListener(this).init();
new PingChecker(this).init();
Objects.requireNonNull(getCommand("ping")).setExecutor(new PingCommands());
Objects.requireNonNull(getCommand("dkick")).setExecutor(new PingCommands());
}
}

View file

@ -30,17 +30,12 @@ public class ChatCommands implements CommandExecutor {
ChatRoom chatRoom = manager.getPlayerChatRoom(player);
if (args.length == 0) { // show room
BaseComponent[] component = new ComponentBuilder("Active chat room: ").color(ChatColor.GOLD)
.append(chatRoom.id).color(chatRoom.color)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(chatRoom.getInfoComponent())))
.create();
player.spigot().sendMessage(component);
player.spigot().sendMessage(chatRoom.getInfoComponent());
} else { // join room
// TODO move joining logic
String id = args[0];
String password = null;
if (args.length > 1) {
password = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")).strip();
password = Arrays.stream(args).skip(1).collect(Collectors.joining(" "));
}
boolean authenticated = false;
@ -105,7 +100,8 @@ public class ChatCommands implements CommandExecutor {
case "delete" -> {
if (argument.equals(chatRoom.id)) {
if (isOwner) {
// TODO
manager.deleteChatRoom(chatRoom);
sender.sendMessage("Room %s deleted".formatted(chatRoom.id));
} else {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
}
@ -134,7 +130,7 @@ public class ChatCommands implements CommandExecutor {
}
case "setpassword" -> {
if (isOwner) {
chatRoom.password = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")).strip();
chatRoom.password = Arrays.stream(args).skip(1).collect(Collectors.joining(" "));
try {
manager.saveChatRoom(chatRoom);
sender.sendMessage("Password changed");
@ -174,13 +170,13 @@ public class ChatCommands implements CommandExecutor {
case "create" ->
sender.sendMessage("Please pass a room name as an argument. The room name must be of characters and digits.");
case "delete" ->
sender.sendMessage("You want to delete room %s. Confirm by passing its name as an argument for this action.".formatted(chatRoom));
sender.sendMessage("You want to delete room %s. Confirm by passing its name as an argument for this action.".formatted(chatRoom.id));
case "setowner" ->
sender.sendMessage("To transfer ownership of room %s, pass the new owner name as an argument for this action.".formatted(chatRoom));
sender.sendMessage("To transfer ownership of room %s, pass the new owner name as an argument for this action.".formatted(chatRoom.id));
case "setpassword" ->
sender.sendMessage("To change the password of room %s, pass the new password as an argument for this action.".formatted(chatRoom));
sender.sendMessage("To change the password of room %s, pass the new password as an argument for this action.".formatted(chatRoom.id));
case "setcolor" ->
sender.sendMessage("To change the message color of room %s, pass the new color as an argument for this action. #hex or color name.".formatted(chatRoom));
sender.sendMessage("To change the message color of room %s, pass the new color as an argument for this action. #hex or color name.".formatted(chatRoom.id));
default ->
sender.sendMessage("Actions: create, delete, setowner, setpassword, setcolor");
}

View file

@ -0,0 +1,39 @@
package eu.m724.tweaks.chat;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.entity.Player;
public class ChatFormatUtils {
public static BaseComponent[] formatPlayer(Player player) {
ChatColor nameColor = ChatColor.of("#" + Integer.toHexString(player.getName().hashCode()).substring(0, 6));
if (player.getCustomName() != null) {
return new ComponentBuilder()
.append("~" + player.getCustomName()).color(nameColor)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(player.getName())))
.create();
} else {
return new ComponentBuilder()
.append(player.getName()).color(nameColor)
.create();
}
}
public static BaseComponent[] chatRoomPrefixShort(ChatRoom chatRoom) {
ChatColor prefixColor = ChatColor.of(chatRoom.color.getColor().darker());
return new ComponentBuilder(chatRoom.id.charAt(0) + " ").color(prefixColor)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(chatRoom.getInfoComponent())))
.create();
}
public static BaseComponent[] formatChatRoom(ChatRoom chatRoom) {
return new ComponentBuilder(chatRoom.id).color(chatRoom.color)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(chatRoom.getInfoComponent())))
.create();
}
}

View file

@ -3,13 +3,12 @@ package eu.m724.tweaks.chat;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
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;
@ -24,12 +23,37 @@ public class ChatListener implements Listener {
ChatRoom chatRoom = chatManager.getPlayerChatRoom(player);
BaseComponent[] component = new ComponentBuilder("Chat room: ").color(ChatColor.GOLD)
.append(chatRoom.id).color(ChatColor.AQUA)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(chatRoom.getInfoComponent())))
.append(ChatFormatUtils.formatChatRoom(chatRoom))
.create();
player.spigot().sendMessage(component);
event.setJoinMessage(null); // TODO room messages
chatRoom.broadcast(
new ComponentBuilder()
.append(ChatFormatUtils.chatRoomPrefixShort(chatRoom))
.append(ChatFormatUtils.formatPlayer(player))
.append(" has joined the server").color(ChatColor.GREEN)
.create()
);
// remove Minecraft join message
event.setJoinMessage(null);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
ChatRoom chatRoom = chatManager.removePlayer(player);
chatRoom.broadcast(
new ComponentBuilder()
.append(ChatFormatUtils.chatRoomPrefixShort(chatRoom))
.append(ChatFormatUtils.formatPlayer(player))
.append(" has left the server").color(ChatColor.RED)
.create()
);
// remove Minecraft quit message
event.setQuitMessage(null);
}
@EventHandler
@ -38,24 +62,14 @@ public class ChatListener implements Listener {
ChatRoom chatRoom = chatManager.getPlayerChatRoom(player);
String message = event.getMessage();
// TODO move sending logic
ChatColor prefixColor = ChatColor.of(chatRoom.color.getColor().darker());
ChatColor nameColor = ChatColor.of("#" + Integer.toHexString(player.getName().hashCode()).substring(0, 6));
ComponentBuilder builder = new ComponentBuilder(chatRoom.id.charAt(0) + " ").color(prefixColor)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(chatRoom.getInfoComponent())));
if (player.getCustomName() != null) {
builder = builder.append("~" + player.getCustomName() + ": ").color(nameColor)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(chatRoom.getInfoComponent())));
} else {
builder = builder.append(player.getName() + ": ").color(nameColor);
}
builder = builder.append(message).color(chatRoom.color);
ComponentBuilder builder = new ComponentBuilder();
builder.append(ChatFormatUtils.chatRoomPrefixShort(chatRoom));
builder.append(ChatFormatUtils.formatPlayer(player)).append(": ");
builder.append(message).color(chatRoom.color);
chatRoom.broadcast(builder.create());
// remove the original message
event.setCancelled(true);
}
}

View file

@ -1,5 +1,7 @@
package eu.m724.tweaks.chat;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentBuilder;
import org.bukkit.NamespacedKey;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
@ -27,6 +29,18 @@ public class ChatManager {
plugin.getServer().getPluginManager().registerEvents(new ChatListener(this), plugin);
}
/**
* removes player from the server and their chatroom
* @param player the player
* @return the chatroom the player was removed from
*/
public ChatRoom removePlayer(Player player) {
ChatRoom chatRoom = playerMap.remove(player);
chatRoom.players.remove(player);
return chatRoom;
}
/**
* Get a chat room by id.<br>
* If the chat room is not loaded, it's loaded.
@ -59,6 +73,22 @@ public class ChatManager {
player.getPersistentDataContainer().set(chatRoomKey, PersistentDataType.STRING, chatRoom.id);
playerMap.put(player, chatRoom);
chatRoom.players.add(player);
chatRoom.broadcast(
new ComponentBuilder()
.append(ChatFormatUtils.chatRoomPrefixShort(chatRoom))
.append(ChatFormatUtils.formatPlayer(player))
.append(" has left the chat room").color(ChatColor.RED)
.create()
);
chatRoom.broadcast(
new ComponentBuilder()
.append(ChatFormatUtils.chatRoomPrefixShort(chatRoom))
.append(ChatFormatUtils.formatPlayer(player))
.append(" has joined the chat room").color(ChatColor.GREEN)
.create()
);
}
/**
@ -123,6 +153,14 @@ public class ChatManager {
ChatRoomLoader.save(plugin, chatRoom);
}
public void deleteChatRoom(ChatRoom chatRoom) {
roomIdMap.remove(chatRoom.id);
ChatRoomLoader.getFile(plugin, chatRoom.id).delete();
chatRoom.players.forEach(player -> {
setPlayerChatRoom(getById("global"), player);
});
}
/**
* If an ID is too short, too long, wrong composition, etc.
*/

View file

@ -25,7 +25,6 @@ public class ChatRoom {
this.owner = owner;
}
// TODO not recompute every time
/**
* @return A nicely formatted text block with info such as room id, owner, online, etc.
*/
@ -46,7 +45,7 @@ public class ChatRoom {
builder = builder.append(playersList.removeFirst().getName()).color(ChatColor.GRAY);
for (Player player : playersList) {
builder = builder.append(", ").color(ChatColor.GRAY)
builder = builder.append(", ").color(ChatColor.GREEN)
.append(player.getName()).color(ChatColor.AQUA);
}
}

View file

@ -12,13 +12,18 @@ import java.nio.file.Paths;
import java.util.UUID;
public class ChatRoomLoader {
private static File getFile(Plugin plugin, String id) {
/**
* Get the file of persistent storage of a chat room
* @return the file or null if ID is invalid
*/
static File getFile(Plugin plugin, String id) {
Path chatRoomsPath = Paths.get(plugin.getDataFolder().getPath(), "rooms");
chatRoomsPath.toFile().mkdirs();
// TODO sanitize
File chatRoomFile = Paths.get(chatRoomsPath.toFile().getPath(), id + ".yml").toFile();
return chatRoomFile;
if (validateId(id) != 0)
throw new RuntimeException("Invalid id: " + id);
return Paths.get(chatRoomsPath.toFile().getPath(), id + ".yml").toFile();
}
/**

View file

@ -20,7 +20,7 @@ public class DoorListener implements Listener {
@EventHandler
public void onBlockDamage(BlockDamageEvent event) {
Block block = event.getBlock();
if (!(block.getBlockData() instanceof Door door)) return;
if (!(block.getBlockData() instanceof Door)) return;
World world = block.getLocation().getWorld();
Player player = event.getPlayer();
@ -41,11 +41,10 @@ public class DoorListener implements Listener {
pitch = ThreadLocalRandom.current().nextFloat(0.5f, 0.7f);
}
PotionEffect weakness = player.getPotionEffect(PotionEffectType.WEAKNESS);
PotionEffect fatigue = player.getPotionEffect(PotionEffectType.MINING_FATIGUE);
int level = (weakness != null ? weakness.getAmplifier() : 0) + (fatigue != null ? fatigue.getAmplifier() : 0);
if (weakness != null || fatigue != null) {
if (fatigue != null) {
int level = fatigue.getAmplifier();
volume /= level / 3f;
pitch /= level;
}
@ -53,7 +52,7 @@ public class DoorListener implements Listener {
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);
//world.spawnParticle(Particle.BLOCK, hitLocation, (int) (10 * volume), door);
}
@EventHandler

View file

@ -14,6 +14,6 @@ public class MsptChecker extends BukkitRunnable {
}
public void init(Plugin plugin) {
this.runTaskTimerAsynchronously(plugin, 0, 100); // 5 secs
this.runTaskTimer(plugin, 0, 100); // 5 secs
}
}

View file

@ -17,7 +17,6 @@ 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<>();
@ -39,8 +38,13 @@ public class PingChecker extends BukkitRunnable {
long start = pending.remove(player);
PlayerPing.pings.put(player, System.nanoTime() - start);
// gotta cancel because the server will kick
event.setCancelled(true);
if (!PlayerPing.kickQueue.contains(player)) {
event.setCancelled(true);
} else {
PlayerPing.kickQueue.remove(player);
}
}
}
});
@ -52,7 +56,8 @@ public class PingChecker extends BukkitRunnable {
@Override
public void run() {
plugin.getServer().getOnlinePlayers().forEach(player -> {
if (pending.containsKey(player)) return;
// 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);

View file

@ -3,6 +3,7 @@ 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.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@ -18,6 +19,25 @@ public class PingCommands implements CommandExecutor {
.append("%.2fms".formatted(PlayerPing.getPingMillis(player))).color(ChatColor.AQUA)
.create();
player.spigot().sendMessage(component);
} else if (command.getName().equals("dkick")) {
if (args.length == 0) {
sender.sendMessage("Include one or more player names");
} else {
for (String name : args) {
Player player = Bukkit.getPlayer(name);
if (player == null) {
sender.sendMessage("Player %s is not online".formatted(name));
}
if (PlayerPing.kick(player)) {
if (player.getName().equals(sender.getName())) {
sender.sendMessage("Kicking yourself? Very well");
}
sender.sendMessage("Player %s will be kicked shortly".formatted(name));
} else {
sender.sendMessage("Player %s is already queued".formatted(name));
}
}
}
}
return true;
}

View file

@ -3,14 +3,16 @@ package eu.m724.tweaks.ping;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.Set;
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;
static volatile long nspt = 0L;
static final Set<Player> kickQueue = ConcurrentHashMap.newKeySet();
public static long getPingNanos(Player player) {
return pings.getOrDefault(player, -1L);
@ -27,4 +29,8 @@ public class PlayerPing {
public static double getMillisPerTick() {
return nspt / 1000000.0; // a mil ns in ms
}
public static boolean kick(Player player) {
return kickQueue.add(player);
}
}

View file

@ -15,4 +15,11 @@ commands:
aliases: [cm, crm]
ping:
description: Your ping
dkick:
description: Kick a player discreetly
permission: tweaks724.dkick
permissions:
tweaks724.dkick:
default: op