Mirror von
https://github.com/Moulberry/AxiomPaperPlugin.git
synchronisiert 2024-11-08 09:30:04 +01:00
Partial ViaVersion support
Dieser Commit ist enthalten in:
Ursprung
914084f4ca
Commit
5ea1f32134
@ -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")
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren