WIP multiversioning
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful

Dieser Commit ist enthalten in:
Lixfel 2023-09-07 15:48:53 +02:00
Ursprung ef7886341e
Commit b8fa0aa640
27 geänderte Dateien mit 507 neuen und 475 gelöschten Zeilen

Datei anzeigen

@ -22,6 +22,7 @@ repositories {
maven("https://jitpack.io") 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 {
@ -38,6 +39,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,16 +1,10 @@
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 ACTIVE_HOTBAR_INDEX = new NamespacedKey("axiom", "active_hotbar_index");

Datei anzeigen

@ -1,105 +1,67 @@
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.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import org.bukkit.Bukkit;
import io.papermc.paper.event.player.PlayerFailMoveEvent;
import io.papermc.paper.network.ChannelInitializeListener;
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.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import org.bukkit.*;
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.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
public class AxiomPaper extends JavaPlugin implements Listener { public class AxiomPaper extends JavaPlugin implements Listener {
@Override public static final String PERMISSION = "axiom.*";
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, this);
CompressedBlockEntity.initialize(this);
Messenger msg = Bukkit.getMessenger(); private static AxiomPaper instance;
public static Plugin getPlugin() {
msg.registerOutgoingPluginChannel(this, "axiom:enable"); return instance;
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) {
if (event.getPlayer().hasPermission("axiom.*") && public static AxiomPacketListener getListener(String channel) {
event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) { return listeners.get(channel);
event.setAllowed(true); }
@Override
public void onEnable() {
instance = this;
try {
Bukkit.getPluginManager().registerEvents(new PaperFailMoveListener(), this);
} catch (NoClassDefFoundError e) {
getLogger().log(Level.WARNING, "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());
}
//TODO multiversion (Except setGamemode)
registerInChannel("hello", new HelloPacketListener());
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("handle_big_payload", new SetBlockBufferPacketListener());
}
private void registerInChannel(String channel, AxiomPacketListener handler) {
listeners.put(channel, handler);
Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:" + channel, (ch, player, message) -> {
if (!player.hasPermission(PERMISSION))
return;
handler.onMessage(player, Unpooled.wrappedBuffer(message));
});
} }
} }

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();
}
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

@ -2,8 +2,6 @@ package com.moulberry.axiom;
import com.moulberry.axiom.persistence.UUIDDataType; import com.moulberry.axiom.persistence.UUIDDataType;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;

Datei anzeigen

@ -3,7 +3,6 @@ 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 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 net.minecraft.network.FriendlyByteBuf;
@ -13,16 +12,15 @@ 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) { } catch (IOException e) {
throw new RuntimeException(e); throw new IllegalStateException(e);
} }
} }

Datei anzeigen

@ -1,7 +1,6 @@
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 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;

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,18 @@
package com.moulberry.axiom.integration;
import com.moulberry.axiom.AxiomPaper;
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(AxiomPaper.PERMISSION) &&
event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) {
event.setAllowed(true);
}
}
}

Datei anzeigen

@ -0,0 +1,22 @@
package com.moulberry.axiom.integration;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.function.IntFunction;
public interface VersionTranslator {
VersionTranslator impl = Bukkit.getPluginManager().getPlugin("ViaVersion") != null ? new ViaVersionTranslator() : new Dummy();
IntFunction<Integer> blockStateMapper(Player player);
class Dummy implements VersionTranslator {
@Override
public IntFunction<Integer> blockStateMapper(Player player) {
return id -> id;
}
}
}

Datei anzeigen

@ -0,0 +1,14 @@
package com.moulberry.axiom.integration;
import org.bukkit.entity.Player;
import java.util.function.IntFunction;
public class ViaVersionDummy implements VersionTranslator {
@Override
public IntFunction<Integer> blockStateMapper(Player player) {
return id -> id;
}
}

Datei anzeigen

@ -0,0 +1,44 @@
package com.moulberry.axiom.integration;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ServerProtocolVersion;
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<Integer> blockStateMapper(Player player) {
List<ProtocolPathEntry> path = protocolManager.getProtocolPath(Via.getAPI().getPlayerVersion(player.getUniqueId()), serverVersion.highestSupportedVersion());
if(path == null)
return id -> id;
List<IntFunction<Integer>> mappers = new ArrayList<>(path.size());
for(ProtocolPathEntry entry : path) {
MappingData mappings = entry.protocol().getMappingData();
if(mappings != null)
mappers.add(mappings::getNewBlockStateId);
}
return id -> {
for(IntFunction<Integer> transformer : mappers)
id = transformer.apply(id);
return id;
};
}
}

Datei anzeigen

@ -1,64 +1,63 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper;
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.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.List; @ChannelHandler.Sharable
public class AxiomBigPayloadHandler extends ChannelInboundHandlerAdapter {
public class AxiomBigPayloadHandler extends ByteToMessageDecoder { private static final int PLUGINMESSAGE_PACKETID = ConnectionProtocol.PLAY.getPacketId(PacketFlow.SERVERBOUND, new ServerboundCustomPayloadPacket(null, null));
private static final ResourceLocation SET_BUFFER = new ResourceLocation("axiom", "set_buffer"); private final Player player;
private final int payloadId;
private final Connection connection;
private final SetBlockBufferPacketListener listener;
public AxiomBigPayloadHandler(int payloadId, Connection connection, SetBlockBufferPacketListener listener) { public AxiomBigPayloadHandler(Player player) {
this.payloadId = payloadId; this.player = player;
this.connection = connection;
this.listener = listener;
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) throws Exception {
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) {
FriendlyByteBuf buf = new FriendlyByteBuf((ByteBuf) msg);
if (buf.readVarInt() == PLUGINMESSAGE_PACKETID) {
ResourceLocation identifier = buf.readResourceLocation();
if (identifier.getNamespace().equals("axiom") && player.hasPermission(AxiomPaper.PERMISSION)) {
AxiomPacketListener listener = AxiomPaper.getListener(identifier.getPath());
//TODO sync!
listener.onMessage(player, in);
in.release();
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);
} }

Datei anzeigen

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

Datei anzeigen

@ -2,49 +2,62 @@ package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.OutChannel;
import com.moulberry.axiom.View; 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.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey; 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.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.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.Collections;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class HelloPacketListener implements PluginMessageListener { public class HelloPacketListener implements AxiomPacketListener, Listener {
private final AxiomPaper plugin; private final Set<Player> axiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final Set<UUID> activeAxiomPlayers;
public HelloPacketListener(AxiomPaper plugin, Set<UUID> activeAxiomPlayers) { public HelloPacketListener() {
this.plugin = plugin; //TODO cleanup (side effects of class)
this.activeAxiomPlayers = activeAxiomPlayers; Bukkit.getPluginManager().registerEvents(this, AxiomPaper.getPlugin());
Bukkit.getScheduler().scheduleSyncRepeatingTask(AxiomPaper.getPlugin(), () -> axiomPlayers.removeIf(player -> {
if (!player.hasPermission(AxiomPaper.PERMISSION)) {
OutChannel.ENABLE.send(player, new byte[] { 0 });
return true;
}
return false;
}), 20, 20);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
axiomPlayers.remove(event.getPlayer());
} }
@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.*")) { FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
int apiVersion = friendlyByteBuf.readVarInt(); int apiVersion = friendlyByteBuf.readVarInt();
friendlyByteBuf.readNbt(); // Discard 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 +68,54 @@ public class HelloPacketListener implements PluginMessageListener {
return; return;
} }
activeAxiomPlayers.add(player.getUniqueId()); axiomPlayers.add(player);
//TODO thread safety
((CraftPlayer)player).getHandle().connection.connection.channel.pipeline().addBefore("decoder", "axiom-big-payload-handler", new AxiomBigPayloadHandler(player));
// Enable // Enable
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); FriendlyByteBuf out = new FriendlyByteBuf(Unpooled.buffer());
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 out.writeVarInt(5); // Maximum Reach
buf.writeVarInt(16); // Max editor views out.writeVarInt(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 = new FriendlyByteBuf(Unpooled.buffer());
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); out.writeItem(net.minecraft.world.item.ItemStack.EMPTY);
} else { } else {
ItemStack stack = hotbarItems.get(new NamespacedKey("axiom", "slot_"+i), ItemStackDataType.INSTANCE); ItemStack stack = hotbarItems.get(new NamespacedKey("axiom", "slot_"+i), ItemStackDataType.INSTANCE);
buf.writeItem(CraftItemStack.asNMSCopy(stack)); out.writeItem(CraftItemStack.asNMSCopy(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 = new FriendlyByteBuf(Unpooled.buffer());
buf.writeUUID(activeView); out.writeUUID(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); out.writeVarInt(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,9 +1,11 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.OutChannel;
import com.moulberry.axiom.buffer.CompressedBlockEntity; import com.moulberry.axiom.buffer.CompressedBlockEntity;
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.core.registries.Registries;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@ -16,27 +18,20 @@ 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.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;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
public class RequestBlockEntityPacketListener implements PluginMessageListener { public class RequestBlockEntityPacketListener implements AxiomPacketListener {
private final AxiomPaper plugin;
public RequestBlockEntityPacketListener(AxiomPaper plugin) {
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)); FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
long id = friendlyByteBuf.readLong(); long id = friendlyByteBuf.readLong();
if (!bukkitPlayer.hasPermission("axiom.*")) { if (!bukkitPlayer.hasPermission("axiom.*")) {
// We always send an 'empty' response in order to make the client happy // We always send an 'empty' response in order to make the client happy
sendEmptyResponse(bukkitPlayer, id); sendEmptyResponse(bukkitPlayer, id); //TODO
return; return;
} }
@ -70,21 +65,21 @@ public class RequestBlockEntityPacketListener implements PluginMessageListener {
} }
// Send response packet // Send response packet
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(16)); FriendlyByteBuf out = new FriendlyByteBuf(Unpooled.buffer(16));
buf.writeLong(id); out.writeLong(id);
buf.writeVarInt(map.size()); out.writeVarInt(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);
} }
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,26 +1,11 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
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.BlockBuffer;
import com.moulberry.axiom.buffer.CompressedBlockEntity; import com.moulberry.axiom.buffer.CompressedBlockEntity;
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 io.netty.buffer.ByteBuf;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.World;
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.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -48,26 +33,19 @@ import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.lighting.LightEngine; import net.minecraft.world.level.lighting.LightEngine;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey; import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
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 xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
public class SetBlockBufferPacketListener { public class SetBlockBufferPacketListener implements AxiomPacketListener {
private final AxiomPaper plugin;
private final Method updateBlockEntityTicker; private final Method updateBlockEntityTicker;
public SetBlockBufferPacketListener(AxiomPaper plugin) { public SetBlockBufferPacketListener() {
this.plugin = plugin;
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar(); ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class); String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class);
@ -80,9 +58,11 @@ public class SetBlockBufferPacketListener {
} }
} }
public boolean onReceive(ServerPlayer player, FriendlyByteBuf friendlyByteBuf) { @Override
MinecraftServer server = player.getServer(); public void onMessage(Player player, ByteBuf buf) {
if (server == null) return false; FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
MinecraftServer server = ((CraftServer)player.getServer()).getServer();
if (server == null) return;
ResourceKey<Level> worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); ResourceKey<Level> worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
friendlyByteBuf.readUUID(); // Discard, we don't need to associate buffers friendlyByteBuf.readUUID(); // Discard, we don't need to associate buffers
@ -102,205 +82,199 @@ public class SetBlockBufferPacketListener {
} else { } else {
throw new RuntimeException("Unknown buffer type: " + type); throw new RuntimeException("Unknown buffer type: " + type);
} }
return true;
} }
private void applyBlockBuffer(ServerPlayer player, MinecraftServer server, BlockBuffer buffer, ResourceKey<Level> worldKey) { private void applyBlockBuffer(Player player, MinecraftServer server, BlockBuffer buffer, ResourceKey<Level> worldKey) {
server.execute(() -> { ServerLevel world = server.getLevel(worldKey);
ServerLevel world = server.getLevel(worldKey); if (world == null) return;
if (world == null) return;
// Call AxiomModifyWorldEvent event // Call AxiomModifyWorldEvent event
AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player.getBukkitEntity(), world.getWorld()); AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player, world.getWorld());
Bukkit.getPluginManager().callEvent(modifyWorldEvent); Bukkit.getPluginManager().callEvent(modifyWorldEvent);
if (modifyWorldEvent.isCancelled()) return; if (modifyWorldEvent.isCancelled()) return;
RegionProtection regionProtection = new RegionProtection(player.getBukkitEntity(), world.getWorld()); RegionProtection regionProtection = new RegionProtection(player, world.getWorld());
// Allowed, apply buffer // Allowed, apply buffer
BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
var lightEngine = world.getChunkSource().getLightEngine(); var lightEngine = world.getChunkSource().getLightEngine();
BlockState emptyState = BlockBuffer.EMPTY_STATE; BlockState emptyState = BlockBuffer.EMPTY_STATE;
for (Long2ObjectMap.Entry<PalettedContainer<BlockState>> entry : buffer.entrySet()) { for (Long2ObjectMap.Entry<PalettedContainer<BlockState>> entry : buffer.entrySet()) {
int cx = BlockPos.getX(entry.getLongKey()); int cx = BlockPos.getX(entry.getLongKey());
int cy = BlockPos.getY(entry.getLongKey()); int cy = BlockPos.getY(entry.getLongKey());
int cz = BlockPos.getZ(entry.getLongKey()); int cz = BlockPos.getZ(entry.getLongKey());
PalettedContainer<BlockState> container = entry.getValue(); PalettedContainer<BlockState> container = entry.getValue();
if (cy < world.getMinSection() || cy >= world.getMaxSection()) { if (cy < world.getMinSection() || cy >= world.getMaxSection()) {
continue; 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 -> {}
} }
}
if (!regionProtection.canBuildInSection(cx, cy, cz)) { Short2ObjectMap<CompressedBlockEntity> blockEntityChunkMap = buffer.getBlockEntityChunkMap(entry.getLongKey());
continue;
}
LevelChunk chunk = world.getChunk(cx, cz); sectionStates.acquire();
chunk.setUnsaved(true); 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;
LevelChunkSection section = chunk.getSection(world.getSectionIndexFromSectionY(cy)); int bx = cx*16 + x;
PalettedContainer<BlockState> sectionStates = section.getStates(); int by = cy*16 + y;
boolean hasOnlyAir = section.hasOnlyAir(); int bz = cz*16 + z;
Heightmap worldSurface = null; blockPos.set(bx, by, bz);
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()); if (hasOnlyAir && blockState.isAir()) {
continue;
}
sectionStates.acquire(); BlockState old = section.setBlockState(x, y, z, blockState, false);
try { if (blockState != old) {
for (int x = 0; x < 16; x++) { Block block = blockState.getBlock();
for (int y = 0; y < 16; y++) { motionBlocking.update(x, by, z, blockState);
for (int z = 0; z < 16; z++) { motionBlockingNoLeaves.update(x, by, z, blockState);
BlockState blockState = container.get(x, y, z); oceanFloor.update(x, by, z, blockState);
if (blockState == emptyState) continue; worldSurface.update(x, by, z, blockState);
int bx = cx*16 + x; if (false) { // Full update
int by = cy*16 + y; old.onRemove(world, blockPos, blockState, false);
int bz = cz*16 + z;
blockPos.set(bx, by, bz); if (sectionStates.get(x, y, z).is(block)) {
blockState.onPlace(world, blockPos, old, false);
if (hasOnlyAir && blockState.isAir()) { }
continue;
} }
BlockState old = section.setBlockState(x, y, z, blockState, false); if (blockState.hasBlockEntity()) {
if (blockState != old) { BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
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 if (blockEntity == null) {
old.onRemove(world, blockPos, blockState, false); // There isn't a block entity here, create it!
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (sectionStates.get(x, y, z).is(block)) { if (blockEntity != null) {
blockState.onPlace(world, blockPos, old, false); chunk.addAndRegisterBlockEntity(blockEntity);
} }
} } else if (blockEntity.getType().isValid(blockState)) {
// Block entity is here and the type is correct
blockEntity.setBlockState(blockState);
if (blockState.hasBlockEntity()) { try {
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); this.updateBlockEntityTicker.invoke(chunk, blockEntity);
} catch (IllegalAccessException | InvocationTargetException e) {
if (blockEntity == null) { throw new RuntimeException(e);
// 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) { } else {
int key = x | (y << 4) | (z << 8); // Block entity type isn't correct, we need to recreate it
CompressedBlockEntity savedBlockEntity = blockEntityChunkMap.get((short) key);
if (savedBlockEntity != null) {
blockEntity.load(savedBlockEntity.decompress());
}
}
} else if (old.hasBlockEntity()) {
chunk.removeBlockEntity(blockPos); chunk.removeBlockEntity(blockPos);
}
world.getChunkSource().blockChanged(blockPos); // todo: maybe simply resend chunk instead of this? blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
if (LightEngine.hasDifferentLightProperties(chunk, blockPos, old, blockState)) { chunk.addAndRegisterBlockEntity(blockEntity);
lightEngine.checkBlock(blockPos); }
} }
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);
} }
} finally {
sectionStates.release();
} }
});
boolean nowHasOnlyAir = section.hasOnlyAir();
if (hasOnlyAir != nowHasOnlyAir) {
world.getChunkSource().getLightEngine().updateSectionStatus(SectionPos.of(cx, cy, cz), nowHasOnlyAir);
}
}
} }
private void applyBiomeBuffer(MinecraftServer server, BiomeBuffer biomeBuffer, ResourceKey<Level> worldKey) { private void applyBiomeBuffer(MinecraftServer server, BiomeBuffer biomeBuffer, ResourceKey<Level> worldKey) {
server.execute(() -> { ServerLevel world = server.getLevel(worldKey);
ServerLevel world = server.getLevel(worldKey); if (world == null) return;
if (world == null) return;
Set<LevelChunk> changedChunks = new HashSet<>(); Set<LevelChunk> changedChunks = new HashSet<>();
int minSection = world.getMinSection(); int minSection = world.getMinSection();
int maxSection = world.getMaxSection(); int maxSection = world.getMaxSection();
Optional<Registry<Biome>> registryOptional = world.registryAccess().registry(Registries.BIOME); Optional<Registry<Biome>> registryOptional = world.registryAccess().registry(Registries.BIOME);
if (registryOptional.isEmpty()) return; if (registryOptional.isEmpty()) return;
Registry<Biome> registry = registryOptional.get(); Registry<Biome> registry = registryOptional.get();
biomeBuffer.forEachEntry((x, y, z, biome) -> { biomeBuffer.forEachEntry((x, y, z, biome) -> {
int cy = y >> 2; int cy = y >> 2;
if (cy < minSection || cy >= maxSection) { if (cy < minSection || cy >= maxSection) {
return; return;
} }
var chunk = (LevelChunk) world.getChunk(x >> 2, z >> 2, ChunkStatus.FULL, false); var chunk = (LevelChunk) world.getChunk(x >> 2, z >> 2, ChunkStatus.FULL, false);
if (chunk == null) return; if (chunk == null) return;
var section = chunk.getSection(cy - minSection); var section = chunk.getSection(cy - minSection);
PalettedContainer<Holder<Biome>> container = (PalettedContainer<Holder<Biome>>) section.getBiomes(); PalettedContainer<Holder<Biome>> container = (PalettedContainer<Holder<Biome>>) section.getBiomes();
var holder = registry.getHolder(biome); var holder = registry.getHolder(biome);
if (holder.isPresent()) { if (holder.isPresent()) {
container.set(x & 3, y & 3, z & 3, holder.get()); container.set(x & 3, y & 3, z & 3, holder.get());
changedChunks.add(chunk); 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)));
}); });
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)));
} }
} }

Datei anzeigen

@ -1,8 +1,8 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.event.AxiomModifyWorldEvent; import com.moulberry.axiom.event.AxiomModifyWorldEvent;
import io.netty.buffer.Unpooled; 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.SectionPos;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
@ -19,23 +19,18 @@ 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.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;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper; import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
public class SetBlockPacketListener implements PluginMessageListener { public class SetBlockPacketListener implements AxiomPacketListener {
private final AxiomPaper plugin;
private final Method updateBlockEntityTicker; private final Method updateBlockEntityTicker;
public SetBlockPacketListener(AxiomPaper plugin) { public SetBlockPacketListener() {
this.plugin = plugin;
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar(); ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class); String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class);
@ -49,20 +44,16 @@ public class SetBlockPacketListener implements PluginMessageListener {
} }
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player bukkitPlayer, @NotNull byte[] message) { public void onMessage(@NotNull Player bukkitPlayer, @NotNull ByteBuf buf) {
if (!bukkitPlayer.hasPermission("axiom.*")) {
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(bukkitPlayer, bukkitPlayer.getWorld());
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)); FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
BlockPos blockPos = friendlyByteBuf.readBlockPos(); BlockPos blockPos = friendlyByteBuf.readBlockPos();
BlockState blockState = friendlyByteBuf.readById(Block.BLOCK_STATE_REGISTRY); BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(VersionTranslator.impl.blockStateMapper(bukkitPlayer).apply(friendlyByteBuf.readVarInt()));
boolean updateNeighbors = friendlyByteBuf.readBoolean(); boolean updateNeighbors = friendlyByteBuf.readBoolean();
int sequenceId = friendlyByteBuf.readInt(); int sequenceId = friendlyByteBuf.readInt();

Datei anzeigen

@ -3,26 +3,21 @@ package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
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 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.*")) { FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
UUID uuid = friendlyByteBuf.readUUID(); UUID uuid = friendlyByteBuf.readUUID();
List<View> views = friendlyByteBuf.readList(View::read); List<View> views = friendlyByteBuf.readList(View::read);

Datei anzeigen

@ -1,28 +1,17 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
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.network.FriendlyByteBuf;
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.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 {
@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);

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

@ -2,26 +2,20 @@ package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
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 net.minecraft.network.FriendlyByteBuf;
import org.bukkit.NamespacedKey; 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.craftbukkit.v1_20_R1.inventory.CraftItemStack;
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;
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.*")) { FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
int index = friendlyByteBuf.readByte(); 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(); net.minecraft.world.item.ItemStack nmsStack = friendlyByteBuf.readItem();

Datei anzeigen

@ -2,7 +2,7 @@ package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
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 net.minecraft.network.FriendlyByteBuf;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Material; import org.bukkit.Material;
@ -12,18 +12,13 @@ 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.*")) { FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
int oldHotbarIndex = friendlyByteBuf.readByte(); int oldHotbarIndex = friendlyByteBuf.readByte();
int activeHotbarIndex = friendlyByteBuf.readByte(); int activeHotbarIndex = friendlyByteBuf.readByte();

Datei anzeigen

@ -1,26 +1,23 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.event.AxiomGameModeChangeEvent;
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 net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import org.bukkit.*; import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
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;
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.*")) { FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf);
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
ResourceKey<Level> resourceKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); ResourceKey<Level> resourceKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
double x = friendlyByteBuf.readDouble(); double x = friendlyByteBuf.readDouble();
double y = friendlyByteBuf.readDouble(); double y = friendlyByteBuf.readDouble();

Datei anzeigen

@ -2,8 +2,12 @@ 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
- Lixfel
api-version: "$apiVersion" api-version: "$apiVersion"
permissions: permissions:
axiom.*: axiom.*: