WSS !!!
This commit is contained in:
@@ -1,12 +1,46 @@
|
|||||||
package fr.redsavant;
|
package fr.redsavant;
|
||||||
|
|
||||||
|
import fr.redsavant.commands.MuteCommand;
|
||||||
|
import fr.redsavant.proximity.*;
|
||||||
|
import fr.redsavant.ws.WebSocketClient;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class LagoonPlugin extends BootstrapPlugin {
|
public class LagoonPlugin extends BootstrapPlugin {
|
||||||
|
|
||||||
|
private WebSocketClient wsClient;
|
||||||
|
private ProximityTask proximityTask;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
|
saveDefaultConfig();
|
||||||
|
|
||||||
|
String roomCode = getConfig().getString("room-code", "CHANGE_ME");
|
||||||
|
URI wsUri = URI.create("wss://lagoon.under-scape.com/ws/rooms/" + roomCode + "/plugins/proximity");
|
||||||
|
|
||||||
|
PlayerCodeManager codeManager = new PlayerCodeManager();
|
||||||
|
Set<UUID> mutedPlayers = new HashSet<>();
|
||||||
|
|
||||||
|
wsClient = new WebSocketClient(wsUri, msg -> {
|
||||||
|
// future reponse de drewen
|
||||||
|
});
|
||||||
|
wsClient.connect();
|
||||||
|
|
||||||
|
getServer().getPluginManager().registerEvents(new ProximityListener(codeManager), this);
|
||||||
|
getCommand("proximity").setExecutor(new MuteCommand(mutedPlayers));
|
||||||
|
|
||||||
|
proximityTask = new ProximityTask(wsClient, codeManager, mutedPlayers);
|
||||||
|
proximityTask.runTaskTimerAsynchronously(this, 20L, 60L);
|
||||||
|
|
||||||
|
getLogger().info("LagoonPlugin enabled — room: " + roomCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
|
if (proximityTask != null) proximityTask.cancel();
|
||||||
|
if (wsClient != null) wsClient.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
43
bukkit/src/main/java/fr/redsavant/commands/MuteCommand.java
Normal file
43
bukkit/src/main/java/fr/redsavant/commands/MuteCommand.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package fr.redsavant.commands;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class MuteCommand implements CommandExecutor {
|
||||||
|
|
||||||
|
private final Set<UUID> mutedPlayers;
|
||||||
|
|
||||||
|
public MuteCommand(Set<UUID> mutedPlayers) {
|
||||||
|
this.mutedPlayers = mutedPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
sender.sendMessage("§cUsage : /proximity mute <player>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player target = Bukkit.getPlayerExact(args[0]);
|
||||||
|
if (target == null) {
|
||||||
|
sender.sendMessage("§cUnknow player");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID uuid = target.getUniqueId();
|
||||||
|
if (mutedPlayers.contains(uuid)) {
|
||||||
|
mutedPlayers.remove(uuid);
|
||||||
|
sender.sendMessage("§a" + target.getName() + " has been unmuted on Lagoon");
|
||||||
|
} else {
|
||||||
|
mutedPlayers.add(uuid);
|
||||||
|
sender.sendMessage("§e" + target.getName() + " has been muted on Lagoon");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package fr.redsavant.proximity;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class PlayerCodeManager {
|
||||||
|
|
||||||
|
private static final String CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // ambiguous chars removed
|
||||||
|
private static final int CODE_LENGTH = 6;
|
||||||
|
private static final SecureRandom RANDOM = new SecureRandom();
|
||||||
|
|
||||||
|
private final Map<UUID, String> codes = new HashMap<>();
|
||||||
|
|
||||||
|
public String getOrCreate(Player player) {
|
||||||
|
return codes.computeIfAbsent(player.getUniqueId(), uuid -> generateCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(UUID uuid) {
|
||||||
|
return codes.get(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(UUID uuid) {
|
||||||
|
codes.remove(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateCode() {
|
||||||
|
StringBuilder sb = new StringBuilder(CODE_LENGTH);
|
||||||
|
for (int i = 0; i < CODE_LENGTH; i++) {
|
||||||
|
sb.append(CHARS.charAt(RANDOM.nextInt(CHARS.length())));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package fr.redsavant.proximity;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
public class ProximityListener implements Listener {
|
||||||
|
|
||||||
|
private final PlayerCodeManager codeManager;
|
||||||
|
|
||||||
|
public ProximityListener(PlayerCodeManager codeManager) {
|
||||||
|
this.codeManager = codeManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onJoin(PlayerJoinEvent event) {
|
||||||
|
String code = codeManager.getOrCreate(event.getPlayer());
|
||||||
|
sendCodeMessage(event.getPlayer(), code);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onQuit(PlayerQuitEvent event) {
|
||||||
|
codeManager.remove(event.getPlayer().getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendCodeMessage(org.bukkit.entity.Player player, String code) {
|
||||||
|
TextComponent msg = new TextComponent("§a[Lagoon] §fTon code de liaison : §b§l" + code);
|
||||||
|
msg.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, code));
|
||||||
|
msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||||
|
new ComponentBuilder("§7Clique pour copier").create()));
|
||||||
|
player.spigot().sendMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package fr.redsavant.proximity;
|
||||||
|
|
||||||
|
import fr.redsavant.ws.ProximityPayload;
|
||||||
|
import fr.redsavant.ws.WebSocketClient;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ProximityTask extends BukkitRunnable {
|
||||||
|
|
||||||
|
private final WebSocketClient wsClient;
|
||||||
|
private final PlayerCodeManager codeManager;
|
||||||
|
private final Set<UUID> mutedPlayers;
|
||||||
|
|
||||||
|
public ProximityTask(WebSocketClient wsClient, PlayerCodeManager codeManager, Set<UUID> mutedPlayers) {
|
||||||
|
this.wsClient = wsClient;
|
||||||
|
this.codeManager = codeManager;
|
||||||
|
this.mutedPlayers = mutedPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!wsClient.isConnected()) return;
|
||||||
|
|
||||||
|
List<ProximityPayload.PlayerEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
String code = codeManager.get(player.getUniqueId());
|
||||||
|
if (code == null) continue;
|
||||||
|
|
||||||
|
int layer = switch (player.getWorld().getEnvironment()) {
|
||||||
|
case NETHER -> 1;
|
||||||
|
case THE_END -> 2;
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
// LE WORLD ZEBI (spigot c'est nul X< )
|
||||||
|
boolean muted = mutedPlayers.contains(player.getUniqueId());
|
||||||
|
|
||||||
|
entries.add(new ProximityPayload.PlayerEntry(
|
||||||
|
player.getLocation().getX(),
|
||||||
|
player.getLocation().getY(),
|
||||||
|
player.getLocation().getZ(),
|
||||||
|
layer,
|
||||||
|
code,
|
||||||
|
muted
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
wsClient.send(ProximityPayload.buildPositionsJson(entries));
|
||||||
|
}
|
||||||
|
}
|
||||||
1
bukkit/src/main/resources/config.yml
Normal file
1
bukkit/src/main/resources/config.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
room-code: "CHANGE_ME"
|
||||||
@@ -6,4 +6,9 @@ authors:
|
|||||||
- RedSavant
|
- RedSavant
|
||||||
- UnderScape (lagoon)
|
- UnderScape (lagoon)
|
||||||
api-version: '1.21'
|
api-version: '1.21'
|
||||||
main: fr.redsavant.LagoonPlugin
|
main: fr.redsavant.LagoonPlugin
|
||||||
|
commands:
|
||||||
|
proximity:
|
||||||
|
description: Base command
|
||||||
|
usage: /proximity mute <player>
|
||||||
|
permission: lagoon.admin
|
||||||
37
commun/src/main/java/fr/redsavant/ws/ProximityPayload.java
Normal file
37
commun/src/main/java/fr/redsavant/ws/ProximityPayload.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package fr.redsavant.ws;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ProximityPayload {
|
||||||
|
|
||||||
|
public record PlayerEntry(double x, double y, double z, int layer, String code, boolean muted) {}
|
||||||
|
|
||||||
|
public static String buildPositionsJson(List<PlayerEntry> entries) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{\"type\":\"positions\",\"data\":[");
|
||||||
|
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
PlayerEntry e = entries.get(i);
|
||||||
|
sb.append("{")
|
||||||
|
.append("\"x\":").append(e.x()).append(",")
|
||||||
|
.append("\"y\":").append(e.y()).append(",")
|
||||||
|
.append("\"z\":").append(e.z()).append(",")
|
||||||
|
.append("\"layer\":").append(e.layer()).append(",")
|
||||||
|
.append("\"code\":\"").append(e.code()).append("\"")
|
||||||
|
.append(",\"muted\":").append(e.muted())
|
||||||
|
.append("}");
|
||||||
|
if (i < entries.size() - 1) sb.append(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("]}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int dimensionToLayer(String dimensionKey) {
|
||||||
|
return switch (dimensionKey) {
|
||||||
|
case "minecraft:the_nether" -> 1;
|
||||||
|
case "minecraft:the_end" -> 2;
|
||||||
|
default -> 0; // overworld
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
94
commun/src/main/java/fr/redsavant/ws/WebSocketClient.java
Normal file
94
commun/src/main/java/fr/redsavant/ws/WebSocketClient.java
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package fr.redsavant.ws;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.WebSocket;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class WebSocketClient implements WebSocket.Listener {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(WebSocketClient.class.getName());
|
||||||
|
|
||||||
|
private final URI uri;
|
||||||
|
private final Consumer<String> onMessage;
|
||||||
|
private WebSocket socket;
|
||||||
|
private boolean connected = false;
|
||||||
|
private final ScheduledExecutorService reconnectScheduler = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
|
public WebSocketClient(URI uri, Consumer<String> onMessage) {
|
||||||
|
this.uri = uri;
|
||||||
|
this.onMessage = onMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect() {
|
||||||
|
HttpClient.newHttpClient()
|
||||||
|
.newWebSocketBuilder()
|
||||||
|
.buildAsync(uri, this)
|
||||||
|
.thenAccept(ws -> {
|
||||||
|
this.socket = ws;
|
||||||
|
this.connected = true;
|
||||||
|
LOGGER.info("WebSocket connected : " + uri);
|
||||||
|
})
|
||||||
|
.exceptionally(ex -> {
|
||||||
|
LOGGER.warning("Failed to connect to WebSocket, retry in 5s : " + ex.getMessage());
|
||||||
|
scheduleReconnect();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String message) {
|
||||||
|
if (socket != null && connected) {
|
||||||
|
socket.sendText(message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
reconnectScheduler.shutdownNow();
|
||||||
|
if (socket != null) {
|
||||||
|
socket.sendClose(WebSocket.NORMAL_CLOSURE, "shutdown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleReconnect() {
|
||||||
|
reconnectScheduler.schedule(this::connect, 5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen(WebSocket webSocket) {
|
||||||
|
connected = true;
|
||||||
|
LOGGER.info("WebSocket opened");
|
||||||
|
webSocket.request(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
|
||||||
|
onMessage.accept(data.toString());
|
||||||
|
webSocket.request(1);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
|
||||||
|
connected = false;
|
||||||
|
LOGGER.warning("WebSocket closed (" + statusCode + "), re-connect in 5s...");
|
||||||
|
scheduleReconnect();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(WebSocket webSocket, Throwable error) {
|
||||||
|
connected = false;
|
||||||
|
LOGGER.warning("WebSocket error : " + error.getMessage());
|
||||||
|
scheduleReconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user