3
0
Mirror von https://github.com/Moulberry/AxiomPaperPlugin.git synchronisiert 2024-11-17 13:50:05 +01:00

Support 4.2.0 changes

Dieser Commit ist enthalten in:
Moulberry 2024-10-11 16:13:48 +08:00
Ursprung 1383782ee5
Commit 3669970898
15 geänderte Dateien mit 279 neuen und 139 gelöschten Zeilen

Datei anzeigen

@ -4,7 +4,7 @@ bom-newest = "1.37"
cloud-paper = "2.0.0-20240516.054251-69" cloud-paper = "2.0.0-20240516.054251-69"
coreprotect = "22.4" coreprotect = "22.4"
paper = "1.20.6-R0.1-SNAPSHOT" paper = "1.20.6-R0.1-SNAPSHOT"
plotsquared = "7.3.9-20240513.192211-13" plotsquared = "7.3.8"
reflection-remapper = "0.1.2-20240315.033304-2" reflection-remapper = "0.1.2-20240315.033304-2"
viaversion-api = "5.0.1" viaversion-api = "5.0.1"
worldguard-bukkit = "7.0.9-SNAPSHOT" worldguard-bukkit = "7.0.9-SNAPSHOT"

Datei anzeigen

@ -9,25 +9,7 @@ import com.moulberry.axiom.event.AxiomModifyWorldEvent;
import com.moulberry.axiom.integration.coreprotect.CoreProtectIntegration; import com.moulberry.axiom.integration.coreprotect.CoreProtectIntegration;
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration; import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
import com.moulberry.axiom.packet.*; import com.moulberry.axiom.packet.*;
import com.moulberry.axiom.packet.impl.BlueprintRequestPacketListener; import com.moulberry.axiom.packet.impl.*;
import com.moulberry.axiom.packet.impl.DeleteEntityPacketListener;
import com.moulberry.axiom.packet.impl.HelloPacketListener;
import com.moulberry.axiom.packet.impl.ManipulateEntityPacketListener;
import com.moulberry.axiom.packet.impl.MarkerNbtRequestPacketListener;
import com.moulberry.axiom.packet.impl.RequestChunkDataPacketListener;
import com.moulberry.axiom.packet.impl.SetBlockBufferPacketListener;
import com.moulberry.axiom.packet.impl.SetBlockPacketListener;
import com.moulberry.axiom.packet.impl.SetEditorViewsPacketListener;
import com.moulberry.axiom.packet.impl.SetFlySpeedPacketListener;
import com.moulberry.axiom.packet.impl.SetGamemodePacketListener;
import com.moulberry.axiom.packet.impl.SetHotbarSlotPacketListener;
import com.moulberry.axiom.packet.impl.SetTimePacketListener;
import com.moulberry.axiom.packet.impl.SetWorldPropertyListener;
import com.moulberry.axiom.packet.impl.SpawnEntityPacketListener;
import com.moulberry.axiom.packet.impl.SwitchActiveHotbarPacketListener;
import com.moulberry.axiom.packet.impl.TeleportPacketListener;
import com.moulberry.axiom.packet.impl.UpdateAnnotationPacketListener;
import com.moulberry.axiom.packet.impl.UploadBlueprintPacketListener;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry; import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
@ -46,6 +28,7 @@ import net.minecraft.network.protocol.game.GameProtocols;
import net.minecraft.network.protocol.game.ServerGamePacketListener; import net.minecraft.network.protocol.game.ServerGamePacketListener;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -68,6 +51,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.IntFunction;
public class AxiomPaper extends JavaPlugin implements Listener { public class AxiomPaper extends JavaPlugin implements Listener {
@ -82,6 +66,9 @@ public class AxiomPaper extends JavaPlugin implements Listener {
public IdMapper<BlockState> allowedBlockRegistry = null; public IdMapper<BlockState> allowedBlockRegistry = null;
private boolean logLargeBlockBufferChanges = false; private boolean logLargeBlockBufferChanges = false;
private int packetCollectionReadLimit = 1024;
private Set<EntityType<?>> whitelistedEntities = new HashSet<>();
private Set<EntityType<?>> blacklistedEntities = new HashSet<>();
public Path blueprintFolder = null; public Path blueprintFolder = null;
public boolean allowAnnotations = false; public boolean allowAnnotations = false;
@ -101,9 +88,21 @@ public class AxiomPaper extends JavaPlugin implements Listener {
this.getLogger().warning("Invalid value for unsupported-axiom-version, expected 'kick', 'warn' or 'ignore'"); this.getLogger().warning("Invalid value for unsupported-axiom-version, expected 'kick', 'warn' or 'ignore'");
} }
boolean allowLargeChunkDataRequest = this.configuration.getBoolean("allow-large-chunk-data-request");
this.logLargeBlockBufferChanges = this.configuration.getBoolean("log-large-block-buffer-changes"); this.logLargeBlockBufferChanges = this.configuration.getBoolean("log-large-block-buffer-changes");
if (this.configuration.getBoolean("allow-large-payload-for-all-packets")) {
packetCollectionReadLimit = Short.MAX_VALUE;
}
this.whitelistedEntities.clear();
this.blacklistedEntities.clear();
for (String whitelistedEntity : this.configuration.getStringList("whitelist-entities")) {
EntityType.byString(whitelistedEntity).ifPresent(this.whitelistedEntities::add);
}
for (String blacklistedEntity : this.configuration.getStringList("blacklist-entities")) {
EntityType.byString(blacklistedEntity).ifPresent(this.blacklistedEntities::add);
}
List<String> disallowedBlocks = this.configuration.getStringList("disallowed-blocks"); List<String> disallowedBlocks = this.configuration.getStringList("disallowed-blocks");
this.allowedBlockRegistry = DisallowedBlocks.createAllowedBlockRegistry(disallowedBlocks); this.allowedBlockRegistry = DisallowedBlocks.createAllowedBlockRegistry(disallowedBlocks);
@ -132,27 +131,29 @@ public class AxiomPaper extends JavaPlugin implements Listener {
Map<String, PacketHandler> largePayloadHandlers = new HashMap<>(); Map<String, PacketHandler> largePayloadHandlers = new HashMap<>();
registerPacketHandler("hello", new HelloPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("hello", new HelloPacketListener(this), msg, LargePayloadBehaviour.FORCE_SMALL, largePayloadHandlers);
registerPacketHandler("set_gamemode", new SetGamemodePacketListener(this), msg, largePayloadHandlers); registerPacketHandler("set_gamemode", new SetGamemodePacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("set_fly_speed", new SetFlySpeedPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("set_fly_speed", new SetFlySpeedPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("set_world_time", new SetTimePacketListener(this), msg, largePayloadHandlers); registerPacketHandler("set_world_time", new SetTimePacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("set_world_property", new SetWorldPropertyListener(this), msg, largePayloadHandlers); registerPacketHandler("set_world_property", new SetWorldPropertyListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("set_block", new SetBlockPacketListener(this), msg, largePayloadHandlers); // set-single-block registerPacketHandler("set_block", new SetBlockPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers); // set-single-block
registerPacketHandler("set_hotbar_slot", new SetHotbarSlotPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("set_hotbar_slot", new SetHotbarSlotPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("switch_active_hotbar", new SwitchActiveHotbarPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("switch_active_hotbar", new SwitchActiveHotbarPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("teleport", new TeleportPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("teleport", new TeleportPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("set_editor_views", new SetEditorViewsPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("set_editor_views", new SetEditorViewsPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("request_chunk_data", new RequestChunkDataPacketListener(this, registerPacketHandler("request_chunk_data", new RequestChunkDataPacketListener(this, !configuration.getBoolean("packet-handlers.request-chunk-data")), msg,
!configuration.getBoolean("packet-handlers.request-chunk-data")), msg, largePayloadHandlers); this.configuration.getBoolean("allow-large-chunk-data-request") ? LargePayloadBehaviour.FORCE_LARGE : LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("spawn_entity", new SpawnEntityPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("request_entity_data", new RequestEntityDataPacketListener(this, !configuration.getBoolean("packet-handlers.request-entity-data")), msg,
registerPacketHandler("manipulate_entity", new ManipulateEntityPacketListener(this), msg, largePayloadHandlers); this.configuration.getBoolean("allow-large-chunk-data-request") ? LargePayloadBehaviour.FORCE_LARGE : LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("delete_entity", new DeleteEntityPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("spawn_entity", new SpawnEntityPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("marker_nbt_request", new MarkerNbtRequestPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("manipulate_entity", new ManipulateEntityPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("request_blueprint", new BlueprintRequestPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("delete_entity", new DeleteEntityPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("marker_nbt_request", new MarkerNbtRequestPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("request_blueprint", new BlueprintRequestPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
registerPacketHandler("set_buffer", new SetBlockBufferPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("set_buffer", new SetBlockBufferPacketListener(this), msg, LargePayloadBehaviour.FORCE_LARGE, largePayloadHandlers);
registerPacketHandler("upload_blueprint", new UploadBlueprintPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("upload_blueprint", new UploadBlueprintPacketListener(this), msg, LargePayloadBehaviour.FORCE_LARGE, largePayloadHandlers);
registerPacketHandler("annotation_update", new UpdateAnnotationPacketListener(this), msg, largePayloadHandlers); registerPacketHandler("annotation_update", new UpdateAnnotationPacketListener(this), msg, LargePayloadBehaviour.FORCE_LARGE, largePayloadHandlers);
if (!largePayloadHandlers.isEmpty()) { if (!largePayloadHandlers.isEmpty()) {
// Hack to figure out the id of the CustomPayload packet // Hack to figure out the id of the CustomPayload packet
@ -299,25 +300,26 @@ public class AxiomPaper extends JavaPlugin implements Listener {
} }
} }
private void registerPacketHandler(String name, PacketHandler handler, Messenger messenger, Map<String, PacketHandler> largePayloadHandlers) { private enum LargePayloadBehaviour {
DEFAULT,
FORCE_LARGE,
FORCE_SMALL
}
private void registerPacketHandler(String name, PacketHandler handler, Messenger messenger, LargePayloadBehaviour behaviour,
Map<String, PacketHandler> largePayloadHandlers) {
String configEntry = "packet-handlers." + name.replace("_", "-"); String configEntry = "packet-handlers." + name.replace("_", "-");
if (name.equals("set_block")) { if (name.equals("set_block")) {
configEntry = "packet-handlers.set-single-block"; configEntry = "packet-handlers.set-single-block";
} else if (name.equals("request_blueprint")) { } else if (name.equals("request_blueprint")) {
configEntry = "packet-handlers.blueprint-request"; configEntry = "packet-handlers.blueprint-request";
} }
if (name.equals("request-chunk-data") || this.configuration.getBoolean(configEntry, true)) { if (name.equals("request_chunk_data") || name.equals("request_entity_data") || this.configuration.getBoolean(configEntry, true)) {
boolean isLargePayload = false; boolean isLargePayload = switch (behaviour) {
case DEFAULT -> this.configuration.getBoolean("allow-large-payload-for-all-packets");
if (name.equals("hello")) { // Hello must use normal system, as non-Axiom players can't send large payloads case FORCE_LARGE -> true;
isLargePayload = false; case FORCE_SMALL -> false;
} else if (this.configuration.getBoolean("allow-large-payload-for-all-packets")) { };
isLargePayload = true;
} else if (name.equals("set_buffer") || name.equals("upload_blueprint") || name.equals("annotation_update")) {
isLargePayload = true;
} else if (name.equals("request_chunk_data") && this.configuration.getBoolean("allow-large-chunk-data-request")) {
isLargePayload = true;
}
if (isLargePayload) { if (isLargePayload) {
largePayloadHandlers.put("axiom:"+name, handler); largePayloadHandlers.put("axiom:"+name, handler);
@ -347,6 +349,10 @@ public class AxiomPaper extends JavaPlugin implements Listener {
return allowedCapabilities; return allowedCapabilities;
} }
public <T> IntFunction<T> limitCollection(IntFunction<T> applier) {
return FriendlyByteBuf.limitValue(applier, this.packetCollectionReadLimit);
}
public boolean logLargeBlockBufferChanges() { public boolean logLargeBlockBufferChanges() {
return this.logLargeBlockBufferChanges; return this.logLargeBlockBufferChanges;
} }
@ -376,6 +382,19 @@ public class AxiomPaper extends JavaPlugin implements Listener {
return activeAxiomPlayers.contains(player.getUniqueId()) && hasAxiomPermission(player, permission, strict); return activeAxiomPlayers.contains(player.getUniqueId()) && hasAxiomPermission(player, permission, strict);
} }
public boolean canEntityBeManipulated(EntityType<?> entityType) {
if (entityType == EntityType.PLAYER) {
return false;
}
if (!this.whitelistedEntities.isEmpty() && !this.whitelistedEntities.contains(entityType)) {
return false;
}
if (this.blacklistedEntities.contains(entityType)) {
return false;
}
return true;
}
public @Nullable RateLimiter getBlockBufferRateLimiter(UUID uuid) { public @Nullable RateLimiter getBlockBufferRateLimiter(UUID uuid) {
return this.playerBlockBufferRateLimiters.get(uuid); return this.playerBlockBufferRateLimiters.get(uuid);
} }

Datei anzeigen

@ -21,6 +21,15 @@ public class NbtSanitization {
"Glowing", "Glowing",
"Tags", "Tags",
"Passengers", "Passengers",
// armor stand
"ArmorItems",
"HandItems",
"Small",
"ShowArms",
"DisabledSlots",
"NoBasePlate",
"Marker",
"Pose",
// marker // marker
"data", "data",
// display entity // display entity
@ -50,6 +59,10 @@ public class NbtSanitization {
); );
public static void sanitizeEntity(CompoundTag entityRoot) { public static void sanitizeEntity(CompoundTag entityRoot) {
if (AxiomPaper.PLUGIN.configuration.getBoolean("disable-entity-sanitization")) {
return;
}
entityRoot.getAllKeys().retainAll(ALLOWED_KEYS); entityRoot.getAllKeys().retainAll(ALLOWED_KEYS);
if (entityRoot.contains("Passengers", Tag.TAG_LIST)) { if (entityRoot.contains("Passengers", Tag.TAG_LIST)) {

Datei anzeigen

@ -9,24 +9,25 @@ import net.minecraft.network.FriendlyByteBuf;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public record BlueprintHeader(String name, String author, List<String> tags, float thumbnailYaw, float thumbnailPitch, boolean lockedThumbnail, int blockCount) { public record BlueprintHeader(String name, String author, List<String> tags, float thumbnailYaw, float thumbnailPitch, boolean lockedThumbnail, int blockCount, boolean containsAir) {
private static final int CURRENT_VERSION = 0; private static final int CURRENT_VERSION = 1;
public void write(FriendlyByteBuf friendlyByteBuf) { public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeUtf(this.name); friendlyByteBuf.writeUtf(this.name);
friendlyByteBuf.writeUtf(this.author); friendlyByteBuf.writeUtf(this.author);
friendlyByteBuf.writeCollection(this.tags, FriendlyByteBuf::writeUtf); friendlyByteBuf.writeCollection(this.tags, FriendlyByteBuf::writeUtf);
friendlyByteBuf.writeInt(this.blockCount); friendlyByteBuf.writeInt(this.blockCount);
friendlyByteBuf.writeBoolean(this.containsAir);
} }
public static BlueprintHeader read(FriendlyByteBuf friendlyByteBuf) { public static BlueprintHeader read(FriendlyByteBuf friendlyByteBuf) {
String name = friendlyByteBuf.readUtf(); String name = friendlyByteBuf.readUtf();
String author = friendlyByteBuf.readUtf(); String author = friendlyByteBuf.readUtf();
List<String> tags = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000), List<String> tags = friendlyByteBuf.readList(FriendlyByteBuf::readUtf);
FriendlyByteBuf::readUtf);
int blockCount = friendlyByteBuf.readInt(); int blockCount = friendlyByteBuf.readInt();
return new BlueprintHeader(name, author, tags, 0, 0, true, blockCount); boolean containsAir = friendlyByteBuf.readBoolean();
return new BlueprintHeader(name, author, tags, 0, 0, true, blockCount, containsAir);
} }
public static BlueprintHeader load(CompoundTag tag) { public static BlueprintHeader load(CompoundTag tag) {
@ -37,13 +38,14 @@ public record BlueprintHeader(String name, String author, List<String> tags, flo
float thumbnailPitch = tag.contains("ThumbnailPitch", Tag.TAG_FLOAT) ? tag.getFloat("ThumbnailPitch") : 30; float thumbnailPitch = tag.contains("ThumbnailPitch", Tag.TAG_FLOAT) ? tag.getFloat("ThumbnailPitch") : 30;
boolean lockedThumbnail = tag.getBoolean("LockedThumbnail"); boolean lockedThumbnail = tag.getBoolean("LockedThumbnail");
int blockCount = tag.getInt("BlockCount"); int blockCount = tag.getInt("BlockCount");
boolean containsAir = tag.getBoolean("ContainsAir");
List<String> tags = new ArrayList<>(); List<String> tags = new ArrayList<>();
for (Tag string : tag.getList("Tags", Tag.TAG_STRING)) { for (Tag string : tag.getList("Tags", Tag.TAG_STRING)) {
tags.add(string.getAsString()); tags.add(string.getAsString());
} }
return new BlueprintHeader(name, author, tags, thumbnailYaw, thumbnailPitch, lockedThumbnail, blockCount); return new BlueprintHeader(name, author, tags, thumbnailYaw, thumbnailPitch, lockedThumbnail, blockCount, containsAir);
} }
public CompoundTag save(CompoundTag tag) { public CompoundTag save(CompoundTag tag) {
@ -60,6 +62,7 @@ public record BlueprintHeader(String name, String author, List<String> tags, flo
tag.putFloat("ThumbnailPitch", this.thumbnailPitch); tag.putFloat("ThumbnailPitch", this.thumbnailPitch);
tag.putBoolean("LockedThumbnail", this.lockedThumbnail); tag.putBoolean("LockedThumbnail", this.lockedThumbnail);
tag.putInt("BlockCount", this.blockCount); tag.putInt("BlockCount", this.blockCount);
tag.putBoolean("ContainsAir", this.containsAir);
return tag; return tag;
} }

Datei anzeigen

@ -23,6 +23,8 @@ import net.minecraft.world.level.chunk.PalettedContainer;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class BlueprintIo { public class BlueprintIo {
@ -111,7 +113,23 @@ public class BlueprintIo {
} }
} }
return new RawBlueprint(header, thumbnailBytes, blockMap, blockEntities); ListTag entitiesTag = blockDataTag.getList("Entities", Tag.TAG_COMPOUND);
List<CompoundTag> entities = new ArrayList<>();
for (Tag tag : entitiesTag) {
CompoundTag entityCompound = (CompoundTag) tag;
// Data Fix
if (blueprintDataVersion != currentDataVersion) {
Dynamic<Tag> dynamic = new Dynamic<>(NbtOps.INSTANCE, entityCompound);
Dynamic<Tag> output = DataFixers.getDataFixer().update(References.ENTITY, dynamic,
blueprintDataVersion, currentDataVersion);
entityCompound = (CompoundTag) output.getValue();
}
entities.add(entityCompound);
}
return new RawBlueprint(header, thumbnailBytes, blockMap, blockEntities, entities);
} }
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC,
@ -137,41 +155,6 @@ public class BlueprintIo {
return map; return map;
} }
public static void writeHeader(Path inPath, Path outPath, BlueprintHeader newHeader) throws IOException {
byte[] thumbnailAndBlockBytes;
try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(inPath))) {
if (inputStream.available() < 4) throw NOT_VALID_BLUEPRINT;
DataInputStream dataInputStream = new DataInputStream(inputStream);
int magic = dataInputStream.readInt();
if (magic != MAGIC) throw NOT_VALID_BLUEPRINT;
// Header
int headerLength = dataInputStream.readInt(); // Ignore header length
if (dataInputStream.skip(headerLength) < headerLength) throw NOT_VALID_BLUEPRINT;
thumbnailAndBlockBytes = dataInputStream.readAllBytes();
}
try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(outPath))) {
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeInt(MAGIC);
// Write header
CompoundTag headerTag = newHeader.save(new CompoundTag());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream os = new DataOutputStream(baos)) {
NbtIo.write(headerTag, os);
}
dataOutputStream.writeInt(baos.size());
baos.writeTo(dataOutputStream);
// Copy remaining bytes
dataOutputStream.write(thumbnailAndBlockBytes);
}
}
public static void writeRaw(OutputStream outputStream, RawBlueprint rawBlueprint) throws IOException { public static void writeRaw(OutputStream outputStream, RawBlueprint rawBlueprint) throws IOException {
DataOutputStream dataOutputStream = new DataOutputStream(outputStream); DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
@ -240,6 +223,11 @@ public class BlueprintIo {
}); });
compound.put("BlockEntities", blockEntitiesTag); compound.put("BlockEntities", blockEntitiesTag);
// Write entities
ListTag entitiesTag = new ListTag();
entitiesTag.addAll(rawBlueprint.entities());
compound.put("Entities", entitiesTag);
baos.reset(); baos.reset();
NbtIo.writeCompressed(compound, baos); NbtIo.writeCompressed(compound, baos);
dataOutputStream.writeInt(baos.size()); dataOutputStream.writeInt(baos.size());

Datei anzeigen

@ -5,14 +5,18 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.PalettedContainer;
import java.util.ArrayList;
import java.util.List;
public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2ObjectMap<PalettedContainer<BlockState>> blocks, public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2ObjectMap<PalettedContainer<BlockState>> blocks,
Long2ObjectMap<CompressedBlockEntity> blockEntities) { Long2ObjectMap<CompressedBlockEntity> blockEntities, List<CompoundTag> entities) {
public static void writeHeader(FriendlyByteBuf friendlyByteBuf, RawBlueprint rawBlueprint) { public static void writeHeader(FriendlyByteBuf friendlyByteBuf, RawBlueprint rawBlueprint) {
rawBlueprint.header.write(friendlyByteBuf); rawBlueprint.header.write(friendlyByteBuf);
@ -23,7 +27,7 @@ public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2Object
BlueprintHeader header = BlueprintHeader.read(friendlyByteBuf); BlueprintHeader header = BlueprintHeader.read(friendlyByteBuf);
byte[] thumbnail = friendlyByteBuf.readByteArray(); byte[] thumbnail = friendlyByteBuf.readByteArray();
return new RawBlueprint(header, thumbnail, null, null); return new RawBlueprint(header, thumbnail, null, null, null);
} }
public static void write(FriendlyByteBuf friendlyByteBuf, RawBlueprint rawBlueprint) { public static void write(FriendlyByteBuf friendlyByteBuf, RawBlueprint rawBlueprint) {
@ -49,6 +53,11 @@ public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2Object
friendlyByteBuf.writeLong(pos); friendlyByteBuf.writeLong(pos);
rawBlueprint.blockEntities.get(pos).write(friendlyByteBuf); rawBlueprint.blockEntities.get(pos).write(friendlyByteBuf);
} }
friendlyByteBuf.writeVarInt(rawBlueprint.entities.size());
for (CompoundTag entity : rawBlueprint.entities) {
friendlyByteBuf.writeNbt(entity);
}
} }
public static RawBlueprint read(FriendlyByteBuf friendlyByteBuf) { public static RawBlueprint read(FriendlyByteBuf friendlyByteBuf) {
@ -78,7 +87,14 @@ public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2Object
blockEntities.put(pos, compressedBlockEntity); blockEntities.put(pos, compressedBlockEntity);
} }
return new RawBlueprint(header, thumbnail, blocks, blockEntities); List<CompoundTag> entities = new ArrayList<>();
int entityCount = friendlyByteBuf.readVarInt();
for (int i = 0; i < entityCount; i++) {
entities.add(friendlyByteBuf.readNbt());
}
return new RawBlueprint(header, thumbnail, blocks, blockEntities, entities);
} }
} }

Datei anzeigen

@ -35,22 +35,17 @@ public class DeleteEntityPacketListener implements PacketHandler {
return; return;
} }
List<UUID> delete = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000), List<UUID> delete = friendlyByteBuf.readCollection(this.plugin.limitCollection(ArrayList::new), buf -> buf.readUUID());
buf -> buf.readUUID());
ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle(); ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle();
List<String> whitelistedEntities = this.plugin.configuration.getStringList("whitelist-entities");
List<String> blacklistedEntities = this.plugin.configuration.getStringList("blacklist-entities");
for (UUID uuid : delete) { for (UUID uuid : delete) {
Entity entity = serverLevel.getEntity(uuid); Entity entity = serverLevel.getEntity(uuid);
if (entity == null || entity instanceof net.minecraft.world.entity.player.Player || entity.hasPassenger(e -> e instanceof net.minecraft.world.entity.player.Player)) continue; if (entity == null || entity instanceof net.minecraft.world.entity.player.Player || entity.hasPassenger(e -> e instanceof net.minecraft.world.entity.player.Player)) continue;
String type = EntityType.getKey(entity.getType()).toString(); if (!this.plugin.canEntityBeManipulated(entity.getType())) {
continue;
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(type)) continue; }
if (blacklistedEntities.contains(type)) continue;
if (!Integration.canBreakBlock(player, if (!Integration.canBreakBlock(player,
player.getWorld().getBlockAt(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ()))) { player.getWorld().getBlockAt(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ()))) {

Datei anzeigen

@ -138,6 +138,9 @@ public class HelloPacketListener implements PacketHandler {
buf.writeVarInt(5); // Maximum Reach buf.writeVarInt(5); // Maximum Reach
buf.writeVarInt(16); // Max editor views buf.writeVarInt(16); // Max editor views
buf.writeBoolean(true); // Editable Views buf.writeBoolean(true); // Editable Views
buf.writeVarInt(0); // No custom data overrides
buf.writeVarInt(0); // No rotation overrides
buf.writeVarInt(1); // Blueprint version
byte[] enableBytes = new byte[buf.writerIndex()]; byte[] enableBytes = new byte[buf.writerIndex()];
buf.getBytes(0, enableBytes); buf.getBytes(0, enableBytes);

Datei anzeigen

@ -49,7 +49,7 @@ public class ManipulateEntityPacketListener implements PacketHandler {
public record ManipulateEntry(UUID uuid, @Nullable Set<RelativeMovement> relativeMovementSet, @Nullable Vec3 position, public record ManipulateEntry(UUID uuid, @Nullable Set<RelativeMovement> relativeMovementSet, @Nullable Vec3 position,
float yaw, float pitch, CompoundTag merge, PassengerManipulation passengerManipulation, List<UUID> passengers) { float yaw, float pitch, CompoundTag merge, PassengerManipulation passengerManipulation, List<UUID> passengers) {
public static ManipulateEntry read(FriendlyByteBuf friendlyByteBuf, Player player) { public static ManipulateEntry read(FriendlyByteBuf friendlyByteBuf, Player player, AxiomPaper plugin) {
UUID uuid = friendlyByteBuf.readUUID(); UUID uuid = friendlyByteBuf.readUUID();
int flags = friendlyByteBuf.readByte(); int flags = friendlyByteBuf.readByte();
@ -69,8 +69,7 @@ public class ManipulateEntityPacketListener implements PacketHandler {
PassengerManipulation passengerManipulation = friendlyByteBuf.readEnum(PassengerManipulation.class); PassengerManipulation passengerManipulation = friendlyByteBuf.readEnum(PassengerManipulation.class);
List<UUID> passengers = List.of(); List<UUID> passengers = List.of();
if (passengerManipulation == PassengerManipulation.ADD_LIST || passengerManipulation == PassengerManipulation.REMOVE_LIST) { if (passengerManipulation == PassengerManipulation.ADD_LIST || passengerManipulation == PassengerManipulation.REMOVE_LIST) {
passengers = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000), passengers = friendlyByteBuf.readCollection(plugin.limitCollection(ArrayList::new), buffer -> buffer.readUUID());
buffer -> buffer.readUUID());
} }
return new ManipulateEntry(uuid, relativeMovementSet, position, yaw, pitch, nbt, return new ManipulateEntry(uuid, relativeMovementSet, position, yaw, pitch, nbt,
@ -90,22 +89,18 @@ public class ManipulateEntityPacketListener implements PacketHandler {
return; return;
} }
List<ManipulateEntry> entries = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000), List<ManipulateEntry> entries = friendlyByteBuf.readCollection(this.plugin.limitCollection(ArrayList::new),
buf -> ManipulateEntry.read(buf, player)); buf -> ManipulateEntry.read(buf, player, this.plugin));
ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle(); ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle();
List<String> whitelistedEntities = this.plugin.configuration.getStringList("whitelist-entities");
List<String> blacklistedEntities = this.plugin.configuration.getStringList("blacklist-entities");
for (ManipulateEntry entry : entries) { for (ManipulateEntry entry : entries) {
Entity entity = serverLevel.getEntity(entry.uuid); Entity entity = serverLevel.getEntity(entry.uuid);
if (entity == null || entity instanceof net.minecraft.world.entity.player.Player || entity.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue; if (entity == null || entity instanceof net.minecraft.world.entity.player.Player || entity.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue;
String type = EntityType.getKey(entity.getType()).toString(); if (!this.plugin.canEntityBeManipulated(entity.getType())) {
continue;
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(type)) continue; }
if (blacklistedEntities.contains(type)) continue;
Vec3 position = entity.position(); Vec3 position = entity.position();
BlockPos containing = BlockPos.containing(position.x, position.y, position.z); BlockPos containing = BlockPos.containing(position.x, position.y, position.z);
@ -168,10 +163,9 @@ public class ManipulateEntityPacketListener implements PacketHandler {
if (passenger == null || passenger.isPassenger() || if (passenger == null || passenger.isPassenger() ||
passenger instanceof net.minecraft.world.entity.player.Player || passenger.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue; passenger instanceof net.minecraft.world.entity.player.Player || passenger.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue;
String passengerType = EntityType.getKey(passenger.getType()).toString(); if (!this.plugin.canEntityBeManipulated(passenger.getType())) {
continue;
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(passengerType)) continue; }
if (blacklistedEntities.contains(passengerType)) continue;
// Prevent mounting loop // Prevent mounting loop
if (passenger.getSelfAndPassengers().anyMatch(entity2 -> entity2 == entity)) { if (passenger.getSelfAndPassengers().anyMatch(entity2 -> entity2 == entity)) {
@ -195,10 +189,9 @@ public class ManipulateEntityPacketListener implements PacketHandler {
if (passenger == null || passenger == entity || passenger instanceof net.minecraft.world.entity.player.Player || if (passenger == null || passenger == entity || passenger instanceof net.minecraft.world.entity.player.Player ||
passenger.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue; passenger.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue;
String passengerType = EntityType.getKey(passenger.getType()).toString(); if (!this.plugin.canEntityBeManipulated(passenger.getType())) {
continue;
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(passengerType)) continue; }
if (blacklistedEntities.contains(passengerType)) continue;
Entity vehicle = passenger.getVehicle(); Entity vehicle = passenger.getVehicle();
if (vehicle == entity) { if (vehicle == entity) {

Datei anzeigen

@ -68,7 +68,7 @@ public class RequestChunkDataPacketListener implements PacketHandler {
ResourceKey<Level> worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); ResourceKey<Level> worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
ServerLevel level = server.getLevel(worldKey); ServerLevel level = server.getLevel(worldKey);
if (level == null) { if (level == null || level != player.serverLevel()) {
sendEmptyResponse(player, id); sendEmptyResponse(player, id);
return; return;
} }

Datei anzeigen

@ -0,0 +1,108 @@
package com.moulberry.axiom.packet.impl;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.VersionHelper;
import com.moulberry.axiom.integration.Integration;
import com.moulberry.axiom.packet.PacketHandler;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import java.util.*;
public class RequestEntityDataPacketListener implements PacketHandler {
public static final ResourceLocation RESPONSE_ID = VersionHelper.createResourceLocation("axiom:response_entity_data");
private final AxiomPaper plugin;
private final boolean forceFail;
public RequestEntityDataPacketListener(AxiomPaper plugin, boolean forceFail) {
this.plugin = plugin;
this.forceFail = forceFail;
}
@Override
public void onReceive(org.bukkit.entity.Player bukkitPlayer, FriendlyByteBuf friendlyByteBuf) {
ServerPlayer player = ((CraftPlayer)bukkitPlayer).getHandle();
long id = friendlyByteBuf.readLong();
if (this.forceFail || !this.plugin.canUseAxiom(bukkitPlayer, "axiom.entity.request_data") || this.plugin.isMismatchedDataVersion(bukkitPlayer.getUniqueId())) {
// We always send an 'empty' response in order to make the client happy
sendResponse(player, id, true, Map.of());
return;
}
if (!this.plugin.canModifyWorld(bukkitPlayer, bukkitPlayer.getWorld())) {
sendResponse(player, id, true, Map.of());
return;
}
List<UUID> request = friendlyByteBuf.readCollection(this.plugin.limitCollection(ArrayList::new), buf -> buf.readUUID());
ServerLevel serverLevel = player.serverLevel();
final int maxPacketSize = 0x100000;
int remainingBytes = maxPacketSize;
Map<UUID, CompoundTag> entityData = new HashMap<>();
Set<UUID> visitedEntities = new HashSet<>();
for (UUID uuid : request) {
if (!visitedEntities.add(uuid)) {
continue;
}
Entity entity = serverLevel.getEntity(uuid);
if (entity == null || entity instanceof Player) {
continue;
}
if (!this.plugin.canEntityBeManipulated(entity.getType())) {
continue;
}
if (!Integration.canPlaceBlock(bukkitPlayer, new Location(bukkitPlayer.getWorld(),
entity.getBlockX(), entity.getBlockY(), entity.getBlockZ()))) {
continue;
}
CompoundTag entityTag = new CompoundTag();
if (entity.save(entityTag)) {
int size = entityTag.sizeInBytes();
if (size >= maxPacketSize) {
sendResponse(player, id, false, Map.of(uuid, entityTag));
continue;
}
// Send partial packet if we've run out of available bytes
if (remainingBytes - size < 0) {
sendResponse(player, id, false, entityData);
entityData.clear();
}
entityData.put(uuid, entityTag);
remainingBytes -= size;
}
}
sendResponse(player, id, true, entityData);
}
private static void sendResponse(ServerPlayer player, long id, boolean finished, Map<UUID, CompoundTag> map) {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
friendlyByteBuf.writeLong(id);
friendlyByteBuf.writeBoolean(finished);
friendlyByteBuf.writeMap(map, (buf, uuid) -> buf.writeUUID(uuid), (buf, nbt) -> buf.writeNbt(nbt));
player.connection.send(new ClientboundCustomPayloadPacket(RESPONSE_ID, friendlyByteBuf));
}
}

Datei anzeigen

@ -92,7 +92,7 @@ public class SetBlockPacketListener implements PacketHandler {
} }
// Read packet // Read packet
IntFunction<Map<BlockPos, BlockState>> mapFunction = FriendlyByteBuf.limitValue(Maps::newLinkedHashMapWithExpectedSize, 512); IntFunction<Map<BlockPos, BlockState>> mapFunction = this.plugin.limitCollection(Maps::newLinkedHashMapWithExpectedSize);
IdMapper<BlockState> registry = this.plugin.getBlockRegistry(bukkitPlayer.getUniqueId()); IdMapper<BlockState> registry = this.plugin.getBlockRegistry(bukkitPlayer.getUniqueId());
Map<BlockPos, BlockState> blocks = friendlyByteBuf.readMap(mapFunction, Map<BlockPos, BlockState> blocks = friendlyByteBuf.readMap(mapFunction,
buf -> buf.readBlockPos(), buf -> buf.readById(registry::byIdOrThrow)); buf -> buf.readBlockPos(), buf -> buf.readById(registry::byIdOrThrow));

Datei anzeigen

@ -54,16 +54,13 @@ public class SpawnEntityPacketListener implements PacketHandler {
return; return;
} }
List<SpawnEntry> entries = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000), List<SpawnEntry> entries = friendlyByteBuf.readCollection(this.plugin.limitCollection(ArrayList::new),
buf -> new SpawnEntry(buf.readUUID(), buf.readDouble(), buf.readDouble(), buf -> new SpawnEntry(buf.readUUID(), buf.readDouble(), buf.readDouble(),
buf.readDouble(), buf.readFloat(), buf.readFloat(), buf.readDouble(), buf.readFloat(), buf.readFloat(),
buf.readNullable(buffer -> buffer.readUUID()), UnknownVersionHelper.readTagUnknown(buf, player))); buf.readNullable(buffer -> buffer.readUUID()), UnknownVersionHelper.readTagUnknown(buf, player)));
ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle(); ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle();
List<String> whitelistedEntities = this.plugin.configuration.getStringList("whitelist-entities");
List<String> blacklistedEntities = this.plugin.configuration.getStringList("blacklist-entities");
for (SpawnEntry entry : entries) { for (SpawnEntry entry : entries) {
Vec3 position = new Vec3(entry.x, entry.y, entry.z); Vec3 position = new Vec3(entry.x, entry.y, entry.z);
@ -99,9 +96,9 @@ public class SpawnEntityPacketListener implements PacketHandler {
AtomicBoolean useNewUuid = new AtomicBoolean(true); AtomicBoolean useNewUuid = new AtomicBoolean(true);
Entity spawned = EntityType.loadEntityRecursive(tag, serverLevel, entity -> { Entity spawned = EntityType.loadEntityRecursive(tag, serverLevel, entity -> {
String type = EntityType.getKey(entity.getType()).toString(); if (!this.plugin.canEntityBeManipulated(entity.getType())) {
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(type)) return null; return null;
if (blacklistedEntities.contains(type)) return null; }
if (useNewUuid.getAndSet(false)) { if (useNewUuid.getAndSet(false)) {
entity.setUUID(entry.newUuid); entity.setUUID(entry.newUuid);

Datei anzeigen

@ -14,7 +14,7 @@ allow-teleport-between-worlds: true
# Whether to allow clients to save/load/share blueprints through the server # Whether to allow clients to save/load/share blueprints through the server
blueprint-sharing: false blueprint-sharing: false
# Allow large chunk data requests, not recommended for public servers # Allow large chunk & entity data requests, not recommended for public servers
allow-large-chunk-data-request: false allow-large-chunk-data-request: false
# Raise the size limit for *all* packets to 2mb instead of 32kb. NOT RECOMMENDED FOR PUBLIC SERVERS # Raise the size limit for *all* packets to 2mb instead of 32kb. NOT RECOMMENDED FOR PUBLIC SERVERS
@ -57,6 +57,9 @@ whitelist-entities:
blacklist-entities: blacklist-entities:
# - "minecraft:ender_dragon" # - "minecraft:ender_dragon"
# Disables entity data sanitization. NOT RECOMMENDED FOR PUBLIC SERVERS
disable-entity-sanitization: false
# True allows players to see/manipulate marker entities # True allows players to see/manipulate marker entities
send-markers: false send-markers: false
@ -95,6 +98,7 @@ packet-handlers:
teleport: true teleport: true
set-editor-views: true set-editor-views: true
request-chunk-data: true request-chunk-data: true
request-entity-data: true
set-buffer: true set-buffer: true
spawn-entity: true spawn-entity: true
manipulate-entity: true manipulate-entity: true

Datei anzeigen

@ -25,6 +25,7 @@ permissions:
axiom.entity.spawn: true axiom.entity.spawn: true
axiom.entity.manipulate: true axiom.entity.manipulate: true
axiom.entity.delete: true axiom.entity.delete: true
axiom.entity.request_data: true
axiom.blueprint.*: axiom.blueprint.*:
description: Allows use of all blueprint-related features description: Allows use of all blueprint-related features
default: op default: op