Mirror von
https://github.com/Moulberry/AxiomPaperPlugin.git
synchronisiert 2024-11-17 05:40:06 +01:00
Merge branch 'master' into entity-manip-event
Dieser Commit ist enthalten in:
Commit
2673221f38
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,6 +15,9 @@ out/
|
||||
plugin/bin/
|
||||
api/bin/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
|
10
README.md
10
README.md
@ -6,3 +6,13 @@ Serverside component for Axiom
|
||||
|
||||
## Download
|
||||
https://modrinth.com/plugin/axiom-paper-plugin/
|
||||
|
||||
## FAQ
|
||||
|
||||
**Axiom works in singleplayer but not when I connect to a multiplayer server running the Axiom Paper Plugin. What gives?**
|
||||
|
||||
First, the player must be an op on the server. If the player does not have op permissions, run `/op <playername>`. This player must then disconnect from the server and reconnect.
|
||||
|
||||
If you're using an alternative solution for permission management, you must give players the `axiom.*` permission.
|
||||
|
||||
If players continue to have issues, they can run the `/whynoaxiom` command for more information.
|
||||
|
@ -1,43 +1,52 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
id("io.papermc.paperweight.userdev") version "1.5.11"
|
||||
id("xyz.jpenilla.run-paper") version "2.2.2" // Adds runServer and runMojangMappedServer tasks for testing
|
||||
alias(libs.plugins.paperweight.userdev)
|
||||
alias(libs.plugins.run.paper) // Adds runServer and runMojangMappedServer tasks for testing
|
||||
|
||||
// Shades and relocates dependencies into our plugin jar. See https://imperceptiblethoughts.com/shadow/introduction/
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
alias(libs.plugins.shadow)
|
||||
}
|
||||
|
||||
group = "com.moulberry.axiom"
|
||||
version = "1.5.9"
|
||||
version = "1.5.11"
|
||||
description = "Serverside component for Axiom on Paper"
|
||||
|
||||
java {
|
||||
// Configure the java toolchain. This allows gradle to auto-provision JDK 17 on systems that only have JDK 8 installed for example.
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
// Configure the java toolchain. This allows gradle to auto-provision JDK 21 on systems that only have JDK 11 installed for example.
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
|
||||
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/")
|
||||
maven("https://maven.enginehub.org/repo/")
|
||||
maven("https://maven.playpro.com")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
paperweight.paperDevBundle("1.20.4-R0.1-SNAPSHOT")
|
||||
implementation("xyz.jpenilla:reflection-remapper:0.1.0-SNAPSHOT")
|
||||
paperweight.paperDevBundle(libs.versions.paper)
|
||||
implementation(libs.reflection.remapper)
|
||||
implementation(libs.cloud.paper)
|
||||
|
||||
// Zstd Compression Library
|
||||
implementation("com.github.luben:zstd-jni:1.5.5-4")
|
||||
implementation(libs.zstd.jni)
|
||||
|
||||
// ViaVersion support
|
||||
compileOnly(libs.viaversion.api)
|
||||
|
||||
// WorldGuard support
|
||||
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT")
|
||||
compileOnly(libs.worldguard.bukkit)
|
||||
|
||||
// PlotSquared support
|
||||
implementation(platform("com.intellectualsites.bom:bom-newest:1.37"))
|
||||
compileOnly("com.intellectualsites.plotsquared:plotsquared-core")
|
||||
compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false }
|
||||
implementation(platform(libs.bom.newest))
|
||||
compileOnly(libs.plotsquared.core)
|
||||
compileOnly(libs.plotsquared.bukkit) { isTransitive = false }
|
||||
|
||||
// CoreProtect support
|
||||
compileOnly(libs.coreprotect)
|
||||
}
|
||||
|
||||
tasks {
|
||||
@ -51,7 +60,7 @@ tasks {
|
||||
|
||||
// Set the release flag. This configures what version bytecode the compiler will emit, as well as what JDK APIs are usable.
|
||||
// See https://openjdk.java.net/jeps/247 for more information.
|
||||
options.release.set(17)
|
||||
options.release.set(21)
|
||||
}
|
||||
javadoc {
|
||||
options.encoding = Charsets.UTF_8.name() // We want UTF-8 for everything
|
||||
|
32
gradle/libs.versions.toml
Normale Datei
32
gradle/libs.versions.toml
Normale Datei
@ -0,0 +1,32 @@
|
||||
[versions]
|
||||
# Dependencies
|
||||
bom-newest = "1.37"
|
||||
cloud-paper = "2.0.0-20240516.054251-69"
|
||||
coreprotect = "22.4"
|
||||
paper = "1.20.6-R0.1-SNAPSHOT"
|
||||
plotsquared = "7.3.9-20240513.192211-13"
|
||||
reflection-remapper = "0.1.2-20240315.033304-2"
|
||||
viaversion-api = "4.10.1-20240505.124211-22"
|
||||
worldguard-bukkit = "7.1.0-20240503.180049-12"
|
||||
zstd-jni = "1.5.5-4"
|
||||
|
||||
# Plugins
|
||||
paperweight-userdev = "1.7.1"
|
||||
run-paper = "2.2.4"
|
||||
shadow = "8.1.7"
|
||||
|
||||
[libraries]
|
||||
bom-newest = { group = "com.intellectualsites.bom", name = "bom-newest", version.ref = "bom-newest" }
|
||||
cloud-paper = { group = "org.incendo", name = "cloud-paper", version.ref = "cloud-paper" }
|
||||
coreprotect = { group = "net.coreprotect", name = "coreprotect", version.ref = "coreprotect" }
|
||||
plotsquared-bukkit = { group = "com.intellectualsites.plotsquared", name = "plotsquared-bukkit", version.ref = "plotsquared" }
|
||||
plotsquared-core = { group = "com.intellectualsites.plotsquared", name = "plotsquared-core", version.ref = "plotsquared" }
|
||||
reflection-remapper = { group = "xyz.jpenilla", name = "reflection-remapper", version.ref = "reflection-remapper" }
|
||||
viaversion-api = { group = "com.viaversion", name = "viaversion-api", version.ref = "viaversion-api" }
|
||||
worldguard-bukkit = { group = "com.sk89q.worldguard", name = "worldguard-bukkit", version.ref = "worldguard-bukkit" }
|
||||
zstd-jni = { group = "com.github.luben", name = "zstd-jni", version = "1.5.5-4" }
|
||||
|
||||
[plugins]
|
||||
paperweight-userdev = { id = "io.papermc.paperweight.userdev", version.ref = "paperweight-userdev" }
|
||||
run-paper = { id = "xyz.jpenilla.run-paper", version.ref = "run-paper" }
|
||||
shadow = { id = "io.github.goooler.shadow", version.ref = "shadow" }
|
@ -3,8 +3,10 @@ package com.moulberry.axiom;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import com.moulberry.axiom.blueprint.ServerBlueprintManager;
|
||||
import com.moulberry.axiom.buffer.CompressedBlockEntity;
|
||||
import com.moulberry.axiom.commands.AxiomDebugCommand;
|
||||
import com.moulberry.axiom.event.AxiomCreateWorldPropertiesEvent;
|
||||
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.world_properties.server.ServerWorldPropertiesRegistry;
|
||||
@ -15,17 +17,19 @@ import io.papermc.paper.event.world.WorldGameRuleChangeEvent;
|
||||
import io.papermc.paper.network.ChannelInitializeListener;
|
||||
import io.papermc.paper.network.ChannelInitializeListenerHolder;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.ConnectionProtocol;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.*;
|
||||
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.network.protocol.game.GameProtocols;
|
||||
import net.minecraft.network.protocol.game.ServerGamePacketListener;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@ -34,9 +38,13 @@ import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.plugin.messaging.Messenger;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
@ -49,6 +57,8 @@ 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 final Map<UUID, Integer> playerProtocolVersion = new ConcurrentHashMap<>();
|
||||
public Configuration configuration;
|
||||
|
||||
public IdMapper<BlockState> allowedBlockRegistry = null;
|
||||
@ -151,21 +161,16 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
RequestChunkDataPacketListener requestChunkDataPacketListener = allowLargeChunkDataRequest ?
|
||||
new RequestChunkDataPacketListener(this) : null;
|
||||
|
||||
// Hack to figure out the id of the CustomPayload packet
|
||||
ProtocolInfo<ServerGamePacketListener> protocol = GameProtocols.SERVERBOUND.bind(k -> new RegistryFriendlyByteBuf(k,
|
||||
MinecraftServer.getServer().registryAccess()));
|
||||
RegistryFriendlyByteBuf friendlyByteBuf = new RegistryFriendlyByteBuf(Unpooled.buffer(), MinecraftServer.getServer().registryAccess());
|
||||
protocol.codec().encode(friendlyByteBuf, new ServerboundCustomPayloadPacket(new DiscardedPayload(new ResourceLocation("dummy"), Unpooled.buffer())));
|
||||
int payloadId = friendlyByteBuf.readVarInt();
|
||||
|
||||
ChannelInitializeListenerHolder.addListener(Key.key("axiom:handle_big_payload"), new ChannelInitializeListener() {
|
||||
@Override
|
||||
public void afterInitChannel(@NonNull Channel channel) {
|
||||
var packets = ConnectionProtocol.PLAY.getPacketsByIds(PacketFlow.SERVERBOUND);
|
||||
int payloadId = -1;
|
||||
for (Map.Entry<Integer, Class<? extends Packet<?>>> entry : packets.entrySet()) {
|
||||
if (entry.getValue() == ServerboundCustomPayloadPacket.class) {
|
||||
payloadId = entry.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (payloadId < 0) {
|
||||
throw new RuntimeException("Failed to find ServerboundCustomPayloadPacket id");
|
||||
}
|
||||
|
||||
Connection connection = (Connection) channel.pipeline().get("packet_handler");
|
||||
channel.pipeline().addBefore("decoder", "axiom-big-payload-handler",
|
||||
new AxiomBigPayloadHandler(payloadId, connection, setBlockBufferPacketListener,
|
||||
@ -213,32 +218,31 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
send = true;
|
||||
}
|
||||
|
||||
BlockPos boundsMin = null;
|
||||
BlockPos boundsMax = null;
|
||||
Set<PlotSquaredIntegration.PlotBox> bounds = Set.of();
|
||||
|
||||
if (!player.hasPermission("axiom.allow_copying_other_plots")) {
|
||||
if (PlotSquaredIntegration.isPlotWorld(player.getWorld())) {
|
||||
PlotSquaredIntegration.PlotBounds editable = PlotSquaredIntegration.getCurrentEditablePlot(player);
|
||||
if (editable != null) {
|
||||
restrictions.lastPlotBounds = editable;
|
||||
boundsMin = editable.min();
|
||||
boundsMax = editable.max();
|
||||
bounds = editable.boxes();
|
||||
} else if (restrictions.lastPlotBounds != null && restrictions.lastPlotBounds.worldName().equals(player.getWorld().getName())) {
|
||||
boundsMin = restrictions.lastPlotBounds.min();
|
||||
boundsMax = restrictions.lastPlotBounds.max();
|
||||
bounds = restrictions.lastPlotBounds.boxes();
|
||||
} else {
|
||||
boundsMin = BlockPos.ZERO;
|
||||
boundsMax = BlockPos.ZERO;
|
||||
bounds = Set.of(new PlotSquaredIntegration.PlotBox(BlockPos.ZERO, BlockPos.ZERO));
|
||||
}
|
||||
}
|
||||
|
||||
if (bounds.size() == 1) {
|
||||
PlotSquaredIntegration.PlotBox plotBounds = bounds.iterator().next();
|
||||
|
||||
int min = Integer.MIN_VALUE;
|
||||
int max = Integer.MAX_VALUE;
|
||||
if (boundsMin != null && boundsMax != null &&
|
||||
boundsMin.getX() == min && boundsMin.getY() == min && boundsMin.getZ() == min &&
|
||||
boundsMax.getX() == max && boundsMax.getY() == max && boundsMax.getZ() == max) {
|
||||
boundsMin = null;
|
||||
boundsMax = null;
|
||||
|
||||
if (plotBounds.min().getX() == min && plotBounds.min().getY() == min && plotBounds.min().getZ() == min &&
|
||||
plotBounds.max().getX() == max && plotBounds.max().getY() == max && plotBounds.max().getZ() == max) {
|
||||
bounds = Set.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,12 +250,10 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
|
||||
if (restrictions.maxSectionsPerSecond != rateLimit ||
|
||||
restrictions.canImportBlocks != allowImportingBlocks ||
|
||||
!Objects.equals(restrictions.boundsMin, boundsMin) ||
|
||||
!Objects.equals(restrictions.boundsMax, boundsMax)) {
|
||||
!Objects.equals(restrictions.bounds, bounds)) {
|
||||
restrictions.maxSectionsPerSecond = rateLimit;
|
||||
restrictions.canImportBlocks = allowImportingBlocks;
|
||||
restrictions.boundsMin = boundsMin;
|
||||
restrictions.boundsMax = boundsMax;
|
||||
restrictions.bounds = bounds;
|
||||
send = true;
|
||||
}
|
||||
|
||||
@ -265,6 +267,8 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
activeAxiomPlayers.retainAll(stillActiveAxiomPlayers);
|
||||
playerBlockBufferRateLimiters.keySet().retainAll(stillActiveAxiomPlayers);
|
||||
playerRestrictions.keySet().retainAll(stillActiveAxiomPlayers);
|
||||
playerBlockRegistry.keySet().retainAll(stillActiveAxiomPlayers);
|
||||
playerProtocolVersion.keySet().retainAll(stillActiveAxiomPlayers);
|
||||
}, 20, 20);
|
||||
|
||||
boolean sendMarkers = configuration.getBoolean("send-markers");
|
||||
@ -274,6 +278,25 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> {
|
||||
WorldExtension.tick(MinecraftServer.getServer(), sendMarkers, maxChunkRelightsPerTick, maxChunkSendsPerTick);
|
||||
}, 1, 1);
|
||||
|
||||
try {
|
||||
LegacyPaperCommandManager<CommandSender> manager = LegacyPaperCommandManager.createNative(
|
||||
this,
|
||||
ExecutionCoordinator.simpleCoordinator()
|
||||
);
|
||||
|
||||
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
|
||||
manager.registerBrigadier();
|
||||
}
|
||||
|
||||
AxiomDebugCommand.register(this, manager);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (CoreProtectIntegration.isEnabled()) {
|
||||
this.getLogger().info("CoreProtect integration enabled");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean logLargeBlockBufferChanges() {
|
||||
@ -281,17 +304,46 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
}
|
||||
|
||||
public boolean hasAxiomPermission(Player player) {
|
||||
return player.hasPermission("axiom.*") || player.isOp();
|
||||
return hasAxiomPermission(player, null, false);
|
||||
}
|
||||
|
||||
public boolean hasAxiomPermission(Player player, String permission, boolean strict) {
|
||||
if (player.hasPermission("axiom.*") || player.isOp()) {
|
||||
return !strict || permission == null || player.hasPermission("axiom.all") || player.hasPermission(permission);
|
||||
} else if (permission != null && !player.hasPermission(permission)) {
|
||||
return false;
|
||||
}
|
||||
return player.hasPermission("axiom.use");
|
||||
}
|
||||
|
||||
public boolean canUseAxiom(Player player) {
|
||||
return hasAxiomPermission(player) && activeAxiomPlayers.contains(player.getUniqueId());
|
||||
return canUseAxiom(player, null, false);
|
||||
}
|
||||
|
||||
public boolean canUseAxiom(Player player, String permission) {
|
||||
return canUseAxiom(player, permission, false);
|
||||
}
|
||||
|
||||
public boolean canUseAxiom(Player player, String permission, boolean strict) {
|
||||
return activeAxiomPlayers.contains(player.getUniqueId()) && hasAxiomPermission(player, permission, strict);
|
||||
}
|
||||
|
||||
public @Nullable RateLimiter getBlockBufferRateLimiter(UUID uuid) {
|
||||
return this.playerBlockBufferRateLimiters.get(uuid);
|
||||
}
|
||||
|
||||
public boolean isMismatchedDataVersion(UUID uuid) {
|
||||
return this.playerProtocolVersion.containsKey(uuid);
|
||||
}
|
||||
|
||||
public int getProtocolVersionFor(UUID uuid) {
|
||||
return this.playerProtocolVersion.getOrDefault(uuid, SharedConstants.getProtocolVersion());
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -363,7 +415,7 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
||||
}
|
||||
|
||||
private ServerWorldPropertiesRegistry createWorldProperties(World world) {
|
||||
ServerWorldPropertiesRegistry registry = new ServerWorldPropertiesRegistry(world);
|
||||
ServerWorldPropertiesRegistry registry = new ServerWorldPropertiesRegistry(new WeakReference<>(world));
|
||||
|
||||
AxiomCreateWorldPropertiesEvent createEvent = new AxiomCreateWorldPropertiesEvent(world, registry);
|
||||
Bukkit.getPluginManager().callEvent(createEvent);
|
||||
|
@ -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;
|
||||
|
@ -6,14 +6,15 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class Restrictions {
|
||||
|
||||
public boolean canImportBlocks = true;
|
||||
public boolean canUseEditor = true;
|
||||
public boolean canEditDisplayEntities = true;
|
||||
public int maxSectionsPerSecond = 0;
|
||||
public BlockPos boundsMin = null;
|
||||
public BlockPos boundsMax = null;
|
||||
public Set<PlotSquaredIntegration.PlotBox> bounds = Set.of();
|
||||
|
||||
public PlotSquaredIntegration.PlotBounds lastPlotBounds = null;
|
||||
|
||||
@ -26,16 +27,21 @@ public class Restrictions {
|
||||
|
||||
buf.writeVarInt(this.maxSectionsPerSecond);
|
||||
|
||||
if (this.boundsMin == null || this.boundsMax == null) {
|
||||
buf.writeBoolean(false);
|
||||
int count = Math.min(64, bounds.size());
|
||||
buf.writeVarInt(count);
|
||||
for (PlotSquaredIntegration.PlotBox bound : this.bounds) {
|
||||
if (count > 0) {
|
||||
count -= 1;
|
||||
} else {
|
||||
buf.writeBoolean(true);
|
||||
int minX = this.boundsMin.getX();
|
||||
int minY = this.boundsMin.getY();
|
||||
int minZ = this.boundsMin.getZ();
|
||||
int maxX = this.boundsMax.getX();
|
||||
int maxY = this.boundsMax.getY();
|
||||
int maxZ = this.boundsMax.getZ();
|
||||
break;
|
||||
}
|
||||
|
||||
int minX = bound.min().getX();
|
||||
int minY = bound.min().getY();
|
||||
int minZ = bound.min().getZ();
|
||||
int maxX = bound.max().getX();
|
||||
int maxY = bound.max().getY();
|
||||
int maxZ = bound.max().getZ();
|
||||
|
||||
if (minX < -33554431) minX = -33554431;
|
||||
if (minX > 33554431) minX = 33554431;
|
||||
@ -61,4 +67,15 @@ public class Restrictions {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Restrictions{" +
|
||||
"canImportBlocks=" + canImportBlocks +
|
||||
", canUseEditor=" + canUseEditor +
|
||||
", canEditDisplayEntities=" + canEditDisplayEntities +
|
||||
", maxSectionsPerSecond=" + maxSectionsPerSecond +
|
||||
", bounds=" + bounds +
|
||||
", lastPlotBounds=" + lastPlotBounds +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
package com.moulberry.axiom;
|
||||
|
||||
import com.moulberry.axiom.packet.CustomByteArrayPayload;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
public class VersionHelper {
|
||||
|
||||
public static void sendCustomPayload(ServerPlayer serverPlayer, ResourceLocation id, byte[] data) {
|
||||
serverPlayer.connection.send(new ClientboundCustomPayloadPacket(new CustomByteArrayPayload(id, data)));
|
||||
serverPlayer.connection.send(new ClientboundCustomPayloadPacket(new DiscardedPayload(id, Unpooled.wrappedBuffer(data))));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
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.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
@ -65,7 +65,7 @@ public class WorldExtension {
|
||||
|
||||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf.writeCollection(markerData, MarkerData::write);
|
||||
buf.writeCollection(Set.of(), FriendlyByteBuf::writeUUID);
|
||||
buf.writeCollection(Set.<UUID>of(), (buffer, uuid) -> buffer.writeUUID(uuid));
|
||||
byte[] bytes = new byte[buf.writerIndex()];
|
||||
buf.getBytes(0, bytes);
|
||||
|
||||
@ -106,7 +106,7 @@ public class WorldExtension {
|
||||
if (!changedData.isEmpty() || !oldUuids.isEmpty()) {
|
||||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf.writeCollection(changedData, MarkerData::write);
|
||||
buf.writeCollection(oldUuids, FriendlyByteBuf::writeUUID);
|
||||
buf.writeCollection(oldUuids, (buffer, uuid) -> buffer.writeUUID(uuid));
|
||||
byte[] bytes = new byte[buf.writerIndex()];
|
||||
buf.getBytes(0, bytes);
|
||||
|
||||
|
@ -128,7 +128,7 @@ public class BlueprintIo {
|
||||
CompoundTag blockStates = compoundTag.getCompound("BlockStates");
|
||||
blockStates = DFUHelper.updatePalettedContainer(blockStates, dataVersion);
|
||||
PalettedContainer<BlockState> container = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, blockStates)
|
||||
.getOrThrow(false, err -> {});
|
||||
.getOrThrow();
|
||||
map.put(BlockPos.asLong(cx, cy, cz), container);
|
||||
}
|
||||
}
|
||||
@ -205,8 +205,7 @@ public class BlueprintIo {
|
||||
tag.putInt("X", cx);
|
||||
tag.putInt("Y", cy);
|
||||
tag.putInt("Z", cz);
|
||||
Tag encoded = BlueprintIo.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, container)
|
||||
.getOrThrow(false, err -> {});
|
||||
Tag encoded = BlueprintIo.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, container).getOrThrow();
|
||||
tag.put("BlockStates", encoded);
|
||||
savedBlockRegions.add(tag);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
155
src/main/java/com/moulberry/axiom/commands/AxiomDebugCommand.java
Normale Datei
155
src/main/java/com/moulberry/axiom/commands/AxiomDebugCommand.java
Normale Datei
@ -0,0 +1,155 @@
|
||||
package com.moulberry.axiom.commands;
|
||||
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.Restrictions;
|
||||
import com.moulberry.axiom.integration.Integration;
|
||||
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import com.moulberry.axiom.integration.worldguard.WorldGuardIntegration;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.bukkit.BukkitCommandManager;
|
||||
import org.incendo.cloud.parser.standard.EnumParser;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.permission.PredicatePermission;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class AxiomDebugCommand {
|
||||
|
||||
/**
|
||||
* Command requires either the axiom.debug permission or for you to have the UUID d0e05de7-6067-454d-beae-c6d19d886191
|
||||
* The command isn't capable of modifying the world, only checking various properties for debugging purposes
|
||||
* It should be 100% safe to give to any player, but is locked behind some restrictions due to the potential
|
||||
* for lagging the server by spamming certain commands
|
||||
*/
|
||||
|
||||
private static final UUID MOULBERRY_UUID = UUID.fromString("d0e05de7-6067-454d-beae-c6d19d886191");
|
||||
|
||||
public static void register(AxiomPaper axiomPaper, BukkitCommandManager<CommandSender> manager) {
|
||||
manager.command(
|
||||
base(manager, "hasAxiomPermission").handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
boolean hasAxiomPermission = axiomPaper.hasAxiomPermission(player);
|
||||
context.sender().sendMessage(Component.text("hasAxiomPermission: " + hasAxiomPermission));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "canUseAxiom").handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
boolean canUseAxiom = axiomPaper.canUseAxiom(player);
|
||||
context.sender().sendMessage(Component.text("canUseAxiom: " + canUseAxiom));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "isMismatchedDataVersion").handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
boolean isMismatchedDataVersion = axiomPaper.isMismatchedDataVersion(player.getUniqueId());
|
||||
context.sender().sendMessage(Component.text("isMismatchedDataVersion: " + isMismatchedDataVersion));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "canModifyWorld").handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
boolean canModifyWorld = axiomPaper.canModifyWorld(player, player.getWorld());
|
||||
context.sender().sendMessage(Component.text("canModifyWorld: " + canModifyWorld));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "isClientListening").required("channel", StringParser.greedyStringParser()).handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
String channel = context.get("channel");
|
||||
boolean isClientListening = player.getListeningPluginChannels().contains(channel);
|
||||
context.sender().sendMessage(Component.text("listening to " + channel +": " + isClientListening));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "hasPermission").required("permission", StringParser.greedyStringParser()).handler(context -> {
|
||||
String permission = context.get("permission");
|
||||
boolean hasPermission = context.sender().hasPermission(permission);
|
||||
context.sender().sendMessage(Component.text("has permission " + permission +": " + hasPermission));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "getRestrictions").handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
Restrictions restrictions = axiomPaper.playerRestrictions.get(player.getUniqueId());
|
||||
if (restrictions == null) {
|
||||
context.sender().sendMessage(Component.text("no restrictions"));
|
||||
} else {
|
||||
context.sender().sendMessage(Component.text("restrictions: " + restrictions));
|
||||
}
|
||||
})
|
||||
);
|
||||
enum IntegrationType {
|
||||
PLOT_SQUARED,
|
||||
WORLD_GUARD
|
||||
}
|
||||
manager.command(
|
||||
base(manager, "canBreakBlockAtCurrentPosition").optional("type", EnumParser.enumParser(IntegrationType.class)).handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
IntegrationType integrationType = (IntegrationType) context.optional("type").orElse(null);
|
||||
|
||||
Block block = player.getWorld().getBlockAt(player.getLocation());
|
||||
|
||||
boolean canBreakBlock;
|
||||
if (integrationType == IntegrationType.PLOT_SQUARED) {
|
||||
canBreakBlock = PlotSquaredIntegration.canBreakBlock(player, block);
|
||||
} else if (integrationType == IntegrationType.WORLD_GUARD) {
|
||||
canBreakBlock = WorldGuardIntegration.canBreakBlock(player, block.getLocation());
|
||||
} else {
|
||||
canBreakBlock = Integration.canBreakBlock(player, block);
|
||||
}
|
||||
context.sender().sendMessage(Component.text("canBreakBlock: " + canBreakBlock));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "canPlaceBlockAtCurrentPosition").optional("type", EnumParser.enumParser(IntegrationType.class)).handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
IntegrationType integrationType = (IntegrationType) context.optional("type").orElse(null);
|
||||
|
||||
boolean canPlaceBlock;
|
||||
if (integrationType == IntegrationType.PLOT_SQUARED) {
|
||||
canPlaceBlock = PlotSquaredIntegration.canPlaceBlock(player, player.getLocation());
|
||||
} else if (integrationType == IntegrationType.WORLD_GUARD) {
|
||||
canPlaceBlock = WorldGuardIntegration.canPlaceBlock(player, player.getLocation());
|
||||
} else {
|
||||
canPlaceBlock = Integration.canPlaceBlock(player, player.getLocation());
|
||||
}
|
||||
context.sender().sendMessage(Component.text("canPlaceBlock: " + canPlaceBlock));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "isPlotWorld").handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
boolean isPlotWorld = PlotSquaredIntegration.isPlotWorld(player.getWorld());
|
||||
context.sender().sendMessage(Component.text("isPlotWorld: " + isPlotWorld));
|
||||
})
|
||||
);
|
||||
manager.command(
|
||||
base(manager, "getCurrentEditablePlot").handler(context -> {
|
||||
if (!(context.sender() instanceof Player player)) return;
|
||||
PlotSquaredIntegration.PlotBounds plotBounds = PlotSquaredIntegration.getCurrentEditablePlot(player);
|
||||
context.sender().sendMessage(Component.text("plotBounds: " + plotBounds));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private static Command.Builder<CommandSender> base(BukkitCommandManager<CommandSender> manager, String subcommand) {
|
||||
return manager.commandBuilder("axiompaperdebug")
|
||||
.literal(subcommand)
|
||||
.senderType(CommandSender.class)
|
||||
.permission(PredicatePermission.of(sender -> {
|
||||
if (sender instanceof Player player) {
|
||||
return player.hasPermission("axiom.debug") || player.getUniqueId().equals(MOULBERRY_UUID);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -13,10 +13,10 @@ public interface SectionPermissionChecker {
|
||||
if (first.noneAllowed() || second.noneAllowed()) {
|
||||
return NONE_ALLOWED;
|
||||
}
|
||||
if (first.allAllowed()) {
|
||||
if (first == ALL_ALLOWED) {
|
||||
return second;
|
||||
}
|
||||
if (second.allAllowed()) {
|
||||
if (second == ALL_ALLOWED) {
|
||||
return first;
|
||||
}
|
||||
|
||||
@ -25,6 +25,10 @@ public interface SectionPermissionChecker {
|
||||
return NONE_ALLOWED;
|
||||
}
|
||||
|
||||
if (first.allAllowed() && second.allAllowed()) {
|
||||
return new AllAllowedInBox(intersect);
|
||||
}
|
||||
|
||||
return new SectionPermissionChecker() {
|
||||
@Override
|
||||
public boolean allAllowed() {
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.moulberry.axiom.integration.coreprotect;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
|
||||
public class CoreProtectIntegration {
|
||||
public static boolean isEnabled() {
|
||||
return CoreProtectIntegrationImpl.isEnabled();
|
||||
}
|
||||
|
||||
public static void logPlacement(String name, BlockState blockState, CraftWorld world, BlockPos pos) {
|
||||
if (!CoreProtectIntegrationImpl.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoreProtectIntegrationImpl.logPlacement(name, blockState, world, pos);
|
||||
}
|
||||
|
||||
public static void logRemoval(String name, BlockState blockState, CraftWorld world, BlockPos pos) {
|
||||
if (!CoreProtectIntegrationImpl.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoreProtectIntegrationImpl.logRemoval(name, blockState, world, pos);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.moulberry.axiom.integration.coreprotect;
|
||||
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import net.coreprotect.CoreProtect;
|
||||
import net.coreprotect.CoreProtectAPI;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.block.CraftBlockState;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class CoreProtectIntegrationImpl {
|
||||
private static final CoreProtectAPI COREPROTECT_API;
|
||||
private static final boolean COREPROTECT_ENABLED;
|
||||
private static final Constructor<CraftBlockState> CRAFT_BLOCK_STATE_CONSTRUCTOR;
|
||||
|
||||
static {
|
||||
COREPROTECT_API = getCoreProtect();
|
||||
Constructor<CraftBlockState> constructor = null;
|
||||
|
||||
if (COREPROTECT_API != null) {
|
||||
try {
|
||||
constructor = CraftBlockState.class.getDeclaredConstructor(World.class, BlockPos.class, BlockState.class);
|
||||
constructor.setAccessible(true);
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
AxiomPaper.PLUGIN.getLogger().warning("Failed to get CraftBlockState constructor for CoreProtect: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
CRAFT_BLOCK_STATE_CONSTRUCTOR = constructor;
|
||||
COREPROTECT_ENABLED = COREPROTECT_API != null && CRAFT_BLOCK_STATE_CONSTRUCTOR != null;
|
||||
}
|
||||
|
||||
private static CoreProtectAPI getCoreProtect() {
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin("CoreProtect");
|
||||
|
||||
if (!(plugin instanceof CoreProtect)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CoreProtectAPI coreProtect = ((CoreProtect) plugin).getAPI();
|
||||
if (!coreProtect.isEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (coreProtect.APIVersion() < 10) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return coreProtect;
|
||||
}
|
||||
|
||||
private static CraftBlockState createCraftBlockState(World world, BlockPos pos, BlockState blockState) {
|
||||
try {
|
||||
return CRAFT_BLOCK_STATE_CONSTRUCTOR.newInstance(world, pos, blockState);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
AxiomPaper.PLUGIN.getLogger().warning("Failed to create CraftBlockState for CoreProtect: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isEnabled() {
|
||||
return COREPROTECT_ENABLED;
|
||||
}
|
||||
|
||||
static void logPlacement(String name, BlockState blockState, CraftWorld world, BlockPos pos) {
|
||||
if (blockState.isAir()) {
|
||||
return;
|
||||
}
|
||||
|
||||
COREPROTECT_API.logPlacement(name, createCraftBlockState(world, pos, blockState));
|
||||
}
|
||||
|
||||
static void logRemoval(String name, BlockState blockState, CraftWorld world, BlockPos pos) {
|
||||
COREPROTECT_API.logRemoval(name, createCraftBlockState(world, pos, blockState));
|
||||
}
|
||||
|
||||
}
|
@ -10,26 +10,13 @@ import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public class PlotSquaredIntegration {
|
||||
|
||||
public record PlotBounds(BlockPos min, BlockPos max, String worldName) {
|
||||
public PlotBounds(CuboidRegion cuboidRegion, String worldName) {
|
||||
this(
|
||||
new BlockPos(
|
||||
cuboidRegion.getMinimumPoint().getBlockX(),
|
||||
cuboidRegion.getMinimumPoint().getBlockY(),
|
||||
cuboidRegion.getMinimumPoint().getBlockZ()
|
||||
),
|
||||
new BlockPos(
|
||||
cuboidRegion.getMaximumPoint().getBlockX(),
|
||||
cuboidRegion.getMaximumPoint().getBlockY(),
|
||||
cuboidRegion.getMaximumPoint().getBlockZ()
|
||||
),
|
||||
worldName
|
||||
);
|
||||
}
|
||||
}
|
||||
public record PlotBox(BlockPos min, BlockPos max) {}
|
||||
public record PlotBounds(Set<PlotBox> boxes, String worldName) {}
|
||||
|
||||
public static boolean canBreakBlock(Player player, Block block) {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled("PlotSquared")) {
|
||||
|
@ -18,6 +18,7 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -147,16 +148,12 @@ public class PlotSquaredIntegrationImpl {
|
||||
BukkitPlayer pp = BukkitUtil.adapt(player);
|
||||
Plot plot = area.getPlot(location);
|
||||
if (plot != null) {
|
||||
Location bottom = plot.getExtendedBottomAbs();
|
||||
Location top = plot.getExtendedTopAbs();
|
||||
CuboidRegion cuboidRegion = new CuboidRegion(bottom.getBlockVector3(), top.getBlockVector3());
|
||||
|
||||
// check unowned plots
|
||||
if (!plot.hasOwner()) {
|
||||
if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_UNOWNED, false)) {
|
||||
return null;
|
||||
} else {
|
||||
return new PlotSquaredIntegration.PlotBounds(cuboidRegion, player.getWorld().getName());
|
||||
return createBounds(plot, player.getWorld().getName());
|
||||
}
|
||||
}
|
||||
// player is breaking another player's plot
|
||||
@ -164,7 +161,7 @@ public class PlotSquaredIntegrationImpl {
|
||||
if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER, false)) {
|
||||
return null;
|
||||
} else {
|
||||
return new PlotSquaredIntegration.PlotBounds(cuboidRegion, player.getWorld().getName());
|
||||
return createBounds(plot, player.getWorld().getName());
|
||||
}
|
||||
}
|
||||
// plot is 'done'
|
||||
@ -172,15 +169,35 @@ public class PlotSquaredIntegrationImpl {
|
||||
if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER, false)) {
|
||||
return null;
|
||||
} else {
|
||||
return new PlotSquaredIntegration.PlotBounds(cuboidRegion, player.getWorld().getName());
|
||||
return createBounds(plot, player.getWorld().getName());
|
||||
}
|
||||
}
|
||||
return new PlotSquaredIntegration.PlotBounds(cuboidRegion, player.getWorld().getName());
|
||||
return createBounds(plot, player.getWorld().getName());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PlotSquaredIntegration.PlotBounds createBounds(Plot plot, String worldName) {
|
||||
Set<PlotSquaredIntegration.PlotBox> boxes = new HashSet<>();
|
||||
|
||||
for (CuboidRegion region : plot.getRegions()) {
|
||||
BlockPos min = new BlockPos(
|
||||
region.getMinimumPoint().getBlockX(),
|
||||
region.getMinimumPoint().getBlockY(),
|
||||
region.getMinimumPoint().getBlockZ()
|
||||
);
|
||||
BlockPos max = new BlockPos(
|
||||
region.getMaximumPoint().getBlockX(),
|
||||
region.getMaximumPoint().getBlockY(),
|
||||
region.getMaximumPoint().getBlockZ()
|
||||
);
|
||||
boxes.add(new PlotSquaredIntegration.PlotBox(min, max));
|
||||
}
|
||||
|
||||
return new PlotSquaredIntegration.PlotBounds(boxes, worldName);
|
||||
}
|
||||
|
||||
static SectionPermissionChecker checkSection(Player player, World world, int sectionX, int sectionY, int sectionZ) {
|
||||
int minX = sectionX * 16;
|
||||
int minY = sectionY * 16;
|
||||
@ -220,12 +237,12 @@ public class PlotSquaredIntegrationImpl {
|
||||
BlockVector3 minPoint = region.getMinimumPoint();
|
||||
BlockVector3 maxPoint = region.getMaximumPoint();
|
||||
|
||||
int minPlotX = Math.max(minPoint.getX(), minX);
|
||||
int minPlotY = Math.max(minPoint.getY(), minY);
|
||||
int minPlotZ = Math.max(minPoint.getZ(), minZ);
|
||||
int maxPlotX = Math.min(maxPoint.getX(), maxX);
|
||||
int maxPlotY = Math.min(maxPoint.getY(), maxY);
|
||||
int maxPlotZ = Math.min(maxPoint.getZ(), maxZ);
|
||||
int minPlotX = Math.max(minPoint.x(), minX);
|
||||
int minPlotY = Math.max(minPoint.y(), minY);
|
||||
int minPlotZ = Math.max(minPoint.z(), minZ);
|
||||
int maxPlotX = Math.min(maxPoint.x(), maxX);
|
||||
int maxPlotY = Math.min(maxPoint.y(), maxY);
|
||||
int maxPlotZ = Math.min(maxPoint.z(), maxZ);
|
||||
|
||||
if (minPlotX > maxPlotX) continue;
|
||||
if (minPlotY > maxPlotY) continue;
|
||||
|
@ -34,15 +34,19 @@ public class WorldGuardIntegrationImpl {
|
||||
private static boolean testBuild(Player player, org.bukkit.Location loc, StateFlag flag) {
|
||||
WorldGuardPlatform platform = WorldGuard.getInstance().getPlatform();
|
||||
|
||||
com.sk89q.worldedit.world.World worldEditWorld = BukkitAdapter.adapt(loc.getWorld());
|
||||
LocalPlayer worldGuardPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
|
||||
|
||||
if (platform.getSessionManager().hasBypass(worldGuardPlayer, worldEditWorld)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RegionContainer regionContainer = platform.getRegionContainer();
|
||||
if (regionContainer == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RegionQuery query = regionContainer.createQuery();
|
||||
|
||||
LocalPlayer worldGuardPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
|
||||
|
||||
return query.testBuild(BukkitAdapter.adapt(loc), worldGuardPlayer, flag);
|
||||
}
|
||||
|
||||
@ -118,12 +122,12 @@ public class WorldGuardIntegrationImpl {
|
||||
BlockVector3 regionMin = region.getMinimumPoint();
|
||||
BlockVector3 regionMax = region.getMaximumPoint();
|
||||
|
||||
int regionMinX = Math.max(regionMin.getBlockX(), cx*16) - minX;
|
||||
int regionMinY = Math.max(regionMin.getBlockY(), cy*16) - minY;
|
||||
int regionMinZ = Math.max(regionMin.getBlockZ(), cz*16) - minZ;
|
||||
int regionMaxX = Math.min(regionMax.getBlockX(), cx*16+15) - minX;
|
||||
int regionMaxY = Math.min(regionMax.getBlockY(), cy*16+15) - minY;
|
||||
int regionMaxZ = Math.min(regionMax.getBlockZ(), cz*16+15) - minZ;
|
||||
int regionMinX = Math.max(regionMin.x(), cx*16) - minX;
|
||||
int regionMinY = Math.max(regionMin.y(), cy*16) - minY;
|
||||
int regionMinZ = Math.max(regionMin.z(), cz*16) - minZ;
|
||||
int regionMaxX = Math.min(regionMax.x(), cx*16+15) - minX;
|
||||
int regionMaxY = Math.min(regionMax.y(), cy*16+15) - minY;
|
||||
int regionMaxZ = Math.min(regionMax.z(), cz*16+15) - minZ;
|
||||
|
||||
Box box = new Box(regionMinX, regionMinY, regionMinZ, regionMaxX, regionMaxY, regionMaxZ);
|
||||
if (value == StateFlag.State.DENY) {
|
||||
|
@ -11,6 +11,7 @@ import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class AxiomBigPayloadHandler extends ByteToMessageDecoder {
|
||||
@ -35,14 +36,31 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder {
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
// Don't process if channel isn't active
|
||||
if (!ctx.channel().isActive()) {
|
||||
in.skipBytes(in.readableBytes());
|
||||
// No bytes to read?! Go away
|
||||
if (in.readableBytes() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't handle if player doesn't have permission to use Axiom
|
||||
ServerPlayer player = connection.getPlayer();
|
||||
if (player == null || !AxiomPaper.PLUGIN.canUseAxiom(player.getBukkitEntity())) {
|
||||
ctx.fireChannelRead(in.retain());
|
||||
|
||||
// Skip remaining bytes
|
||||
if (in.readableBytes() > 0) {
|
||||
in.skipBytes(in.readableBytes());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't process if channel isn't active
|
||||
if (!ctx.channel().isActive()) {
|
||||
if (in.readableBytes() > 0) {
|
||||
in.skipBytes(in.readableBytes());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int i = in.readableBytes();
|
||||
if (i != 0) {
|
||||
int readerIndex = in.readerIndex();
|
||||
boolean success = false;
|
||||
try {
|
||||
@ -52,44 +70,58 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder {
|
||||
if (packetId == payloadId) {
|
||||
ResourceLocation identifier = buf.readResourceLocation();
|
||||
if (identifier.equals(SET_BUFFER)) {
|
||||
ServerPlayer player = connection.getPlayer();
|
||||
if (AxiomPaper.PLUGIN.canUseAxiom(player.getBukkitEntity())) {
|
||||
setBlockBuffer.onReceive(player, buf);
|
||||
success = true;
|
||||
in.skipBytes(in.readableBytes());
|
||||
return;
|
||||
if (in.readableBytes() > 0) {
|
||||
throw new IOException("Axiom packet " + identifier + " was larger than I expected, found " + in.readableBytes() +
|
||||
" bytes extra whilst reading packet");
|
||||
}
|
||||
return;
|
||||
} 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;
|
||||
if (in.readableBytes() > 0) {
|
||||
throw new IOException("Axiom packet " + identifier + " was larger than I expected, found " + in.readableBytes() +
|
||||
" bytes extra whilst reading packet");
|
||||
}
|
||||
return;
|
||||
} else if (requestChunkDataPacketListener != null && identifier.equals(REQUEST_CHUNK_DATA)) {
|
||||
ServerPlayer player = connection.getPlayer();
|
||||
if (AxiomPaper.PLUGIN.canUseAxiom(player.getBukkitEntity())) {
|
||||
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;
|
||||
if (in.readableBytes() > 0) {
|
||||
in.skipBytes(in.readableBytes());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (!(t instanceof IndexOutOfBoundsException)) {
|
||||
// Skip remaining bytes
|
||||
success = true;
|
||||
if (in.readableBytes() > 0) {
|
||||
in.skipBytes(in.readableBytes());
|
||||
}
|
||||
|
||||
// Throw error, will disconnect client
|
||||
throw t;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
} finally {
|
||||
if (!success) {
|
||||
in.readerIndex(readerIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fireChannelRead(in.retain());
|
||||
|
||||
|
@ -6,9 +6,11 @@ import com.moulberry.axiom.blueprint.RawBlueprint;
|
||||
import com.moulberry.axiom.blueprint.ServerBlueprintManager;
|
||||
import com.moulberry.axiom.blueprint.ServerBlueprintRegistry;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -26,7 +28,20 @@ public class BlueprintRequestPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.blueprint.request")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.plugin.isMismatchedDataVersion(player.getUniqueId())) {
|
||||
player.sendMessage(Component.text("Axiom+ViaVersion: This feature isn't supported. Switch your client version to " + SharedConstants.VERSION_STRING + " to use this"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
package com.moulberry.axiom.packet;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record CustomByteArrayPayload(ResourceLocation id, byte[] bytes) implements CustomPacketPayload {
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeBytes(bytes);
|
||||
}
|
||||
}
|
@ -4,11 +4,12 @@ import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.integration.Integration;
|
||||
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -26,11 +27,15 @@ public class DeleteEntityPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
return;
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!player.hasPermission("axiom.entity.*") && !player.hasPermission("axiom.entity.delete")) {
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.entity.delete", true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -40,7 +45,7 @@ public class DeleteEntityPacketListener implements PluginMessageListener {
|
||||
|
||||
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
|
||||
List<UUID> delete = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000),
|
||||
FriendlyByteBuf::readUUID);
|
||||
buf -> buf.readUUID());
|
||||
|
||||
ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle();
|
||||
|
||||
|
@ -1,25 +1,29 @@
|
||||
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.protocol.version.ProtocolVersion;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
@ -28,7 +32,6 @@ import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class HelloPacketListener implements PluginMessageListener {
|
||||
@ -41,6 +44,14 @@ public class HelloPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.hasAxiomPermission(player)) {
|
||||
return;
|
||||
}
|
||||
@ -48,22 +59,71 @@ public class HelloPacketListener implements PluginMessageListener {
|
||||
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
|
||||
int apiVersion = friendlyByteBuf.readVarInt();
|
||||
int dataVersion = friendlyByteBuf.readVarInt();
|
||||
friendlyByteBuf.readNbt(); // Discard
|
||||
// note - skipping NBT here. friendlyByteBuf.readNBT();
|
||||
|
||||
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?");
|
||||
|
||||
String incompatibleDataVersion = plugin.configuration.getString("incompatible-data-version");
|
||||
if (incompatibleDataVersion == null) incompatibleDataVersion = "kick";
|
||||
if (incompatibleDataVersion == null) incompatibleDataVersion = "warn";
|
||||
|
||||
Component incompatibleWarning = Component.text("Axiom: Incompatible data version detected (client " + dataVersion +
|
||||
", server " + serverDataVersion + ")");
|
||||
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
|
||||
if (incompatibleDataVersion.equals("warn")) {
|
||||
player.sendMessage(text.color(NamedTextColor.RED));
|
||||
player.sendMessage(incompatibleWarning.color(NamedTextColor.RED));
|
||||
return;
|
||||
} else if (!incompatibleDataVersion.equals("ignore")) {
|
||||
player.kick(text);
|
||||
player.kick(incompatibleWarning);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
int playerVersion = Via.getAPI().getPlayerVersion(player.getUniqueId());
|
||||
if (playerVersion == SharedConstants.getProtocolVersion()) {
|
||||
// Likely using via on the proxy, try to get protocol version from data version
|
||||
if (dataVersion < 3337) {
|
||||
player.sendMessage(incompatibleWarning.color(NamedTextColor.RED));
|
||||
return;
|
||||
} else if (dataVersion == 3337) {
|
||||
playerVersion = 762; // 1.19.4
|
||||
} else if (dataVersion <= 3465) {
|
||||
playerVersion = 763; // 1.20.1
|
||||
} else if (dataVersion <= 3578) {
|
||||
playerVersion = 764; // 1.20.2
|
||||
} else if (dataVersion <= 3700) {
|
||||
playerVersion = 765; // 1.20.3 / 1.20.4
|
||||
} else if (dataVersion <= 3837) {
|
||||
playerVersion = 766; // 1.20.3 / 1.20.4
|
||||
} else {
|
||||
player.sendMessage(incompatibleWarning.color(NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
IdMapper<BlockState> mapper;
|
||||
try {
|
||||
mapper = ViaVersionHelper.getBlockRegistryForVersion(this.plugin.allowedBlockRegistry, playerVersion);
|
||||
} catch (Exception e) {
|
||||
String clientDescription = "client: " + ProtocolVersion.getProtocol(playerVersion);
|
||||
String serverDescription = "server: " + ProtocolVersion.getProtocol(SharedConstants.getProtocolVersion());
|
||||
String description = clientDescription + " <-> " + serverDescription;
|
||||
Component text = Component.text("Axiom+ViaVersion: " + e.getMessage() + " (" + description + ")");
|
||||
|
||||
if (incompatibleDataVersion.equals("warn")) {
|
||||
player.sendMessage(text.color(NamedTextColor.RED));
|
||||
} else {
|
||||
player.kick(text);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.playerBlockRegistry.put(player.getUniqueId(), mapper);
|
||||
this.plugin.playerProtocolVersion.put(player.getUniqueId(), playerVersion);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if (apiVersion != AxiomConstants.API_VERSION) {
|
||||
@ -85,7 +145,7 @@ public class HelloPacketListener implements PluginMessageListener {
|
||||
Component text = Component.text("This server requires the use of Axiom 2.3 or later. Contact the server administrator if you believe this is unintentional");
|
||||
|
||||
String unsupportedRestrictions = plugin.configuration.getString("client-doesnt-support-restrictions");
|
||||
if (unsupportedRestrictions == null) unsupportedRestrictions = "kick";
|
||||
if (unsupportedRestrictions == null) unsupportedRestrictions = "warn";
|
||||
if (unsupportedRestrictions.equals("warn")) {
|
||||
player.sendMessage(text.color(NamedTextColor.RED));
|
||||
return;
|
||||
@ -110,7 +170,7 @@ public class HelloPacketListener implements PluginMessageListener {
|
||||
}
|
||||
|
||||
// Enable
|
||||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), MinecraftServer.getServer().registryAccess());
|
||||
buf.writeBoolean(true);
|
||||
buf.writeInt(handshakeEvent.getMaxBufferSize()); // Max Buffer Size
|
||||
buf.writeBoolean(false); // No source info
|
||||
@ -125,18 +185,19 @@ public class HelloPacketListener implements PluginMessageListener {
|
||||
|
||||
// Initialize Hotbars
|
||||
PersistentDataContainer container = player.getPersistentDataContainer();
|
||||
if (!this.plugin.isMismatchedDataVersion(player.getUniqueId())) {
|
||||
int activeHotbarIndex = container.getOrDefault(AxiomConstants.ACTIVE_HOTBAR_INDEX, PersistentDataType.BYTE, (byte) 0);
|
||||
PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER);
|
||||
if (hotbarItems != null) {
|
||||
buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), MinecraftServer.getServer().registryAccess());
|
||||
buf.writeByte((byte) activeHotbarIndex);
|
||||
for (int i=0; i<9*9; i++) {
|
||||
// Ignore selected hotbar
|
||||
if (i / 9 == activeHotbarIndex) {
|
||||
buf.writeItem(net.minecraft.world.item.ItemStack.EMPTY);
|
||||
net.minecraft.world.item.ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, net.minecraft.world.item.ItemStack.EMPTY);
|
||||
} else {
|
||||
ItemStack stack = hotbarItems.get(new NamespacedKey("axiom", "slot_"+i), ItemStackDataType.INSTANCE);
|
||||
buf.writeItem(CraftItemStack.asNMSCopy(stack));
|
||||
net.minecraft.world.item.ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, CraftItemStack.asNMSCopy(stack));
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,11 +205,12 @@ public class HelloPacketListener implements PluginMessageListener {
|
||||
buf.getBytes(0, bytes);
|
||||
player.sendPluginMessage(this.plugin, "axiom:initialize_hotbars", bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize Views
|
||||
UUID activeView = container.get(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE);
|
||||
if (activeView != null) {
|
||||
buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), MinecraftServer.getServer().registryAccess());
|
||||
buf.writeUUID(activeView);
|
||||
|
||||
PersistentDataContainer[] views = container.get(AxiomConstants.VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY);
|
||||
|
@ -4,7 +4,11 @@ import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.NbtSanitization;
|
||||
import com.moulberry.axiom.event.AxiomManipulateEntityEvent;
|
||||
import com.moulberry.axiom.integration.Integration;
|
||||
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import com.moulberry.axiom.viaversion.UnknownVersionHelper;
|
||||
import com.moulberry.axiom.viaversion.ViaVersionHelper;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@ -19,7 +23,7 @@ import net.minecraft.world.entity.decoration.ItemFrame;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -46,7 +50,7 @@ public class ManipulateEntityPacketListener implements PluginMessageListener {
|
||||
|
||||
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) {
|
||||
public static ManipulateEntry read(FriendlyByteBuf friendlyByteBuf, Player player) {
|
||||
UUID uuid = friendlyByteBuf.readUUID();
|
||||
|
||||
int flags = friendlyByteBuf.readByte();
|
||||
@ -61,13 +65,13 @@ public class ManipulateEntityPacketListener implements PluginMessageListener {
|
||||
pitch = friendlyByteBuf.readFloat();
|
||||
}
|
||||
|
||||
CompoundTag nbt = friendlyByteBuf.readNbt();
|
||||
CompoundTag nbt = UnknownVersionHelper.readTagUnknown(friendlyByteBuf, player);
|
||||
|
||||
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);
|
||||
buffer -> buffer.readUUID());
|
||||
}
|
||||
|
||||
return new ManipulateEntry(uuid, relativeMovementSet, position, yaw, pitch, nbt,
|
||||
@ -79,11 +83,15 @@ public class ManipulateEntityPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
return;
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!player.hasPermission("axiom.entity.*") && !player.hasPermission("axiom.entity.manipulate")) {
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.entity.manipulate", true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -93,7 +101,7 @@ public class ManipulateEntityPacketListener implements PluginMessageListener {
|
||||
|
||||
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
|
||||
List<ManipulateEntry> entries = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000),
|
||||
ManipulateEntry::read);
|
||||
buf -> ManipulateEntry.read(buf, player));
|
||||
|
||||
ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle();
|
||||
|
||||
|
@ -3,12 +3,13 @@ package com.moulberry.axiom.packet;
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.marker.MarkerData;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Marker;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -24,11 +25,15 @@ public class MarkerNbtRequestPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
return;
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!player.hasPermission("axiom.entity.*") && !player.hasPermission("axiom.entity.manipulate")) {
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.entity.manipulate", true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,10 @@ 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.kyori.adventure.text.Component;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@ -20,11 +22,11 @@ import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -41,12 +43,20 @@ public class RequestChunkDataPacketListener implements PluginMessageListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player bukkitPlayer, @NotNull byte[] message) {
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player bukkitPlayer, byte[] message) {
|
||||
ServerPlayer player = ((CraftPlayer)bukkitPlayer).getHandle();
|
||||
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
|
||||
long id = friendlyByteBuf.readLong();
|
||||
|
||||
if (!this.plugin.canUseAxiom(bukkitPlayer)) {
|
||||
if (!this.plugin.canUseAxiom(bukkitPlayer, "axiom.chunk.request") || this.plugin.isMismatchedDataVersion(bukkitPlayer.getUniqueId())) {
|
||||
// We always send an 'empty' response in order to make the client happy
|
||||
sendEmptyResponse(player, id);
|
||||
return;
|
||||
@ -105,7 +115,7 @@ public class RequestChunkDataPacketListener implements PluginMessageListener {
|
||||
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(mutableBlockPos, LevelChunk.EntityCreationType.IMMEDIATE);
|
||||
if (blockEntity != null) {
|
||||
CompoundTag tag = blockEntity.saveWithoutMetadata();
|
||||
CompoundTag tag = blockEntity.saveWithoutMetadata(player.registryAccess());
|
||||
blockEntityMap.put(pos, CompressedBlockEntity.compress(tag, baos));
|
||||
}
|
||||
}
|
||||
@ -144,7 +154,7 @@ public class RequestChunkDataPacketListener implements PluginMessageListener {
|
||||
mutableBlockPos.set(sx*16 + x, sy*16 + y, sz*16 + z);
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(mutableBlockPos, LevelChunk.EntityCreationType.CHECK);
|
||||
if (blockEntity != null) {
|
||||
CompoundTag tag = blockEntity.saveWithoutMetadata();
|
||||
CompoundTag tag = blockEntity.saveWithoutMetadata(player.registryAccess());
|
||||
blockEntityMap.put(mutableBlockPos.asLong(), CompressedBlockEntity.compress(tag, baos));
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ import com.moulberry.axiom.buffer.BlockBuffer;
|
||||
import com.moulberry.axiom.buffer.CompressedBlockEntity;
|
||||
import com.moulberry.axiom.integration.Integration;
|
||||
import com.moulberry.axiom.integration.SectionPermissionChecker;
|
||||
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import com.moulberry.axiom.integration.coreprotect.CoreProtectIntegration;
|
||||
import com.moulberry.axiom.viaversion.UnknownVersionHelper;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
|
||||
import net.minecraft.ChatFormatting;
|
||||
@ -33,7 +34,7 @@ import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
@ -77,7 +78,7 @@ public class SetBlockBufferPacketListener {
|
||||
boolean continuation = friendlyByteBuf.readBoolean();
|
||||
|
||||
if (!continuation) {
|
||||
friendlyByteBuf.readNbt(); // Discard sourceInfo
|
||||
UnknownVersionHelper.skipTagUnknown(friendlyByteBuf, player.getBukkitEntity());
|
||||
}
|
||||
|
||||
RateLimiter rateLimiter = this.plugin.getBlockBufferRateLimiter(player.getUUID());
|
||||
@ -85,7 +86,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));
|
||||
@ -124,7 +125,7 @@ public class SetBlockBufferPacketListener {
|
||||
ServerLevel world = player.serverLevel();
|
||||
if (!world.dimension().equals(worldKey)) return;
|
||||
|
||||
if (!this.plugin.canUseAxiom(player.getBukkitEntity())) {
|
||||
if (!this.plugin.canUseAxiom(player.getBukkitEntity(), "axiom.build.section")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -281,12 +282,20 @@ public class SetBlockBufferPacketListener {
|
||||
int key = x | (y << 4) | (z << 8);
|
||||
CompressedBlockEntity savedBlockEntity = blockEntityChunkMap.get((short) key);
|
||||
if (savedBlockEntity != null) {
|
||||
blockEntity.load(savedBlockEntity.decompress());
|
||||
blockEntity.loadWithComponents(savedBlockEntity.decompress(), player.registryAccess());
|
||||
}
|
||||
}
|
||||
} else if (old.hasBlockEntity()) {
|
||||
chunk.removeBlockEntity(blockPos);
|
||||
}
|
||||
|
||||
if (CoreProtectIntegration.isEnabled() && old != blockState) {
|
||||
String changedBy = player.getBukkitEntity().getName();
|
||||
BlockPos changedPos = new BlockPos(bx, by, bz);
|
||||
|
||||
CoreProtectIntegration.logRemoval(changedBy, old, world.getWorld(), changedPos);
|
||||
CoreProtectIntegration.logPlacement(changedBy, blockState, world.getWorld(), changedPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -316,7 +325,7 @@ public class SetBlockBufferPacketListener {
|
||||
ServerLevel world = player.serverLevel();
|
||||
if (!world.dimension().equals(worldKey)) return;
|
||||
|
||||
if (!this.plugin.canUseAxiom(player.getBukkitEntity())) {
|
||||
if (!this.plugin.canUseAxiom(player.getBukkitEntity(), "axiom.build.section")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -340,16 +349,16 @@ public class SetBlockBufferPacketListener {
|
||||
return;
|
||||
}
|
||||
|
||||
var holder = registry.getHolder(biome);
|
||||
if (holder.isPresent()) {
|
||||
var chunk = (LevelChunk) world.getChunk(x >> 2, z >> 2, ChunkStatus.FULL, false);
|
||||
if (chunk == null) return;
|
||||
|
||||
var section = chunk.getSection(cy - minSection);
|
||||
PalettedContainer<Holder<Biome>> container = (PalettedContainer<Holder<Biome>>) section.getBiomes();
|
||||
|
||||
var holder = registry.getHolder(biome);
|
||||
if (holder.isPresent()) {
|
||||
if (!Integration.canPlaceBlock(player.getBukkitEntity(),
|
||||
new Location(player.getBukkitEntity().getWorld(), x+1, y+1, z+1))) return;
|
||||
new Location(player.getBukkitEntity().getWorld(), (x<<2)+1, (y<<2)+1, (z<<2)+1))) return;
|
||||
|
||||
container.set(x & 3, y & 3, z & 3, holder.get());
|
||||
changedChunks.add(chunk);
|
||||
|
@ -3,10 +3,13 @@ package com.moulberry.axiom.packet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.integration.Integration;
|
||||
import com.moulberry.axiom.integration.coreprotect.CoreProtectIntegration;
|
||||
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
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;
|
||||
@ -29,9 +32,9 @@ import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlock;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
@ -67,8 +70,16 @@ public class SetBlockPacketListener implements PluginMessageListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player bukkitPlayer, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(bukkitPlayer)) {
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player bukkitPlayer, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(bukkitPlayer, "axiom.build.place")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -79,8 +90,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));
|
||||
buf -> buf.readBlockPos(), buf -> buf.readById(registry::byIdOrThrow));
|
||||
boolean updateNeighbors = friendlyByteBuf.readBoolean();
|
||||
|
||||
int reason = friendlyByteBuf.readVarInt();
|
||||
@ -129,6 +141,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;
|
||||
@ -143,8 +159,14 @@ public class SetBlockPacketListener implements PluginMessageListener {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CoreProtectIntegration.isEnabled()) {
|
||||
BlockState old = player.level().getBlockState(blockPos);
|
||||
CoreProtectIntegration.logRemoval(bukkitPlayer.getName(), old, world, blockPos);
|
||||
}
|
||||
|
||||
// Place block
|
||||
player.level().setBlock(blockPos, blockState, 3);
|
||||
CoreProtectIntegration.logPlacement(bukkitPlayer.getName(), blockState, world, blockPos);
|
||||
}
|
||||
} else {
|
||||
int count = 0;
|
||||
@ -154,6 +176,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;
|
||||
@ -258,6 +284,14 @@ public class SetBlockPacketListener implements PluginMessageListener {
|
||||
if (oldPoi.isPresent()) level.getPoiManager().remove(blockPos);
|
||||
if (newPoi.isPresent()) level.getPoiManager().add(blockPos, newPoi.get());
|
||||
}
|
||||
|
||||
if (CoreProtectIntegration.isEnabled()) {
|
||||
String changedBy = player.getBukkitEntity().getName();
|
||||
BlockPos changedPos = new BlockPos(bx, by, bz);
|
||||
|
||||
CoreProtectIntegration.logRemoval(changedBy, old, world, changedPos);
|
||||
CoreProtectIntegration.logPlacement(changedBy, blockState, world, changedPos);
|
||||
}
|
||||
}
|
||||
|
||||
boolean nowHasOnlyAir = section.hasOnlyAir();
|
||||
|
@ -6,6 +6,7 @@ import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.View;
|
||||
import com.moulberry.axiom.persistence.UUIDDataType;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
@ -26,7 +27,15 @@ public class SetEditorViewsPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.editor.views")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,10 @@ package com.moulberry.axiom.packet;
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.event.AxiomFlySpeedChangeEvent;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -19,7 +20,15 @@ public class SetFlySpeedPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.player.speed")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,12 @@ package com.moulberry.axiom.packet;
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.event.AxiomGameModeChangeEvent;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -21,7 +22,15 @@ public class SetGamemodePacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.player.gamemode")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,15 @@ package com.moulberry.axiom.packet;
|
||||
import com.moulberry.axiom.AxiomConstants;
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.persistence.ItemStackDataType;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
@ -22,14 +27,22 @@ public class SetHotbarSlotPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.player.hotbar") || this.plugin.isMismatchedDataVersion(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
|
||||
RegistryFriendlyByteBuf friendlyByteBuf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(message), ((CraftPlayer)player).getHandle().registryAccess());
|
||||
int index = friendlyByteBuf.readByte();
|
||||
if (index < 0 || index >= 9*9) return;
|
||||
net.minecraft.world.item.ItemStack nmsStack = friendlyByteBuf.readItem();
|
||||
net.minecraft.world.item.ItemStack nmsStack = ItemStack.OPTIONAL_STREAM_CODEC.decode(friendlyByteBuf);
|
||||
|
||||
PersistentDataContainer container = player.getPersistentDataContainer();
|
||||
PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER);
|
||||
|
@ -4,6 +4,7 @@ import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.event.AxiomTimeChangeEvent;
|
||||
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
@ -11,7 +12,7 @@ import net.minecraft.server.level.ServerLevel;
|
||||
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.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -25,7 +26,15 @@ public class SetTimePacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.world.time")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
|
||||
import com.moulberry.axiom.world_properties.server.ServerWorldPropertyHolder;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -20,7 +21,15 @@ public class SetWorldPropertyListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.world.property")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,10 @@ import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.NbtSanitization;
|
||||
import com.moulberry.axiom.integration.Integration;
|
||||
import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration;
|
||||
import com.moulberry.axiom.viaversion.UnknownVersionHelper;
|
||||
import com.moulberry.axiom.viaversion.ViaVersionHelper;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@ -18,7 +21,7 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -38,22 +41,21 @@ public class SpawnEntityPacketListener implements PluginMessageListener {
|
||||
|
||||
private record SpawnEntry(UUID newUuid, double x, double y, double z, float yaw, float pitch,
|
||||
@Nullable UUID copyFrom, CompoundTag tag) {
|
||||
public SpawnEntry(FriendlyByteBuf friendlyByteBuf) {
|
||||
this(friendlyByteBuf.readUUID(), friendlyByteBuf.readDouble(), friendlyByteBuf.readDouble(),
|
||||
friendlyByteBuf.readDouble(), friendlyByteBuf.readFloat(), friendlyByteBuf.readFloat(),
|
||||
friendlyByteBuf.readNullable(FriendlyByteBuf::readUUID), friendlyByteBuf.readNbt());
|
||||
}
|
||||
}
|
||||
|
||||
private static final Rotation[] ROTATION_VALUES = Rotation.values();
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
return;
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!player.hasPermission("axiom.entity.*") && !player.hasPermission("axiom.entity.spawn")) {
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.entity.spawn", true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -62,7 +64,10 @@ public class SpawnEntityPacketListener implements PluginMessageListener {
|
||||
}
|
||||
|
||||
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
|
||||
List<SpawnEntry> entries = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000), SpawnEntry::new);
|
||||
List<SpawnEntry> entries = friendlyByteBuf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 1000),
|
||||
buf -> new SpawnEntry(buf.readUUID(), buf.readDouble(), buf.readDouble(),
|
||||
buf.readDouble(), buf.readFloat(), buf.readFloat(),
|
||||
buf.readNullable(buffer -> buffer.readUUID()), UnknownVersionHelper.readTagUnknown(buf, player)));
|
||||
|
||||
ServerLevel serverLevel = ((CraftWorld)player.getWorld()).getHandle();
|
||||
|
||||
|
@ -4,11 +4,14 @@ import com.moulberry.axiom.AxiomConstants;
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.persistence.ItemStackDataType;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
@ -25,17 +28,25 @@ public class SwitchActiveHotbarPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.player.hotbar") || this.plugin.isMismatchedDataVersion(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
|
||||
RegistryFriendlyByteBuf friendlyByteBuf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(message), ((CraftPlayer)player).getHandle().registryAccess());
|
||||
int oldHotbarIndex = friendlyByteBuf.readByte();
|
||||
int activeHotbarIndex = friendlyByteBuf.readByte();
|
||||
|
||||
ItemStack[] hotbarItems = new ItemStack[9];
|
||||
for (int i=0; i<9; i++) {
|
||||
hotbarItems[i] = CraftItemStack.asCraftMirror(friendlyByteBuf.readItem());
|
||||
hotbarItems[i] = CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.OPTIONAL_STREAM_CODEC.decode(friendlyByteBuf));
|
||||
}
|
||||
|
||||
PersistentDataContainer container = player.getPersistentDataContainer();
|
||||
|
@ -4,13 +4,14 @@ import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.event.AxiomUnknownTeleportEvent;
|
||||
import com.moulberry.axiom.event.AxiomTeleportEvent;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -24,7 +25,15 @@ public class TeleportPacketListener implements PluginMessageListener {
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player)) {
|
||||
try {
|
||||
this.process(player, message);
|
||||
} catch (Throwable t) {
|
||||
player.kick(Component.text("Error while processing packet " + channel + ": " + t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Player player, byte[] message) {
|
||||
if (!this.plugin.canUseAxiom(player, "axiom.world.teleport")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,9 @@ import com.moulberry.axiom.blueprint.BlueprintIo;
|
||||
import com.moulberry.axiom.blueprint.RawBlueprint;
|
||||
import com.moulberry.axiom.blueprint.ServerBlueprintManager;
|
||||
import com.moulberry.axiom.blueprint.ServerBlueprintRegistry;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
@ -22,7 +24,12 @@ public class UploadBlueprintPacketListener {
|
||||
}
|
||||
|
||||
public void onReceive(ServerPlayer serverPlayer, FriendlyByteBuf friendlyByteBuf) {
|
||||
if (!this.plugin.canUseAxiom(serverPlayer.getBukkitEntity())) {
|
||||
if (!this.plugin.canUseAxiom(serverPlayer.getBukkitEntity(), "axiom.blueprint.upload")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.plugin.isMismatchedDataVersion(serverPlayer.getUUID())) {
|
||||
serverPlayer.sendSystemMessage(Component.literal("Axiom+ViaVersion: This feature isn't supported. Switch your client version to " + SharedConstants.VERSION_STRING + " to use this"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -47,6 +54,10 @@ public class UploadBlueprintPacketListener {
|
||||
return;
|
||||
}
|
||||
|
||||
String pathName = pathStr.substring(0, pathStr.length()-3);
|
||||
|
||||
serverPlayer.getServer().execute(() -> {
|
||||
try {
|
||||
Path path = this.plugin.blueprintFolder.resolve(relative);
|
||||
|
||||
// Write file
|
||||
@ -62,10 +73,15 @@ public class UploadBlueprintPacketListener {
|
||||
}
|
||||
|
||||
// Update registry
|
||||
registry.blueprints().put("/" + pathStr.substring(0, pathStr.length()-3), rawBlueprint);
|
||||
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,8 +1,9 @@
|
||||
package com.moulberry.axiom.persistence;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.persistence.CraftPersistentDataContainer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
@ -28,8 +29,7 @@ public class ItemStackDataType implements PersistentDataType<PersistentDataConta
|
||||
public @NotNull PersistentDataContainer toPrimitive(@NotNull ItemStack complex, @NotNull PersistentDataAdapterContext context) {
|
||||
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(complex);
|
||||
if (nmsStack == null) nmsStack = net.minecraft.world.item.ItemStack.EMPTY;
|
||||
CompoundTag tag = new CompoundTag();
|
||||
nmsStack.save(tag);
|
||||
CompoundTag tag = (CompoundTag) nmsStack.save(MinecraftServer.getServer().registryAccess());
|
||||
|
||||
PersistentDataContainer container = context.newPersistentDataContainer();
|
||||
((CraftPersistentDataContainer)container).putAll(tag);
|
||||
@ -39,7 +39,7 @@ public class ItemStackDataType implements PersistentDataType<PersistentDataConta
|
||||
@Override
|
||||
public @NotNull ItemStack fromPrimitive(@NotNull PersistentDataContainer primitive, @NotNull PersistentDataAdapterContext context) {
|
||||
CompoundTag tag = ((CraftPersistentDataContainer)primitive).toTagCompound();
|
||||
net.minecraft.world.item.ItemStack nmsStack = net.minecraft.world.item.ItemStack.of(tag);
|
||||
net.minecraft.world.item.ItemStack nmsStack = net.minecraft.world.item.ItemStack.parseOptional(MinecraftServer.getServer().registryAccess(), tag);
|
||||
|
||||
return CraftItemStack.asCraftMirror(nmsStack);
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package com.moulberry.axiom.viaversion;
|
||||
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class UnknownVersionHelper {
|
||||
|
||||
public static void skipTagUnknown(FriendlyByteBuf friendlyByteBuf, Player player) {
|
||||
if (AxiomPaper.PLUGIN.isMismatchedDataVersion(player.getUniqueId())) {
|
||||
try {
|
||||
ViaVersionHelper.skipTagViaVersion(friendlyByteBuf, player);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
friendlyByteBuf.readNbt(); // Discard
|
||||
}
|
||||
}
|
||||
|
||||
public static CompoundTag readTagUnknown(FriendlyByteBuf friendlyByteBuf, Player player) {
|
||||
if (AxiomPaper.PLUGIN.isMismatchedDataVersion(player.getUniqueId())) {
|
||||
try {
|
||||
return ViaVersionHelper.readTagViaVersion(friendlyByteBuf, player);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
return friendlyByteBuf.readNbt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,2 +1,138 @@
|
||||
package com.moulberry.axiom.viaversion;public class ViaVersionHelper {
|
||||
package com.moulberry.axiom.viaversion;
|
||||
|
||||
import com.moulberry.axiom.AxiomPaper;
|
||||
import com.moulberry.axiom.buffer.BlockBuffer;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.data.MappingData;
|
||||
import com.viaversion.viaversion.api.data.Mappings;
|
||||
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ViaVersionHelper {
|
||||
|
||||
private static final Int2ObjectOpenHashMap<IdMapper<BlockState>> blockRegistryCache = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectOpenHashMap<String> blockRegistryErrorCache = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public static IdMapper<BlockState> getBlockRegistryForVersion(IdMapper<BlockState> mapper, int playerVersion) {
|
||||
if (blockRegistryErrorCache.containsKey(playerVersion)) {
|
||||
throw new RuntimeException(blockRegistryErrorCache.get(playerVersion));
|
||||
}
|
||||
if (blockRegistryCache.containsKey(playerVersion)) {
|
||||
return blockRegistryCache.get(playerVersion);
|
||||
}
|
||||
|
||||
List<ProtocolPathEntry> path = Via.getManager().getProtocolManager().getProtocolPath(playerVersion,
|
||||
SharedConstants.getProtocolVersion());
|
||||
|
||||
if (path == null) {
|
||||
blockRegistryErrorCache.put(playerVersion, "Failed to find protocol path");
|
||||
throw new RuntimeException("Failed to find protocol path");
|
||||
}
|
||||
|
||||
for (int i = path.size()-1; i >= 0; i--) {
|
||||
ProtocolPathEntry protocolPathEntry = path.get(i);
|
||||
|
||||
MappingData mappingData = protocolPathEntry.protocol().getMappingData();
|
||||
|
||||
if (mappingData == null) {
|
||||
blockRegistryErrorCache.put(playerVersion, "Failed to load mapping data (" + protocolPathEntry + ")");
|
||||
throw new RuntimeException("Failed to load mapping data (" + protocolPathEntry + ")");
|
||||
}
|
||||
|
||||
Mappings blockStateMappings = mappingData.getBlockStateMappings();
|
||||
|
||||
if (blockStateMappings == null) {
|
||||
blockRegistryErrorCache.put(playerVersion, "Failed to load BlockState mappings (" + protocolPathEntry + ")");
|
||||
throw new RuntimeException("Failed to load BlockState mappings (" + protocolPathEntry + ")");
|
||||
}
|
||||
|
||||
mapper = ViaVersionHelper.applyMappings(mapper, blockStateMappings);
|
||||
}
|
||||
|
||||
blockRegistryCache.put(playerVersion, mapper);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
private static IdMapper<BlockState> applyMappings(IdMapper<BlockState> registry, Mappings 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;
|
||||
}
|
||||
|
||||
private static final int UNNAMED_COMPOUND_TAG_CHANGE_VERSION = 764; // 1.20.2
|
||||
|
||||
public static void skipTagViaVersion(FriendlyByteBuf friendlyByteBuf, Player player) throws Exception {
|
||||
skipTagViaVersion(friendlyByteBuf, AxiomPaper.PLUGIN.getProtocolVersionFor(player.getUniqueId()));
|
||||
}
|
||||
|
||||
public static void skipTagViaVersion(FriendlyByteBuf friendlyByteBuf, int playerVersion) throws Exception {
|
||||
getTagType(playerVersion).read(friendlyByteBuf);
|
||||
}
|
||||
|
||||
public static CompoundTag readTagViaVersion(FriendlyByteBuf friendlyByteBuf, Player player) throws Exception {
|
||||
return readTagViaVersion(friendlyByteBuf, AxiomPaper.PLUGIN.getProtocolVersionFor(player.getUniqueId()));
|
||||
}
|
||||
|
||||
public static CompoundTag readTagViaVersion(FriendlyByteBuf friendlyByteBuf, int playerVersion) throws Exception {
|
||||
Type<com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag> from = getTagType(playerVersion);
|
||||
Type<com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag> to = getTagType(SharedConstants.getProtocolVersion());
|
||||
|
||||
return readTagViaVersion(friendlyByteBuf, from, to);
|
||||
}
|
||||
|
||||
private static Type<com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag> getTagType(int version) {
|
||||
if (version < UNNAMED_COMPOUND_TAG_CHANGE_VERSION) {
|
||||
return Type.NAMED_COMPOUND_TAG;
|
||||
} else {
|
||||
return Type.COMPOUND_TAG;
|
||||
}
|
||||
}
|
||||
|
||||
private static CompoundTag readTagViaVersion(FriendlyByteBuf friendlyByteBuf,
|
||||
Type<com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag> from,
|
||||
Type<com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag> to) throws Exception {
|
||||
if (from == to) {
|
||||
return friendlyByteBuf.readNbt();
|
||||
}
|
||||
|
||||
com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag tag = from.read(friendlyByteBuf);
|
||||
FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer());
|
||||
to.write(buffer, tag);
|
||||
return buffer.readNbt();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ public abstract class WorldPropertyDataType<T> {
|
||||
if (value == null) value = Items.AIR;
|
||||
|
||||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(8));
|
||||
buf.writeId(BuiltInRegistries.ITEM, value);
|
||||
buf.writeById(BuiltInRegistries.ITEM::getIdOrThrow, value);
|
||||
|
||||
byte[] bytes = new byte[buf.writerIndex()];
|
||||
buf.getBytes(0, bytes);
|
||||
@ -97,7 +97,7 @@ public abstract class WorldPropertyDataType<T> {
|
||||
@Override
|
||||
public Item deserialize(byte[] bytes) {
|
||||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(bytes));
|
||||
return buf.readById(BuiltInRegistries.ITEM);
|
||||
return buf.readById(BuiltInRegistries.ITEM::byIdOrThrow);
|
||||
}
|
||||
};
|
||||
|
||||
@ -112,7 +112,7 @@ public abstract class WorldPropertyDataType<T> {
|
||||
if (value == null) value = Blocks.AIR;
|
||||
|
||||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(8));
|
||||
buf.writeId(BuiltInRegistries.BLOCK, value);
|
||||
buf.writeById(BuiltInRegistries.BLOCK::getIdOrThrow, value);
|
||||
|
||||
byte[] bytes = new byte[buf.writerIndex()];
|
||||
buf.getBytes(0, bytes);
|
||||
@ -122,7 +122,7 @@ public abstract class WorldPropertyDataType<T> {
|
||||
@Override
|
||||
public Block deserialize(byte[] bytes) {
|
||||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(bytes));
|
||||
return buf.readById(BuiltInRegistries.BLOCK);
|
||||
return buf.readById(BuiltInRegistries.BLOCK::byIdOrThrow);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,19 +9,20 @@ import net.minecraft.server.level.ServerLevel;
|
||||
import org.bukkit.GameRule;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.*;
|
||||
|
||||
public class ServerWorldPropertiesRegistry {
|
||||
|
||||
private final LinkedHashMap<WorldPropertyCategory, List<ServerWorldPropertyHolder<?>>> propertyList = new LinkedHashMap<>();
|
||||
private final Map<ResourceLocation, ServerWorldPropertyHolder<?>> propertyMap = new HashMap<>();
|
||||
private final World world;
|
||||
private final Reference<World> world;
|
||||
|
||||
public ServerWorldPropertiesRegistry(World world) {
|
||||
public ServerWorldPropertiesRegistry(Reference<World> world) {
|
||||
this.world = world;
|
||||
this.registerDefault();
|
||||
}
|
||||
@ -32,9 +33,14 @@ public class ServerWorldPropertiesRegistry {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addCategory(WorldPropertyCategory category, List<ServerWorldPropertyBase<?>> properties) {
|
||||
World world = this.world.get();
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ServerWorldPropertyHolder<?>> holders = new ArrayList<>();
|
||||
for (ServerWorldPropertyBase<?> property : properties) {
|
||||
Object defaultValue = property.getDefaultValue(this.world);
|
||||
Object defaultValue = property.getDefaultValue(world);
|
||||
holders.add(new ServerWorldPropertyHolder<>(defaultValue, (ServerWorldPropertyBase<Object>) property));
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import com.moulberry.axiom.world_properties.WorldPropertyWidgetType;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public abstract class ServerWorldPropertyBase<T> {
|
||||
|
@ -9,7 +9,7 @@ import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -5,20 +5,63 @@ description: $description
|
||||
authors:
|
||||
- Moulberry
|
||||
api-version: "$apiVersion"
|
||||
softdepend: ["CoreProtect", "ViaVersion", "WorldGuard", "PlotSquared"]
|
||||
permissions:
|
||||
axiom.*:
|
||||
axiom.all:
|
||||
description: Allows use of all default Axiom features
|
||||
default: op
|
||||
|
||||
children:
|
||||
axiom.*: true
|
||||
axiom.use:
|
||||
description: Allows basic use of axiom
|
||||
default: op
|
||||
axiom.debug:
|
||||
description: Allows use of the /axiompaperdebug command
|
||||
default: op
|
||||
axiom.entity.*:
|
||||
description: Allows use of all entity-related features (spawning, manipulating, deleting)
|
||||
default: op
|
||||
axiom.entity.spawn:
|
||||
description: Allows entity spawning
|
||||
axiom.entity.manipulate:
|
||||
description: Allows entity manipulation
|
||||
axiom.entity.delete:
|
||||
description: Allows entity deletion
|
||||
children:
|
||||
axiom.entity.spawn: true
|
||||
axiom.entity.manipulate: true
|
||||
axiom.entity.delete: true
|
||||
axiom.blueprint.*:
|
||||
description: Allows use of all blueprint-related features
|
||||
default: op
|
||||
children:
|
||||
axiom.blueprint.upload: true
|
||||
axiom.blueprint.request: true
|
||||
axiom.blueprint.manifest: true
|
||||
axiom.chunk.*:
|
||||
description: Allows use of all chunk-related features
|
||||
default: op
|
||||
children:
|
||||
axiom.chunk.request: true
|
||||
axiom.build.*:
|
||||
description: Allows use of all build-related features
|
||||
default: op
|
||||
children:
|
||||
axiom.build.place: true
|
||||
axiom.build.section: true
|
||||
axiom.editor.*:
|
||||
description: Allows use of all editor-related features
|
||||
default: op
|
||||
children:
|
||||
axiom.editor.views: true
|
||||
axiom.player.*:
|
||||
description: Allows use of all player-related features
|
||||
default: op
|
||||
children:
|
||||
axiom.player.speed: true
|
||||
axiom.player.gamemode: true
|
||||
axiom.player.hotbar: true
|
||||
axiom.world.*:
|
||||
description: Allows use of all world-related features
|
||||
default: op
|
||||
children:
|
||||
axiom.world.time: true
|
||||
axiom.world.property: true
|
||||
axiom.world.teleport: true
|
||||
|
||||
axiom.allow_copying_other_plots:
|
||||
description: This permission allows users to copy other user's plots
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren