diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25e2861..7252706 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,10 +12,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: - java-version: '11' + java-version: '21' distribution: 'adopt' cache: 'gradle' - name: Build with Gradle diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 8e01ac0..fdaff33 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: - java-version: '11' + java-version: '21' distribution: 'adopt' cache: 'gradle' - name: Bump version and push tag diff --git a/common/build.gradle.kts b/common/build.gradle.kts index bf98760..d65e1e8 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -4,22 +4,30 @@ plugins { repositories { mavenCentral() + maven { + url = uri("https://repo.papermc.io/repository/maven-public/") + } } -val javaTarget = 8 // Sponge targets a minimum of Java 17 +val javaTarget = 25 java { - sourceCompatibility = JavaVersion.toVersion(javaTarget) - targetCompatibility = JavaVersion.toVersion(javaTarget) - if (JavaVersion.current() < JavaVersion.toVersion(javaTarget)) { - toolchain.languageVersion.set(JavaLanguageVersion.of(javaTarget)) + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaTarget)) + vendor.set(JvmVendorSpec.matching("")) + } +} + +tasks { + withType().configureEach { + options.release.set(javaTarget) } } dependencies { - compileOnly("com.google.guava:guava:21.0") - compileOnly("com.google.code.gson:gson:2.8.0") - compileOnly("org.spongepowered:configurate-core:4.1.2") - compileOnly("net.kyori:adventure-api:4.10.0") - compileOnly("net.kyori:adventure-text-minimessage:4.10.0") - compileOnly("net.kyori:adventure-nbt:4.15.0") + compileOnly("com.google.guava:guava:33.4.8-jre") + compileOnly("com.google.code.gson:gson:2.13.1") + implementation("org.spongepowered:configurate-core:4.2.0") + implementation("net.kyori:adventure-api:4.22.0") + implementation("net.kyori:adventure-text-minimessage:4.22.0") + implementation("net.kyori:adventure-nbt:4.22.0") } diff --git a/common/src/main/java/com/funniray/minimap/common/JavaMinimapPlugin.java b/common/src/main/java/com/funniray/minimap/common/JavaMinimapPlugin.java index 1ef419e..e07c566 100644 --- a/common/src/main/java/com/funniray/minimap/common/JavaMinimapPlugin.java +++ b/common/src/main/java/com/funniray/minimap/common/JavaMinimapPlugin.java @@ -25,7 +25,13 @@ public abstract class JavaMinimapPlugin implements MinimapPlugin { "journeymap:admin_save", "journeymap:teleport_req", "journeymap:common", + "journeymap:mp_options_req", + "journeymap:chunk_overlay", + "journeymap:remove_player", + "journeymap:player_loc", + "journeymap:waypoint", "worldinfo:world_id", + "xaerolib:main", "xaerominimap:main", "xaeroworldmap:main", "voxelmap:settings" @@ -69,20 +75,26 @@ public void loadConfig() { @Override public void handleSwitchWorld(MinimapWorld world, MinimapPlayer player) { - xaerosHandler.sendXaerosConfig(player); + xaerosHandler.sendXaerosWorldChangePackets(player); + jmHandler.sendPermissions(player); voxelHandler.sendSettings(player); } @Override public void handlePlayerJoined(MinimapPlayer player) { - xaerosHandler.sendXaerosHandshake(player); - xaerosHandler.sendXaerosConfig(player); + jmHandler.sendInitialState(player); + handlePlayerJoinedRepeat(player); + } + + public void handlePlayerJoinedRepeat(MinimapPlayer player) { + xaerosHandler.sendXaerosJoinPackets(player); voxelHandler.sendSettings(player); } @Override public void handlePlayerLeft(MinimapPlayer player) { jmHandler.playerLeft(player); + xaerosHandler.playerLeft(player); } public void saveConfig() { @@ -106,6 +118,7 @@ public void onPluginMessage(String channel, MinimapPlayer player, byte[] message break; case "xaerominimap": case "xaeroworldmap": + case "xaerolib": xaerosHandler.onPluginMessage(channel, player, message); break; case "worldinfo": diff --git a/common/src/main/java/com/funniray/minimap/common/api/MinimapPlayer.java b/common/src/main/java/com/funniray/minimap/common/api/MinimapPlayer.java index c7ddb76..cfb0e97 100644 --- a/common/src/main/java/com/funniray/minimap/common/api/MinimapPlayer.java +++ b/common/src/main/java/com/funniray/minimap/common/api/MinimapPlayer.java @@ -3,10 +3,21 @@ import com.funniray.minimap.common.version.Version; import net.kyori.adventure.text.Component; +import java.util.List; import java.util.UUID; public interface MinimapPlayer { + record PluginMessage(String channel, byte[] payload) { + } + void sendPluginMessage(byte[] message, String channel); + + default void sendPluginMessages(List messages) { + for (PluginMessage message : messages) { + sendPluginMessage(message.payload(), message.channel()); + } + } + void sendMessage(Component message); void teleport(MinimapLocation location); MinimapLocation getLocation(); @@ -15,5 +26,10 @@ public interface MinimapPlayer { UUID getUniqueId(); String getUsername(); boolean hasPermission(String string); + + default boolean hasExplicitPermission(String string) { + return hasPermission(string); + } + Version getVersion(); } diff --git a/common/src/main/java/com/funniray/minimap/common/api/MinimapServer.java b/common/src/main/java/com/funniray/minimap/common/api/MinimapServer.java index a8324be..bfb64ac 100644 --- a/common/src/main/java/com/funniray/minimap/common/api/MinimapServer.java +++ b/common/src/main/java/com/funniray/minimap/common/api/MinimapServer.java @@ -3,12 +3,13 @@ import com.funniray.minimap.common.version.Version; import java.util.List; +import java.util.function.Consumer; public interface MinimapServer { Version getMinecraftVersion(); String getLoaderVersion(); String getLoaderName(); - List getPlayers(); + void forEachPlayer(Consumer action); List getWorlds(); } diff --git a/common/src/main/java/com/funniray/minimap/common/jm/JMHandler.java b/common/src/main/java/com/funniray/minimap/common/jm/JMHandler.java index 1ded89e..7296ef0 100644 --- a/common/src/main/java/com/funniray/minimap/common/jm/JMHandler.java +++ b/common/src/main/java/com/funniray/minimap/common/jm/JMHandler.java @@ -16,28 +16,55 @@ import com.google.gson.Gson; import net.kyori.adventure.text.minimessage.MiniMessage; -import java.awt.print.Pageable; -import java.util.*; +import java.util.Optional; public class JMHandler implements MessageHandler { private final JavaMinimapPlugin plugin; - // JM for 1.21 and above no longer has a leading 0 byte at the start of packets. - // We need to note who is on a modern version and not send them (or parse) leading 0 bytes - private final Map modernList = new HashMap<>(); + private static final byte PACKET_MARKER = 42; + private static final String VERSION_CHANNEL = "journeymap:version"; + private static final String PERMISSIONS_CHANNEL = "journeymap:perm_req"; + private static final String ADMIN_REQUEST_CHANNEL = "journeymap:admin_req"; + private static final String ADMIN_SAVE_CHANNEL = "journeymap:admin_save"; + private static final String TELEPORT_CHANNEL = "journeymap:teleport_req"; + private static final String MULTIPLAYER_OPTIONS_CHANNEL = "journeymap:mp_options_req"; public JMHandler(JavaMinimapPlugin plugin) { this.plugin = plugin; } + public void sendInitialState(MinimapPlayer player) { + sendHandshake(player); + sendPermissions(player); + } + + public void sendHandshake(MinimapPlayer player) { + String payload = new Gson().toJson(new JMVersion()); + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + NetworkUtils.writeUtf(payload, out); + player.sendPluginMessage(out.toByteArray(), VERSION_CHANNEL); + } + private Optional getWorldFromKeyedName(String keyedWorld) { return JavaMinimapPlugin.getInstance().getServer().getWorlds().stream().filter(w->w.getKeyedName().equals(keyedWorld)).findFirst(); } - public void handleMPOptions(MinimapPlayer player, byte[] message, String replyChannel, int replyByte) { - player.sendMessage(MiniMessage.miniMessage().deserialize("Multiplayer options are not implemented.")); + public void sendPermissions(MinimapPlayer player) { + handlePerm(player, PERMISSIONS_CHANNEL); } - public void handleTeleport(MinimapPlayer player, byte[] message, String replyChannel, int replyByte) { + public void handleMPOptions(MinimapPlayer player, byte[] message) { + if (message.length > 0) { + player.sendMessage(MiniMessage.miniMessage().deserialize("Saving JourneyMap multiplayer options is not implemented.")); + return; + } + + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeByte(PACKET_MARKER); + NetworkUtils.writeUtf(new Gson().toJson(new MultiplayerOptionsConfig()), out); + player.sendPluginMessage(out.toByteArray(), MULTIPLAYER_OPTIONS_CHANNEL); + } + + public void handleTeleport(MinimapPlayer player, byte[] message) { if (!player.hasPermission("minimap.jm.teleport")) { player.sendMessage(MiniMessage.miniMessage().deserialize("You don't have permission to teleport.")); return; @@ -49,7 +76,6 @@ public void handleTeleport(MinimapPlayer player, byte[] message, String replyCha } ByteArrayDataInput in = ByteStreams.newDataInput(message); - if (!modern(player)) in.readByte(); double x = in.readDouble(); double y = in.readDouble(); double z = in.readDouble(); @@ -65,49 +91,24 @@ public void handleTeleport(MinimapPlayer player, byte[] message, String replyCha player.teleport(world.get().getLocation(x,y,z)); } - public void handleAdminReq(MinimapPlayer player, byte[] message, String replyChannel, int replyByte) { - ByteArrayDataInput in = ByteStreams.newDataInput(message); - if (!modern(player)) in.readByte(); - in.readByte(); - ServerPropType type = ServerPropType.getFromType(in.readInt()); - Gson gson = new Gson(); - if (type != ServerPropType.GLOBAL) { + public void handleAdminReq(MinimapPlayer player, byte[] message) { + if (!canViewServerProperties(player)) { + player.sendMessage(MiniMessage.miniMessage().deserialize("You don't have permission to view JourneyMap server options.")); return; } - HashMap payloads = new HashMap<>(); - - payloads.put("GLOBAL", gson.toJson(plugin.getConfig().globalJourneymapConfig)); - payloads.put("DEFAULT", gson.toJson(plugin.getConfig().defaultWorldConfig)); - - plugin.getServer().getWorlds().stream() - .map(MinimapWorld::getName) - .forEach(s->payloads.put(s,gson.toJson(plugin.getConfig().getWorldConfig(s)))); - - for (Map.Entry ent : payloads.entrySet()) { - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - if (!modern(player)) out.writeByte(replyByte); - out.writeByte(42); - if (ent.getKey().equals("GLOBAL")) { - out.writeInt(ServerPropType.GLOBAL.getId()); - NetworkUtils.writeUtf("", out); - } else if (ent.getKey().equals("DEFAULT")) { - out.writeInt(ServerPropType.DEFAULT.getId()); - NetworkUtils.writeUtf("", out); - } else { - out.writeInt(ServerPropType.DIMENSION.getId()); - NetworkUtils.writeUtf(ent.getKey(), out); - } - NetworkUtils.writeUtf(ent.getValue(), out); - player.sendPluginMessage(out.toByteArray(), replyChannel); - } + ByteArrayDataInput in = ByteStreams.newDataInput(message); + in.readByte(); + ServerPropType type = ServerPropType.getFromType(in.readInt()); + String dimension = NetworkUtils.readUtf(in); + NetworkUtils.readUtf(in); + sendAdminData(player, type, dimension); } - public void handleAdminSave(MinimapPlayer player, byte[] message, String replyChannel, int replyByte) { + public void handleAdminSave(MinimapPlayer player, byte[] message) { if (!player.hasPermission("minimap.jm.admin")) return; ByteArrayDataInput in = ByteStreams.newDataInput(message); - if (!modern(player)) in.readByte(); in.readByte(); int type = in.readInt(); String dimension = NetworkUtils.readUtf(in); @@ -120,35 +121,41 @@ public void handleAdminSave(MinimapPlayer player, byte[] message, String replyCh } else if (type == 2 || type == 3) { JMWorldConfig newConfig = gson.fromJson(payload, JMWorldConfig.class); if (type == 3) { - MinimapConfig.WorldConfig worldConfig = plugin.getConfig().getWorldConfig(dimension); + MinimapConfig.WorldConfig worldConfig = getWorldConfig(dimension); worldConfig.journeymapConfig = newConfig; - System.out.println(dimension); } else { plugin.getConfig().defaultWorldConfig = newConfig; } } plugin.saveConfig(); + plugin.getServer().forEachPlayer(this::sendPermissions); + } - // Probably not correct, as a 1.16 admin could send an invalid packet - // to a newer client that's also online - String permChannel = "journeymap:perm_req"; - int replyInt = 0; - if (replyChannel.equals("journeymap:common")) { - permChannel = "journeymap:common"; - replyInt = 2; + private void sendAdminData(MinimapPlayer player, ServerPropType type, String dimension) { + Gson gson = new Gson(); + String payload; + if (type == ServerPropType.GLOBAL) { + payload = gson.toJson(plugin.getConfig().globalJourneymapConfig); + dimension = ""; + } else if (type == ServerPropType.DEFAULT) { + payload = gson.toJson(plugin.getConfig().defaultWorldConfig); + dimension = ""; + } else { + payload = gson.toJson(getWorldConfig(dimension).journeymapConfig); } - String finalPermChannel = permChannel; - int finalReplyInt = replyInt; - plugin.getServer().getPlayers().forEach(p->handlePerm(p, new byte[0], finalPermChannel, finalReplyInt)); + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeByte(PACKET_MARKER); + out.writeInt(type.getId()); + NetworkUtils.writeUtf(dimension, out); + NetworkUtils.writeUtf(payload, out); + player.sendPluginMessage(out.toByteArray(), ADMIN_REQUEST_CHANNEL); } - public void handleVersion(MinimapPlayer player, byte[] message, String replyChannel) { - modernList.put(player.getUniqueId(), message.length > 0 && message[0] != 0); + public void handleVersion(MinimapPlayer player, byte[] message) { Gson gson = new Gson(); JMVersion serverVersion = new JMVersion(); ByteArrayDataInput in = ByteStreams.newDataInput(message); - if (!modern(player)) in.readByte(); String sent = NetworkUtils.readUtf(in); JMVersion clientVersion = gson.fromJson(sent, JMVersion.class); @@ -158,9 +165,9 @@ public void handleVersion(MinimapPlayer player, byte[] message, String replyChan String payload = gson.toJson(serverVersion); ByteArrayDataOutput out = ByteStreams.newDataOutput(); - if (!modern(player)) out.writeByte(0); NetworkUtils.writeUtf(payload, out); - player.sendPluginMessage(out.toByteArray(), replyChannel); + player.sendPluginMessage(out.toByteArray(), VERSION_CHANNEL); + sendPermissions(player); } public JMConfig getEffectiveConfig(MinimapPlayer player) { @@ -172,70 +179,67 @@ public JMConfig getEffectiveConfig(MinimapPlayer player) { return config; } - public void handlePerm(MinimapPlayer player, byte[] message, String replyChannel, int replyByte) { - modernList.putIfAbsent(player.getUniqueId(), message.length > 0 && message[0] == 42); - JMConfig config = getEffectiveConfig(player); + public void handlePerm(MinimapPlayer player, String replyChannel) { + JMConfig config = getEffectiveConfig(player).normalizeForClient(); Gson gson = new Gson(); String payload = gson.toJson(config); ByteArrayDataOutput out = ByteStreams.newDataOutput(); - if (!modern(player)) out.writeByte(replyByte); - out.writeByte(42); + out.writeByte(PACKET_MARKER); out.writeBoolean(player.hasPermission("minimap.jm.admin")); NetworkUtils.writeUtf(payload, out); out.writeBoolean(true); player.sendPluginMessage(out.toByteArray(), replyChannel); } - private boolean modern(MinimapPlayer player) { - Boolean modern = modernList.get(player.getUniqueId()); - if (modern == null) return false; - return modern; + private MinimapConfig.WorldConfig getWorldConfig(String dimension) { + String worldName = getWorldFromKeyedName(dimension) + .map(MinimapWorld::getName) + .orElse(dimension); + return plugin.getConfig().getWorldConfig(worldName); + } + + private boolean canViewServerProperties(MinimapPlayer player) { + return player.hasPermission("minimap.jm.admin") + || Boolean.parseBoolean(plugin.getConfig().globalJourneymapConfig.viewOnlyServerProperties); } public void playerLeft(MinimapPlayer player) { - modernList.remove(player.getUniqueId()); } @Override public void onPluginMessage(String channel, MinimapPlayer player, byte[] message) { switch (channel.split(":")[1]) { case "version": - handleVersion(player, message, channel); + handleVersion(player, message); break; case "perm_req": - handlePerm(player, message, channel, 0); + handlePerm(player, channel); break; case "admin_req": - handleAdminReq(player, message, channel, 0); + handleAdminReq(player, message); break; case "admin_save": - handleAdminSave(player, message, channel, 0); + handleAdminSave(player, message); break; case "teleport_req": - handleTeleport(player, message, channel, 0); + handleTeleport(player, message); + break; + case "mp_options_req": + handleMPOptions(player, message); break; case "common": - ByteArrayDataInput in = ByteStreams.newDataInput(message); - byte type = in.readByte(); - switch (type) { - case 0: - handleAdminReq(player, message, channel, type); - break; - case 1: - handleAdminSave(player, message, channel, type); - break; - case 2: - handlePerm(player, message, channel, type); - break; - case 4: - handleTeleport(player, message, channel, type); - break; - case 5: - handleMPOptions(player, message, channel, type); - break; - } break; } } + + private static class MultiplayerOptionsConfig { + public String loadedChunksEntity = "false"; + public String loadedChunksBlock = "false"; + public String loadedChunksFull = "false"; + public String loadedChunksInaccessible = "false"; + public String visible = "true"; + public String hideSelfUnderground = "false"; + public String configVersion = new JMVersion().journeymap_version.full; + } } diff --git a/common/src/main/java/com/funniray/minimap/common/jm/data/JMConfig.java b/common/src/main/java/com/funniray/minimap/common/jm/data/JMConfig.java index 3bccc8a..67913cf 100644 --- a/common/src/main/java/com/funniray/minimap/common/jm/data/JMConfig.java +++ b/common/src/main/java/com/funniray/minimap/common/jm/data/JMConfig.java @@ -1,10 +1,15 @@ package com.funniray.minimap.common.jm.data; import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; @ConfigSerializable public class JMConfig { public String journeymapEnabled = "true"; + @Comment("JourneyMap 6 client permission: enables the in-game minimap when JourneyMap itself is allowed.") + public String minimapEnabled = "true"; + @Comment("JourneyMap 6 client permission: hides JourneyMap coordinate display where supported.") + public String hideCoordinates = "false"; public String useWorldId = "true"; public String viewOnlyServerProperties = "true"; public String allowMultiplayerSettings = "ALL"; @@ -45,6 +50,8 @@ public JMConfig copy() { JMConfig clone = new JMConfig(); clone.journeymapEnabled = this.journeymapEnabled; + clone.minimapEnabled = this.minimapEnabled; + clone.hideCoordinates = this.hideCoordinates; clone.useWorldId = this.useWorldId; clone.viewOnlyServerProperties = this.viewOnlyServerProperties; clone.allowMultiplayerSettings = this.allowMultiplayerSettings; @@ -79,7 +86,35 @@ public JMConfig copy() { clone.villagerRadarEnabled = this.villagerRadarEnabled; clone.animalRadarEnabled = this.animalRadarEnabled; clone.mobRadarEnabled = this.mobRadarEnabled; + clone.configVersion = this.configVersion; return clone; } + + public JMConfig normalizeForClient() { + JMConfig normalized = this.copy(); + if (Boolean.parseBoolean(normalized.journeymapEnabled)) { + return normalized; + } + + normalized.minimapEnabled = "false"; + normalized.allowMultiplayerSettings = "NONE"; + normalized.worldPlayerRadar = "NONE"; + normalized.allowDeathPoints = "false"; + normalized.showInGameBeacons = "false"; + normalized.allowWaypoints = "false"; + normalized.allowRightClickTeleport = "false"; + normalized.teleportEnabled = "false"; + normalized.surfaceMapping = "NONE"; + normalized.topoMapping = "NONE"; + normalized.biomeMapping = "NONE"; + normalized.caveMapping = "NONE"; + normalized.radarEnabled = "NONE"; + normalized.playerRadarEnabled = "false"; + normalized.playerRadarNamesEnabled = "false"; + normalized.villagerRadarEnabled = "false"; + normalized.animalRadarEnabled = "false"; + normalized.mobRadarEnabled = "false"; + return normalized; + } } diff --git a/common/src/main/java/com/funniray/minimap/common/jm/data/JMVersion.java b/common/src/main/java/com/funniray/minimap/common/jm/data/JMVersion.java index afead47..54a0324 100644 --- a/common/src/main/java/com/funniray/minimap/common/jm/data/JMVersion.java +++ b/common/src/main/java/com/funniray/minimap/common/jm/data/JMVersion.java @@ -16,7 +16,7 @@ public static class VersionDetails { public final String patch; public VersionDetails() { - this(6,1,0,"-beta99"); + this(6,0,0,"-beta.67"); } public VersionDetails(int major, int minor, int micro, String patch) { @@ -24,7 +24,7 @@ public VersionDetails(int major, int minor, int micro, String patch) { this.minor = minor; this.micro = micro; this.patch = patch; - this.full = String.format("%d.%d.%d%s",major,minor,micro,patch); + this.full = String.format("%d.%d.%d%s", major, minor, micro, patch == null ? "" : patch); } } } diff --git a/common/src/main/java/com/funniray/minimap/common/network/NetworkUtils.java b/common/src/main/java/com/funniray/minimap/common/network/NetworkUtils.java index e564e7c..19c3e0b 100644 --- a/common/src/main/java/com/funniray/minimap/common/network/NetworkUtils.java +++ b/common/src/main/java/com/funniray/minimap/common/network/NetworkUtils.java @@ -57,7 +57,7 @@ public static String readUtf(ByteArrayDataInput in) { byte[] out = new byte[j]; in.readFully(out, 0, j); - String s = new String(out); + String s = new String(out, StandardCharsets.UTF_8); if (s.length() > MAX_STRING_LENGTH) { throw new RuntimeException("The received string length is longer than maximum allowed (" + j + " > " + MAX_STRING_LENGTH + ")"); diff --git a/common/src/main/java/com/funniray/minimap/common/voxel/VoxelHandler.java b/common/src/main/java/com/funniray/minimap/common/voxel/VoxelHandler.java index 7256741..af19ad7 100644 --- a/common/src/main/java/com/funniray/minimap/common/voxel/VoxelHandler.java +++ b/common/src/main/java/com/funniray/minimap/common/voxel/VoxelHandler.java @@ -13,7 +13,7 @@ public class VoxelHandler { private final JavaMinimapPlugin plugin; - public String VOXEL_SETTINGS_CHANNEL = "voxelmap:settings"; + public static final String VOXEL_SETTINGS_CHANNEL = "voxelmap:settings"; public VoxelHandler(JavaMinimapPlugin plugin) { this.plugin = plugin; @@ -29,9 +29,8 @@ public void sendSettings(MinimapPlayer player) { String configJson = new Gson().toJson(config.applyOverrides(player)); ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeByte(42); + out.writeByte(0); NetworkUtils.writeUtf(configJson, out); player.sendPluginMessage(out.toByteArray(), VOXEL_SETTINGS_CHANNEL); } - } diff --git a/common/src/main/java/com/funniray/minimap/common/voxel/data/VoxelConfig.java b/common/src/main/java/com/funniray/minimap/common/voxel/data/VoxelConfig.java index 780b365..326238e 100644 --- a/common/src/main/java/com/funniray/minimap/common/voxel/data/VoxelConfig.java +++ b/common/src/main/java/com/funniray/minimap/common/voxel/data/VoxelConfig.java @@ -12,10 +12,10 @@ public class VoxelConfig { public String teleportCommand = "tp %p %x %y %z"; private boolean getOverride(String nodeSuffix, boolean def, MinimapPlayer player) { - if (player.hasPermission("minimap.override."+nodeSuffix+".enabled")) { - return true; - } else if (player.hasPermission("minimap.override."+nodeSuffix+".disabled")) { + if (player.hasExplicitPermission("minimap.override."+nodeSuffix+".disabled")) { return false; + } else if (player.hasExplicitPermission("minimap.override."+nodeSuffix+".enabled")) { + return true; } return def; @@ -27,6 +27,7 @@ public VoxelConfig applyOverrides(MinimapPlayer player) { newConfig.radarMobsAllowed = getOverride("radar-mobs", this.radarMobsAllowed, player); newConfig.radarPlayersAllowed = getOverride("radar-players", this.radarPlayersAllowed, player); newConfig.cavesAllowed = getOverride("cave-mode", this.cavesAllowed, player); + newConfig.teleportCommand = this.teleportCommand; return newConfig; } diff --git a/common/src/main/java/com/funniray/minimap/common/xaeros/XaerosConfig.java b/common/src/main/java/com/funniray/minimap/common/xaeros/XaerosConfig.java index 7d434b8..416656f 100644 --- a/common/src/main/java/com/funniray/minimap/common/xaeros/XaerosConfig.java +++ b/common/src/main/java/com/funniray/minimap/common/xaeros/XaerosConfig.java @@ -10,10 +10,10 @@ public class XaerosConfig { public boolean radar = true; private boolean getOverride(String nodeSuffix, boolean def, MinimapPlayer player) { - if (player.hasPermission("minimap.override."+nodeSuffix+".enabled")) { - return true; - } else if (player.hasPermission("minimap.override."+nodeSuffix+".disabled")) { + if (player.hasExplicitPermission("minimap.override."+nodeSuffix+".disabled")) { return false; + } else if (player.hasExplicitPermission("minimap.override."+nodeSuffix+".enabled")) { + return true; } return def; diff --git a/common/src/main/java/com/funniray/minimap/common/xaeros/XaerosHandler.java b/common/src/main/java/com/funniray/minimap/common/xaeros/XaerosHandler.java index 2e1ad58..e528c71 100644 --- a/common/src/main/java/com/funniray/minimap/common/xaeros/XaerosHandler.java +++ b/common/src/main/java/com/funniray/minimap/common/xaeros/XaerosHandler.java @@ -3,44 +3,101 @@ import com.funniray.minimap.common.JavaMinimapPlugin; import com.funniray.minimap.common.api.MessageHandler; import com.funniray.minimap.common.api.MinimapPlayer; -import com.funniray.minimap.common.version.Version; import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; +import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.BinaryTagIO; +import net.kyori.adventure.nbt.BinaryTagTypes; import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; import net.kyori.adventure.text.minimessage.MiniMessage; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class XaerosHandler implements MessageHandler { private final JavaMinimapPlugin plugin; - public static String XAEROS_CHANNEL = "xaerominimap:main"; - public static String XAEROS_MAP_CHANNEL = "xaeroworldmap:main"; + public static final String XAEROS_CHANNEL = "xaerominimap:main"; + public static final String XAEROS_MAP_CHANNEL = "xaeroworldmap:main"; + public static final String XAEROLIB_CHANNEL = "xaerolib:main"; + private static final int XAEROS_NETWORK_VERSION = 3; + private static final Set XAERO_CONFIG_CHANNELS = Set.of(XAEROS_CHANNEL, XAEROS_MAP_CHANNEL); + private final Map> detectedConfigChannels = new ConcurrentHashMap<>(); public XaerosHandler(JavaMinimapPlugin plugin) { this.plugin = plugin; } + public void sendXaerosJoinPackets(MinimapPlayer player) { + List messages = new ArrayList<>(); + addXaerosHandshakeMessages(messages); + addXaerosConfigMessages(messages, getEffectiveConfig(player), getDetectedConfigChannels(player)); + player.sendPluginMessages(messages); + } + + public void sendXaerosWorldChangePackets(MinimapPlayer player) { + List messages = new ArrayList<>(); + messages.add(pluginMessage(XAEROLIB_CHANNEL, oneByteXaeroLibPacket(1))); + addXaerosConfigMessages(messages, getEffectiveConfig(player), getDetectedConfigChannels(player)); + player.sendPluginMessages(messages); + } + public void sendXaerosHandshake(MinimapPlayer player) { + List messages = new ArrayList<>(); + addXaerosHandshakeMessages(messages); + player.sendPluginMessages(messages); + } + + private void addXaerosHandshakeMessages(List messages) { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeByte(1); - out.writeInt(2); - player.sendPluginMessage(out.toByteArray(), XAEROS_CHANNEL); - player.sendPluginMessage(out.toByteArray(), XAEROS_MAP_CHANNEL); + out.writeInt(XAEROS_NETWORK_VERSION); + byte[] legacyHandshake = out.toByteArray(); + messages.add(pluginMessage(XAEROS_CHANNEL, legacyHandshake)); + messages.add(pluginMessage(XAEROS_MAP_CHANNEL, legacyHandshake)); + messages.add(pluginMessage(XAEROLIB_CHANNEL, oneByteXaeroLibPacket(0))); + messages.add(pluginMessage(XAEROLIB_CHANNEL, oneByteXaeroLibPacket(1))); + } + + public void sendXaeroLibDimensionHandshake(MinimapPlayer player) { + player.sendPluginMessages(List.of(pluginMessage(XAEROLIB_CHANNEL, oneByteXaeroLibPacket(1)))); } public void sendXaerosConfig(MinimapPlayer player) { + List messages = new ArrayList<>(); + addXaerosConfigMessages(messages, getEffectiveConfig(player), getDetectedConfigChannels(player)); + player.sendPluginMessages(messages); + } + + public void playerLeft(MinimapPlayer player) { + detectedConfigChannels.remove(player.getUniqueId()); + } + + private XaerosConfig getEffectiveConfig(MinimapPlayer player) { XaerosWorldConfig worldConfig = plugin.getConfig().getWorldConfig(player.getLocation().getWorld().getName()).xaerosConfig; XaerosConfig config = plugin.getConfig().globalXaerosConfig; if (worldConfig != null && worldConfig.enabled) { config = worldConfig; } - config = config.applyOverrides(player); + return config.applyOverrides(player); + } + + private void addXaerosConfigMessages(List messages, XaerosConfig config, Set configChannels) { + addLegacyRulesConfig(messages, config); + addXaeroLibConfig(messages, config, configChannels); + } + private void addLegacyRulesConfig(List messages, XaerosConfig config) { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeByte(4); CompoundBinaryTag tag = CompoundBinaryTag.builder() @@ -49,21 +106,130 @@ public void sendXaerosConfig(MinimapPlayer player) { .putBoolean("r", config.radar) .build(); try { - if (player.getVersion().greaterThanEqual(new Version(1,20,3))) { - BinaryTagIO.writer().writeNameless(tag, out); - } else { - BinaryTagIO.writer().write(tag, out); - } - byte[] arr = out.toByteArray(); - player.sendPluginMessage(arr, XAEROS_CHANNEL); - player.sendPluginMessage(arr, XAEROS_MAP_CHANNEL); + writeCurrentNbt(tag, out); + messages.add(pluginMessage(XAEROS_CHANNEL, out.toByteArray())); + + out = ByteStreams.newDataOutput(); + out.writeByte(4); + tag = CompoundBinaryTag.builder() + .putBoolean("cm", config.caveMode) + .putBoolean("ncm", config.netherCaveMode) + .build(); + writeCurrentNbt(tag, out); + messages.add(pluginMessage(XAEROS_MAP_CHANNEL, out.toByteArray())); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void sendXaeroLibServerHandshake(MinimapPlayer player) { + player.sendPluginMessages(List.of(pluginMessage(XAEROLIB_CHANNEL, oneByteXaeroLibPacket(0)))); + } + + private byte[] oneByteXaeroLibPacket(int packetIndex) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeByte(packetIndex); + out.writeByte(1); + return out.toByteArray(); + } + + private void addXaeroLibConfig(List messages, XaerosConfig config, Set configChannels) { + if (configChannels.contains(XAEROS_CHANNEL)) { + messages.add(pluginMessage(XAEROLIB_CHANNEL, configChannelPacket(XAEROS_CHANNEL))); + messages.add(pluginMessage(XAEROLIB_CHANNEL, enforcedConfigPacket(minimapOptions(config)))); + } + if (configChannels.contains(XAEROS_MAP_CHANNEL)) { + messages.add(pluginMessage(XAEROLIB_CHANNEL, configChannelPacket(XAEROS_MAP_CHANNEL))); + messages.add(pluginMessage(XAEROLIB_CHANNEL, enforcedConfigPacket(worldMapOptions(config)))); + } + } + + private Set getDetectedConfigChannels(MinimapPlayer player) { + return detectedConfigChannels.getOrDefault(player.getUniqueId(), Set.of()); + } + + private Set markConfigChannelDetected(MinimapPlayer player, String channel) { + if (!XAERO_CONFIG_CHANNELS.contains(channel)) { + return Set.of(); + } + + Set channels = detectedConfigChannels.computeIfAbsent(player.getUniqueId(), uuid -> ConcurrentHashMap.newKeySet()); + channels.add(channel); + return Set.of(channel); + } + + private MinimapPlayer.PluginMessage pluginMessage(String channel, byte[] payload) { + return new MinimapPlayer.PluginMessage(channel, payload); + } + + private byte[] configChannelPacket(String configChannelId) { + CompoundBinaryTag tag = CompoundBinaryTag.builder() + .putString("c", configChannelId) + .build(); + return nbtXaeroLibPacket(2, tag); + } + + private byte[] enforcedConfigPacket(CompoundBinaryTag options) { + CompoundBinaryTag tag = CompoundBinaryTag.builder() + .putBoolean("r", false) + .put("o", options) + .build(); + return nbtXaeroLibPacket(3, tag); + } + + private byte[] nbtXaeroLibPacket(int packetIndex, CompoundBinaryTag tag) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeByte(packetIndex); + try { + writeCurrentNbt(tag, out); } catch (IOException e) { throw new RuntimeException(e); } + return out.toByteArray(); + } + + private CompoundBinaryTag minimapOptions(XaerosConfig config) { + return CompoundBinaryTag.builder() + .putBoolean("minimap_cave_mode_allowed", config.caveMode || config.netherCaveMode) + .put("minimap_cave_mode_allowed_dimensions", caveModeDimensions(config)) + .putBoolean("display_radar", config.radar) + .build(); + } + + private CompoundBinaryTag worldMapOptions(XaerosConfig config) { + return CompoundBinaryTag.builder() + .putBoolean("cave_mode_allowed", config.caveMode || config.netherCaveMode) + .put("cave_mode_allowed_dimensions", caveModeDimensions(config)) + .putBoolean("display_minimap_radar", config.radar) + .build(); + } + + private ListBinaryTag caveModeDimensions(XaerosConfig config) { + if (config.caveMode && config.netherCaveMode) { + return ListBinaryTag.empty(); + } + + List dimensions = new ArrayList<>(); + if (config.caveMode) { + dimensions.add(StringBinaryTag.stringBinaryTag("minecraft:overworld")); + dimensions.add(StringBinaryTag.stringBinaryTag("minecraft:the_end")); + } + if (config.netherCaveMode) { + dimensions.add(StringBinaryTag.stringBinaryTag("minecraft:the_nether")); + } + return ListBinaryTag.listBinaryTag(BinaryTagTypes.STRING, dimensions); + } + + private void writeCurrentNbt(CompoundBinaryTag tag, ByteArrayDataOutput out) throws IOException { + BinaryTagIO.writer().writeNameless(tag, out); } @Override public void onPluginMessage(String channel, MinimapPlayer player, byte[] message) { + if (channel.equals(XAEROLIB_CHANNEL)) { + return; + } + ByteArrayDataInput in = ByteStreams.newDataInput(message); if (in.readByte() == 1) { int version = in.readInt(); @@ -72,7 +238,10 @@ public void onPluginMessage(String channel, MinimapPlayer player, byte[] message return; } - sendXaerosConfig(player); + Set configChannels = markConfigChannelDetected(player, channel); + List messages = new ArrayList<>(); + addXaerosConfigMessages(messages, getEffectiveConfig(player), configChannels); + player.sendPluginMessages(messages); } } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927..a4b76b9 100755 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fc..c61a118 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..f3b75f3 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +133,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +200,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +216,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd3..9d21a21 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle.kts b/settings.gradle.kts index 29adc34..cfabda9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,4 @@ rootProject.name = "minimap" include("common") -include("sponge") +//include("sponge") include("spigot") diff --git a/spigot/build.gradle.kts b/spigot/build.gradle.kts index c3366fa..2299a0e 100644 --- a/spigot/build.gradle.kts +++ b/spigot/build.gradle.kts @@ -1,10 +1,10 @@ plugins { `java-library` - id("com.github.johnrengelman.shadow") version "7.1.2" - id("net.minecrell.plugin-yml.bukkit") version "0.5.2" + id("com.gradleup.shadow") version "9.3.2" } val versionStr = (System.getenv("VERSION")?: "v1.0.0").removePrefix("v") +val pluginVersion = versionStr group = "com.funniray.minimap" version = versionStr @@ -12,7 +12,7 @@ version = versionStr repositories { mavenCentral() maven { - url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + url = uri("https://repo.papermc.io/repository/maven-public/") } maven { url = uri("https://oss.sonatype.org/content/groups/public/") @@ -24,29 +24,37 @@ repositories { dependencies { // Main Dependencies - compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT") - compileOnly("com.viaversion:viaversion-api:4.9.2") + compileOnly("io.papermc.paper:paper-api:26.1.2.build.19-alpha") + compileOnly("com.viaversion:viaversion-api:5.0.1") implementation(project(":common")) // Common Dependencies - implementation("org.spongepowered:configurate-core:4.1.2") - implementation("org.spongepowered:configurate-yaml:4.1.2") - implementation("net.kyori:adventure-api:4.10.0") - implementation("net.kyori:adventure-platform-bukkit:4.3.1") - implementation("net.kyori:adventure-text-minimessage:4.10.0") - implementation("net.kyori:adventure-nbt:4.15.0") + implementation("org.spongepowered:configurate-core:4.2.0") + implementation("org.spongepowered:configurate-yaml:4.2.0") + implementation("net.kyori:adventure-api:4.22.0") + implementation("net.kyori:adventure-platform-bukkit:4.4.0") + implementation("net.kyori:adventure-text-minimessage:4.22.0") + implementation("net.kyori:adventure-nbt:4.22.0") } -val javaTarget = 11 +val javaTarget = 25 java { - sourceCompatibility = JavaVersion.toVersion(javaTarget) - targetCompatibility = JavaVersion.toVersion(javaTarget) - if (JavaVersion.current() < JavaVersion.toVersion(javaTarget)) { - toolchain.languageVersion.set(JavaLanguageVersion.of(javaTarget)) + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaTarget)) + vendor.set(JvmVendorSpec.matching("")) } } tasks { + withType().configureEach { + options.release.set(javaTarget) + } + processResources { + inputs.property("version", pluginVersion) + filesMatching("plugin.yml") { + expand("version" to pluginVersion) + } + } build { dependsOn(shadowJar) } @@ -57,23 +65,8 @@ tasks { exclude("com/google/gson/**") exclude("org/apache/commons/**") exclude("org/yaml/snakeyaml/**") - archiveBaseName.set("${rootProject.name}-spigot") + archiveBaseName.set("minimap-control-folia") archiveClassifier.set("") - doLast { - copy { - from(archiveFile) - into("${rootProject.projectDir}/build") - } - } + destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs")) } } - -bukkit { - name = "MinimapControl" - main = "com.funniray.minimap.spigot.SpigotMinimap" - authors = listOf("funniray") - description = "Control minimap settings from server-side software" - - apiVersion = "1.13" - softDepend = listOf("viaversion") -} diff --git a/spigot/src/main/java/com/funniray/minimap/spigot/SpigotMain.java b/spigot/src/main/java/com/funniray/minimap/spigot/SpigotMain.java index 3f5f08c..5bbc15a 100644 --- a/spigot/src/main/java/com/funniray/minimap/spigot/SpigotMain.java +++ b/spigot/src/main/java/com/funniray/minimap/spigot/SpigotMain.java @@ -5,7 +5,7 @@ import com.funniray.minimap.spigot.impl.SpigotPlayer; import com.funniray.minimap.spigot.impl.SpigotServer; import com.funniray.minimap.spigot.impl.SpigotWorld; -import org.bukkit.Bukkit; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -19,6 +19,9 @@ import org.spongepowered.configurate.yaml.YamlConfigurationLoader; import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; public class SpigotMain extends JavaMinimapPlugin implements PluginMessageListener, Listener { public SpigotMinimap plugin; @@ -50,24 +53,51 @@ public ConfigurationLoader getConfigLoader() { } @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - this.onPluginMessage(channel, new SpigotPlayer(player), message); + public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) { + player.getScheduler().run(plugin, task -> { + if (player.isOnline()) { + this.onPluginMessage(channel, new SpigotPlayer(player), message); + } + }, null); } + private final Map playerTaskMap = new HashMap<>(); + @EventHandler public void onJoin(PlayerJoinEvent event) { // The player join event is slightly too early. I unfortunately don't know an event that fires late enough for Xaeros to recognize the packet // If anyone knows, please let me know - plugin.getServer().getScheduler().runTaskLater(plugin, ()->this.handlePlayerJoined(new SpigotPlayer(event.getPlayer())), 40L); + Player player = event.getPlayer(); + player.getScheduler().runDelayed(plugin, task -> { + if (player.isOnline()) { + this.handlePlayerJoined(new SpigotPlayer(player)); + } + }, null, 40L); + ScheduledTask task = player.getScheduler().runAtFixedRate(this.plugin, scheduledTask -> { + if (player.isOnline()) { + this.handlePlayerJoinedRepeat(new SpigotPlayer(player)); + } + }, null, 40L, 40L); + this.playerTaskMap.put(player.getUniqueId(), task); } @EventHandler public void onLeft(PlayerQuitEvent event) { + ScheduledTask task = this.playerTaskMap.get(event.getPlayer().getUniqueId()); + if (task != null) { + task.cancel(); + this.playerTaskMap.remove(event.getPlayer().getUniqueId()); + } this.handlePlayerLeft(new SpigotPlayer(event.getPlayer())); } @EventHandler public void onWorldChange(PlayerChangedWorldEvent event) { - this.handleSwitchWorld(new SpigotWorld(event.getPlayer().getWorld()), new SpigotPlayer(event.getPlayer())); + Player player = event.getPlayer(); + player.getScheduler().run(plugin, task -> { + if (player.isOnline()) { + this.handleSwitchWorld(new SpigotWorld(player.getWorld()), new SpigotPlayer(player)); + } + }, null); } } diff --git a/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotLocation.java b/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotLocation.java index 8e6e8c8..11e752c 100644 --- a/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotLocation.java +++ b/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotLocation.java @@ -5,7 +5,7 @@ import org.bukkit.Location; public class SpigotLocation implements MinimapLocation { - private Location nativeLocation; + private final Location nativeLocation; public SpigotLocation(Location nativeLocation) { this.nativeLocation = nativeLocation; diff --git a/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotPlayer.java b/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotPlayer.java index 42bdf42..e5b93f2 100644 --- a/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotPlayer.java +++ b/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotPlayer.java @@ -4,12 +4,10 @@ import com.funniray.minimap.common.api.MinimapPlayer; import com.funniray.minimap.common.version.Version; import com.funniray.minimap.spigot.SpigotMinimap; -import net.kyori.adventure.platform.bukkit.MinecraftComponentSerializer; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import java.util.List; import java.util.UUID; public class SpigotPlayer implements MinimapPlayer { @@ -21,17 +19,28 @@ public SpigotPlayer(Player player) { @Override public void sendPluginMessage(byte[] message, String channel) { - Bukkit.getScheduler().runTask(SpigotMinimap.getInstance(), ()->nativePlayer.sendPluginMessage(SpigotMinimap.getInstance(), channel, message)); + sendPluginMessages(List.of(new PluginMessage(channel, message))); + } + + @Override + public void sendPluginMessages(List messages) { + nativePlayer.getScheduler().run(SpigotMinimap.getInstance(), task -> { + if (nativePlayer.isOnline()) { + for (PluginMessage message : messages) { + nativePlayer.sendPluginMessage(SpigotMinimap.getInstance(), message.channel(), message.payload()); + } + } + }, null); } @Override public void sendMessage(Component message) { - SpigotMinimap.getInstance().adventure().player(nativePlayer).sendMessage(message); + nativePlayer.sendMessage(message); } @Override public void teleport(MinimapLocation location) { - nativePlayer.teleport(((SpigotLocation) location).getNativeLocation()); + nativePlayer.teleportAsync(((SpigotLocation) location).getNativeLocation()); } @Override @@ -41,7 +50,7 @@ public MinimapLocation getLocation() { @Override public void disconnect(Component reason) { - nativePlayer.kickPlayer(LegacyComponentSerializer.legacy('\u00a7').serialize(reason)); + nativePlayer.kick(reason); } @Override @@ -59,6 +68,11 @@ public boolean hasPermission(String string) { return nativePlayer.hasPermission(string); } + @Override + public boolean hasExplicitPermission(String string) { + return nativePlayer.isPermissionSet(string) && nativePlayer.hasPermission(string); + } + @Override public Version getVersion() { SpigotMinimap plugin = SpigotMinimap.getInstance(); diff --git a/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotServer.java b/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotServer.java index b75eb10..9f4bd5a 100644 --- a/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotServer.java +++ b/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotServer.java @@ -4,9 +4,12 @@ import com.funniray.minimap.common.api.MinimapServer; import com.funniray.minimap.common.api.MinimapWorld; import com.funniray.minimap.common.version.Version; +import com.funniray.minimap.spigot.SpigotMinimap; import org.bukkit.Bukkit; +import org.bukkit.entity.Player; import java.util.List; +import java.util.function.Consumer; import java.util.stream.Collectors; import static java.lang.Integer.parseInt; @@ -15,12 +18,13 @@ public class SpigotServer implements MinimapServer { @Override public Version getMinecraftVersion() { String[] ver = Bukkit.getBukkitVersion().split("-")[0].split("\\."); - if (ver.length < 3) { - return new Version(parseInt(ver[0]), parseInt(ver[1]), 0); - } else if (ver.length == 3) { - return new Version(parseInt(ver[0]), parseInt(ver[1]), parseInt(ver[2])); - } else { - throw new RuntimeException("Cannot parse version "+Bukkit.getBukkitVersion()); + try { + int major = parseInt(ver[0]); + int minor = ver.length > 1 ? parseInt(ver[1]) : 0; + int patch = ver.length > 2 ? parseInt(ver[2]) : 0; + return new Version(major, minor, patch); + } catch (NumberFormatException e) { + throw new RuntimeException("Cannot parse version " + Bukkit.getBukkitVersion()); } } @@ -35,10 +39,17 @@ public String getLoaderName() { } @Override - public List getPlayers() { - return Bukkit.getServer().getOnlinePlayers().stream() - .map(SpigotPlayer::new) - .collect(Collectors.toList()); + public void forEachPlayer(Consumer action) { + SpigotMinimap plugin = SpigotMinimap.getInstance(); + Bukkit.getGlobalRegionScheduler().run(plugin, task -> { + for (Player player : Bukkit.getOnlinePlayers()) { + player.getScheduler().run(plugin, playerTask -> { + if (player.isOnline()) { + action.accept(new SpigotPlayer(player)); + } + }, null); + } + }); } @Override diff --git a/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotWorld.java b/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotWorld.java index f9ae698..a920806 100644 --- a/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotWorld.java +++ b/spigot/src/main/java/com/funniray/minimap/spigot/impl/SpigotWorld.java @@ -8,7 +8,7 @@ import java.lang.reflect.Method; public class SpigotWorld implements MinimapWorld { - private World nativeWorld; + private final World nativeWorld; public SpigotWorld(World nativeWorld) { this.nativeWorld = nativeWorld; diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml new file mode 100644 index 0000000..d292662 --- /dev/null +++ b/spigot/src/main/resources/plugin.yml @@ -0,0 +1,48 @@ +name: MinimapControl +main: com.funniray.minimap.spigot.SpigotMinimap +version: "${version}" +authors: + - funniray +description: Control minimap settings from server-side software +api-version: "26.1" +folia-supported: true +softdepend: + - viaversion +channels: + - xaerolib:main + - xaerominimap:main + - xaeroworldmap:main + - journeymap:version + - journeymap:perm_req + - journeymap:admin_req + - journeymap:admin_save + - journeymap:teleport_req + - journeymap:mp_options_req + - journeymap:chunk_overlay + - journeymap:remove_player + - journeymap:common + - journeymap:player_loc + - journeymap:waypoint + - worldinfo:world_id + - voxelmap:settings +permissions: + minimap.override.radar.enabled: + default: false + minimap.override.radar.disabled: + default: false + minimap.override.cave-mode.enabled: + default: false + minimap.override.cave-mode.disabled: + default: false + minimap.override.nether-cave-mode.enabled: + default: false + minimap.override.nether-cave-mode.disabled: + default: false + minimap.override.radar-mobs.enabled: + default: false + minimap.override.radar-mobs.disabled: + default: false + minimap.override.radar-players.enabled: + default: false + minimap.override.radar-players.disabled: + default: false