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

Partial ViaVersion support

Dieser Commit ist enthalten in:
Moulberry 2024-05-03 19:23:31 +08:00
Ursprung ec99e2df42
Commit ceca1afe68
12 geänderte Dateien mit 165 neuen und 47 gelöschten Zeilen

Datei anzeigen

@ -18,6 +18,7 @@ java {
repositories {
mavenCentral()
maven("https://repo.viaversion.com")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://jitpack.io")
maven("https://repo.papermc.io/repository/maven-public/")
@ -31,6 +32,9 @@ dependencies {
// Zstd Compression Library
implementation("com.github.luben:zstd-jni:1.5.5-4")
// ViaVersion support
compileOnly("com.viaversion:viaversion-api:4.9.4-SNAPSHOT")
// WorldGuard support
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT")

Datei anzeigen

@ -36,6 +36,7 @@ import org.bukkit.plugin.messaging.Messenger;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.Nullable;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -49,6 +50,7 @@ public class AxiomPaper extends JavaPlugin implements Listener {
public final Set<UUID> activeAxiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>());
public final Map<UUID, RateLimiter> playerBlockBufferRateLimiters = new ConcurrentHashMap<>();
public final Map<UUID, Restrictions> playerRestrictions = new ConcurrentHashMap<>();
public final Map<UUID, IdMapper<BlockState>> playerBlockRegistry = new ConcurrentHashMap<>();
public Configuration configuration;
public IdMapper<BlockState> allowedBlockRegistry = null;
@ -265,6 +267,7 @@ public class AxiomPaper extends JavaPlugin implements Listener {
activeAxiomPlayers.retainAll(stillActiveAxiomPlayers);
playerBlockBufferRateLimiters.keySet().retainAll(stillActiveAxiomPlayers);
playerRestrictions.keySet().retainAll(stillActiveAxiomPlayers);
playerBlockRegistry.keySet().retainAll(stillActiveAxiomPlayers);
}, 20, 20);
boolean sendMarkers = configuration.getBoolean("send-markers");
@ -292,6 +295,14 @@ public class AxiomPaper extends JavaPlugin implements Listener {
return this.playerBlockBufferRateLimiters.get(uuid);
}
public boolean hasCustomBlockRegistry(UUID uuid) {
return this.playerBlockRegistry.containsKey(uuid);
}
public IdMapper<BlockState> getBlockRegistry(UUID uuid) {
return this.playerBlockRegistry.getOrDefault(uuid, this.allowedBlockRegistry);
}
private final WeakHashMap<World, ServerWorldPropertiesRegistry> worldProperties = new WeakHashMap<>();
public @Nullable ServerWorldPropertiesRegistry getWorldPropertiesIfPresent(World world) {

Datei anzeigen

@ -3,6 +3,7 @@ package com.moulberry.axiom;
import com.mojang.brigadier.StringReader;
import com.mojang.datafixers.util.Either;
import com.moulberry.axiom.buffer.BlockBuffer;
import com.viaversion.viaversion.api.data.Mappings;
import net.minecraft.commands.arguments.blocks.BlockPredicateArgument;
import net.minecraft.commands.arguments.blocks.BlockStateParser;
import net.minecraft.core.IdMapper;

Datei anzeigen

@ -9,6 +9,7 @@ import it.unimi.dsi.fastutil.objects.ObjectSet;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.IdMapper;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
@ -23,18 +24,16 @@ public class BlockBuffer {
private final Long2ObjectMap<PalettedContainer<BlockState>> values;
private IdMapper<BlockState> registry;
private PalettedContainer<BlockState> last = null;
private long lastId = AxiomConstants.MIN_POSITION_LONG;
private final Long2ObjectMap<Short2ObjectMap<CompressedBlockEntity>> blockEntities = new Long2ObjectOpenHashMap<>();
private long totalBlockEntities = 0;
private long totalBlockEntityBytes = 0;
public BlockBuffer() {
public BlockBuffer(IdMapper<BlockState> registry) {
this.values = new Long2ObjectOpenHashMap<>();
}
public BlockBuffer(Long2ObjectMap<PalettedContainer<BlockState>> values) {
this.values = values;
this.registry = registry;
}
public void save(FriendlyByteBuf friendlyByteBuf) {
@ -57,8 +56,9 @@ public class BlockBuffer {
friendlyByteBuf.writeLong(AxiomConstants.MIN_POSITION_LONG);
}
public static BlockBuffer load(FriendlyByteBuf friendlyByteBuf, @Nullable RateLimiter rateLimiter, AtomicBoolean reachedRateLimit) {
BlockBuffer buffer = new BlockBuffer();
public static BlockBuffer load(FriendlyByteBuf friendlyByteBuf, @Nullable RateLimiter rateLimiter, AtomicBoolean reachedRateLimit,
IdMapper<BlockState> registry) {
BlockBuffer buffer = new BlockBuffer(registry);
long totalBlockEntities = 0;
long totalBlockEntityBytes = 0;
@ -177,7 +177,7 @@ public class BlockBuffer {
public PalettedContainer<BlockState> getOrCreateSection(long id) {
if (this.last == null || id != this.lastId) {
this.lastId = id;
this.last = this.values.computeIfAbsent(id, k -> new PalettedContainer<>(AxiomPaper.PLUGIN.allowedBlockRegistry,
this.last = this.values.computeIfAbsent(id, k -> new PalettedContainer<>(this.registry,
EMPTY_STATE, PalettedContainer.Strategy.SECTION_STATES));
}

Datei anzeigen

@ -62,8 +62,7 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder {
} else if (identifier.equals(UPLOAD_BLUEPRINT)) {
ServerPlayer player = connection.getPlayer();
if (AxiomPaper.PLUGIN.canUseAxiom(player.getBukkitEntity())) {
player.getServer().execute(() -> uploadBlueprint.onReceive(player, buf));
uploadBlueprint.onReceive(player, buf);
success = true;
in.skipBytes(in.readableBytes());
return;
@ -74,8 +73,15 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder {
byte[] bytes = new byte[buf.writerIndex() - buf.readerIndex()];
buf.getBytes(buf.readerIndex(), bytes);
player.getServer().execute(() -> requestChunkDataPacketListener.onPluginMessageReceived(
identifier.toString(), player.getBukkitEntity(), bytes));
player.getServer().execute(() -> {
try {
requestChunkDataPacketListener.onPluginMessageReceived(
identifier.toString(), player.getBukkitEntity(), bytes);
} catch (Throwable t) {
player.getBukkitEntity().kick(net.kyori.adventure.text.Component.text(
"An error occured while requesting chunk data: " + t.getMessage()));
}
});
success = true;
in.skipBytes(in.readableBytes());

Datei anzeigen

@ -1,15 +1,17 @@
package com.moulberry.axiom.packet;
import com.google.common.util.concurrent.RateLimiter;
import com.moulberry.axiom.AxiomConstants;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.View;
import com.moulberry.axiom.WorldExtension;
import com.moulberry.axiom.*;
import com.moulberry.axiom.blueprint.ServerBlueprintManager;
import com.moulberry.axiom.event.AxiomHandshakeEvent;
import com.moulberry.axiom.persistence.ItemStackDataType;
import com.moulberry.axiom.persistence.UUIDDataType;
import com.moulberry.axiom.viaversion.ViaVersionHelper;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.*;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag;
import io.netty.buffer.Unpooled;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -52,18 +54,52 @@ public class HelloPacketListener implements PluginMessageListener {
int serverDataVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
if (dataVersion != serverDataVersion) {
Component text = Component.text("Axiom: Incompatible data version detected (client " + dataVersion +
", server " + serverDataVersion + "), are you using ViaVersion?");
if (!Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
Component text = Component.text("Axiom: Incompatible data version detected (client " + dataVersion +
", server " + serverDataVersion + ")");
String incompatibleDataVersion = plugin.configuration.getString("incompatible-data-version");
if (incompatibleDataVersion == null) incompatibleDataVersion = "kick";
if (incompatibleDataVersion.equals("warn")) {
String incompatibleDataVersion = plugin.configuration.getString("incompatible-data-version");
if (incompatibleDataVersion == null) incompatibleDataVersion = "kick";
if (incompatibleDataVersion.equals("warn")) {
player.sendMessage(text.color(NamedTextColor.RED));
return;
} else if (!incompatibleDataVersion.equals("ignore")) {
player.kick(text);
return;
}
} else {
// int playerVersion = Via.getAPI().getPlayerVersion(player.getUniqueId());
// if (ProtocolVersion.isRegistered(playerVersion)) {
// ProtocolVersion version = ProtocolVersion.getProtocol(playerVersion);
// String name = version.getName().split("/")[0];
//
//
// }
CompoundTag tag = MappingDataLoader.loadNBT("mappings-1.20.2to1.20.3.nbt");
if (tag == null) {
player.kick(Component.text("Axiom+ViaVersion: Failed to load mappings (1.20.2 <-> 1.20.3)"));
return;
}
Mappings mappings = MappingDataLoader.loadMappings(tag, "blockstates");
if (mappings == null) {
player.kick(Component.text("Axiom+ViaVersion: Failed to load mapped blockstates (1.20.2 <-> 1.20.3"));
return;
}
this.plugin.playerBlockRegistry.put(player.getUniqueId(), ViaVersionHelper.applyMappings(this.plugin.allowedBlockRegistry,
BiMappings.of(mappings).inverse()));
Component text = Component.text("Axiom: Warning, client and server versions don't match. " +
"Axiom will try to use ViaVersion conversions, but this process may cause problems");
player.sendMessage(text.color(NamedTextColor.RED));
return;
} else if (!incompatibleDataVersion.equals("ignore")) {
player.kick(text);
return;
}
// inverse.getNewIdOrDefault()
}
if (apiVersion != AxiomConstants.API_VERSION) {

Datei anzeigen

@ -5,6 +5,7 @@ import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.VersionHelper;
import com.moulberry.axiom.buffer.CompressedBlockEntity;
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
import com.viaversion.viaversion.api.Via;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.longs.*;
import net.minecraft.core.BlockPos;
@ -46,7 +47,7 @@ public class RequestChunkDataPacketListener implements PluginMessageListener {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
long id = friendlyByteBuf.readLong();
if (!this.plugin.canUseAxiom(bukkitPlayer)) {
if (!this.plugin.canUseAxiom(bukkitPlayer) || this.plugin.hasCustomBlockRegistry(bukkitPlayer.getUniqueId())) {
// We always send an 'empty' response in order to make the client happy
sendEmptyResponse(player, id);
return;

Datei anzeigen

@ -85,7 +85,7 @@ public class SetBlockBufferPacketListener {
byte type = friendlyByteBuf.readByte();
if (type == 0) {
AtomicBoolean reachedRateLimit = new AtomicBoolean(false);
BlockBuffer buffer = BlockBuffer.load(friendlyByteBuf, rateLimiter, reachedRateLimit);
BlockBuffer buffer = BlockBuffer.load(friendlyByteBuf, rateLimiter, reachedRateLimit, this.plugin.getBlockRegistry(player.getUUID()));
if (reachedRateLimit.get()) {
player.sendSystemMessage(Component.literal("[Axiom] Exceeded server rate-limit of " + (int)rateLimiter.getRate() + " sections per second")
.withStyle(ChatFormatting.RED));

Datei anzeigen

@ -7,6 +7,7 @@ import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
import io.netty.buffer.Unpooled;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.IdMapper;
import net.minecraft.core.SectionPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerLevel;
@ -79,8 +80,9 @@ public class SetBlockPacketListener implements PluginMessageListener {
// Read packet
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
IntFunction<Map<BlockPos, BlockState>> mapFunction = FriendlyByteBuf.limitValue(Maps::newLinkedHashMapWithExpectedSize, 512);
IdMapper<BlockState> registry = this.plugin.getBlockRegistry(bukkitPlayer.getUniqueId());
Map<BlockPos, BlockState> blocks = friendlyByteBuf.readMap(mapFunction,
FriendlyByteBuf::readBlockPos, buf -> buf.readById(this.plugin.allowedBlockRegistry));
FriendlyByteBuf::readBlockPos, buf -> buf.readById(registry));
boolean updateNeighbors = friendlyByteBuf.readBoolean();
int reason = friendlyByteBuf.readVarInt();
@ -129,6 +131,10 @@ public class SetBlockPacketListener implements PluginMessageListener {
BlockPos blockPos = entry.getKey();
BlockState blockState = entry.getValue();
if (blockState == null) {
continue;
}
// Disallow in unloaded chunks
if (!player.level().isLoaded(blockPos)) {
continue;
@ -154,6 +160,10 @@ public class SetBlockPacketListener implements PluginMessageListener {
BlockPos blockPos = entry.getKey();
BlockState blockState = entry.getValue();
if (blockState == null) {
continue;
}
// Disallow in unloaded chunks
if (!player.level().isLoaded(blockPos)) {
continue;

Datei anzeigen

@ -47,25 +47,34 @@ public class UploadBlueprintPacketListener {
return;
}
Path path = this.plugin.blueprintFolder.resolve(relative);
String pathName = pathStr.substring(0, pathStr.length()-3);
// Write file
try {
Files.createDirectories(path.getParent());
} catch (IOException e) {
return;
}
try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(path))) {
BlueprintIo.writeRaw(outputStream, rawBlueprint);
} catch (IOException e) {
return;
}
serverPlayer.getServer().execute(() -> {
try {
Path path = this.plugin.blueprintFolder.resolve(relative);
// Update registry
registry.blueprints().put("/" + pathStr.substring(0, pathStr.length()-3), rawBlueprint);
// Write file
try {
Files.createDirectories(path.getParent());
} catch (IOException e) {
return;
}
try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(path))) {
BlueprintIo.writeRaw(outputStream, rawBlueprint);
} catch (IOException e) {
return;
}
// Resend manifest
ServerBlueprintManager.sendManifest(serverPlayer.getServer().getPlayerList().getPlayers());
// Update registry
registry.blueprints().put("/" + pathName, rawBlueprint);
// Resend manifest
ServerBlueprintManager.sendManifest(serverPlayer.getServer().getPlayerList().getPlayers());
} catch (Throwable t) {
serverPlayer.getBukkitEntity().kick(net.kyori.adventure.text.Component.text(
"An error occured while uploading blueprint: " + t.getMessage()));
}
});
}
}

Datei anzeigen

@ -1,2 +1,42 @@
package com.moulberry.axiom.viaversion;public class ViaVersionHelper {
package com.moulberry.axiom.viaversion;
import com.moulberry.axiom.buffer.BlockBuffer;
import com.viaversion.viaversion.api.data.BiMappings;
import com.viaversion.viaversion.api.data.Mappings;
import net.minecraft.core.IdMapper;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
public class ViaVersionHelper {
public static IdMapper<BlockState> applyMappings(IdMapper<BlockState> registry, BiMappings mappings) {
IdMapper<BlockState> newBlockRegistry = new IdMapper<>();
// Add empty mappings for non-existent blocks
int size = mappings.mappedSize();
for (int i = 0; i < size; i++) {
newBlockRegistry.addMapping(BlockBuffer.EMPTY_STATE, i);
}
// Map blocks
for (int i = 0; i < registry.size(); i++) {
BlockState blockState = registry.byId(i);
if (blockState != null) {
int newId = mappings.getNewId(i);
if (newId >= 0) {
newBlockRegistry.addMapping(blockState, newId);
}
}
}
// Ensure block -> id is correct for the empty state
int newEmptyStateId = mappings.getNewId(registry.getId(BlockBuffer.EMPTY_STATE));
if (newEmptyStateId >= 0) {
newBlockRegistry.addMapping(BlockBuffer.EMPTY_STATE, newEmptyStateId);
}
return newBlockRegistry;
}
}

Datei anzeigen

@ -8,7 +8,7 @@ api-version: "$apiVersion"
permissions:
axiom.*:
description: Allows use of all default Axiom features
default: op
default: true # op
axiom.entity.*:
description: Allows use of all entity-related features (spawning, manipulating, deleting)
@ -22,7 +22,7 @@ permissions:
axiom.allow_copying_other_plots:
description: This permission allows users to copy other user's plots
default: true
default: false # true
axiom.can_import_blocks:
description: Allows players to import schematics/blueprints into Axiom
default: true