Skip to content

Instantly share code, notes, and snippets.

@gamerchamper
Created April 2, 2026 04:41
Show Gist options
  • Select an option

  • Save gamerchamper/ab735970609f7e1f344e2989f272be1a to your computer and use it in GitHub Desktop.

Select an option

Save gamerchamper/ab735970609f7e1f344e2989f272be1a to your computer and use it in GitHub Desktop.
main-paper
package com.normalsmp.normalSurvivalWorldsPaper;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import net.kyori.adventure.text.Component;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.event.*;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
public final class NormalSurvivalWorldsPaper extends JavaPlugin implements Listener {
private static final String CHANNEL = "nsw:playerdata";
private String serverName;
private String targetServer;
private double minX, maxX, minZ, maxZ;
private double buffer;
private final boolean DEBUG = true;
@Override
public void onEnable() {
saveDefaultConfig();
serverName = getConfig().getString("server-name");
targetServer = getConfig().getString("target-server");
minX = getConfig().getDouble("border.min-x");
maxX = getConfig().getDouble("border.max-x");
minZ = getConfig().getDouble("border.min-z");
maxZ = getConfig().getDouble("border.max-z");
buffer = getConfig().getDouble("border.buffer", 2);
getServer().getMessenger().registerOutgoingPluginChannel(this, CHANNEL);
getServer().getMessenger().registerIncomingPluginChannel(this, CHANNEL, this::onMessage);
Bukkit.getPluginManager().registerEvents(this, this);
// 🔁 Constant sync loop
new BukkitRunnable() {
@Override
public void run() {
for (Player p : Bukkit.getOnlinePlayers()) {
sendData(p);
}
}
}.runTaskTimer(this, 10L, 10L);
getLogger().info("[NSW] Paper initialized.");
getLogger().info("[NSW] Server: " + serverName + " -> Target: " + targetServer);
getLogger().info("[NSW] Border X: " + minX + " → " + maxX + " | Z: " + minZ + " → " + maxZ);
}
// =========================
// 📥 RECEIVE RESPONSE
// =========================
private void onMessage(String channel, Player player, byte[] message) {
ByteArrayDataInput in = ByteStreams.newDataInput(message);
try {
String sub = in.readUTF();
if (sub.equals("RESPONSE")) {
double x = in.readDouble();
double y = in.readDouble();
double z = in.readDouble();
float yaw = in.readFloat();
float pitch = in.readFloat();
Location loc = new Location(player.getWorld(), x, y, z, yaw, pitch);
player.teleport(loc);
if (DEBUG) {
getLogger().info("[SYNCED] " + player.getName() +
" -> X:" + round(x) + " Y:" + round(y) + " Z:" + round(z));
}
}
} catch (Exception e) {
getLogger().severe("[ERROR] Failed to read incoming message");
e.printStackTrace();
}
}
// =========================
// 📤 REQUEST DATA ON JOIN
// =========================
@EventHandler
public void onJoin(PlayerJoinEvent e) {
Player p = e.getPlayer();
Bukkit.getScheduler().runTaskLater(this, () -> {
requestData(p);
}, 20L);
}
private void requestData(Player player) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("REQUEST");
out.writeUTF(player.getUniqueId().toString());
player.sendPluginMessage(this, CHANNEL, out.toByteArray());
if (DEBUG) {
getLogger().info("[REQUEST] Sent for " + player.getName());
}
}
// =========================
// 📤 SEND DATA TO VELOCITY
// =========================
private void sendData(Player player) {
Location loc = player.getLocation();
double x = loc.getX();
double y = loc.getY();
double z = loc.getZ();
String edge = detectEdge(x, z);
String nearEdge = detectNearEdge(x, z);
double distance = distanceToBorder(x, z);
boolean borderHit = !edge.equals("NONE");
// 🔔 Actionbar warning
if (!nearEdge.equals("NONE") && distance <= 10 && distance > 0) {
player.sendActionBar(Component.text("§cBorder in " + (int) distance + " blocks!"));
}
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("DATA");
out.writeUTF(player.getUniqueId().toString());
out.writeUTF(player.getName());
out.writeUTF(serverName);
out.writeUTF(player.getWorld().getName());
out.writeDouble(x);
out.writeDouble(y);
out.writeDouble(z);
out.writeFloat(loc.getYaw());
out.writeFloat(loc.getPitch());
out.writeBoolean(borderHit);
out.writeUTF(targetServer);
out.writeUTF(edge);
player.sendPluginMessage(this, CHANNEL, out.toByteArray());
if (DEBUG && borderHit) {
getLogger().info("[BORDER HIT] " + player.getName() +
" EDGE:" + edge +
" X:" + round(x) +
" Z:" + round(z));
}
}
// =========================
// EDGE LOGIC (BOX)
// =========================
private String detectEdge(double x, double z) {
if (x >= maxX) return "EAST";
if (x <= minX) return "WEST";
if (z >= maxZ) return "SOUTH";
if (z <= minZ) return "NORTH";
return "NONE";
}
private String detectNearEdge(double x, double z) {
if (x >= maxX - buffer) return "EAST";
if (x <= minX + buffer) return "WEST";
if (z >= maxZ - buffer) return "SOUTH";
if (z <= minZ + buffer) return "NORTH";
return "NONE";
}
private double distanceToBorder(double x, double z) {
double dx = Math.min(Math.abs(maxX - x), Math.abs(x - minX));
double dz = Math.min(Math.abs(maxZ - z), Math.abs(z - minZ));
return Math.min(dx, dz);
}
private double round(double val) {
return Math.round(val * 100.0) / 100.0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment