Mirror von
https://github.com/Moulberry/AxiomPaperPlugin.git
synchronisiert 2024-11-17 05:40:06 +01:00
Support 4.2.0 changes
Dieser Commit ist enthalten in:
Ursprung
3ac78c2093
Commit
aadace89de
@ -4,7 +4,7 @@ bom-newest = "1.37"
|
||||
cloud-paper = "2.0.0-20240516.054251-69"
|
||||
coreprotect = "22.4"
|
||||
paper = "1.20.4-R0.1-SNAPSHOT"
|
||||
plotsquared = "7.3.9-20240513.192211-13"
|
||||
plotsquared = "7.3.8"
|
||||
reflection-remapper = "0.1.2-20240315.033304-2"
|
||||
viaversion-api = "5.0.1"
|
||||
worldguard-bukkit = "7.0.9-SNAPSHOT"
|
||||
|
@ -9,25 +9,7 @@ import com.moulberry.axiom.event.AxiomModifyWorldEvent;
|
||||
import com.moulberry.axiom.integration.coreprotect.CoreProtectIntegration;
|
||||
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import com.moulberry.axiom.packet.*;
|
||||
import com.moulberry.axiom.packet.impl.BlueprintRequestPacketListener;
|
||||
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.packet.impl.*;
|
||||
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
@ -46,6 +28,7 @@ import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -68,6 +51,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
|
||||
@ -82,6 +66,9 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
|
||||
public IdMapper<BlockState> allowedBlockRegistry = null;
|
||||
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 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'");
|
||||
}
|
||||
|
||||
boolean allowLargeChunkDataRequest = this.configuration.getBoolean("allow-large-chunk-data-request");
|
||||
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");
|
||||
this.allowedBlockRegistry = DisallowedBlocks.createAllowedBlockRegistry(disallowedBlocks);
|
||||
|
||||
@ -132,27 +131,29 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
|
||||
Map<String, PacketHandler> largePayloadHandlers = new HashMap<>();
|
||||
|
||||
registerPacketHandler("hello", new HelloPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("set_gamemode", new SetGamemodePacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("set_fly_speed", new SetFlySpeedPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("set_world_time", new SetTimePacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("set_world_property", new SetWorldPropertyListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("set_block", new SetBlockPacketListener(this), msg, largePayloadHandlers); // set-single-block
|
||||
registerPacketHandler("set_hotbar_slot", new SetHotbarSlotPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("switch_active_hotbar", new SwitchActiveHotbarPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("teleport", new TeleportPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("set_editor_views", new SetEditorViewsPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("request_chunk_data", new RequestChunkDataPacketListener(this,
|
||||
!configuration.getBoolean("packet-handlers.request-chunk-data")), msg, largePayloadHandlers);
|
||||
registerPacketHandler("spawn_entity", new SpawnEntityPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("manipulate_entity", new ManipulateEntityPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("delete_entity", new DeleteEntityPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("marker_nbt_request", new MarkerNbtRequestPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("request_blueprint", new BlueprintRequestPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("hello", new HelloPacketListener(this), msg, LargePayloadBehaviour.FORCE_SMALL, largePayloadHandlers);
|
||||
registerPacketHandler("set_gamemode", new SetGamemodePacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("set_fly_speed", new SetFlySpeedPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("set_world_time", new SetTimePacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("set_world_property", new SetWorldPropertyListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("set_block", new SetBlockPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers); // set-single-block
|
||||
registerPacketHandler("set_hotbar_slot", new SetHotbarSlotPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("switch_active_hotbar", new SwitchActiveHotbarPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("teleport", new TeleportPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("set_editor_views", new SetEditorViewsPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("request_chunk_data", new RequestChunkDataPacketListener(this, !configuration.getBoolean("packet-handlers.request-chunk-data")), msg,
|
||||
this.configuration.getBoolean("allow-large-chunk-data-request") ? LargePayloadBehaviour.FORCE_LARGE : LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("request_entity_data", new RequestEntityDataPacketListener(this, !configuration.getBoolean("packet-handlers.request-entity-data")), msg,
|
||||
this.configuration.getBoolean("allow-large-chunk-data-request") ? LargePayloadBehaviour.FORCE_LARGE : LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("spawn_entity", new SpawnEntityPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, largePayloadHandlers);
|
||||
registerPacketHandler("manipulate_entity", new ManipulateEntityPacketListener(this), msg, LargePayloadBehaviour.DEFAULT, 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("upload_blueprint", new UploadBlueprintPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("annotation_update", new UpdateAnnotationPacketListener(this), msg, largePayloadHandlers);
|
||||
registerPacketHandler("set_buffer", new SetBlockBufferPacketListener(this), msg, LargePayloadBehaviour.FORCE_LARGE, largePayloadHandlers);
|
||||
registerPacketHandler("upload_blueprint", new UploadBlueprintPacketListener(this), msg, LargePayloadBehaviour.FORCE_LARGE, largePayloadHandlers);
|
||||
registerPacketHandler("annotation_update", new UpdateAnnotationPacketListener(this), msg, LargePayloadBehaviour.FORCE_LARGE, largePayloadHandlers);
|
||||
|
||||
if (!largePayloadHandlers.isEmpty()) {
|
||||
ChannelInitializeListenerHolder.addListener(Key.key("axiom:handle_big_payload"), new ChannelInitializeListener() {
|
||||
@ -302,25 +303,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("_", "-");
|
||||
if (name.equals("set_block")) {
|
||||
configEntry = "packet-handlers.set-single-block";
|
||||
} else if (name.equals("request_blueprint")) {
|
||||
configEntry = "packet-handlers.blueprint-request";
|
||||
}
|
||||
if (name.equals("request-chunk-data") || this.configuration.getBoolean(configEntry, true)) {
|
||||
boolean isLargePayload = false;
|
||||
|
||||
if (name.equals("hello")) { // Hello must use normal system, as non-Axiom players can't send large payloads
|
||||
isLargePayload = 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 (name.equals("request_chunk_data") || name.equals("request_entity_data") || this.configuration.getBoolean(configEntry, true)) {
|
||||
boolean isLargePayload = switch (behaviour) {
|
||||
case DEFAULT -> this.configuration.getBoolean("allow-large-payload-for-all-packets");
|
||||
case FORCE_LARGE -> true;
|
||||
case FORCE_SMALL -> false;
|
||||
};
|
||||
|
||||
if (isLargePayload) {
|
||||
largePayloadHandlers.put("axiom:"+name, handler);
|
||||
@ -350,6 +352,10 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
return allowedCapabilities;
|
||||
}
|
||||
|
||||
public <T> IntFunction<T> limitCollection(IntFunction<T> applier) {
|
||||
return FriendlyByteBuf.limitValue(applier, this.packetCollectionReadLimit);
|
||||
}
|
||||
|
||||
public boolean logLargeBlockBufferChanges() {
|
||||
return this.logLargeBlockBufferChanges;
|
||||
}
|
||||
@ -379,6 +385,19 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
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) {
|
||||
return this.playerBlockBufferRateLimiters.get(uuid);
|
||||
}
|
||||
|
@ -21,6 +21,15 @@ public class NbtSanitization {
|
||||
"Glowing",
|
||||
"Tags",
|
||||
"Passengers",
|
||||
// armor stand
|
||||
"ArmorItems",
|
||||
"HandItems",
|
||||
"Small",
|
||||
"ShowArms",
|
||||
"DisabledSlots",
|
||||
"NoBasePlate",
|
||||
"Marker",
|
||||
"Pose",
|
||||
// marker
|
||||
"data",
|
||||
// display entity
|
||||
@ -50,6 +59,10 @@ public class NbtSanitization {
|
||||
);
|
||||
|
||||
public static void sanitizeEntity(CompoundTag entityRoot) {
|
||||
if (AxiomPaper.PLUGIN.configuration.getBoolean("disable-entity-sanitization")) {
|
||||
return;
|
||||
}
|
||||
|
||||
entityRoot.getAllKeys().retainAll(ALLOWED_KEYS);
|
||||
|
||||
if (entityRoot.contains("Passengers", Tag.TAG_LIST)) {
|
||||
|
@ -9,24 +9,25 @@ import net.minecraft.network.FriendlyByteBuf;
|
||||
import java.util.ArrayList;
|
||||
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) {
|
||||
friendlyByteBuf.writeUtf(this.name);
|
||||
friendlyByteBuf.writeUtf(this.author);
|
||||
friendlyByteBuf.writeCollection(this.tags, FriendlyByteBuf::writeUtf);
|
||||
friendlyByteBuf.writeInt(this.blockCount);
|
||||
friendlyByteBuf.writeBoolean(this.containsAir);
|
||||
}
|
||||
|
||||
public static BlueprintHeader read(FriendlyByteBuf friendlyByteBuf) {
|
||||
String name = friendlyByteBuf.readUtf();
|
||||
String author = friendlyByteBuf.readUtf();
|
||||
List<String> tags = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000),
|
||||
FriendlyByteBuf::readUtf);
|
||||
List<String> tags = friendlyByteBuf.readList(FriendlyByteBuf::readUtf);
|
||||
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) {
|
||||
@ -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;
|
||||
boolean lockedThumbnail = tag.getBoolean("LockedThumbnail");
|
||||
int blockCount = tag.getInt("BlockCount");
|
||||
boolean containsAir = tag.getBoolean("ContainsAir");
|
||||
|
||||
List<String> tags = new ArrayList<>();
|
||||
for (Tag string : tag.getList("Tags", Tag.TAG_STRING)) {
|
||||
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) {
|
||||
@ -60,6 +62,7 @@ public record BlueprintHeader(String name, String author, List<String> tags, flo
|
||||
tag.putFloat("ThumbnailPitch", this.thumbnailPitch);
|
||||
tag.putBoolean("LockedThumbnail", this.lockedThumbnail);
|
||||
tag.putInt("BlockCount", this.blockCount);
|
||||
tag.putBoolean("ContainsAir", this.containsAir);
|
||||
return tag;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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,
|
||||
@ -137,41 +155,6 @@ public class BlueprintIo {
|
||||
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 {
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
|
||||
|
||||
@ -241,6 +224,11 @@ public class BlueprintIo {
|
||||
});
|
||||
compound.put("BlockEntities", blockEntitiesTag);
|
||||
|
||||
// Write entities
|
||||
ListTag entitiesTag = new ListTag();
|
||||
entitiesTag.addAll(rawBlueprint.entities());
|
||||
compound.put("Entities", entitiesTag);
|
||||
|
||||
baos.reset();
|
||||
NbtIo.writeCompressed(compound, baos);
|
||||
dataOutputStream.writeInt(baos.size());
|
||||
|
@ -5,14 +5,18 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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) {
|
||||
rawBlueprint.header.write(friendlyByteBuf);
|
||||
@ -23,7 +27,7 @@ public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2Object
|
||||
BlueprintHeader header = BlueprintHeader.read(friendlyByteBuf);
|
||||
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) {
|
||||
@ -49,6 +53,11 @@ public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2Object
|
||||
friendlyByteBuf.writeLong(pos);
|
||||
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) {
|
||||
@ -62,7 +71,7 @@ public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2Object
|
||||
long pos = friendlyByteBuf.readLong();
|
||||
|
||||
PalettedContainer<BlockState> palettedContainer = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY,
|
||||
Blocks.STRUCTURE_VOID.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
Blocks.STRUCTURE_VOID.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
palettedContainer.read(friendlyByteBuf);
|
||||
|
||||
blocks.put(pos, palettedContainer);
|
||||
@ -78,7 +87,14 @@ public record RawBlueprint(BlueprintHeader header, byte[] thumbnail, Long2Object
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,22 +35,17 @@ public class DeleteEntityPacketListener implements PacketHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
List<UUID> delete = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000),
|
||||
FriendlyByteBuf::readUUID);
|
||||
List<UUID> delete = friendlyByteBuf.readCollection(this.plugin.limitCollection(ArrayList::new), FriendlyByteBuf::readUUID);
|
||||
|
||||
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) {
|
||||
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;
|
||||
|
||||
String type = EntityType.getKey(entity.getType()).toString();
|
||||
|
||||
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(type)) continue;
|
||||
if (blacklistedEntities.contains(type)) continue;
|
||||
if (!this.plugin.canEntityBeManipulated(entity.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Integration.canBreakBlock(player,
|
||||
player.getWorld().getBlockAt(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ()))) {
|
||||
|
@ -136,6 +136,9 @@ public class HelloPacketListener implements PacketHandler {
|
||||
buf.writeVarInt(5); // Maximum Reach
|
||||
buf.writeVarInt(16); // Max editor 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()];
|
||||
buf.getBytes(0, enableBytes);
|
||||
|
@ -49,7 +49,7 @@ public class ManipulateEntityPacketListener implements PacketHandler {
|
||||
|
||||
public record ManipulateEntry(UUID uuid, @Nullable Set<RelativeMovement> relativeMovementSet, @Nullable Vec3 position,
|
||||
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();
|
||||
|
||||
int flags = friendlyByteBuf.readByte();
|
||||
@ -69,8 +69,7 @@ public class ManipulateEntityPacketListener implements PacketHandler {
|
||||
PassengerManipulation passengerManipulation = friendlyByteBuf.readEnum(PassengerManipulation.class);
|
||||
List<UUID> passengers = List.of();
|
||||
if (passengerManipulation == PassengerManipulation.ADD_LIST || passengerManipulation == PassengerManipulation.REMOVE_LIST) {
|
||||
passengers = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000),
|
||||
FriendlyByteBuf::readUUID);
|
||||
passengers = friendlyByteBuf.readCollection(plugin.limitCollection(ArrayList::new), FriendlyByteBuf::readUUID);
|
||||
}
|
||||
|
||||
return new ManipulateEntry(uuid, relativeMovementSet, position, yaw, pitch, nbt,
|
||||
@ -90,22 +89,18 @@ public class ManipulateEntityPacketListener implements PacketHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ManipulateEntry> entries = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000),
|
||||
buf -> ManipulateEntry.read(buf, player));
|
||||
List<ManipulateEntry> entries = friendlyByteBuf.readCollection(this.plugin.limitCollection(ArrayList::new),
|
||||
buf -> ManipulateEntry.read(buf, player, this.plugin));
|
||||
|
||||
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) {
|
||||
Entity entity = serverLevel.getEntity(entry.uuid);
|
||||
if (entity == null || entity instanceof net.minecraft.world.entity.player.Player || entity.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue;
|
||||
|
||||
String type = EntityType.getKey(entity.getType()).toString();
|
||||
|
||||
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(type)) continue;
|
||||
if (blacklistedEntities.contains(type)) continue;
|
||||
if (!this.plugin.canEntityBeManipulated(entity.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec3 position = entity.position();
|
||||
BlockPos containing = BlockPos.containing(position.x, position.y, position.z);
|
||||
@ -168,10 +163,9 @@ public class ManipulateEntityPacketListener implements PacketHandler {
|
||||
if (passenger == null || passenger.isPassenger() ||
|
||||
passenger instanceof net.minecraft.world.entity.player.Player || passenger.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue;
|
||||
|
||||
String passengerType = EntityType.getKey(passenger.getType()).toString();
|
||||
|
||||
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(passengerType)) continue;
|
||||
if (blacklistedEntities.contains(passengerType)) continue;
|
||||
if (!this.plugin.canEntityBeManipulated(passenger.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prevent mounting loop
|
||||
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 ||
|
||||
passenger.hasPassenger(ManipulateEntityPacketListener::isPlayer)) continue;
|
||||
|
||||
String passengerType = EntityType.getKey(passenger.getType()).toString();
|
||||
|
||||
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(passengerType)) continue;
|
||||
if (blacklistedEntities.contains(passengerType)) continue;
|
||||
if (!this.plugin.canEntityBeManipulated(passenger.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Entity vehicle = passenger.getVehicle();
|
||||
if (vehicle == entity) {
|
||||
|
@ -68,7 +68,7 @@ public class RequestChunkDataPacketListener implements PacketHandler {
|
||||
|
||||
ResourceKey<Level> worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
|
||||
ServerLevel level = server.getLevel(worldKey);
|
||||
if (level == null) {
|
||||
if (level == null || level != player.serverLevel()) {
|
||||
sendEmptyResponse(player, id);
|
||||
return;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -92,7 +92,7 @@ public class SetBlockPacketListener implements PacketHandler {
|
||||
}
|
||||
|
||||
// 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());
|
||||
Map<BlockPos, BlockState> blocks = friendlyByteBuf.readMap(mapFunction,
|
||||
FriendlyByteBuf::readBlockPos, buf -> buf.readById(registry));
|
||||
|
@ -54,16 +54,13 @@ public class SpawnEntityPacketListener implements PacketHandler {
|
||||
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.readDouble(), buf.readFloat(), buf.readFloat(),
|
||||
buf.readNullable(FriendlyByteBuf::readUUID), UnknownVersionHelper.readTagUnknown(buf, player)));
|
||||
|
||||
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) {
|
||||
Vec3 position = new Vec3(entry.x, entry.y, entry.z);
|
||||
|
||||
@ -99,9 +96,9 @@ public class SpawnEntityPacketListener implements PacketHandler {
|
||||
AtomicBoolean useNewUuid = new AtomicBoolean(true);
|
||||
|
||||
Entity spawned = EntityType.loadEntityRecursive(tag, serverLevel, entity -> {
|
||||
String type = EntityType.getKey(entity.getType()).toString();
|
||||
if (!whitelistedEntities.isEmpty() && !whitelistedEntities.contains(type)) return null;
|
||||
if (blacklistedEntities.contains(type)) return null;
|
||||
if (!this.plugin.canEntityBeManipulated(entity.getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (useNewUuid.getAndSet(false)) {
|
||||
entity.setUUID(entry.newUuid);
|
||||
|
@ -14,7 +14,7 @@ allow-teleport-between-worlds: true
|
||||
# Whether to allow clients to save/load/share blueprints through the server
|
||||
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
|
||||
|
||||
# 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:
|
||||
# - "minecraft:ender_dragon"
|
||||
|
||||
# Disables entity data sanitization. NOT RECOMMENDED FOR PUBLIC SERVERS
|
||||
disable-entity-sanitization: false
|
||||
|
||||
# True allows players to see/manipulate marker entities
|
||||
send-markers: false
|
||||
|
||||
@ -95,6 +98,7 @@ packet-handlers:
|
||||
teleport: true
|
||||
set-editor-views: true
|
||||
request-chunk-data: true
|
||||
request-entity-data: true
|
||||
set-buffer: true
|
||||
spawn-entity: true
|
||||
manipulate-entity: true
|
||||
|
@ -25,6 +25,7 @@ permissions:
|
||||
axiom.entity.spawn: true
|
||||
axiom.entity.manipulate: true
|
||||
axiom.entity.delete: true
|
||||
axiom.entity.request_data: true
|
||||
axiom.blueprint.*:
|
||||
description: Allows use of all blueprint-related features
|
||||
default: op
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren