3
0
Mirror von https://github.com/Moulberry/AxiomPaperPlugin.git synchronisiert 2024-11-14 20:20:07 +01:00

Add marker manipulation & nbt sanitization

Dieser Commit ist enthalten in:
Moulberry 2024-01-21 19:24:51 +08:00
Ursprung ceefcf1bce
Commit 15161356f5
9 geänderte Dateien mit 345 neuen und 16 gelöschten Zeilen

Datei anzeigen

@ -84,6 +84,8 @@ public class AxiomPaper extends JavaPlugin implements Listener {
msg.registerOutgoingPluginChannel(this, "axiom:set_world_property"); msg.registerOutgoingPluginChannel(this, "axiom:set_world_property");
msg.registerOutgoingPluginChannel(this, "axiom:ack_world_properties"); msg.registerOutgoingPluginChannel(this, "axiom:ack_world_properties");
msg.registerOutgoingPluginChannel(this, "axiom:restrictions"); msg.registerOutgoingPluginChannel(this, "axiom:restrictions");
msg.registerOutgoingPluginChannel(this, "axiom:marker_data");
msg.registerOutgoingPluginChannel(this, "axiom:marker_nbt_response");
if (configuration.getBoolean("packet-handlers.hello")) { if (configuration.getBoolean("packet-handlers.hello")) {
msg.registerIncomingPluginChannel(this, "axiom:hello", new HelloPacketListener(this)); msg.registerIncomingPluginChannel(this, "axiom:hello", new HelloPacketListener(this));
@ -127,6 +129,9 @@ public class AxiomPaper extends JavaPlugin implements Listener {
if (configuration.getBoolean("packet-handlers.delete-entity")) { if (configuration.getBoolean("packet-handlers.delete-entity")) {
msg.registerIncomingPluginChannel(this, "axiom:delete_entity", new DeleteEntityPacketListener(this)); msg.registerIncomingPluginChannel(this, "axiom:delete_entity", new DeleteEntityPacketListener(this));
} }
if (configuration.getBoolean("packet-handlers.marker-nbt-request")) {
msg.registerIncomingPluginChannel(this, "axiom:marker_nbt_request", new MarkerNbtRequestPacketListener(this));
}
if (configuration.getBoolean("packet-handlers.set-buffer")) { if (configuration.getBoolean("packet-handlers.set-buffer")) {
SetBlockBufferPacketListener setBlockBufferPacketListener = new SetBlockBufferPacketListener(this); SetBlockBufferPacketListener setBlockBufferPacketListener = new SetBlockBufferPacketListener(this);
@ -238,11 +243,12 @@ public class AxiomPaper extends JavaPlugin implements Listener {
playerRestrictions.keySet().retainAll(stillActiveAxiomPlayers); playerRestrictions.keySet().retainAll(stillActiveAxiomPlayers);
}, 20, 20); }, 20, 20);
boolean sendMarkers = configuration.getBoolean("send-markers");
int maxChunkRelightsPerTick = configuration.getInt("max-chunk-relights-per-tick"); int maxChunkRelightsPerTick = configuration.getInt("max-chunk-relights-per-tick");
int maxChunkSendsPerTick = configuration.getInt("max-chunk-sends-per-tick"); int maxChunkSendsPerTick = configuration.getInt("max-chunk-sends-per-tick");
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> { Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> {
WorldExtension.tick(MinecraftServer.getServer(), maxChunkRelightsPerTick, maxChunkSendsPerTick); WorldExtension.tick(MinecraftServer.getServer(), sendMarkers, maxChunkRelightsPerTick, maxChunkSendsPerTick);
}, 1, 1); }, 1, 1);
} }
@ -292,17 +298,22 @@ public class AxiomPaper extends JavaPlugin implements Listener {
@EventHandler @EventHandler
public void onFailMove(PlayerFailMoveEvent event) { public void onFailMove(PlayerFailMoveEvent event) {
if (event.getPlayer().hasPermission("axiom.*")) { if (!this.activeAxiomPlayers.contains(event.getPlayer().getUniqueId())) {
if (event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) { return;
event.setAllowed(true); // Support for arcball camera }
} else if (event.getPlayer().isFlying()) { if (event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) {
event.setAllowed(true); // Support for noclip event.setAllowed(true); // Support for arcball camera
} } else if (event.getPlayer().isFlying()) {
event.setAllowed(true); // Support for noclip
} }
} }
@EventHandler @EventHandler
public void onChangedWorld(PlayerChangedWorldEvent event) { public void onChangedWorld(PlayerChangedWorldEvent event) {
if (!this.activeAxiomPlayers.contains(event.getPlayer().getUniqueId())) {
return;
}
World world = event.getPlayer().getWorld(); World world = event.getPlayer().getWorld();
ServerWorldPropertiesRegistry properties = getOrCreateWorldProperties(world); ServerWorldPropertiesRegistry properties = getOrCreateWorldProperties(world);
@ -312,6 +323,8 @@ public class AxiomPaper extends JavaPlugin implements Listener {
} else { } else {
properties.registerFor(this, event.getPlayer()); properties.registerFor(this, event.getPlayer());
} }
WorldExtension.onPlayerJoin(world, event.getPlayer());
} }
@EventHandler @EventHandler

Datei anzeigen

@ -0,0 +1,63 @@
package com.moulberry.axiom;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import java.util.Set;
public class NbtSanitization {
private static final Set<String> ALLOWED_KEYS = Set.of(
"id", // entity id
// generic
"Pos",
"Rotation",
"Invulnerable",
"CustomName",
"CustomNameVisible",
"Silent",
"NoGravity",
"Glowing",
"Tags",
"Passengers",
// marker
"data",
// display entity
"transformation",
"interpolation_duration",
"start_interpolation",
"teleport_duration",
"billboard",
"view_range",
"shadow_radius",
"shadow_strength",
"width",
"height",
"glow_color_override",
"brightness",
"line_width",
"text_opacity",
"background",
"shadow",
"see_through",
"default_background",
"alignment",
"text",
"block_state",
"item",
"item_display"
);
public static void sanitizeEntity(CompoundTag entityRoot) {
entityRoot.getAllKeys().retainAll(ALLOWED_KEYS);
if (entityRoot.contains("Passengers", Tag.TAG_LIST)) {
ListTag listTag = entityRoot.getList("Passengers", Tag.TAG_COMPOUND);
for (Tag tag : listTag) {
sanitizeEntity((CompoundTag) tag);
}
}
}
}

Datei anzeigen

@ -1,15 +1,24 @@
package com.moulberry.axiom; package com.moulberry.axiom;
import com.moulberry.axiom.marker.MarkerData;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.longs.*;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Marker;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.*; import java.util.*;
@ -23,15 +32,16 @@ public class WorldExtension {
return extension; return extension;
} }
public static void tick(MinecraftServer server, int maxChunkRelightsPerTick, int maxChunkSendsPerTick) { public static void onPlayerJoin(World world, Player player) {
ServerLevel level = ((CraftWorld)world).getHandle();
get(level).onPlayerJoin(player);
}
public static void tick(MinecraftServer server, boolean sendMarkers, int maxChunkRelightsPerTick, int maxChunkSendsPerTick) {
extensions.keySet().retainAll(server.levelKeys()); extensions.keySet().retainAll(server.levelKeys());
for (ServerLevel level : server.getAllLevels()) { for (ServerLevel level : server.getAllLevels()) {
WorldExtension extension = extensions.get(level.dimension()); get(level).tick(sendMarkers, maxChunkRelightsPerTick, maxChunkSendsPerTick);
if (extension != null) {
extension.level = level;
extension.tick(maxChunkRelightsPerTick, maxChunkSendsPerTick);
}
} }
} }
@ -39,6 +49,7 @@ public class WorldExtension {
private final LongSet pendingChunksToSend = new LongOpenHashSet(); private final LongSet pendingChunksToSend = new LongOpenHashSet();
private final LongSet pendingChunksToLight = new LongOpenHashSet(); private final LongSet pendingChunksToLight = new LongOpenHashSet();
private final Map<UUID, MarkerData> previousMarkerData = new HashMap<>();
public void sendChunk(int cx, int cz) { public void sendChunk(int cx, int cz) {
this.pendingChunksToSend.add(ChunkPos.asLong(cx, cz)); this.pendingChunksToSend.add(ChunkPos.asLong(cx, cz));
@ -48,7 +59,66 @@ public class WorldExtension {
this.pendingChunksToLight.add(ChunkPos.asLong(cx, cz)); this.pendingChunksToLight.add(ChunkPos.asLong(cx, cz));
} }
public void tick(int maxChunkRelightsPerTick, int maxChunkSendsPerTick) { public void onPlayerJoin(Player player) {
if (!this.previousMarkerData.isEmpty()) {
List<MarkerData> markerData = new ArrayList<>(this.previousMarkerData.values());
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeCollection(markerData, MarkerData::write);
buf.writeCollection(Set.of(), FriendlyByteBuf::writeUUID);
byte[] bytes = new byte[buf.writerIndex()];
buf.getBytes(0, bytes);
player.sendPluginMessage(AxiomPaper.PLUGIN, "axiom:marker_data", bytes);
}
}
public void tick(boolean sendMarkers, int maxChunkRelightsPerTick, int maxChunkSendsPerTick) {
if (sendMarkers) {
this.tickMarkers();
}
this.tickChunkRelight(maxChunkRelightsPerTick, maxChunkSendsPerTick);
}
private void tickMarkers() {
List<MarkerData> changedData = new ArrayList<>();
Set<UUID> allMarkers = new HashSet<>();
for (Entity entity : this.level.getEntities().getAll()) {
if (entity instanceof Marker marker) {
MarkerData currentData = MarkerData.createFrom(marker);
MarkerData previousData = this.previousMarkerData.get(marker.getUUID());
if (!Objects.equals(currentData, previousData)) {
this.previousMarkerData.put(marker.getUUID(), currentData);
changedData.add(currentData);
}
allMarkers.add(marker.getUUID());
}
}
Set<UUID> oldUuids = new HashSet<>(this.previousMarkerData.keySet());
oldUuids.removeAll(allMarkers);
this.previousMarkerData.keySet().removeAll(oldUuids);
if (!changedData.isEmpty() || !oldUuids.isEmpty()) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeCollection(changedData, MarkerData::write);
buf.writeCollection(oldUuids, FriendlyByteBuf::writeUUID);
byte[] bytes = new byte[buf.writerIndex()];
buf.getBytes(0, bytes);
for (ServerPlayer player : this.level.players()) {
if (AxiomPaper.PLUGIN.activeAxiomPlayers.contains(player.getUUID())) {
player.getBukkitEntity().sendPluginMessage(AxiomPaper.PLUGIN, "axiom:marker_data", bytes);
}
}
}
}
private void tickChunkRelight(int maxChunkRelightsPerTick, int maxChunkSendsPerTick) {
ChunkMap chunkMap = this.level.getChunkSource().chunkMap; ChunkMap chunkMap = this.level.getChunkSource().chunkMap;
boolean sendAll = maxChunkSendsPerTick <= 0; boolean sendAll = maxChunkSendsPerTick <= 0;

Datei anzeigen

@ -0,0 +1,105 @@
package com.moulberry.axiom.marker;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.Marker;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.UUID;
public record MarkerData(UUID uuid, Vec3 position, @Nullable String name, @Nullable Vec3 minRegion, @Nullable Vec3 maxRegion) {
public static MarkerData read(FriendlyByteBuf friendlyByteBuf) {
UUID uuid = friendlyByteBuf.readUUID();
Vec3 position = new Vec3(friendlyByteBuf.readDouble(), friendlyByteBuf.readDouble(), friendlyByteBuf.readDouble());
String name = friendlyByteBuf.readNullable(FriendlyByteBuf::readUtf);
Vec3 minRegion = null;
Vec3 maxRegion = null;
if (friendlyByteBuf.readBoolean()) {
minRegion = new Vec3(friendlyByteBuf.readDouble(), friendlyByteBuf.readDouble(), friendlyByteBuf.readDouble());
maxRegion = new Vec3(friendlyByteBuf.readDouble(), friendlyByteBuf.readDouble(), friendlyByteBuf.readDouble());
}
return new MarkerData(uuid, position, name, minRegion, maxRegion);
}
public static void write(FriendlyByteBuf friendlyByteBuf, MarkerData markerData) {
friendlyByteBuf.writeUUID(markerData.uuid);
friendlyByteBuf.writeDouble(markerData.position.x);
friendlyByteBuf.writeDouble(markerData.position.y);
friendlyByteBuf.writeDouble(markerData.position.z);
friendlyByteBuf.writeNullable(markerData.name, FriendlyByteBuf::writeUtf);
if (markerData.minRegion != null && markerData.maxRegion != null) {
friendlyByteBuf.writeBoolean(true);
friendlyByteBuf.writeDouble(markerData.minRegion.x);
friendlyByteBuf.writeDouble(markerData.minRegion.y);
friendlyByteBuf.writeDouble(markerData.minRegion.z);
friendlyByteBuf.writeDouble(markerData.maxRegion.x);
friendlyByteBuf.writeDouble(markerData.maxRegion.y);
friendlyByteBuf.writeDouble(markerData.maxRegion.z);
} else {
friendlyByteBuf.writeBoolean(false);
}
}
private static final Field dataField;
static {
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
String fieldName = reflectionRemapper.remapFieldName(Marker.class, "data");
try {
dataField = Marker.class.getDeclaredField(fieldName);
dataField.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static CompoundTag getData(Marker marker) {
try {
return (CompoundTag) dataField.get(marker);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static MarkerData createFrom(Marker marker) {
Vec3 position = marker.position();
CompoundTag data = getData(marker);
String name = data.getString("name").trim();
if (name.isEmpty()) name = null;
Vec3 minRegion = null;
Vec3 maxRegion = null;
if (data.contains("min", Tag.TAG_LIST) && data.contains("max", Tag.TAG_LIST)) {
ListTag min = data.getList("min", Tag.TAG_DOUBLE);
ListTag max = data.getList("max", Tag.TAG_DOUBLE);
if (min.size() == 3 && max.size() == 3) {
double minX = min.getDouble(0);
double minY = min.getDouble(1);
double minZ = min.getDouble(2);
double maxX = max.getDouble(0);
double maxY = max.getDouble(1);
double maxZ = max.getDouble(2);
minRegion = new Vec3(minX, minY, minZ);
maxRegion = new Vec3(maxX, maxY, maxZ);
}
}
return new MarkerData(marker.getUUID(), position, name, minRegion, maxRegion);
}
}

Datei anzeigen

@ -4,6 +4,7 @@ import com.google.common.util.concurrent.RateLimiter;
import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.View; import com.moulberry.axiom.View;
import com.moulberry.axiom.WorldExtension;
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;
@ -167,6 +168,8 @@ public class HelloPacketListener implements PluginMessageListener {
} else { } else {
properties.registerFor(plugin, player); properties.registerFor(plugin, player);
} }
WorldExtension.onPlayerJoin(world, player);
} }
} }

Datei anzeigen

@ -1,6 +1,7 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.NbtSanitization;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -104,6 +105,8 @@ public class ManipulateEntityPacketListener implements PluginMessageListener {
if (blacklistedEntities.contains(type)) continue; if (blacklistedEntities.contains(type)) continue;
if (entry.merge != null && !entry.merge.isEmpty()) { if (entry.merge != null && !entry.merge.isEmpty()) {
NbtSanitization.sanitizeEntity(entry.merge);
CompoundTag compoundTag = entity.saveWithoutId(new CompoundTag()); CompoundTag compoundTag = entity.saveWithoutId(new CompoundTag());
compoundTag = merge(compoundTag, entry.merge); compoundTag = merge(compoundTag, entry.merge);
entity.load(compoundTag); entity.load(compoundTag);

Datei anzeigen

@ -0,0 +1,66 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.event.AxiomTimeChangeEvent;
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
import com.moulberry.axiom.marker.MarkerData;
import io.netty.buffer.Unpooled;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Marker;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
import java.util.UUID;
public class MarkerNbtRequestPacketListener implements PluginMessageListener {
private final AxiomPaper plugin;
public MarkerNbtRequestPacketListener(AxiomPaper plugin) {
this.plugin = plugin;
}
@Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
if (!this.plugin.canUseAxiom(player)) {
return;
}
if (!player.hasPermission("axiom.entity.*") && !player.hasPermission("axiom.entity.manipulate")) {
return;
}
if (!this.plugin.canModifyWorld(player, player.getWorld())) {
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
UUID uuid = friendlyByteBuf.readUUID();
ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle();
Entity entity = serverLevel.getEntity(uuid);
if (entity instanceof Marker marker) {
CompoundTag data = MarkerData.getData(marker);
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeUUID(uuid);
buf.writeNbt(data);
byte[] bytes = new byte[buf.writerIndex()];
buf.getBytes(0, bytes);
player.sendPluginMessage(AxiomPaper.PLUGIN, "axiom:marker_nbt_response", bytes);
}
}
}

Datei anzeigen

@ -1,6 +1,7 @@
package com.moulberry.axiom.packet; package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.NbtSanitization;
import com.moulberry.axiom.event.AxiomTeleportEvent; import com.moulberry.axiom.event.AxiomTeleportEvent;
import com.moulberry.axiom.event.AxiomUnknownTeleportEvent; import com.moulberry.axiom.event.AxiomUnknownTeleportEvent;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
@ -81,9 +82,11 @@ public class SpawnEntityPacketListener implements PluginMessageListener {
continue; continue;
} }
if (serverLevel.getEntity(entry.newUuid) != null) continue;
CompoundTag tag = entry.tag == null ? new CompoundTag() : entry.tag; CompoundTag tag = entry.tag == null ? new CompoundTag() : entry.tag;
if (serverLevel.getEntity(entry.newUuid) != null) continue; NbtSanitization.sanitizeEntity(tag);
if (entry.copyFrom != null) { if (entry.copyFrom != null) {
Entity entityCopyFrom = serverLevel.getEntity(entry.copyFrom); Entity entityCopyFrom = serverLevel.getEntity(entry.copyFrom);

Datei anzeigen

@ -19,7 +19,6 @@ incompatible-data-version: "warn"
unsupported-axiom-version: "warn" unsupported-axiom-version: "warn"
client-doesnt-support-restrictions: "ignore" client-doesnt-support-restrictions: "ignore"
# Maximum packet size. Must not be less than 32767 # Maximum packet size. Must not be less than 32767
max-block-buffer-packet-size: 0x100000 max-block-buffer-packet-size: 0x100000
@ -51,6 +50,9 @@ whitelist-entities:
blacklist-entities: blacklist-entities:
# - "minecraft:ender_dragon" # - "minecraft:ender_dragon"
# True allows players to see/manipulate marker entities
send-markers: false
# Disallowed blocks # Disallowed blocks
disallowed-blocks: disallowed-blocks:
# - "minecraft:wheat" # - "minecraft:wheat"
@ -73,3 +75,4 @@ packet-handlers:
spawn-entity: true spawn-entity: true
manipulate-entity: true manipulate-entity: true
delete-entity: true delete-entity: true
marker-nbt-request: true