WIP Multiversion (SetBlock missing)
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed

Dieser Commit ist enthalten in:
Lixfel 2023-09-08 16:58:02 +02:00
Ursprung 7d82fed462
Commit 6f44fad535
24 geänderte Dateien mit 597 neuen und 321 gelöschten Zeilen

Datei anzeigen

@ -18,8 +18,6 @@ java {
repositories {
mavenCentral()
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://jitpack.io")
maven("https://maven.enginehub.org/repo/")
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.viaversion.com")
@ -27,7 +25,6 @@ repositories {
dependencies {
paperweight.paperDevBundle("1.20.1-R0.1-SNAPSHOT")
implementation("xyz.jpenilla:reflection-remapper:0.1.0-SNAPSHOT")
// Zstd Compression Library
implementation("com.github.luben:zstd-jni:1.5.5-4")

Datei anzeigen

@ -7,10 +7,16 @@ public class AxiomConstants {
public static final long MIN_POSITION_LONG = 0b1000000000000000000000000010000000000000000000000000100000000000L;
public static final int API_VERSION = 5;
public static final NamespacedKey ACTIVE_HOTBAR_INDEX = new NamespacedKey("axiom", "active_hotbar_index");
public static final NamespacedKey HOTBAR_DATA = new NamespacedKey("axiom", "hotbar_data");
public static final NamespacedKey ACTIVE_VIEW = new NamespacedKey("axiom", "active_view");
public static final NamespacedKey VIEWS = new NamespacedKey("axiom", "views");
public static final String PERMISSION = "axiom.*";
public static final NamespacedKey ACTIVE_HOTBAR_INDEX = axiomKey("active_hotbar_index");
public static final NamespacedKey HOTBAR_DATA = axiomKey("hotbar_data");
public static final NamespacedKey ACTIVE_VIEW = axiomKey("active_view");
public static final NamespacedKey VIEWS = axiomKey("views");
public static NamespacedKey axiomKey(String key) {
return new NamespacedKey("axiom", key);
}
}

Datei anzeigen

@ -2,8 +2,12 @@ package com.moulberry.axiom;
import com.moulberry.axiom.integration.PaperFailMoveListener;
import com.moulberry.axiom.packet.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
@ -14,19 +18,22 @@ import java.util.logging.Level;
public class AxiomPaper extends JavaPlugin implements Listener {
public static final String PERMISSION = "axiom.*";
private static AxiomPaper instance;
public static Plugin getPlugin() {
return instance;
}
private static final Map<String, AxiomPacketListener> listeners = new ConcurrentHashMap<>();
public static AxiomPacketListener getListener(String channel) {
return listeners.get(channel);
}
private static final Class<Player> CraftPlayer = Reflection.getClass("org.bukkit.craftbukkit.entity.CraftPlayer");
private static final Reflection.Method<Player, ServerPlayer> getHandle = Reflection.getTypedMethod(CraftPlayer, "getHandle", ServerPlayer.class);
public static ServerPlayer convert(Player player) {
return getHandle.invoke(player);
}
@Override
public void onEnable() {
instance = this;
@ -41,26 +48,28 @@ public class AxiomPaper extends JavaPlugin implements Listener {
Bukkit.getMessenger().registerOutgoingPluginChannel(this, channel.getChannel());
}
//TODO multiversion (Except setGamemode)
registerInChannel("hello", new HelloPacketListener());
registerInChannel("hello", new HelloPacketListener(new AxiomPlayerManager()));
registerInChannel("set_gamemode", new SetGamemodePacketListener());
registerInChannel("set_fly_speed", new SetFlySpeedPacketListener());
registerInChannel("set_block", new SetBlockPacketListener());
registerInChannel("set_block", new SetBlockPacketListener()); //TODO multiversion
registerInChannel("set_hotbar_slot", new SetHotbarSlotPacketListener());
registerInChannel("switch_active_hotbar", new SwitchActiveHotbarPacketListener());
registerInChannel("teleport", new TeleportPacketListener());
registerInChannel("set_editor_views", new SetEditorViewsPacketListener());
registerInChannel("request_block_entity", new RequestBlockEntityPacketListener());
registerInChannel("handle_big_payload", new SetBlockBufferPacketListener());
registerInChannel("handle_big_payload", new SetBlockBufferPacketListener()); //TODO multiversion
}
private void registerInChannel(String channel, AxiomPacketListener handler) {
listeners.put(channel, handler);
Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:" + channel, (ch, player, message) -> {
if (!player.hasPermission(PERMISSION))
ByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
if (!player.hasPermission(AxiomConstants.PERMISSION)) {
handler.onMissingPerm(player, buf);
return;
}
handler.onMessage(player, Unpooled.wrappedBuffer(message));
handler.onMessage(player, buf);
});
}

Datei anzeigen

@ -0,0 +1,38 @@
package com.moulberry.axiom;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class AxiomPlayerManager implements Listener {
private final Set<Player> axiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>());
public AxiomPlayerManager() {
Bukkit.getPluginManager().registerEvents(this, AxiomPaper.getPlugin());
Bukkit.getScheduler().scheduleSyncRepeatingTask(AxiomPaper.getPlugin(), () -> axiomPlayers.removeIf(player -> {
if (!player.hasPermission(AxiomConstants.PERMISSION)) {
OutChannel.ENABLE.send(player, new byte[] { 0 });
return true;
}
return false;
}), 20, 20);
}
public void add(Player player) {
axiomPlayers.add(player);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
axiomPlayers.remove(event.getPlayer());
}
}

Datei anzeigen

@ -0,0 +1,137 @@
package com.moulberry.axiom;
import org.bukkit.Bukkit;
import java.lang.reflect.*;
import java.util.Arrays;
public class Reflection {
private Reflection() {}
public interface Field<T, R> {
R get(T target);
void set(T target, R value);
}
public static <T, R> Field<T, R> getField(Class<T> target, Class<R> fieldType, Class<?>... parameters) {
return getField(target, fieldType, 0, parameters);
}
public static <T, R> Field<T, R> getField(Class<T> target, Class<R> fieldType, int index, Class<?>... parameters) {
for (final java.lang.reflect.Field field : target.getDeclaredFields()) {
if(matching(field, fieldType, parameters) && index-- <= 0) {
field.setAccessible(true);
return new Field<>() {
@Override
@SuppressWarnings("unchecked")
public R get(T target) {
try {
return (R) field.get(target);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot access reflection.", e);
}
}
@Override
public void set(T target, R value) {
try {
field.set(target, value);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot access reflection.", e);
}
}
};
}
}
if (target.getSuperclass() != null) {
try {
return getField((Class<T>) target.getSuperclass(), fieldType, index, parameters);
} catch (ClassCastException e) {}
}
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
}
private static <T> boolean matching(java.lang.reflect.Field field, Class<T> fieldType, Class<?>... parameters) {
if(!fieldType.isAssignableFrom(field.getType()))
return false;
if(parameters.length > 0) {
Type[] arguments = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
for(int i = 0; i < parameters.length; i++) {
if(arguments[i] instanceof ParameterizedType ? ((ParameterizedType) arguments[i]).getRawType() != parameters[i] : arguments[i] != parameters[i])
return false;
}
}
return true;
}
public interface Method<T, R> {
R invoke(T target, Object... arguments);
}
public static <T> Method<T, Object> getMethod(Class<T> clazz, Class<?>... params) {
return getTypedMethod(clazz, null, null, 0, params);
}
public static <T> Method<T, Object> getMethod(Class<T> clazz, String methodName, Class<?>... params) {
return getTypedMethod(clazz, methodName, null, 0, params);
}
public static <T, R> Method<T, R> getTypedMethod(Class<T> clazz, Class<R> returnType, Class<?>... params) {
return getTypedMethod(clazz, null, returnType, 0, params);
}
public static <T, R> Method<T, R> getTypedMethod(Class<T> clazz, Class<R> returnType, int index, Class<?>... params) {
return getTypedMethod(clazz, null, returnType, index, params);
}
public static <T, R> Method<T, R> getTypedMethod(Class<T> clazz, String methodName, Class<R> returnType, Class<?>... params) {
return getTypedMethod(clazz, methodName, returnType, 0, params);
}
private static <T, R> Method<T, R> getTypedMethod(Class<T> clazz, String methodName, Class<R> returnType, int index, Class<?>... params) {
for (final java.lang.reflect.Method method : clazz.getDeclaredMethods()) {
if ((methodName == null || method.getName().equals(methodName))
&& (returnType == null || method.getReturnType().equals(returnType))
&& Arrays.equals(method.getParameterTypes(), params)
&& index-- <= 0) {
method.setAccessible(true);
return (target, arguments) -> {
try {
return (R) method.invoke(target, arguments);
} catch (Exception e) {
throw new IllegalArgumentException("Cannot invoke method " + method, e);
}
};
}
}
if (clazz.getSuperclass() != null) {
try {
return getTypedMethod((Class<T>) clazz.getSuperclass(), methodName, returnType, index, params);
} catch (ClassCastException e) {}
}
throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params)));
}
private static final String ORG_BUKKIT_CRAFTBUKKIT = Bukkit.getServer().getClass().getPackage().getName();
public static <T> Class<T> getClass(String name) {
if(name.startsWith("org.bukkit.craftbukkit"))
name = ORG_BUKKIT_CRAFTBUKKIT + name.substring(22);
try {
return (Class<T>) Class.forName(name);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot find " + name, e);
}
}
}

Datei anzeigen

@ -1,13 +1,12 @@
package com.moulberry.axiom;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.persistence.UUIDDataType;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import io.netty.buffer.ByteBuf;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
@ -15,73 +14,76 @@ import java.util.UUID;
public final class View {
public String name;
public final UUID uuid;
public boolean pinLevel = false;
public boolean pinLocation = false;
private ResourceKey<Level> level = null;
private Vec3 position = null;
private float yaw;
private float pitch;
private final String name;
private final UUID uuid;
private boolean pinLevel = false;
private boolean pinLocation = false;
private World level = null;
private Location location = null;
public View(String name, UUID uuid) {
this.name = name;
this.uuid = uuid;
}
public void write(FriendlyByteBuf byteBuf) {
byteBuf.writeUtf(this.name, 64);
byteBuf.writeUUID(this.uuid);
public void write(ByteBuf buf) {
MojBuf.writeUtf(buf, this.name, 64);
MojBuf.writeUUID(buf, this.uuid);
byteBuf.writeBoolean(this.pinLevel);
buf.writeBoolean(this.pinLevel);
if (this.pinLevel && this.level != null) {
byteBuf.writeBoolean(true);
byteBuf.writeResourceKey(this.level);
buf.writeBoolean(true);
MojBuf.writeKey(buf, this.level.getKey());
} else {
byteBuf.writeBoolean(false);
buf.writeBoolean(false);
}
byteBuf.writeBoolean(this.pinLocation);
if (this.position != null) {
byteBuf.writeBoolean(true);
byteBuf.writeDouble(this.position.x);
byteBuf.writeDouble(this.position.y);
byteBuf.writeDouble(this.position.z);
byteBuf.writeFloat(this.yaw);
byteBuf.writeFloat(this.pitch);
buf.writeBoolean(this.pinLocation);
if (this.location != null) {
buf.writeBoolean(true);
buf.writeDouble(this.location.getX());
buf.writeDouble(this.location.getY());
buf.writeDouble(this.location.getZ());
buf.writeFloat(this.location.getYaw());
buf.writeFloat(this.location.getPitch());
} else {
byteBuf.writeBoolean(false);
buf.writeBoolean(false);
}
}
public static View read(FriendlyByteBuf byteBuf) {
View view = new View(byteBuf.readUtf(64), byteBuf.readUUID());
public static View read(ByteBuf buf) {
View view = new View(MojBuf.readUtf(buf, 64), MojBuf.readUUID(buf));
view.pinLevel = byteBuf.readBoolean();
if (byteBuf.readBoolean()) {
view.level = byteBuf.readResourceKey(Registries.DIMENSION);
view.pinLevel = buf.readBoolean();
if (buf.readBoolean()) {
view.level = Bukkit.getWorld(MojBuf.readKey(buf));
}
view.pinLocation = byteBuf.readBoolean();
if (byteBuf.readBoolean()) {
view.position = new Vec3(byteBuf.readDouble(), byteBuf.readDouble(), byteBuf.readDouble());
view.yaw = byteBuf.readFloat();
view.pitch = byteBuf.readFloat();
view.pinLocation = buf.readBoolean();
if (buf.readBoolean()) {
view.location = new Location(view.level,
buf.readDouble(),
buf.readDouble(),
buf.readDouble(),
buf.readFloat(),
buf.readFloat()
);
}
return view;
}
private static final NamespacedKey NAME_KEY = new NamespacedKey("axiom", "view_name");
private static final NamespacedKey UUID_KEY = new NamespacedKey("axiom", "view_uuid");
private static final NamespacedKey PIN_LEVEL_KEY = new NamespacedKey("axiom", "view_pin_level");
private static final NamespacedKey LEVEL_KEY = new NamespacedKey("axiom", "view_level");
private static final NamespacedKey PIN_LOCATION_KEY = new NamespacedKey("axiom", "view_pin_location");
private static final NamespacedKey X_KEY = new NamespacedKey("axiom", "view_x");
private static final NamespacedKey Y_KEY = new NamespacedKey("axiom", "view_y");
private static final NamespacedKey Z_KEY = new NamespacedKey("axiom", "view_z");
private static final NamespacedKey YAW_KEY = new NamespacedKey("axiom", "view_yaw");
private static final NamespacedKey PITCH_KEY = new NamespacedKey("axiom", "view_pitch");
private static final NamespacedKey NAME_KEY = AxiomConstants.axiomKey("view_name");
private static final NamespacedKey UUID_KEY = AxiomConstants.axiomKey("view_uuid");
private static final NamespacedKey PIN_LEVEL_KEY = AxiomConstants.axiomKey("view_pin_level");
private static final NamespacedKey LEVEL_KEY = AxiomConstants.axiomKey("view_level");
private static final NamespacedKey PIN_LOCATION_KEY = AxiomConstants.axiomKey("view_pin_location");
private static final NamespacedKey X_KEY = AxiomConstants.axiomKey("view_x");
private static final NamespacedKey Y_KEY = AxiomConstants.axiomKey("view_y");
private static final NamespacedKey Z_KEY = AxiomConstants.axiomKey("view_z");
private static final NamespacedKey YAW_KEY = AxiomConstants.axiomKey("view_yaw");
private static final NamespacedKey PITCH_KEY = AxiomConstants.axiomKey("view_pitch");
public void save(PersistentDataContainer container) {
container.set(NAME_KEY, PersistentDataType.STRING, this.name);
@ -89,16 +91,16 @@ public final class View {
container.set(PIN_LEVEL_KEY, PersistentDataType.BOOLEAN, this.pinLevel);
if (this.pinLevel && this.level != null) {
container.set(LEVEL_KEY, PersistentDataType.STRING, this.level.location().toString());
container.set(LEVEL_KEY, PersistentDataType.STRING, this.level.getKey().asString());
}
container.set(PIN_LOCATION_KEY, PersistentDataType.BOOLEAN, this.pinLocation);
if (this.position != null) {
container.set(X_KEY, PersistentDataType.DOUBLE, this.position.x);
container.set(Y_KEY, PersistentDataType.DOUBLE, this.position.y);
container.set(Z_KEY, PersistentDataType.DOUBLE, this.position.z);
container.set(YAW_KEY, PersistentDataType.FLOAT, this.yaw);
container.set(PITCH_KEY, PersistentDataType.FLOAT, this.pitch);
if (this.location != null) {
container.set(X_KEY, PersistentDataType.DOUBLE, this.location.getX());
container.set(Y_KEY, PersistentDataType.DOUBLE, this.location.getY());
container.set(Z_KEY, PersistentDataType.DOUBLE, this.location.getZ());
container.set(YAW_KEY, PersistentDataType.FLOAT, this.location.getYaw());
container.set(PITCH_KEY, PersistentDataType.FLOAT, this.location.getPitch());
}
}
@ -111,17 +113,18 @@ public final class View {
view.pinLevel = tag.getOrDefault(PIN_LEVEL_KEY, PersistentDataType.BOOLEAN, false);
if (tag.has(LEVEL_KEY)) {
String level = tag.get(LEVEL_KEY, PersistentDataType.STRING);
view.level = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(level));
view.level = Bukkit.getWorld(NamespacedKey.fromString(level));
}
view.pinLocation = tag.getOrDefault(PIN_LOCATION_KEY, PersistentDataType.BOOLEAN, false);
if (tag.has(X_KEY) && tag.has(Y_KEY) && tag.has(Z_KEY)) {
double x = tag.getOrDefault(X_KEY, PersistentDataType.DOUBLE, 0.0);
double y = tag.getOrDefault(Y_KEY, PersistentDataType.DOUBLE, 0.0);
double z = tag.getOrDefault(Z_KEY, PersistentDataType.DOUBLE, 0.0);
view.position = new Vec3(x, y, z);
view.yaw = tag.getOrDefault(YAW_KEY, PersistentDataType.FLOAT, 0.0f);
view.pitch = tag.getOrDefault(PITCH_KEY, PersistentDataType.FLOAT, 0.0f);
view.location = new Location(view.level,
tag.getOrDefault(X_KEY, PersistentDataType.DOUBLE, 0.0),
tag.getOrDefault(Y_KEY, PersistentDataType.DOUBLE, 0.0),
tag.getOrDefault(Z_KEY, PersistentDataType.DOUBLE, 0.0),
tag.getOrDefault(YAW_KEY, PersistentDataType.FLOAT, 0.0f),
tag.getOrDefault(PITCH_KEY, PersistentDataType.FLOAT, 0.0f)
);
}
return view;

Datei anzeigen

@ -1,5 +1,6 @@
package com.moulberry.axiom.buffer;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
@ -15,14 +16,14 @@ public class BiomeBuffer {
this.palette = palette;
}
public static BiomeBuffer load(FriendlyByteBuf friendlyByteBuf) {
int paletteSize = friendlyByteBuf.readByte();
public static BiomeBuffer load(ByteBuf buf) {
int paletteSize = buf.readByte();
ResourceKey<Biome>[] palette = new ResourceKey[255];
for (int i = 0; i < paletteSize; i++) {
ResourceKey<Biome> key = friendlyByteBuf.readResourceKey(Registries.BIOME);
ResourceKey<Biome> key = buf.readResourceKey(Registries.BIOME);
palette[i] = key;
}
Position2ByteMap map = Position2ByteMap.load(friendlyByteBuf);
Position2ByteMap map = Position2ByteMap.load(buf);
return new BiomeBuffer(map, palette);
}

Datei anzeigen

@ -1,6 +1,7 @@
package com.moulberry.axiom.buffer;
import com.moulberry.axiom.AxiomConstants;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectSet;
@ -27,22 +28,22 @@ public class BlockBuffer {
this.values = new Long2ObjectOpenHashMap<>();
}
public static BlockBuffer load(FriendlyByteBuf friendlyByteBuf) {
public static BlockBuffer load(ByteBuf buf) {
BlockBuffer buffer = new BlockBuffer();
while (true) {
long index = friendlyByteBuf.readLong();
long index = buf.readLong();
if (index == AxiomConstants.MIN_POSITION_LONG) break;
PalettedContainer<BlockState> palettedContainer = buffer.getOrCreateSection(index);
palettedContainer.read(friendlyByteBuf);
palettedContainer.read((FriendlyByteBuf) buf);
int blockEntitySize = Math.min(4096, friendlyByteBuf.readVarInt());
int blockEntitySize = Math.min(4096, MojBuf.readVarInt(buf));
if (blockEntitySize > 0) {
Short2ObjectMap<CompressedBlockEntity> map = new Short2ObjectOpenHashMap<>(blockEntitySize);
for (int i = 0; i < blockEntitySize; i++) {
short offset = friendlyByteBuf.readShort();
CompressedBlockEntity blockEntity = CompressedBlockEntity.read(friendlyByteBuf);
short offset = buf.readShort();
CompressedBlockEntity blockEntity = CompressedBlockEntity.read(buf);
map.put(offset, blockEntity);
}
buffer.blockEntities.put(index, map);

Datei anzeigen

@ -3,9 +3,10 @@ package com.moulberry.axiom.buffer;
import com.github.luben.zstd.Zstd;
import com.github.luben.zstd.ZstdDictCompress;
import com.github.luben.zstd.ZstdDictDecompress;
import com.moulberry.axiom.Reflection;
import io.netty.buffer.ByteBuf;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.network.FriendlyByteBuf;
import java.io.*;
import java.util.Objects;
@ -24,41 +25,35 @@ public record CompressedBlockEntity(int originalSize, byte compressionDict, byte
}
}
private static final Reflection.Method<NbtIo, Object> write = Reflection.getMethod(NbtIo.class, CompoundTag.class, DataOutput.class);
public static CompressedBlockEntity compress(CompoundTag tag, ByteArrayOutputStream baos) {
try {
baos.reset();
DataOutputStream dos = new DataOutputStream(baos);
NbtIo.write(tag, dos);
byte[] uncompressed = baos.toByteArray();
byte[] compressed = Zstd.compress(uncompressed, zstdDictCompress);
return new CompressedBlockEntity(uncompressed.length, (byte) 0, compressed);
} catch (IOException e) {
throw new RuntimeException(e);
}
baos.reset();
DataOutputStream dos = new DataOutputStream(baos);
write.invoke(null, tag, dos);
byte[] uncompressed = baos.toByteArray();
byte[] compressed = Zstd.compress(uncompressed, zstdDictCompress);
return new CompressedBlockEntity(uncompressed.length, (byte) 0, compressed);
}
private static final Reflection.Method<NbtIo, CompoundTag> read = Reflection.getTypedMethod(NbtIo.class, CompoundTag.class, DataInput.class);
public CompoundTag decompress() {
if (this.compressionDict != 0) throw new UnsupportedOperationException("Unknown compression dict: " + this.compressionDict);
try {
byte[] nbt = Zstd.decompress(this.compressed, zstdDictDecompress, this.originalSize);
return NbtIo.read(new DataInputStream(new ByteArrayInputStream(nbt)));
} catch (IOException e) {
throw new RuntimeException(e);
}
byte[] nbt = Zstd.decompress(this.compressed, zstdDictDecompress, this.originalSize);
return read.invoke(null, new DataInputStream(new ByteArrayInputStream(nbt)));
}
public static CompressedBlockEntity read(FriendlyByteBuf friendlyByteBuf) {
int originalSize = friendlyByteBuf.readVarInt();
byte compressionDict = friendlyByteBuf.readByte();
byte[] compressed = friendlyByteBuf.readByteArray();
public static CompressedBlockEntity read(ByteBuf buf) {
int originalSize = MojBuf.readVarInt(buf);
byte compressionDict = buf.readByte();
byte[] compressed = MojBuf.readByteArray(buf);
return new CompressedBlockEntity(originalSize, compressionDict, compressed);
}
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeVarInt(this.originalSize);
friendlyByteBuf.writeByte(this.compressionDict);
friendlyByteBuf.writeByteArray(this.compressed);
public void write(ByteBuf buf) {
MojBuf.writeVarInt(buf, this.originalSize);
buf.writeByte(this.compressionDict);
MojBuf.writeByteArray(buf, this.compressed);
}
}

Datei anzeigen

@ -0,0 +1,116 @@
package com.moulberry.axiom.buffer;
import com.moulberry.axiom.Reflection;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
public class MojBuf {
private MojBuf() {}
public static ByteBuf unpooled() {
return new FriendlyByteBuf(Unpooled.buffer());
}
private static final Reflection.Method<FriendlyByteBuf, String> readUtf = Reflection.getTypedMethod(FriendlyByteBuf.class, String.class, int.class);
public static String readUtf(ByteBuf buf, int maxLength) {
return readUtf.invoke((FriendlyByteBuf) buf, maxLength);
}
public static String readUtf(ByteBuf buf) {
return readUtf(buf, Short.MAX_VALUE);
}
public static NamespacedKey readKey(ByteBuf buf) {
return NamespacedKey.fromString(readUtf(buf));
}
private static final Reflection.Method<FriendlyByteBuf, FriendlyByteBuf> writeUtf = Reflection.getTypedMethod(FriendlyByteBuf.class, FriendlyByteBuf.class, String.class, int.class);
public static void writeUtf(ByteBuf buf, String string, int maxLength) {
writeUtf.invoke((FriendlyByteBuf) buf, string, maxLength);
}
public static void writeUtf(ByteBuf buf, String string) {
writeUtf(buf, string, Short.MAX_VALUE);
}
public static void writeKey(ByteBuf buf, NamespacedKey key) {
writeUtf(buf, key.asString());
}
// is the first method returning an int with no arguments in 1.20.1
private static final Reflection.Method<FriendlyByteBuf, Integer> readVarInt = Reflection.getTypedMethod(FriendlyByteBuf.class, int.class);
public static int readVarInt(ByteBuf buf) {
return readVarInt.invoke((FriendlyByteBuf) buf);
}
private static final Reflection.Method<FriendlyByteBuf, FriendlyByteBuf> writeVarInt = Reflection.getTypedMethod(FriendlyByteBuf.class, FriendlyByteBuf.class, int.class);
public static void writeVarInt(ByteBuf buf, int value) {
writeVarInt.invoke((FriendlyByteBuf) buf, value);
}
private static final Reflection.Method<FriendlyByteBuf, CompoundTag> readNbt = Reflection.getTypedMethod(FriendlyByteBuf.class, CompoundTag.class);
public static CompoundTag readNbt(ByteBuf buf) {
return readNbt.invoke((FriendlyByteBuf) buf);
}
private static final Reflection.Method<FriendlyByteBuf, UUID> readUUID = Reflection.getTypedMethod(FriendlyByteBuf.class, UUID.class);
public static UUID readUUID(ByteBuf buf) {
return readUUID.invoke((FriendlyByteBuf) buf);
}
private static final Reflection.Method<FriendlyByteBuf, FriendlyByteBuf> writeUUID = Reflection.getTypedMethod(FriendlyByteBuf.class, FriendlyByteBuf.class, UUID.class);
public static void writeUUID(ByteBuf buf, UUID uuid) {
writeUUID.invoke((FriendlyByteBuf) buf, uuid);
}
private static final Class<ItemStack> CraftItemStack = Reflection.getClass("org.bukkit.craftbukkit.inventory.CraftItemStack");
private static final Reflection.Method<ItemStack, ItemStack> asCraftMirror = Reflection.getTypedMethod(CraftItemStack, "asCraftMirror", CraftItemStack, net.minecraft.world.item.ItemStack.class);
public static ItemStack toBukkit(net.minecraft.world.item.ItemStack stack) {
return asCraftMirror.invoke(null, stack);
}
private static final Reflection.Method<ItemStack, net.minecraft.world.item.ItemStack> asNMSCopy = Reflection.getTypedMethod(CraftItemStack, net.minecraft.world.item.ItemStack.class, ItemStack.class);
public static net.minecraft.world.item.ItemStack toMojang(ItemStack stack) {
return asNMSCopy.invoke(null, stack);
}
private static final Reflection.Method<FriendlyByteBuf, net.minecraft.world.item.ItemStack> readItem = Reflection.getTypedMethod(FriendlyByteBuf.class, net.minecraft.world.item.ItemStack.class);
public static ItemStack readItem(ByteBuf buf) {
return toBukkit(readItem.invoke((FriendlyByteBuf) buf));
}
private static final Reflection.Method<FriendlyByteBuf, FriendlyByteBuf> writeItem = Reflection.getTypedMethod(FriendlyByteBuf.class, FriendlyByteBuf.class, net.minecraft.world.item.ItemStack.class);
public static void writeItem(ByteBuf buf, ItemStack stack) {
writeItem.invoke((FriendlyByteBuf) buf, toMojang(stack));
}
public static byte[] readByteArray(ByteBuf buf) {
byte[] array = new byte[readVarInt(buf)];
buf.readBytes(array);
return array;
}
public static void writeByteArray(ByteBuf buf, byte[] array) {
writeVarInt(buf, array.length);
buf.writeBytes(array);
}
public static <T> List<T> readList(ByteBuf buf, Function<ByteBuf, T> reader) {
int length = readVarInt(buf);
List<T> list = new ArrayList<>(length);
for(int i = 0; i < length; i++)
list.add(reader.apply(buf));
return list;
}
}

Datei anzeigen

@ -1,6 +1,6 @@
package com.moulberry.axiom.integration;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.AxiomConstants;
import io.papermc.paper.event.player.PlayerFailMoveEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@ -9,7 +9,7 @@ public class PaperFailMoveListener implements Listener {
@EventHandler
public void onFailMove(PlayerFailMoveEvent event) {
if (event.getPlayer().hasPermission(AxiomPaper.PERMISSION) &&
if (event.getPlayer().hasPermission(AxiomConstants.PERMISSION) &&
event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) {
event.setAllowed(true);
}

Datei anzeigen

@ -1,6 +1,9 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.Reflection;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
@ -8,25 +11,36 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
import io.papermc.paper.network.ConnectionEvent;
import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@ChannelHandler.Sharable
public class AxiomBigPayloadHandler extends ChannelInboundHandlerAdapter {
private static final int PLUGINMESSAGE_PACKETID = ConnectionProtocol.PLAY.getPacketId(PacketFlow.SERVERBOUND, new ServerboundCustomPayloadPacket(null, null));
private static final Reflection.Method<ConnectionProtocol, Integer> getPacketId = Reflection.getTypedMethod(ConnectionProtocol.class, int.class, PacketFlow.class, Packet.class);
private static final int PLUGINMESSAGE_PACKETID = getPacketId.invoke(ConnectionProtocol.PLAY, PacketFlow.SERVERBOUND, new ServerboundCustomPayloadPacket(null, null));
private final Player player;
private final List<QueuedPacket> packets = new ArrayList<>();
private final BukkitTask task;
public AxiomBigPayloadHandler(Player player) {
this.player = player;
this.task = Bukkit.getScheduler().runTaskTimer(AxiomPaper.getPlugin(), this::runHandlers, 1, 1);
}
@Override
public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) throws Exception {
public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) {
if(!(msg instanceof ByteBuf in)) {
ctx.fireChannelRead(msg);
return;
@ -35,15 +49,13 @@ public class AxiomBigPayloadHandler extends ChannelInboundHandlerAdapter {
int readerIndexBackup = in.readerIndex();
if(in.readableBytes() != 0) {
FriendlyByteBuf buf = new FriendlyByteBuf((ByteBuf) msg);
if (buf.readVarInt() == PLUGINMESSAGE_PACKETID) {
ResourceLocation identifier = buf.readResourceLocation();
if (identifier.getNamespace().equals("axiom") && player.hasPermission(AxiomPaper.PERMISSION)) {
AxiomPacketListener listener = AxiomPaper.getListener(identifier.getPath());
//TODO sync!
listener.onMessage(player, in);
in.release();
ByteBuf buf = new FriendlyByteBuf(in);
if (MojBuf.readVarInt(buf) == PLUGINMESSAGE_PACKETID) {
NamespacedKey id = MojBuf.readKey(buf);
if (id.getNamespace().equals("axiom") && player.hasPermission(AxiomConstants.PERMISSION)) {
synchronized (packets) {
packets.add(new QueuedPacket(id.getKey(), buf));
}
return;
}
}
@ -62,4 +74,29 @@ public class AxiomBigPayloadHandler extends ChannelInboundHandlerAdapter {
super.userEventTriggered(ctx, evt);
}
@Override
public void channelInactive(@NotNull ChannelHandlerContext ctx) {
if(!task.isCancelled())
task.cancel();
ctx.fireChannelInactive();
}
private void runHandlers() {
List<QueuedPacket> queue;
synchronized (packets) {
if(packets.isEmpty())
return;
queue = new ArrayList<>(packets);
packets.clear();
}
for(QueuedPacket packet : queue) {
AxiomPaper.getListener(packet.name).onMessage(player, packet.in);
packet.in.release();
}
}
private record QueuedPacket(String name, ByteBuf in) {}
}

Datei anzeigen

@ -7,4 +7,6 @@ public interface AxiomPacketListener {
void onMessage(Player player, ByteBuf buf);
default void onMissingPerm(Player player, ByteBuf buf) {}
}

Datei anzeigen

@ -1,60 +1,42 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.OutChannel;
import com.moulberry.axiom.View;
import com.moulberry.axiom.*;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.event.AxiomHandshakeEvent;
import com.moulberry.axiom.persistence.ItemStackDataType;
import com.moulberry.axiom.persistence.UUIDDataType;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import net.kyori.adventure.text.Component;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.Connection;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class HelloPacketListener implements AxiomPacketListener, Listener {
private final Set<Player> axiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>());
private static final Reflection.Field<ServerPlayer, ServerGamePacketListenerImpl> serverPlayerConnection = Reflection.getField(ServerPlayer.class, ServerGamePacketListenerImpl.class);
private static final Reflection.Field<ServerGamePacketListenerImpl, Connection> packetListenerConnection = Reflection.getField(ServerGamePacketListenerImpl.class, Connection.class);
private static final Reflection.Field<Connection, Channel> channel = Reflection.getField(Connection.class, Channel.class);
public HelloPacketListener() {
//TODO cleanup (side effects of class)
Bukkit.getPluginManager().registerEvents(this, AxiomPaper.getPlugin());
Bukkit.getScheduler().scheduleSyncRepeatingTask(AxiomPaper.getPlugin(), () -> axiomPlayers.removeIf(player -> {
if (!player.hasPermission(AxiomPaper.PERMISSION)) {
OutChannel.ENABLE.send(player, new byte[] { 0 });
return true;
}
private final AxiomPlayerManager axiomPlayers;
return false;
}), 20, 20);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
axiomPlayers.remove(event.getPlayer());
public HelloPacketListener(AxiomPlayerManager axiomPlayers) {
this.axiomPlayers = axiomPlayers;
}
@Override
public void onMessage(Player player, ByteBuf buf) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
int apiVersion = friendlyByteBuf.readVarInt();
friendlyByteBuf.readNbt(); // Discard
int apiVersion = MojBuf.readVarInt(buf);
MojBuf.readNbt(buf); // Discard
if (apiVersion != AxiomConstants.API_VERSION) {
player.sendMessage(Component.text("Unsupported Axiom API Version. Server supports " + AxiomConstants.API_VERSION + ", while client is " + apiVersion));
@ -69,18 +51,24 @@ public class HelloPacketListener implements AxiomPacketListener, Listener {
}
axiomPlayers.add(player);
//TODO thread safety
((CraftPlayer)player).getHandle().connection.connection.channel.pipeline().addBefore("decoder", "axiom-big-payload-handler", new AxiomBigPayloadHandler(player));
// Welcome to multiversioning!
channel.get(
packetListenerConnection.get(
serverPlayerConnection.get(
AxiomPaper.convert(player)
)
)
).pipeline().addBefore("decoder", "axiom-big-payload-handler", new AxiomBigPayloadHandler(player));
// Enable
FriendlyByteBuf out = new FriendlyByteBuf(Unpooled.buffer());
ByteBuf out = MojBuf.unpooled();
out.writeBoolean(true);
out.writeByte(0); // todo: world properties
out.writeInt(handshakeEvent.getMaxBufferSize()); // Max Buffer Size
out.writeBoolean(false); // No source info
out.writeBoolean(false); // No source settings
out.writeVarInt(5); // Maximum Reach
out.writeVarInt(16); // Max editor views
MojBuf.writeVarInt(buf, 5); // Maximum Reach
MojBuf.writeVarInt(buf, 16); // Max editor views
out.writeBoolean(true); // Editable Views
OutChannel.ENABLE.send(player, out);
@ -89,15 +77,15 @@ public class HelloPacketListener implements AxiomPacketListener, Listener {
int activeHotbarIndex = container.getOrDefault(AxiomConstants.ACTIVE_HOTBAR_INDEX, PersistentDataType.BYTE, (byte) 0);
PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER);
if (hotbarItems != null) {
out = new FriendlyByteBuf(Unpooled.buffer());
out = MojBuf.unpooled();
out.writeByte((byte) activeHotbarIndex);
for (int i=0; i<9*9; i++) {
// Ignore selected hotbar
if (i / 9 == activeHotbarIndex) {
out.writeItem(net.minecraft.world.item.ItemStack.EMPTY);
MojBuf.writeItem(out, new ItemStack(Material.AIR, 0));
} else {
ItemStack stack = hotbarItems.get(new NamespacedKey("axiom", "slot_"+i), ItemStackDataType.INSTANCE);
out.writeItem(CraftItemStack.asNMSCopy(stack));
ItemStack stack = hotbarItems.get(AxiomConstants.axiomKey("slot_"+i), ItemStackDataType.INSTANCE);
MojBuf.writeItem(out, stack);
}
}
OutChannel.INITIALIZE_HOTBARS.send(player, out);
@ -106,11 +94,11 @@ public class HelloPacketListener implements AxiomPacketListener, Listener {
// Initialize Views
UUID activeView = container.get(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE);
if (activeView != null) {
out = new FriendlyByteBuf(Unpooled.buffer());
out.writeUUID(activeView);
out = MojBuf.unpooled();
MojBuf.writeUUID(buf, activeView);
PersistentDataContainer[] views = container.get(AxiomConstants.VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY);
out.writeVarInt(views.length);
MojBuf.writeVarInt(out, views.length);
for (PersistentDataContainer view : views) {
View.load(view).write(out);
}

Datei anzeigen

@ -1,22 +1,19 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.OutChannel;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.buffer.CompressedBlockEntity;
import com.moulberry.axiom.buffer.MojBuf;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@ -24,50 +21,43 @@ import java.io.ByteArrayOutputStream;
public class RequestBlockEntityPacketListener implements AxiomPacketListener {
private static final Class<World> CraftWorld = Reflection.getClass("org.bukkit.craftbukkit.CraftWorld");
private static final Reflection.Method<World, ServerLevel> getHandle = Reflection.getTypedMethod(CraftWorld, ServerLevel.class);
private static final Reflection.Method<BlockPos, BlockPos> of = Reflection.getTypedMethod(BlockPos.class, BlockPos.class, long.class);
private static final Reflection.Method<ServerLevel, BlockEntity> getBlockEntity = Reflection.getTypedMethod(ServerLevel.class, BlockEntity.class, BlockPos.class);
private static final Reflection.Method<BlockEntity, CompoundTag> saveWithoutMetadata = Reflection.getTypedMethod(BlockEntity.class, CompoundTag.class, 2);
@Override
public void onMessage(@NotNull Player bukkitPlayer, @NotNull ByteBuf buf) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
long id = friendlyByteBuf.readLong();
long id = buf.readLong();
if (!bukkitPlayer.hasPermission("axiom.*")) {
// We always send an 'empty' response in order to make the client happy
sendEmptyResponse(bukkitPlayer, id); //TODO
return;
}
ServerPlayer player = ((CraftPlayer)bukkitPlayer).getHandle();
MinecraftServer server = player.getServer();
if (server == null) {
sendEmptyResponse(bukkitPlayer, id);
return;
}
ResourceKey<Level> worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
ServerLevel level = server.getLevel(worldKey);
if (level == null) {
World world = Bukkit.getWorld(MojBuf.readKey(buf));
if (world == null) {
sendEmptyResponse(bukkitPlayer, id);
return;
}
ServerLevel level = getHandle.invoke(world);
Long2ObjectMap<CompressedBlockEntity> map = new Long2ObjectOpenHashMap<>();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
// Save and compress block entities
int count = friendlyByteBuf.readVarInt();
int count = MojBuf.readVarInt(buf);
for (int i = 0; i < count; i++) {
long pos = friendlyByteBuf.readLong();
BlockEntity blockEntity = level.getBlockEntity(mutableBlockPos.set(pos));
long pos = buf.readLong();
BlockEntity blockEntity = getBlockEntity.invoke(level, of.invoke(null, pos));
if (blockEntity != null) {
CompoundTag tag = blockEntity.saveWithoutMetadata();
CompoundTag tag = saveWithoutMetadata.invoke(blockEntity);
map.put(pos, CompressedBlockEntity.compress(tag, baos));
}
}
// Send response packet
FriendlyByteBuf out = new FriendlyByteBuf(Unpooled.buffer(16));
ByteBuf out = MojBuf.unpooled();
out.writeLong(id);
out.writeVarInt(map.size());
MojBuf.writeVarInt(buf, map.size());
for (Long2ObjectMap.Entry<CompressedBlockEntity> entry : map.long2ObjectEntrySet()) {
out.writeLong(entry.getLongKey());
entry.getValue().write(out);
@ -75,6 +65,11 @@ public class RequestBlockEntityPacketListener implements AxiomPacketListener {
OutChannel.BLOCK_ENTITIES.send(bukkitPlayer, out);
}
@Override
public void onMissingPerm(Player player, ByteBuf buf) {
sendEmptyResponse(player, buf.readLong());
}
private void sendEmptyResponse(Player player, long id) {
ByteBuf buf = Unpooled.buffer(16);
buf.writeLong(id);

Datei anzeigen

@ -1,8 +1,10 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.buffer.BiomeBuffer;
import com.moulberry.axiom.buffer.BlockBuffer;
import com.moulberry.axiom.buffer.CompressedBlockEntity;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.event.AxiomModifyWorldEvent;
import com.moulberry.axiom.integration.RegionProtection;
import io.netty.buffer.ByteBuf;
@ -13,7 +15,6 @@ import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundChunksBiomesPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
@ -35,49 +36,31 @@ import net.minecraft.world.level.lighting.LightEngine;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.entity.Player;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
public class SetBlockBufferPacketListener implements AxiomPacketListener {
private final Method updateBlockEntityTicker;
public SetBlockBufferPacketListener() {
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class);
try {
this.updateBlockEntityTicker = LevelChunk.class.getDeclaredMethod(methodName, BlockEntity.class);
this.updateBlockEntityTicker.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private static final Reflection.Method<LevelChunk, Object> updateBlockEntityTicker = Reflection.getMethod(LevelChunk.class, BlockEntity.class);
@Override
public void onMessage(Player player, ByteBuf buf) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
MinecraftServer server = ((CraftServer)player.getServer()).getServer();
if (server == null) return;
ResourceKey<Level> worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
friendlyByteBuf.readUUID(); // Discard, we don't need to associate buffers
boolean continuation = friendlyByteBuf.readBoolean();
ResourceKey<Level> worldKey = buf.readResourceKey(Registries.DIMENSION);
MojBuf.readUUID(buf); // Discard, we don't need to associate buffers
if (!continuation) {
friendlyByteBuf.readNbt(); // Discard sourceInfo
if (!buf.readBoolean()) {
MojBuf.readNbt(buf); // Discard sourceInfo
}
byte type = friendlyByteBuf.readByte();
byte type = buf.readByte();
if (type == 0) {
BlockBuffer buffer = BlockBuffer.load(friendlyByteBuf);
BlockBuffer buffer = BlockBuffer.load(buf);
applyBlockBuffer(player, server, buffer, worldKey);
} else if (type == 1) {
BiomeBuffer buffer = BiomeBuffer.load(friendlyByteBuf);
BiomeBuffer buffer = BiomeBuffer.load(buf);
applyBiomeBuffer(server, buffer, worldKey);
} else {
throw new RuntimeException("Unknown buffer type: " + type);
@ -186,11 +169,7 @@ public class SetBlockBufferPacketListener implements AxiomPacketListener {
// Block entity is here and the type is correct
blockEntity.setBlockState(blockState);
try {
this.updateBlockEntityTicker.invoke(chunk, blockEntity);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
updateBlockEntityTicker.invoke(chunk, blockEntity);
} else {
// Block entity type isn't correct, we need to recreate it
chunk.removeBlockEntity(blockPos);

Datei anzeigen

@ -1,11 +1,11 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.event.AxiomModifyWorldEvent;
import com.moulberry.axiom.integration.VersionTranslator;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.block.Block;
@ -20,28 +20,12 @@ import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
public class SetBlockPacketListener implements AxiomPacketListener {
private final Method updateBlockEntityTicker;
public SetBlockPacketListener() {
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class);
try {
this.updateBlockEntityTicker = LevelChunk.class.getDeclaredMethod(methodName, BlockEntity.class);
this.updateBlockEntityTicker.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private static final Reflection.Method<LevelChunk, Object> updateBlockEntityTicker = Reflection.getMethod(LevelChunk.class, BlockEntity.class);
@Override
public void onMessage(@NotNull Player bukkitPlayer, @NotNull ByteBuf buf) {
@ -51,11 +35,10 @@ public class SetBlockPacketListener implements AxiomPacketListener {
if (modifyWorldEvent.isCancelled()) return;
// Read packet
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
BlockPos blockPos = friendlyByteBuf.readBlockPos();
BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(VersionTranslator.impl.blockStateMapper(bukkitPlayer).apply(friendlyByteBuf.readVarInt()));
boolean updateNeighbors = friendlyByteBuf.readBoolean();
int sequenceId = friendlyByteBuf.readInt();
BlockPos blockPos = buf.readBlockPos();
BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(VersionTranslator.impl.blockStateMapper(bukkitPlayer).apply(buf.readVarInt()));
boolean updateNeighbors = buf.readBoolean();
int sequenceId = buf.readInt();
ServerPlayer player = ((CraftPlayer)bukkitPlayer).getHandle();
@ -116,11 +99,7 @@ public class SetBlockPacketListener implements AxiomPacketListener {
// Just update the state and ticker and move on
blockEntity.setBlockState(blockState);
try {
this.updateBlockEntityTicker.invoke(chunk, blockEntity);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
updateBlockEntityTicker.invoke(chunk, blockEntity);
} else {
// Block entity type isn't correct, we need to recreate it
chunk.removeBlockEntity(blockPos);

Datei anzeigen

@ -1,10 +1,10 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.View;
import com.moulberry.axiom.persistence.UUIDDataType;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
@ -17,9 +17,8 @@ public class SetEditorViewsPacketListener implements AxiomPacketListener {
@Override
public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
UUID uuid = friendlyByteBuf.readUUID();
List<View> views = friendlyByteBuf.readList(View::read);
UUID uuid = MojBuf.readUUID(buf);
List<View> views = MojBuf.readList(buf, View::read);
PersistentDataContainer container = player.getPersistentDataContainer();
container.set(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE, uuid);

Datei anzeigen

@ -1,14 +1,20 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.event.AxiomFlySpeedChangeEvent;
import io.netty.buffer.ByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Abilities;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class SetFlySpeedPacketListener implements AxiomPacketListener {
private static final Reflection.Method<ServerPlayer, Abilities> getAbilities = Reflection.getTypedMethod(ServerPlayer.class, Abilities.class);
private static final Reflection.Field<Abilities, Float> flyingSpeed = Reflection.getField(Abilities.class, float.class);
@Override
public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
float flySpeed = buf.readFloat();
@ -19,7 +25,7 @@ public class SetFlySpeedPacketListener implements AxiomPacketListener {
if (flySpeedChangeEvent.isCancelled()) return;
// Change flying speed
((CraftPlayer)player).getHandle().getAbilities().setFlyingSpeed(flySpeed);
flyingSpeed.set(getAbilities.invoke(AxiomPaper.convert(player)), flySpeed);
}
}

Datei anzeigen

@ -1,12 +1,11 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.persistence.ItemStackDataType;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
@ -15,15 +14,14 @@ public class SetHotbarSlotPacketListener implements AxiomPacketListener {
@Override
public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
int index = friendlyByteBuf.readByte();
int index = buf.readByte();
if (index < 0 || index >= 9*9) return;
net.minecraft.world.item.ItemStack nmsStack = friendlyByteBuf.readItem();
ItemStack stack = MojBuf.readItem(buf);
PersistentDataContainer container = player.getPersistentDataContainer();
PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER);
if (hotbarItems == null) hotbarItems = container.getAdapterContext().newPersistentDataContainer();
hotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, CraftItemStack.asCraftMirror(nmsStack));
hotbarItems.set(AxiomConstants.axiomKey("slot_"+index), ItemStackDataType.INSTANCE, stack);
container.set(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER, hotbarItems);
}

Datei anzeigen

@ -1,13 +1,11 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.persistence.ItemStackDataType;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
@ -18,13 +16,12 @@ public class SwitchActiveHotbarPacketListener implements AxiomPacketListener {
@Override
public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
int oldHotbarIndex = friendlyByteBuf.readByte();
int activeHotbarIndex = friendlyByteBuf.readByte();
int oldHotbarIndex = buf.readByte();
int activeHotbarIndex = buf.readByte();
ItemStack[] hotbarItems = new ItemStack[9];
for (int i=0; i<9; i++) {
hotbarItems[i] = CraftItemStack.asCraftMirror(friendlyByteBuf.readItem());
hotbarItems[i] = MojBuf.readItem(buf);
}
PersistentDataContainer container = player.getPersistentDataContainer();
@ -40,10 +37,10 @@ public class SwitchActiveHotbarPacketListener implements AxiomPacketListener {
} else {
stack = stack.clone();
}
containerHotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, stack);
containerHotbarItems.set(AxiomConstants.axiomKey("slot_"+index), ItemStackDataType.INSTANCE, stack);
}
int index = activeHotbarIndex*9 + i;
containerHotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, hotbarItems[i].clone());
containerHotbarItems.set(AxiomConstants.axiomKey("slot_"+index), ItemStackDataType.INSTANCE, hotbarItems[i].clone());
if (player.getGameMode() == GameMode.CREATIVE) player.getInventory().setItem(i, hotbarItems[i]);
}

Datei anzeigen

@ -1,14 +1,10 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.event.AxiomTeleportEvent;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@ -16,17 +12,14 @@ import org.jetbrains.annotations.NotNull;
public class TeleportPacketListener implements AxiomPacketListener {
@Override
public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
ResourceKey<Level> resourceKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
double x = friendlyByteBuf.readDouble();
double y = friendlyByteBuf.readDouble();
double z = friendlyByteBuf.readDouble();
float yRot = friendlyByteBuf.readFloat();
float xRot = friendlyByteBuf.readFloat();
public void onMessage(@NotNull Player player, @NotNull FriendlyByteBuf buf) {
World world = Bukkit.getWorld(MojBuf.readKey(buf));
double x = buf.readDouble();
double y = buf.readDouble();
double z = buf.readDouble();
float yRot = buf.readFloat();
float xRot = buf.readFloat();
NamespacedKey namespacedKey = new NamespacedKey(resourceKey.location().getNamespace(), resourceKey.location().getPath());
World world = Bukkit.getWorld(namespacedKey);
if (world == null) return;
// Call event

Datei anzeigen

@ -1,8 +1,8 @@
package com.moulberry.axiom.persistence;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.buffer.MojBuf;
import net.minecraft.nbt.CompoundTag;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_R1.persistence.CraftPersistentDataContainer;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataAdapterContext;
import org.bukkit.persistence.PersistentDataContainer;
@ -10,9 +10,9 @@ import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
public class ItemStackDataType implements PersistentDataType<PersistentDataContainer, ItemStack> {
public static ItemStackDataType INSTANCE = new ItemStackDataType();
private ItemStackDataType() {
}
private ItemStackDataType() {}
public static final ItemStackDataType INSTANCE = new ItemStackDataType();
@Override
public @NotNull Class<PersistentDataContainer> getPrimitiveType() {
@ -24,23 +24,23 @@ public class ItemStackDataType implements PersistentDataType<PersistentDataConta
return ItemStack.class;
}
private static final Class<PersistentDataContainer> CraftPersistentDataContainer = Reflection.getClass("org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer");
private static final Reflection.Method<PersistentDataContainer, Object> putAll = Reflection.getMethod(CraftPersistentDataContainer, "putAll", CompoundTag.class);
private static final Reflection.Method<PersistentDataContainer, CompoundTag> toTagCompound = Reflection.getTypedMethod(CraftPersistentDataContainer, "toTagCompound", CompoundTag.class);
private static final Reflection.Method<net.minecraft.world.item.ItemStack, CompoundTag> save = Reflection.getTypedMethod(net.minecraft.world.item.ItemStack.class, CompoundTag.class, CompoundTag.class);
@Override
public @NotNull PersistentDataContainer toPrimitive(@NotNull ItemStack complex, @NotNull PersistentDataAdapterContext context) {
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(complex);
if (nmsStack == null) nmsStack = net.minecraft.world.item.ItemStack.EMPTY;
CompoundTag tag = new CompoundTag();
nmsStack.save(tag);
public @NotNull PersistentDataContainer toPrimitive(@NotNull ItemStack stack, @NotNull PersistentDataAdapterContext context) {
CompoundTag tag = save.invoke(MojBuf.toMojang(stack), new CompoundTag());
PersistentDataContainer container = context.newPersistentDataContainer();
((CraftPersistentDataContainer)container).putAll(tag);
putAll.invoke(container, tag);
return container;
}
private static final Reflection.Method<net.minecraft.world.item.ItemStack, net.minecraft.world.item.ItemStack> of = Reflection.getTypedMethod(net.minecraft.world.item.ItemStack.class, net.minecraft.world.item.ItemStack.class, CompoundTag.class);
@Override
public @NotNull ItemStack fromPrimitive(@NotNull PersistentDataContainer primitive, @NotNull PersistentDataAdapterContext context) {
CompoundTag tag = ((CraftPersistentDataContainer)primitive).toTagCompound();
net.minecraft.world.item.ItemStack nmsStack = net.minecraft.world.item.ItemStack.of(tag);
return CraftItemStack.asCraftMirror(nmsStack);
return MojBuf.toBukkit(of.invoke(null, toTagCompound.invoke(primitive)));
}
}

Datei anzeigen

@ -7,9 +7,9 @@ import java.nio.ByteBuffer;
import java.util.UUID;
public class UUIDDataType implements PersistentDataType<byte[], UUID> {
public static UUIDDataType INSTANCE = new UUIDDataType();
private UUIDDataType() {
}
private UUIDDataType() {}
public static final UUIDDataType INSTANCE = new UUIDDataType();
public Class<byte[]> getPrimitiveType() {
return byte[].class;