Commits vergleichen

...

29 Commits

Autor SHA1 Nachricht Datum
5846ba3e8c Fix ViaVersion order
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-15 17:57:09 +02:00
f3327c7f13 Fix RequestBlockEntityPacketListener writing
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-15 17:39:00 +02:00
9d3d8f7a63 Test inverse
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-15 17:18:24 +02:00
47ae33a92a Test inverse
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-15 17:17:34 +02:00
c98ca468fd Fix ViaVersion translation direction
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-15 17:02:24 +02:00
028448be8d Fix Version construction
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-15 16:37:03 +02:00
610ba248ea Fix 1.19.3 AxiomBigPayloadHandler injection
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-15 16:05:58 +02:00
162bfc8fe2 Add 1.19.3 compatibility layer
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-14 12:19:20 +02:00
6f5617ded3 Fix Biome coordinate translation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:52:20 +02:00
28823b19aa Fix Biome coordinate translation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:50:17 +02:00
a892c966a3 Reduce api-version
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:42:36 +02:00
2213d44289 Fix getBlockEntity args
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:40:44 +02:00
307fe08bd6 Fix negative chunk coordinate conversion
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:37:35 +02:00
5797d0e0be Fix getXYZ of Vec3i
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:33:08 +02:00
1045f35f26 Fix set block packet division
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:27:37 +02:00
4955631c49 Fix chunk y position
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:24:55 +02:00
3d32ddbfac Fix chunk y position
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 21:22:10 +02:00
59eed20542 Fix set_buffer
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 20:51:03 +02:00
15fa621147 Fix handler of unknown axiom channels
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 20:48:18 +02:00
yoyosource
af866deeac Fix HelloPacketListener using buf instead of out
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-13 20:00:50 +02:00
81edb4c970 Fix initial capacity
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-12 21:58:18 +02:00
e31d4e27d1 Fix channel lowercase
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-12 21:47:11 +02:00
a0367320c3 Full Multiversion (untested)
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-12 17:32:07 +02:00
8cfed96b39 Compilable (Small sections remaining)
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-09 10:36:10 +02:00
e9a2b15259 WIP Multiversion (Small sections remaining)
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-09-09 10:32:44 +02:00
6f44fad535 WIP Multiversion (SetBlock missing)
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-09-08 16:58:02 +02:00
7d82fed462 Refactor RegionProtection
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-07 16:21:13 +02:00
35c4443996 Remove unused
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-07 16:01:39 +02:00
b8fa0aa640 WIP multiversioning
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-07 15:48:53 +02:00
40 geänderte Dateien mit 1382 neuen und 1269 gelöschten Zeilen

Datei anzeigen

@ -18,15 +18,13 @@ java {
repositories { repositories {
mavenCentral() mavenCentral()
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://jitpack.io")
maven("https://maven.enginehub.org/repo/") maven("https://maven.enginehub.org/repo/")
maven("https://repo.papermc.io/repository/maven-public/") maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.viaversion.com")
} }
dependencies { dependencies {
paperweight.paperDevBundle("1.20.1-R0.1-SNAPSHOT") paperweight.paperDevBundle("1.20.1-R0.1-SNAPSHOT")
implementation("xyz.jpenilla:reflection-remapper:0.1.0-SNAPSHOT")
// Zstd Compression Library // Zstd Compression Library
implementation("com.github.luben:zstd-jni:1.5.5-4") implementation("com.github.luben:zstd-jni:1.5.5-4")
@ -38,6 +36,9 @@ dependencies {
implementation(platform("com.intellectualsites.bom:bom-newest:1.37")) implementation(platform("com.intellectualsites.bom:bom-newest:1.37"))
compileOnly("com.intellectualsites.plotsquared:plotsquared-core") compileOnly("com.intellectualsites.plotsquared:plotsquared-core")
compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false } compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false }
// ViaVersion support
compileOnly("com.viaversion:viaversion-api:4.7.0")
} }
tasks { tasks {

Datei anzeigen

@ -1,22 +1,22 @@
package com.moulberry.axiom; package com.moulberry.axiom;
import net.minecraft.core.BlockPos;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
public class AxiomConstants { public class AxiomConstants {
public static final long MIN_POSITION_LONG = BlockPos.asLong(-33554432, -2048, -33554432); public static final long MIN_POSITION_LONG = 0b1000000000000000000000000010000000000000000000000000100000000000L;
static {
if (MIN_POSITION_LONG != 0b1000000000000000000000000010000000000000000000000000100000000000L) {
throw new Error("BlockPos representation changed!");
}
}
public static final int API_VERSION = 5; 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 String PERMISSION = "axiom.*";
public static final NamespacedKey VIEWS = new NamespacedKey("axiom", "views");
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

@ -1,105 +1,129 @@
package com.moulberry.axiom; package com.moulberry.axiom;
import com.moulberry.axiom.buffer.CompressedBlockEntity; import com.moulberry.axiom.integration.PaperFailMoveListener;
import com.moulberry.axiom.packet.*; import com.moulberry.axiom.packet.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.papermc.paper.event.player.PlayerFailMoveEvent; import io.papermc.paper.event.player.PlayerFailMoveEvent;
import io.papermc.paper.network.ChannelInitializeListener; import net.minecraft.core.BlockPos;
import io.papermc.paper.network.ChannelInitializeListenerHolder;
import net.kyori.adventure.key.Key;
import net.minecraft.network.Connection;
import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow; import net.minecraft.server.level.*;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket; import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.bukkit.*; import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.messaging.Messenger;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.*; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
public class AxiomPaper extends JavaPlugin implements Listener { public class AxiomPaper extends JavaPlugin implements Listener {
@Override private static AxiomPaper instance;
public void onEnable() { public static Plugin getPlugin() {
Bukkit.getPluginManager().registerEvents(this, this); return instance;
CompressedBlockEntity.initialize(this);
Messenger msg = Bukkit.getMessenger();
msg.registerOutgoingPluginChannel(this, "axiom:enable");
msg.registerOutgoingPluginChannel(this, "axiom:initialize_hotbars");
msg.registerOutgoingPluginChannel(this, "axiom:set_editor_views");
msg.registerOutgoingPluginChannel(this, "axiom:block_entities");
final Set<UUID> activeAxiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>());
msg.registerIncomingPluginChannel(this, "axiom:hello", new HelloPacketListener(this, activeAxiomPlayers));
msg.registerIncomingPluginChannel(this, "axiom:set_gamemode", new SetGamemodePacketListener());
msg.registerIncomingPluginChannel(this, "axiom:set_fly_speed", new SetFlySpeedPacketListener());
msg.registerIncomingPluginChannel(this, "axiom:set_block", new SetBlockPacketListener(this));
msg.registerIncomingPluginChannel(this, "axiom:set_hotbar_slot", new SetHotbarSlotPacketListener());
msg.registerIncomingPluginChannel(this, "axiom:switch_active_hotbar", new SwitchActiveHotbarPacketListener());
msg.registerIncomingPluginChannel(this, "axiom:teleport", new TeleportPacketListener());
msg.registerIncomingPluginChannel(this, "axiom:set_editor_views", new SetEditorViewsPacketListener());
msg.registerIncomingPluginChannel(this, "axiom:request_block_entity", new RequestBlockEntityPacketListener(this));
SetBlockBufferPacketListener setBlockBufferPacketListener = new SetBlockBufferPacketListener(this);
ChannelInitializeListenerHolder.addListener(Key.key("axiom:handle_big_payload"), new ChannelInitializeListener() {
@Override
public void afterInitChannel(@NonNull Channel channel) {
var packets = ConnectionProtocol.PLAY.getPacketsByIds(PacketFlow.SERVERBOUND);
int payloadId = -1;
for (Map.Entry<Integer, Class<? extends Packet<?>>> entry : packets.entrySet()) {
if (entry.getValue() == ServerboundCustomPayloadPacket.class) {
payloadId = entry.getKey();
break;
}
}
if (payloadId < 0) {
throw new RuntimeException("Failed to find ServerboundCustomPayloadPacket id");
}
Connection connection = (Connection) channel.pipeline().get("packet_handler");
channel.pipeline().addBefore("decoder", "axiom-big-payload-handler",
new AxiomBigPayloadHandler(payloadId, connection, setBlockBufferPacketListener));
}
});
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> {
HashSet<UUID> newActiveAxiomPlayers = new HashSet<>();
for (Player player : Bukkit.getServer().getOnlinePlayers()) {
if (activeAxiomPlayers.contains(player.getUniqueId())) {
if (!player.hasPermission("axiom.*")) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeBoolean(false);
player.sendPluginMessage(this, "axiom:enable", buf.accessByteBufWithCorrectSize());
} else {
newActiveAxiomPlayers.add(player.getUniqueId());
}
}
}
activeAxiomPlayers.clear();
activeAxiomPlayers.addAll(newActiveAxiomPlayers);
}, 20, 20);
} }
@EventHandler private static final Map<String, AxiomPacketListener> listeners = new ConcurrentHashMap<>();
public void onFailMove(PlayerFailMoveEvent event) { public static AxiomPacketListener getListener(String channel) {
if (event.getPlayer().hasPermission("axiom.*") && return listeners.get(channel);
event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) { }
event.setAllowed(true);
private static final Class<Player> CraftPlayer = Reflection.getClass("org.bukkit.craftbukkit.entity.CraftPlayer");
private static final Reflection.Method<Player, ServerPlayer> playerGetHandle = Reflection.getTypedMethod(CraftPlayer, "getHandle", ServerPlayer.class);
public static ServerPlayer convert(Player player) {
return playerGetHandle.invoke(player);
}
private static final Class<World> CraftWorld = Reflection.getClass("org.bukkit.craftbukkit.CraftWorld");
private static final Reflection.Method<World, ServerLevel> worldGetHandle = Reflection.getTypedMethod(CraftWorld, "getHandle", ServerLevel.class);
public static ServerLevel convert(World world) {
return worldGetHandle.invoke(world);
}
private static final Reflection.Method<Level, LevelChunk> getChunk = Reflection.getTypedMethod(Level.class, LevelChunk.class, int.class, int.class);
public static LevelChunk getChunk(Level level, int cx, int cz) {
return getChunk.invoke(level, cx, cz);
}
private static final Reflection.Method<BlockPos, BlockPos> of = Reflection.getTypedMethod(BlockPos.class, BlockPos.class, long.class);
public static BlockPos convert(long packed) {
return of.invoke(null, packed);
}
private static final Reflection.Field<ServerPlayer, ServerGamePacketListenerImpl> serverPlayerConnection = Reflection.getField(ServerPlayer.class, ServerGamePacketListenerImpl.class);
public static ServerGamePacketListenerImpl getConnection(ServerPlayer player) {
return serverPlayerConnection.get(player);
}
private static final Reflection.Method<ServerLevel, ServerChunkCache> getChunkSource = Reflection.getTypedMethod(ServerLevel.class, ServerChunkCache.class);
public static ServerChunkCache getChunkSource(ServerLevel level) {
return getChunkSource.invoke(level);
}
private static final Reflection.Method<ServerChunkCache, ThreadedLevelLightEngine> getLightEngine = Reflection.getTypedMethod(ServerChunkCache.class, ThreadedLevelLightEngine.class);
public static ThreadedLevelLightEngine getLightEngine(ServerChunkCache chunkSource) {
return getLightEngine.invoke(chunkSource);
}
private static final Reflection.Method<ServerPlayerConnection, Void> send = Reflection.getMethod(ServerPlayerConnection.class, Packet.class);
public static void sendPacket(ServerPlayer player, Packet<?> packet) {
send.invoke(AxiomPaper.getConnection(player), packet);
}
private static final Reflection.Field<ServerChunkCache, ChunkMap> chunkMap = Reflection.getField(ServerChunkCache.class, ChunkMap.class);
private static final Reflection.Method<ChunkMap, List> getPlayers = Reflection.getTypedMethod(ChunkMap.class, List.class, ChunkPos.class, boolean.class);
public static List<ServerPlayer> getPlayersSeeingChunk(ServerLevel level, ChunkPos pos) {
ChunkMap map = chunkMap.get(AxiomPaper.getChunkSource(level));
return (List<ServerPlayer>)getPlayers.invoke(map, pos, false);
}
@Override
public void onEnable() {
instance = this;
try {
PlayerFailMoveEvent.class.getName(); //Try load class
Bukkit.getPluginManager().registerEvents(new PaperFailMoveListener(), this);
} catch (NoClassDefFoundError e) {
getLogger().log(java.util.logging.Level.INFO, "Axiom players may move too quickly according to the server. Use a current Paper version or increase the 'moved-too-quickly-multiplier' in spigot.yml.");
} }
for(OutChannel channel : OutChannel.values()) {
Bukkit.getMessenger().registerOutgoingPluginChannel(this, channel.getChannel());
}
registerInChannel("hello", new HelloPacketListener(new AxiomPlayerManager()));
registerInChannel("set_gamemode", new SetGamemodePacketListener());
registerInChannel("set_fly_speed", new SetFlySpeedPacketListener());
registerInChannel("set_block", new SetBlockPacketListener());
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("set_buffer", new SetBlockBufferPacketListener());
}
private void registerInChannel(String channel, AxiomPacketListener handler) {
listeners.put(channel, handler);
Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:" + channel, (ch, player, message) -> {
ByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
if (!player.hasPermission(AxiomConstants.PERMISSION)) {
handler.onMissingPerm(player, buf);
return;
}
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,32 @@
package com.moulberry.axiom;
import io.netty.buffer.ByteBuf;
import org.bukkit.entity.Player;
public enum OutChannel {
ENABLE,
INITIALIZE_HOTBARS,
SET_EDITOR_VIEWS,
BLOCK_ENTITIES;
private final String channel;
OutChannel() {
channel = "axiom:" + name().toLowerCase();
}
public String getChannel() {
return channel;
}
public void send(Player player, ByteBuf buf) {
// trustin https://stackoverflow.com/a/19309312 CC BY-SA 3.0
byte[] array = new byte[buf.readableBytes()];
buf.getBytes(buf.readerIndex(), array);
send(player, array);
}
public void send(Player player, byte[] array) {
player.sendPluginMessage(AxiomPaper.getPlugin(), channel, array);
}
}

Datei anzeigen

@ -0,0 +1,169 @@
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, Void> getMethod(Class<T> clazz, Class<?>... params) {
return getTypedMethod(clazz, null, Void.TYPE, 0, params);
}
public static <T> Method<T, Void> getMethod(Class<T> clazz, int index, Class<?>... params) {
return getTypedMethod(clazz, null, Void.TYPE, index, params);
}
public static <T> Method<T, Void> getMethod(Class<T> clazz, String methodName, Class<?>... params) {
return getTypedMethod(clazz, methodName, Void.TYPE, 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)));
}
public interface Constructor<T> {
T newInstance(Object... arguments);
}
public static <T> Constructor<T> getConstructor(Class<T> clazz, Class<?>... params) {
try {
java.lang.reflect.Constructor<T> constructor = clazz.getDeclaredConstructor(params);
constructor.setAccessible(true);
return arguments -> {
try {
return constructor.newInstance(arguments);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalArgumentException("Cannot invoke constructor " + constructor, e);
}
};
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Cannot find matching constructor");
}
}
private static final String ORG_BUKKIT_CRAFTBUKKIT = Bukkit.getServer().getClass().getPackage().getName();
public static final int VERSION; // Format: 2 digit minor version, 2 digit CraftBukkit revision: eg. 2001 for v1_20_R1
static {
String[] version = ORG_BUKKIT_CRAFTBUKKIT.substring(ORG_BUKKIT_CRAFTBUKKIT.lastIndexOf('.')).split("_");
VERSION = Integer.parseInt(version[1]) * 100 + Integer.parseInt(version[2].substring(1));
}
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,15 +1,12 @@
package com.moulberry.axiom; package com.moulberry.axiom;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.persistence.UUIDDataType; import com.moulberry.axiom.persistence.UUIDDataType;
import net.minecraft.core.registries.Registries; import io.netty.buffer.ByteBuf;
import net.minecraft.nbt.CompoundTag; import org.bukkit.Bukkit;
import net.minecraft.nbt.Tag; import org.bukkit.Location;
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 org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
@ -17,73 +14,76 @@ import java.util.UUID;
public final class View { public final class View {
public String name; private final String name;
public final UUID uuid; private final UUID uuid;
public boolean pinLevel = false; private boolean pinLevel = false;
public boolean pinLocation = false; private boolean pinLocation = false;
private ResourceKey<Level> level = null; private World level = null;
private Vec3 position = null; private Location location = null;
private float yaw;
private float pitch;
public View(String name, UUID uuid) { public View(String name, UUID uuid) {
this.name = name; this.name = name;
this.uuid = uuid; this.uuid = uuid;
} }
public void write(FriendlyByteBuf byteBuf) { public void write(ByteBuf buf) {
byteBuf.writeUtf(this.name, 64); MojBuf.writeUtf(buf, this.name, 64);
byteBuf.writeUUID(this.uuid); MojBuf.writeUUID(buf, this.uuid);
byteBuf.writeBoolean(this.pinLevel); buf.writeBoolean(this.pinLevel);
if (this.pinLevel && this.level != null) { if (this.pinLevel && this.level != null) {
byteBuf.writeBoolean(true); buf.writeBoolean(true);
byteBuf.writeResourceKey(this.level); MojBuf.writeKey(buf, this.level.getKey());
} else { } else {
byteBuf.writeBoolean(false); buf.writeBoolean(false);
} }
byteBuf.writeBoolean(this.pinLocation); buf.writeBoolean(this.pinLocation);
if (this.position != null) { if (this.location != null) {
byteBuf.writeBoolean(true); buf.writeBoolean(true);
byteBuf.writeDouble(this.position.x); buf.writeDouble(this.location.getX());
byteBuf.writeDouble(this.position.y); buf.writeDouble(this.location.getY());
byteBuf.writeDouble(this.position.z); buf.writeDouble(this.location.getZ());
byteBuf.writeFloat(this.yaw); buf.writeFloat(this.location.getYaw());
byteBuf.writeFloat(this.pitch); buf.writeFloat(this.location.getPitch());
} else { } else {
byteBuf.writeBoolean(false); buf.writeBoolean(false);
} }
} }
public static View read(FriendlyByteBuf byteBuf) { public static View read(ByteBuf buf) {
View view = new View(byteBuf.readUtf(64), byteBuf.readUUID()); View view = new View(MojBuf.readUtf(buf, 64), MojBuf.readUUID(buf));
view.pinLevel = byteBuf.readBoolean(); view.pinLevel = buf.readBoolean();
if (byteBuf.readBoolean()) { if (buf.readBoolean()) {
view.level = byteBuf.readResourceKey(Registries.DIMENSION); view.level = Bukkit.getWorld(MojBuf.readKey(buf));
} }
view.pinLocation = byteBuf.readBoolean(); view.pinLocation = buf.readBoolean();
if (byteBuf.readBoolean()) { if (buf.readBoolean()) {
view.position = new Vec3(byteBuf.readDouble(), byteBuf.readDouble(), byteBuf.readDouble()); view.location = new Location(view.level,
view.yaw = byteBuf.readFloat(); buf.readDouble(),
view.pitch = byteBuf.readFloat(); buf.readDouble(),
buf.readDouble(),
buf.readFloat(),
buf.readFloat()
);
} }
return view; 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 NAME_KEY = AxiomConstants.axiomKey("view_name");
private static final NamespacedKey PIN_LEVEL_KEY = new NamespacedKey("axiom", "view_pin_level"); private static final NamespacedKey UUID_KEY = AxiomConstants.axiomKey("view_uuid");
private static final NamespacedKey LEVEL_KEY = new NamespacedKey("axiom", "view_level"); private static final NamespacedKey PIN_LEVEL_KEY = AxiomConstants.axiomKey("view_pin_level");
private static final NamespacedKey PIN_LOCATION_KEY = new NamespacedKey("axiom", "view_pin_location"); private static final NamespacedKey LEVEL_KEY = AxiomConstants.axiomKey("view_level");
private static final NamespacedKey X_KEY = new NamespacedKey("axiom", "view_x"); private static final NamespacedKey PIN_LOCATION_KEY = AxiomConstants.axiomKey("view_pin_location");
private static final NamespacedKey Y_KEY = new NamespacedKey("axiom", "view_y"); private static final NamespacedKey X_KEY = AxiomConstants.axiomKey("view_x");
private static final NamespacedKey Z_KEY = new NamespacedKey("axiom", "view_z"); private static final NamespacedKey Y_KEY = AxiomConstants.axiomKey("view_y");
private static final NamespacedKey YAW_KEY = new NamespacedKey("axiom", "view_yaw"); private static final NamespacedKey Z_KEY = AxiomConstants.axiomKey("view_z");
private static final NamespacedKey PITCH_KEY = new NamespacedKey("axiom", "view_pitch"); 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) { public void save(PersistentDataContainer container) {
container.set(NAME_KEY, PersistentDataType.STRING, this.name); container.set(NAME_KEY, PersistentDataType.STRING, this.name);
@ -91,16 +91,16 @@ public final class View {
container.set(PIN_LEVEL_KEY, PersistentDataType.BOOLEAN, this.pinLevel); container.set(PIN_LEVEL_KEY, PersistentDataType.BOOLEAN, this.pinLevel);
if (this.pinLevel && this.level != null) { 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); container.set(PIN_LOCATION_KEY, PersistentDataType.BOOLEAN, this.pinLocation);
if (this.position != null) { if (this.location != null) {
container.set(X_KEY, PersistentDataType.DOUBLE, this.position.x); container.set(X_KEY, PersistentDataType.DOUBLE, this.location.getX());
container.set(Y_KEY, PersistentDataType.DOUBLE, this.position.y); container.set(Y_KEY, PersistentDataType.DOUBLE, this.location.getY());
container.set(Z_KEY, PersistentDataType.DOUBLE, this.position.z); container.set(Z_KEY, PersistentDataType.DOUBLE, this.location.getZ());
container.set(YAW_KEY, PersistentDataType.FLOAT, this.yaw); container.set(YAW_KEY, PersistentDataType.FLOAT, this.location.getYaw());
container.set(PITCH_KEY, PersistentDataType.FLOAT, this.pitch); container.set(PITCH_KEY, PersistentDataType.FLOAT, this.location.getPitch());
} }
} }
@ -113,17 +113,18 @@ public final class View {
view.pinLevel = tag.getOrDefault(PIN_LEVEL_KEY, PersistentDataType.BOOLEAN, false); view.pinLevel = tag.getOrDefault(PIN_LEVEL_KEY, PersistentDataType.BOOLEAN, false);
if (tag.has(LEVEL_KEY)) { if (tag.has(LEVEL_KEY)) {
String level = tag.get(LEVEL_KEY, PersistentDataType.STRING); 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); view.pinLocation = tag.getOrDefault(PIN_LOCATION_KEY, PersistentDataType.BOOLEAN, false);
if (tag.has(X_KEY) && tag.has(Y_KEY) && tag.has(Z_KEY)) { if (tag.has(X_KEY) && tag.has(Y_KEY) && tag.has(Z_KEY)) {
double x = tag.getOrDefault(X_KEY, PersistentDataType.DOUBLE, 0.0); view.location = new Location(view.level,
double y = tag.getOrDefault(Y_KEY, PersistentDataType.DOUBLE, 0.0); tag.getOrDefault(X_KEY, PersistentDataType.DOUBLE, 0.0),
double z = tag.getOrDefault(Z_KEY, PersistentDataType.DOUBLE, 0.0); tag.getOrDefault(Y_KEY, PersistentDataType.DOUBLE, 0.0),
view.position = new Vec3(x, y, z); tag.getOrDefault(Z_KEY, PersistentDataType.DOUBLE, 0.0),
view.yaw = tag.getOrDefault(YAW_KEY, PersistentDataType.FLOAT, 0.0f); tag.getOrDefault(YAW_KEY, PersistentDataType.FLOAT, 0.0f),
view.pitch = tag.getOrDefault(PITCH_KEY, PersistentDataType.FLOAT, 0.0f); tag.getOrDefault(PITCH_KEY, PersistentDataType.FLOAT, 0.0f)
);
} }
return view; return view;

Datei anzeigen

@ -1,88 +1,26 @@
package com.moulberry.axiom.buffer; package com.moulberry.axiom.buffer;
import it.unimi.dsi.fastutil.objects.Object2ByteMap; import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; import org.bukkit.block.Biome;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
public class BiomeBuffer { public class BiomeBuffer {
private final Position2ByteMap map; private final Position2ByteMap map;
private final ResourceKey<Biome>[] palette; private final Biome[] palette;
private final Object2ByteMap<ResourceKey<Biome>> paletteReverse;
private int paletteSize = 0;
public BiomeBuffer() { public BiomeBuffer(ByteBuf buf) {
this.map = new Position2ByteMap(); palette = new Biome[buf.readByte()];
this.palette = new ResourceKey[255]; for (int i = 0; i < palette.length; i++) {
this.paletteReverse = new Object2ByteOpenHashMap<>(); palette[i] = Biome.valueOf(MojBuf.readKey(buf).getKey().toUpperCase());
}
private BiomeBuffer(Position2ByteMap map, ResourceKey<Biome>[] palette, Object2ByteMap<ResourceKey<Biome>> paletteReverse) {
this.map = map;
this.palette = palette;
this.paletteReverse = paletteReverse;
this.paletteSize = this.paletteReverse.size();
}
public void save(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeByte(this.paletteSize);
for (int i = 0; i < this.paletteSize; i++) {
friendlyByteBuf.writeResourceKey(this.palette[i]);
} }
this.map.save(friendlyByteBuf);
map = Position2ByteMap.load(buf);
} }
public static BiomeBuffer load(FriendlyByteBuf friendlyByteBuf) { public void forEachEntry(PositionConsumer<Biome> consumer) {
int paletteSize = friendlyByteBuf.readByte();
ResourceKey<Biome>[] palette = new ResourceKey[255];
Object2ByteMap<ResourceKey<Biome>> paletteReverse = new Object2ByteOpenHashMap<>();
for (int i = 0; i < paletteSize; i++) {
ResourceKey<Biome> key = friendlyByteBuf.readResourceKey(Registries.BIOME);
palette[i] = key;
paletteReverse.put(key, (byte)(i+1));
}
Position2ByteMap map = Position2ByteMap.load(friendlyByteBuf);
return new BiomeBuffer(map, palette, paletteReverse);
}
public void clear() {
this.map.clear();
}
public void forEachEntry(PositionConsumer<ResourceKey<Biome>> consumer) {
this.map.forEachEntry((x, y, z, v) -> { this.map.forEachEntry((x, y, z, v) -> {
if (v != 0) consumer.accept(x, y, z, this.palette[(v & 0xFF) - 1]); if (v != 0) consumer.accept(x, y, z, this.palette[(v & 0xFF) - 1]);
}); });
} }
public ResourceKey<Biome> get(int quartX, int quartY, int quartZ) {
int index = this.map.get(quartX, quartY, quartZ) & 0xFF;
if (index == 0) return null;
return this.palette[index - 1];
}
private int getPaletteIndex(ResourceKey<Biome> biome) {
int index = this.paletteReverse.getOrDefault(biome, (byte) 0) & 0xFF;
if (index != 0) return index;
index = this.paletteSize;
if (index >= this.palette.length) {
throw new UnsupportedOperationException("Too many biomes! :(");
}
this.palette[index] = biome;
this.paletteReverse.put(biome, (byte)(index + 1));
this.paletteSize += 1;
return index + 1;
}
public void set(int quartX, int quartY, int quartZ, ResourceKey<Biome> biome) {
this.map.put(quartX, quartY, quartZ, (byte) this.getPaletteIndex(biome));
}
} }

Datei anzeigen

@ -1,180 +0,0 @@
package com.moulberry.axiom.buffer;
import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.AxiomPaper;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.PalettedContainer;
import org.jetbrains.annotations.Nullable;
public class BlockBuffer {
public static final BlockState EMPTY_STATE = Blocks.STRUCTURE_VOID.defaultBlockState();
private final Long2ObjectMap<PalettedContainer<BlockState>> values;
private PalettedContainer<BlockState> last = null;
private long lastId = AxiomConstants.MIN_POSITION_LONG;
private final Long2ObjectMap<Short2ObjectMap<CompressedBlockEntity>> blockEntities = new Long2ObjectOpenHashMap<>();
public BlockBuffer() {
this.values = new Long2ObjectOpenHashMap<>();
}
public BlockBuffer(Long2ObjectMap<PalettedContainer<BlockState>> values) {
this.values = values;
}
public void save(FriendlyByteBuf friendlyByteBuf) {
for (Long2ObjectMap.Entry<PalettedContainer<BlockState>> entry : this.entrySet()) {
friendlyByteBuf.writeLong(entry.getLongKey());
entry.getValue().write(friendlyByteBuf);
Short2ObjectMap<CompressedBlockEntity> blockEntities = this.blockEntities.get(entry.getLongKey());
if (blockEntities != null) {
friendlyByteBuf.writeVarInt(blockEntities.size());
for (Short2ObjectMap.Entry<CompressedBlockEntity> entry2 : blockEntities.short2ObjectEntrySet()) {
friendlyByteBuf.writeShort(entry2.getShortKey());
entry2.getValue().write(friendlyByteBuf);
}
} else {
friendlyByteBuf.writeVarInt(0);
}
}
friendlyByteBuf.writeLong(AxiomConstants.MIN_POSITION_LONG);
}
public static BlockBuffer load(FriendlyByteBuf friendlyByteBuf) {
BlockBuffer buffer = new BlockBuffer();
while (true) {
long index = friendlyByteBuf.readLong();
if (index == AxiomConstants.MIN_POSITION_LONG) break;
PalettedContainer<BlockState> palettedContainer = buffer.getOrCreateSection(index);
palettedContainer.read(friendlyByteBuf);
int blockEntitySize = Math.min(4096, friendlyByteBuf.readVarInt());
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);
map.put(offset, blockEntity);
}
buffer.blockEntities.put(index, map);
}
}
return buffer;
}
public void clear() {
this.last = null;
this.lastId = AxiomConstants.MIN_POSITION_LONG;
this.values.clear();
}
public void putBlockEntity(int x, int y, int z, CompressedBlockEntity blockEntity) {
long cpos = BlockPos.asLong(x >> 4, y >> 4, z >> 4);
Short2ObjectMap<CompressedBlockEntity> chunkMap = this.blockEntities.computeIfAbsent(cpos, k -> new Short2ObjectOpenHashMap<>());
int key = (x & 0xF) | ((y & 0xF) << 4) | ((z & 0xF) << 8);
chunkMap.put((short)key, blockEntity);
}
@Nullable
public CompressedBlockEntity getBlockEntity(int x, int y, int z) {
long cpos = BlockPos.asLong(x >> 4, y >> 4, z >> 4);
Short2ObjectMap<CompressedBlockEntity> chunkMap = this.blockEntities.get(cpos);
if (chunkMap == null) return null;
int key = (x & 0xF) | ((y & 0xF) << 4) | ((z & 0xF) << 8);
return chunkMap.get((short)key);
}
@Nullable
public Short2ObjectMap<CompressedBlockEntity> getBlockEntityChunkMap(long cpos) {
return this.blockEntities.get(cpos);
}
public BlockState get(int x, int y, int z) {
var container = this.getSectionForCoord(x, y, z);
if (container == null) {
return null;
}
var state = container.get(x & 0xF, y & 0xF, z & 0xF);
if (state == EMPTY_STATE) {
return null;
} else {
return state;
}
}
public void set(int x, int y, int z, BlockState state) {
var container = this.getOrCreateSectionForCoord(x, y, z);
var old = container.getAndSet(x & 0xF, y & 0xF, z & 0xF, state);
}
public void set(int cx, int cy, int cz, int lx, int ly, int lz, BlockState state) {
var container = this.getOrCreateSection(BlockPos.asLong(cx, cy, cz));
var old = container.getAndSet(lx, ly, lz, state);
}
public BlockState remove(int x, int y, int z) {
var container = this.getSectionForCoord(x, y, z);
if (container == null) {
return null;
}
var state = container.get(x & 0xF, y & 0xF, z & 0xF);
if (state == EMPTY_STATE) {
return null;
} else {
container.set(x & 0xF, y & 0xF, z & 0xF, EMPTY_STATE);
return state;
}
}
public ObjectSet<Long2ObjectMap.Entry<PalettedContainer<BlockState>>> entrySet() {
return this.values.long2ObjectEntrySet();
}
public PalettedContainer<BlockState> getSectionForCoord(int x, int y, int z) {
long id = BlockPos.asLong(x >> 4, y >> 4, z >> 4);
if (id != this.lastId) {
this.lastId = id;
this.last = this.values.get(id);
}
return this.last;
}
public PalettedContainer<BlockState> getOrCreateSectionForCoord(int x, int y, int z) {
long id = BlockPos.asLong(x >> 4, y >> 4, z >> 4);
return this.getOrCreateSection(id);
}
public PalettedContainer<BlockState> getOrCreateSection(long id) {
if (this.last == null || id != this.lastId) {
this.lastId = id;
this.last = this.values.computeIfAbsent(id, k -> new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY,
EMPTY_STATE, PalettedContainer.Strategy.SECTION_STATES));
}
return this.last;
}
}

Datei anzeigen

@ -3,64 +3,57 @@ package com.moulberry.axiom.buffer;
import com.github.luben.zstd.Zstd; import com.github.luben.zstd.Zstd;
import com.github.luben.zstd.ZstdDictCompress; import com.github.luben.zstd.ZstdDictCompress;
import com.github.luben.zstd.ZstdDictDecompress; import com.github.luben.zstd.ZstdDictDecompress;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.Reflection;
import io.netty.buffer.ByteBuf;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NbtIo;
import net.minecraft.network.FriendlyByteBuf;
import java.io.*; import java.io.*;
import java.util.Objects; import java.util.Objects;
public record CompressedBlockEntity(int originalSize, byte compressionDict, byte[] compressed) { public record CompressedBlockEntity(int originalSize, byte compressionDict, byte[] compressed) {
private static ZstdDictCompress zstdDictCompress = null; private static final ZstdDictCompress zstdDictCompress;
private static ZstdDictDecompress zstdDictDecompress = null; private static final ZstdDictDecompress zstdDictDecompress;
static {
public static void initialize(AxiomPaper plugin) { try (InputStream is = Objects.requireNonNull(CompressedBlockEntity.class.getClassLoader().getResourceAsStream("zstd_dictionaries/block_entities_v1.dict"))) {
try (InputStream is = Objects.requireNonNull(plugin.getResource("zstd_dictionaries/block_entities_v1.dict"))) {
byte[] bytes = is.readAllBytes(); byte[] bytes = is.readAllBytes();
zstdDictCompress = new ZstdDictCompress(bytes, Zstd.defaultCompressionLevel()); zstdDictCompress = new ZstdDictCompress(bytes, Zstd.defaultCompressionLevel());
zstdDictDecompress = new ZstdDictDecompress(bytes); zstdDictDecompress = new ZstdDictDecompress(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
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) { } catch (IOException e) {
throw new RuntimeException(e); throw new IllegalStateException(e);
} }
} }
private static final Reflection.Method<NbtIo, Void> write = Reflection.getMethod(NbtIo.class, CompoundTag.class, DataOutput.class);
public static CompressedBlockEntity compress(CompoundTag tag, ByteArrayOutputStream baos) {
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() { public CompoundTag decompress() {
if (this.compressionDict != 0) throw new UnsupportedOperationException("Unknown compression dict: " + this.compressionDict); if (this.compressionDict != 0) throw new UnsupportedOperationException("Unknown compression dict: " + this.compressionDict);
try { byte[] nbt = Zstd.decompress(this.compressed, zstdDictDecompress, this.originalSize);
byte[] nbt = Zstd.decompress(this.compressed, zstdDictDecompress, this.originalSize); return read.invoke(null, new DataInputStream(new ByteArrayInputStream(nbt)));
return NbtIo.read(new DataInputStream(new ByteArrayInputStream(nbt)));
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
public static CompressedBlockEntity read(FriendlyByteBuf friendlyByteBuf) { public static CompressedBlockEntity read(ByteBuf buf) {
int originalSize = friendlyByteBuf.readVarInt(); int originalSize = MojBuf.readVarInt(buf);
byte compressionDict = friendlyByteBuf.readByte(); byte compressionDict = buf.readByte();
byte[] compressed = friendlyByteBuf.readByteArray(); byte[] compressed = MojBuf.readByteArray(buf);
return new CompressedBlockEntity(originalSize, compressionDict, compressed); return new CompressedBlockEntity(originalSize, compressionDict, compressed);
} }
public void write(FriendlyByteBuf friendlyByteBuf) { public void write(ByteBuf buf) {
friendlyByteBuf.writeVarInt(this.originalSize); MojBuf.writeVarInt(buf, this.originalSize);
friendlyByteBuf.writeByte(this.compressionDict); buf.writeByte(this.compressionDict);
friendlyByteBuf.writeByteArray(this.compressed); 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,14 +1,11 @@
package com.moulberry.axiom.buffer; package com.moulberry.axiom.buffer;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.Reflection;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import java.util.Arrays;
import java.util.function.LongFunction;
public class Position2ByteMap { public class Position2ByteMap {
@ -18,122 +15,35 @@ public class Position2ByteMap {
} }
private final byte defaultValue; private final byte defaultValue;
private final LongFunction<byte[]> defaultFunction;
private final Long2ObjectMap<byte[]> map = new Long2ObjectOpenHashMap<>(); private final Long2ObjectMap<byte[]> map = new Long2ObjectOpenHashMap<>();
private long lastChunkPos = AxiomConstants.MIN_POSITION_LONG;
private byte[] lastChunk = null;
public Position2ByteMap() {
this((byte) 0);
}
public Position2ByteMap(byte defaultValue) { public Position2ByteMap(byte defaultValue) {
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
if (defaultValue == 0) {
this.defaultFunction = k -> new byte[16*16*16];
} else {
this.defaultFunction = k -> {
byte[] array = new byte[16*16*16];
Arrays.fill(array, defaultValue);
return array;
};
}
} }
public void save(FriendlyByteBuf friendlyByteBuf) { public static Position2ByteMap load(ByteBuf byteBuf) {
friendlyByteBuf.writeByte(this.defaultValue); Position2ByteMap map = new Position2ByteMap(byteBuf.readByte());
for (Long2ObjectMap.Entry<byte[]> entry : this.map.long2ObjectEntrySet()) {
friendlyByteBuf.writeLong(entry.getLongKey());
friendlyByteBuf.writeBytes(entry.getValue());
}
friendlyByteBuf.writeLong(AxiomConstants.MIN_POSITION_LONG);
}
public static Position2ByteMap load(FriendlyByteBuf friendlyByteBuf) {
Position2ByteMap map = new Position2ByteMap(friendlyByteBuf.readByte());
while (true) { while (true) {
long pos = friendlyByteBuf.readLong(); long pos = byteBuf.readLong();
if (pos == AxiomConstants.MIN_POSITION_LONG) break; if (pos == AxiomConstants.MIN_POSITION_LONG) break;
byte[] bytes = new byte[16*16*16]; byte[] bytes = new byte[16*16*16];
friendlyByteBuf.readBytes(bytes); byteBuf.readBytes(bytes);
map.map.put(pos, bytes); map.map.put(pos, bytes);
} }
return map; return map;
} }
public void clear() { private static final Reflection.Method<BlockPos, Integer> getX = Reflection.getTypedMethod(BlockPos.class, int.class, 0, long.class);
this.map.clear(); private static final Reflection.Method<BlockPos, Integer> getY = Reflection.getTypedMethod(BlockPos.class, int.class, 1, long.class);
this.lastChunkPos = AxiomConstants.MIN_POSITION_LONG; private static final Reflection.Method<BlockPos, Integer> getZ = Reflection.getTypedMethod(BlockPos.class, int.class, 2, long.class);
this.lastChunk = null;
}
public byte get(int x, int y, int z) {
int xC = x >> 4;
int yC = y >> 4;
int zC = z >> 4;
byte[] array = this.getChunk(xC, yC, zC);
if (array == null) return this.defaultValue;
return array[(x&15) + (y&15)*16 + (z&15)*16*16];
}
public void put(int x, int y, int z, byte v) {
int xC = x >> 4;
int yC = y >> 4;
int zC = z >> 4;
byte[] array = this.getOrCreateChunk(xC, yC, zC);
array[(x&15) + (y&15)*16 + (z&15)*16*16] = v;
}
public byte add(int x, int y, int z, byte v) {
if (v == 0) return this.get(x, y, z);
int xC = x >> 4;
int yC = y >> 4;
int zC = z >> 4;
byte[] array = this.getOrCreateChunk(xC, yC, zC);
return array[(x&15) + (y&15)*16 + (z&15)*16*16] += v;
}
public byte binaryAnd(int x, int y, int z, byte v) {
int xC = x >> 4;
int yC = y >> 4;
int zC = z >> 4;
byte[] array = this.getOrCreateChunk(xC, yC, zC);
return array[(x&15) + (y&15)*16 + (z&15)*16*16] &= v;
}
public boolean min(int x, int y, int z, byte v) {
int xC = x >> 4;
int yC = y >> 4;
int zC = z >> 4;
byte[] array = this.getOrCreateChunk(xC, yC, zC);
int index = (x&15) + (y&15)*16 + (z&15)*16*16;
if (v < array[index]) {
array[index] = v;
return true;
} else {
return false;
}
}
public void forEachEntry(EntryConsumer consumer) { public void forEachEntry(EntryConsumer consumer) {
for (Long2ObjectMap.Entry<byte[]> entry : this.map.long2ObjectEntrySet()) { for (Long2ObjectMap.Entry<byte[]> entry : this.map.long2ObjectEntrySet()) {
int cx = BlockPos.getX(entry.getLongKey()) * 16; int cx = getX.invoke(null, entry.getLongKey()) * 16;
int cy = BlockPos.getY(entry.getLongKey()) * 16; int cy = getY.invoke(null, entry.getLongKey()) * 16;
int cz = BlockPos.getZ(entry.getLongKey()) * 16; int cz = getZ.invoke(null, entry.getLongKey()) * 16;
int index = 0; int index = 0;
for (int z=0; z<16; z++) { for (int z=0; z<16; z++) {
@ -149,32 +59,4 @@ public class Position2ByteMap {
} }
} }
public byte[] getChunk(int xC, int yC, int zC) {
return this.getChunk(BlockPos.asLong(xC, yC, zC));
}
public byte[] getChunk(long pos) {
if (this.lastChunkPos != pos) {
byte[] chunk = this.map.get(pos);
this.lastChunkPos = pos;
this.lastChunk = chunk;
}
return this.lastChunk;
}
public byte[] getOrCreateChunk(int xC, int yC, int zC) {
return this.getOrCreateChunk(BlockPos.asLong(xC, yC, zC));
}
public byte[] getOrCreateChunk(long pos) {
if (this.lastChunk == null || this.lastChunkPos != pos) {
byte[] chunk = this.map.computeIfAbsent(pos, this.defaultFunction);
this.lastChunkPos = pos;
this.lastChunk = chunk;
}
return this.lastChunk;
}
} }

Datei anzeigen

@ -1,6 +1,5 @@
package com.moulberry.axiom.event; package com.moulberry.axiom.event;
import org.bukkit.GameMode;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;

Datei anzeigen

@ -1,7 +1,6 @@
package com.moulberry.axiom.event; package com.moulberry.axiom.event;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;

Datei anzeigen

@ -1,7 +1,6 @@
package com.moulberry.axiom.event; package com.moulberry.axiom.event;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;

Datei anzeigen

@ -0,0 +1,63 @@
package com.moulberry.axiom.integration;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.buffer.PositionConsumer;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.DefaultedRegistry;
import net.minecraft.core.IdMapper;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.PalettedContainer;
import org.bukkit.entity.Player;
import java.util.function.IntFunction;
public class NoVersionTranslator implements VersionTranslator {
private static IdMapper<BlockState> BLOCK_STATE_REGISTRY = Reflection.getField(Block.class, IdMapper.class, BlockState.class).get(null);
public static BlockState idToState(int state) {
return BLOCK_STATE_REGISTRY.byId(state);
}
private static final BlockState EMPTY_STATE =
Reflection.getTypedMethod(Block.class, BlockState.class).invoke((Block) // Block.defaultBlockState()
Reflection.getField(BuiltInRegistries.class, DefaultedRegistry.class, Block.class).get(null) // BuiltInRegistries.BLOCK
.get(new ResourceLocation("structure_void"))
);
public static void iterOverSection(PosBlockState container, PositionConsumer<BlockState> consumer) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
BlockState state = container.at(x, y, z);
if (state == EMPTY_STATE) continue;
consumer.accept(x, y, z, state);
}
}
}
}
@Override
public IntFunction<BlockState> blockStateMapper(Player player) {
return NoVersionTranslator::idToState;
}
private static final PalettedContainer.Strategy SECTION_STATES = Reflection.getField(PalettedContainer.Strategy.class, PalettedContainer.Strategy.class).get(null);
private static final Reflection.Method<PalettedContainer, Void> read = Reflection.getMethod(PalettedContainer.class, FriendlyByteBuf.class);
@Override
public void readPalettedContainer(Player player, ByteBuf buf, PositionConsumer<BlockState> consumer) {
//EMPTY_STATE is completely ignored
PalettedContainer<BlockState> container = new PalettedContainer<>(BLOCK_STATE_REGISTRY, EMPTY_STATE, SECTION_STATES);
read.invoke(container, buf);
iterOverSection(container::get, consumer);
}
@FunctionalInterface
public interface PosBlockState {
BlockState at(int sx, int sy, int sz);
}
}

Datei anzeigen

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

Datei anzeigen

@ -4,24 +4,20 @@ import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
public class RegionProtection { import java.util.function.BiFunction;
private final RegionProtectionWorldGuard worldGuard; public interface RegionProtection {
public RegionProtection(Player player, World world) { BiFunction<Player, World, RegionProtection> getProtection = Bukkit.getPluginManager().isPluginEnabled("WorldGuard") ? WorldGuardProtection::create : (player, world) -> new Dummy();
if (Bukkit.getPluginManager().isPluginEnabled("WorldGuard")) {
this.worldGuard = RegionProtectionWorldGuard.tryCreate(player, world); boolean canBuildInSection(int cx, int cy, int cz);
} else {
this.worldGuard = null; class Dummy implements RegionProtection {
@Override
public boolean canBuildInSection(int cx, int cy, int cz) {
return true;
} }
} }
public boolean canBuildInSection(int cx, int cy, int cz) {
if (this.worldGuard != null && !this.worldGuard.canBuildInSection(cx, cy, cz)) return false;
// todo: PlotSquared
return true;
}
} }

Datei anzeigen

@ -0,0 +1,19 @@
package com.moulberry.axiom.integration;
import com.moulberry.axiom.buffer.PositionConsumer;
import io.netty.buffer.ByteBuf;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.function.IntFunction;
public interface VersionTranslator {
VersionTranslator impl = Bukkit.getPluginManager().isPluginEnabled("ViaVersion") ? new ViaVersionTranslator() : new NoVersionTranslator();
IntFunction<BlockState> blockStateMapper(Player player);
void readPalettedContainer(Player player, ByteBuf buf, PositionConsumer<BlockState> consumer);
}

Datei anzeigen

@ -0,0 +1,72 @@
package com.moulberry.axiom.integration;
import com.moulberry.axiom.buffer.PositionConsumer;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ServerProtocolVersion;
import com.viaversion.viaversion.api.type.types.version.PaletteType1_18;
import io.netty.buffer.ByteBuf;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntFunction;
public class ViaVersionTranslator implements VersionTranslator {
private final ProtocolManager protocolManager;
private final ServerProtocolVersion serverVersion;
public ViaVersionTranslator() {
protocolManager = Via.getManager().getProtocolManager();
serverVersion = Via.getAPI().getServerVersion();
}
@Override
public IntFunction<BlockState> blockStateMapper(Player player) {
List<ProtocolPathEntry> path = protocolManager.getProtocolPath(Via.getAPI().getPlayerVersion(player.getUniqueId()), serverVersion.highestSupportedVersion());
if(path == null)
return NoVersionTranslator::idToState;
List<IntFunction<Integer>> mappers = new ArrayList<>(path.size());
for(ProtocolPathEntry entry : path) {
MappingData mappings = entry.protocol().getMappingData();
if(mappings != null)
mappers.add(mappings.getBlockStateMappings().inverse()::getNewId);
}
return id -> {
for(IntFunction<Integer> transformer : mappers)
id = transformer.apply(id);
return NoVersionTranslator.idToState(id);
};
}
@Override
public void readPalettedContainer(Player player, ByteBuf buf, PositionConsumer<BlockState> consumer) {
//TODO GlobalPaletteBits depend on player version (currently only 1.20.1, depending on Axiom version)
DataPalette container;
try {
container = new PaletteType1_18(PaletteType.BLOCKS, 15).read(buf);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
BlockState[] palette = new BlockState[container.size()];
IntFunction<BlockState> mapper = blockStateMapper(player);
for(int i = 0; i < container.size(); i++) {
palette[i] = mapper.apply(container.idByIndex(i));
}
NoVersionTranslator.iterOverSection(
(sx, sy, sz) -> palette[container.paletteIndexAt(container.index(sx, sy, sz))],
consumer
);
}
}

Datei anzeigen

@ -15,37 +15,36 @@ import com.sk89q.worldguard.protection.regions.RegionContainer;
import com.sk89q.worldguard.protection.regions.RegionQuery; import com.sk89q.worldguard.protection.regions.RegionQuery;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
public class RegionProtectionWorldGuard { public class WorldGuardProtection implements RegionProtection {
private static final WorldGuardPlatform platform = WorldGuard.getInstance().getPlatform();
private static final RegionContainer regionContainer = platform.getRegionContainer();
private final LocalPlayer player; private final LocalPlayer player;
private final RegionManager regionManager; private final RegionManager regionManager;
public RegionProtectionWorldGuard(LocalPlayer player, RegionManager regionManager) { public WorldGuardProtection(LocalPlayer player, RegionManager regionManager) {
this.player = player; this.player = player;
this.regionManager = regionManager; this.regionManager = regionManager;
} }
@Nullable public static RegionProtection create(Player player, World world) {
public static RegionProtectionWorldGuard tryCreate(Player player, World world) { com.sk89q.worldedit.world.World worldGuardWorld = BukkitAdapter.adapt(world);
WorldGuardPlatform platform = WorldGuard.getInstance().getPlatform();
RegionContainer regionContainer = platform.getRegionContainer();
com.sk89q.worldedit.world.World worldEditWorld = BukkitAdapter.adapt(world);
LocalPlayer worldGuardPlayer = WorldGuardPlugin.inst().wrapPlayer(player); LocalPlayer worldGuardPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
// Don't do any protection if player has bypass // Don't do any protection if player has bypass
if (platform.getSessionManager().hasBypass(worldGuardPlayer, worldEditWorld)) { if (platform.getSessionManager().hasBypass(worldGuardPlayer, worldGuardWorld)) {
return null; return new RegionProtection.Dummy();
} }
RegionManager regionManager = regionContainer.get(worldEditWorld); RegionManager regionManager = regionContainer.get(worldGuardWorld);
if (regionManager == null) return null; if (regionManager == null) return new RegionProtection.Dummy();
return new RegionProtectionWorldGuard(worldGuardPlayer, regionManager); return new WorldGuardProtection(worldGuardPlayer, regionManager);
} }
@Override
public boolean canBuildInSection(int cx, int cy, int cz) { public boolean canBuildInSection(int cx, int cy, int cz) {
BlockVector3 min = BlockVector3.at(cx*16, cy*16, cz*16); BlockVector3 min = BlockVector3.at(cx*16, cy*16, cz*16);
BlockVector3 max = BlockVector3.at(cx*16+15, cy*16+15, cz*16+15); BlockVector3 max = BlockVector3.at(cx*16+15, cy*16+15, cz*16+15);

Datei anzeigen

@ -1,66 +1,104 @@
package com.moulberry.axiom.packet; 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.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.papermc.paper.network.ConnectionEvent; import io.papermc.paper.network.ConnectionEvent;
import net.minecraft.network.Connection; import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation; import net.minecraft.network.protocol.Packet;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
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; import java.util.List;
public class AxiomBigPayloadHandler extends ByteToMessageDecoder { @ChannelHandler.Sharable
public class AxiomBigPayloadHandler extends ChannelInboundHandlerAdapter {
private static final ResourceLocation SET_BUFFER = new ResourceLocation("axiom", "set_buffer"); private static final Reflection.Method<ConnectionProtocol, Integer> getPacketId = Reflection.getTypedMethod(ConnectionProtocol.class, Reflection.VERSION > 1902 ? int.class : Integer.class, PacketFlow.class, Packet.class);
private final int payloadId; private static final int PLUGINMESSAGE_PACKETID = getPacketId.invoke(ConnectionProtocol.PLAY, PacketFlow.SERVERBOUND, new ServerboundCustomPayloadPacket(null, null));
private final Connection connection;
private final SetBlockBufferPacketListener listener;
public AxiomBigPayloadHandler(int payloadId, Connection connection, SetBlockBufferPacketListener listener) { private final Player player;
this.payloadId = payloadId;
this.connection = connection; private final List<QueuedPacket> packets = new ArrayList<>();
this.listener = listener; private final BukkitTask task;
public AxiomBigPayloadHandler(Player player) {
this.player = player;
this.task = Bukkit.getScheduler().runTaskTimer(AxiomPaper.getPlugin(), this::runHandlers, 1, 1);
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) {
try { if(!(msg instanceof ByteBuf in)) {
int readerIndex = in.readerIndex(); ctx.fireChannelRead(msg);
int i = in.readableBytes(); return;
if (i != 0) {
FriendlyByteBuf buf = new FriendlyByteBuf(in);
int packetId = buf.readVarInt();
if (packetId == payloadId) {
ResourceLocation identifier = buf.readResourceLocation();
if (identifier.equals(SET_BUFFER)) {
ServerPlayer player = connection.getPlayer();
if (player != null && player.getBukkitEntity().hasPermission("axiom.*")) {
if (listener.onReceive(player, buf)) {
return;
}
}
}
}
}
in.readerIndex(readerIndex);
} catch (Exception e) {
e.printStackTrace();
} }
ctx.fireChannelRead(in.retain()); int readerIndexBackup = in.readerIndex();
if(in.readableBytes() != 0) {
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;
}
}
}
in.readerIndex(readerIndexBackup);
ctx.fireChannelRead(in);
} }
@Override @Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt == ConnectionEvent.COMPRESSION_THRESHOLD_SET || evt == ConnectionEvent.COMPRESSION_DISABLED) { if (evt == ConnectionEvent.COMPRESSION_THRESHOLD_SET || evt == ConnectionEvent.COMPRESSION_DISABLED) {
ctx.channel().pipeline().remove("axiom-big-payload-handler"); ctx.channel().pipeline().remove("axiom-big-payload-handler");
ctx.channel().pipeline().addBefore("decoder", "axiom-big-payload-handler", ctx.channel().pipeline().addBefore("decoder", "axiom-big-payload-handler", this);
new AxiomBigPayloadHandler(payloadId, connection, listener));
} }
super.userEventTriggered(ctx, evt); 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) {
AxiomPacketListener handler = AxiomPaper.getListener(packet.name);
if(handler != null)
handler.onMessage(player, packet.in);
packet.in.release();
}
}
private record QueuedPacket(String name, ByteBuf in) {}
} }

Datei anzeigen

@ -0,0 +1,12 @@
package com.moulberry.axiom.packet;
import io.netty.buffer.ByteBuf;
import org.bukkit.entity.Player;
public interface AxiomPacketListener {
void onMessage(Player player, ByteBuf buf);
default void onMissingPerm(Player player, ByteBuf buf) {}
}

Datei anzeigen

@ -0,0 +1,144 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.version.VersionWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.lighting.LightEventListener;
import org.bukkit.World;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class ChunkSectionModifier {
private static final Reflection.Method<Heightmap.Types, String> getSerializationKey = Reflection.getTypedMethod(Heightmap.Types.class, String.class);
private static final List<Heightmap.Types> types = Arrays.stream(Heightmap.Types.values()).filter(type -> List.of("WORLD_SURFACE", "OCEAN_FLOOR", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES").contains(getSerializationKey.invoke(type))).toList();
private final int cx;
private final int cy;
private final int cz;
private final ServerChunkCache chunkSource;
private final LightEventListener lightEngine;
private final LevelChunk chunk;
private final LevelChunkSection section;
private final Heightmap[] heightmaps;
private final boolean hadOnlyAir;
private static final Reflection.Method<ChunkAccess, LevelChunkSection> getSection = Reflection.getTypedMethod(ChunkAccess.class, LevelChunkSection.class, int.class);
private static final Reflection.Method<LevelChunkSection, Boolean> hasOnlyAir = Reflection.getTypedMethod(LevelChunkSection.class, boolean.class, 1);
private static final Reflection.Field<ChunkAccess, Map> chunkHeightmaps = Reflection.getField(ChunkAccess.class, Map.class);
public ChunkSectionModifier(World world, int cx, int cy, int cz) {
this.cx = cx;
this.cy = cy;
this.cz = cz;
ServerLevel level = AxiomPaper.convert(world);
chunkSource = AxiomPaper.getChunkSource(level);
lightEngine = AxiomPaper.getLightEngine(chunkSource);
chunk = AxiomPaper.getChunk(level, cx, cz);
section = getSection.invoke(chunk, cy - world.getMinHeight() / 16);
hadOnlyAir = hasOnlyAir.invoke(section);
heightmaps = types.stream().map(chunkHeightmaps.get(chunk)::get).toArray(Heightmap[]::new);
}
private static final Reflection.Method<LevelChunkSection, BlockState> setBlockState = Reflection.getTypedMethod(LevelChunkSection.class, BlockState.class, int.class, int.class, int.class, BlockState.class, boolean.class);
private static final Reflection.Method<Heightmap, Boolean> update = Reflection.getTypedMethod(Heightmap.class, boolean.class, int.class, int.class, int.class, BlockState.class);
private static final Reflection.Method<BlockState, Block> getBlock = Reflection.getTypedMethod(BlockState.class, Block.class);
private static final Reflection.Method<ChunkAccess, Void> removeBlockEntity = Reflection.getMethod(ChunkAccess.class, BlockPos.class);
private static final Reflection.Method<ServerChunkCache, Void> blockChanged = Reflection.getMethod(ServerChunkCache.class, BlockPos.class);
private static final Reflection.Method<LightEventListener, Void> checkBlock = Reflection.getMethod(LightEventListener.class, BlockPos.class);
public void setState(int sx, int sy, int sz, BlockState state) {
BlockState old = setBlockState.invoke(section, sx, sy, sz, state, false);
if (state == old)
return;
int by = cy * 16 + sy;
BlockPos pos = new BlockPos(cx * 16 + sx, by, cz * 16 + sz);
for (Heightmap heightmap : heightmaps)
update.invoke(heightmap, sx, by, sz, state);
if (getBlock.invoke(state) instanceof EntityBlock) {
setBlockEntity(state, pos);
} else if (getBlock.invoke(old) instanceof EntityBlock) {
removeBlockEntity.invoke(chunk, pos);
}
blockChanged.invoke(chunkSource, pos);
if (VersionWrapper.impl.hasDifferentLightProperties(chunk, pos, old, state)) {
checkBlock.invoke(lightEngine, pos);
}
}
private static final Reflection.Method<BlockGetter, BlockEntity> getBlockEntity = Reflection.getTypedMethod(BlockGetter.class, BlockEntity.class, BlockPos.class);
private static final Reflection.Method<BlockEntity, Void> load = Reflection.getMethod(BlockEntity.class, CompoundTag.class);
public void setBlockEntity(int sx, int sy, int sz, CompoundTag tag) {
BlockEntity blockEntity = getBlockEntity.invoke(chunk, new BlockPos(cx * 16 + sx, cy * 16 + sy, cz * 16 + sz));
if(blockEntity != null)
load.invoke(blockEntity, tag);
}
private static final Reflection.Method<BlockEntity, BlockEntityType> getType = Reflection.getTypedMethod(BlockEntity.class, BlockEntityType.class);
private static final Reflection.Method<BlockEntityType, Boolean> isValid = Reflection.getTypedMethod(BlockEntityType.class, boolean.class, BlockState.class);
private static final Reflection.Method<BlockEntity, Void> blockEntitySetBlockState = Reflection.getMethod(BlockEntity.class, BlockState.class);
private static final Reflection.Method<LevelChunk, Void> updateBlockEntityTicker = Reflection.getMethod(LevelChunk.class, 2, BlockEntity.class);
private static final Reflection.Method<EntityBlock, BlockEntity> newBlockEntity = Reflection.getTypedMethod(EntityBlock.class, BlockEntity.class, BlockPos.class, BlockState.class);
private static final Reflection.Method<LevelChunk, Void> addAndRegisterBlockEntity = Reflection.getMethod(LevelChunk.class, BlockEntity.class);
private void setBlockEntity(BlockState state, BlockPos pos) {
BlockEntity blockEntity = getBlockEntity.invoke(chunk, pos);
if (blockEntity != null) {
if (isValid.invoke(getType.invoke(blockEntity), state)) {
// Block entity is here and the type is correct
blockEntitySetBlockState.invoke(blockEntity, state);
updateBlockEntityTicker.invoke(chunk, blockEntity);
} else {
// Block entity type isn't correct, we need to recreate it
removeBlockEntity.invoke(chunk, pos);
blockEntity = null;
}
}
if (blockEntity == null) {
// There isn't a block entity here, create it!
EntityBlock block = (EntityBlock)getBlock.invoke(state);
blockEntity = newBlockEntity.invoke(block, pos, state);
if (blockEntity != null) {
addAndRegisterBlockEntity.invoke(chunk, blockEntity);
}
}
}
private static final Reflection.Method<SectionPos, SectionPos> of = Reflection.getTypedMethod(SectionPos.class, SectionPos.class, int.class, int.class, int.class);
private static final Reflection.Method<LightEventListener, Void> updateSectionStatus = Reflection.getMethod(LightEventListener.class, SectionPos.class, boolean.class);
private static final Reflection.Method<ChunkAccess, Void> setUnsaved = Reflection.getMethod(ChunkAccess.class, boolean.class);
public void finish() {
boolean onlyAir = hasOnlyAir.invoke(section);
if (hadOnlyAir != onlyAir) {
updateSectionStatus.invoke(lightEngine, of.invoke(null, cx, cy, cz), onlyAir);
}
setUnsaved.invoke(chunk, true);
}
}

Datei anzeigen

@ -1,50 +1,43 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.*;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.View;
import com.moulberry.axiom.event.AxiomHandshakeEvent; import com.moulberry.axiom.event.AxiomHandshakeEvent;
import com.moulberry.axiom.persistence.ItemStackDataType; import com.moulberry.axiom.persistence.ItemStackDataType;
import com.moulberry.axiom.persistence.UUIDDataType; import com.moulberry.axiom.persistence.UUIDDataType;
import io.netty.buffer.Unpooled; import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.Connection;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey; import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class HelloPacketListener implements PluginMessageListener { public class HelloPacketListener implements AxiomPacketListener, Listener {
private final AxiomPaper plugin; private static final Reflection.Field<ServerGamePacketListenerImpl, Connection> packetListenerConnection = Reflection.getField(ServerGamePacketListenerImpl.class, Connection.class);
private final Set<UUID> activeAxiomPlayers; private static final Reflection.Field<Connection, Channel> channel = Reflection.getField(Connection.class, Channel.class);
public HelloPacketListener(AxiomPaper plugin, Set<UUID> activeAxiomPlayers) { private final AxiomPlayerManager axiomPlayers;
this.plugin = plugin;
this.activeAxiomPlayers = activeAxiomPlayers; public HelloPacketListener(AxiomPlayerManager axiomPlayers) {
this.axiomPlayers = axiomPlayers;
} }
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { public void onMessage(Player player, ByteBuf buf) {
if (!player.hasPermission("axiom.*")) { int apiVersion = MojBuf.readVarInt(buf);
return; MojBuf.readNbt(buf); // Discard
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
int apiVersion = friendlyByteBuf.readVarInt();
friendlyByteBuf.readNbt(); // Discard
if (apiVersion != AxiomConstants.API_VERSION) { if (apiVersion != AxiomConstants.API_VERSION) {
player.kick(Component.text("Unsupported Axiom API Version. Server supports " + AxiomConstants.API_VERSION + player.sendMessage(Component.text("Unsupported Axiom API Version. Server supports " + AxiomConstants.API_VERSION + ", while client is " + apiVersion));
", while client is " + apiVersion));
return; return;
} }
@ -55,52 +48,60 @@ public class HelloPacketListener implements PluginMessageListener {
return; return;
} }
activeAxiomPlayers.add(player.getUniqueId()); axiomPlayers.add(player);
// Welcome to multiversioning!
channel.get(
packetListenerConnection.get(
AxiomPaper.getConnection(
AxiomPaper.convert(player)
)
)
).pipeline().addBefore("decoder", "axiom-big-payload-handler", new AxiomBigPayloadHandler(player));
// Enable // Enable
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); ByteBuf out = MojBuf.unpooled();
buf.writeBoolean(true); out.writeBoolean(true);
buf.writeByte(0); // todo: world properties out.writeByte(0); // todo: world properties
buf.writeInt(handshakeEvent.getMaxBufferSize()); // Max Buffer Size out.writeInt(handshakeEvent.getMaxBufferSize()); // Max Buffer Size
buf.writeBoolean(false); // No source info out.writeBoolean(false); // No source info
buf.writeBoolean(false); // No source settings out.writeBoolean(false); // No source settings
buf.writeVarInt(5); // Maximum Reach MojBuf.writeVarInt(out, 5); // Maximum Reach
buf.writeVarInt(16); // Max editor views MojBuf.writeVarInt(out, 16); // Max editor views
buf.writeBoolean(true); // Editable Views out.writeBoolean(true); // Editable Views
player.sendPluginMessage(this.plugin, "axiom:enable", buf.accessByteBufWithCorrectSize()); OutChannel.ENABLE.send(player, out);
// Initialize Hotbars // Initialize Hotbars
PersistentDataContainer container = player.getPersistentDataContainer(); PersistentDataContainer container = player.getPersistentDataContainer();
int activeHotbarIndex = container.getOrDefault(AxiomConstants.ACTIVE_HOTBAR_INDEX, PersistentDataType.BYTE, (byte) 0); int activeHotbarIndex = container.getOrDefault(AxiomConstants.ACTIVE_HOTBAR_INDEX, PersistentDataType.BYTE, (byte) 0);
PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER); PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER);
if (hotbarItems != null) { if (hotbarItems != null) {
buf = new FriendlyByteBuf(Unpooled.buffer()); out = MojBuf.unpooled();
buf.writeByte((byte) activeHotbarIndex); out.writeByte((byte) activeHotbarIndex);
for (int i=0; i<9*9; i++) { for (int i=0; i<9*9; i++) {
// Ignore selected hotbar // Ignore selected hotbar
if (i / 9 == activeHotbarIndex) { if (i / 9 == activeHotbarIndex) {
buf.writeItem(net.minecraft.world.item.ItemStack.EMPTY); MojBuf.writeItem(out, new ItemStack(Material.AIR, 0));
} else { } else {
ItemStack stack = hotbarItems.get(new NamespacedKey("axiom", "slot_"+i), ItemStackDataType.INSTANCE); ItemStack stack = hotbarItems.get(AxiomConstants.axiomKey("slot_"+i), ItemStackDataType.INSTANCE);
buf.writeItem(CraftItemStack.asNMSCopy(stack)); MojBuf.writeItem(out, stack);
} }
} }
player.sendPluginMessage(this.plugin, "axiom:initialize_hotbars", buf.accessByteBufWithCorrectSize()); OutChannel.INITIALIZE_HOTBARS.send(player, out);
} }
// Initialize Views // Initialize Views
UUID activeView = container.get(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE); UUID activeView = container.get(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE);
if (activeView != null) { if (activeView != null) {
buf = new FriendlyByteBuf(Unpooled.buffer()); out = MojBuf.unpooled();
buf.writeUUID(activeView); MojBuf.writeUUID(out, activeView);
PersistentDataContainer[] views = container.get(AxiomConstants.VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY); PersistentDataContainer[] views = container.get(AxiomConstants.VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY);
buf.writeVarInt(views.length); MojBuf.writeVarInt(out, views.length);
for (PersistentDataContainer view : views) { for (PersistentDataContainer view : views) {
View.load(view).write(buf); View.load(view).write(out);
} }
player.sendPluginMessage(this.plugin, "axiom:set_editor_views", buf.accessByteBufWithCorrectSize()); OutChannel.SET_EDITOR_VIEWS.send(player, out);
} }
} }

Datei anzeigen

@ -1,90 +1,77 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.OutChannel;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.buffer.CompressedBlockEntity; import com.moulberry.axiom.buffer.CompressedBlockEntity;
import com.moulberry.axiom.buffer.MojBuf;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag; 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.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity; 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.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
public class RequestBlockEntityPacketListener implements PluginMessageListener { public class RequestBlockEntityPacketListener implements AxiomPacketListener {
private final AxiomPaper plugin; private static final Reflection.Method<ServerLevel, BlockEntity> getBlockEntity = Reflection.getTypedMethod(ServerLevel.class, BlockEntity.class, BlockPos.class);
public RequestBlockEntityPacketListener(AxiomPaper plugin) { private static final Reflection.Method<BlockEntity, CompoundTag> saveWithoutMetadata = Reflection.getTypedMethod(BlockEntity.class, CompoundTag.class, 2);
this.plugin = plugin;
}
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player bukkitPlayer, @NotNull byte[] message) { public void onMessage(@NotNull Player bukkitPlayer, @NotNull ByteBuf buf) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); long id = buf.readLong();
long id = friendlyByteBuf.readLong();
if (!bukkitPlayer.hasPermission("axiom.*")) { World world = Bukkit.getWorld(MojBuf.readKey(buf));
// We always send an 'empty' response in order to make the client happy if (world == null) {
sendEmptyResponse(bukkitPlayer, id);
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) {
sendEmptyResponse(bukkitPlayer, id); sendEmptyResponse(bukkitPlayer, id);
return; return;
} }
ServerLevel level = AxiomPaper.convert(world);
Long2ObjectMap<CompressedBlockEntity> map = new Long2ObjectOpenHashMap<>(); Long2ObjectMap<CompressedBlockEntity> map = new Long2ObjectOpenHashMap<>();
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
// Save and compress block entities // Save and compress block entities
int count = friendlyByteBuf.readVarInt(); int count = MojBuf.readVarInt(buf);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
long pos = friendlyByteBuf.readLong(); long pos = buf.readLong();
BlockEntity blockEntity = level.getBlockEntity(mutableBlockPos.set(pos)); BlockEntity blockEntity = getBlockEntity.invoke(level, AxiomPaper.convert(pos));
if (blockEntity != null) { if (blockEntity != null) {
CompoundTag tag = blockEntity.saveWithoutMetadata(); CompoundTag tag = saveWithoutMetadata.invoke(blockEntity);
map.put(pos, CompressedBlockEntity.compress(tag, baos)); map.put(pos, CompressedBlockEntity.compress(tag, baos));
} }
} }
// Send response packet // Send response packet
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(16)); ByteBuf out = MojBuf.unpooled();
buf.writeLong(id); out.writeLong(id);
buf.writeVarInt(map.size()); MojBuf.writeVarInt(out, map.size());
for (Long2ObjectMap.Entry<CompressedBlockEntity> entry : map.long2ObjectEntrySet()) { for (Long2ObjectMap.Entry<CompressedBlockEntity> entry : map.long2ObjectEntrySet()) {
buf.writeLong(entry.getLongKey()); out.writeLong(entry.getLongKey());
entry.getValue().write(buf); entry.getValue().write(out);
} }
bukkitPlayer.sendPluginMessage(this.plugin, "axiom:block_entities", buf.accessByteBufWithCorrectSize()); 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) { private void sendEmptyResponse(Player player, long id) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(16)); ByteBuf buf = Unpooled.buffer(16);
buf.writeLong(id); buf.writeLong(id);
buf.writeByte(0); // no block entities buf.writeByte(0); // no block entities
player.sendPluginMessage(this.plugin, "axiom:block_entities", buf.accessByteBufWithCorrectSize()); OutChannel.BLOCK_ENTITIES.send(player, buf);
} }
} }

Datei anzeigen

@ -1,306 +1,87 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.buffer.BiomeBuffer; import com.moulberry.axiom.buffer.BiomeBuffer;
import com.moulberry.axiom.buffer.BlockBuffer;
import com.moulberry.axiom.buffer.CompressedBlockEntity; import com.moulberry.axiom.buffer.CompressedBlockEntity;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.event.AxiomModifyWorldEvent; import com.moulberry.axiom.event.AxiomModifyWorldEvent;
import com.moulberry.axiom.integration.RegionProtection; import com.moulberry.axiom.integration.RegionProtection;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.moulberry.axiom.integration.VersionTranslator;
import com.sk89q.worldedit.math.BlockVector3; import com.moulberry.axiom.version.VersionWrapper;
import com.sk89q.worldedit.world.World; import io.netty.buffer.ByteBuf;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import com.sk89q.worldguard.protection.regions.RegionQuery;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
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;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.lighting.LightEngine;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey; import org.bukkit.Chunk;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import java.lang.reflect.InvocationTargetException; import java.util.HashSet;
import java.lang.reflect.Method; import java.util.Set;
import java.util.*;
public class SetBlockBufferPacketListener { public class SetBlockBufferPacketListener implements AxiomPacketListener {
private final AxiomPaper plugin; @Override
private final Method updateBlockEntityTicker; public void onMessage(Player player, ByteBuf buf) {
World world = Bukkit.getWorld(MojBuf.readKey(buf));
public SetBlockBufferPacketListener(AxiomPaper plugin) { // Call AxiomModifyWorldEvent event
this.plugin = plugin; AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player, world);
Bukkit.getPluginManager().callEvent(modifyWorldEvent);
if (modifyWorldEvent.isCancelled()) return;
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar(); MojBuf.readUUID(buf); // Discard, we don't need to associate buffers
String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class); if (!buf.readBoolean()) {
MojBuf.readNbt(buf); // Discard sourceInfo
try {
this.updateBlockEntityTicker = LevelChunk.class.getDeclaredMethod(methodName, BlockEntity.class);
this.updateBlockEntityTicker.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public boolean onReceive(ServerPlayer player, FriendlyByteBuf friendlyByteBuf) {
MinecraftServer server = player.getServer();
if (server == null) return false;
ResourceKey<Level> worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
friendlyByteBuf.readUUID(); // Discard, we don't need to associate buffers
boolean continuation = friendlyByteBuf.readBoolean();
if (!continuation) {
friendlyByteBuf.readNbt(); // Discard sourceInfo
} }
byte type = friendlyByteBuf.readByte(); byte type = buf.readByte();
if (type == 0) { if (type == 0) {
BlockBuffer buffer = BlockBuffer.load(friendlyByteBuf); applyBlockBuffer(world, player, buf);
applyBlockBuffer(player, server, buffer, worldKey);
} else if (type == 1) { } else if (type == 1) {
BiomeBuffer buffer = BiomeBuffer.load(friendlyByteBuf); applyBiomeBuffer(world, new BiomeBuffer(buf));
applyBiomeBuffer(server, buffer, worldKey);
} else { } else {
throw new RuntimeException("Unknown buffer type: " + type); throw new IllegalArgumentException("Unknown buffer type: " + type);
} }
return true;
} }
private void applyBlockBuffer(ServerPlayer player, MinecraftServer server, BlockBuffer buffer, ResourceKey<Level> worldKey) { private void applyBlockBuffer(World world, Player player, ByteBuf buf) {
server.execute(() -> { RegionProtection regionProtection = RegionProtection.getProtection.apply(player, world);
ServerLevel world = server.getLevel(worldKey);
if (world == null) return;
// Call AxiomModifyWorldEvent event long sectionPos;
AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player.getBukkitEntity(), world.getWorld()); while ((sectionPos = buf.readLong()) != AxiomConstants.MIN_POSITION_LONG) {
Bukkit.getPluginManager().callEvent(modifyWorldEvent); int cx = BlockPos.getX(sectionPos);
if (modifyWorldEvent.isCancelled()) return; int cy = BlockPos.getY(sectionPos);
int cz = BlockPos.getZ(sectionPos);
RegionProtection regionProtection = new RegionProtection(player.getBukkitEntity(), world.getWorld()); boolean canBuildInSection = regionProtection.canBuildInSection(cx, cy, cz);
ChunkSectionModifier section = new ChunkSectionModifier(world, cx, cy, cz);
// Allowed, apply buffer VersionTranslator.impl.readPalettedContainer(player, buf, canBuildInSection ? section::setState : (x, y, z, blockState) -> {});
BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
var lightEngine = world.getChunkSource().getLightEngine(); int blockEntitySize = Math.min(4096, Math.max(0, MojBuf.readVarInt(buf)));
for (int i = 0; i < blockEntitySize; i++) {
BlockState emptyState = BlockBuffer.EMPTY_STATE; short offset = buf.readShort();
CompressedBlockEntity entity = CompressedBlockEntity.read(buf);
for (Long2ObjectMap.Entry<PalettedContainer<BlockState>> entry : buffer.entrySet()) { if(canBuildInSection)
int cx = BlockPos.getX(entry.getLongKey()); section.setBlockEntity(offset & 0xF, (offset >> 4) & 0xF, offset >> 8, entity.decompress());
int cy = BlockPos.getY(entry.getLongKey());
int cz = BlockPos.getZ(entry.getLongKey());
PalettedContainer<BlockState> container = entry.getValue();
if (cy < world.getMinSection() || cy >= world.getMaxSection()) {
continue;
}
if (!regionProtection.canBuildInSection(cx, cy, cz)) {
continue;
}
LevelChunk chunk = world.getChunk(cx, cz);
chunk.setUnsaved(true);
LevelChunkSection section = chunk.getSection(world.getSectionIndexFromSectionY(cy));
PalettedContainer<BlockState> sectionStates = section.getStates();
boolean hasOnlyAir = section.hasOnlyAir();
Heightmap worldSurface = null;
Heightmap oceanFloor = null;
Heightmap motionBlocking = null;
Heightmap motionBlockingNoLeaves = null;
for (Map.Entry<Heightmap.Types, Heightmap> heightmap : chunk.getHeightmaps()) {
switch (heightmap.getKey()) {
case WORLD_SURFACE -> worldSurface = heightmap.getValue();
case OCEAN_FLOOR -> oceanFloor = heightmap.getValue();
case MOTION_BLOCKING -> motionBlocking = heightmap.getValue();
case MOTION_BLOCKING_NO_LEAVES -> motionBlockingNoLeaves = heightmap.getValue();
default -> {}
}
}
Short2ObjectMap<CompressedBlockEntity> blockEntityChunkMap = buffer.getBlockEntityChunkMap(entry.getLongKey());
sectionStates.acquire();
try {
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
BlockState blockState = container.get(x, y, z);
if (blockState == emptyState) continue;
int bx = cx*16 + x;
int by = cy*16 + y;
int bz = cz*16 + z;
blockPos.set(bx, by, bz);
if (hasOnlyAir && blockState.isAir()) {
continue;
}
BlockState old = section.setBlockState(x, y, z, blockState, false);
if (blockState != old) {
Block block = blockState.getBlock();
motionBlocking.update(x, by, z, blockState);
motionBlockingNoLeaves.update(x, by, z, blockState);
oceanFloor.update(x, by, z, blockState);
worldSurface.update(x, by, z, blockState);
if (false) { // Full update
old.onRemove(world, blockPos, blockState, false);
if (sectionStates.get(x, y, z).is(block)) {
blockState.onPlace(world, blockPos, old, false);
}
}
if (blockState.hasBlockEntity()) {
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
if (blockEntity == null) {
// There isn't a block entity here, create it!
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
chunk.addAndRegisterBlockEntity(blockEntity);
}
} else if (blockEntity.getType().isValid(blockState)) {
// 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);
}
} else {
// Block entity type isn't correct, we need to recreate it
chunk.removeBlockEntity(blockPos);
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
chunk.addAndRegisterBlockEntity(blockEntity);
}
}
if (blockEntity != null && blockEntityChunkMap != null) {
int key = x | (y << 4) | (z << 8);
CompressedBlockEntity savedBlockEntity = blockEntityChunkMap.get((short) key);
if (savedBlockEntity != null) {
blockEntity.load(savedBlockEntity.decompress());
}
}
} else if (old.hasBlockEntity()) {
chunk.removeBlockEntity(blockPos);
}
world.getChunkSource().blockChanged(blockPos); // todo: maybe simply resend chunk instead of this?
if (LightEngine.hasDifferentLightProperties(chunk, blockPos, old, blockState)) {
lightEngine.checkBlock(blockPos);
}
}
}
}
}
} finally {
sectionStates.release();
}
boolean nowHasOnlyAir = section.hasOnlyAir();
if (hasOnlyAir != nowHasOnlyAir) {
world.getChunkSource().getLightEngine().updateSectionStatus(SectionPos.of(cx, cy, cz), nowHasOnlyAir);
}
} }
});
if(canBuildInSection)
section.finish();
}
} }
private void applyBiomeBuffer(World world, BiomeBuffer biomeBuffer) {
private void applyBiomeBuffer(MinecraftServer server, BiomeBuffer biomeBuffer, ResourceKey<Level> worldKey) { Set<Chunk> changedChunks = new HashSet<>();
server.execute(() -> { biomeBuffer.forEachEntry((x, y, z, biome) -> {
ServerLevel world = server.getLevel(worldKey); world.setBiome(x*4, y*4, z*4, biome);
if (world == null) return; int cx = x/4 - (x < 0 ? 1 : 0);
int cz = z/4 - (z < 0 ? 1 : 0);
Set<LevelChunk> changedChunks = new HashSet<>(); changedChunks.add(world.getChunkAt(cx, cz));
int minSection = world.getMinSection();
int maxSection = world.getMaxSection();
Optional<Registry<Biome>> registryOptional = world.registryAccess().registry(Registries.BIOME);
if (registryOptional.isEmpty()) return;
Registry<Biome> registry = registryOptional.get();
biomeBuffer.forEachEntry((x, y, z, biome) -> {
int cy = y >> 2;
if (cy < minSection || cy >= maxSection) {
return;
}
var chunk = (LevelChunk) world.getChunk(x >> 2, z >> 2, ChunkStatus.FULL, false);
if (chunk == null) return;
var section = chunk.getSection(cy - minSection);
PalettedContainer<Holder<Biome>> container = (PalettedContainer<Holder<Biome>>) section.getBiomes();
var holder = registry.getHolder(biome);
if (holder.isPresent()) {
container.set(x & 3, y & 3, z & 3, holder.get());
changedChunks.add(chunk);
}
});
var chunkMap = world.getChunkSource().chunkMap;
HashMap<ServerPlayer, List<LevelChunk>> map = new HashMap<>();
for (LevelChunk chunk : changedChunks) {
chunk.setUnsaved(true);
ChunkPos chunkPos = chunk.getPos();
for (ServerPlayer serverPlayer2 : chunkMap.getPlayers(chunkPos, false)) {
map.computeIfAbsent(serverPlayer2, serverPlayer -> new ArrayList<>()).add(chunk);
}
}
map.forEach((serverPlayer, list) -> serverPlayer.connection.send(ClientboundChunksBiomesPacket.forChunks(list)));
}); });
VersionWrapper.impl.publishBiomeChange(AxiomPaper.convert(world), changedChunks);
} }
} }

Datei anzeigen

@ -1,162 +1,68 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.event.AxiomModifyWorldEvent; import com.moulberry.axiom.event.AxiomModifyWorldEvent;
import io.netty.buffer.Unpooled; import com.moulberry.axiom.integration.RegionProtection;
import com.moulberry.axiom.integration.VersionTranslator;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos; import net.minecraft.core.Vec3i;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.block.Block; import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.lighting.LightEngine;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import java.lang.reflect.InvocationTargetException; public class SetBlockPacketListener implements AxiomPacketListener {
import java.lang.reflect.Method;
import java.util.Map;
import java.util.logging.Level;
public class SetBlockPacketListener implements PluginMessageListener { private static final Reflection.Method<Level, Boolean> setBlock = Reflection.getTypedMethod(Level.class, boolean.class, BlockPos.class, BlockState.class, int.class);
private final AxiomPaper plugin; private static final Reflection.Method<Vec3i, Integer> getX = Reflection.getTypedMethod(Vec3i.class, int.class, 1);
private final Method updateBlockEntityTicker; private static final Reflection.Method<Vec3i, Integer> getY = Reflection.getTypedMethod(Vec3i.class, int.class, 2);
private static final Reflection.Method<Vec3i, Integer> getZ = Reflection.getTypedMethod(Vec3i.class, int.class, 3);
public SetBlockPacketListener(AxiomPaper plugin) {
this.plugin = plugin;
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<ServerGamePacketListenerImpl, Void> ackBlockChangesUpTo = Reflection.getMethod(ServerGamePacketListenerImpl.class, int.class);
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player bukkitPlayer, @NotNull byte[] message) { public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
if (!bukkitPlayer.hasPermission("axiom.*")) { World world = player.getWorld();
return;
}
// Check if player is allowed to modify this world // Check if player is allowed to modify this world
AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(bukkitPlayer, bukkitPlayer.getWorld()); AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player, world);
Bukkit.getPluginManager().callEvent(modifyWorldEvent); Bukkit.getPluginManager().callEvent(modifyWorldEvent);
if (modifyWorldEvent.isCancelled()) return; if (modifyWorldEvent.isCancelled()) return;
// Read packet // Read packet
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); BlockPos pos = AxiomPaper.convert(buf.readLong());
BlockPos blockPos = friendlyByteBuf.readBlockPos(); BlockState state = VersionTranslator.impl.blockStateMapper(player).apply(MojBuf.readVarInt(buf));
BlockState blockState = friendlyByteBuf.readById(Block.BLOCK_STATE_REGISTRY); boolean updateNeighbors = buf.readBoolean();
boolean updateNeighbors = friendlyByteBuf.readBoolean(); int sequenceId = buf.readInt();
int sequenceId = friendlyByteBuf.readInt();
ServerPlayer player = ((CraftPlayer)bukkitPlayer).getHandle(); ServerPlayer serverPlayer = AxiomPaper.convert(player);
int x = getX.invoke(pos);
int y = getY.invoke(pos);
int z = getZ.invoke(pos);
int cx = x / 16 - (x < 0 ? 1 : 0);
int cy = y / 16 - (y < 0 ? 1 : 0);
int cz = z / 16 - (z < 0 ? 1 : 0);
// Update blocks // Update blocks
if (updateNeighbors) { if(RegionProtection.getProtection.apply(player, world).canBuildInSection(cx, cy, cz)) {
player.level().setBlock(blockPos, blockState, 3); if (updateNeighbors) {
} else { setBlock.invoke(AxiomPaper.convert(player.getWorld()), pos, state, 3);
int bx = blockPos.getX(); } else {
int by = blockPos.getY(); ChunkSectionModifier section = new ChunkSectionModifier(player.getWorld(), cx, cy, cz);
int bz = blockPos.getZ(); section.setState(x & 0xF, y & 0xF, z & 0xF, state);
int x = bx & 0xF; section.finish();
int y = by & 0xF;
int z = bz & 0xF;
int cx = bx >> 4;
int cy = by >> 4;
int cz = bz >> 4;
ServerLevel level = player.serverLevel();
LevelChunk chunk = level.getChunk(cx, cz);
chunk.setUnsaved(true);
LevelChunkSection section = chunk.getSection(level.getSectionIndexFromSectionY(cy));
boolean hasOnlyAir = section.hasOnlyAir();
Heightmap worldSurface = null;
Heightmap oceanFloor = null;
Heightmap motionBlocking = null;
Heightmap motionBlockingNoLeaves = null;
for (Map.Entry<Heightmap.Types, Heightmap> heightmap : chunk.getHeightmaps()) {
switch (heightmap.getKey()) {
case WORLD_SURFACE -> worldSurface = heightmap.getValue();
case OCEAN_FLOOR -> oceanFloor = heightmap.getValue();
case MOTION_BLOCKING -> motionBlocking = heightmap.getValue();
case MOTION_BLOCKING_NO_LEAVES -> motionBlockingNoLeaves = heightmap.getValue();
default -> {}
}
}
BlockState old = section.setBlockState(x, y, z, blockState, false);
if (blockState != old) {
Block block = blockState.getBlock();
motionBlocking.update(x, by, z, blockState);
motionBlockingNoLeaves.update(x, by, z, blockState);
oceanFloor.update(x, by, z, blockState);
worldSurface.update(x, by, z, blockState);
if (blockState.hasBlockEntity()) {
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
if (blockEntity == null) {
// There isn't a block entity here, create it!
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
chunk.addAndRegisterBlockEntity(blockEntity);
}
} else if (blockEntity.getType().isValid(blockState)) {
// Block entity is here and the type is correct
// 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);
}
} else {
// Block entity type isn't correct, we need to recreate it
chunk.removeBlockEntity(blockPos);
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
chunk.addAndRegisterBlockEntity(blockEntity);
}
}
} else if (old.hasBlockEntity()) {
chunk.removeBlockEntity(blockPos);
}
level.getChunkSource().blockChanged(blockPos);
if (LightEngine.hasDifferentLightProperties(chunk, blockPos, old, blockState)) {
level.getChunkSource().getLightEngine().checkBlock(blockPos);
}
}
boolean nowHasOnlyAir = section.hasOnlyAir();
if (hasOnlyAir != nowHasOnlyAir) {
level.getChunkSource().getLightEngine().updateSectionStatus(SectionPos.of(cx, cy, cz), nowHasOnlyAir);
} }
} }
if (sequenceId >= 0) { if (sequenceId >= 0) {
player.connection.ackBlockChangesUpTo(sequenceId); ackBlockChangesUpTo.invoke(AxiomPaper.getConnection(serverPlayer), sequenceId);
} }
} }

Datei anzeigen

@ -1,30 +1,24 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.View; import com.moulberry.axiom.View;
import com.moulberry.axiom.persistence.UUIDDataType; import com.moulberry.axiom.persistence.UUIDDataType;
import io.netty.buffer.Unpooled; import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public class SetEditorViewsPacketListener implements PluginMessageListener { public class SetEditorViewsPacketListener implements AxiomPacketListener {
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
if (!player.hasPermission("axiom.*")) { UUID uuid = MojBuf.readUUID(buf);
return; List<View> views = MojBuf.readList(buf, View::read);
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
UUID uuid = friendlyByteBuf.readUUID();
List<View> views = friendlyByteBuf.readList(View::read);
PersistentDataContainer container = player.getPersistentDataContainer(); PersistentDataContainer container = player.getPersistentDataContainer();
container.set(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE, uuid); container.set(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE, uuid);

Datei anzeigen

@ -1,28 +1,23 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.Reflection;
import com.moulberry.axiom.event.AxiomFlySpeedChangeEvent; import com.moulberry.axiom.event.AxiomFlySpeedChangeEvent;
import com.moulberry.axiom.event.AxiomGameModeChangeEvent; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.entity.player.Abilities;
import net.minecraft.world.level.GameType;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class SetFlySpeedPacketListener implements PluginMessageListener { 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 @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
if (!player.hasPermission("axiom.*")) { float flySpeed = buf.readFloat();
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
float flySpeed = friendlyByteBuf.readFloat();
// Call event // Call event
AxiomFlySpeedChangeEvent flySpeedChangeEvent = new AxiomFlySpeedChangeEvent(player, flySpeed); AxiomFlySpeedChangeEvent flySpeedChangeEvent = new AxiomFlySpeedChangeEvent(player, flySpeed);
@ -30,7 +25,7 @@ public class SetFlySpeedPacketListener implements PluginMessageListener {
if (flySpeedChangeEvent.isCancelled()) return; if (flySpeedChangeEvent.isCancelled()) return;
// Change flying speed // Change flying speed
((CraftPlayer)player).getHandle().getAbilities().setFlyingSpeed(flySpeed); flyingSpeed.set(getAbilities.invoke(AxiomPaper.convert(player)), flySpeed);
} }
} }

Datei anzeigen

@ -1,34 +1,26 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.event.AxiomGameModeChangeEvent; import com.moulberry.axiom.event.AxiomGameModeChangeEvent;
import io.netty.buffer.Unpooled; import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.GameType;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
public class SetGamemodePacketListener implements PluginMessageListener { public class SetGamemodePacketListener implements AxiomPacketListener {
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { public void onMessage(Player player, ByteBuf buf) {
if (!player.hasPermission("axiom.*")) { @SuppressWarnings("deprecation")
return; GameMode gameMode = GameMode.getByValue(buf.readByte());
} assert gameMode != null;
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
GameType gameType = GameType.byId(friendlyByteBuf.readByte());
// Call event // Call event
AxiomGameModeChangeEvent gameModeChangeEvent = new AxiomGameModeChangeEvent(player, GameMode.getByValue(gameType.getId())); AxiomGameModeChangeEvent gameModeChangeEvent = new AxiomGameModeChangeEvent(player, gameMode);
Bukkit.getPluginManager().callEvent(gameModeChangeEvent); Bukkit.getPluginManager().callEvent(gameModeChangeEvent);
if (gameModeChangeEvent.isCancelled()) return; if (gameModeChangeEvent.isCancelled()) return;
// Change gamemode // Change gamemode
((CraftPlayer)player).getHandle().setGameMode(gameType); player.setGameMode(gameMode);
} }
} }

Datei anzeigen

@ -1,35 +1,27 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.persistence.ItemStackDataType; import com.moulberry.axiom.persistence.ItemStackDataType;
import io.netty.buffer.Unpooled; import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
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.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class SetHotbarSlotPacketListener implements PluginMessageListener { public class SetHotbarSlotPacketListener implements AxiomPacketListener {
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
if (!player.hasPermission("axiom.*")) { int index = buf.readByte();
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
int index = friendlyByteBuf.readByte();
if (index < 0 || index >= 9*9) return; 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 container = player.getPersistentDataContainer();
PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER); PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER);
if (hotbarItems == null) hotbarItems = container.getAdapterContext().newPersistentDataContainer(); 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); container.set(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER, hotbarItems);
} }

Datei anzeigen

@ -1,35 +1,27 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.persistence.ItemStackDataType; import com.moulberry.axiom.persistence.ItemStackDataType;
import io.netty.buffer.Unpooled; import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Material; 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.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class SwitchActiveHotbarPacketListener implements PluginMessageListener { public class SwitchActiveHotbarPacketListener implements AxiomPacketListener {
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
if (!player.hasPermission("axiom.*")) { int oldHotbarIndex = buf.readByte();
return; int activeHotbarIndex = buf.readByte();
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
int oldHotbarIndex = friendlyByteBuf.readByte();
int activeHotbarIndex = friendlyByteBuf.readByte();
ItemStack[] hotbarItems = new ItemStack[9]; ItemStack[] hotbarItems = new ItemStack[9];
for (int i=0; i<9; i++) { for (int i=0; i<9; i++) {
hotbarItems[i] = CraftItemStack.asCraftMirror(friendlyByteBuf.readItem()); hotbarItems[i] = MojBuf.readItem(buf);
} }
PersistentDataContainer container = player.getPersistentDataContainer(); PersistentDataContainer container = player.getPersistentDataContainer();
@ -45,10 +37,10 @@ public class SwitchActiveHotbarPacketListener implements PluginMessageListener {
} else { } else {
stack = stack.clone(); 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; 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]); if (player.getGameMode() == GameMode.CREATIVE) player.getInventory().setItem(i, hotbarItems[i]);
} }

Datei anzeigen

@ -1,35 +1,25 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.event.AxiomGameModeChangeEvent; import com.moulberry.axiom.buffer.MojBuf;
import com.moulberry.axiom.event.AxiomTeleportEvent; import com.moulberry.axiom.event.AxiomTeleportEvent;
import io.netty.buffer.Unpooled; import io.netty.buffer.ByteBuf;
import net.minecraft.core.registries.Registries; import org.bukkit.Bukkit;
import net.minecraft.network.FriendlyByteBuf; import org.bukkit.Location;
import net.minecraft.resources.ResourceKey; import org.bukkit.World;
import net.minecraft.world.level.Level;
import org.bukkit.*;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class TeleportPacketListener implements PluginMessageListener { public class TeleportPacketListener implements AxiomPacketListener {
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
if (!player.hasPermission("axiom.*")) { World world = Bukkit.getWorld(MojBuf.readKey(buf));
return; double x = buf.readDouble();
} double y = buf.readDouble();
double z = buf.readDouble();
float yRot = buf.readFloat();
float xRot = buf.readFloat();
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
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();
NamespacedKey namespacedKey = new NamespacedKey(resourceKey.location().getNamespace(), resourceKey.location().getPath());
World world = Bukkit.getWorld(namespacedKey);
if (world == null) return; if (world == null) return;
// Call event // Call event

Datei anzeigen

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

Datei anzeigen

@ -7,9 +7,9 @@ import java.nio.ByteBuffer;
import java.util.UUID; import java.util.UUID;
public class UUIDDataType implements PersistentDataType<byte[], 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() { public Class<byte[]> getPrimitiveType() {
return byte[].class; return byte[].class;

Datei anzeigen

@ -0,0 +1,47 @@
package com.moulberry.axiom.version;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.Reflection;
import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Chunk;
import java.util.BitSet;
import java.util.Set;
public class Version19R2 implements VersionWrapper {
private static final Reflection.Method<BlockState, Integer> getLightBlock = Reflection.getTypedMethod(BlockState.class, int.class, BlockGetter.class, BlockPos.class);
private static final Reflection.Field<BlockState, Integer> lightEmission = Reflection.getField(BlockState.class, int.class);
private static final Reflection.Field<BlockState, Boolean> useShapeForLightOcclusion = Reflection.getField(BlockState.class, boolean.class);
@Override
public boolean hasDifferentLightProperties(BlockGetter chunk, BlockPos pos, BlockState old, BlockState state) {
return getLightBlock.invoke(old, chunk, pos) != getLightBlock.invoke(state, chunk, pos) || lightEmission.get(old) != lightEmission.get(state) || useShapeForLightOcclusion.get(old) || useShapeForLightOcclusion.get(state);
}
private static final Class<?> LevelLightEngine = Reflection.getClass("net.minecraft.world.level.lighting.LightEngine");
private static final Reflection.Constructor<ClientboundLevelChunkWithLightPacket> newClientboundLevelChunkWithLightPacket = Reflection.getConstructor(
ClientboundLevelChunkWithLightPacket.class,
LevelChunk.class, LevelLightEngine, BitSet.class, BitSet.class, boolean.class
);
@Override
public void publishBiomeChange(ServerLevel level, Set<Chunk> chunks) {
ThreadedLevelLightEngine lightEngine = AxiomPaper.getLightEngine(AxiomPaper.getChunkSource(level));
for (Chunk chunk : chunks) {
ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ());
LevelChunk nativeChunk = AxiomPaper.getChunk(level, chunk.getX(), chunk.getZ());
ClientboundLevelChunkWithLightPacket packet = newClientboundLevelChunkWithLightPacket.newInstance(nativeChunk, lightEngine, null, null, true);
for (ServerPlayer player : AxiomPaper.getPlayersSeeingChunk(level, chunkPos)) {
AxiomPaper.sendPacket(player, packet);
}
}
}
}

Datei anzeigen

@ -0,0 +1,42 @@
package com.moulberry.axiom.version;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.Reflection;
import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.game.ClientboundChunksBiomesPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.lighting.LightEngine;
import org.bukkit.Chunk;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
public class Version20R1 implements VersionWrapper {
private static final Reflection.Method<LightEngine, Boolean> hasDifferentLightProperties = Reflection.getTypedMethod(LightEngine.class, boolean.class, BlockGetter.class, BlockPos.class, BlockState.class, BlockState.class);
@Override
public boolean hasDifferentLightProperties(BlockGetter chunk, BlockPos pos, BlockState old, BlockState state) {
return hasDifferentLightProperties.invoke(null, chunk, pos, old, state);
}
private static final Reflection.Method<ClientboundChunksBiomesPacket, ClientboundChunksBiomesPacket> forChunks = Reflection.getTypedMethod(ClientboundChunksBiomesPacket.class, ClientboundChunksBiomesPacket.class, List.class);
@Override
public void publishBiomeChange(ServerLevel level, Set<Chunk> chunks) {
HashMap<ServerPlayer, List<LevelChunk>> playerChunks = new HashMap<>();
for (Chunk chunk : chunks) {
ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ());
LevelChunk nativeChunk = AxiomPaper.getChunk(level, chunk.getX(), chunk.getZ());
for (ServerPlayer player : AxiomPaper.getPlayersSeeingChunk(level, chunkPos)) {
playerChunks.computeIfAbsent(player, p -> new ArrayList<>()).add(nativeChunk);
}
}
playerChunks.forEach((serverPlayer, list) -> AxiomPaper.sendPacket(serverPlayer, forChunks.invoke(null, list)));
}
}

Datei anzeigen

@ -0,0 +1,18 @@
package com.moulberry.axiom.version;
import com.moulberry.axiom.Reflection;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Chunk;
import java.util.Set;
public interface VersionWrapper {
VersionWrapper impl = Reflection.VERSION > 1902 ? new Version20R1() : new Version19R2();
boolean hasDifferentLightProperties(BlockGetter chunk, BlockPos pos, BlockState old, BlockState state);
void publishBiomeChange(ServerLevel level, Set<Chunk> chunks);
}

Datei anzeigen

@ -2,9 +2,13 @@ name: $name
version: $version version: $version
main: com.moulberry.axiom.AxiomPaper main: com.moulberry.axiom.AxiomPaper
description: $description description: $description
softdepend:
- ViaVersion
- WorldGuard
authors: authors:
- Moulberry - Moulberry
api-version: "$apiVersion" - Lixfel
api-version: "1.17"
permissions: permissions:
axiom.*: axiom.*:
description: Allows use of all Axiom features description: Allows use of all Axiom features