diff --git a/README.md b/README.md
index a615f8f..1690d42 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Visualizes the crafting process
## Compatibility
-Compatible with Spigot & Paper 1.16.5, 1.17, 1.17.1, 1.18.1 and 1.18.2
+Compatible with Spigot & Paper 1.16.5, 1.17, 1.17.1, 1.18.1, 1.18.2, 1.19, 1.19.1, 1.19.2, 1.19.3 and 1.19.4
## How it works
diff --git a/api/pom.xml b/api/pom.xml
index 088d823..967d5c4 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -5,7 +5,7 @@
parent
dev.cerus.visualcrafting
- 1.0.7
+ 1.0.8
4.0.0
diff --git a/bukkit-16R3/pom.xml b/bukkit-16R3/pom.xml
index 6d539da..4b3fdd2 100644
--- a/bukkit-16R3/pom.xml
+++ b/bukkit-16R3/pom.xml
@@ -5,7 +5,7 @@
parent
dev.cerus.visualcrafting
- 1.0.7
+ 1.0.8
4.0.0
diff --git a/bukkit-17R1/pom.xml b/bukkit-17R1/pom.xml
index cd55ba7..e016e74 100644
--- a/bukkit-17R1/pom.xml
+++ b/bukkit-17R1/pom.xml
@@ -5,7 +5,7 @@
parent
dev.cerus.visualcrafting
- 1.0.7
+ 1.0.8
4.0.0
diff --git a/bukkit-18R1/pom.xml b/bukkit-18R1/pom.xml
index 8fef0f4..165a084 100644
--- a/bukkit-18R1/pom.xml
+++ b/bukkit-18R1/pom.xml
@@ -5,7 +5,7 @@
parent
dev.cerus.visualcrafting
- 1.0.7
+ 1.0.8
4.0.0
diff --git a/bukkit-18R2/pom.xml b/bukkit-18R2/pom.xml
index 1a9cc96..154c5aa 100644
--- a/bukkit-18R2/pom.xml
+++ b/bukkit-18R2/pom.xml
@@ -5,7 +5,7 @@
parent
dev.cerus.visualcrafting
- 1.0.7
+ 1.0.8
4.0.0
diff --git a/bukkit-19R1/pom.xml b/bukkit-19R1/pom.xml
index 4944e43..3b5db74 100644
--- a/bukkit-19R1/pom.xml
+++ b/bukkit-19R1/pom.xml
@@ -5,7 +5,7 @@
parent
dev.cerus.visualcrafting
- 1.0.7
+ 1.0.8
4.0.0
diff --git a/bukkit-19R2/pom.xml b/bukkit-19R2/pom.xml
index 3a3a9f8..84a5639 100644
--- a/bukkit-19R2/pom.xml
+++ b/bukkit-19R2/pom.xml
@@ -5,7 +5,7 @@
parent
dev.cerus.visualcrafting
- 1.0.7
+ 1.0.8
4.0.0
diff --git a/bukkit-19R3/pom.xml b/bukkit-19R3/pom.xml
new file mode 100644
index 0000000..2d4d4f2
--- /dev/null
+++ b/bukkit-19R3/pom.xml
@@ -0,0 +1,36 @@
+
+
+ 4.0.0
+
+ dev.cerus.visualcrafting
+ parent
+ 1.0.8
+
+
+ bukkit-19R3
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ dev.cerus.visualcrafting
+ api
+ ${project.parent.version}
+ provided
+
+
+
+ org.bukkit
+ craftbukkit
+ 1.19.4-R0.1-SNAPSHOT
+ provided
+
+
+
+
\ No newline at end of file
diff --git a/bukkit-19R3/src/main/java/dev/cerus/visualcrafting/v19r3/VersionAdapter19R3.java b/bukkit-19R3/src/main/java/dev/cerus/visualcrafting/v19r3/VersionAdapter19R3.java
new file mode 100644
index 0000000..1f1ae75
--- /dev/null
+++ b/bukkit-19R3/src/main/java/dev/cerus/visualcrafting/v19r3/VersionAdapter19R3.java
@@ -0,0 +1,186 @@
+package dev.cerus.visualcrafting.v19r3;
+
+import dev.cerus.visualcrafting.api.config.Config;
+import dev.cerus.visualcrafting.api.version.FakeMap;
+import dev.cerus.visualcrafting.api.version.VersionAdapter;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.UUID;
+import java.util.function.BiConsumer;
+import net.minecraft.network.NetworkManager;
+import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
+import net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy;
+import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
+import net.minecraft.network.protocol.game.PacketPlayOutMap;
+import net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity;
+import net.minecraft.network.syncher.DataWatcher;
+import net.minecraft.network.syncher.DataWatcherRegistry;
+import net.minecraft.server.network.PlayerConnection;
+import net.minecraft.world.entity.EntityTypes;
+import net.minecraft.world.level.saveddata.maps.WorldMap;
+import net.minecraft.world.phys.Vec3D;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Rotation;
+import org.bukkit.block.BlockFace;
+import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+public class VersionAdapter19R3 extends VersionAdapter {
+
+ private Config config;
+ private BiConsumer entityClickCallback;
+ private int nextEntityId;
+ private int nextMapId;
+ private Field netManField;
+
+ private NetworkManager getNetworkManager(final PlayerConnection b) {
+ try {
+ if (this.netManField == null) {
+ this.netManField = PlayerConnection.class.getDeclaredField("h");
+ this.netManField.setAccessible(true);
+ }
+ return (NetworkManager) this.netManField.get(b);
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void init(final Config config, final BiConsumer entityClickCallback) {
+ this.config = config;
+ this.entityClickCallback = entityClickCallback;
+ this.nextEntityId = config.entityIdRangeMin();
+ this.nextMapId = config.mapIdRangeMin();
+ }
+
+ @Override
+ public void inject(final Player player) {
+ if (this.config.enablePacketListening()) {
+ final NetworkManager netMan = this.getNetworkManager(((CraftPlayer) player).getHandle().b);
+ netMan.m.pipeline()
+ .addBefore("packet_handler", "visual_crafting", new ChannelDuplexHandler() {
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
+ if (msg instanceof PacketPlayInUseEntity useEntity) {
+ VersionAdapter19R3.this.handlePacketIn(player, useEntity);
+ }
+ super.channelRead(ctx, msg);
+ }
+ });
+ }
+ }
+
+ private void handlePacketIn(final Player player, final PacketPlayInUseEntity packet) {
+ try {
+ final Field a = packet.getClass().getDeclaredField("a");
+ a.setAccessible(true);
+ this.entityClickCallback.accept(player, (Integer) a.get(packet));
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public int spawnItemFrame(final Location location, final BlockFace direction) {
+ final int eid = this.getNewEntityId();
+ final PacketPlayOutSpawnEntity packet = new PacketPlayOutSpawnEntity(
+ eid,
+ UUID.randomUUID(),
+ location.getBlockX(),
+ location.getBlockY(),
+ location.getBlockZ(),
+ direction == BlockFace.DOWN ? 90 : direction == BlockFace.UP ? -90 : 0,
+ switch (direction) {
+ case NORTH -> -180;
+ case EAST -> -90;
+ case WEST -> 90;
+ default -> 0;
+ },
+ EntityTypes.af,
+ switch (direction) {
+ case UP -> 1;
+ case NORTH -> 2;
+ case SOUTH -> 3;
+ case WEST -> 4;
+ case EAST -> 5;
+ default -> 0;
+ },
+ new Vec3D(0, 0, 0),
+ switch (direction) {
+ case NORTH -> -180;
+ case EAST -> -90;
+ case WEST -> 90;
+ default -> 0;
+ }
+ );
+ Bukkit.getOnlinePlayers().forEach(player -> this.sendPacket(player, packet));
+ return eid;
+ }
+
+ @Override
+ public void updateItemFrame(final int frameId, final ItemStack itemStack, final Rotation rotation, final boolean invisible) {
+ final PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(frameId, Arrays.asList(
+ new DataWatcher.b<>(8, DataWatcherRegistry.h, CraftItemStack.asNMSCopy(itemStack)),
+ new DataWatcher.b<>(9, DataWatcherRegistry.b, rotation.ordinal()),
+ new DataWatcher.b<>(0, DataWatcherRegistry.a, (byte) (invisible ? 0x20 : 0))
+ ));
+ Bukkit.getOnlinePlayers().forEach(player -> this.sendPacket(player, packet));
+ }
+
+ @Override
+ public void destroyEntity(final int entityId) {
+ final PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(entityId);
+ Bukkit.getOnlinePlayers().forEach(player -> this.sendPacket(player, packet));
+ }
+
+ @Override
+ public FakeMap createMap() {
+ return this.createMap(this.getNewMapId());
+ }
+
+ @Override
+ public void sendMap(final FakeMap map) {
+ final PacketPlayOutMap packet = new PacketPlayOutMap(
+ map.getId(),
+ (byte) 0,
+ true,
+ Collections.emptyList(),
+ new WorldMap.b(0,
+ 0,
+ 128,
+ 128,
+ this.getMapData(map))
+ );
+ Bukkit.getOnlinePlayers().forEach(player -> this.sendPacket(player, packet));
+ }
+
+ private void sendPacket(final Player player, final Packet> packet) {
+ ((CraftPlayer) player).getHandle().b.a(packet);
+ }
+
+ private int getNewEntityId() {
+ if (this.nextEntityId >= this.config.entityIdRangeMax()) {
+ this.nextEntityId = this.config.entityIdRangeMin();
+ return this.nextEntityId;
+ } else {
+ return this.nextEntityId++;
+ }
+ }
+
+ private int getNewMapId() {
+ if (this.nextMapId >= this.config.mapIdRangeMax()) {
+ this.nextMapId = this.config.mapIdRangeMin();
+ return this.nextMapId;
+ } else {
+ return this.nextMapId++;
+ }
+ }
+
+}
diff --git a/plugin/pom.xml b/plugin/pom.xml
index ece4639..fc4522c 100644
--- a/plugin/pom.xml
+++ b/plugin/pom.xml
@@ -5,7 +5,7 @@
parent
dev.cerus.visualcrafting
- 1.0.7
+ 1.0.8
4.0.0
@@ -65,6 +65,12 @@
${project.parent.version}
compile
+
+ dev.cerus.visualcrafting
+ bukkit-19R3
+ ${project.parent.version}
+ compile
+
dev.cerus.maps
diff --git a/plugin/src/main/java/dev/cerus/visualcrafting/plugin/VisualCraftingPlugin.java b/plugin/src/main/java/dev/cerus/visualcrafting/plugin/VisualCraftingPlugin.java
index c21b75c..d794534 100644
--- a/plugin/src/main/java/dev/cerus/visualcrafting/plugin/VisualCraftingPlugin.java
+++ b/plugin/src/main/java/dev/cerus/visualcrafting/plugin/VisualCraftingPlugin.java
@@ -13,6 +13,7 @@
import dev.cerus.visualcrafting.v18r2.VersionAdapter18R2;
import dev.cerus.visualcrafting.v19r1.VersionAdapter19R1;
import dev.cerus.visualcrafting.v19r2.VersionAdapter19R2;
+import dev.cerus.visualcrafting.v19r3.VersionAdapter19R3;
import java.io.File;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
@@ -26,8 +27,17 @@ public class VisualCraftingPlugin extends JavaPlugin implements Config {
public void onEnable() {
this.saveDefaultConfig();
+ String version = Bukkit.getVersion();
+ version = version.substring(version.indexOf("MC: ") + 4, version.lastIndexOf(')'));
+ if (!version.matches("\\d+\\.\\d+(\\.\\d+)?")) {
+ this.getLogger().severe("Could not detect server version for " + version);
+ this.getPluginLoader().disablePlugin(this);
+ return;
+ }
+ final String major = version.split("\\.")[1];
+
// Initialize textures
- final File textureCacheFile = new File(this.getDataFolder(), "textures");
+ final File textureCacheFile = new File(this.getDataFolder(), "textures_" + major);
final TextureCache textureCache = new TextureCache();
if (textureCacheFile.exists()) {
textureCache.read(textureCacheFile);
@@ -41,8 +51,6 @@ public void onEnable() {
}
// Initialize version adapter
- String version = Bukkit.getVersion();
- version = version.substring(version.indexOf("MC: ") + 4, version.lastIndexOf(')'));
final VersionAdapter versionAdapter = switch (version) {
case "1.16.5" -> new VersionAdapter16R3();
case "1.17", "1.17.1" -> new VersionAdapter17R1();
@@ -50,6 +58,7 @@ public void onEnable() {
case "1.18.2" -> new VersionAdapter18R2();
case "1.19", "1.19.1", "1.19.2" -> new VersionAdapter19R1();
case "1.19.3" -> new VersionAdapter19R2();
+ case "1.19.4" -> new VersionAdapter19R3();
default -> null;
};
if (versionAdapter == null) {
diff --git a/plugin/src/main/java/dev/cerus/visualcrafting/plugin/texture/TextureDownloader.java b/plugin/src/main/java/dev/cerus/visualcrafting/plugin/texture/TextureDownloader.java
index b6f216d..24cecc4 100644
--- a/plugin/src/main/java/dev/cerus/visualcrafting/plugin/texture/TextureDownloader.java
+++ b/plugin/src/main/java/dev/cerus/visualcrafting/plugin/texture/TextureDownloader.java
@@ -25,6 +25,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.imageio.ImageIO;
+import org.bukkit.Bukkit;
/**
* Downloads and converts textures from Mojang
@@ -62,7 +63,7 @@ public CompletableFuture downloadTextures(final Logger logger,
// Get download url
final String clientDownloadUrl;
try {
- clientDownloadUrl = this.getLatestClientUrl();
+ clientDownloadUrl = this.getMatchingClientUrl();
} catch (final IOException e) {
future.completeExceptionally(e);
return;
@@ -126,24 +127,24 @@ private void extractAndConvert(final File folder,
}
/**
- * Gets the latest Minecraft client url from the Minecraft version manifest
+ * Gets a matching Minecraft client url from the Minecraft version manifest
*
* @return The latest client download url
*
* @throws IOException In case of IO errors
*/
- private String getLatestClientUrl() throws IOException {
- // Get version manifest and pull out the latest release id
+ private String getMatchingClientUrl() throws IOException {
+ // Get version manifest, get server version
final JsonObject versionsObj = new JsonParser().parse(this.get(VERSION_MANIFEST_URL)).getAsJsonObject();
- final JsonObject latestObj = versionsObj.get("latest").getAsJsonObject();
- final String latestRelease = latestObj.get("release").getAsString();
+ String version = Bukkit.getVersion();
+ version = version.substring(version.indexOf("MC: ") + 4, version.lastIndexOf(')'));
// Loop through entries until we find the release
String url = null;
final JsonArray versionsArr = versionsObj.get("versions").getAsJsonArray();
for (final JsonElement element : versionsArr) {
final JsonObject obj = element.getAsJsonObject();
- if (obj.get("id").getAsString().equals(latestRelease)) {
+ if (obj.get("id").getAsString().equals(version)) {
url = obj.get("url").getAsString();
}
}
diff --git a/pom.xml b/pom.xml
index 7281e8e..68a6e5d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
dev.cerus.visualcrafting
parent
pom
- 1.0.7
+ 1.0.8
plugin
api
@@ -17,6 +17,7 @@
bukkit-18R2
bukkit-19R1
bukkit-19R2
+ bukkit-19R3