Move patches to unapplied
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
--- a/net/minecraft/ChatFormatting.java
|
||||
+++ b/net/minecraft/ChatFormatting.java
|
||||
@@ -112,6 +112,18 @@
|
||||
return name == null ? null : FORMATTING_BY_NAME.get(cleanName(name));
|
||||
}
|
||||
|
||||
+ // Paper start - add method to get by hex value
|
||||
+ @Nullable public static ChatFormatting getByHexValue(int i) {
|
||||
+ for (ChatFormatting value : values()) {
|
||||
+ if (value.getColor() != null && value.getColor() == i) {
|
||||
+ return value;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Paper end - add method to get by hex value
|
||||
+
|
||||
@Nullable
|
||||
public static ChatFormatting getById(int colorIndex) {
|
||||
if (colorIndex < 0) {
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/net/minecraft/CrashReport.java
|
||||
+++ b/net/minecraft/CrashReport.java
|
||||
@@ -34,8 +34,10 @@
|
||||
private final SystemReport systemReport = new SystemReport();
|
||||
|
||||
public CrashReport(String message, Throwable cause) {
|
||||
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(cause); // Paper
|
||||
this.title = message;
|
||||
this.exception = cause;
|
||||
+ this.systemReport.setDetail("CraftBukkit Information", new org.bukkit.craftbukkit.CraftCrashReport()); // CraftBukkit
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
@@ -251,7 +253,7 @@
|
||||
}
|
||||
|
||||
public static void preload() {
|
||||
- MemoryReserve.allocate();
|
||||
+ // MemoryReserve.allocate(); // Paper - Disable memory reserve allocating
|
||||
(new CrashReport("Don't panic!", new Throwable())).getFriendlyReport(ReportType.CRASH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/CrashReportCategory.java
|
||||
+++ b/net/minecraft/CrashReportCategory.java
|
||||
@@ -110,6 +110,7 @@
|
||||
} else {
|
||||
this.stackTrace = new StackTraceElement[stackTraceElements.length - 3 - ignoredCallCount];
|
||||
System.arraycopy(stackTraceElements, 3 + ignoredCallCount, this.stackTrace, 0, this.stackTrace.length);
|
||||
+ this.stackTrace = io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(this.stackTrace); // Paper
|
||||
return this.stackTrace.length;
|
||||
}
|
||||
}
|
||||
143
paper-server/patches/unapplied/net/minecraft/Util.java.patch
Normal file
143
paper-server/patches/unapplied/net/minecraft/Util.java.patch
Normal file
@@ -0,0 +1,143 @@
|
||||
--- a/net/minecraft/Util.java
|
||||
+++ b/net/minecraft/Util.java
|
||||
@@ -92,9 +92,26 @@
|
||||
private static final int DEFAULT_MAX_THREADS = 255;
|
||||
private static final int DEFAULT_SAFE_FILE_OPERATION_RETRIES = 10;
|
||||
private static final String MAX_THREADS_SYSTEM_PROPERTY = "max.bg.threads";
|
||||
- private static final TracingExecutor BACKGROUND_EXECUTOR = makeExecutor("Main");
|
||||
+ private static final TracingExecutor BACKGROUND_EXECUTOR = makeExecutor("Main", -1); // Paper - Perf: add priority
|
||||
private static final TracingExecutor IO_POOL = makeIoExecutor("IO-Worker-", false);
|
||||
+ public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool
|
||||
private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true);
|
||||
+ // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
|
||||
+ public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() {
|
||||
+
|
||||
+ private final AtomicInteger count = new AtomicInteger();
|
||||
+
|
||||
+ @Override
|
||||
+ public Thread newThread(Runnable run) {
|
||||
+ Thread ret = new Thread(run);
|
||||
+ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement());
|
||||
+ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> {
|
||||
+ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
|
||||
+ });
|
||||
+ return ret;
|
||||
+ }
|
||||
+ });
|
||||
+ // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
|
||||
private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT);
|
||||
public static final int LINEAR_LOOKUP_THRESHOLD = 8;
|
||||
private static final Set<String> ALLOWED_UNTRUSTED_LINK_PROTOCOLS = Set.of("http", "https");
|
||||
@@ -136,7 +153,7 @@
|
||||
}
|
||||
|
||||
public static long getNanos() {
|
||||
- return timeSource.getAsLong();
|
||||
+ return System.nanoTime(); // Paper
|
||||
}
|
||||
|
||||
public static long getEpochMillis() {
|
||||
@@ -147,15 +164,16 @@
|
||||
return FILENAME_DATE_TIME_FORMATTER.format(ZonedDateTime.now());
|
||||
}
|
||||
|
||||
- private static TracingExecutor makeExecutor(String name) {
|
||||
+ private static TracingExecutor makeExecutor(String name, final int priorityModifier) { // Paper - Perf: add priority
|
||||
int i = maxAllowedExecutorThreads();
|
||||
- ExecutorService executorService;
|
||||
+ // Paper start - Perf: use simpler thread pool that allows 1 thread and reduce worldgen thread worker count for low core count CPUs
|
||||
+ final ExecutorService executorService;
|
||||
if (i <= 0) {
|
||||
executorService = MoreExecutors.newDirectExecutorService();
|
||||
} else {
|
||||
- AtomicInteger atomicInteger = new AtomicInteger(1);
|
||||
- executorService = new ForkJoinPool(i, pool -> {
|
||||
- final String string2 = "Worker-" + name + "-" + atomicInteger.getAndIncrement();
|
||||
+ executorService = Executors.newFixedThreadPool(i, target -> new io.papermc.paper.util.ServerWorkerThread(target, name, priorityModifier));
|
||||
+ }
|
||||
+ /* final String string2 = "Worker-" + name + "-" + atomicInteger.getAndIncrement();
|
||||
ForkJoinWorkerThread forkJoinWorkerThread = new ForkJoinWorkerThread(pool) {
|
||||
@Override
|
||||
protected void onStart() {
|
||||
@@ -177,13 +195,26 @@
|
||||
forkJoinWorkerThread.setName(string2);
|
||||
return forkJoinWorkerThread;
|
||||
}, Util::onThreadException, true);
|
||||
- }
|
||||
+ }*/
|
||||
|
||||
return new TracingExecutor(executorService);
|
||||
}
|
||||
|
||||
public static int maxAllowedExecutorThreads() {
|
||||
- return Mth.clamp(Runtime.getRuntime().availableProcessors() - 1, 1, getMaxThreads());
|
||||
+ // Paper start - Perf: use simpler thread pool that allows 1 thread and reduce worldgen thread worker count for low core count CPUs
|
||||
+ final int cpus = Runtime.getRuntime().availableProcessors() / 2;
|
||||
+ int maxExecutorThreads;
|
||||
+ if (cpus <= 4) {
|
||||
+ maxExecutorThreads = cpus <= 2 ? 1 : 2;
|
||||
+ } else if (cpus <= 8) {
|
||||
+ // [5, 8]
|
||||
+ maxExecutorThreads = Math.max(3, cpus - 2);
|
||||
+ } else {
|
||||
+ maxExecutorThreads = cpus * 2 / 3;
|
||||
+ }
|
||||
+ maxExecutorThreads = Math.min(8, maxExecutorThreads);
|
||||
+ return Integer.getInteger("Paper.WorkerThreadCount", maxExecutorThreads);
|
||||
+ // Paper end - Perf: use simpler thread pool that allows 1 thread and reduce worldgen thread worker count for low core count CPUs
|
||||
}
|
||||
|
||||
private static int getMaxThreads() {
|
||||
@@ -229,10 +260,25 @@
|
||||
TracyClient.setThreadName(string2, namePrefix.hashCode());
|
||||
thread.setName(string2);
|
||||
thread.setDaemon(daemon);
|
||||
+ thread.setUncaughtExceptionHandler(Util::onThreadException);
|
||||
+ return thread;
|
||||
+ }));
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - Separate dimension data IO pool
|
||||
+ private static TracingExecutor makeExtraIoExecutor(String namePrefix) {
|
||||
+ AtomicInteger atomicInteger = new AtomicInteger(1);
|
||||
+ return new TracingExecutor(Executors.newFixedThreadPool(4, runnable -> {
|
||||
+ Thread thread = new Thread(runnable);
|
||||
+ String string2 = namePrefix + atomicInteger.getAndIncrement();
|
||||
+ TracyClient.setThreadName(string2, namePrefix.hashCode());
|
||||
+ thread.setName(string2);
|
||||
+ thread.setDaemon(false);
|
||||
thread.setUncaughtExceptionHandler(Util::onThreadException);
|
||||
return thread;
|
||||
}));
|
||||
}
|
||||
+ // Paper end - Separate dimension data IO pool
|
||||
|
||||
public static void throwAsRuntime(Throwable t) {
|
||||
throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(t);
|
||||
@@ -537,7 +583,7 @@
|
||||
public static <K extends Enum<K>, V> EnumMap<K, V> makeEnumMap(Class<K> enumClass, Function<K, V> mapper) {
|
||||
EnumMap<K, V> enumMap = new EnumMap<>(enumClass);
|
||||
|
||||
- for (K enum_ : (Enum[])enumClass.getEnumConstants()) {
|
||||
+ for (K enum_ : enumClass.getEnumConstants()) { // Paper - decompile error
|
||||
enumMap.put(enum_, mapper.apply(enum_));
|
||||
}
|
||||
|
||||
@@ -1057,16 +1103,7 @@
|
||||
}
|
||||
|
||||
public void openUri(URI uri) {
|
||||
- try {
|
||||
- Process process = AccessController.doPrivileged(
|
||||
- (PrivilegedExceptionAction<Process>)(() -> Runtime.getRuntime().exec(this.getOpenUriArguments(uri)))
|
||||
- );
|
||||
- process.getInputStream().close();
|
||||
- process.getErrorStream().close();
|
||||
- process.getOutputStream().close();
|
||||
- } catch (IOException | PrivilegedActionException var3) {
|
||||
- Util.LOGGER.error("Couldn't open location '{}'", uri, var3);
|
||||
- }
|
||||
+ throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code
|
||||
}
|
||||
|
||||
public void openFile(File file) {
|
||||
@@ -0,0 +1,24 @@
|
||||
--- a/net/minecraft/advancements/AdvancementHolder.java
|
||||
+++ b/net/minecraft/advancements/AdvancementHolder.java
|
||||
@@ -5,6 +5,10 @@
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.craftbukkit.advancement.CraftAdvancement;
|
||||
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
+// CraftBukkit end
|
||||
|
||||
public record AdvancementHolder(ResourceLocation id, Advancement value) {
|
||||
|
||||
@@ -38,4 +42,10 @@
|
||||
public String toString() {
|
||||
return this.id.toString();
|
||||
}
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ public final org.bukkit.advancement.Advancement toBukkit() {
|
||||
+ return new CraftAdvancement(this);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
--- a/net/minecraft/advancements/AdvancementTree.java
|
||||
+++ b/net/minecraft/advancements/AdvancementTree.java
|
||||
@@ -35,7 +35,7 @@
|
||||
this.remove(advancementnode1);
|
||||
}
|
||||
|
||||
- AdvancementTree.LOGGER.info("Forgot about advancement {}", advancement.holder());
|
||||
+ AdvancementTree.LOGGER.debug("Forgot about advancement {}", advancement.holder()); // Paper - Improve logging and errors
|
||||
this.nodes.remove(advancement.holder().id());
|
||||
if (advancement.parent() == null) {
|
||||
this.roots.remove(advancement);
|
||||
@@ -77,7 +77,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- AdvancementTree.LOGGER.info("Loaded {} advancements", this.nodes.size());
|
||||
+ // AdvancementTree.LOGGER.info("Loaded {} advancements", this.nodes.size()); // CraftBukkit - moved to AdvancementDataWorld#reload // Paper - Improve logging and errors; you say it was moved... but it wasn't :) it should be moved however, since this is called when the API creates an advancement
|
||||
}
|
||||
|
||||
private boolean tryInsert(AdvancementHolder advancement) {
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/advancements/DisplayInfo.java
|
||||
+++ b/net/minecraft/advancements/DisplayInfo.java
|
||||
@@ -37,6 +37,7 @@
|
||||
private final boolean hidden;
|
||||
private float x;
|
||||
private float y;
|
||||
+ public final io.papermc.paper.advancement.AdvancementDisplay paper = new io.papermc.paper.advancement.PaperAdvancementDisplay(this); // Paper - Add more advancement API
|
||||
|
||||
public DisplayInfo(
|
||||
ItemStack icon,
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/advancements/critereon/LocationPredicate.java
|
||||
+++ b/net/minecraft/advancements/critereon/LocationPredicate.java
|
||||
@@ -44,7 +44,7 @@
|
||||
public boolean matches(ServerLevel world, double x, double y, double z) {
|
||||
if (this.position.isPresent() && !this.position.get().matches(x, y, z)) {
|
||||
return false;
|
||||
- } else if (this.dimension.isPresent() && this.dimension.get() != world.dimension()) {
|
||||
+ } else if (this.dimension.isPresent() && this.dimension.get() != (io.papermc.paper.configuration.GlobalConfiguration.get().misc.strictAdvancementDimensionCheck ? world.dimension() : org.bukkit.craftbukkit.util.CraftDimensionUtil.getMainDimensionKey(world))) { // Paper - Add option for strict advancement dimension checks
|
||||
return false;
|
||||
} else {
|
||||
BlockPos blockPos = BlockPos.containing(x, y, z);
|
||||
@@ -0,0 +1,52 @@
|
||||
--- a/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
|
||||
+++ b/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
|
||||
@@ -15,41 +15,41 @@
|
||||
import net.minecraft.world.level.storage.loot.LootContext;
|
||||
|
||||
public abstract class SimpleCriterionTrigger<T extends SimpleCriterionTrigger.SimpleInstance> implements CriterionTrigger<T> {
|
||||
- private final Map<PlayerAdvancements, Set<CriterionTrigger.Listener<T>>> players = Maps.newIdentityHashMap();
|
||||
+ // private final Map<PlayerAdvancements, Set<CriterionTrigger.Listener<T>>> players = Maps.newIdentityHashMap(); // Paper - fix AdvancementDataPlayer leak; moved into AdvancementDataPlayer to fix memory leak
|
||||
|
||||
@Override
|
||||
public final void addPlayerListener(PlayerAdvancements manager, CriterionTrigger.Listener<T> conditions) {
|
||||
- this.players.computeIfAbsent(manager, managerx -> Sets.newHashSet()).add(conditions);
|
||||
+ manager.criterionData.computeIfAbsent(this, managerx -> Sets.newHashSet()).add(conditions); // Paper - fix AdvancementDataPlayer leak
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void removePlayerListener(PlayerAdvancements manager, CriterionTrigger.Listener<T> conditions) {
|
||||
- Set<CriterionTrigger.Listener<T>> set = this.players.get(manager);
|
||||
+ Set<CriterionTrigger.Listener<T>> set = (Set) manager.criterionData.get(this); // Paper - fix AdvancementDataPlayer leak
|
||||
if (set != null) {
|
||||
set.remove(conditions);
|
||||
if (set.isEmpty()) {
|
||||
- this.players.remove(manager);
|
||||
+ manager.criterionData.remove(this); // Paper - fix AdvancementDataPlayer leak
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void removePlayerListeners(PlayerAdvancements tracker) {
|
||||
- this.players.remove(tracker);
|
||||
+ tracker.criterionData.remove(this); // Paper - fix AdvancementDataPlayer leak
|
||||
}
|
||||
|
||||
protected void trigger(ServerPlayer player, Predicate<T> predicate) {
|
||||
PlayerAdvancements playerAdvancements = player.getAdvancements();
|
||||
- Set<CriterionTrigger.Listener<T>> set = this.players.get(playerAdvancements);
|
||||
+ Set<CriterionTrigger.Listener<T>> set = (Set) playerAdvancements.criterionData.get(this); // Paper - fix AdvancementDataPlayer leak
|
||||
if (set != null && !set.isEmpty()) {
|
||||
- LootContext lootContext = EntityPredicate.createContext(player, player);
|
||||
+ LootContext lootContext = null; // EntityPredicate.createContext(player, player); // Paper - Perf: lazily create LootContext for criterions
|
||||
List<CriterionTrigger.Listener<T>> list = null;
|
||||
|
||||
for (CriterionTrigger.Listener<T> listener : set) {
|
||||
T simpleInstance = listener.trigger();
|
||||
if (predicate.test(simpleInstance)) {
|
||||
Optional<ContextAwarePredicate> optional = simpleInstance.player();
|
||||
- if (optional.isEmpty() || optional.get().matches(lootContext)) {
|
||||
+ if (optional.isEmpty() || optional.get().matches(lootContext = (lootContext == null ? EntityPredicate.createContext(player, player) : lootContext))) { // Paper - Perf: lazily create LootContext for criterions
|
||||
if (list == null) {
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/commands/CommandSource.java
|
||||
+++ b/net/minecraft/commands/CommandSource.java
|
||||
@@ -22,6 +22,13 @@
|
||||
public boolean shouldInformAdmins() {
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ @Override
|
||||
+ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) {
|
||||
+ return io.papermc.paper.brigadier.NullCommandSender.INSTANCE; // Paper - expose a no-op CommandSender
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
};
|
||||
|
||||
void sendSystemMessage(Component message);
|
||||
@@ -35,4 +42,6 @@
|
||||
default boolean alwaysAccepts() {
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+ org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper); // CraftBukkit
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
--- a/net/minecraft/commands/CommandSourceStack.java
|
||||
+++ b/net/minecraft/commands/CommandSourceStack.java
|
||||
@@ -45,9 +45,9 @@
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.phys.Vec2;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
+import com.mojang.brigadier.tree.CommandNode; // CraftBukkit
|
||||
|
||||
-public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider {
|
||||
-
|
||||
+public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider, io.papermc.paper.command.brigadier.PaperCommandSourceStack { // Paper - Brigadier API
|
||||
public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player"));
|
||||
public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity"));
|
||||
public final CommandSource source;
|
||||
@@ -65,6 +65,8 @@
|
||||
private final Vec2 rotation;
|
||||
private final CommandSigningContext signingContext;
|
||||
private final TaskChainer chatMessageChainer;
|
||||
+ public java.util.Map<Thread, CommandNode> currentCommand = new java.util.concurrent.ConcurrentHashMap<>(); // CraftBukkit // Paper - Thread Safe Vanilla Command permission checking
|
||||
+ public boolean bypassSelectorPermissions = false; // Paper - add bypass for selector permissions
|
||||
|
||||
public CommandSourceStack(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) {
|
||||
this(output, pos, rot, world, level, name, displayName, server, entity, false, CommandResultCallback.EMPTY, EntityAnchorArgument.Anchor.FEET, CommandSigningContext.ANONYMOUS, TaskChainer.immediate(server));
|
||||
@@ -171,8 +173,43 @@
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(int level) {
|
||||
+ // CraftBukkit start
|
||||
+ // Paper start - Thread Safe Vanilla Command permission checking
|
||||
+ CommandNode currentCommand = this.currentCommand.get(Thread.currentThread());
|
||||
+ if (currentCommand != null) {
|
||||
+ return this.hasPermission(level, org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(currentCommand));
|
||||
+ // Paper end - Thread Safe Vanilla Command permission checking
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
return this.permissionLevel >= level;
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - Fix permission levels for command blocks
|
||||
+ private boolean forceRespectPermissionLevel() {
|
||||
+ return this.source == CommandSource.NULL || (this.source instanceof final net.minecraft.world.level.BaseCommandBlock commandBlock && commandBlock.getLevel().paperConfig().commandBlocks.forceFollowPermLevel);
|
||||
+ }
|
||||
+ // Paper end - Fix permission levels for command blocks
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ public boolean hasPermission(int i, String bukkitPermission) {
|
||||
+ // Paper start - Fix permission levels for command blocks
|
||||
+ final java.util.function.BooleanSupplier hasBukkitPerm = () -> this.source == CommandSource.NULL /*treat NULL as having all bukkit perms*/ || this.getBukkitSender().hasPermission(bukkitPermission); // lazily check bukkit perms to the benefit of custom permission setups
|
||||
+ // if the server is null, we must check the vanilla perm level system
|
||||
+ // if ignoreVanillaPermissions is true, we can skip vanilla perms and just run the bukkit perm check
|
||||
+ //noinspection ConstantValue
|
||||
+ if (this.getServer() == null || !this.getServer().server.ignoreVanillaPermissions) { // server & level are null for command function loading
|
||||
+ final boolean hasPermLevel = this.permissionLevel >= i;
|
||||
+ if (this.forceRespectPermissionLevel()) { // NULL CommandSource and command blocks (if setting is enabled) should always pass the vanilla perm check
|
||||
+ return hasPermLevel && hasBukkitPerm.getAsBoolean();
|
||||
+ } else { // otherwise check vanilla perm first then bukkit perm, matching upstream behavior
|
||||
+ return hasPermLevel || hasBukkitPerm.getAsBoolean();
|
||||
+ }
|
||||
+ }
|
||||
+ return hasBukkitPerm.getAsBoolean();
|
||||
+ // Paper end - Fix permission levels for command blocks
|
||||
}
|
||||
+ // CraftBukkit end
|
||||
|
||||
public Vec3 getPosition() {
|
||||
return this.worldPosition;
|
||||
@@ -302,21 +339,26 @@
|
||||
while (iterator.hasNext()) {
|
||||
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||
|
||||
- if (entityplayer.commandSource() != this.source && this.server.getPlayerList().isOp(entityplayer.getGameProfile())) {
|
||||
+ if (entityplayer.commandSource() != this.source && entityplayer.getBukkitEntity().hasPermission("minecraft.admin.command_feedback")) { // CraftBukkit
|
||||
entityplayer.sendSystemMessage(ichatmutablecomponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- if (this.source != this.server && this.server.getGameRules().getBoolean(GameRules.RULE_LOGADMINCOMMANDS)) {
|
||||
+ if (this.source != this.server && this.server.getGameRules().getBoolean(GameRules.RULE_LOGADMINCOMMANDS) && !org.spigotmc.SpigotConfig.silentCommandBlocks) { // Spigot
|
||||
this.server.sendSystemMessage(ichatmutablecomponent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void sendFailure(Component message) {
|
||||
+ // Paper start - Add UnknownCommandEvent
|
||||
+ this.sendFailure(message, true);
|
||||
+ }
|
||||
+ public void sendFailure(Component message, boolean withStyle) {
|
||||
+ // Paper end - Add UnknownCommandEvent
|
||||
if (this.source.acceptsFailure() && !this.silent) {
|
||||
- this.source.sendSystemMessage(Component.empty().append(message).withStyle(ChatFormatting.RED));
|
||||
+ this.source.sendSystemMessage(withStyle ? Component.empty().append(message).withStyle(ChatFormatting.RED) : message); // Paper - Add UnknownCommandEvent
|
||||
}
|
||||
|
||||
}
|
||||
@@ -400,4 +442,16 @@
|
||||
public boolean isSilent() {
|
||||
return this.silent;
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public CommandSourceStack getHandle() {
|
||||
+ return this;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+ // CraftBukkit start
|
||||
+ public org.bukkit.command.CommandSender getBukkitSender() {
|
||||
+ return this.source.getBukkitSender(this);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -138,6 +138,14 @@
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import org.slf4j.Logger;
|
||||
+
|
||||
+// CraftBukkit start
|
||||
+import com.google.common.base.Joiner;
|
||||
+import java.util.Collection;
|
||||
+import java.util.LinkedHashSet;
|
||||
+import org.bukkit.event.player.PlayerCommandSendEvent;
|
||||
+import org.bukkit.event.server.ServerCommandEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public class Commands {
|
||||
|
||||
@@ -151,6 +159,7 @@
|
||||
private final com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher = new com.mojang.brigadier.CommandDispatcher();
|
||||
|
||||
public Commands(Commands.CommandSelection environment, CommandBuildContext commandRegistryAccess) {
|
||||
+ // Paper
|
||||
AdvancementCommands.register(this.dispatcher);
|
||||
AttributeCommand.register(this.dispatcher, commandRegistryAccess);
|
||||
ExecuteCommand.register(this.dispatcher, commandRegistryAccess);
|
||||
@@ -250,8 +259,33 @@
|
||||
|
||||
if (environment.includeIntegrated) {
|
||||
PublishCommand.register(this.dispatcher);
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - Vanilla command permission fixes
|
||||
+ for (final CommandNode<CommandSourceStack> node : this.dispatcher.getRoot().getChildren()) {
|
||||
+ if (node.getRequirement() == com.mojang.brigadier.builder.ArgumentBuilder.<CommandSourceStack>defaultRequirement()) {
|
||||
+ node.requirement = stack -> stack.source == CommandSource.NULL || stack.getBukkitSender().hasPermission(org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(node));
|
||||
+ }
|
||||
}
|
||||
+ // Paper end - Vanilla command permission fixes
|
||||
+ // Paper start - Brigadier Command API
|
||||
+ // Create legacy minecraft namespace commands
|
||||
+ for (final CommandNode<CommandSourceStack> node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) {
|
||||
+ // The brigadier dispatcher is not able to resolve nested redirects.
|
||||
+ // E.g. registering the alias minecraft:tp cannot redirect to tp, as tp itself redirects to teleport.
|
||||
+ // Instead, target the first none redirecting node.
|
||||
+ CommandNode<CommandSourceStack> flattenedAliasTarget = node;
|
||||
+ while (flattenedAliasTarget.getRedirect() != null) flattenedAliasTarget = flattenedAliasTarget.getRedirect();
|
||||
|
||||
+ this.dispatcher.register(
|
||||
+ com.mojang.brigadier.builder.LiteralArgumentBuilder.<CommandSourceStack>literal("minecraft:" + node.getName())
|
||||
+ .executes(flattenedAliasTarget.getCommand())
|
||||
+ .requires(flattenedAliasTarget.getRequirement())
|
||||
+ .redirect(flattenedAliasTarget)
|
||||
+ );
|
||||
+ }
|
||||
+ // Paper end - Brigadier Command API
|
||||
+ // Paper - remove public constructor, no longer needed
|
||||
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
|
||||
}
|
||||
|
||||
@@ -262,30 +296,72 @@
|
||||
return new ParseResults(commandcontextbuilder1, parseResults.getReader(), parseResults.getExceptions());
|
||||
}
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ public void dispatchServerCommand(CommandSourceStack sender, String command) {
|
||||
+ Joiner joiner = Joiner.on(" ");
|
||||
+ if (command.startsWith("/")) {
|
||||
+ command = command.substring(1);
|
||||
+ }
|
||||
+
|
||||
+ ServerCommandEvent event = new ServerCommandEvent(sender.getBukkitSender(), command);
|
||||
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
||||
+ if (event.isCancelled()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ command = event.getCommand();
|
||||
+
|
||||
+ String[] args = command.split(" ");
|
||||
+ if (args.length == 0) return; // Paper - empty commands shall not be dispatched
|
||||
+
|
||||
+ // Paper - Fix permission levels for command blocks
|
||||
+
|
||||
+ // Handle vanilla commands; // Paper - handled in CommandNode/CommandDispatcher
|
||||
+
|
||||
+ String newCommand = joiner.join(args);
|
||||
+ this.performPrefixedCommand(sender, newCommand, newCommand);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
public void performPrefixedCommand(CommandSourceStack source, String command) {
|
||||
- command = command.startsWith("/") ? command.substring(1) : command;
|
||||
- this.performCommand(this.dispatcher.parse(command, source), command);
|
||||
+ // CraftBukkit start
|
||||
+ this.performPrefixedCommand(source, command, command);
|
||||
+ }
|
||||
+
|
||||
+ public void performPrefixedCommand(CommandSourceStack commandlistenerwrapper, String s, String label) {
|
||||
+ s = s.startsWith("/") ? s.substring(1) : s;
|
||||
+ this.performCommand(this.dispatcher.parse(s, commandlistenerwrapper), s, label);
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
public void performCommand(ParseResults<CommandSourceStack> parseResults, String command) {
|
||||
- CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseResults.getContext().getSource();
|
||||
+ this.performCommand(parseResults, command, command);
|
||||
+ }
|
||||
|
||||
+ public void performCommand(ParseResults<CommandSourceStack> parseresults, String s, String label) { // CraftBukkit
|
||||
+ // Paper start
|
||||
+ this.performCommand(parseresults, s, label, false);
|
||||
+ }
|
||||
+ public void performCommand(ParseResults<CommandSourceStack> parseresults, String s, String label, boolean throwCommandError) {
|
||||
+ // Paper end
|
||||
+ CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource();
|
||||
+
|
||||
Profiler.get().push(() -> {
|
||||
- return "/" + command;
|
||||
+ return "/" + s;
|
||||
});
|
||||
- ContextChain<CommandSourceStack> contextchain = Commands.finishParsing(parseResults, command, commandlistenerwrapper);
|
||||
+ ContextChain contextchain = this.finishParsing(parseresults, s, commandlistenerwrapper, label); // CraftBukkit // Paper - Add UnknownCommandEvent
|
||||
|
||||
try {
|
||||
if (contextchain != null) {
|
||||
Commands.executeCommandInContext(commandlistenerwrapper, (executioncontext) -> {
|
||||
- ExecutionContext.queueInitialCommandExecution(executioncontext, command, contextchain, commandlistenerwrapper, CommandResultCallback.EMPTY);
|
||||
+ ExecutionContext.queueInitialCommandExecution(executioncontext, s, contextchain, commandlistenerwrapper, CommandResultCallback.EMPTY);
|
||||
});
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
+ if (throwCommandError) throw exception;
|
||||
MutableComponent ichatmutablecomponent = Component.literal(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage());
|
||||
|
||||
- if (Commands.LOGGER.isDebugEnabled()) {
|
||||
- Commands.LOGGER.error("Command exception: /{}", command, exception);
|
||||
+ Commands.LOGGER.error("Command exception: /{}", s, exception); // Paper - always show execution exception in console log
|
||||
+ if (commandlistenerwrapper.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper - Debugging
|
||||
StackTraceElement[] astacktraceelement = exception.getStackTrace();
|
||||
|
||||
for (int i = 0; i < Math.min(astacktraceelement.length, 3); ++i) {
|
||||
@@ -298,7 +374,7 @@
|
||||
}));
|
||||
if (SharedConstants.IS_RUNNING_IN_IDE) {
|
||||
commandlistenerwrapper.sendFailure(Component.literal(Util.describeError(exception)));
|
||||
- Commands.LOGGER.error("'/{}' threw an exception", command, exception);
|
||||
+ Commands.LOGGER.error("'/{}' threw an exception", s, exception);
|
||||
}
|
||||
} finally {
|
||||
Profiler.get().pop();
|
||||
@@ -307,18 +383,22 @@
|
||||
}
|
||||
|
||||
@Nullable
|
||||
- private static ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseResults, String command, CommandSourceStack source) {
|
||||
+ private ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseresults, String s, CommandSourceStack commandlistenerwrapper, String label) { // CraftBukkit // Paper - Add UnknownCommandEvent
|
||||
try {
|
||||
- Commands.validateParseResults(parseResults);
|
||||
- return (ContextChain) ContextChain.tryFlatten(parseResults.getContext().build(command)).orElseThrow(() -> {
|
||||
- return CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseResults.getReader());
|
||||
+ Commands.validateParseResults(parseresults);
|
||||
+ return (ContextChain) ContextChain.tryFlatten(parseresults.getContext().build(s)).orElseThrow(() -> {
|
||||
+ return CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseresults.getReader());
|
||||
});
|
||||
} catch (CommandSyntaxException commandsyntaxexception) {
|
||||
- source.sendFailure(ComponentUtils.fromMessage(commandsyntaxexception.getRawMessage()));
|
||||
+ // Paper start - Add UnknownCommandEvent
|
||||
+ final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text();
|
||||
+ // commandlistenerwrapper.sendFailure(ComponentUtils.fromMessage(commandsyntaxexception.getRawMessage()));
|
||||
+ builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(commandsyntaxexception.getRawMessage()));
|
||||
+ // Paper end - Add UnknownCommandEvent
|
||||
if (commandsyntaxexception.getInput() != null && commandsyntaxexception.getCursor() >= 0) {
|
||||
int i = Math.min(commandsyntaxexception.getInput().length(), commandsyntaxexception.getCursor());
|
||||
MutableComponent ichatmutablecomponent = Component.empty().withStyle(ChatFormatting.GRAY).withStyle((chatmodifier) -> {
|
||||
- return chatmodifier.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command));
|
||||
+ return chatmodifier.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + label)); // CraftBukkit // Paper
|
||||
});
|
||||
|
||||
if (i > 10) {
|
||||
@@ -333,8 +413,18 @@
|
||||
}
|
||||
|
||||
ichatmutablecomponent.append((Component) Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));
|
||||
- source.sendFailure(ichatmutablecomponent);
|
||||
+ // Paper start - Add UnknownCommandEvent
|
||||
+ // commandlistenerwrapper.sendFailure(ichatmutablecomponent);
|
||||
+ builder
|
||||
+ .append(net.kyori.adventure.text.Component.newline())
|
||||
+ .append(io.papermc.paper.adventure.PaperAdventure.asAdventure(ichatmutablecomponent));
|
||||
}
|
||||
+ org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(commandlistenerwrapper.getBukkitSender(), s, org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty() ? null : builder.build());
|
||||
+ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
+ if (event.message() != null) {
|
||||
+ commandlistenerwrapper.sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false);
|
||||
+ // Paper end - Add UnknownCommandEvent
|
||||
+ }
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -368,7 +458,7 @@
|
||||
|
||||
executioncontext1.close();
|
||||
} finally {
|
||||
- Commands.CURRENT_EXECUTION_CONTEXT.set((Object) null);
|
||||
+ Commands.CURRENT_EXECUTION_CONTEXT.set(null); // CraftBukkit - decompile error
|
||||
}
|
||||
} else {
|
||||
callback.accept(executioncontext);
|
||||
@@ -377,30 +467,133 @@
|
||||
}
|
||||
|
||||
public void sendCommands(ServerPlayer player) {
|
||||
- Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newHashMap();
|
||||
+ // Paper start - Send empty commands if tab completion is disabled
|
||||
+ if (org.spigotmc.SpigotConfig.tabComplete < 0) {
|
||||
+ player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>()));
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - Send empty commands if tab completion is disabled
|
||||
+ // CraftBukkit start
|
||||
+ // Register Vanilla commands into builtRoot as before
|
||||
+ // Paper start - Perf: Async command map building
|
||||
+ // Copy root children to avoid concurrent modification during building
|
||||
+ final Collection<CommandNode<CommandSourceStack>> commandNodes = new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren());
|
||||
+ COMMAND_SENDING_POOL.execute(() -> this.sendAsync(player, commandNodes));
|
||||
+ }
|
||||
+
|
||||
+ // Fixed pool, but with discard policy
|
||||
+ public static final java.util.concurrent.ExecutorService COMMAND_SENDING_POOL = new java.util.concurrent.ThreadPoolExecutor(
|
||||
+ 2, 2, 0, java.util.concurrent.TimeUnit.MILLISECONDS,
|
||||
+ new java.util.concurrent.LinkedBlockingQueue<>(),
|
||||
+ new com.google.common.util.concurrent.ThreadFactoryBuilder()
|
||||
+ .setNameFormat("Paper Async Command Builder Thread Pool - %1$d")
|
||||
+ .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER))
|
||||
+ .build(),
|
||||
+ new java.util.concurrent.ThreadPoolExecutor.DiscardPolicy()
|
||||
+ );
|
||||
+
|
||||
+ private void sendAsync(ServerPlayer player, Collection<CommandNode<CommandSourceStack>> dispatcherRootChildren) {
|
||||
+ // Paper end - Perf: Async command map building
|
||||
+ Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues
|
||||
+ // Paper - brigadier API removes the need to fill the map twice
|
||||
RootCommandNode<SharedSuggestionProvider> rootcommandnode = new RootCommandNode();
|
||||
|
||||
map.put(this.dispatcher.getRoot(), rootcommandnode);
|
||||
- this.fillUsableCommands(this.dispatcher.getRoot(), rootcommandnode, player.createCommandSourceStack(), map);
|
||||
+ this.fillUsableCommands(dispatcherRootChildren, rootcommandnode, player.createCommandSourceStack(), map); // Paper - Perf: Async command map building; pass copy of children
|
||||
+
|
||||
+ Collection<String> bukkit = new LinkedHashSet<>();
|
||||
+ for (CommandNode node : rootcommandnode.getChildren()) {
|
||||
+ bukkit.add(node.getName());
|
||||
+ }
|
||||
+ // Paper start - Perf: Async command map building
|
||||
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper - Brigadier API
|
||||
+ net.minecraft.server.MinecraftServer.getServer().execute(() -> {
|
||||
+ runSync(player, bukkit, rootcommandnode);
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ private void runSync(ServerPlayer player, Collection<String> bukkit, RootCommandNode<SharedSuggestionProvider> rootcommandnode) {
|
||||
+ // Paper end - Perf: Async command map building
|
||||
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, true).callEvent(); // Paper - Brigadier API
|
||||
+ PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit));
|
||||
+ event.getPlayer().getServer().getPluginManager().callEvent(event);
|
||||
+
|
||||
+ // Remove labels that were removed during the event
|
||||
+ for (String orig : bukkit) {
|
||||
+ if (!event.getCommands().contains(orig)) {
|
||||
+ rootcommandnode.removeCommand(orig);
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
player.connection.send(new ClientboundCommandsPacket(rootcommandnode));
|
||||
}
|
||||
|
||||
- private void fillUsableCommands(CommandNode<CommandSourceStack> tree, CommandNode<SharedSuggestionProvider> result, CommandSourceStack source, Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> resultNodes) {
|
||||
- Iterator iterator = tree.getChildren().iterator();
|
||||
+ // Paper start - Perf: Async command map building; pass copy of children
|
||||
+ private void fillUsableCommands(Collection<CommandNode<CommandSourceStack>> children, CommandNode<SharedSuggestionProvider> result, CommandSourceStack source, Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> resultNodes) {
|
||||
+ resultNodes.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains( ":" )); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below
|
||||
+ Iterator iterator = children.iterator();
|
||||
+ // Paper end - Perf: Async command map building
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
CommandNode<CommandSourceStack> commandnode2 = (CommandNode) iterator.next();
|
||||
+ // Paper start - Brigadier API
|
||||
+ if (commandnode2.clientNode != null) {
|
||||
+ commandnode2 = commandnode2.clientNode;
|
||||
+ }
|
||||
+ // Paper end - Brigadier API
|
||||
+ if ( !org.spigotmc.SpigotConfig.sendNamespaced && commandnode2.getName().contains( ":" ) ) continue; // Spigot
|
||||
|
||||
if (commandnode2.canUse(source)) {
|
||||
- ArgumentBuilder<SharedSuggestionProvider, ?> argumentbuilder = commandnode2.createBuilder();
|
||||
+ ArgumentBuilder argumentbuilder = commandnode2.createBuilder(); // CraftBukkit - decompile error
|
||||
+ // Paper start
|
||||
+ /*
|
||||
+ Because of how commands can be yeeted right left and center due to bad bukkit practices
|
||||
+ we need to be able to ensure that ALL commands are registered (even redirects).
|
||||
|
||||
+ What this will do is IF the redirect seems to be "dead" it will create a builder and essentially populate (flatten)
|
||||
+ all the children from the dead redirect to the node.
|
||||
+
|
||||
+ So, if minecraft:msg redirects to msg but the original msg node has been overriden minecraft:msg will now act as msg and will explicilty inherit its children.
|
||||
+
|
||||
+ The only way to fix this is to either:
|
||||
+ - Send EVERYTHING flattened, don't use redirects
|
||||
+ - Don't allow command nodes to be deleted
|
||||
+ - Do this :)
|
||||
+ */
|
||||
+
|
||||
+ // Is there an invalid command redirect?
|
||||
+ if (argumentbuilder.getRedirect() != null && (CommandNode) resultNodes.get(argumentbuilder.getRedirect()) == null) {
|
||||
+ // Create the argument builder with the same values as the specified node, but with a different literal and populated children
|
||||
+
|
||||
+ CommandNode<CommandSourceStack> redirect = argumentbuilder.getRedirect();
|
||||
+ // Diff copied from LiteralCommand#createBuilder
|
||||
+ final com.mojang.brigadier.builder.LiteralArgumentBuilder<CommandSourceStack> builder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(commandnode2.getName());
|
||||
+ builder.requires(redirect.getRequirement());
|
||||
+ // builder.forward(redirect.getRedirect(), redirect.getRedirectModifier(), redirect.isFork()); We don't want to migrate the forward, since it's invalid.
|
||||
+ if (redirect.getCommand() != null) {
|
||||
+ builder.executes(redirect.getCommand());
|
||||
+ }
|
||||
+ // Diff copied from LiteralCommand#createBuilder
|
||||
+ for (CommandNode<CommandSourceStack> child : redirect.getChildren()) {
|
||||
+ builder.then(child);
|
||||
+ }
|
||||
+
|
||||
+ argumentbuilder = builder;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
argumentbuilder.requires((icompletionprovider) -> {
|
||||
return true;
|
||||
});
|
||||
if (argumentbuilder.getCommand() != null) {
|
||||
- argumentbuilder.executes((commandcontext) -> {
|
||||
- return 0;
|
||||
+ // Paper start - fix suggestions due to falsely equal nodes
|
||||
+ argumentbuilder.executes(new com.mojang.brigadier.Command<io.papermc.paper.command.brigadier.CommandSourceStack>() {
|
||||
+ @Override
|
||||
+ public int run(com.mojang.brigadier.context.CommandContext<io.papermc.paper.command.brigadier.CommandSourceStack> commandContext) throws CommandSyntaxException {
|
||||
+ return 0;
|
||||
+ }
|
||||
});
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
if (argumentbuilder instanceof RequiredArgumentBuilder) {
|
||||
@@ -415,12 +608,12 @@
|
||||
argumentbuilder.redirect((CommandNode) resultNodes.get(argumentbuilder.getRedirect()));
|
||||
}
|
||||
|
||||
- CommandNode<SharedSuggestionProvider> commandnode3 = argumentbuilder.build();
|
||||
+ CommandNode commandnode3 = argumentbuilder.build(); // CraftBukkit - decompile error
|
||||
|
||||
resultNodes.put(commandnode2, commandnode3);
|
||||
result.addChild(commandnode3);
|
||||
if (!commandnode2.getChildren().isEmpty()) {
|
||||
- this.fillUsableCommands(commandnode2, commandnode3, source, resultNodes);
|
||||
+ this.fillUsableCommands(commandnode2.getChildren(), commandnode3, source, resultNodes); // Paper - Perf: Async command map building; pass children directly
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,7 +674,7 @@
|
||||
}
|
||||
|
||||
private <T> HolderLookup.RegistryLookup.Delegate<T> createLookup(final HolderLookup.RegistryLookup<T> original) {
|
||||
- return new HolderLookup.RegistryLookup.Delegate<T>(this) {
|
||||
+ return new HolderLookup.RegistryLookup.Delegate<T>() { // CraftBukkit - decompile error
|
||||
@Override
|
||||
public HolderLookup.RegistryLookup<T> parent() {
|
||||
return original;
|
||||
@@ -0,0 +1,52 @@
|
||||
--- a/net/minecraft/commands/arguments/EntityArgument.java
|
||||
+++ b/net/minecraft/commands/arguments/EntityArgument.java
|
||||
@@ -102,21 +102,27 @@
|
||||
}
|
||||
|
||||
private EntitySelector parse(StringReader reader, boolean allowAtSelectors) throws CommandSyntaxException {
|
||||
+ // CraftBukkit start
|
||||
+ return this.parse(reader, allowAtSelectors, false);
|
||||
+ }
|
||||
+
|
||||
+ public EntitySelector parse(StringReader stringreader, boolean flag, boolean overridePermissions) throws CommandSyntaxException {
|
||||
+ // CraftBukkit end
|
||||
boolean flag1 = false;
|
||||
- EntitySelectorParser argumentparserselector = new EntitySelectorParser(reader, allowAtSelectors);
|
||||
- EntitySelector entityselector = argumentparserselector.parse();
|
||||
+ EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, flag);
|
||||
+ EntitySelector entityselector = argumentparserselector.parse(overridePermissions); // CraftBukkit
|
||||
|
||||
if (entityselector.getMaxResults() > 1 && this.single) {
|
||||
if (this.playersOnly) {
|
||||
- reader.setCursor(0);
|
||||
- throw EntityArgument.ERROR_NOT_SINGLE_PLAYER.createWithContext(reader);
|
||||
+ stringreader.setCursor(0);
|
||||
+ throw EntityArgument.ERROR_NOT_SINGLE_PLAYER.createWithContext(stringreader);
|
||||
} else {
|
||||
- reader.setCursor(0);
|
||||
- throw EntityArgument.ERROR_NOT_SINGLE_ENTITY.createWithContext(reader);
|
||||
+ stringreader.setCursor(0);
|
||||
+ throw EntityArgument.ERROR_NOT_SINGLE_ENTITY.createWithContext(stringreader);
|
||||
}
|
||||
} else if (entityselector.includesEntities() && this.playersOnly && !entityselector.isSelfSelector()) {
|
||||
- reader.setCursor(0);
|
||||
- throw EntityArgument.ERROR_ONLY_PLAYERS_ALLOWED.createWithContext(reader);
|
||||
+ stringreader.setCursor(0);
|
||||
+ throw EntityArgument.ERROR_ONLY_PLAYERS_ALLOWED.createWithContext(stringreader);
|
||||
} else {
|
||||
return entityselector;
|
||||
}
|
||||
@@ -129,7 +135,12 @@
|
||||
StringReader stringreader = new StringReader(suggestionsbuilder.getInput());
|
||||
|
||||
stringreader.setCursor(suggestionsbuilder.getStart());
|
||||
- EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, EntitySelectorParser.allowSelectors(icompletionprovider));
|
||||
+ // Paper start - Fix EntityArgument permissions
|
||||
+ final boolean permission = object instanceof CommandSourceStack stack
|
||||
+ ? stack.bypassSelectorPermissions || stack.hasPermission(2, "minecraft.command.selector")
|
||||
+ : icompletionprovider.hasPermission(2);
|
||||
+ EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, permission);
|
||||
+ // Paper end - Fix EntityArgument permissions
|
||||
|
||||
try {
|
||||
argumentparserselector.parse();
|
||||
@@ -0,0 +1,41 @@
|
||||
--- a/net/minecraft/commands/arguments/MessageArgument.java
|
||||
+++ b/net/minecraft/commands/arguments/MessageArgument.java
|
||||
@@ -40,6 +40,11 @@
|
||||
|
||||
public static void resolveChatMessage(CommandContext<CommandSourceStack> context, String name, Consumer<PlayerChatMessage> callback) throws CommandSyntaxException {
|
||||
MessageArgument.Message message = context.getArgument(name, MessageArgument.Message.class);
|
||||
+ // Paper start
|
||||
+ resolveChatMessage(message, context, name, callback);
|
||||
+ }
|
||||
+ public static void resolveChatMessage(MessageArgument.Message message, CommandContext<CommandSourceStack> context, String name, Consumer<PlayerChatMessage> callback) throws CommandSyntaxException {
|
||||
+ // Paper end
|
||||
CommandSourceStack commandSourceStack = context.getSource();
|
||||
Component component = message.resolveComponent(commandSourceStack);
|
||||
CommandSigningContext commandSigningContext = commandSourceStack.getSigningContext();
|
||||
@@ -54,17 +59,21 @@
|
||||
private static void resolveSignedMessage(Consumer<PlayerChatMessage> callback, CommandSourceStack source, PlayerChatMessage message) {
|
||||
MinecraftServer minecraftServer = source.getServer();
|
||||
CompletableFuture<FilteredText> completableFuture = filterPlainText(source, message);
|
||||
- Component component = minecraftServer.getChatDecorator().decorate(source.getPlayer(), message.decoratedContent());
|
||||
- source.getChatMessageChainer().append(completableFuture, filtered -> {
|
||||
- PlayerChatMessage playerChatMessage2 = message.withUnsignedContent(component).filter(filtered.mask());
|
||||
+ // Paper start - support asynchronous chat decoration
|
||||
+ CompletableFuture<Component> componentFuture = minecraftServer.getChatDecorator().decorate(source.getPlayer(), source, message.decoratedContent());
|
||||
+ source.getChatMessageChainer().append(CompletableFuture.allOf(completableFuture, componentFuture), filtered -> {
|
||||
+ PlayerChatMessage playerChatMessage2 = message.withUnsignedContent(componentFuture.join()).filter(completableFuture.join().mask());
|
||||
+ // Paper end - support asynchronous chat decoration
|
||||
callback.accept(playerChatMessage2);
|
||||
});
|
||||
}
|
||||
|
||||
private static void resolveDisguisedMessage(Consumer<PlayerChatMessage> callback, CommandSourceStack source, PlayerChatMessage message) {
|
||||
ChatDecorator chatDecorator = source.getServer().getChatDecorator();
|
||||
- Component component = chatDecorator.decorate(source.getPlayer(), message.decoratedContent());
|
||||
- callback.accept(message.withUnsignedContent(component));
|
||||
+ // Paper start - support asynchronous chat decoration
|
||||
+ CompletableFuture<Component> componentFuture = chatDecorator.decorate(source.getPlayer(), source, message.decoratedContent());
|
||||
+ source.getChatMessageChainer().append(componentFuture, (result) -> callback.accept(message.withUnsignedContent(result)));
|
||||
+ // Paper end - support asynchronous chat decoration
|
||||
}
|
||||
|
||||
private static CompletableFuture<FilteredText> filterPlainText(CommandSourceStack source, PlayerChatMessage message) {
|
||||
@@ -0,0 +1,38 @@
|
||||
--- a/net/minecraft/commands/arguments/blocks/BlockStateParser.java
|
||||
+++ b/net/minecraft/commands/arguments/blocks/BlockStateParser.java
|
||||
@@ -67,7 +67,7 @@
|
||||
private final StringReader reader;
|
||||
private final boolean forTesting;
|
||||
private final boolean allowNbt;
|
||||
- private final Map<Property<?>, Comparable<?>> properties = Maps.newHashMap();
|
||||
+ private final Map<Property<?>, Comparable<?>> properties = Maps.newLinkedHashMap(); // CraftBukkit - stable
|
||||
private final Map<String, String> vagueProperties = Maps.newHashMap();
|
||||
private ResourceLocation id = ResourceLocation.withDefaultNamespace("");
|
||||
@Nullable
|
||||
@@ -275,7 +275,7 @@
|
||||
Iterator iterator = property.getPossibleValues().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
- T t0 = (Comparable) iterator.next();
|
||||
+ T t0 = (T) iterator.next(); // CraftBukkit - decompile error
|
||||
|
||||
if (t0 instanceof Integer integer) {
|
||||
builder.suggest(integer);
|
||||
@@ -545,7 +545,7 @@
|
||||
Optional<T> optional = property.getValue(value);
|
||||
|
||||
if (optional.isPresent()) {
|
||||
- this.state = (BlockState) this.state.setValue(property, (Comparable) optional.get());
|
||||
+ this.state = (BlockState) this.state.setValue(property, (T) optional.get()); // CraftBukkit - decompile error
|
||||
this.properties.put(property, (Comparable) optional.get());
|
||||
} else {
|
||||
this.reader.setCursor(cursor);
|
||||
@@ -581,7 +581,7 @@
|
||||
private static <T extends Comparable<T>> void appendProperty(StringBuilder builder, Property<T> property, Comparable<?> value) {
|
||||
builder.append(property.getName());
|
||||
builder.append('=');
|
||||
- builder.append(property.getName(value));
|
||||
+ builder.append(property.getName((T) value)); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
public static record BlockResult(BlockState blockState, Map<Property<?>, Comparable<?>> properties, @Nullable CompoundTag nbt) {
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/commands/arguments/item/ItemInput.java
|
||||
+++ b/net/minecraft/commands/arguments/item/ItemInput.java
|
||||
@@ -78,6 +78,6 @@
|
||||
}
|
||||
|
||||
private String getItemName() {
|
||||
- return this.item.unwrapKey().map(ResourceKey::location).orElseGet(() -> "unknown[" + this.item + "]").toString();
|
||||
+ return this.item.unwrapKey().<Object>map(ResourceKey::location).orElseGet(() -> "unknown[" + this.item + "]").toString(); // Paper - decompile fix
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/commands/arguments/selector/EntitySelector.java
|
||||
+++ b/net/minecraft/commands/arguments/selector/EntitySelector.java
|
||||
@@ -93,7 +93,7 @@
|
||||
}
|
||||
|
||||
private void checkPermissions(CommandSourceStack source) throws CommandSyntaxException {
|
||||
- if (this.usesSelector && !source.hasPermission(2)) {
|
||||
+ if (!source.bypassSelectorPermissions && (this.usesSelector && !source.hasPermission(2, "minecraft.command.selector"))) { // CraftBukkit // Paper - add bypass for selector perms
|
||||
throw EntityArgument.ERROR_SELECTORS_NOT_ALLOWED.create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
--- a/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
+++ b/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
@@ -133,7 +133,7 @@
|
||||
boolean flag;
|
||||
|
||||
if (source instanceof SharedSuggestionProvider icompletionprovider) {
|
||||
- if (icompletionprovider.hasPermission(2)) {
|
||||
+ if (source instanceof net.minecraft.commands.CommandSourceStack stack ? stack.bypassSelectorPermissions || stack.hasPermission(2, "minecraft.command.selector") : icompletionprovider.hasPermission(2)) { // Paper - Fix EntityArgument permissions
|
||||
flag = true;
|
||||
return flag;
|
||||
}
|
||||
@@ -158,7 +158,7 @@
|
||||
axisalignedbb = this.createAabb(this.deltaX == null ? 0.0D : this.deltaX, this.deltaY == null ? 0.0D : this.deltaY, this.deltaZ == null ? 0.0D : this.deltaZ);
|
||||
}
|
||||
|
||||
- Function function;
|
||||
+ Function<Vec3, Vec3> function; // CraftBukkit - decompile error
|
||||
|
||||
if (this.x == null && this.y == null && this.z == null) {
|
||||
function = (vec3d) -> {
|
||||
@@ -215,8 +215,10 @@
|
||||
};
|
||||
}
|
||||
|
||||
- protected void parseSelector() throws CommandSyntaxException {
|
||||
- this.usesSelectors = true;
|
||||
+ // CraftBukkit start
|
||||
+ protected void parseSelector(boolean overridePermissions) throws CommandSyntaxException {
|
||||
+ this.usesSelectors = !overridePermissions;
|
||||
+ // CraftBukkit end
|
||||
this.suggestions = this::suggestSelector;
|
||||
if (!this.reader.canRead()) {
|
||||
throw EntitySelectorParser.ERROR_MISSING_SELECTOR_TYPE.createWithContext(this.reader);
|
||||
@@ -505,6 +507,12 @@
|
||||
}
|
||||
|
||||
public EntitySelector parse() throws CommandSyntaxException {
|
||||
+ // CraftBukkit start
|
||||
+ return this.parse(false);
|
||||
+ }
|
||||
+
|
||||
+ public EntitySelector parse(boolean overridePermissions) throws CommandSyntaxException {
|
||||
+ // CraftBukkit end
|
||||
this.startPosition = this.reader.getCursor();
|
||||
this.suggestions = this::suggestNameOrSelector;
|
||||
if (this.reader.canRead() && this.reader.peek() == '@') {
|
||||
@@ -513,7 +521,7 @@
|
||||
}
|
||||
|
||||
this.reader.skip();
|
||||
- this.parseSelector();
|
||||
+ this.parseSelector(overridePermissions); // CraftBukkit
|
||||
} else {
|
||||
this.parseNameOrUUID();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/commands/arguments/selector/SelectorPattern.java
|
||||
+++ b/net/minecraft/commands/arguments/selector/SelectorPattern.java
|
||||
@@ -13,7 +13,7 @@
|
||||
EntitySelectorParser entitySelectorParser = new EntitySelectorParser(new StringReader(selector), true);
|
||||
return DataResult.success(new SelectorPattern(selector, entitySelectorParser.parse()));
|
||||
} catch (CommandSyntaxException var2) {
|
||||
- return DataResult.error(() -> "Invalid selector component: " + selector + ": " + var2.getMessage());
|
||||
+ return DataResult.error(() -> "Invalid selector component"); // Paper - limit selector error message
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
--- a/net/minecraft/core/BlockPos.java
|
||||
+++ b/net/minecraft/core/BlockPos.java
|
||||
@@ -158,67 +158,84 @@
|
||||
|
||||
@Override
|
||||
public BlockPos above() {
|
||||
- return this.relative(Direction.UP);
|
||||
+ return new BlockPos(this.getX(), this.getY() + 1, this.getZ()); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos above(int distance) {
|
||||
- return this.relative(Direction.UP, distance);
|
||||
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() + distance, this.getZ()); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos below() {
|
||||
- return this.relative(Direction.DOWN);
|
||||
+ return new BlockPos(this.getX(), this.getY() - 1, this.getZ()); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos below(int i) {
|
||||
- return this.relative(Direction.DOWN, i);
|
||||
+ return i == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() - i, this.getZ()); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos north() {
|
||||
- return this.relative(Direction.NORTH);
|
||||
+ return new BlockPos(this.getX(), this.getY(), this.getZ() - 1); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos north(int distance) {
|
||||
- return this.relative(Direction.NORTH, distance);
|
||||
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() - distance); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos south() {
|
||||
- return this.relative(Direction.SOUTH);
|
||||
+ return new BlockPos(this.getX(), this.getY(), this.getZ() + 1); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos south(int distance) {
|
||||
- return this.relative(Direction.SOUTH, distance);
|
||||
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() + distance); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos west() {
|
||||
- return this.relative(Direction.WEST);
|
||||
+ return new BlockPos(this.getX() - 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos west(int distance) {
|
||||
- return this.relative(Direction.WEST, distance);
|
||||
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX() - distance, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos east() {
|
||||
- return this.relative(Direction.EAST);
|
||||
+ return new BlockPos(this.getX() + 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos east(int distance) {
|
||||
- return this.relative(Direction.EAST, distance);
|
||||
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX() + distance, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos relative(Direction direction) {
|
||||
+ // Paper start - Perf: Optimize BlockPosition
|
||||
+ switch(direction) {
|
||||
+ case UP:
|
||||
+ return new BlockPos(this.getX(), this.getY() + 1, this.getZ());
|
||||
+ case DOWN:
|
||||
+ return new BlockPos(this.getX(), this.getY() - 1, this.getZ());
|
||||
+ case NORTH:
|
||||
+ return new BlockPos(this.getX(), this.getY(), this.getZ() - 1);
|
||||
+ case SOUTH:
|
||||
+ return new BlockPos(this.getX(), this.getY(), this.getZ() + 1);
|
||||
+ case WEST:
|
||||
+ return new BlockPos(this.getX() - 1, this.getY(), this.getZ());
|
||||
+ case EAST:
|
||||
+ return new BlockPos(this.getX() + 1, this.getY(), this.getZ());
|
||||
+ default:
|
||||
return new BlockPos(this.getX() + direction.getStepX(), this.getY() + direction.getStepY(), this.getZ() + direction.getStepZ());
|
||||
+ }
|
||||
+ // Paper end - Perf: Optimize BlockPosition
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -324,9 +341,11 @@
|
||||
|
||||
public static Iterable<BlockPos> withinManhattan(BlockPos center, int rangeX, int rangeY, int rangeZ) {
|
||||
int i = rangeX + rangeY + rangeZ;
|
||||
- int j = center.getX();
|
||||
- int k = center.getY();
|
||||
- int l = center.getZ();
|
||||
+ // Paper start - rename variables to fix conflict with anonymous class (remap fix)
|
||||
+ int centerX = center.getX();
|
||||
+ int centerY = center.getY();
|
||||
+ int centerZ = center.getZ();
|
||||
+ // Paper end
|
||||
return () -> new AbstractIterator<BlockPos>() {
|
||||
private final BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
|
||||
private int currentDepth;
|
||||
@@ -340,7 +359,7 @@
|
||||
protected BlockPos computeNext() {
|
||||
if (this.zMirror) {
|
||||
this.zMirror = false;
|
||||
- this.cursor.setZ(l - (this.cursor.getZ() - l));
|
||||
+ this.cursor.setZ(centerZ - (this.cursor.getZ() - centerZ)); // Paper - remap fix
|
||||
return this.cursor;
|
||||
} else {
|
||||
BlockPos blockPos;
|
||||
@@ -366,7 +385,7 @@
|
||||
int k = this.currentDepth - Math.abs(i) - Math.abs(j);
|
||||
if (k <= rangeZ) {
|
||||
this.zMirror = k != 0;
|
||||
- blockPos = this.cursor.set(j + i, k + j, l + k);
|
||||
+ blockPos = this.cursor.set(centerX + i, centerY + j, centerZ + k); // Paper - remap fix
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,12 +463,12 @@
|
||||
if (this.index == l) {
|
||||
return this.endOfData();
|
||||
} else {
|
||||
- int i = this.index % i;
|
||||
- int j = this.index / i;
|
||||
- int k = j % j;
|
||||
- int l = j / j;
|
||||
+ int offsetX = this.index % i; // Paper - decomp fix
|
||||
+ int u = this.index / i; // Paper - decomp fix
|
||||
+ int offsetY = u % j; // Paper - decomp fix
|
||||
+ int offsetZ = u / j; // Paper - decomp fix
|
||||
this.index++;
|
||||
- return this.cursor.set(startX + i, startY + k, startZ + l);
|
||||
+ return this.cursor.set(startX + offsetX, startY + offsetY, startZ + offsetZ); // Paper - decomp fix
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -569,9 +588,9 @@
|
||||
}
|
||||
|
||||
public BlockPos.MutableBlockPos set(int x, int y, int z) {
|
||||
- this.setX(x);
|
||||
- this.setY(y);
|
||||
- this.setZ(z);
|
||||
+ this.x = x; // Paper - Perf: Manually inline methods in BlockPosition
|
||||
+ this.y = y; // Paper - Perf: Manually inline methods in BlockPosition
|
||||
+ this.z = z; // Paper - Perf: Manually inline methods in BlockPosition
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -636,19 +655,19 @@
|
||||
|
||||
@Override
|
||||
public BlockPos.MutableBlockPos setX(int i) {
|
||||
- super.setX(i);
|
||||
+ this.x = i; // Paper - Perf: Manually inline methods in BlockPosition
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos.MutableBlockPos setY(int i) {
|
||||
- super.setY(i);
|
||||
+ this.y = i; // Paper - Perf: Manually inline methods in BlockPosition
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos.MutableBlockPos setZ(int i) {
|
||||
- super.setZ(i);
|
||||
+ this.z = i; // Paper - Perf: Manually inline methods in BlockPosition
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
--- a/net/minecraft/core/Direction.java
|
||||
+++ b/net/minecraft/core/Direction.java
|
||||
@@ -57,6 +57,12 @@
|
||||
.sorted(Comparator.comparingInt(direction -> direction.data2d))
|
||||
.toArray(Direction[]::new);
|
||||
|
||||
+ // Paper start - Perf: Inline shift direction fields
|
||||
+ private final int adjX;
|
||||
+ private final int adjY;
|
||||
+ private final int adjZ;
|
||||
+ // Paper end - Perf: Inline shift direction fields
|
||||
+
|
||||
private Direction(
|
||||
final int id,
|
||||
final int idOpposite,
|
||||
@@ -74,6 +80,11 @@
|
||||
this.axisDirection = direction;
|
||||
this.normal = vector;
|
||||
this.normalVec3 = Vec3.atLowerCornerOf(vector);
|
||||
+ // Paper start - Perf: Inline shift direction fields
|
||||
+ this.adjX = vector.getX();
|
||||
+ this.adjY = vector.getY();
|
||||
+ this.adjZ = vector.getZ();
|
||||
+ // Paper end - Perf: Inline shift direction fields
|
||||
}
|
||||
|
||||
public static Direction[] orderedByNearest(Entity entity) {
|
||||
@@ -247,15 +258,15 @@
|
||||
}
|
||||
|
||||
public int getStepX() {
|
||||
- return this.normal.getX();
|
||||
+ return this.adjX; // Paper - Perf: Inline shift direction fields
|
||||
}
|
||||
|
||||
public int getStepY() {
|
||||
- return this.normal.getY();
|
||||
+ return this.adjY; // Paper - Perf: Inline shift direction fields
|
||||
}
|
||||
|
||||
public int getStepZ() {
|
||||
- return this.normal.getZ();
|
||||
+ return this.adjZ; // Paper - Perf: Inline shift direction fields
|
||||
}
|
||||
|
||||
public Vector3f step() {
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/core/Holder.java
|
||||
+++ b/net/minecraft/core/Holder.java
|
||||
@@ -230,7 +230,7 @@
|
||||
}
|
||||
|
||||
void bindTags(Collection<TagKey<T>> tags) {
|
||||
- this.tags = Set.copyOf(tags);
|
||||
+ this.tags = java.util.Collections.unmodifiableSet(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(tags)); // Paper
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,33 @@
|
||||
--- a/net/minecraft/core/MappedRegistry.java
|
||||
+++ b/net/minecraft/core/MappedRegistry.java
|
||||
@@ -33,11 +33,11 @@
|
||||
public class MappedRegistry<T> implements WritableRegistry<T> {
|
||||
private final ResourceKey<? extends Registry<T>> key;
|
||||
private final ObjectList<Holder.Reference<T>> byId = new ObjectArrayList<>(256);
|
||||
- private final Reference2IntMap<T> toId = Util.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1));
|
||||
- private final Map<ResourceLocation, Holder.Reference<T>> byLocation = new HashMap<>();
|
||||
- private final Map<ResourceKey<T>, Holder.Reference<T>> byKey = new HashMap<>();
|
||||
- private final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>();
|
||||
- private final Map<ResourceKey<T>, RegistrationInfo> registrationInfos = new IdentityHashMap<>();
|
||||
+ private final Reference2IntMap<T> toId = Util.make(new Reference2IntOpenHashMap<>(2048), map -> map.defaultReturnValue(-1)); // Paper - Perf: Use bigger expected size to reduce collisions
|
||||
+ private final Map<ResourceLocation, Holder.Reference<T>> byLocation = new HashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
|
||||
+ private final Map<ResourceKey<T>, Holder.Reference<T>> byKey = new HashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
|
||||
+ private final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
|
||||
+ private final Map<ResourceKey<T>, RegistrationInfo> registrationInfos = new IdentityHashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
|
||||
private Lifecycle registryLifecycle;
|
||||
private final Map<TagKey<T>, HolderSet.Named<T>> frozenTags = new IdentityHashMap<>();
|
||||
MappedRegistry.TagSet<T> allTags = MappedRegistry.TagSet.unbound();
|
||||
@@ -508,5 +508,13 @@
|
||||
void forEach(BiConsumer<? super TagKey<T>, ? super HolderSet.Named<T>> consumer);
|
||||
|
||||
Stream<HolderSet.Named<T>> getTags();
|
||||
+ }
|
||||
+ // Paper start
|
||||
+ // used to clear intrusive holders from GameEvent, Item, Block, EntityType, and Fluid from unused instances of those types
|
||||
+ public void clearIntrusiveHolder(final T instance) {
|
||||
+ if (this.unregisteredIntrusiveHolders != null) {
|
||||
+ this.unregisteredIntrusiveHolders.remove(instance);
|
||||
+ }
|
||||
}
|
||||
+ // Paper end
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
--- a/net/minecraft/core/Rotations.java
|
||||
+++ b/net/minecraft/core/Rotations.java
|
||||
@@ -34,6 +34,18 @@
|
||||
this(serialized.getFloat(0), serialized.getFloat(1), serialized.getFloat(2));
|
||||
}
|
||||
|
||||
+ // Paper start - faster alternative constructor
|
||||
+ private Rotations(float x, float y, float z, Void dummy_var) {
|
||||
+ this.x = x;
|
||||
+ this.y = y;
|
||||
+ this.z = z;
|
||||
+ }
|
||||
+
|
||||
+ public static Rotations createWithoutValidityChecks(float x, float y, float z) {
|
||||
+ return new Rotations(x, y, z, null);
|
||||
+ }
|
||||
+ // Paper end - faster alternative constructor
|
||||
+
|
||||
public ListTag save() {
|
||||
ListTag listTag = new ListTag();
|
||||
listTag.add(FloatTag.valueOf(this.x));
|
||||
@@ -0,0 +1,49 @@
|
||||
--- a/net/minecraft/core/Vec3i.java
|
||||
+++ b/net/minecraft/core/Vec3i.java
|
||||
@@ -16,9 +16,9 @@
|
||||
vec -> IntStream.of(vec.getX(), vec.getY(), vec.getZ())
|
||||
);
|
||||
public static final Vec3i ZERO = new Vec3i(0, 0, 0);
|
||||
- private int x;
|
||||
- private int y;
|
||||
- private int z;
|
||||
+ protected int x; // Paper - Perf: Manually inline methods in BlockPosition; protected
|
||||
+ protected int y; // Paper - Perf: Manually inline methods in BlockPosition; protected
|
||||
+ protected int z; // Paper - Perf: Manually inline methods in BlockPosition; protected
|
||||
|
||||
public static Codec<Vec3i> offsetCodec(int maxAbsValue) {
|
||||
return CODEC.validate(
|
||||
@@ -35,12 +35,12 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
- public boolean equals(Object object) {
|
||||
+ public final boolean equals(Object object) { // Paper - Perf: Final for inline
|
||||
return this == object || object instanceof Vec3i vec3i && this.getX() == vec3i.getX() && this.getY() == vec3i.getY() && this.getZ() == vec3i.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
- public int hashCode() {
|
||||
+ public final int hashCode() { // Paper - Perf: Final for inline
|
||||
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
|
||||
}
|
||||
|
||||
@@ -53,15 +53,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
- public int getX() {
|
||||
+ public final int getX() { // Paper - Perf: Final for inline
|
||||
return this.x;
|
||||
}
|
||||
|
||||
- public int getY() {
|
||||
+ public final int getY() { // Paper - Perf: Final for inline
|
||||
return this.y;
|
||||
}
|
||||
|
||||
- public int getZ() {
|
||||
+ public final int getZ() { // Paper - Perf: Final for inline
|
||||
return this.z;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
--- a/net/minecraft/core/cauldron/CauldronInteraction.java
|
||||
+++ b/net/minecraft/core/cauldron/CauldronInteraction.java
|
||||
@@ -35,20 +35,25 @@
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.event.block.CauldronLevelChangeEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public interface CauldronInteraction {
|
||||
|
||||
Map<String, CauldronInteraction.InteractionMap> INTERACTIONS = new Object2ObjectArrayMap();
|
||||
- Codec<CauldronInteraction.InteractionMap> CODEC;
|
||||
- CauldronInteraction.InteractionMap EMPTY;
|
||||
- CauldronInteraction.InteractionMap WATER;
|
||||
- CauldronInteraction.InteractionMap LAVA;
|
||||
- CauldronInteraction.InteractionMap POWDER_SNOW;
|
||||
+ // CraftBukkit start - decompile errors
|
||||
+ Codec<CauldronInteraction.InteractionMap> CODEC = Codec.stringResolver(CauldronInteraction.InteractionMap::name, CauldronInteraction.INTERACTIONS::get);
|
||||
+ CauldronInteraction.InteractionMap EMPTY = CauldronInteraction.newInteractionMap("empty");
|
||||
+ CauldronInteraction.InteractionMap WATER = CauldronInteraction.newInteractionMap("water");
|
||||
+ CauldronInteraction.InteractionMap LAVA = CauldronInteraction.newInteractionMap("lava");
|
||||
+ CauldronInteraction.InteractionMap POWDER_SNOW = CauldronInteraction.newInteractionMap("powder_snow");
|
||||
+ // CraftBukkit end
|
||||
|
||||
static CauldronInteraction.InteractionMap newInteractionMap(String name) {
|
||||
Object2ObjectOpenHashMap<Item, CauldronInteraction> object2objectopenhashmap = new Object2ObjectOpenHashMap();
|
||||
|
||||
- object2objectopenhashmap.defaultReturnValue((iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
|
||||
+ object2objectopenhashmap.defaultReturnValue((iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
|
||||
return InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
});
|
||||
CauldronInteraction.InteractionMap cauldroninteraction_a = new CauldronInteraction.InteractionMap(name, object2objectopenhashmap);
|
||||
@@ -57,23 +62,28 @@
|
||||
return cauldroninteraction_a;
|
||||
}
|
||||
|
||||
- InteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack);
|
||||
+ InteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection); // Paper - add hitDirection
|
||||
|
||||
static void bootStrap() {
|
||||
Map<Item, CauldronInteraction> map = CauldronInteraction.EMPTY.map();
|
||||
|
||||
CauldronInteraction.addDefaultInteractions(map);
|
||||
- map.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
|
||||
+ map.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
|
||||
PotionContents potioncontents = (PotionContents) itemstack.get(DataComponents.POTION_CONTENTS);
|
||||
|
||||
if (potioncontents != null && potioncontents.is(Potions.WATER)) {
|
||||
if (!world.isClientSide) {
|
||||
+ // CraftBukkit start
|
||||
+ if (!LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, Blocks.WATER_CAULDRON.defaultBlockState(), entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
entityhuman.setItemInHand(enumhand, ItemUtils.createFilledResult(itemstack, entityhuman, new ItemStack(Items.GLASS_BOTTLE)));
|
||||
entityhuman.awardStat(Stats.USE_CAULDRON);
|
||||
entityhuman.awardStat(Stats.ITEM_USED.get(item));
|
||||
- world.setBlockAndUpdate(blockposition, Blocks.WATER_CAULDRON.defaultBlockState());
|
||||
+ // world.setBlockAndUpdate(blockposition, Blocks.WATER_CAULDRON.defaultBlockState()); // CraftBukkit
|
||||
world.playSound((Player) null, blockposition, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PLACE, blockposition);
|
||||
}
|
||||
@@ -86,26 +96,31 @@
|
||||
Map<Item, CauldronInteraction> map1 = CauldronInteraction.WATER.map();
|
||||
|
||||
CauldronInteraction.addDefaultInteractions(map1);
|
||||
- map1.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
|
||||
+ map1.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
|
||||
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.WATER_BUCKET), (iblockdata1) -> {
|
||||
return (Integer) iblockdata1.getValue(LayeredCauldronBlock.LEVEL) == 3;
|
||||
- }, SoundEvents.BUCKET_FILL);
|
||||
+ }, SoundEvents.BUCKET_FILL, hitDirection); // Paper - add hitDirection
|
||||
});
|
||||
- map1.put(Items.GLASS_BOTTLE, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
|
||||
+ map1.put(Items.GLASS_BOTTLE, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
|
||||
if (!world.isClientSide) {
|
||||
+ // CraftBukkit start
|
||||
+ if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) {
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
entityhuman.setItemInHand(enumhand, ItemUtils.createFilledResult(itemstack, entityhuman, PotionContents.createItemStack(Items.POTION, Potions.WATER)));
|
||||
entityhuman.awardStat(Stats.USE_CAULDRON);
|
||||
entityhuman.awardStat(Stats.ITEM_USED.get(item));
|
||||
- LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition);
|
||||
+ // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
|
||||
world.playSound((Player) null, blockposition, SoundEvents.BOTTLE_FILL, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PICKUP, blockposition);
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
});
|
||||
- map1.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
|
||||
+ map1.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
|
||||
if ((Integer) iblockdata.getValue(LayeredCauldronBlock.LEVEL) == 3) {
|
||||
return InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} else {
|
||||
@@ -113,10 +128,15 @@
|
||||
|
||||
if (potioncontents != null && potioncontents.is(Potions.WATER)) {
|
||||
if (!world.isClientSide) {
|
||||
+ // CraftBukkit start
|
||||
+ if (!LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, iblockdata.cycle(LayeredCauldronBlock.LEVEL), entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
entityhuman.setItemInHand(enumhand, ItemUtils.createFilledResult(itemstack, entityhuman, new ItemStack(Items.GLASS_BOTTLE)));
|
||||
entityhuman.awardStat(Stats.USE_CAULDRON);
|
||||
entityhuman.awardStat(Stats.ITEM_USED.get(itemstack.getItem()));
|
||||
- world.setBlockAndUpdate(blockposition, (BlockState) iblockdata.cycle(LayeredCauldronBlock.LEVEL));
|
||||
+ // world.setBlockAndUpdate(blockposition, (IBlockData) iblockdata.cycle(LayeredCauldronBlock.LEVEL)); // CraftBukkit
|
||||
world.playSound((Player) null, blockposition, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PLACE, blockposition);
|
||||
}
|
||||
@@ -167,18 +187,18 @@
|
||||
map1.put(Items.YELLOW_SHULKER_BOX, CauldronInteraction::shulkerBoxInteraction);
|
||||
Map<Item, CauldronInteraction> map2 = CauldronInteraction.LAVA.map();
|
||||
|
||||
- map2.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
|
||||
+ map2.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
|
||||
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.LAVA_BUCKET), (iblockdata1) -> {
|
||||
return true;
|
||||
- }, SoundEvents.BUCKET_FILL_LAVA);
|
||||
+ }, SoundEvents.BUCKET_FILL_LAVA, hitDirection); // Paper - add hitDirection
|
||||
});
|
||||
CauldronInteraction.addDefaultInteractions(map2);
|
||||
Map<Item, CauldronInteraction> map3 = CauldronInteraction.POWDER_SNOW.map();
|
||||
|
||||
- map3.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
|
||||
+ map3.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
|
||||
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.POWDER_SNOW_BUCKET), (iblockdata1) -> {
|
||||
return (Integer) iblockdata1.getValue(LayeredCauldronBlock.LEVEL) == 3;
|
||||
- }, SoundEvents.BUCKET_FILL_POWDER_SNOW);
|
||||
+ }, SoundEvents.BUCKET_FILL_POWDER_SNOW, hitDirection); // Paper - add hitDirection
|
||||
});
|
||||
CauldronInteraction.addDefaultInteractions(map3);
|
||||
}
|
||||
@@ -190,16 +210,35 @@
|
||||
}
|
||||
|
||||
static InteractionResult fillBucket(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, ItemStack output, Predicate<BlockState> fullPredicate, SoundEvent soundEvent) {
|
||||
+ // Paper start - add hitDirection
|
||||
+ return fillBucket(state, world, pos, player, hand, stack, output, fullPredicate, soundEvent, null); // Paper - add hitDirection
|
||||
+ }
|
||||
+ static InteractionResult fillBucket(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, ItemStack output, Predicate<BlockState> fullPredicate, SoundEvent soundEvent, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) {
|
||||
+ // Paper end - add hitDirection
|
||||
if (!fullPredicate.test(state)) {
|
||||
return InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} else {
|
||||
if (!world.isClientSide) {
|
||||
+ // Paper start - fire PlayerBucketFillEvent
|
||||
+ if (hitDirection != null) {
|
||||
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent((net.minecraft.server.level.ServerLevel) world, player, pos, pos, hitDirection, stack, output.getItem(), hand);
|
||||
+ if (event.isCancelled()) {
|
||||
+ return InteractionResult.PASS;
|
||||
+ }
|
||||
+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
|
||||
+ }
|
||||
+ // Paper end - fire PlayerBucketFillEvent
|
||||
+ // CraftBukkit start
|
||||
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.CAULDRON.defaultBlockState(), player, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL, false)) { // Paper - Call CauldronLevelChangeEvent
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
Item item = stack.getItem();
|
||||
|
||||
player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, output));
|
||||
player.awardStat(Stats.USE_CAULDRON);
|
||||
player.awardStat(Stats.ITEM_USED.get(item));
|
||||
- world.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState());
|
||||
+ // world.setBlockAndUpdate(blockposition, Blocks.CAULDRON.defaultBlockState()); // CraftBukkit
|
||||
world.playSound((Player) null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PICKUP, pos);
|
||||
}
|
||||
@@ -209,13 +248,33 @@
|
||||
}
|
||||
|
||||
static InteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent) {
|
||||
+ // Paper start - add hitDirection
|
||||
+ return emptyBucket(world, pos, player, hand, stack, state, soundEvent, null);
|
||||
+ }
|
||||
+ static InteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) {
|
||||
+ // Paper end - add hitDirection
|
||||
if (!world.isClientSide) {
|
||||
+ // Paper start - fire PlayerBucketEmptyEvent
|
||||
+ ItemStack output = new ItemStack(Items.BUCKET);
|
||||
+ if (hitDirection != null) {
|
||||
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent((net.minecraft.server.level.ServerLevel) world, player, pos, pos, hitDirection, stack, hand);
|
||||
+ if (event.isCancelled()) {
|
||||
+ return InteractionResult.PASS;
|
||||
+ }
|
||||
+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
|
||||
+ }
|
||||
+ // Paper end - fire PlayerBucketEmptyEvent
|
||||
+ // CraftBukkit start
|
||||
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, state, player, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
Item item = stack.getItem();
|
||||
|
||||
- player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, new ItemStack(Items.BUCKET)));
|
||||
+ player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, output)); // Paper
|
||||
player.awardStat(Stats.FILL_CAULDRON);
|
||||
player.awardStat(Stats.ITEM_USED.get(item));
|
||||
- world.setBlockAndUpdate(pos, state);
|
||||
+ // world.setBlockAndUpdate(blockposition, iblockdata); // CraftBukkit
|
||||
world.playSound((Player) null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PLACE, pos);
|
||||
}
|
||||
@@ -223,65 +282,79 @@
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
- private static InteractionResult fillWaterInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
- return CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY);
|
||||
+ private static InteractionResult fillWaterInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
+ return CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY, hitDirection); // Paper - add hitDirection
|
||||
}
|
||||
|
||||
- private static InteractionResult fillLavaInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
- return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA));
|
||||
+ private static InteractionResult fillLavaInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
+ return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection)); // Paper - add hitDirection
|
||||
}
|
||||
|
||||
- private static InteractionResult fillPowderSnowInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
- return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW));
|
||||
+ private static InteractionResult fillPowderSnowInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
+ return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW, hitDirection)); // Paper - add hitDirection
|
||||
}
|
||||
|
||||
- private static InteractionResult shulkerBoxInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
+ private static InteractionResult shulkerBoxInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
Block block = Block.byItem(stack.getItem());
|
||||
|
||||
if (!(block instanceof ShulkerBoxBlock)) {
|
||||
return InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} else {
|
||||
if (!world.isClientSide) {
|
||||
+ // CraftBukkit start
|
||||
+ if (!LayeredCauldronBlock.lowerFillLevel(state, world, pos, player, CauldronLevelChangeEvent.ChangeReason.SHULKER_WASH)) {
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
ItemStack itemstack1 = stack.transmuteCopy(Blocks.SHULKER_BOX, 1);
|
||||
|
||||
player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, itemstack1, false));
|
||||
player.awardStat(Stats.CLEAN_SHULKER_BOX);
|
||||
- LayeredCauldronBlock.lowerFillLevel(state, world, pos);
|
||||
+ // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
|
||||
}
|
||||
-
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
- private static InteractionResult bannerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
+ private static InteractionResult bannerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
BannerPatternLayers bannerpatternlayers = (BannerPatternLayers) stack.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY);
|
||||
|
||||
if (bannerpatternlayers.layers().isEmpty()) {
|
||||
return InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} else {
|
||||
if (!world.isClientSide) {
|
||||
+ // CraftBukkit start
|
||||
+ if (!LayeredCauldronBlock.lowerFillLevel(state, world, pos, player, CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) {
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
ItemStack itemstack1 = stack.copyWithCount(1);
|
||||
|
||||
itemstack1.set(DataComponents.BANNER_PATTERNS, bannerpatternlayers.removeLast());
|
||||
player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, itemstack1, false));
|
||||
player.awardStat(Stats.CLEAN_BANNER);
|
||||
- LayeredCauldronBlock.lowerFillLevel(state, world, pos);
|
||||
+ // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
- private static InteractionResult dyedItemIteration(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
+ private static InteractionResult dyedItemIteration(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
if (!stack.is(ItemTags.DYEABLE)) {
|
||||
return InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} else if (!stack.has(DataComponents.DYED_COLOR)) {
|
||||
return InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} else {
|
||||
if (!world.isClientSide) {
|
||||
+ // CraftBukkit start
|
||||
+ if (!LayeredCauldronBlock.lowerFillLevel(state, world, pos, player, CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) {
|
||||
+ return InteractionResult.SUCCESS;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
stack.remove(DataComponents.DYED_COLOR);
|
||||
player.awardStat(Stats.CLEAN_ARMOR);
|
||||
- LayeredCauldronBlock.lowerFillLevel(state, world, pos);
|
||||
+ // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
@@ -294,8 +367,10 @@
|
||||
return fluid.is(FluidTags.WATER);
|
||||
}
|
||||
|
||||
+ // CraftBukkit start - decompile errors
|
||||
+ /*
|
||||
static {
|
||||
- Function function = CauldronInteraction.InteractionMap::name;
|
||||
+ Function function = CauldronInteraction.a::name;
|
||||
Map map = CauldronInteraction.INTERACTIONS;
|
||||
|
||||
Objects.requireNonNull(map);
|
||||
@@ -305,6 +380,8 @@
|
||||
LAVA = newInteractionMap("lava");
|
||||
POWDER_SNOW = newInteractionMap("powder_snow");
|
||||
}
|
||||
+ */
|
||||
+ // CraftBukkit end
|
||||
|
||||
public static record InteractionMap(String name, Map<Item, CauldronInteraction> map) {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
--- a/net/minecraft/core/component/DataComponentPatch.java
|
||||
+++ b/net/minecraft/core/component/DataComponentPatch.java
|
||||
@@ -61,7 +61,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- return reference2objectmap;
|
||||
+ return (Reference2ObjectMap) reference2objectmap; // CraftBukkit - decompile error
|
||||
});
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, DataComponentPatch> STREAM_CODEC = new StreamCodec<RegistryFriendlyByteBuf, DataComponentPatch>() {
|
||||
public DataComponentPatch decode(RegistryFriendlyByteBuf registryfriendlybytebuf) {
|
||||
@@ -144,7 +144,13 @@
|
||||
}
|
||||
|
||||
private static <T> void encodeComponent(RegistryFriendlyByteBuf buf, DataComponentType<T> type, Object value) {
|
||||
- type.streamCodec().encode(buf, value);
|
||||
+ // Paper start - codec errors of random anonymous classes are useless
|
||||
+ try {
|
||||
+ type.streamCodec().encode(buf, (T) value); // CraftBukkit - decompile error
|
||||
+ } catch (final Exception e) {
|
||||
+ throw new RuntimeException("Error encoding component " + type, e);
|
||||
+ }
|
||||
+ // Paper end - codec errors of random anonymous classes are useless
|
||||
}
|
||||
};
|
||||
private static final String REMOVED_PREFIX = "!";
|
||||
@@ -270,7 +276,43 @@
|
||||
private final Reference2ObjectMap<DataComponentType<?>, Optional<?>> map = new Reference2ObjectArrayMap();
|
||||
|
||||
Builder() {}
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ public void copy(DataComponentPatch orig) {
|
||||
+ this.map.putAll(orig.map);
|
||||
+ }
|
||||
+
|
||||
+ public void clear(DataComponentType<?> type) {
|
||||
+ this.map.remove(type);
|
||||
+ }
|
||||
+
|
||||
+ public boolean isSet(DataComponentType<?> type) {
|
||||
+ return this.map.containsKey(type);
|
||||
+ }
|
||||
+
|
||||
+ public boolean isEmpty() {
|
||||
+ return this.map.isEmpty();
|
||||
+ }
|
||||
|
||||
+ @Override
|
||||
+ public boolean equals(Object object) {
|
||||
+ if (this == object) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ if (object instanceof DataComponentPatch.Builder patch) {
|
||||
+ return this.map.equals(patch.map);
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int hashCode() {
|
||||
+ return this.map.hashCode();
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
public <T> DataComponentPatch.Builder set(DataComponentType<T> type, T value) {
|
||||
this.map.put(type, Optional.of(value));
|
||||
return this;
|
||||
@@ -0,0 +1,24 @@
|
||||
--- a/net/minecraft/core/component/DataComponents.java
|
||||
+++ b/net/minecraft/core/component/DataComponents.java
|
||||
@@ -180,10 +180,10 @@
|
||||
"map_post_processing", builder -> builder.networkSynchronized(MapPostProcessing.STREAM_CODEC)
|
||||
);
|
||||
public static final DataComponentType<ChargedProjectiles> CHARGED_PROJECTILES = register(
|
||||
- "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(ChargedProjectiles.STREAM_CODEC).cacheEncoding()
|
||||
+ "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.CHARGED_PROJECTILES).cacheEncoding() // Paper - sanitize charged projectiles
|
||||
);
|
||||
public static final DataComponentType<BundleContents> BUNDLE_CONTENTS = register(
|
||||
- "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(BundleContents.STREAM_CODEC).cacheEncoding()
|
||||
+ "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.BUNDLE_CONTENTS).cacheEncoding() // Paper - sanitize bundle contents
|
||||
);
|
||||
public static final DataComponentType<PotionContents> POTION_CONTENTS = register(
|
||||
"potion_contents", builder -> builder.persistent(PotionContents.CODEC).networkSynchronized(PotionContents.STREAM_CODEC).cacheEncoding()
|
||||
@@ -250,7 +250,7 @@
|
||||
"pot_decorations", builder -> builder.persistent(PotDecorations.CODEC).networkSynchronized(PotDecorations.STREAM_CODEC).cacheEncoding()
|
||||
);
|
||||
public static final DataComponentType<ItemContainerContents> CONTAINER = register(
|
||||
- "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(ItemContainerContents.STREAM_CODEC).cacheEncoding()
|
||||
+ "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.CONTAINER).cacheEncoding() // Paper - sanitize container contents
|
||||
);
|
||||
public static final DataComponentType<BlockItemStateProperties> BLOCK_STATE = register(
|
||||
"block_state", builder -> builder.persistent(BlockItemStateProperties.CODEC).networkSynchronized(BlockItemStateProperties.STREAM_CODEC).cacheEncoding()
|
||||
@@ -0,0 +1,58 @@
|
||||
--- a/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
|
||||
@@ -11,6 +11,11 @@
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.event.block.BlockDispenseEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
|
||||
|
||||
@@ -43,14 +48,40 @@
|
||||
d4 = 0.0D;
|
||||
}
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink at end and single item in event
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d1, d2 + d4, d3));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
AbstractBoat abstractboat = (AbstractBoat) this.type.create(worldserver, EntitySpawnReason.DISPENSER);
|
||||
|
||||
if (abstractboat != null) {
|
||||
- abstractboat.setInitialPos(d1, d2 + d4, d3);
|
||||
+ abstractboat.setInitialPos(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); // CraftBukkit
|
||||
EntityType.createDefaultStackConfig(worldserver, stack, (Player) null).accept(abstractboat);
|
||||
abstractboat.setYRot(enumdirection.toYRot());
|
||||
- worldserver.addFreshEntity(abstractboat);
|
||||
- stack.shrink(1);
|
||||
+ if (worldserver.addFreshEntity(abstractboat) && shrink) stack.shrink(1); // Paper - if entity add was successful and supposed to shrink
|
||||
}
|
||||
|
||||
return stack;
|
||||
@@ -0,0 +1,126 @@
|
||||
--- a/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
|
||||
@@ -6,47 +6,114 @@
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.craftbukkit.util.CraftVector;
|
||||
+import org.bukkit.event.block.BlockDispenseEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public class DefaultDispenseItemBehavior implements DispenseItemBehavior {
|
||||
+ private Direction enumdirection; // Paper - cache facing direction
|
||||
|
||||
private static final int DEFAULT_ACCURACY = 6;
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ private boolean dropper;
|
||||
+
|
||||
+ public DefaultDispenseItemBehavior(boolean dropper) {
|
||||
+ this.dropper = dropper;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
public DefaultDispenseItemBehavior() {}
|
||||
|
||||
@Override
|
||||
public final ItemStack dispense(BlockSource pointer, ItemStack stack) {
|
||||
+ enumdirection = pointer.state().getValue(DispenserBlock.FACING); // Paper - cache facing direction
|
||||
ItemStack itemstack1 = this.execute(pointer, stack);
|
||||
|
||||
this.playSound(pointer);
|
||||
- this.playAnimation(pointer, (Direction) pointer.state().getValue(DispenserBlock.FACING));
|
||||
+ this.playAnimation(pointer, enumdirection); // Paper - cache facing direction
|
||||
return itemstack1;
|
||||
}
|
||||
|
||||
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
|
||||
- Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
|
||||
+ // Paper - cached enum direction
|
||||
Position iposition = DispenserBlock.getDispensePosition(pointer);
|
||||
ItemStack itemstack1 = stack.split(1);
|
||||
|
||||
- DefaultDispenseItemBehavior.spawnItem(pointer.level(), itemstack1, 6, enumdirection, iposition);
|
||||
+ // CraftBukkit start
|
||||
+ if (!DefaultDispenseItemBehavior.spawnItem(pointer.level(), itemstack1, 6, enumdirection, pointer, this.dropper)) {
|
||||
+ stack.grow(1);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
return stack;
|
||||
}
|
||||
|
||||
public static void spawnItem(Level world, ItemStack stack, int speed, Direction side, Position pos) {
|
||||
- double d0 = pos.x();
|
||||
- double d1 = pos.y();
|
||||
- double d2 = pos.z();
|
||||
+ // CraftBukkit start
|
||||
+ ItemEntity entityitem = DefaultDispenseItemBehavior.prepareItem(world, stack, speed, side, pos);
|
||||
+ world.addFreshEntity(entityitem);
|
||||
+ }
|
||||
|
||||
- if (side.getAxis() == Direction.Axis.Y) {
|
||||
+ private static ItemEntity prepareItem(Level world, ItemStack itemstack, int i, Direction enumdirection, Position iposition) {
|
||||
+ // CraftBukkit end
|
||||
+ double d0 = iposition.x();
|
||||
+ double d1 = iposition.y();
|
||||
+ double d2 = iposition.z();
|
||||
+
|
||||
+ if (enumdirection.getAxis() == Direction.Axis.Y) {
|
||||
d1 -= 0.125D;
|
||||
} else {
|
||||
d1 -= 0.15625D;
|
||||
}
|
||||
|
||||
- ItemEntity entityitem = new ItemEntity(world, d0, d1, d2, stack);
|
||||
+ ItemEntity entityitem = new ItemEntity(world, d0, d1, d2, itemstack);
|
||||
double d3 = world.random.nextDouble() * 0.1D + 0.2D;
|
||||
|
||||
- entityitem.setDeltaMovement(world.random.triangle((double) side.getStepX() * d3, 0.0172275D * (double) speed), world.random.triangle(0.2D, 0.0172275D * (double) speed), world.random.triangle((double) side.getStepZ() * d3, 0.0172275D * (double) speed));
|
||||
+ entityitem.setDeltaMovement(world.random.triangle((double) enumdirection.getStepX() * d3, 0.0172275D * (double) i), world.random.triangle(0.2D, 0.0172275D * (double) i), world.random.triangle((double) enumdirection.getStepZ() * d3, 0.0172275D * (double) i));
|
||||
+ // CraftBukkit start
|
||||
+ return entityitem;
|
||||
+ }
|
||||
+
|
||||
+ // CraftBukkit - void -> boolean return, IPosition -> ISourceBlock last argument, dropper
|
||||
+ public static boolean spawnItem(Level world, ItemStack itemstack, int i, Direction enumdirection, BlockSource sourceblock, boolean dropper) {
|
||||
+ if (itemstack.isEmpty()) return true;
|
||||
+ Position iposition = DispenserBlock.getDispensePosition(sourceblock);
|
||||
+ ItemEntity entityitem = DefaultDispenseItemBehavior.prepareItem(world, itemstack, i, enumdirection, iposition);
|
||||
+
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(world, sourceblock.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), CraftVector.toBukkit(entityitem.getDeltaMovement()));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ world.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ entityitem.setItem(CraftItemStack.asNMSCopy(event.getItem()));
|
||||
+ entityitem.setDeltaMovement(CraftVector.toNMS(event.getVelocity()));
|
||||
+
|
||||
+ if (!dropper && !event.getItem().getType().equals(craftItem.getType())) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(sourceblock, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior.getClass() != DefaultDispenseItemBehavior.class) {
|
||||
+ idispensebehavior.dispense(sourceblock, eventStack);
|
||||
+ } else {
|
||||
+ world.addFreshEntity(entityitem);
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
world.addFreshEntity(entityitem);
|
||||
+
|
||||
+ return true;
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
protected void playSound(BlockSource pointer) {
|
||||
@@ -0,0 +1,651 @@
|
||||
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
@@ -28,6 +28,7 @@
|
||||
import net.minecraft.world.entity.item.PrimedTnt;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BoneMealItem;
|
||||
+import net.minecraft.world.item.BucketItem;
|
||||
import net.minecraft.world.item.DispensibleContainerItem;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.HoneycombItem;
|
||||
@@ -47,7 +48,9 @@
|
||||
import net.minecraft.world.level.block.CandleCakeBlock;
|
||||
import net.minecraft.world.level.block.CarvedPumpkinBlock;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
+import net.minecraft.world.level.block.LiquidBlockContainer;
|
||||
import net.minecraft.world.level.block.RespawnAnchorBlock;
|
||||
+import net.minecraft.world.level.block.SaplingBlock;
|
||||
import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||
import net.minecraft.world.level.block.SkullBlock;
|
||||
import net.minecraft.world.level.block.TntBlock;
|
||||
@@ -62,6 +65,17 @@
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.slf4j.Logger;
|
||||
+import org.bukkit.Location;
|
||||
+import org.bukkit.TreeType;
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.craftbukkit.util.CraftLocation;
|
||||
+import org.bukkit.craftbukkit.util.DummyGeneratorAccess;
|
||||
+import org.bukkit.event.block.BlockDispenseArmorEvent;
|
||||
+import org.bukkit.event.block.BlockDispenseEvent;
|
||||
+import org.bukkit.event.block.BlockFertilizeEvent;
|
||||
+import org.bukkit.event.world.StructureGrowEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public interface DispenseItemBehavior {
|
||||
|
||||
@@ -90,14 +104,47 @@
|
||||
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
|
||||
EntityType<?> entitytypes = ((SpawnEggItem) stack.getItem()).getType(pointer.level().registryAccess(), stack);
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ ServerLevel worldserver = pointer.level();
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ // Paper start - track changed items in the dispense event
|
||||
+ itemstack1 = CraftItemStack.unwrap(event.getItem()); // unwrap is safe because the stack won't be modified
|
||||
+ entitytypes = ((SpawnEggItem) itemstack1.getItem()).getType(worldserver.registryAccess(), itemstack1);
|
||||
+ // Paper end - track changed item from dispense event
|
||||
+ }
|
||||
+
|
||||
try {
|
||||
- entitytypes.spawn(pointer.level(), stack, (Player) null, pointer.pos().relative(enumdirection), EntitySpawnReason.DISPENSER, enumdirection != Direction.UP, false);
|
||||
+ entitytypes.spawn(pointer.level(), itemstack1, (Player) null, pointer.pos().relative(enumdirection), EntitySpawnReason.DISPENSER, enumdirection != Direction.UP, false); // Paper - track changed item in dispense event
|
||||
} catch (Exception exception) {
|
||||
- null.LOGGER.error("Error while dispensing spawn egg from dispenser at {}", pointer.pos(), exception);
|
||||
+ DispenseItemBehavior.LOGGER.error("Error while dispensing spawn egg from dispenser at {}", pointer.pos(), exception); // CraftBukkit - decompile error
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
- stack.shrink(1);
|
||||
+ if (shrink) stack.shrink(1); // Paper - actually handle here
|
||||
+ // CraftBukkit end
|
||||
pointer.level().gameEvent((Entity) null, (Holder) GameEvent.ENTITY_PLACE, pointer.pos());
|
||||
return stack;
|
||||
}
|
||||
@@ -116,13 +163,43 @@
|
||||
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
|
||||
BlockPos blockposition = pointer.pos().relative(enumdirection);
|
||||
ServerLevel worldserver = pointer.level();
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
+ final ItemStack newStack = CraftItemStack.unwrap(event.getItem()); // Paper - use event itemstack (unwrap is fine here because the stack won't be modified)
|
||||
Consumer<ArmorStand> consumer = EntityType.appendDefaultStackConfig((entityarmorstand) -> {
|
||||
entityarmorstand.setYRot(enumdirection.toYRot());
|
||||
- }, worldserver, stack, (Player) null);
|
||||
+ }, worldserver, newStack, (Player) null); // Paper - track changed items in the dispense event
|
||||
ArmorStand entityarmorstand = (ArmorStand) EntityType.ARMOR_STAND.spawn(worldserver, consumer, blockposition, EntitySpawnReason.DISPENSER, false, false);
|
||||
|
||||
if (entityarmorstand != null) {
|
||||
- stack.shrink(1);
|
||||
+ if (shrink) stack.shrink(1); // Paper - actually handle here
|
||||
}
|
||||
|
||||
return stack;
|
||||
@@ -141,7 +218,36 @@
|
||||
});
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
- ((Saddleable) list.get(0)).equipSaddle(stack.split(1), SoundSource.BLOCKS);
|
||||
+ // CraftBukkit start
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
|
||||
+ ServerLevel world = pointer.level();
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
+
|
||||
+ BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) list.get(0).getBukkitEntity());
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ world.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ ((Saddleable) list.get(0)).equipSaddle(CraftItemStack.asNMSCopy(event.getItem()), SoundSource.BLOCKS); // Paper - track changed items in dispense event
|
||||
+ // CraftBukkit end
|
||||
+ if (shrink) stack.shrink(1); // Paper - actually handle here
|
||||
this.setSuccess(true);
|
||||
return stack;
|
||||
} else {
|
||||
@@ -166,9 +272,38 @@
|
||||
}
|
||||
|
||||
entityhorsechestedabstract = (AbstractChestedHorse) iterator1.next();
|
||||
- } while (!entityhorsechestedabstract.isTamed() || !entityhorsechestedabstract.getSlot(499).set(stack));
|
||||
+ // CraftBukkit start
|
||||
+ } while (!entityhorsechestedabstract.isTamed());
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below
|
||||
+ ServerLevel world = pointer.level();
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
- stack.shrink(1);
|
||||
+ BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) entityhorsechestedabstract.getBukkitEntity());
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ world.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below (this was actually missing and should be here, added it commented out to be consistent)
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ entityhorsechestedabstract.getSlot(499).set(CraftItemStack.asNMSCopy(event.getItem()));
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
+ if (shrink) stack.shrink(1); // Paper - actually handle here
|
||||
this.setSuccess(true);
|
||||
return stack;
|
||||
}
|
||||
@@ -202,8 +337,50 @@
|
||||
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
|
||||
ServerLevel worldserver = pointer.level();
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ int x = blockposition.getX();
|
||||
+ int y = blockposition.getY();
|
||||
+ int z = blockposition.getZ();
|
||||
+ BlockState iblockdata = worldserver.getBlockState(blockposition);
|
||||
+ ItemStack dispensedItem = stack; // Paper - track changed item from the dispense event
|
||||
+ // Paper start - correctly check if the bucket place will succeed
|
||||
+ /* Taken from SolidBucketItem#emptyContents */
|
||||
+ boolean willEmptyContentsSolidBucketItem = dispensiblecontaineritem instanceof net.minecraft.world.item.SolidBucketItem && worldserver.isInWorldBounds(blockposition) && iblockdata.isAir();
|
||||
+ /* Taken from BucketItem#emptyContents */
|
||||
+ boolean willEmptyBucketItem = dispensiblecontaineritem instanceof final BucketItem bucketItem && bucketItem.content instanceof net.minecraft.world.level.material.FlowingFluid && (iblockdata.isAir() || iblockdata.canBeReplaced(bucketItem.content) || (iblockdata.getBlock() instanceof LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, worldserver, blockposition, iblockdata, bucketItem.content)));
|
||||
+ if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) {
|
||||
+ // Paper end - correctly check if the bucket place will succeed
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - track changed item from dispense event
|
||||
+ dispensedItem = CraftItemStack.unwrap(event.getItem()); // unwrap is safe here as the stack isn't mutated
|
||||
+ dispensiblecontaineritem = (DispensibleContainerItem) dispensedItem.getItem();
|
||||
+ // Paper end - track changed item from dispense event
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (dispensiblecontaineritem.emptyContents((Player) null, worldserver, blockposition, (BlockHitResult) null)) {
|
||||
- dispensiblecontaineritem.checkExtraContent((Player) null, worldserver, stack, blockposition);
|
||||
+ dispensiblecontaineritem.checkExtraContent((Player) null, worldserver, dispensedItem, blockposition); // Paper - track changed item from dispense event
|
||||
return this.consumeWithRemainder(pointer, stack, new ItemStack(Items.BUCKET));
|
||||
} else {
|
||||
return this.defaultDispenseItemBehavior.dispense(pointer, stack);
|
||||
@@ -229,7 +406,7 @@
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (block instanceof BucketPickup ifluidsource) {
|
||||
- ItemStack itemstack1 = ifluidsource.pickupBlock((Player) null, worldserver, blockposition, iblockdata);
|
||||
+ ItemStack itemstack1 = ifluidsource.pickupBlock((Player) null, DummyGeneratorAccess.INSTANCE, blockposition, iblockdata); // CraftBukkit
|
||||
|
||||
if (itemstack1.isEmpty()) {
|
||||
return super.execute(pointer, stack);
|
||||
@@ -237,6 +414,32 @@
|
||||
worldserver.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PICKUP, blockposition);
|
||||
Item item = itemstack1.getItem();
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ itemstack1 = ifluidsource.pickupBlock((Player) null, worldserver, blockposition, iblockdata); // From above
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
return this.consumeWithRemainder(pointer, stack, new ItemStack(item));
|
||||
}
|
||||
} else {
|
||||
@@ -249,16 +452,44 @@
|
||||
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
|
||||
ServerLevel worldserver = pointer.level();
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
this.setSuccess(true);
|
||||
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
|
||||
BlockPos blockposition = pointer.pos().relative(enumdirection);
|
||||
BlockState iblockdata = worldserver.getBlockState(blockposition);
|
||||
|
||||
if (BaseFireBlock.canBePlacedAt(worldserver, blockposition, enumdirection)) {
|
||||
- worldserver.setBlockAndUpdate(blockposition, BaseFireBlock.getState(worldserver, blockposition));
|
||||
- worldserver.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_PLACE, blockposition);
|
||||
+ // CraftBukkit start - Ignition by dispensing flint and steel
|
||||
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(worldserver, blockposition, pointer.pos()).isCancelled()) {
|
||||
+ worldserver.setBlockAndUpdate(blockposition, BaseFireBlock.getState(worldserver, blockposition));
|
||||
+ worldserver.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_PLACE, blockposition);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
} else if (!CampfireBlock.canLight(iblockdata) && !CandleBlock.canLight(iblockdata) && !CandleCakeBlock.canLight(iblockdata)) {
|
||||
- if (iblockdata.getBlock() instanceof TntBlock) {
|
||||
+ if (iblockdata.getBlock() instanceof TntBlock && org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(worldserver, blockposition, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, pointer.pos())) { // CraftBukkit - TNTPrimeEvent
|
||||
TntBlock.explode(worldserver, blockposition);
|
||||
worldserver.removeBlock(blockposition, false);
|
||||
} else {
|
||||
@@ -283,13 +514,64 @@
|
||||
this.setSuccess(true);
|
||||
ServerLevel worldserver = pointer.level();
|
||||
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
||||
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ worldserver.captureTreeGeneration = true;
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (!BoneMealItem.growCrop(stack, worldserver, blockposition) && !BoneMealItem.growWaterPlant(stack, worldserver, blockposition, (Direction) null)) {
|
||||
this.setSuccess(false);
|
||||
} else if (!worldserver.isClientSide) {
|
||||
worldserver.levelEvent(1505, blockposition, 15);
|
||||
}
|
||||
+ // CraftBukkit start
|
||||
+ worldserver.captureTreeGeneration = false;
|
||||
+ if (worldserver.capturedBlockStates.size() > 0) {
|
||||
+ TreeType treeType = SaplingBlock.treeType;
|
||||
+ SaplingBlock.treeType = null;
|
||||
+ Location location = CraftLocation.toBukkit(blockposition, worldserver.getWorld());
|
||||
+ List<org.bukkit.block.BlockState> blocks = new java.util.ArrayList<>(worldserver.capturedBlockStates.values());
|
||||
+ worldserver.capturedBlockStates.clear();
|
||||
+ StructureGrowEvent structureEvent = null;
|
||||
+ if (treeType != null) {
|
||||
+ structureEvent = new StructureGrowEvent(location, treeType, false, null, blocks);
|
||||
+ org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent);
|
||||
+ }
|
||||
|
||||
+ BlockFertilizeEvent fertilizeEvent = new BlockFertilizeEvent(location.getBlock(), null, blocks);
|
||||
+ fertilizeEvent.setCancelled(structureEvent != null && structureEvent.isCancelled());
|
||||
+ org.bukkit.Bukkit.getPluginManager().callEvent(fertilizeEvent);
|
||||
+
|
||||
+ if (!fertilizeEvent.isCancelled()) {
|
||||
+ for (org.bukkit.block.BlockState blockstate : blocks) {
|
||||
+ blockstate.update(true);
|
||||
+ worldserver.checkCapturedTreeStateForObserverNotify(blockposition, (org.bukkit.craftbukkit.block.CraftBlockState) blockstate); // Paper - notify observers even if grow failed
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
return stack;
|
||||
}
|
||||
});
|
||||
@@ -298,12 +580,42 @@
|
||||
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
|
||||
ServerLevel worldserver = pointer.level();
|
||||
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
|
||||
- PrimedTnt entitytntprimed = new PrimedTnt(worldserver, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (LivingEntity) null);
|
||||
+ // CraftBukkit start
|
||||
+ // EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(worldserver, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null);
|
||||
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink at end and single item in event
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ PrimedTnt entitytntprimed = new PrimedTnt(worldserver, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (LivingEntity) null);
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
worldserver.addFreshEntity(entitytntprimed);
|
||||
worldserver.playSound((Player) null, entitytntprimed.getX(), entitytntprimed.getY(), entitytntprimed.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
worldserver.gameEvent((Entity) null, (Holder) GameEvent.ENTITY_PLACE, blockposition);
|
||||
- stack.shrink(1);
|
||||
+ if (shrink) stack.shrink(1); // Paper - actually handle here
|
||||
return stack;
|
||||
}
|
||||
});
|
||||
@@ -313,7 +625,31 @@
|
||||
ServerLevel worldserver = pointer.level();
|
||||
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
|
||||
BlockPos blockposition = pointer.pos().relative(enumdirection);
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (worldserver.isEmptyBlock(blockposition) && WitherSkullBlock.canSpawnMob(worldserver, blockposition, stack)) {
|
||||
worldserver.setBlock(blockposition, (BlockState) Blocks.WITHER_SKELETON_SKULL.defaultBlockState().setValue(SkullBlock.ROTATION, RotationSegment.convertToSegment(enumdirection)), 3);
|
||||
worldserver.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_PLACE, blockposition);
|
||||
@@ -326,7 +662,7 @@
|
||||
stack.shrink(1);
|
||||
this.setSuccess(true);
|
||||
} else {
|
||||
- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack));
|
||||
+ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this)); // Paper - fix possible StackOverflowError
|
||||
}
|
||||
|
||||
return stack;
|
||||
@@ -339,6 +675,30 @@
|
||||
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
|
||||
CarvedPumpkinBlock blockpumpkincarved = (CarvedPumpkinBlock) Blocks.CARVED_PUMPKIN;
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (worldserver.isEmptyBlock(blockposition) && blockpumpkincarved.canSpawnGolem(worldserver, blockposition)) {
|
||||
if (!worldserver.isClientSide) {
|
||||
worldserver.setBlock(blockposition, blockpumpkincarved.defaultBlockState(), 3);
|
||||
@@ -348,7 +708,7 @@
|
||||
stack.shrink(1);
|
||||
this.setSuccess(true);
|
||||
} else {
|
||||
- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack));
|
||||
+ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this)); // Paper - fix possible StackOverflowError
|
||||
}
|
||||
|
||||
return stack;
|
||||
@@ -377,6 +737,30 @@
|
||||
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
|
||||
BlockState iblockdata = worldserver.getBlockState(blockposition);
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - only single item in event
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (iblockdata.is(BlockTags.BEEHIVES, (blockbase_blockdata) -> {
|
||||
return blockbase_blockdata.hasProperty(BeehiveBlock.HONEY_LEVEL) && blockbase_blockdata.getBlock() instanceof BeehiveBlock;
|
||||
}) && (Integer) iblockdata.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) {
|
||||
@@ -402,6 +786,13 @@
|
||||
this.setSuccess(true);
|
||||
if (iblockdata.is(Blocks.RESPAWN_ANCHOR)) {
|
||||
if ((Integer) iblockdata.getValue(RespawnAnchorBlock.CHARGE) != 4) {
|
||||
+ // Paper start - Call missing BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(pointer, blockposition, stack, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call missing BlockDispenseEvent
|
||||
RespawnAnchorBlock.charge((Entity) null, worldserver, blockposition, iblockdata);
|
||||
stack.shrink(1);
|
||||
} else {
|
||||
@@ -426,6 +817,31 @@
|
||||
this.setSuccess(false);
|
||||
return stack;
|
||||
} else {
|
||||
+ // CraftBukkit start
|
||||
+ ItemStack itemstack1 = stack;
|
||||
+ ServerLevel world = pointer.level();
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); // Paper - ignore stack size on damageable items
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) list.get(0).getBukkitEntity());
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ world.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
Iterator iterator1 = list.iterator();
|
||||
|
||||
Armadillo armadillo;
|
||||
@@ -454,6 +870,13 @@
|
||||
Optional<BlockState> optional = HoneycombItem.getWaxed(iblockdata);
|
||||
|
||||
if (optional.isPresent()) {
|
||||
+ // Paper start - Call missing BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(pointer, blockposition, stack, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call missing BlockDispenseEvent
|
||||
worldserver.setBlockAndUpdate(blockposition, (BlockState) optional.get());
|
||||
worldserver.levelEvent(3003, blockposition, 0);
|
||||
stack.shrink(1);
|
||||
@@ -481,6 +904,12 @@
|
||||
if (!worldserver.getBlockState(blockposition1).is(BlockTags.CONVERTABLE_TO_MUD)) {
|
||||
return this.defaultDispenseItemBehavior.dispense(pointer, stack);
|
||||
} else {
|
||||
+ // Paper start - Call missing BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(pointer, blockposition1, stack, this);
|
||||
+ if (result != null) {
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call missing BlockDispenseEvent
|
||||
if (!worldserver.isClientSide) {
|
||||
for (int k = 0; k < 5; ++k) {
|
||||
worldserver.sendParticles(ParticleTypes.SPLASH, (double) blockposition.getX() + worldserver.random.nextDouble(), (double) (blockposition.getY() + 1), (double) blockposition.getZ() + worldserver.random.nextDouble(), 1, 0.0D, 0.0D, 0.0D, 1.0D);
|
||||
@@ -0,0 +1,82 @@
|
||||
--- a/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
|
||||
@@ -7,8 +7,13 @@
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
+import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.event.block.BlockDispenseArmorEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
|
||||
|
||||
@@ -18,10 +23,15 @@
|
||||
|
||||
@Override
|
||||
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
|
||||
- return EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack) ? stack : super.execute(pointer, stack);
|
||||
+ return EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this) ? stack : super.execute(pointer, stack); // Paper - fix possible StackOverflowError
|
||||
}
|
||||
|
||||
- public static boolean dispenseEquipment(BlockSource pointer, ItemStack stack) {
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
|
||||
+ public static boolean dispenseEquipment(BlockSource pointer, ItemStack armor) {
|
||||
+ // Paper start
|
||||
+ return dispenseEquipment(pointer, armor, null);
|
||||
+ }
|
||||
+ public static boolean dispenseEquipment(BlockSource pointer, ItemStack stack, @javax.annotation.Nullable DispenseItemBehavior currentBehavior) {
|
||||
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
|
||||
List<LivingEntity> list = pointer.level().getEntitiesOfClass(LivingEntity.class, new AABB(blockposition), (entityliving) -> {
|
||||
return entityliving.canEquipWithDispenser(stack);
|
||||
@@ -32,9 +42,37 @@
|
||||
} else {
|
||||
LivingEntity entityliving = (LivingEntity) list.getFirst();
|
||||
EquipmentSlot enumitemslot = entityliving.getEquipmentSlotForItem(stack);
|
||||
- ItemStack itemstack1 = stack.split(1);
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
|
||||
|
||||
- entityliving.setItemSlot(enumitemslot, itemstack1);
|
||||
+ // CraftBukkit start
|
||||
+ Level world = pointer.level();
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
+
|
||||
+ BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) entityliving.getBukkitEntity());
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ world.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && (currentBehavior == null || idispensebehavior != currentBehavior)) { // Paper - fix possible StackOverflowError
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ entityliving.setItemSlot(enumitemslot, CraftItemStack.asNMSCopy(event.getItem()));
|
||||
+ // CraftBukkit end
|
||||
if (entityliving instanceof Mob) {
|
||||
Mob entityinsentient = (Mob) entityliving;
|
||||
|
||||
@@ -42,6 +80,7 @@
|
||||
entityinsentient.setPersistenceRequired();
|
||||
}
|
||||
|
||||
+ if (shrink) stack.shrink(1); // Paper - shrink here
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
--- a/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
|
||||
@@ -15,6 +15,11 @@
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.RailShape;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.event.block.BlockDispenseEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public class MinecartDispenseItemBehavior extends DefaultDispenseItemBehavior {
|
||||
|
||||
@@ -62,11 +67,40 @@
|
||||
}
|
||||
|
||||
Vec3 vec3d1 = new Vec3(d0, d1 + d3, d2);
|
||||
- AbstractMinecart entityminecartabstract = AbstractMinecart.createMinecart(worldserver, vec3d1.x, vec3d1.y, vec3d1.z, this.entityType, EntitySpawnReason.DISPENSER, stack, (Player) null);
|
||||
+ // CraftBukkit start
|
||||
+ // EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.createMinecart(worldserver, vec3d1.x, vec3d1.y, vec3d1.z, this.entityType, EntitySpawnReason.DISPENSER, itemstack, (EntityHuman) null);
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
|
||||
+ org.bukkit.block.Block block2 = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block2, craftItem.clone(), new org.bukkit.util.Vector(vec3d1.x, vec3d1.y, vec3d1.z));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ AbstractMinecart entityminecartabstract = AbstractMinecart.createMinecart(worldserver, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.entityType, EntitySpawnReason.DISPENSER, itemstack1, (Player) null);
|
||||
+
|
||||
if (entityminecartabstract != null) {
|
||||
- worldserver.addFreshEntity(entityminecartabstract);
|
||||
- stack.shrink(1);
|
||||
+ if (worldserver.addFreshEntity(entityminecartabstract) && shrink) stack.shrink(1); // Paper - if entity add was successful and supposed to shrink
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
return stack;
|
||||
@@ -0,0 +1,58 @@
|
||||
--- a/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
|
||||
@@ -8,6 +8,11 @@
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.ProjectileItem;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.event.block.BlockDispenseEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior {
|
||||
|
||||
@@ -31,8 +36,41 @@
|
||||
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
|
||||
Position iposition = this.dispenseConfig.positionFunction().getDispensePosition(pointer, enumdirection);
|
||||
|
||||
- Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, stack, enumdirection), worldserver, stack, (double) enumdirection.getStepX(), (double) enumdirection.getStepY(), (double) enumdirection.getStepZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty());
|
||||
- stack.shrink(1);
|
||||
+ // CraftBukkit start
|
||||
+ // IProjectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, itemstack, enumdirection), worldserver, itemstack, (double) enumdirection.getStepX(), (double) enumdirection.getStepY(), (double) enumdirection.getStepZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // CraftBukkit - call when finish the BlockDispenseEvent
|
||||
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
|
||||
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) enumdirection.getStepX(), (double) enumdirection.getStepY(), (double) enumdirection.getStepZ()));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ // stack.grow(1); // Paper - shrink below
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ boolean shrink = true; // Paper
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ shrink = false; // Paper - shrink below
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // SPIGOT-7923: Avoid create projectiles with empty item
|
||||
+ if (!itemstack1.isEmpty()) {
|
||||
+ Projectile iprojectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, CraftItemStack.unwrap(event.getItem()), enumdirection), worldserver, itemstack1, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies
|
||||
+ iprojectile.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(pointer.blockEntity());
|
||||
+ }
|
||||
+ if (shrink) stack.shrink(1); // Paper - actually handle here
|
||||
+ // CraftBukkit end
|
||||
return stack;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
--- a/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
|
||||
@@ -22,6 +22,12 @@
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.event.block.BlockDispenseEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior {
|
||||
|
||||
@@ -30,11 +36,34 @@
|
||||
@Override
|
||||
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
|
||||
ServerLevel worldserver = pointer.level();
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items
|
||||
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (!worldserver.isClientSide()) {
|
||||
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
|
||||
|
||||
- this.setSuccess(ShearsDispenseItemBehavior.tryShearBeehive(worldserver, blockposition) || ShearsDispenseItemBehavior.tryShearLivingEntity(worldserver, blockposition, stack));
|
||||
+ this.setSuccess(ShearsDispenseItemBehavior.tryShearBeehive(worldserver, blockposition) || ShearsDispenseItemBehavior.tryShearLivingEntity(worldserver, blockposition, stack, bukkitBlock, craftItem)); // CraftBukkit
|
||||
if (this.isSuccess()) {
|
||||
stack.hurtAndBreak(1, worldserver, (ServerPlayer) null, (item) -> {
|
||||
});
|
||||
@@ -64,8 +93,8 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
- private static boolean tryShearLivingEntity(ServerLevel world, BlockPos pos, ItemStack shears) {
|
||||
- List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, new AABB(pos), EntitySelector.NO_SPECTATORS);
|
||||
+ private static boolean tryShearLivingEntity(ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, org.bukkit.block.Block bukkitBlock, CraftItemStack craftItem) { // CraftBukkit - add args
|
||||
+ List<LivingEntity> list = worldserver.getEntitiesOfClass(LivingEntity.class, new AABB(blockposition), EntitySelector.NO_SPECTATORS);
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
@@ -73,8 +102,16 @@
|
||||
|
||||
if (entityliving instanceof Shearable ishearable) {
|
||||
if (ishearable.readyForShearing()) {
|
||||
- ishearable.shear(world, SoundSource.BLOCKS, shears);
|
||||
- world.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, pos);
|
||||
+ // CraftBukkit start
|
||||
+ // Paper start - Add drops to shear events
|
||||
+ org.bukkit.event.block.BlockShearEntityEvent event = CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem, ishearable.generateDefaultDrops(worldserver, itemstack));
|
||||
+ if (event.isCancelled()) {
|
||||
+ // Paper end - Add drops to shear events
|
||||
+ continue;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ ishearable.shear(worldserver, SoundSource.BLOCKS, itemstack, CraftItemStack.asNMSCopy(event.getDrops())); // Paper - Add drops to shear events
|
||||
+ worldserver.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, blockposition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
--- a/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
|
||||
@@ -10,6 +10,12 @@
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
+// CraftBukkit start
|
||||
+import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
+import org.bukkit.event.block.BlockDispenseEvent;
|
||||
+// CraftBukkit end
|
||||
+
|
||||
public class ShulkerBoxDispenseBehavior extends OptionalDispenseItemBehavior {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -26,8 +32,37 @@
|
||||
BlockPos blockposition = pointer.pos().relative(enumdirection);
|
||||
Direction enumdirection1 = pointer.level().isEmptyBlock(blockposition.below()) ? enumdirection : Direction.UP;
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(pointer.level(), pointer.pos());
|
||||
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
||||
+
|
||||
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
+ if (!DispenserBlock.eventFired) {
|
||||
+ pointer.level().getCraftServer().getPluginManager().callEvent(event);
|
||||
+ }
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return stack;
|
||||
+ }
|
||||
+
|
||||
+ if (!event.getItem().equals(craftItem)) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
|
||||
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
|
||||
+ idispensebehavior.dispense(pointer, eventStack);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
try {
|
||||
- this.setSuccess(((BlockItem) item).place(new DirectionalPlaceContext(pointer.level(), blockposition, enumdirection, stack, enumdirection1)).consumesAction());
|
||||
+ // Paper start - track changed items in the dispense event
|
||||
+ this.setSuccess(((BlockItem) item).place(new DirectionalPlaceContext(pointer.level(), blockposition, enumdirection, CraftItemStack.asNMSCopy(event.getItem()), enumdirection1)).consumesAction());
|
||||
+ if (this.isSuccess()) {
|
||||
+ stack.shrink(1); // vanilla shrink is in the place function above, manually handle it here
|
||||
+ }
|
||||
+ // Paper end - track changed items in the dispense event
|
||||
} catch (Exception exception) {
|
||||
ShulkerBoxDispenseBehavior.LOGGER.error("Error trying to place shulker box at {}", blockposition, exception);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
--- a/net/minecraft/core/registries/BuiltInRegistries.java
|
||||
+++ b/net/minecraft/core/registries/BuiltInRegistries.java
|
||||
@@ -296,6 +296,17 @@
|
||||
public static final Registry<SlotDisplay.Type<?>> SLOT_DISPLAY = registerSimple(Registries.SLOT_DISPLAY, SlotDisplays::bootstrap);
|
||||
public static final Registry<RecipeBookCategory> RECIPE_BOOK_CATEGORY = registerSimple(Registries.RECIPE_BOOK_CATEGORY, RecipeBookCategories::bootstrap);
|
||||
public static final Registry<? extends Registry<?>> REGISTRY = WRITABLE_REGISTRY;
|
||||
+ // Paper start - add built-in registry conversions
|
||||
+ public static final io.papermc.paper.registry.data.util.Conversions BUILT_IN_CONVERSIONS = new io.papermc.paper.registry.data.util.Conversions(new net.minecraft.resources.RegistryOps.RegistryInfoLookup() {
|
||||
+ @Override
|
||||
+ public <T> java.util.Optional<net.minecraft.resources.RegistryOps.RegistryInfo<T>> lookup(final ResourceKey<? extends Registry<? extends T>> registryRef) {
|
||||
+ final Registry<T> registry = net.minecraft.server.RegistryLayer.STATIC_ACCESS.lookupOrThrow(registryRef);
|
||||
+ return java.util.Optional.of(
|
||||
+ new net.minecraft.resources.RegistryOps.RegistryInfo<>(registry, registry, Lifecycle.experimental())
|
||||
+ );
|
||||
+ }
|
||||
+ });
|
||||
+ // Paper end - add built-in registry conversions
|
||||
|
||||
private static <T> Registry<T> registerSimple(ResourceKey<? extends Registry<T>> key, BuiltInRegistries.RegistryBootstrap<T> initializer) {
|
||||
return internalRegister(key, new MappedRegistry<>(key, Lifecycle.stable(), false), initializer);
|
||||
@@ -323,14 +334,22 @@
|
||||
ResourceKey<? extends Registry<T>> key, R registry, BuiltInRegistries.RegistryBootstrap<T> initializer
|
||||
) {
|
||||
Bootstrap.checkBootstrapCalled(() -> "registry " + key.location());
|
||||
+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(registry.key(), registry); // Paper - initialize API registry
|
||||
ResourceLocation resourceLocation = key.location();
|
||||
LOADERS.put(resourceLocation, () -> initializer.run(registry));
|
||||
- WRITABLE_REGISTRY.register((ResourceKey<WritableRegistry<?>>)key, registry, RegistrationInfo.BUILT_IN);
|
||||
+ WRITABLE_REGISTRY.register((ResourceKey)key, registry, RegistrationInfo.BUILT_IN); // Paper - decompile fix
|
||||
return registry;
|
||||
}
|
||||
|
||||
public static void bootStrap() {
|
||||
+ // Paper start
|
||||
+ bootStrap(() -> {});
|
||||
+ }
|
||||
+ public static void bootStrap(Runnable runnable) {
|
||||
+ // Paper end
|
||||
+ REGISTRY.freeze(); // Paper - freeze main registry early
|
||||
createContents();
|
||||
+ runnable.run(); // Paper
|
||||
freeze();
|
||||
validate(REGISTRY);
|
||||
}
|
||||
@@ -348,6 +367,7 @@
|
||||
|
||||
for (Registry<?> registry : REGISTRY) {
|
||||
bindBootstrappedTagsToEmpty(registry);
|
||||
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), BUILT_IN_CONVERSIONS); // Paper
|
||||
registry.freeze();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/data/loot/packs/VanillaChestLoot.java
|
||||
+++ b/net/minecraft/data/loot/packs/VanillaChestLoot.java
|
||||
@@ -946,7 +946,6 @@
|
||||
.add(
|
||||
LootItem.lootTableItem(Items.COMPASS)
|
||||
.apply(SetItemCountFunction.setCount(ConstantValue.exactly(1.0F)))
|
||||
- .apply(SetItemDamageFunction.setDamage(UniformGenerator.between(0.15F, 0.8F)))
|
||||
.setWeight(1)
|
||||
)
|
||||
.add(LootItem.lootTableItem(Items.BUCKET).apply(SetItemCountFunction.setCount(UniformGenerator.between(1.0F, 2.0F))).setWeight(1))
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/nbt/ByteArrayTag.java
|
||||
+++ b/net/minecraft/nbt/ByteArrayTag.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.nbt;
|
||||
|
||||
import java.io.DataInput;
|
||||
@@ -24,6 +25,7 @@
|
||||
private static byte[] readAccounted(DataInput input, NbtAccounter tracker) throws IOException {
|
||||
tracker.accountBytes(24L);
|
||||
int i = input.readInt();
|
||||
+ com.google.common.base.Preconditions.checkArgument( i < 1 << 24); // Spigot
|
||||
|
||||
tracker.accountBytes(1L, (long) i);
|
||||
byte[] abyte = new byte[i];
|
||||
@@ -0,0 +1,74 @@
|
||||
--- a/net/minecraft/nbt/CompoundTag.java
|
||||
+++ b/net/minecraft/nbt/CompoundTag.java
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
private static CompoundTag loadCompound(DataInput input, NbtAccounter tracker) throws IOException {
|
||||
tracker.accountBytes(48L);
|
||||
- Map<String, Tag> map = Maps.newHashMap();
|
||||
+ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> map = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - Reduce memory footprint of CompoundTag
|
||||
|
||||
byte b;
|
||||
while ((b = input.readByte()) != 0) {
|
||||
@@ -166,7 +166,7 @@
|
||||
}
|
||||
|
||||
public CompoundTag() {
|
||||
- this(Maps.newHashMap());
|
||||
+ this(new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f)); // Paper - Reduce memory footprint of CompoundTag
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -232,14 +232,34 @@
|
||||
}
|
||||
|
||||
public void putUUID(String key, UUID value) {
|
||||
+ // Paper start - Support old UUID format
|
||||
+ if (this.contains(key + "Most", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC) && this.contains(key + "Least", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
|
||||
+ this.tags.remove(key + "Most");
|
||||
+ this.tags.remove(key + "Least");
|
||||
+ }
|
||||
+ // Paper end - Support old UUID format
|
||||
this.tags.put(key, NbtUtils.createUUID(value));
|
||||
}
|
||||
|
||||
+
|
||||
+ /**
|
||||
+ * You must use {@link #hasUUID(String)} before or else it <b>will</b> throw an NPE.
|
||||
+ */
|
||||
public UUID getUUID(String key) {
|
||||
+ // Paper start - Support old UUID format
|
||||
+ if (!contains(key, 11) && this.contains(key + "Most", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC) && this.contains(key + "Least", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
|
||||
+ return new UUID(this.getLong(key + "Most"), this.getLong(key + "Least"));
|
||||
+ }
|
||||
+ // Paper end - Support old UUID format
|
||||
return NbtUtils.loadUUID(this.get(key));
|
||||
}
|
||||
|
||||
public boolean hasUUID(String key) {
|
||||
+ // Paper start - Support old UUID format
|
||||
+ if (this.contains(key + "Most", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC) && this.contains(key + "Least", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - Support old UUID format
|
||||
Tag tag = this.get(key);
|
||||
return tag != null && tag.getType() == IntArrayTag.TYPE && ((IntArrayTag)tag).getAsIntArray().length == 4;
|
||||
}
|
||||
@@ -477,8 +497,16 @@
|
||||
|
||||
@Override
|
||||
public CompoundTag copy() {
|
||||
- Map<String, Tag> map = Maps.newHashMap(Maps.transformValues(this.tags, Tag::copy));
|
||||
- return new CompoundTag(map);
|
||||
+ // Paper start - Reduce memory footprint of CompoundTag
|
||||
+ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> ret = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(this.tags.size(), 0.8f);
|
||||
+ java.util.Iterator<java.util.Map.Entry<String, Tag>> iterator = (this.tags instanceof it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) ? ((it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap)this.tags).object2ObjectEntrySet().fastIterator() : this.tags.entrySet().iterator();
|
||||
+ while (iterator.hasNext()) {
|
||||
+ Map.Entry<String, Tag> entry = iterator.next();
|
||||
+ ret.put(entry.getKey(), entry.getValue().copy());
|
||||
+ }
|
||||
+
|
||||
+ return new CompoundTag(ret);
|
||||
+ // Paper end - Reduce memory footprint of CompoundTag
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/nbt/IntArrayTag.java
|
||||
+++ b/net/minecraft/nbt/IntArrayTag.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.nbt;
|
||||
|
||||
import java.io.DataInput;
|
||||
@@ -24,6 +25,7 @@
|
||||
private static int[] readAccounted(DataInput input, NbtAccounter tracker) throws IOException {
|
||||
tracker.accountBytes(24L);
|
||||
int i = input.readInt();
|
||||
+ com.google.common.base.Preconditions.checkArgument( i < 1 << 24); // Spigot
|
||||
|
||||
tracker.accountBytes(4L, (long) i);
|
||||
int[] aint = new int[i];
|
||||
@@ -0,0 +1,20 @@
|
||||
--- a/net/minecraft/nbt/NbtIo.java
|
||||
+++ b/net/minecraft/nbt/NbtIo.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.nbt;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
@@ -324,6 +325,12 @@
|
||||
}
|
||||
|
||||
public static CompoundTag read(DataInput input, NbtAccounter tracker) throws IOException {
|
||||
+ // Spigot start
|
||||
+ if ( input instanceof io.netty.buffer.ByteBufInputStream )
|
||||
+ {
|
||||
+ input = new DataInputStream(new org.spigotmc.LimitStream((InputStream) input, tracker));
|
||||
+ }
|
||||
+ // Spigot end
|
||||
Tag nbtbase = NbtIo.readUnnamedTag(input, tracker);
|
||||
|
||||
if (nbtbase instanceof CompoundTag) {
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/nbt/NbtUtils.java
|
||||
+++ b/net/minecraft/nbt/NbtUtils.java
|
||||
@@ -149,8 +149,10 @@
|
||||
if (!nbt.contains("Name", 8)) {
|
||||
return Blocks.AIR.defaultBlockState();
|
||||
} else {
|
||||
- ResourceLocation resourceLocation = ResourceLocation.parse(nbt.getString("Name"));
|
||||
- Optional<? extends Holder<Block>> optional = blockLookup.get(ResourceKey.create(Registries.BLOCK, resourceLocation));
|
||||
+ // Paper start - Validate resource location
|
||||
+ ResourceLocation resourceLocation = ResourceLocation.tryParse(nbt.getString("Name"));
|
||||
+ Optional<? extends Holder<Block>> optional = resourceLocation != null ? blockLookup.get(ResourceKey.create(Registries.BLOCK, resourceLocation)) : Optional.empty();
|
||||
+ // Paper end - Validate resource location
|
||||
if (optional.isEmpty()) {
|
||||
return Blocks.AIR.defaultBlockState();
|
||||
} else {
|
||||
@@ -0,0 +1,69 @@
|
||||
--- a/net/minecraft/nbt/TagParser.java
|
||||
+++ b/net/minecraft/nbt/TagParser.java
|
||||
@@ -49,6 +49,7 @@
|
||||
}, CompoundTag::toString);
|
||||
public static final Codec<CompoundTag> LENIENT_CODEC = Codec.withAlternative(AS_CODEC, CompoundTag.CODEC);
|
||||
private final StringReader reader;
|
||||
+ private int depth; // Paper
|
||||
|
||||
public static CompoundTag parseTag(String string) throws CommandSyntaxException {
|
||||
return new TagParser(new StringReader(string)).readSingleStruct();
|
||||
@@ -159,6 +160,7 @@
|
||||
|
||||
public CompoundTag readStruct() throws CommandSyntaxException {
|
||||
this.expect('{');
|
||||
+ this.increaseDepth(); // Paper
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
this.reader.skipWhitespace();
|
||||
|
||||
@@ -182,6 +184,7 @@
|
||||
}
|
||||
|
||||
this.expect('}');
|
||||
+ this.depth--; // Paper
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
@@ -191,6 +194,7 @@
|
||||
if (!this.reader.canRead()) {
|
||||
throw ERROR_EXPECTED_VALUE.createWithContext(this.reader);
|
||||
} else {
|
||||
+ this.increaseDepth(); // Paper
|
||||
ListTag listTag = new ListTag();
|
||||
TagType<?> tagType = null;
|
||||
|
||||
@@ -216,6 +220,7 @@
|
||||
}
|
||||
|
||||
this.expect(']');
|
||||
+ this.depth--; // Paper
|
||||
return listTag;
|
||||
}
|
||||
}
|
||||
@@ -253,11 +258,11 @@
|
||||
}
|
||||
|
||||
if (typeReader == ByteTag.TYPE) {
|
||||
- list.add((T)((NumericTag)tag).getAsByte());
|
||||
+ list.add((T)(Byte)((NumericTag)tag).getAsByte()); // Paper - decompile fix
|
||||
} else if (typeReader == LongTag.TYPE) {
|
||||
- list.add((T)((NumericTag)tag).getAsLong());
|
||||
+ list.add((T)(Long)((NumericTag)tag).getAsLong()); // Paper - decompile fix
|
||||
} else {
|
||||
- list.add((T)((NumericTag)tag).getAsInt());
|
||||
+ list.add((T)(Integer)((NumericTag)tag).getAsInt()); // Paper - decompile fix
|
||||
}
|
||||
|
||||
if (!this.hasElementSeparator()) {
|
||||
@@ -288,4 +293,11 @@
|
||||
this.reader.skipWhitespace();
|
||||
this.reader.expect(c);
|
||||
}
|
||||
+
|
||||
+ private void increaseDepth() throws CommandSyntaxException {
|
||||
+ this.depth++;
|
||||
+ if (this.depth > 512) {
|
||||
+ throw new io.papermc.paper.brigadier.TagParseCommandSyntaxException("NBT tag is too complex, depth > 512");
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -82,13 +82,13 @@
|
||||
marker.add(Connection.PACKET_MARKER);
|
||||
});
|
||||
public static final Supplier<NioEventLoopGroup> NETWORK_WORKER_GROUP = Suppliers.memoize(() -> {
|
||||
- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
|
||||
+ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
public static final Supplier<EpollEventLoopGroup> NETWORK_EPOLL_WORKER_GROUP = Suppliers.memoize(() -> {
|
||||
- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
|
||||
+ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
public static final Supplier<DefaultEventLoopGroup> LOCAL_WORKER_GROUP = Suppliers.memoize(() -> {
|
||||
- return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
|
||||
+ return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
private static final ProtocolInfo<ServerHandshakePacketListener> INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND;
|
||||
private final PacketFlow receiving;
|
||||
@@ -96,6 +96,11 @@
|
||||
private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
|
||||
public Channel channel;
|
||||
public SocketAddress address;
|
||||
+ // Spigot Start
|
||||
+ public java.util.UUID spoofedUUID;
|
||||
+ public com.mojang.authlib.properties.Property[] spoofedProfile;
|
||||
+ public boolean preparing = true;
|
||||
+ // Spigot End
|
||||
@Nullable
|
||||
private volatile PacketListener disconnectListener;
|
||||
@Nullable
|
||||
@@ -114,7 +119,42 @@
|
||||
private volatile DisconnectionDetails delayedDisconnect;
|
||||
@Nullable
|
||||
BandwidthDebugMonitor bandwidthDebugMonitor;
|
||||
+ public String hostname = ""; // CraftBukkit - add field
|
||||
+ // Paper start - NetworkClient implementation
|
||||
+ public int protocolVersion;
|
||||
+ public java.net.InetSocketAddress virtualHost;
|
||||
+ private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush"); // Paper - Disable explicit network manager flushing
|
||||
+ // Paper end
|
||||
|
||||
+ // Paper start - add utility methods
|
||||
+ public final net.minecraft.server.level.ServerPlayer getPlayer() {
|
||||
+ if (this.packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl impl) {
|
||||
+ return impl.player;
|
||||
+ } else if (this.packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl impl) {
|
||||
+ org.bukkit.craftbukkit.entity.CraftPlayer player = impl.getCraftPlayer();
|
||||
+ return player == null ? null : player.getHandle();
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Paper end - add utility methods
|
||||
+ // Paper start - packet limiter
|
||||
+ protected final Object PACKET_LIMIT_LOCK = new Object();
|
||||
+ protected final @Nullable io.papermc.paper.util.IntervalledCounter allPacketCounts = io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.isEnabled() ? new io.papermc.paper.util.IntervalledCounter(
|
||||
+ (long)(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.interval() * 1.0e9)
|
||||
+ ) : null;
|
||||
+ protected final java.util.Map<Class<? extends net.minecraft.network.protocol.Packet<?>>, io.papermc.paper.util.IntervalledCounter> packetSpecificLimits = new java.util.HashMap<>();
|
||||
+
|
||||
+ private boolean stopReadingPackets;
|
||||
+ private void killForPacketSpam() {
|
||||
+ this.sendPacket(new ClientboundDisconnectPacket(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.kickMessage)), PacketSendListener.thenRun(() -> {
|
||||
+ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.kickMessage));
|
||||
+ }), true);
|
||||
+ this.setReadOnly();
|
||||
+ this.stopReadingPackets = true;
|
||||
+ }
|
||||
+ // Paper end - packet limiter
|
||||
+ @Nullable public SocketAddress haProxyAddress; // Paper - Add API to get player's proxy address
|
||||
+
|
||||
public Connection(PacketFlow side) {
|
||||
this.receiving = side;
|
||||
}
|
||||
@@ -123,6 +163,9 @@
|
||||
super.channelActive(channelhandlercontext);
|
||||
this.channel = channelhandlercontext.channel();
|
||||
this.address = this.channel.remoteAddress();
|
||||
+ // Spigot Start
|
||||
+ this.preparing = false;
|
||||
+ // Spigot End
|
||||
if (this.delayedDisconnect != null) {
|
||||
this.disconnect(this.delayedDisconnect);
|
||||
}
|
||||
@@ -134,6 +177,21 @@
|
||||
}
|
||||
|
||||
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
|
||||
+ // Paper start - Handle large packets disconnecting client
|
||||
+ if (throwable instanceof io.netty.handler.codec.EncoderException && throwable.getCause() instanceof PacketEncoder.PacketTooLargeException packetTooLargeException) {
|
||||
+ final Packet<?> packet = packetTooLargeException.getPacket();
|
||||
+ if (packet.packetTooLarge(this)) {
|
||||
+ ProtocolSwapHandler.handleOutboundTerminalPacket(channelhandlercontext, packet);
|
||||
+ return;
|
||||
+ } else if (packet.isSkippable()) {
|
||||
+ Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
|
||||
+ ProtocolSwapHandler.handleOutboundTerminalPacket(channelhandlercontext, packet);
|
||||
+ return;
|
||||
+ } else {
|
||||
+ throwable = throwable.getCause();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Handle large packets disconnecting client
|
||||
if (throwable instanceof SkipPacketException) {
|
||||
Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
|
||||
} else {
|
||||
@@ -141,8 +199,10 @@
|
||||
|
||||
this.handlingFault = true;
|
||||
if (this.channel.isOpen()) {
|
||||
+ net.minecraft.server.level.ServerPlayer player = this.getPlayer(); // Paper - Add API for quit reason
|
||||
if (throwable instanceof TimeoutException) {
|
||||
Connection.LOGGER.debug("Timeout", throwable);
|
||||
+ if (player != null) player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.TIMED_OUT; // Paper - Add API for quit reason
|
||||
this.disconnect((Component) Component.translatable("disconnect.timeout"));
|
||||
} else {
|
||||
MutableComponent ichatmutablecomponent = Component.translatable("disconnect.genericReason", "Internal Exception: " + String.valueOf(throwable));
|
||||
@@ -155,9 +215,11 @@
|
||||
disconnectiondetails = new DisconnectionDetails(ichatmutablecomponent);
|
||||
}
|
||||
|
||||
+ if (player != null) player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.ERRONEOUS_STATE; // Paper - Add API for quit reason
|
||||
if (flag) {
|
||||
Connection.LOGGER.debug("Failed to sent packet", throwable);
|
||||
- if (this.getSending() == PacketFlow.CLIENTBOUND) {
|
||||
+ boolean doesDisconnectExist = this.packetListener.protocol() != ConnectionProtocol.STATUS && this.packetListener.protocol() != ConnectionProtocol.HANDSHAKING; // Paper
|
||||
+ if (this.getSending() == PacketFlow.CLIENTBOUND && doesDisconnectExist) { // Paper
|
||||
Packet<?> packet = this.sendLoginDisconnect ? new ClientboundLoginDisconnectPacket(ichatmutablecomponent) : new ClientboundDisconnectPacket(ichatmutablecomponent);
|
||||
|
||||
this.send((Packet) packet, PacketSendListener.thenRun(() -> {
|
||||
@@ -176,6 +238,7 @@
|
||||
|
||||
}
|
||||
}
|
||||
+ if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) io.papermc.paper.util.TraceUtil.printStackTrace(throwable); // Spigot // Paper
|
||||
}
|
||||
|
||||
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
|
||||
@@ -185,11 +248,61 @@
|
||||
if (packetlistener == null) {
|
||||
throw new IllegalStateException("Received a packet before the packet listener was initialized");
|
||||
} else {
|
||||
+ // Paper start - packet limiter
|
||||
+ if (this.stopReadingPackets) {
|
||||
+ return;
|
||||
+ }
|
||||
+ if (this.allPacketCounts != null ||
|
||||
+ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.containsKey(packet.getClass())) {
|
||||
+ long time = System.nanoTime();
|
||||
+ synchronized (PACKET_LIMIT_LOCK) {
|
||||
+ if (this.allPacketCounts != null) {
|
||||
+ this.allPacketCounts.updateAndAdd(1, time);
|
||||
+ if (this.allPacketCounts.getRate() >= io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.maxPacketRate()) {
|
||||
+ this.killForPacketSpam();
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (Class<?> check = packet.getClass(); check != Object.class; check = check.getSuperclass()) {
|
||||
+ io.papermc.paper.configuration.GlobalConfiguration.PacketLimiter.PacketLimit packetSpecificLimit =
|
||||
+ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.get(check);
|
||||
+ if (packetSpecificLimit == null || !packetSpecificLimit.isEnabled()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ io.papermc.paper.util.IntervalledCounter counter = this.packetSpecificLimits.computeIfAbsent((Class)check, (clazz) -> {
|
||||
+ return new io.papermc.paper.util.IntervalledCounter((long)(packetSpecificLimit.interval() * 1.0e9));
|
||||
+ });
|
||||
+ counter.updateAndAdd(1, time);
|
||||
+ if (counter.getRate() >= packetSpecificLimit.maxPacketRate()) {
|
||||
+ switch (packetSpecificLimit.action()) {
|
||||
+ case DROP:
|
||||
+ return;
|
||||
+ case KICK:
|
||||
+ String deobfedPacketName = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(check.getName());
|
||||
+
|
||||
+ String playerName;
|
||||
+ if (this.packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl impl) {
|
||||
+ playerName = impl.getOwner().getName();
|
||||
+ } else {
|
||||
+ playerName = this.getLoggableAddress(net.minecraft.server.MinecraftServer.getServer().logIPs());
|
||||
+ }
|
||||
+
|
||||
+ Connection.LOGGER.warn("{} kicked for packet spamming: {}", playerName, deobfedPacketName.substring(deobfedPacketName.lastIndexOf(".") + 1));
|
||||
+ this.killForPacketSpam();
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - packet limiter
|
||||
if (packetlistener.shouldHandleMessage(packet)) {
|
||||
try {
|
||||
Connection.genericsFtw(packet, packetlistener);
|
||||
} catch (RunningOnDifferentThreadException cancelledpackethandleexception) {
|
||||
;
|
||||
+ } catch (io.papermc.paper.util.ServerStopRejectedExecutionException ignored) { // Paper - do not prematurely disconnect players on stop
|
||||
} catch (RejectedExecutionException rejectedexecutionexception) {
|
||||
this.disconnect((Component) Component.translatable("multiplayer.disconnect.server_shutdown"));
|
||||
} catch (ClassCastException classcastexception) {
|
||||
@@ -205,7 +318,7 @@
|
||||
}
|
||||
|
||||
private static <T extends PacketListener> void genericsFtw(Packet<T> packet, PacketListener listener) {
|
||||
- packet.handle(listener);
|
||||
+ packet.handle((T) listener); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
private void validateListener(ProtocolInfo<?> state, PacketListener listener) {
|
||||
@@ -418,12 +531,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
|
||||
+ private static int joinAttemptsThisTick; // Paper - Buffer joins to world
|
||||
+ private static int currTick; // Paper - Buffer joins to world
|
||||
public void tick() {
|
||||
this.flushQueue();
|
||||
+ // Paper start - Buffer joins to world
|
||||
+ if (Connection.currTick != net.minecraft.server.MinecraftServer.currentTick) {
|
||||
+ Connection.currTick = net.minecraft.server.MinecraftServer.currentTick;
|
||||
+ Connection.joinAttemptsThisTick = 0;
|
||||
+ }
|
||||
+ // Paper end - Buffer joins to world
|
||||
PacketListener packetlistener = this.packetListener;
|
||||
|
||||
if (packetlistener instanceof TickablePacketListener tickablepacketlistener) {
|
||||
+ // Paper start - Buffer joins to world
|
||||
+ if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener)
|
||||
+ || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING
|
||||
+ || Connection.joinAttemptsThisTick++ < MAX_PER_TICK) {
|
||||
tickablepacketlistener.tick();
|
||||
+ } // Paper end - Buffer joins to world
|
||||
}
|
||||
|
||||
if (!this.isConnected() && !this.disconnectionHandled) {
|
||||
@@ -431,7 +558,7 @@
|
||||
}
|
||||
|
||||
if (this.channel != null) {
|
||||
- this.channel.flush();
|
||||
+ if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - Disable explicit network manager flushing; we don't need to explicit flush here, but allow opt in incase issues are found to a better version
|
||||
}
|
||||
|
||||
if (this.tickCount++ % 20 == 0) {
|
||||
@@ -464,12 +591,15 @@
|
||||
}
|
||||
|
||||
public void disconnect(DisconnectionDetails disconnectionInfo) {
|
||||
+ // Spigot Start
|
||||
+ this.preparing = false;
|
||||
+ // Spigot End
|
||||
if (this.channel == null) {
|
||||
this.delayedDisconnect = disconnectionInfo;
|
||||
}
|
||||
|
||||
if (this.isConnected()) {
|
||||
- this.channel.close().awaitUninterruptibly();
|
||||
+ this.channel.close(); // We can't wait as this may be called from an event loop.
|
||||
this.disconnectionDetails = disconnectionInfo;
|
||||
}
|
||||
|
||||
@@ -537,7 +667,7 @@
|
||||
}
|
||||
|
||||
public void configurePacketHandler(ChannelPipeline pipeline) {
|
||||
- pipeline.addLast("hackfix", new ChannelOutboundHandlerAdapter(this) {
|
||||
+ pipeline.addLast("hackfix", new ChannelOutboundHandlerAdapter() { // CraftBukkit - decompile error
|
||||
public void write(ChannelHandlerContext channelhandlercontext, Object object, ChannelPromise channelpromise) throws Exception {
|
||||
super.write(channelhandlercontext, object, channelpromise);
|
||||
}
|
||||
@@ -613,6 +743,14 @@
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start - add proper async disconnect
|
||||
+ public void enableAutoRead() {
|
||||
+ if (this.channel != null) {
|
||||
+ this.channel.config().setAutoRead(true);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - add proper async disconnect
|
||||
+
|
||||
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
|
||||
if (compressionThreshold >= 0) {
|
||||
ChannelHandler channelhandler = this.channel.pipeline().get("decompress");
|
||||
@@ -633,6 +771,7 @@
|
||||
} else {
|
||||
this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
|
||||
}
|
||||
+ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
|
||||
} else {
|
||||
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
|
||||
this.channel.pipeline().remove("decompress");
|
||||
@@ -641,6 +780,7 @@
|
||||
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
|
||||
this.channel.pipeline().remove("compress");
|
||||
}
|
||||
+ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_DISABLED); // Paper - Add Channel initialization listeners
|
||||
}
|
||||
|
||||
}
|
||||
@@ -661,6 +801,27 @@
|
||||
|
||||
packetlistener1.onDisconnect(disconnectiondetails);
|
||||
}
|
||||
+ this.pendingActions.clear(); // Free up packet queue.
|
||||
+ // Paper start - Add PlayerConnectionCloseEvent
|
||||
+ final PacketListener packetListener = this.getPacketListener();
|
||||
+ if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
|
||||
+ /* Player was logged in, either game listener or configuration listener */
|
||||
+ final com.mojang.authlib.GameProfile profile = commonPacketListener.getOwner();
|
||||
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(),
|
||||
+ profile.getName(), ((InetSocketAddress) this.address).getAddress(), false).callEvent();
|
||||
+ } else if (packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginListener) {
|
||||
+ /* Player is login stage */
|
||||
+ switch (loginListener.state) {
|
||||
+ case VERIFYING:
|
||||
+ case WAITING_FOR_DUPE_DISCONNECT:
|
||||
+ case PROTOCOL_SWITCHING:
|
||||
+ case ACCEPTED:
|
||||
+ final com.mojang.authlib.GameProfile profile = loginListener.authenticatedProfile; /* Should be non-null at this stage */
|
||||
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(), profile.getName(),
|
||||
+ ((InetSocketAddress) this.address).getAddress(), false).callEvent();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Add PlayerConnectionCloseEvent
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
--- a/net/minecraft/network/FriendlyByteBuf.java
|
||||
+++ b/net/minecraft/network/FriendlyByteBuf.java
|
||||
@@ -72,6 +72,7 @@
|
||||
|
||||
public static final int DEFAULT_NBT_QUOTA = 2097152;
|
||||
private final ByteBuf source;
|
||||
+ @Nullable public final java.util.Locale adventure$locale; // Paper - track player's locale for server-side translations
|
||||
public static final short MAX_STRING_LENGTH = Short.MAX_VALUE;
|
||||
public static final int MAX_COMPONENT_STRING_LENGTH = 262144;
|
||||
private static final int PUBLIC_KEY_SIZE = 256;
|
||||
@@ -80,6 +81,7 @@
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
public FriendlyByteBuf(ByteBuf parent) {
|
||||
+ this.adventure$locale = PacketEncoder.ADVENTURE_LOCALE.get(); // Paper - track player's locale for server-side translations
|
||||
this.source = parent;
|
||||
}
|
||||
|
||||
@@ -120,11 +122,16 @@
|
||||
}
|
||||
|
||||
public <T> void writeJsonWithCodec(Codec<T> codec, T value) {
|
||||
+ // Paper start - Adventure; add max length parameter
|
||||
+ this.writeJsonWithCodec(codec, value, MAX_STRING_LENGTH);
|
||||
+ }
|
||||
+ public <T> void writeJsonWithCodec(Codec<T> codec, T value, int maxLength) {
|
||||
+ // Paper end - Adventure; add max length parameter
|
||||
DataResult<JsonElement> dataresult = codec.encodeStart(JsonOps.INSTANCE, value);
|
||||
|
||||
this.writeUtf(FriendlyByteBuf.GSON.toJson((JsonElement) dataresult.getOrThrow((s) -> {
|
||||
return new EncoderException("Failed to encode: " + s + " " + String.valueOf(value));
|
||||
- })));
|
||||
+ })), maxLength); // Paper - Adventure; add max length parameter
|
||||
}
|
||||
|
||||
public static <T> IntFunction<T> limitValue(IntFunction<T> applier, int max) {
|
||||
@@ -139,7 +146,7 @@
|
||||
|
||||
public <T, C extends Collection<T>> C readCollection(IntFunction<C> collectionFactory, StreamDecoder<? super FriendlyByteBuf, T> reader) {
|
||||
int i = this.readVarInt();
|
||||
- C c0 = (Collection) collectionFactory.apply(i);
|
||||
+ C c0 = collectionFactory.apply(i); // CraftBukkit - decompile error
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
c0.add(reader.decode(this));
|
||||
@@ -150,7 +157,7 @@
|
||||
|
||||
public <T> void writeCollection(Collection<T> collection, StreamEncoder<? super FriendlyByteBuf, T> writer) {
|
||||
this.writeVarInt(collection.size());
|
||||
- Iterator iterator = collection.iterator();
|
||||
+ Iterator<T> iterator = collection.iterator(); // CraftBukkit - decompile error
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
T t0 = iterator.next();
|
||||
@@ -177,12 +184,12 @@
|
||||
|
||||
public void writeIntIdList(IntList list) {
|
||||
this.writeVarInt(list.size());
|
||||
- list.forEach(this::writeVarInt);
|
||||
+ list.forEach((java.util.function.IntConsumer) this::writeVarInt); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
public <K, V, M extends Map<K, V>> M readMap(IntFunction<M> mapFactory, StreamDecoder<? super FriendlyByteBuf, K> keyReader, StreamDecoder<? super FriendlyByteBuf, V> valueReader) {
|
||||
int i = this.readVarInt();
|
||||
- M m0 = (Map) mapFactory.apply(i);
|
||||
+ M m0 = mapFactory.apply(i); // CraftBukkit - decompile error
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
K k0 = keyReader.decode(this);
|
||||
@@ -216,7 +223,7 @@
|
||||
}
|
||||
|
||||
public <E extends Enum<E>> void writeEnumSet(EnumSet<E> enumSet, Class<E> type) {
|
||||
- E[] ae = (Enum[]) type.getEnumConstants();
|
||||
+ E[] ae = type.getEnumConstants(); // CraftBukkit - decompile error
|
||||
BitSet bitset = new BitSet(ae.length);
|
||||
|
||||
for (int i = 0; i < ae.length; ++i) {
|
||||
@@ -227,7 +234,7 @@
|
||||
}
|
||||
|
||||
public <E extends Enum<E>> EnumSet<E> readEnumSet(Class<E> type) {
|
||||
- E[] ae = (Enum[]) type.getEnumConstants();
|
||||
+ E[] ae = type.getEnumConstants(); // CraftBukkit - decompile error
|
||||
BitSet bitset = this.readFixedBitSet(ae.length);
|
||||
EnumSet<E> enumset = EnumSet.noneOf(type);
|
||||
|
||||
@@ -498,7 +505,7 @@
|
||||
}
|
||||
|
||||
public <T extends Enum<T>> T readEnum(Class<T> enumClass) {
|
||||
- return ((Enum[]) enumClass.getEnumConstants())[this.readVarInt()];
|
||||
+ return ((T[]) enumClass.getEnumConstants())[this.readVarInt()]; // CraftBukkit - fix decompile error
|
||||
}
|
||||
|
||||
public FriendlyByteBuf writeEnum(Enum<?> instance) {
|
||||
@@ -565,7 +572,7 @@
|
||||
|
||||
try {
|
||||
NbtIo.writeAnyTag((Tag) nbt, new ByteBufOutputStream(buf));
|
||||
- } catch (IOException ioexception) {
|
||||
+ } catch (Exception ioexception) { // CraftBukkit - IOException -> Exception
|
||||
throw new EncoderException(ioexception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
--- a/net/minecraft/network/PacketEncoder.java
|
||||
+++ b/net/minecraft/network/PacketEncoder.java
|
||||
@@ -17,10 +17,12 @@
|
||||
this.protocolInfo = state;
|
||||
}
|
||||
|
||||
+ static final ThreadLocal<java.util.Locale> ADVENTURE_LOCALE = ThreadLocal.withInitial(() -> null); // Paper - adventure; set player's locale
|
||||
protected void encode(ChannelHandlerContext channelHandlerContext, Packet<T> packet, ByteBuf byteBuf) throws Exception {
|
||||
PacketType<? extends Packet<? super T>> packetType = packet.type();
|
||||
|
||||
try {
|
||||
+ ADVENTURE_LOCALE.set(channelHandlerContext.channel().attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).get()); // Paper - adventure; set player's locale
|
||||
this.protocolInfo.codec().encode(byteBuf, packet);
|
||||
int i = byteBuf.readableBytes();
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
@@ -31,14 +33,40 @@
|
||||
|
||||
JvmProfiler.INSTANCE.onPacketSent(this.protocolInfo.id(), packetType, channelHandlerContext.channel().remoteAddress(), i);
|
||||
} catch (Throwable var9) {
|
||||
- LOGGER.error("Error sending packet {}", packetType, var9);
|
||||
+ LOGGER.error("Error sending packet {} (skippable? {})", packetType, packet.isSkippable(), var9);
|
||||
if (packet.isSkippable()) {
|
||||
throw new SkipPacketException(var9);
|
||||
}
|
||||
|
||||
throw var9;
|
||||
} finally {
|
||||
+ // Paper start - Handle large packets disconnecting client
|
||||
+ int packetLength = byteBuf.readableBytes();
|
||||
+ if (packetLength > MAX_PACKET_SIZE || (packetLength > MAX_FINAL_PACKET_SIZE && packet.hasLargePacketFallback())) {
|
||||
+ throw new PacketTooLargeException(packet, packetLength);
|
||||
+ }
|
||||
+ // Paper end - Handle large packets disconnecting client
|
||||
ProtocolSwapHandler.handleOutboundTerminalPacket(channelHandlerContext, packet);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ // packet size is encoded into 3-byte varint
|
||||
+ private static final int MAX_FINAL_PACKET_SIZE = (1 << 21) - 1;
|
||||
+ // Vanilla Max size for the encoder (before compression)
|
||||
+ private static final int MAX_PACKET_SIZE = 8388608;
|
||||
+
|
||||
+ public static class PacketTooLargeException extends RuntimeException {
|
||||
+ private final Packet<?> packet;
|
||||
+
|
||||
+ PacketTooLargeException(Packet<?> packet, int packetLength) {
|
||||
+ super("PacketTooLarge - " + packet.getClass().getSimpleName() + " is " + packetLength + ". Max is " + MAX_PACKET_SIZE);
|
||||
+ this.packet = packet;
|
||||
+ }
|
||||
+
|
||||
+ public Packet<?> getPacket() {
|
||||
+ return this.packet;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
--- a/net/minecraft/network/VarInt.java
|
||||
+++ b/net/minecraft/network/VarInt.java
|
||||
@@ -9,6 +9,18 @@
|
||||
private static final int DATA_BITS_PER_BYTE = 7;
|
||||
|
||||
public static int getByteSize(int i) {
|
||||
+ // Paper start - Optimize VarInts
|
||||
+ return VARINT_EXACT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(i)];
|
||||
+ }
|
||||
+ private static final int[] VARINT_EXACT_BYTE_LENGTHS = new int[33];
|
||||
+ static {
|
||||
+ for (int i = 0; i <= 32; ++i) {
|
||||
+ VARINT_EXACT_BYTE_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d);
|
||||
+ }
|
||||
+ VARINT_EXACT_BYTE_LENGTHS[32] = 1; // Special case for the number 0.
|
||||
+ }
|
||||
+ public static int getByteSizeOld(int i) {
|
||||
+ // Paper end - Optimize VarInts
|
||||
for (int j = 1; j < 5; j++) {
|
||||
if ((i & -1 << j * 7) == 0) {
|
||||
return j;
|
||||
@@ -39,6 +51,21 @@
|
||||
}
|
||||
|
||||
public static ByteBuf write(ByteBuf buf, int i) {
|
||||
+ // Paper start - Optimize VarInts
|
||||
+ // Peel the one and two byte count cases explicitly as they are the most common VarInt sizes
|
||||
+ // that the proxy will write, to improve inlining.
|
||||
+ if ((i & (0xFFFFFFFF << 7)) == 0) {
|
||||
+ buf.writeByte(i);
|
||||
+ } else if ((i & (0xFFFFFFFF << 14)) == 0) {
|
||||
+ int w = (i & 0x7F | 0x80) << 8 | (i >>> 7);
|
||||
+ buf.writeShort(w);
|
||||
+ } else {
|
||||
+ writeOld(buf, i);
|
||||
+ }
|
||||
+ return buf;
|
||||
+ }
|
||||
+ public static ByteBuf writeOld(ByteBuf buf, int i) {
|
||||
+ // Paper end - Optimize VarInts
|
||||
while ((i & -128) != 0) {
|
||||
buf.writeByte(i & 127 | 128);
|
||||
i >>>= 7;
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/network/Varint21FrameDecoder.java
|
||||
+++ b/net/minecraft/network/Varint21FrameDecoder.java
|
||||
@@ -39,6 +39,12 @@
|
||||
}
|
||||
|
||||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) {
|
||||
+ // Paper start - Perf: Optimize exception handling; if channel is not active just discard the packet
|
||||
+ if (!channelHandlerContext.channel().isActive()) {
|
||||
+ byteBuf.skipBytes(byteBuf.readableBytes());
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - Perf: Optimize exception handling
|
||||
byteBuf.markReaderIndex();
|
||||
this.helperBuf.clear();
|
||||
if (!copyVarint(byteBuf, this.helperBuf)) {
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/network/chat/ChatDecorator.java
|
||||
+++ b/net/minecraft/network/chat/ChatDecorator.java
|
||||
@@ -2,10 +2,18 @@
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
+import java.util.concurrent.CompletableFuture; // Paper
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ChatDecorator {
|
||||
- ChatDecorator PLAIN = (sender, message) -> message;
|
||||
+ ChatDecorator PLAIN = (sender, message) -> CompletableFuture.completedFuture(message); // Paper - adventure; support async chat decoration events
|
||||
|
||||
- Component decorate(@Nullable ServerPlayer sender, Component message);
|
||||
+ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper - adventure; support chat decoration events (callers should use the overload with CommandSourceStack)
|
||||
+ CompletableFuture<Component> decorate(@Nullable ServerPlayer sender, Component message); // Paper - adventure; support async chat decoration events
|
||||
+
|
||||
+ // Paper start - adventure; support async chat decoration events
|
||||
+ default CompletableFuture<Component> decorate(@Nullable ServerPlayer sender, @Nullable net.minecraft.commands.CommandSourceStack commandSourceStack, Component message) {
|
||||
+ throw new UnsupportedOperationException("Must override this implementation");
|
||||
+ }
|
||||
+ // Paper end - adventure; support async chat decoration events
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
--- a/net/minecraft/network/chat/Component.java
|
||||
+++ b/net/minecraft/network/chat/Component.java
|
||||
@@ -37,9 +37,23 @@
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
+// CraftBukkit start
|
||||
+import java.util.stream.Stream;
|
||||
+// CraftBukkit end
|
||||
|
||||
-public interface Component extends Message, FormattedText {
|
||||
+public interface Component extends Message, FormattedText, Iterable<Component> { // CraftBukkit
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ default Stream<Component> stream() {
|
||||
+ return com.google.common.collect.Streams.concat(new Stream[]{Stream.of(this), this.getSiblings().stream().flatMap(Component::stream)});
|
||||
+ }
|
||||
|
||||
+ @Override
|
||||
+ default Iterator<Component> iterator() {
|
||||
+ return this.stream().iterator();
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
Style getStyle();
|
||||
|
||||
ComponentContents getContents();
|
||||
@@ -0,0 +1,99 @@
|
||||
--- a/net/minecraft/network/chat/ComponentSerialization.java
|
||||
+++ b/net/minecraft/network/chat/ComponentSerialization.java
|
||||
@@ -37,9 +37,31 @@
|
||||
|
||||
public class ComponentSerialization {
|
||||
public static final Codec<Component> CODEC = Codec.recursive("Component", ComponentSerialization::createCodec);
|
||||
- public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistries(CODEC);
|
||||
+ public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_CODEC = createTranslationAware(() -> net.minecraft.nbt.NbtAccounter.create(net.minecraft.network.FriendlyByteBuf.DEFAULT_NBT_QUOTA)); // Paper - adventure
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, Optional<Component>> OPTIONAL_STREAM_CODEC = STREAM_CODEC.apply(ByteBufCodecs::optional);
|
||||
- public static final StreamCodec<RegistryFriendlyByteBuf, Component> TRUSTED_STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(CODEC);
|
||||
+ // Paper start - adventure; use locale from bytebuf for translation
|
||||
+ public static final ThreadLocal<Boolean> DONT_RENDER_TRANSLATABLES = ThreadLocal.withInitial(() -> false);
|
||||
+ public static final StreamCodec<RegistryFriendlyByteBuf, Component> TRUSTED_STREAM_CODEC = createTranslationAware(net.minecraft.nbt.NbtAccounter::unlimitedHeap);
|
||||
+ private static StreamCodec<RegistryFriendlyByteBuf, Component> createTranslationAware(final Supplier<net.minecraft.nbt.NbtAccounter> sizeTracker) {
|
||||
+ return new StreamCodec<>() {
|
||||
+ final StreamCodec<ByteBuf, net.minecraft.nbt.Tag> streamCodec = ByteBufCodecs.tagCodec(sizeTracker);
|
||||
+ @Override
|
||||
+ public Component decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) {
|
||||
+ net.minecraft.nbt.Tag tag = this.streamCodec.decode(registryFriendlyByteBuf);
|
||||
+ RegistryOps<net.minecraft.nbt.Tag> registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE);
|
||||
+ return CODEC.parse(registryOps, tag).getOrThrow(error -> new io.netty.handler.codec.DecoderException("Failed to decode: " + error + " " + tag));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, Component object) {
|
||||
+ RegistryOps<net.minecraft.nbt.Tag> registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE);
|
||||
+ net.minecraft.nbt.Tag tag = (DONT_RENDER_TRANSLATABLES.get() ? CODEC : ComponentSerialization.localizedCodec(registryFriendlyByteBuf.adventure$locale))
|
||||
+ .encodeStart(registryOps, object).getOrThrow(error -> new io.netty.handler.codec.EncoderException("Failed to encode: " + error + " " + object));
|
||||
+ this.streamCodec.encode(registryFriendlyByteBuf, tag);
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+ // Paper end - adventure; use locale from bytebuf for translation
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, Optional<Component>> TRUSTED_OPTIONAL_STREAM_CODEC = TRUSTED_STREAM_CODEC.apply(
|
||||
ByteBufCodecs::optional
|
||||
);
|
||||
@@ -100,7 +122,27 @@
|
||||
return ExtraCodecs.orCompressed(mapCodec3, mapCodec2);
|
||||
}
|
||||
|
||||
+ // Paper start - adventure; create separate codec for each locale
|
||||
+ private static final java.util.Map<java.util.Locale, Codec<Component>> LOCALIZED_CODECS = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
+
|
||||
+ public static Codec<Component> localizedCodec(final java.util.@org.checkerframework.checker.nullness.qual.Nullable Locale locale) {
|
||||
+ if (locale == null) {
|
||||
+ return CODEC;
|
||||
+ }
|
||||
+ return LOCALIZED_CODECS.computeIfAbsent(locale,
|
||||
+ loc -> Codec.recursive("Component", selfCodec -> createCodec(selfCodec, loc)));
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ // Paper end - adventure; create separate codec for each locale
|
||||
+
|
||||
private static Codec<Component> createCodec(Codec<Component> selfCodec) {
|
||||
+ // Paper start - adventure; create separate codec for each locale
|
||||
+ return createCodec(selfCodec, null);
|
||||
+ }
|
||||
+
|
||||
+ private static Codec<Component> createCodec(Codec<Component> selfCodec, @javax.annotation.Nullable java.util.Locale locale) {
|
||||
+ // Paper end - adventure; create separate codec for each locale
|
||||
ComponentContents.Type<?>[] types = new ComponentContents.Type[]{
|
||||
PlainTextContents.TYPE, TranslatableContents.TYPE, KeybindContents.TYPE, ScoreContents.TYPE, SelectorContents.TYPE, NbtContents.TYPE
|
||||
};
|
||||
@@ -113,6 +155,34 @@
|
||||
)
|
||||
.apply(instance, MutableComponent::new)
|
||||
);
|
||||
+ // Paper start - adventure; create separate codec for each locale
|
||||
+ final Codec<Component> origCodec = codec;
|
||||
+ codec = new Codec<>() {
|
||||
+ @Override
|
||||
+ public <T> DataResult<com.mojang.datafixers.util.Pair<Component, T>> decode(final DynamicOps<T> ops, final T input) {
|
||||
+ return origCodec.decode(ops, input);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T> DataResult<T> encode(final Component input, final DynamicOps<T> ops, final T prefix) {
|
||||
+ final net.kyori.adventure.text.Component adventureComponent;
|
||||
+ if (input instanceof io.papermc.paper.adventure.AdventureComponent adv) {
|
||||
+ adventureComponent = adv.adventure$component();
|
||||
+ } else if (locale != null && input.getContents() instanceof TranslatableContents && io.papermc.paper.adventure.PaperAdventure.hasAnyTranslations()) {
|
||||
+ adventureComponent = io.papermc.paper.adventure.PaperAdventure.asAdventure(input);
|
||||
+ } else {
|
||||
+ return origCodec.encode(input, ops, prefix);
|
||||
+ }
|
||||
+ return io.papermc.paper.adventure.PaperAdventure.localizedCodec(locale)
|
||||
+ .encode(adventureComponent, ops, prefix);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String toString() {
|
||||
+ return origCodec.toString() + "[AdventureComponentAware]";
|
||||
+ }
|
||||
+ };
|
||||
+ // Paper end - adventure; create separate codec for each locale
|
||||
return Codec.either(Codec.either(Codec.STRING, ExtraCodecs.nonEmptyList(selfCodec.listOf())), codec)
|
||||
.xmap(either -> either.map(either2 -> either2.map(Component::literal, ComponentSerialization::createFromList), text -> (Component)text), text -> {
|
||||
String string = text.tryCollapseToString();
|
||||
@@ -0,0 +1,42 @@
|
||||
--- a/net/minecraft/network/chat/ComponentUtils.java
|
||||
+++ b/net/minecraft/network/chat/ComponentUtils.java
|
||||
@@ -33,14 +33,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ @io.papermc.paper.annotation.DoNotUse // Paper - validate separators - right now this method is only used for separator evaluation. Error on build if this changes to re-evaluate.
|
||||
public static Optional<MutableComponent> updateForEntity(@Nullable CommandSourceStack source, Optional<Component> text, @Nullable Entity sender, int depth) throws CommandSyntaxException {
|
||||
return text.isPresent() ? Optional.of(updateForEntity(source, text.get(), sender, depth)) : Optional.empty();
|
||||
}
|
||||
|
||||
+ // Paper start - validate separator
|
||||
+ public static Optional<MutableComponent> updateSeparatorForEntity(@Nullable CommandSourceStack source, Optional<Component> text, @Nullable Entity sender, int depth) throws CommandSyntaxException {
|
||||
+ if (text.isEmpty() || !isValidSelector(text.get())) return Optional.empty();
|
||||
+ return Optional.of(updateForEntity(source, text.get(), sender, depth));
|
||||
+ }
|
||||
+ public static boolean isValidSelector(final Component component) {
|
||||
+ final ComponentContents contents = component.getContents();
|
||||
+
|
||||
+ if (contents instanceof net.minecraft.network.chat.contents.NbtContents || contents instanceof net.minecraft.network.chat.contents.SelectorContents) return false;
|
||||
+ if (contents instanceof final net.minecraft.network.chat.contents.TranslatableContents translatableContents) {
|
||||
+ for (final Object arg : translatableContents.getArgs()) {
|
||||
+ if (arg instanceof final Component argumentAsComponent && !isValidSelector(argumentAsComponent)) return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - validate separator
|
||||
+
|
||||
public static MutableComponent updateForEntity(@Nullable CommandSourceStack source, Component text, @Nullable Entity sender, int depth) throws CommandSyntaxException {
|
||||
if (depth > 100) {
|
||||
return text.copy();
|
||||
} else {
|
||||
+ // Paper start - adventure; pass actual vanilla component
|
||||
+ if (text instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent) {
|
||||
+ text = adventureComponent.deepConverted();
|
||||
+ }
|
||||
+ // Paper end - adventure; pass actual vanilla component
|
||||
MutableComponent mutableComponent = text.getContents().resolve(source, sender, depth + 1);
|
||||
|
||||
for (Component component : text.getSiblings()) {
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/network/chat/MessageSignature.java
|
||||
+++ b/net/minecraft/network/chat/MessageSignature.java
|
||||
@@ -13,6 +13,7 @@
|
||||
import net.minecraft.util.SignatureValidator;
|
||||
|
||||
public record MessageSignature(byte[] bytes) {
|
||||
+ public net.kyori.adventure.chat.SignedMessage.Signature adventure() { return () -> this.bytes; } // Paper - adventure; support signed messages
|
||||
public static final Codec<MessageSignature> CODEC = ExtraCodecs.BASE64_STRING.xmap(MessageSignature::new, MessageSignature::bytes);
|
||||
public static final int BYTES = 256;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/network/chat/MutableComponent.java
|
||||
+++ b/net/minecraft/network/chat/MutableComponent.java
|
||||
@@ -94,6 +94,11 @@
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
+ // Paper start - make AdventureComponent equivalent
|
||||
+ if (object instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent) {
|
||||
+ object = adventureComponent.deepConverted();
|
||||
+ }
|
||||
+ // Paper end - make AdventureComponent equivalent
|
||||
return this == object
|
||||
|| object instanceof MutableComponent mutableComponent
|
||||
&& this.contents.equals(mutableComponent.contents)
|
||||
@@ -0,0 +1,44 @@
|
||||
--- a/net/minecraft/network/chat/OutgoingChatMessage.java
|
||||
+++ b/net/minecraft/network/chat/OutgoingChatMessage.java
|
||||
@@ -7,6 +7,12 @@
|
||||
|
||||
void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params);
|
||||
|
||||
+ // Paper start
|
||||
+ default void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params, @javax.annotation.Nullable Component unsigned) {
|
||||
+ this.sendToPlayer(sender, filterMaskEnabled, params);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
static OutgoingChatMessage create(PlayerChatMessage message) {
|
||||
return (OutgoingChatMessage)(message.isSystem()
|
||||
? new OutgoingChatMessage.Disguised(message.decoratedContent())
|
||||
@@ -16,8 +22,13 @@
|
||||
public static record Disguised(@Override Component content) implements OutgoingChatMessage {
|
||||
@Override
|
||||
public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params) {
|
||||
- sender.connection.sendDisguisedChatMessage(this.content, params);
|
||||
+ // Paper start
|
||||
+ this.sendToPlayer(sender, filterMaskEnabled, params, null);
|
||||
}
|
||||
+ public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params, @javax.annotation.Nullable Component unsigned) {
|
||||
+ sender.connection.sendDisguisedChatMessage(unsigned != null ? unsigned : this.content, params);
|
||||
+ // Paper end
|
||||
+ }
|
||||
}
|
||||
|
||||
public static record Player(PlayerChatMessage message) implements OutgoingChatMessage {
|
||||
@@ -28,7 +39,13 @@
|
||||
|
||||
@Override
|
||||
public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params) {
|
||||
+ // Paper start
|
||||
+ this.sendToPlayer(sender, filterMaskEnabled, params, null);
|
||||
+ }
|
||||
+ public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params, @javax.annotation.Nullable Component unsigned) {
|
||||
+ // Paper end
|
||||
PlayerChatMessage playerChatMessage = this.message.filter(filterMaskEnabled);
|
||||
+ playerChatMessage = unsigned != null ? playerChatMessage.withUnsignedContent(unsigned) : playerChatMessage; // Paper
|
||||
if (!playerChatMessage.isFullyFiltered()) {
|
||||
sender.connection.sendPlayerChatMessage(playerChatMessage, params);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
--- a/net/minecraft/network/chat/PlayerChatMessage.java
|
||||
+++ b/net/minecraft/network/chat/PlayerChatMessage.java
|
||||
@@ -17,6 +17,42 @@
|
||||
public record PlayerChatMessage(
|
||||
SignedMessageLink link, @Nullable MessageSignature signature, SignedMessageBody signedBody, @Nullable Component unsignedContent, FilterMask filterMask
|
||||
) {
|
||||
+ // Paper start - adventure; support signed messages
|
||||
+ public final class AdventureView implements net.kyori.adventure.chat.SignedMessage {
|
||||
+ private AdventureView() {
|
||||
+ }
|
||||
+ @Override
|
||||
+ public @org.jetbrains.annotations.NotNull Instant timestamp() {
|
||||
+ return PlayerChatMessage.this.timeStamp();
|
||||
+ }
|
||||
+ @Override
|
||||
+ public long salt() {
|
||||
+ return PlayerChatMessage.this.salt();
|
||||
+ }
|
||||
+ @Override
|
||||
+ public @org.jetbrains.annotations.Nullable Signature signature() {
|
||||
+ return PlayerChatMessage.this.signature == null ? null : PlayerChatMessage.this.signature.adventure();
|
||||
+ }
|
||||
+ @Override
|
||||
+ public net.kyori.adventure.text.@org.jetbrains.annotations.Nullable Component unsignedContent() {
|
||||
+ return PlayerChatMessage.this.unsignedContent() == null ? null : io.papermc.paper.adventure.PaperAdventure.asAdventure(PlayerChatMessage.this.unsignedContent());
|
||||
+ }
|
||||
+ @Override
|
||||
+ public @org.jetbrains.annotations.NotNull String message() {
|
||||
+ return PlayerChatMessage.this.signedContent();
|
||||
+ }
|
||||
+ @Override
|
||||
+ public @org.jetbrains.annotations.NotNull net.kyori.adventure.identity.Identity identity() {
|
||||
+ return net.kyori.adventure.identity.Identity.identity(PlayerChatMessage.this.sender());
|
||||
+ }
|
||||
+ public PlayerChatMessage playerChatMessage() {
|
||||
+ return PlayerChatMessage.this;
|
||||
+ }
|
||||
+ }
|
||||
+ public AdventureView adventureView() {
|
||||
+ return new AdventureView();
|
||||
+ }
|
||||
+ // Paper end - adventure; support signed messages
|
||||
public static final MapCodec<PlayerChatMessage> MAP_CODEC = RecordCodecBuilder.mapCodec(
|
||||
instance -> instance.group(
|
||||
SignedMessageLink.CODEC.fieldOf("link").forGetter(PlayerChatMessage::link),
|
||||
@@ -47,7 +83,14 @@
|
||||
}
|
||||
|
||||
public PlayerChatMessage withUnsignedContent(Component unsignedContent) {
|
||||
- Component component = !unsignedContent.equals(Component.literal(this.signedContent())) ? unsignedContent : null;
|
||||
+ // Paper start - adventure
|
||||
+ final Component component;
|
||||
+ if (unsignedContent instanceof io.papermc.paper.adventure.AdventureComponent advComponent) {
|
||||
+ component = this.signedContent().equals(io.papermc.paper.adventure.AdventureCodecs.tryCollapseToString(advComponent.adventure$component())) ? null : unsignedContent;
|
||||
+ } else {
|
||||
+ component = !unsignedContent.equals(Component.literal(this.signedContent())) ? unsignedContent : null;
|
||||
+ }
|
||||
+ // Paper end - adventure
|
||||
return new PlayerChatMessage(this.link, this.signature, this.signedBody, component, this.filterMask);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
--- a/net/minecraft/network/chat/SignedMessageChain.java
|
||||
+++ b/net/minecraft/network/chat/SignedMessageChain.java
|
||||
@@ -40,14 +40,14 @@
|
||||
if (signature == null) {
|
||||
throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.MISSING_PROFILE_KEY);
|
||||
} else if (playerPublicKey.data().hasExpired()) {
|
||||
- throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY);
|
||||
+ throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes
|
||||
} else {
|
||||
SignedMessageLink signedMessageLink = SignedMessageChain.this.nextLink;
|
||||
if (signedMessageLink == null) {
|
||||
throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.CHAIN_BROKEN);
|
||||
} else if (body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) {
|
||||
this.setChainBroken();
|
||||
- throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT);
|
||||
+ throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT, org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event causes
|
||||
} else {
|
||||
SignedMessageChain.this.lastTimeStamp = body.timeStamp();
|
||||
PlayerChatMessage playerChatMessage = new PlayerChatMessage(signedMessageLink, signature, body, null, FilterMask.PASS_THROUGH);
|
||||
@@ -80,9 +80,16 @@
|
||||
static final Component INVALID_SIGNATURE = Component.translatable("chat.disabled.invalid_signature");
|
||||
static final Component OUT_OF_ORDER_CHAT = Component.translatable("chat.disabled.out_of_order_chat");
|
||||
|
||||
- public DecodeException(Component message) {
|
||||
+ // Paper start
|
||||
+ public final org.bukkit.event.player.PlayerKickEvent.Cause kickCause;
|
||||
+ public DecodeException(Component message, org.bukkit.event.player.PlayerKickEvent.Cause event) {
|
||||
super(message);
|
||||
+ this.kickCause = event;
|
||||
}
|
||||
+ // Paper end
|
||||
+ public DecodeException(Component message) {
|
||||
+ this(message, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); // Paper
|
||||
+ }
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
@@ -0,0 +1,37 @@
|
||||
--- a/net/minecraft/network/chat/TextColor.java
|
||||
+++ b/net/minecraft/network/chat/TextColor.java
|
||||
@@ -17,7 +17,7 @@
|
||||
private static final String CUSTOM_COLOR_PREFIX = "#";
|
||||
public static final Codec<TextColor> CODEC = Codec.STRING.comapFlatMap(TextColor::parseColor, TextColor::serialize);
|
||||
private static final Map<ChatFormatting, TextColor> LEGACY_FORMAT_TO_COLOR = (Map) Stream.of(ChatFormatting.values()).filter(ChatFormatting::isColor).collect(ImmutableMap.toImmutableMap(Function.identity(), (enumchatformat) -> {
|
||||
- return new TextColor(enumchatformat.getColor(), enumchatformat.getName());
|
||||
+ return new TextColor(enumchatformat.getColor(), enumchatformat.getName(), enumchatformat); // CraftBukkit
|
||||
}));
|
||||
private static final Map<String, TextColor> NAMED_COLORS = (Map) TextColor.LEGACY_FORMAT_TO_COLOR.values().stream().collect(ImmutableMap.toImmutableMap((chathexcolor) -> {
|
||||
return chathexcolor.name;
|
||||
@@ -25,16 +25,22 @@
|
||||
private final int value;
|
||||
@Nullable
|
||||
public final String name;
|
||||
+ // CraftBukkit start
|
||||
+ @Nullable
|
||||
+ public final ChatFormatting format;
|
||||
|
||||
- private TextColor(int rgb, String name) {
|
||||
- this.value = rgb & 16777215;
|
||||
- this.name = name;
|
||||
+ private TextColor(int i, String s, ChatFormatting format) {
|
||||
+ this.value = i & 16777215;
|
||||
+ this.name = s;
|
||||
+ this.format = format;
|
||||
}
|
||||
|
||||
private TextColor(int rgb) {
|
||||
this.value = rgb & 16777215;
|
||||
this.name = null;
|
||||
+ this.format = null;
|
||||
}
|
||||
+ // CraftBukkit end
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
@@ -0,0 +1,20 @@
|
||||
--- a/net/minecraft/network/chat/contents/NbtContents.java
|
||||
+++ b/net/minecraft/network/chat/contents/NbtContents.java
|
||||
@@ -120,7 +120,7 @@
|
||||
}).map(Tag::getAsString);
|
||||
if (this.interpreting) {
|
||||
Component component = DataFixUtils.orElse(
|
||||
- ComponentUtils.updateForEntity(source, this.separator, sender, depth), ComponentUtils.DEFAULT_NO_STYLE_SEPARATOR
|
||||
+ ComponentUtils.updateSeparatorForEntity(source, this.separator, sender, depth), ComponentUtils.DEFAULT_NO_STYLE_SEPARATOR // Paper - validate separator
|
||||
);
|
||||
return stream.flatMap(text -> {
|
||||
try {
|
||||
@@ -132,7 +132,7 @@
|
||||
}
|
||||
}).reduce((accumulator, current) -> accumulator.append(component).append(current)).orElseGet(Component::empty);
|
||||
} else {
|
||||
- return ComponentUtils.updateForEntity(source, this.separator, sender, depth)
|
||||
+ return ComponentUtils.updateSeparatorForEntity(source, this.separator, sender, depth) // Paper - validate separator
|
||||
.map(
|
||||
text -> stream.map(Component::literal)
|
||||
.reduce((accumulator, current) -> accumulator.append(text).append(current))
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/network/chat/contents/SelectorContents.java
|
||||
+++ b/net/minecraft/network/chat/contents/SelectorContents.java
|
||||
@@ -36,7 +36,7 @@
|
||||
if (source == null) {
|
||||
return Component.empty();
|
||||
} else {
|
||||
- Optional<? extends Component> optional = ComponentUtils.updateForEntity(source, this.separator, sender, depth);
|
||||
+ Optional<? extends Component> optional = ComponentUtils.updateSeparatorForEntity(source, this.separator, sender, depth); // Paper - validate separator
|
||||
return ComponentUtils.formatList(this.selector.resolved().findEntities(source), optional, Entity::getDisplayName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
--- a/net/minecraft/network/chat/contents/TranslatableContents.java
|
||||
+++ b/net/minecraft/network/chat/contents/TranslatableContents.java
|
||||
@@ -181,6 +181,15 @@
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> visit(FormattedText.ContentConsumer<T> visitor) {
|
||||
+ // Paper start - Count visited parts
|
||||
+ try {
|
||||
+ return this.visit(new TranslatableContentConsumer<>(visitor));
|
||||
+ } catch (IllegalArgumentException ignored) {
|
||||
+ return visitor.accept("...");
|
||||
+ }
|
||||
+ }
|
||||
+ private <T> Optional<T> visit(TranslatableContentConsumer<T> visitor) {
|
||||
+ // Paper end - Count visited parts
|
||||
this.decompose();
|
||||
|
||||
for (FormattedText formattedText : this.decomposedParts) {
|
||||
@@ -191,7 +200,26 @@
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
+ }
|
||||
+ // Paper start - Count visited parts
|
||||
+ private static final class TranslatableContentConsumer<T> implements FormattedText.ContentConsumer<T> {
|
||||
+ private static final IllegalArgumentException EX = new IllegalArgumentException("Too long");
|
||||
+ private final FormattedText.ContentConsumer<T> visitor;
|
||||
+ private int visited;
|
||||
+
|
||||
+ private TranslatableContentConsumer(FormattedText.ContentConsumer<T> visitor) {
|
||||
+ this.visitor = visitor;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Optional<T> accept(final String asString) {
|
||||
+ if (visited++ > 32) {
|
||||
+ throw EX;
|
||||
+ }
|
||||
+ return this.visitor.accept(asString);
|
||||
+ }
|
||||
}
|
||||
+ // Paper end - Count visited parts
|
||||
|
||||
@Override
|
||||
public MutableComponent resolve(@Nullable CommandSourceStack source, @Nullable Entity sender, int depth) throws CommandSyntaxException {
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/net/minecraft/network/protocol/Packet.java
|
||||
+++ b/net/minecraft/network/protocol/Packet.java
|
||||
@@ -11,6 +11,19 @@
|
||||
|
||||
void handle(T listener);
|
||||
|
||||
+ // Paper start
|
||||
+ default boolean hasLargePacketFallback() {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * override {@link #hasLargePacketFallback()} to return true when overriding in subclasses
|
||||
+ */
|
||||
+ default boolean packetTooLarge(net.minecraft.network.Connection manager) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
default boolean isSkippable() {
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
--- a/net/minecraft/network/protocol/PacketUtils.java
|
||||
+++ b/net/minecraft/network/protocol/PacketUtils.java
|
||||
@@ -6,10 +6,15 @@
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.network.PacketListener;
|
||||
+import org.slf4j.Logger;
|
||||
+
|
||||
+// CraftBukkit start
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.RunningOnDifferentThreadException;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.server.network.ServerCommonPacketListenerImpl;
|
||||
+// CraftBukkit end
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
-import org.slf4j.Logger;
|
||||
|
||||
public class PacketUtils {
|
||||
|
||||
@@ -24,6 +29,7 @@
|
||||
public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, BlockableEventLoop<?> engine) throws RunningOnDifferentThreadException {
|
||||
if (!engine.isSameThread()) {
|
||||
engine.executeIfPossible(() -> {
|
||||
+ if (listener instanceof ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // CraftBukkit - Don't handle sync packets for kicked players
|
||||
if (listener.shouldHandleMessage(packet)) {
|
||||
try {
|
||||
packet.handle(listener);
|
||||
@@ -0,0 +1,20 @@
|
||||
--- a/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java
|
||||
+++ b/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
-import net.minecraft.Util;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
@@ -16,8 +15,7 @@
|
||||
private static final int MAX_PAYLOAD_SIZE = 32767;
|
||||
public static final StreamCodec<FriendlyByteBuf, ServerboundCustomPayloadPacket> STREAM_CODEC = CustomPacketPayload.codec((minecraftkey) -> {
|
||||
return DiscardedPayload.codec(minecraftkey, 32767);
|
||||
- }, (List) Util.make(Lists.newArrayList(new CustomPacketPayload.TypeAndCodec[]{new CustomPacketPayload.TypeAndCodec<>(BrandPayload.TYPE, BrandPayload.STREAM_CODEC)}), (arraylist) -> {
|
||||
- })).map(ServerboundCustomPayloadPacket::new, ServerboundCustomPayloadPacket::payload);
|
||||
+ }, java.util.Collections.emptyList()).map(ServerboundCustomPayloadPacket::new, ServerboundCustomPayloadPacket::payload); // CraftBukkit - treat all packets the same
|
||||
|
||||
@Override
|
||||
public PacketType<ServerboundCustomPayloadPacket> type() {
|
||||
@@ -0,0 +1,24 @@
|
||||
--- a/net/minecraft/network/protocol/common/custom/DiscardedPayload.java
|
||||
+++ b/net/minecraft/network/protocol/common/custom/DiscardedPayload.java
|
||||
@@ -4,16 +4,18 @@
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
-public record DiscardedPayload(ResourceLocation id) implements CustomPacketPayload {
|
||||
+public record DiscardedPayload(ResourceLocation id, io.netty.buffer.ByteBuf data) implements CustomPacketPayload { // CraftBukkit - store data
|
||||
|
||||
public static <T extends FriendlyByteBuf> StreamCodec<T, DiscardedPayload> codec(ResourceLocation id, int maxBytes) {
|
||||
return CustomPacketPayload.codec((discardedpayload, packetdataserializer) -> {
|
||||
+ packetdataserializer.writeBytes(discardedpayload.data); // CraftBukkit - serialize
|
||||
}, (packetdataserializer) -> {
|
||||
int j = packetdataserializer.readableBytes();
|
||||
|
||||
if (j >= 0 && j <= maxBytes) {
|
||||
- packetdataserializer.skipBytes(j);
|
||||
- return new DiscardedPayload(id);
|
||||
+ // CraftBukkit start
|
||||
+ return new DiscardedPayload(id, packetdataserializer.readBytes(j));
|
||||
+ // CraftBukkit end
|
||||
} else {
|
||||
throw new IllegalArgumentException("Payload may not be larger than " + maxBytes + " bytes");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket.java
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
public static ClientboundBlockEntityDataPacket create(BlockEntity blockEntity, BiFunction<BlockEntity, RegistryAccess, CompoundTag> nbtGetter) {
|
||||
RegistryAccess registryAccess = blockEntity.getLevel().registryAccess();
|
||||
- return new ClientboundBlockEntityDataPacket(blockEntity.getBlockPos(), blockEntity.getType(), nbtGetter.apply(blockEntity, registryAccess));
|
||||
+ return new ClientboundBlockEntityDataPacket(blockEntity.getBlockPos(), blockEntity.getType(), blockEntity.sanitizeSentNbt(nbtGetter.apply(blockEntity, registryAccess))); // Paper - Sanitize sent data
|
||||
}
|
||||
|
||||
public static ClientboundBlockEntityDataPacket create(BlockEntity blockEntity) {
|
||||
@@ -0,0 +1,24 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
|
||||
@@ -36,6 +36,21 @@
|
||||
this.carriedItem = ItemStack.OPTIONAL_STREAM_CODEC.decode(buf);
|
||||
}
|
||||
|
||||
+ // Paper start - Handle large packets disconnecting client
|
||||
+ @Override
|
||||
+ public boolean hasLargePacketFallback() {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean packetTooLarge(net.minecraft.network.Connection manager) {
|
||||
+ for (int i = 0 ; i < this.items.size() ; i++) {
|
||||
+ manager.send(new ClientboundContainerSetSlotPacket(this.containerId, this.stateId, i, this.items.get(i)));
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - Handle large packets disconnecting client
|
||||
+
|
||||
private void write(RegistryFriendlyByteBuf buf) {
|
||||
buf.writeContainerId(this.containerId);
|
||||
buf.writeVarInt(this.stateId);
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundInitializeBorderPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundInitializeBorderPacket.java
|
||||
@@ -30,8 +30,10 @@
|
||||
}
|
||||
|
||||
public ClientboundInitializeBorderPacket(WorldBorder worldBorder) {
|
||||
- this.newCenterX = worldBorder.getCenterX();
|
||||
- this.newCenterZ = worldBorder.getCenterZ();
|
||||
+ // CraftBukkit start - multiply out nether border
|
||||
+ this.newCenterX = worldBorder.getCenterX() * worldBorder.world.dimensionType().coordinateScale();
|
||||
+ this.newCenterZ = worldBorder.getCenterZ() * worldBorder.world.dimensionType().coordinateScale();
|
||||
+ // CraftBukkit end
|
||||
this.oldSize = worldBorder.getSize();
|
||||
this.newSize = worldBorder.getLerpTarget();
|
||||
this.lerpTime = worldBorder.getLerpRemainingTime();
|
||||
@@ -0,0 +1,19 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
@@ -52,7 +52,7 @@
|
||||
throw new RuntimeException("Can't read heightmap in packet for [" + x + ", " + z + "]");
|
||||
} else {
|
||||
int i = buf.readVarInt();
|
||||
- if (i > 2097152) {
|
||||
+ if (i > 2097152) { // Paper - diff on change - if this changes, update PacketEncoder
|
||||
throw new RuntimeException("Chunk Packet trying to allocate too much memory on read.");
|
||||
} else {
|
||||
this.buffer = new byte[i];
|
||||
@@ -154,6 +154,7 @@
|
||||
CompoundTag compoundTag = blockEntity.getUpdateTag(blockEntity.getLevel().registryAccess());
|
||||
BlockPos blockPos = blockEntity.getBlockPos();
|
||||
int i = SectionPos.sectionRelative(blockPos.getX()) << 4 | SectionPos.sectionRelative(blockPos.getZ());
|
||||
+ blockEntity.sanitizeSentNbt(compoundTag); // Paper - Sanitize sent data
|
||||
return new ClientboundLevelChunkPacketData.BlockEntityInfo(i, blockPos.getY(), blockEntity.getType(), compoundTag.isEmpty() ? null : compoundTag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket.java
|
||||
@@ -38,7 +38,18 @@
|
||||
this.actions = EnumSet.of(action);
|
||||
this.entries = List.of(new ClientboundPlayerInfoUpdatePacket.Entry(player));
|
||||
}
|
||||
+ // Paper start - Add Listing API for Player
|
||||
+ public ClientboundPlayerInfoUpdatePacket(EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions, List<ClientboundPlayerInfoUpdatePacket.Entry> entries) {
|
||||
+ this.actions = actions;
|
||||
+ this.entries = entries;
|
||||
+ }
|
||||
|
||||
+ public ClientboundPlayerInfoUpdatePacket(EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions, ClientboundPlayerInfoUpdatePacket.Entry entry) {
|
||||
+ this.actions = actions;
|
||||
+ this.entries = List.of(entry);
|
||||
+ }
|
||||
+ // Paper end - Add Listing API for Player
|
||||
+
|
||||
public static ClientboundPlayerInfoUpdatePacket createPlayerInitializing(Collection<ServerPlayer> players) {
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> enumSet = EnumSet.of(
|
||||
ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER,
|
||||
@@ -53,6 +64,46 @@
|
||||
return new ClientboundPlayerInfoUpdatePacket(enumSet, players);
|
||||
}
|
||||
|
||||
+ // Paper start - Add Listing API for Player
|
||||
+ public static ClientboundPlayerInfoUpdatePacket createPlayerInitializing(Collection<ServerPlayer> players, ServerPlayer forPlayer) {
|
||||
+ final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> enumSet = EnumSet.of(
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER
|
||||
+ );
|
||||
+ final List<ClientboundPlayerInfoUpdatePacket.Entry> entries = new java.util.ArrayList<>(players.size());
|
||||
+ final org.bukkit.craftbukkit.entity.CraftPlayer bukkitEntity = forPlayer.getBukkitEntity();
|
||||
+ for (final ServerPlayer player : players) {
|
||||
+ entries.add(new ClientboundPlayerInfoUpdatePacket.Entry(player, bukkitEntity.isListed(player.getBukkitEntity())));
|
||||
+ }
|
||||
+ return new ClientboundPlayerInfoUpdatePacket(enumSet, entries);
|
||||
+ }
|
||||
+
|
||||
+ public static ClientboundPlayerInfoUpdatePacket createSinglePlayerInitializing(ServerPlayer player, boolean listed) {
|
||||
+ final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> enumSet = EnumSet.of(
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT,
|
||||
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER
|
||||
+ );
|
||||
+ final List<ClientboundPlayerInfoUpdatePacket.Entry> entries = List.of(new Entry(player, listed));
|
||||
+ return new ClientboundPlayerInfoUpdatePacket(enumSet, entries);
|
||||
+ }
|
||||
+
|
||||
+ public static ClientboundPlayerInfoUpdatePacket updateListed(UUID playerInfoId, boolean listed) {
|
||||
+ EnumSet<ClientboundPlayerInfoUpdatePacket.Action> enumSet = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
|
||||
+ return new ClientboundPlayerInfoUpdatePacket(enumSet, new ClientboundPlayerInfoUpdatePacket.Entry(playerInfoId, listed));
|
||||
+ }
|
||||
+ // Paper end - Add Listing API for Player
|
||||
private ClientboundPlayerInfoUpdatePacket(RegistryFriendlyByteBuf buf) {
|
||||
this.actions = buf.readEnumSet(ClientboundPlayerInfoUpdatePacket.Action.class);
|
||||
this.entries = buf.readList(buf2 -> {
|
||||
@@ -116,7 +167,15 @@
|
||||
}),
|
||||
INITIALIZE_CHAT(
|
||||
(serialized, buf) -> serialized.chatSession = buf.readNullable(RemoteChatSession.Data::read),
|
||||
- (buf, entry) -> buf.writeNullable(entry.chatSession, RemoteChatSession.Data::write)
|
||||
+ // Paper start - Prevent causing expired keys from impacting new joins
|
||||
+ (buf, entry) -> {
|
||||
+ RemoteChatSession.Data chatSession = entry.chatSession;
|
||||
+ if (chatSession != null && chatSession.profilePublicKey().hasExpired()) {
|
||||
+ chatSession = null;
|
||||
+ }
|
||||
+ buf.writeNullable(chatSession, RemoteChatSession.Data::write);
|
||||
+ }
|
||||
+ // Paper end - Prevent causing expired keys from impacting new joins
|
||||
),
|
||||
UPDATE_GAME_MODE((serialized, buf) -> serialized.gameMode = GameType.byId(buf.readVarInt()), (buf, entry) -> buf.writeVarInt(entry.gameMode().getId())),
|
||||
UPDATE_LISTED((serialized, buf) -> serialized.listed = buf.readBoolean(), (buf, entry) -> buf.writeBoolean(entry.listed())),
|
||||
@@ -157,10 +216,15 @@
|
||||
@Nullable RemoteChatSession.Data chatSession
|
||||
) {
|
||||
Entry(ServerPlayer player) {
|
||||
+ // Paper start - Add Listing API for Player
|
||||
+ this(player, true);
|
||||
+ }
|
||||
+ Entry(ServerPlayer player, boolean listed) {
|
||||
this(
|
||||
+ // Paper end - Add Listing API for Player
|
||||
player.getUUID(),
|
||||
player.getGameProfile(),
|
||||
- true,
|
||||
+ listed, // Paper - Add Listing API for Player
|
||||
player.connection.latency(),
|
||||
player.gameMode.getGameModeForPlayer(),
|
||||
player.getTabListDisplayName(),
|
||||
@@ -169,6 +233,11 @@
|
||||
Optionull.map(player.getChatSession(), RemoteChatSession::asData)
|
||||
);
|
||||
}
|
||||
+ // Paper start - Add Listing API for Player
|
||||
+ Entry(UUID profileId, boolean listed) {
|
||||
+ this(profileId, null, listed, 0, GameType.DEFAULT_MODE, null, true, 0, null);
|
||||
+ }
|
||||
+ // Paper end - Add Listing API for Player
|
||||
}
|
||||
|
||||
static class EntryBuilder {
|
||||
@@ -0,0 +1,38 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
|
||||
@@ -33,11 +33,19 @@
|
||||
short short0 = (Short) shortiterator.next();
|
||||
|
||||
this.positions[j] = short0;
|
||||
- this.states[j] = section.getBlockState(SectionPos.sectionRelativeX(short0), SectionPos.sectionRelativeY(short0), SectionPos.sectionRelativeZ(short0));
|
||||
+ this.states[j] = (section != null) ? section.getBlockState(SectionPos.sectionRelativeX(short0), SectionPos.sectionRelativeY(short0), SectionPos.sectionRelativeZ(short0)) : net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(); // CraftBukkit - SPIGOT-6076, Mojang bug when empty chunk section notified
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+ // CraftBukkit start - Add constructor
|
||||
+ public ClientboundSectionBlocksUpdatePacket(SectionPos sectionposition, ShortSet shortset, BlockState[] states) {
|
||||
+ this.sectionPos = sectionposition;
|
||||
+ this.positions = shortset.toShortArray();
|
||||
+ this.states = states;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
private ClientboundSectionBlocksUpdatePacket(FriendlyByteBuf buf) {
|
||||
this.sectionPos = SectionPos.of(buf.readLong());
|
||||
int i = buf.readVarInt();
|
||||
@@ -54,6 +62,14 @@
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start - Multi Block Change API
|
||||
+ public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<BlockState> blockChanges) {
|
||||
+ this.sectionPos = sectionPos;
|
||||
+ this.positions = blockChanges.keySet().toShortArray();
|
||||
+ this.states = blockChanges.values().toArray(new BlockState[0]);
|
||||
+ }
|
||||
+ // Paper end - Multi Block Change API
|
||||
+
|
||||
private void write(FriendlyByteBuf buf) {
|
||||
buf.writeLong(this.sectionPos.asLong());
|
||||
buf.writeVarInt(this.positions.length);
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundSetBorderCenterPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundSetBorderCenterPacket.java
|
||||
@@ -13,8 +13,10 @@
|
||||
private final double newCenterZ;
|
||||
|
||||
public ClientboundSetBorderCenterPacket(WorldBorder worldBorder) {
|
||||
- this.newCenterX = worldBorder.getCenterX();
|
||||
- this.newCenterZ = worldBorder.getCenterZ();
|
||||
+ // CraftBukkit start - multiply out nether border
|
||||
+ this.newCenterX = worldBorder.getCenterX() * (worldBorder.world != null ? worldBorder.world.dimensionType().coordinateScale() : 1.0);
|
||||
+ this.newCenterZ = worldBorder.getCenterZ() * (worldBorder.world != null ? worldBorder.world.dimensionType().coordinateScale() : 1.0);
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
private ClientboundSetBorderCenterPacket(FriendlyByteBuf buf) {
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java
|
||||
@@ -19,9 +19,11 @@
|
||||
}
|
||||
|
||||
private static void pack(List<SynchedEntityData.DataValue<?>> trackedValues, RegistryFriendlyByteBuf buf) {
|
||||
+ try (var ignored = io.papermc.paper.util.DataSanitizationUtil.start(true)) { // Paper - data sanitization
|
||||
for (SynchedEntityData.DataValue<?> dataValue : trackedValues) {
|
||||
dataValue.write(buf);
|
||||
}
|
||||
+ } // Paper - data sanitization
|
||||
|
||||
buf.writeByte(255);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java
|
||||
@@ -19,6 +19,13 @@
|
||||
private final List<Pair<EquipmentSlot, ItemStack>> slots;
|
||||
|
||||
public ClientboundSetEquipmentPacket(int entityId, List<Pair<EquipmentSlot, ItemStack>> equipmentList) {
|
||||
+ // Paper start - data sanitization
|
||||
+ this(entityId, equipmentList, false);
|
||||
+ }
|
||||
+ private boolean sanitize;
|
||||
+ public ClientboundSetEquipmentPacket(int entityId, List<Pair<EquipmentSlot, ItemStack>> equipmentList, boolean sanitize) {
|
||||
+ this.sanitize = sanitize;
|
||||
+ // Paper end - data sanitization
|
||||
this.entity = entityId;
|
||||
this.slots = equipmentList;
|
||||
}
|
||||
@@ -40,6 +47,7 @@
|
||||
buf.writeVarInt(this.entity);
|
||||
int i = this.slots.size();
|
||||
|
||||
+ try (var ignored = io.papermc.paper.util.DataSanitizationUtil.start(this.sanitize)) { // Paper - data sanitization
|
||||
for (int j = 0; j < i; j++) {
|
||||
Pair<EquipmentSlot, ItemStack> pair = this.slots.get(j);
|
||||
EquipmentSlot equipmentSlot = pair.getFirst();
|
||||
@@ -48,6 +56,7 @@
|
||||
buf.writeByte(bl ? k | -128 : k);
|
||||
ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, pair.getSecond());
|
||||
}
|
||||
+ } // Paper - data sanitization
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java
|
||||
@@ -58,6 +58,11 @@
|
||||
);
|
||||
}
|
||||
|
||||
+ // Paper start - Multiple Entries with Scoreboards
|
||||
+ public static ClientboundSetPlayerTeamPacket createMultiplePlayerPacket(PlayerTeam team, Collection<String> players, ClientboundSetPlayerTeamPacket.Action operation) {
|
||||
+ return new ClientboundSetPlayerTeamPacket(team.getName(), operation == ClientboundSetPlayerTeamPacket.Action.ADD ? 3 : 4, Optional.empty(), players);
|
||||
+ }
|
||||
+ // Paper end - Multiple Entries with Scoreboards
|
||||
private ClientboundSetPlayerTeamPacket(RegistryFriendlyByteBuf buf) {
|
||||
this.name = buf.readUtf();
|
||||
this.method = buf.readByte();
|
||||
@@ -200,7 +205,7 @@
|
||||
ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buf, this.displayName);
|
||||
buf.writeByte(this.options);
|
||||
buf.writeUtf(this.nametagVisibility);
|
||||
- buf.writeUtf(this.collisionRule);
|
||||
+ buf.writeUtf(!io.papermc.paper.configuration.GlobalConfiguration.get().collisions.enablePlayerCollisions ? "never" : this.collisionRule); // Paper - Configurable player collision
|
||||
buf.writeEnum(this.color);
|
||||
ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buf, this.playerPrefix);
|
||||
ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buf, this.playerSuffix);
|
||||
@@ -0,0 +1,25 @@
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundSystemChatPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundSystemChatPacket.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.network.protocol.game;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
@@ -12,6 +13,17 @@
|
||||
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, ClientboundSystemChatPacket> STREAM_CODEC = StreamCodec.composite(ComponentSerialization.TRUSTED_STREAM_CODEC, ClientboundSystemChatPacket::content, ByteBufCodecs.BOOL, ClientboundSystemChatPacket::overlay, ClientboundSystemChatPacket::new);
|
||||
|
||||
+ // Spigot start
|
||||
+ public ClientboundSystemChatPacket(net.md_5.bungee.api.chat.BaseComponent[] content, boolean overlay) {
|
||||
+ this(org.bukkit.craftbukkit.util.CraftChatMessage.fromJSON(net.md_5.bungee.chat.ComponentSerializer.toString(content)), overlay);
|
||||
+ }
|
||||
+ // Spigot end
|
||||
+ // Paper start
|
||||
+ public ClientboundSystemChatPacket(net.kyori.adventure.text.Component content, boolean overlay) {
|
||||
+ this(io.papermc.paper.adventure.PaperAdventure.asVanilla(content), overlay);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public PacketType<ClientboundSystemChatPacket> type() {
|
||||
return GamePacketTypes.CLIENTBOUND_SYSTEM_CHAT;
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
private ServerboundCommandSuggestionPacket(FriendlyByteBuf buf) {
|
||||
this.id = buf.readVarInt();
|
||||
- this.command = buf.readUtf(32500);
|
||||
+ this.command = buf.readUtf(2048); // Paper
|
||||
}
|
||||
|
||||
private void write(FriendlyByteBuf buf) {
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/net/minecraft/network/protocol/game/ServerboundInteractPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ServerboundInteractPacket.java
|
||||
@@ -176,4 +176,14 @@
|
||||
buf.writeEnum(this.hand);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Paper start - PlayerUseUnknownEntityEvent
|
||||
+ public int getEntityId() {
|
||||
+ return this.entityId;
|
||||
+ }
|
||||
+
|
||||
+ public boolean isAttack() {
|
||||
+ return this.action.getType() == ActionType.ATTACK;
|
||||
+ }
|
||||
+ // Paper end - PlayerUseUnknownEntityEvent
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/network/protocol/game/ServerboundUseItemOnPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ServerboundUseItemOnPacket.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.network.protocol.game;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -13,6 +14,7 @@
|
||||
private final BlockHitResult blockHit;
|
||||
private final InteractionHand hand;
|
||||
private final int sequence;
|
||||
+ public long timestamp; // Spigot
|
||||
|
||||
public ServerboundUseItemOnPacket(InteractionHand hand, BlockHitResult blockHitResult, int sequence) {
|
||||
this.hand = hand;
|
||||
@@ -21,6 +23,7 @@
|
||||
}
|
||||
|
||||
private ServerboundUseItemOnPacket(FriendlyByteBuf buf) {
|
||||
+ this.timestamp = System.currentTimeMillis(); // Spigot
|
||||
this.hand = (InteractionHand) buf.readEnum(InteractionHand.class);
|
||||
this.blockHit = buf.readBlockHitResult();
|
||||
this.sequence = buf.readVarInt();
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/network/protocol/game/ServerboundUseItemPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ServerboundUseItemPacket.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.network.protocol.game;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -13,6 +14,7 @@
|
||||
private final int sequence;
|
||||
private final float yRot;
|
||||
private final float xRot;
|
||||
+ public long timestamp; // Spigot
|
||||
|
||||
public ServerboundUseItemPacket(InteractionHand hand, int sequence, float yaw, float pitch) {
|
||||
this.hand = hand;
|
||||
@@ -22,6 +24,7 @@
|
||||
}
|
||||
|
||||
private ServerboundUseItemPacket(FriendlyByteBuf buf) {
|
||||
+ this.timestamp = System.currentTimeMillis(); // Spigot
|
||||
this.hand = (InteractionHand) buf.readEnum(InteractionHand.class);
|
||||
this.sequence = buf.readVarInt();
|
||||
this.yRot = buf.readFloat();
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/net/minecraft/network/protocol/game/VecDeltaCodec.java
|
||||
+++ b/net/minecraft/network/protocol/game/VecDeltaCodec.java
|
||||
@@ -5,16 +5,16 @@
|
||||
|
||||
public class VecDeltaCodec {
|
||||
private static final double TRUNCATION_STEPS = 4096.0;
|
||||
- private Vec3 base = Vec3.ZERO;
|
||||
+ public Vec3 base = Vec3.ZERO; // Paper
|
||||
|
||||
@VisibleForTesting
|
||||
static long encode(double value) {
|
||||
- return Math.round(value * 4096.0);
|
||||
+ return Math.round(value * 4096.0); // Paper - Fix MC-4; diff on change
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static double decode(long value) {
|
||||
- return (double)value / 4096.0;
|
||||
+ return value / 4096.0; // Paper - Fix MC-4; diff on change
|
||||
}
|
||||
|
||||
public Vec3 decode(long x, long y, long z) {
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/net/minecraft/network/protocol/handshake/ClientIntentionPacket.java
|
||||
+++ b/net/minecraft/network/protocol/handshake/ClientIntentionPacket.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.network.protocol.handshake;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -11,7 +12,8 @@
|
||||
private static final int MAX_HOST_LENGTH = 255;
|
||||
|
||||
private ClientIntentionPacket(FriendlyByteBuf buf) {
|
||||
- this(buf.readVarInt(), buf.readUtf(255), buf.readUnsignedShort(), ClientIntent.byId(buf.readVarInt()));
|
||||
+ // Spigot - increase max hostName length
|
||||
+ this(buf.readVarInt(), buf.readUtf(Short.MAX_VALUE), buf.readUnsignedShort(), ClientIntent.byId(buf.readVarInt()));
|
||||
}
|
||||
|
||||
private void write(FriendlyByteBuf buf) {
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/net/minecraft/network/protocol/login/ClientboundCustomQueryPacket.java
|
||||
+++ b/net/minecraft/network/protocol/login/ClientboundCustomQueryPacket.java
|
||||
@@ -47,4 +47,14 @@
|
||||
public void handle(ClientLoginPacketListener listener) {
|
||||
listener.handleCustomQuery(this);
|
||||
}
|
||||
+
|
||||
+ // Paper start - MC Utils - default query payloads
|
||||
+ public static record PlayerInfoChannelPayload(ResourceLocation id, FriendlyByteBuf buffer) implements CustomQueryPayload {
|
||||
+
|
||||
+ @Override
|
||||
+ public void write(final FriendlyByteBuf buf) {
|
||||
+ buf.writeBytes(this.buffer.copy());
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - MC Utils - default query payloads
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
--- a/net/minecraft/network/protocol/login/ClientboundLoginDisconnectPacket.java
|
||||
+++ b/net/minecraft/network/protocol/login/ClientboundLoginDisconnectPacket.java
|
||||
@@ -18,11 +18,16 @@
|
||||
}
|
||||
|
||||
private ClientboundLoginDisconnectPacket(FriendlyByteBuf buf) {
|
||||
- this.reason = Component.Serializer.fromJsonLenient(buf.readUtf(262144), RegistryAccess.EMPTY);
|
||||
+ this.reason = Component.Serializer.fromJsonLenient(buf.readUtf(FriendlyByteBuf.MAX_COMPONENT_STRING_LENGTH), RegistryAccess.EMPTY); // Paper - diff on change
|
||||
}
|
||||
|
||||
private void write(FriendlyByteBuf buf) {
|
||||
- buf.writeUtf(Component.Serializer.toJson(this.reason, RegistryAccess.EMPTY));
|
||||
+ // Paper start - Adventure
|
||||
+ // buf.writeUtf(Component.Serializer.toJson(this.reason, RegistryAccess.EMPTY));
|
||||
+ // In the login phase, buf.adventure$locale field is most likely null, but plugins may use internals to set it via the channel attribute
|
||||
+ java.util.Locale bufLocale = buf.adventure$locale;
|
||||
+ buf.writeJsonWithCodec(net.minecraft.network.chat.ComponentSerialization.localizedCodec(bufLocale == null ? java.util.Locale.US : bufLocale), this.reason, FriendlyByteBuf.MAX_COMPONENT_STRING_LENGTH);
|
||||
+ // Paper end - Adventure
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,43 @@
|
||||
--- a/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java
|
||||
+++ b/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java
|
||||
@@ -20,7 +20,17 @@
|
||||
}
|
||||
|
||||
private static CustomQueryAnswerPayload readPayload(int queryId, FriendlyByteBuf buf) {
|
||||
- return readUnknownPayload(buf);
|
||||
+ // Paper start - MC Utils - default query payloads
|
||||
+ FriendlyByteBuf buffer = buf.readNullable((buf2) -> {
|
||||
+ int i = buf2.readableBytes();
|
||||
+ if (i >= 0 && i <= MAX_PAYLOAD_SIZE) {
|
||||
+ return new FriendlyByteBuf(buf2.readBytes(i));
|
||||
+ } else {
|
||||
+ throw new IllegalArgumentException("Payload may not be larger than " + MAX_PAYLOAD_SIZE + " bytes");
|
||||
+ }
|
||||
+ });
|
||||
+ return buffer == null ? null : new net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket.QueryAnswerPayload(buffer);
|
||||
+ // Paper end - MC Utils - default query payloads
|
||||
}
|
||||
|
||||
private static CustomQueryAnswerPayload readUnknownPayload(FriendlyByteBuf buf) {
|
||||
@@ -47,4 +57,21 @@
|
||||
public void handle(ServerLoginPacketListener listener) {
|
||||
listener.handleCustomQueryPacket(this);
|
||||
}
|
||||
+
|
||||
+ // Paper start - MC Utils - default query payloads
|
||||
+ public static final class QueryAnswerPayload implements CustomQueryAnswerPayload {
|
||||
+
|
||||
+ public final FriendlyByteBuf buffer;
|
||||
+
|
||||
+ public QueryAnswerPayload(final net.minecraft.network.FriendlyByteBuf buffer) {
|
||||
+ this.buffer = buffer;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void write(final net.minecraft.network.FriendlyByteBuf buf) {
|
||||
+ buf.writeBytes(this.buffer.copy());
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - MC Utils - default query payloads
|
||||
+
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
--- a/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
+++ b/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
@@ -50,8 +50,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
- private <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) {
|
||||
- return this.itemsById[key.id()];
|
||||
+ public <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) { // Paper - public
|
||||
+ return (SynchedEntityData.DataItem<T>) this.itemsById[key.id()]; // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
public <T> T get(EntityDataAccessor<T> data) {
|
||||
@@ -74,6 +74,13 @@
|
||||
|
||||
}
|
||||
|
||||
+ // CraftBukkit start - add method from above
|
||||
+ public <T> void markDirty(EntityDataAccessor<T> datawatcherobject) {
|
||||
+ this.getItem(datawatcherobject).setDirty(true);
|
||||
+ this.isDirty = true;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
public boolean isDirty() {
|
||||
return this.isDirty;
|
||||
}
|
||||
@@ -140,10 +147,24 @@
|
||||
if (!Objects.equals(from.serializer(), to.accessor.serializer())) {
|
||||
throw new IllegalStateException(String.format(Locale.ROOT, "Invalid entity data item type for field %d on entity %s: old=%s(%s), new=%s(%s)", to.accessor.id(), this.entity, to.value, to.value.getClass(), from.value, from.value.getClass()));
|
||||
} else {
|
||||
- to.setValue(from.value);
|
||||
+ to.setValue((T) from.value); // CraftBukkit - decompile error
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ // We need to pack all as we cannot rely on "non default values" or "dirty" ones.
|
||||
+ // Because these values can possibly be desynced on the client.
|
||||
+ @Nullable
|
||||
+ public List<SynchedEntityData.DataValue<?>> packAll() {
|
||||
+ final List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
|
||||
+ for (final DataItem<?> dataItem : this.itemsById) {
|
||||
+ list.add(dataItem.value());
|
||||
+ }
|
||||
+
|
||||
+ return list;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public static class DataItem<T> {
|
||||
|
||||
final EntityDataAccessor<T> accessor;
|
||||
@@ -0,0 +1,79 @@
|
||||
--- a/net/minecraft/resources/RegistryDataLoader.java
|
||||
+++ b/net/minecraft/resources/RegistryDataLoader.java
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
public class RegistryDataLoader {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
- private static final Comparator<ResourceKey<?>> ERROR_KEY_COMPARATOR = Comparator.comparing(ResourceKey::registry).thenComparing(ResourceKey::location);
|
||||
+ private static final Comparator<ResourceKey<?>> ERROR_KEY_COMPARATOR = Comparator.<ResourceKey<?>, ResourceLocation>comparing(ResourceKey::registry).thenComparing(ResourceKey::location); // Paper - decompile fix
|
||||
private static final RegistrationInfo NETWORK_REGISTRATION_INFO = new RegistrationInfo(Optional.empty(), Lifecycle.experimental());
|
||||
private static final Function<Optional<KnownPack>, RegistrationInfo> REGISTRATION_INFO_CACHE = Util.memoize(knownPacks -> {
|
||||
Lifecycle lifecycle = knownPacks.map(KnownPack::isVanilla).map(vanilla -> Lifecycle.stable()).orElse(Lifecycle.experimental());
|
||||
@@ -238,13 +238,13 @@
|
||||
}
|
||||
|
||||
private static <E> void loadElementFromResource(
|
||||
- WritableRegistry<E> registry, Decoder<E> decoder, RegistryOps<JsonElement> ops, ResourceKey<E> key, Resource resource, RegistrationInfo entryInfo
|
||||
+ WritableRegistry<E> registry, Decoder<E> decoder, RegistryOps<JsonElement> ops, ResourceKey<E> key, Resource resource, RegistrationInfo entryInfo, io.papermc.paper.registry.data.util.Conversions conversions // Paper - pass conversions
|
||||
) throws IOException {
|
||||
try (Reader reader = resource.openAsReader()) {
|
||||
JsonElement jsonElement = JsonParser.parseReader(reader);
|
||||
DataResult<E> dataResult = decoder.parse(ops, jsonElement);
|
||||
E object = dataResult.getOrThrow();
|
||||
- registry.register(key, object, entryInfo);
|
||||
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(registry, key, object, entryInfo, conversions); // Paper - register with listeners
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,6 +258,7 @@
|
||||
FileToIdConverter fileToIdConverter = FileToIdConverter.registry(registry.key());
|
||||
RegistryOps<JsonElement> registryOps = RegistryOps.create(JsonOps.INSTANCE, infoGetter);
|
||||
|
||||
+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(infoGetter); // Paper - create conversions
|
||||
for (Entry<ResourceLocation, Resource> entry : fileToIdConverter.listMatchingResources(resourceManager).entrySet()) {
|
||||
ResourceLocation resourceLocation = entry.getKey();
|
||||
ResourceKey<E> resourceKey = ResourceKey.create(registry.key(), fileToIdConverter.fileToId(resourceLocation));
|
||||
@@ -265,7 +266,7 @@
|
||||
RegistrationInfo registrationInfo = REGISTRATION_INFO_CACHE.apply(resource.knownPackInfo());
|
||||
|
||||
try {
|
||||
- loadElementFromResource(registry, elementDecoder, registryOps, resourceKey, resource, registrationInfo);
|
||||
+ loadElementFromResource(registry, elementDecoder, registryOps, resourceKey, resource, registrationInfo, conversions); // Paper - pass conversions
|
||||
} catch (Exception var14) {
|
||||
errors.put(
|
||||
resourceKey,
|
||||
@@ -274,7 +275,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
- TagLoader.loadTagsForRegistry(resourceManager, registry);
|
||||
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), conversions); // Paper - run pre-freeze listeners
|
||||
+ TagLoader.loadTagsForRegistry(resourceManager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - tag lifecycle - add cause
|
||||
}
|
||||
|
||||
static <E> void loadContentsFromNetwork(
|
||||
@@ -291,6 +293,7 @@
|
||||
RegistryOps<JsonElement> registryOps2 = RegistryOps.create(JsonOps.INSTANCE, infoGetter);
|
||||
FileToIdConverter fileToIdConverter = FileToIdConverter.registry(registry.key());
|
||||
|
||||
+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(infoGetter); // Paper - create conversions
|
||||
for (RegistrySynchronization.PackedRegistryEntry packedRegistryEntry : networkedRegistryData.elements) {
|
||||
ResourceKey<E> resourceKey = ResourceKey.create(registry.key(), packedRegistryEntry.id());
|
||||
Optional<Tag> optional = packedRegistryEntry.data();
|
||||
@@ -309,7 +312,7 @@
|
||||
|
||||
try {
|
||||
Resource resource = factory.getResourceOrThrow(resourceLocation);
|
||||
- loadElementFromResource(registry, decoder, registryOps2, resourceKey, resource, NETWORK_REGISTRATION_INFO);
|
||||
+ loadElementFromResource(registry, decoder, registryOps2, resourceKey, resource, NETWORK_REGISTRATION_INFO, conversions); // Paper - pass conversions
|
||||
} catch (Exception var17) {
|
||||
loadingErrors.put(resourceKey, new IllegalStateException("Failed to parse local data", var17));
|
||||
}
|
||||
@@ -349,6 +352,7 @@
|
||||
|
||||
RegistryDataLoader.Loader<T> create(Lifecycle lifecycle, Map<ResourceKey<?>, Exception> errors) {
|
||||
WritableRegistry<T> writableRegistry = new MappedRegistry<>(this.key, lifecycle);
|
||||
+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(this.key, writableRegistry); // Paper - initialize API registry
|
||||
return new RegistryDataLoader.Loader<>(this, writableRegistry, errors);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
--- a/net/minecraft/resources/ResourceLocation.java
|
||||
+++ b/net/minecraft/resources/ResourceLocation.java
|
||||
@@ -32,6 +32,7 @@
|
||||
public static final char NAMESPACE_SEPARATOR = ':';
|
||||
public static final String DEFAULT_NAMESPACE = "minecraft";
|
||||
public static final String REALMS_NAMESPACE = "realms";
|
||||
+ public static final String PAPER_NAMESPACE = "paper"; // Paper
|
||||
private final String namespace;
|
||||
private final String path;
|
||||
|
||||
@@ -40,6 +41,13 @@
|
||||
|
||||
assert isValidPath(path);
|
||||
|
||||
+ // Paper start - Validate ResourceLocation
|
||||
+ // Check for the max network string length (capped at Short.MAX_VALUE) as well as the max bytes of a StringTag (length written as an unsigned short)
|
||||
+ final String resourceLocation = namespace + ":" + path;
|
||||
+ if (resourceLocation.length() > Short.MAX_VALUE || io.netty.buffer.ByteBufUtil.utf8MaxBytes(resourceLocation) > 2 * Short.MAX_VALUE + 1) {
|
||||
+ throw new ResourceLocationException("Resource location too long: " + resourceLocation);
|
||||
+ }
|
||||
+ // Paper end - Validate ResourceLocation
|
||||
this.namespace = namespace;
|
||||
this.path = path;
|
||||
}
|
||||
@@ -246,7 +254,7 @@
|
||||
|
||||
private static String assertValidNamespace(String namespace, String path) {
|
||||
if (!isValidNamespace(namespace)) {
|
||||
- throw new ResourceLocationException("Non [a-z0-9_.-] character in namespace of location: " + namespace + ":" + path);
|
||||
+ throw new ResourceLocationException("Non [a-z0-9_.-] character in namespace of location: " + org.apache.commons.lang3.StringUtils.normalizeSpace(namespace) + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper - Sanitize ResourceLocation error logging
|
||||
} else {
|
||||
return namespace;
|
||||
}
|
||||
@@ -267,7 +275,7 @@
|
||||
|
||||
private static String assertValidPath(String namespace, String path) {
|
||||
if (!isValidPath(path)) {
|
||||
- throw new ResourceLocationException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + path);
|
||||
+ throw new ResourceLocationException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper - Sanitize ResourceLocation error logging
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
--- a/net/minecraft/server/Bootstrap.java
|
||||
+++ b/net/minecraft/server/Bootstrap.java
|
||||
@@ -17,6 +17,9 @@
|
||||
import net.minecraft.core.dispenser.DispenseItemBehavior;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.locale.Language;
|
||||
+import net.minecraft.util.datafix.fixes.BlockStateData;
|
||||
+import net.minecraft.util.datafix.fixes.ItemIdFix;
|
||||
+import net.minecraft.util.datafix.fixes.ItemSpawnEggFix;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
@@ -30,7 +33,8 @@
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
-@SuppressForbidden(a = "System.out setup")
|
||||
+@SuppressForbidden(reason = "System.out setup")
|
||||
+// CraftBukkit end
|
||||
public class Bootstrap {
|
||||
|
||||
public static final PrintStream STDOUT = System.out;
|
||||
@@ -42,9 +46,27 @@
|
||||
|
||||
public static void bootStrap() {
|
||||
if (!Bootstrap.isBootstrapped) {
|
||||
+ // CraftBukkit start
|
||||
+ /*String name = Bootstrap.class.getSimpleName(); // Paper
|
||||
+ switch (name) {
|
||||
+ case "DispenserRegistry":
|
||||
+ break;
|
||||
+ case "Bootstrap":
|
||||
+ System.err.println("***************************************************************************");
|
||||
+ System.err.println("*** WARNING: This server jar may only be used for development purposes. ***");
|
||||
+ System.err.println("***************************************************************************");
|
||||
+ break;
|
||||
+ default:
|
||||
+ System.err.println("**********************************************************************");
|
||||
+ System.err.println("*** WARNING: This server jar is unsupported, use at your own risk. ***");
|
||||
+ System.err.println("**********************************************************************");
|
||||
+ break;
|
||||
+ }*/ // Paper
|
||||
+ // CraftBukkit end
|
||||
Bootstrap.isBootstrapped = true;
|
||||
Instant instant = Instant.now();
|
||||
|
||||
+ io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler.enterBootstrappers(); // Paper - Entrypoint for bootstrapping
|
||||
if (BuiltInRegistries.REGISTRY.keySet().isEmpty()) {
|
||||
throw new IllegalStateException("Unable to load registries");
|
||||
} else {
|
||||
@@ -56,11 +78,77 @@
|
||||
EntitySelectorOptions.bootStrap();
|
||||
DispenseItemBehavior.bootStrap();
|
||||
CauldronInteraction.bootStrap();
|
||||
- BuiltInRegistries.bootStrap();
|
||||
+ // Paper start
|
||||
+ BuiltInRegistries.bootStrap(() -> {
|
||||
+ });
|
||||
+ // Paper end
|
||||
CreativeModeTabs.validate();
|
||||
Bootstrap.wrapStreams();
|
||||
Bootstrap.bootstrapDuration.set(Duration.between(instant, Instant.now()).toMillis());
|
||||
}
|
||||
+ // CraftBukkit start - easier than fixing the decompile
|
||||
+ BlockStateData.register(1008, "{Name:'minecraft:oak_sign',Properties:{rotation:'0'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'0'}}");
|
||||
+ BlockStateData.register(1009, "{Name:'minecraft:oak_sign',Properties:{rotation:'1'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'1'}}");
|
||||
+ BlockStateData.register(1010, "{Name:'minecraft:oak_sign',Properties:{rotation:'2'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'2'}}");
|
||||
+ BlockStateData.register(1011, "{Name:'minecraft:oak_sign',Properties:{rotation:'3'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'3'}}");
|
||||
+ BlockStateData.register(1012, "{Name:'minecraft:oak_sign',Properties:{rotation:'4'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'4'}}");
|
||||
+ BlockStateData.register(1013, "{Name:'minecraft:oak_sign',Properties:{rotation:'5'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'5'}}");
|
||||
+ BlockStateData.register(1014, "{Name:'minecraft:oak_sign',Properties:{rotation:'6'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'6'}}");
|
||||
+ BlockStateData.register(1015, "{Name:'minecraft:oak_sign',Properties:{rotation:'7'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'7'}}");
|
||||
+ BlockStateData.register(1016, "{Name:'minecraft:oak_sign',Properties:{rotation:'8'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'8'}}");
|
||||
+ BlockStateData.register(1017, "{Name:'minecraft:oak_sign',Properties:{rotation:'9'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'9'}}");
|
||||
+ BlockStateData.register(1018, "{Name:'minecraft:oak_sign',Properties:{rotation:'10'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'10'}}");
|
||||
+ BlockStateData.register(1019, "{Name:'minecraft:oak_sign',Properties:{rotation:'11'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'11'}}");
|
||||
+ BlockStateData.register(1020, "{Name:'minecraft:oak_sign',Properties:{rotation:'12'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'12'}}");
|
||||
+ BlockStateData.register(1021, "{Name:'minecraft:oak_sign',Properties:{rotation:'13'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'13'}}");
|
||||
+ BlockStateData.register(1022, "{Name:'minecraft:oak_sign',Properties:{rotation:'14'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'14'}}");
|
||||
+ BlockStateData.register(1023, "{Name:'minecraft:oak_sign',Properties:{rotation:'15'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'15'}}");
|
||||
+ ItemIdFix.ITEM_NAMES.put(323, "minecraft:oak_sign");
|
||||
+
|
||||
+ BlockStateData.register(1440, "{Name:\'minecraft:portal\',Properties:{axis:\'x\'}}", new String[]{"{Name:\'minecraft:portal\',Properties:{axis:\'x\'}}"});
|
||||
+
|
||||
+ ItemIdFix.ITEM_NAMES.put(409, "minecraft:prismarine_shard");
|
||||
+ ItemIdFix.ITEM_NAMES.put(410, "minecraft:prismarine_crystals");
|
||||
+ ItemIdFix.ITEM_NAMES.put(411, "minecraft:rabbit");
|
||||
+ ItemIdFix.ITEM_NAMES.put(412, "minecraft:cooked_rabbit");
|
||||
+ ItemIdFix.ITEM_NAMES.put(413, "minecraft:rabbit_stew");
|
||||
+ ItemIdFix.ITEM_NAMES.put(414, "minecraft:rabbit_foot");
|
||||
+ ItemIdFix.ITEM_NAMES.put(415, "minecraft:rabbit_hide");
|
||||
+ ItemIdFix.ITEM_NAMES.put(416, "minecraft:armor_stand");
|
||||
+
|
||||
+ ItemIdFix.ITEM_NAMES.put(423, "minecraft:mutton");
|
||||
+ ItemIdFix.ITEM_NAMES.put(424, "minecraft:cooked_mutton");
|
||||
+ ItemIdFix.ITEM_NAMES.put(425, "minecraft:banner");
|
||||
+ ItemIdFix.ITEM_NAMES.put(426, "minecraft:end_crystal");
|
||||
+ ItemIdFix.ITEM_NAMES.put(427, "minecraft:spruce_door");
|
||||
+ ItemIdFix.ITEM_NAMES.put(428, "minecraft:birch_door");
|
||||
+ ItemIdFix.ITEM_NAMES.put(429, "minecraft:jungle_door");
|
||||
+ ItemIdFix.ITEM_NAMES.put(430, "minecraft:acacia_door");
|
||||
+ ItemIdFix.ITEM_NAMES.put(431, "minecraft:dark_oak_door");
|
||||
+ ItemIdFix.ITEM_NAMES.put(432, "minecraft:chorus_fruit");
|
||||
+ ItemIdFix.ITEM_NAMES.put(433, "minecraft:chorus_fruit_popped");
|
||||
+ ItemIdFix.ITEM_NAMES.put(434, "minecraft:beetroot");
|
||||
+ ItemIdFix.ITEM_NAMES.put(435, "minecraft:beetroot_seeds");
|
||||
+ ItemIdFix.ITEM_NAMES.put(436, "minecraft:beetroot_soup");
|
||||
+ ItemIdFix.ITEM_NAMES.put(437, "minecraft:dragon_breath");
|
||||
+ ItemIdFix.ITEM_NAMES.put(438, "minecraft:splash_potion");
|
||||
+ ItemIdFix.ITEM_NAMES.put(439, "minecraft:spectral_arrow");
|
||||
+ ItemIdFix.ITEM_NAMES.put(440, "minecraft:tipped_arrow");
|
||||
+ ItemIdFix.ITEM_NAMES.put(441, "minecraft:lingering_potion");
|
||||
+ ItemIdFix.ITEM_NAMES.put(442, "minecraft:shield");
|
||||
+ ItemIdFix.ITEM_NAMES.put(443, "minecraft:elytra");
|
||||
+ ItemIdFix.ITEM_NAMES.put(444, "minecraft:spruce_boat");
|
||||
+ ItemIdFix.ITEM_NAMES.put(445, "minecraft:birch_boat");
|
||||
+ ItemIdFix.ITEM_NAMES.put(446, "minecraft:jungle_boat");
|
||||
+ ItemIdFix.ITEM_NAMES.put(447, "minecraft:acacia_boat");
|
||||
+ ItemIdFix.ITEM_NAMES.put(448, "minecraft:dark_oak_boat");
|
||||
+ ItemIdFix.ITEM_NAMES.put(449, "minecraft:totem_of_undying");
|
||||
+ ItemIdFix.ITEM_NAMES.put(450, "minecraft:shulker_shell");
|
||||
+ ItemIdFix.ITEM_NAMES.put(452, "minecraft:iron_nugget");
|
||||
+ ItemIdFix.ITEM_NAMES.put(453, "minecraft:knowledge_book");
|
||||
+
|
||||
+ ItemSpawnEggFix.ID_TO_ENTITY[23] = "Arrow";
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
--- a/net/minecraft/server/Main.java
|
||||
+++ b/net/minecraft/server/Main.java
|
||||
@@ -38,6 +38,7 @@
|
||||
import net.minecraft.server.dedicated.DedicatedServerProperties;
|
||||
import net.minecraft.server.dedicated.DedicatedServerSettings;
|
||||
import net.minecraft.server.level.progress.LoggerChunkProgressListener;
|
||||
+import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.repository.PackRepository;
|
||||
import net.minecraft.server.packs.repository.ServerPacksSource;
|
||||
import net.minecraft.util.Mth;
|
||||
@@ -55,22 +56,31 @@
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPresets;
|
||||
import net.minecraft.world.level.storage.LevelDataAndDimensions;
|
||||
+import net.minecraft.world.level.storage.LevelResource;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.LevelSummary;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
-import net.minecraft.world.level.storage.WorldData;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
+// CraftBukkit start
|
||||
+import com.google.common.base.Charsets;
|
||||
+import java.io.InputStreamReader;
|
||||
+import java.util.concurrent.atomic.AtomicReference;
|
||||
+import net.minecraft.SharedConstants;
|
||||
+import org.bukkit.configuration.file.YamlConfiguration;
|
||||
+// CraftBukkit end
|
||||
+
|
||||
public class Main {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public Main() {}
|
||||
|
||||
- @SuppressForbidden(a = "System.out needed before bootstrap")
|
||||
+ @SuppressForbidden(reason = "System.out needed before bootstrap") // CraftBukkit - decompile error
|
||||
@DontObfuscate
|
||||
- public static void main(String[] args) {
|
||||
+ public static void main(final OptionSet optionset) { // CraftBukkit - replaces main(String[] astring)
|
||||
SharedConstants.tryDetectVersion();
|
||||
+ /* CraftBukkit start - Replace everything
|
||||
OptionParser optionparser = new OptionParser();
|
||||
OptionSpec<Void> optionspec = optionparser.accepts("nogui");
|
||||
OptionSpec<Void> optionspec1 = optionparser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits");
|
||||
@@ -90,50 +100,104 @@
|
||||
OptionSpec<String> optionspec15 = optionparser.nonOptions();
|
||||
|
||||
try {
|
||||
- OptionSet optionset = optionparser.parse(args);
|
||||
+ OptionSet optionset = optionparser.parse(astring);
|
||||
|
||||
if (optionset.has(optionspec8)) {
|
||||
optionparser.printHelpOn(System.err);
|
||||
return;
|
||||
}
|
||||
+ */ // CraftBukkit end
|
||||
|
||||
- Path path = (Path) optionset.valueOf(optionspec14);
|
||||
+ try {
|
||||
|
||||
+ Path path = (Path) optionset.valueOf("pidFile"); // CraftBukkit
|
||||
+
|
||||
if (path != null) {
|
||||
Main.writePidFile(path);
|
||||
}
|
||||
|
||||
CrashReport.preload();
|
||||
- if (optionset.has(optionspec13)) {
|
||||
+ if (optionset.has("jfrProfile")) { // CraftBukkit
|
||||
JvmProfiler.INSTANCE.start(Environment.SERVER);
|
||||
}
|
||||
|
||||
+ io.papermc.paper.plugin.PluginInitializerManager.load(optionset); // Paper
|
||||
Bootstrap.bootStrap();
|
||||
Bootstrap.validate();
|
||||
Util.startTimerHackThread();
|
||||
Path path1 = Paths.get("server.properties");
|
||||
- DedicatedServerSettings dedicatedserversettings = new DedicatedServerSettings(path1);
|
||||
+ DedicatedServerSettings dedicatedserversettings = new DedicatedServerSettings(optionset); // CraftBukkit - CLI argument support
|
||||
|
||||
dedicatedserversettings.forceSave();
|
||||
RegionFileVersion.configure(dedicatedserversettings.getProperties().regionFileComression);
|
||||
Path path2 = Paths.get("eula.txt");
|
||||
Eula eula = new Eula(path2);
|
||||
+ // Paper start - load config files early for access below if needed
|
||||
+ org.bukkit.configuration.file.YamlConfiguration bukkitConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("bukkit-settings"));
|
||||
+ org.bukkit.configuration.file.YamlConfiguration spigotConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("spigot-settings"));
|
||||
+ // Paper end - load config files early for access below if needed
|
||||
|
||||
- if (optionset.has(optionspec1)) {
|
||||
+ if (optionset.has("initSettings")) { // CraftBukkit
|
||||
+ // CraftBukkit start - SPIGOT-5761: Create bukkit.yml and commands.yml if not present
|
||||
+ File configFile = (File) optionset.valueOf("bukkit-settings");
|
||||
+ YamlConfiguration configuration = YamlConfiguration.loadConfiguration(configFile);
|
||||
+ configuration.options().copyDefaults(true);
|
||||
+ configuration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(Main.class.getClassLoader().getResourceAsStream("configurations/bukkit.yml"), Charsets.UTF_8)));
|
||||
+ configuration.save(configFile);
|
||||
+
|
||||
+ File commandFile = (File) optionset.valueOf("commands-settings");
|
||||
+ YamlConfiguration commandsConfiguration = YamlConfiguration.loadConfiguration(commandFile);
|
||||
+ commandsConfiguration.options().copyDefaults(true);
|
||||
+ commandsConfiguration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(Main.class.getClassLoader().getResourceAsStream("configurations/commands.yml"), Charsets.UTF_8)));
|
||||
+ commandsConfiguration.save(commandFile);
|
||||
+ // CraftBukkit end
|
||||
Main.LOGGER.info("Initialized '{}' and '{}'", path1.toAbsolutePath(), path2.toAbsolutePath());
|
||||
return;
|
||||
}
|
||||
|
||||
- if (!eula.hasAgreedToEULA()) {
|
||||
+ // Spigot Start
|
||||
+ boolean eulaAgreed = Boolean.getBoolean( "com.mojang.eula.agree" );
|
||||
+ if ( eulaAgreed )
|
||||
+ {
|
||||
+ System.err.println( "You have used the Spigot command line EULA agreement flag." );
|
||||
+ System.err.println( "By using this setting you are indicating your agreement to Mojang's EULA (https://account.mojang.com/documents/minecraft_eula)." );
|
||||
+ System.err.println( "If you do not agree to the above EULA please stop your server and remove this flag immediately." );
|
||||
+ }
|
||||
+ // Spigot End
|
||||
+ if (!eula.hasAgreedToEULA() && !eulaAgreed) { // Spigot
|
||||
Main.LOGGER.info("You need to agree to the EULA in order to run the server. Go to eula.txt for more info.");
|
||||
return;
|
||||
}
|
||||
|
||||
- File file = new File((String) optionset.valueOf(optionspec9));
|
||||
- Services services = Services.create(new YggdrasilAuthenticationService(Proxy.NO_PROXY), file);
|
||||
- String s = (String) Optional.ofNullable((String) optionset.valueOf(optionspec10)).orElse(dedicatedserversettings.getProperties().levelName);
|
||||
+ // Paper start - Detect headless JRE
|
||||
+ String awtException = io.papermc.paper.util.ServerEnvironment.awtDependencyCheck();
|
||||
+ if (awtException != null) {
|
||||
+ Main.LOGGER.error("You are using a headless JRE distribution.");
|
||||
+ Main.LOGGER.error("This distribution is missing certain graphic libraries that the Minecraft server needs to function.");
|
||||
+ Main.LOGGER.error("For instructions on how to install the non-headless JRE, see https://docs.papermc.io/misc/java-install");
|
||||
+ Main.LOGGER.error("");
|
||||
+ Main.LOGGER.error(awtException);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - Detect headless JRE
|
||||
+
|
||||
+ org.spigotmc.SpigotConfig.disabledAdvancements = spigotConfiguration.getStringList("advancements.disabled"); // Paper - fix SPIGOT-5885, must be set early in init
|
||||
+ // Paper start - fix SPIGOT-5824
|
||||
+ File file;
|
||||
+ File userCacheFile = new File(Services.USERID_CACHE_FILE);
|
||||
+ if (optionset.has("universe")) {
|
||||
+ file = (File) optionset.valueOf("universe"); // CraftBukkit
|
||||
+ userCacheFile = new File(file, Services.USERID_CACHE_FILE);
|
||||
+ } else {
|
||||
+ file = new File(bukkitConfiguration.getString("settings.world-container", "."));
|
||||
+ }
|
||||
+ // Paper end - fix SPIGOT-5824
|
||||
+ Services services = Services.create(new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY), file, userCacheFile, optionset); // Paper - pass OptionSet to load paper config files; override authentication service; fix world-container
|
||||
+ // CraftBukkit start
|
||||
+ String s = (String) Optional.ofNullable((String) optionset.valueOf("world")).orElse(dedicatedserversettings.getProperties().levelName);
|
||||
LevelStorageSource convertable = LevelStorageSource.createDefault(file.toPath());
|
||||
- LevelStorageSource.LevelStorageAccess convertable_conversionsession = convertable.validateAndCreateAccess(s);
|
||||
+ LevelStorageSource.LevelStorageAccess convertable_conversionsession = convertable.validateAndCreateAccess(s, LevelStem.OVERWORLD);
|
||||
+ // CraftBukkit end
|
||||
Dynamic dynamic;
|
||||
|
||||
if (convertable_conversionsession.hasWorldData()) {
|
||||
@@ -174,13 +238,31 @@
|
||||
}
|
||||
|
||||
Dynamic<?> dynamic1 = dynamic;
|
||||
- boolean flag = optionset.has(optionspec7);
|
||||
+ boolean flag = optionset.has("safeMode"); // CraftBukkit
|
||||
|
||||
if (flag) {
|
||||
Main.LOGGER.warn("Safe mode active, only vanilla datapack will be loaded");
|
||||
}
|
||||
|
||||
PackRepository resourcepackrepository = ServerPacksSource.createPackRepository(convertable_conversionsession);
|
||||
+ // CraftBukkit start
|
||||
+ File bukkitDataPackFolder = new File(convertable_conversionsession.getLevelPath(LevelResource.DATAPACK_DIR).toFile(), "bukkit");
|
||||
+ if (!bukkitDataPackFolder.exists()) {
|
||||
+ bukkitDataPackFolder.mkdirs();
|
||||
+ }
|
||||
+ File mcMeta = new File(bukkitDataPackFolder, "pack.mcmeta");
|
||||
+ try {
|
||||
+ com.google.common.io.Files.write("{\n"
|
||||
+ + " \"pack\": {\n"
|
||||
+ + " \"description\": \"Data pack for resources provided by Bukkit plugins\",\n"
|
||||
+ + " \"pack_format\": " + SharedConstants.getCurrentVersion().getPackVersion(PackType.SERVER_DATA) + "\n"
|
||||
+ + " }\n"
|
||||
+ + "}\n", mcMeta, com.google.common.base.Charsets.UTF_8);
|
||||
+ } catch (java.io.IOException ex) {
|
||||
+ throw new RuntimeException("Could not initialize Bukkit datapack", ex);
|
||||
+ }
|
||||
+ AtomicReference<WorldLoader.DataLoadContext> worldLoader = new AtomicReference<>();
|
||||
+ // CraftBukkit end
|
||||
|
||||
WorldStem worldstem;
|
||||
|
||||
@@ -189,6 +271,7 @@
|
||||
|
||||
worldstem = (WorldStem) Util.blockUntilDone((executor) -> {
|
||||
return WorldLoader.load(worldloader_c, (worldloader_a) -> {
|
||||
+ worldLoader.set(worldloader_a); // CraftBukkit
|
||||
Registry<LevelStem> iregistry = worldloader_a.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM);
|
||||
|
||||
if (dynamic1 != null) {
|
||||
@@ -201,7 +284,7 @@
|
||||
WorldOptions worldoptions;
|
||||
WorldDimensions worlddimensions;
|
||||
|
||||
- if (optionset.has(optionspec2)) {
|
||||
+ if (optionset.has("demo")) { // CraftBukkit
|
||||
worldsettings = MinecraftServer.DEMO_SETTINGS;
|
||||
worldoptions = WorldOptions.DEMO_OPTIONS;
|
||||
worlddimensions = WorldPresets.createNormalWorldDimensions(worldloader_a.datapackWorldgen());
|
||||
@@ -209,7 +292,7 @@
|
||||
DedicatedServerProperties dedicatedserverproperties = dedicatedserversettings.getProperties();
|
||||
|
||||
worldsettings = new LevelSettings(dedicatedserverproperties.levelName, dedicatedserverproperties.gamemode, dedicatedserverproperties.hardcore, dedicatedserverproperties.difficulty, false, new GameRules(worldloader_a.dataConfiguration().enabledFeatures()), worldloader_a.dataConfiguration());
|
||||
- worldoptions = optionset.has(optionspec3) ? dedicatedserverproperties.worldOptions.withBonusChest(true) : dedicatedserverproperties.worldOptions;
|
||||
+ worldoptions = optionset.has("bonusChest") ? dedicatedserverproperties.worldOptions.withBonusChest(true) : dedicatedserverproperties.worldOptions; // CraftBukkit
|
||||
worlddimensions = dedicatedserverproperties.createDimensions(worldloader_a.datapackWorldgen());
|
||||
}
|
||||
|
||||
@@ -225,32 +308,47 @@
|
||||
return;
|
||||
}
|
||||
|
||||
- RegistryAccess.Frozen iregistrycustom_dimension = worldstem.registries().compositeAccess();
|
||||
+ /*
|
||||
+ IRegistryCustom.Dimension iregistrycustom_dimension = worldstem.registries().compositeAccess();
|
||||
boolean flag1 = optionset.has(optionspec6);
|
||||
|
||||
if (optionset.has(optionspec4) || flag1) {
|
||||
- Main.forceUpgrade(convertable_conversionsession, DataFixers.getDataFixer(), optionset.has(optionspec5), () -> {
|
||||
+ forceUpgrade(convertable_conversionsession, DataConverterRegistry.getDataFixer(), optionset.has(optionspec5), () -> {
|
||||
return true;
|
||||
}, iregistrycustom_dimension, flag1);
|
||||
}
|
||||
|
||||
- WorldData savedata = worldstem.worldData();
|
||||
+ SaveData savedata = worldstem.worldData();
|
||||
|
||||
convertable_conversionsession.saveDataTag(iregistrycustom_dimension, savedata);
|
||||
+ */
|
||||
final DedicatedServer dedicatedserver = (DedicatedServer) MinecraftServer.spin((thread) -> {
|
||||
- DedicatedServer dedicatedserver1 = new DedicatedServer(thread, convertable_conversionsession, resourcepackrepository, worldstem, dedicatedserversettings, DataFixers.getDataFixer(), services, LoggerChunkProgressListener::createFromGameruleRadius);
|
||||
+ DedicatedServer dedicatedserver1 = new DedicatedServer(optionset, worldLoader.get(), thread, convertable_conversionsession, resourcepackrepository, worldstem, dedicatedserversettings, DataFixers.getDataFixer(), services, LoggerChunkProgressListener::createFromGameruleRadius);
|
||||
|
||||
+ /*
|
||||
dedicatedserver1.setPort((Integer) optionset.valueOf(optionspec11));
|
||||
- dedicatedserver1.setDemo(optionset.has(optionspec2));
|
||||
+ */
|
||||
+ dedicatedserver1.setDemo(optionset.has("demo")); // Paper
|
||||
+ /*
|
||||
dedicatedserver1.setId((String) optionset.valueOf(optionspec12));
|
||||
- boolean flag2 = !optionset.has(optionspec) && !optionset.valuesOf(optionspec15).contains("nogui");
|
||||
+ */
|
||||
+ boolean flag2 = !optionset.has("nogui") && !optionset.nonOptionArguments().contains("nogui");
|
||||
|
||||
+ if(!Boolean.parseBoolean(System.getenv().getOrDefault("PAPER_DISABLE_SERVER_GUI", String.valueOf(false)))) // Paper - Add environment variable to disable server gui
|
||||
if (flag2 && !GraphicsEnvironment.isHeadless()) {
|
||||
dedicatedserver1.showGui();
|
||||
}
|
||||
|
||||
+ if (optionset.has("port")) {
|
||||
+ int port = (Integer) optionset.valueOf("port");
|
||||
+ if (port > 0) {
|
||||
+ dedicatedserver1.setPort(port);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return dedicatedserver1;
|
||||
});
|
||||
+ /* CraftBukkit start
|
||||
Thread thread = new Thread("Server Shutdown Thread") {
|
||||
public void run() {
|
||||
dedicatedserver.halt(true);
|
||||
@@ -259,6 +357,7 @@
|
||||
|
||||
thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(Main.LOGGER));
|
||||
Runtime.getRuntime().addShutdownHook(thread);
|
||||
+ */ // CraftBukkit end
|
||||
} catch (Exception exception1) {
|
||||
Main.LOGGER.error(LogUtils.FATAL_MARKER, "Failed to start the minecraft server", exception1);
|
||||
}
|
||||
@@ -295,7 +394,7 @@
|
||||
}
|
||||
|
||||
public static void forceUpgrade(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, boolean eraseCache, BooleanSupplier continueCheck, RegistryAccess dynamicRegistryManager, boolean recreateRegionFiles) {
|
||||
- Main.LOGGER.info("Forcing world upgrade!");
|
||||
+ Main.LOGGER.info("Forcing world upgrade! {}", session.getLevelId()); // CraftBukkit
|
||||
WorldUpgrader worldupgrader = new WorldUpgrader(session, dataFixer, dynamicRegistryManager, eraseCache, recreateRegionFiles);
|
||||
|
||||
try {
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
--- a/net/minecraft/server/PlayerAdvancements.java
|
||||
+++ b/net/minecraft/server/PlayerAdvancements.java
|
||||
@@ -50,7 +50,7 @@
|
||||
public class PlayerAdvancements {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
- private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().create();
|
||||
+ private static final Gson GSON = (new GsonBuilder()).create(); // Paper - Remove pretty printing from advancements
|
||||
private final PlayerList playerList;
|
||||
private final Path playerSavePath;
|
||||
private AdvancementTree tree;
|
||||
@@ -63,6 +63,7 @@
|
||||
private AdvancementHolder lastSelectedTab;
|
||||
private boolean isFirstPacket = true;
|
||||
private final Codec<PlayerAdvancements.Data> codec;
|
||||
+ public final Map<net.minecraft.advancements.critereon.SimpleCriterionTrigger<?>, Set<CriterionTrigger.Listener<?>>> criterionData = new java.util.IdentityHashMap<>(); // Paper - fix advancement data player leakage
|
||||
|
||||
public PlayerAdvancements(DataFixer dataFixer, PlayerList playerManager, ServerAdvancementManager advancementLoader, Path filePath, ServerPlayer owner) {
|
||||
this.playerList = playerManager;
|
||||
@@ -162,6 +163,7 @@
|
||||
}
|
||||
|
||||
public void save() {
|
||||
+ if (org.spigotmc.SpigotConfig.disableAdvancementSaving) return; // Spigot
|
||||
JsonElement jsonelement = (JsonElement) this.codec.encodeStart(JsonOps.INSTANCE, this.asData()).getOrThrow();
|
||||
|
||||
try {
|
||||
@@ -196,6 +198,7 @@
|
||||
AdvancementHolder advancementholder = loader.get(minecraftkey);
|
||||
|
||||
if (advancementholder == null) {
|
||||
+ if (!minecraftkey.getNamespace().equals("minecraft")) return; // CraftBukkit
|
||||
PlayerAdvancements.LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", minecraftkey, this.playerSavePath);
|
||||
} else {
|
||||
this.startProgress(advancementholder, advancementprogress);
|
||||
@@ -223,14 +226,31 @@
|
||||
boolean flag1 = advancementprogress.isDone();
|
||||
|
||||
if (advancementprogress.grantProgress(criterionName)) {
|
||||
+ // Paper start - Add PlayerAdvancementCriterionGrantEvent
|
||||
+ if (!new com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent(this.player.getBukkitEntity(), advancement.toBukkit(), criterionName).callEvent()) {
|
||||
+ advancementprogress.revokeProgress(criterionName);
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end - Add PlayerAdvancementCriterionGrantEvent
|
||||
this.unregisterListeners(advancement);
|
||||
this.progressChanged.add(advancement);
|
||||
flag = true;
|
||||
if (!flag1 && advancementprogress.isDone()) {
|
||||
+ // Paper start - Add Adventure message to PlayerAdvancementDoneEvent
|
||||
+ final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> {
|
||||
+ return java.util.Optional.ofNullable(
|
||||
+ info.shouldAnnounceChat() ? io.papermc.paper.adventure.PaperAdventure.asAdventure(info.getType().createAnnouncement(advancement, this.player)) : null
|
||||
+ );
|
||||
+ }).orElse(null);
|
||||
+ final org.bukkit.event.player.PlayerAdvancementDoneEvent event = new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.toBukkit(), message);
|
||||
+ this.player.level().getCraftServer().getPluginManager().callEvent(event); // CraftBukkit
|
||||
+ // Paper end
|
||||
advancement.value().rewards().grant(this.player);
|
||||
advancement.value().display().ifPresent((advancementdisplay) -> {
|
||||
- if (advancementdisplay.shouldAnnounceChat() && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) {
|
||||
- this.playerList.broadcastSystemMessage(advancementdisplay.getType().createAnnouncement(advancement, this.player), false);
|
||||
+ // Paper start - Add Adventure message to PlayerAdvancementDoneEvent
|
||||
+ if (event.message() != null && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) {
|
||||
+ this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
--- a/net/minecraft/server/ReloadableServerRegistries.java
|
||||
+++ b/net/minecraft/server/ReloadableServerRegistries.java
|
||||
@@ -50,8 +50,9 @@
|
||||
);
|
||||
HolderLookup.Provider provider = HolderLookup.Provider.create(list.stream());
|
||||
RegistryOps<JsonElement> registryOps = provider.createSerializationContext(JsonOps.INSTANCE);
|
||||
+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryOps.lookupProvider); // Paper
|
||||
List<CompletableFuture<WritableRegistry<?>>> list2 = LootDataType.values()
|
||||
- .map(type -> scheduleRegistryLoad((LootDataType<?>)type, registryOps, resourceManager, prepareExecutor))
|
||||
+ .map(type -> scheduleRegistryLoad((LootDataType<?>)type, registryOps, resourceManager, prepareExecutor, conversions)) // Paper
|
||||
.toList();
|
||||
CompletableFuture<List<WritableRegistry<?>>> completableFuture = Util.sequence(list2);
|
||||
return completableFuture.thenApplyAsync(
|
||||
@@ -60,14 +61,15 @@
|
||||
}
|
||||
|
||||
private static <T> CompletableFuture<WritableRegistry<?>> scheduleRegistryLoad(
|
||||
- LootDataType<T> type, RegistryOps<JsonElement> ops, ResourceManager resourceManager, Executor prepareExecutor
|
||||
+ LootDataType<T> type, RegistryOps<JsonElement> ops, ResourceManager resourceManager, Executor prepareExecutor, io.papermc.paper.registry.data.util.Conversions conversions // Paper
|
||||
) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
WritableRegistry<T> writableRegistry = new MappedRegistry<>(type.registryKey(), Lifecycle.experimental());
|
||||
+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerReloadableRegistry(type.registryKey(), writableRegistry); // Paper - register reloadable registry
|
||||
Map<ResourceLocation, T> map = new HashMap<>();
|
||||
SimpleJsonResourceReloadListener.scanDirectory(resourceManager, type.registryKey(), ops, type.codec(), map);
|
||||
- map.forEach((id, value) -> writableRegistry.register(ResourceKey.create(type.registryKey(), id), (T)value, DEFAULT_REGISTRATION_INFO));
|
||||
- TagLoader.loadTagsForRegistry(resourceManager, writableRegistry);
|
||||
+ map.forEach((id, value) -> io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(writableRegistry, ResourceKey.create(type.registryKey(), id), value, DEFAULT_REGISTRATION_INFO, conversions)); // Paper - register with listeners
|
||||
+ TagLoader.loadTagsForRegistry(resourceManager, writableRegistry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // Paper - tag life cycle - reload
|
||||
return writableRegistry;
|
||||
}, prepareExecutor);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
--- a/net/minecraft/server/ReloadableServerResources.java
|
||||
+++ b/net/minecraft/server/ReloadableServerResources.java
|
||||
@@ -39,6 +39,7 @@
|
||||
this.postponedTags = pendingTagLoads;
|
||||
this.recipes = new RecipeManager(registries);
|
||||
this.commands = new Commands(environment, CommandBuildContext.simple(registries, enabledFeatures));
|
||||
+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setDispatcher(this.commands, CommandBuildContext.simple(registries, enabledFeatures)); // Paper - Brigadier Command API
|
||||
this.advancements = new ServerAdvancementManager(registries);
|
||||
this.functionLibrary = new ServerFunctionLibrary(functionPermissionLevel, this.commands.getDispatcher());
|
||||
}
|
||||
@@ -83,6 +84,14 @@
|
||||
ReloadableServerResources reloadableServerResources = new ReloadableServerResources(
|
||||
reloadResult.layers(), reloadResult.lookupWithUpdatedTags(), enabledFeatures, environment, pendingTagLoads, functionPermissionLevel
|
||||
);
|
||||
+ // Paper start - call commands event for bootstraps
|
||||
+ //noinspection ConstantValue
|
||||
+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(
|
||||
+ io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS,
|
||||
+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE,
|
||||
+ io.papermc.paper.plugin.bootstrap.BootstrapContext.class,
|
||||
+ MinecraftServer.getServer() == null ? io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL : io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD);
|
||||
+ // Paper end - call commands event
|
||||
return SimpleReloadInstance.create(
|
||||
resourceManager,
|
||||
reloadableServerResources.listeners(),
|
||||
@@ -0,0 +1,39 @@
|
||||
--- a/net/minecraft/server/ServerAdvancementManager.java
|
||||
+++ b/net/minecraft/server/ServerAdvancementManager.java
|
||||
@@ -21,10 +21,14 @@
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
+// CraftBukkit start
|
||||
+import java.util.HashMap;
|
||||
+// CraftBukkit end
|
||||
+
|
||||
public class ServerAdvancementManager extends SimpleJsonResourceReloadListener<Advancement> {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
- public Map<ResourceLocation, AdvancementHolder> advancements = Map.of();
|
||||
+ public Map<ResourceLocation, AdvancementHolder> advancements = new HashMap<>(); // CraftBukkit - SPIGOT-7734: mutable
|
||||
private AdvancementTree tree = new AdvancementTree();
|
||||
private final HolderLookup.Provider registries;
|
||||
|
||||
@@ -37,13 +41,19 @@
|
||||
Builder<ResourceLocation, AdvancementHolder> builder = ImmutableMap.builder();
|
||||
|
||||
prepared.forEach((minecraftkey, advancement) -> {
|
||||
+ // Spigot start
|
||||
+ if (org.spigotmc.SpigotConfig.disabledAdvancements != null && (org.spigotmc.SpigotConfig.disabledAdvancements.contains("*") || org.spigotmc.SpigotConfig.disabledAdvancements.contains(minecraftkey.toString()) || org.spigotmc.SpigotConfig.disabledAdvancements.contains(minecraftkey.getNamespace()))) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Spigot end
|
||||
this.validate(minecraftkey, advancement);
|
||||
builder.put(minecraftkey, new AdvancementHolder(minecraftkey, advancement));
|
||||
});
|
||||
- this.advancements = builder.buildOrThrow();
|
||||
+ this.advancements = new HashMap<>(builder.buildOrThrow()); // CraftBukkit - SPIGOT-7734: mutable
|
||||
AdvancementTree advancementtree = new AdvancementTree();
|
||||
|
||||
advancementtree.addAll(this.advancements.values());
|
||||
+ LOGGER.info("Loaded {} advancements", advancementtree.nodes().size()); // Paper - Improve logging and errors; moved from AdvancementTree#addAll
|
||||
Iterator iterator = advancementtree.roots().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/server/ServerFunctionLibrary.java
|
||||
+++ b/net/minecraft/server/ServerFunctionLibrary.java
|
||||
@@ -113,7 +113,7 @@
|
||||
return null;
|
||||
}).join());
|
||||
this.functions = builder.build();
|
||||
- this.tags = this.tagsLoader.build((Map<ResourceLocation, List<TagLoader.EntryWithSource>>)intermediate.getFirst());
|
||||
+ this.tags = this.tagsLoader.build((Map<ResourceLocation, List<TagLoader.EntryWithSource>>)intermediate.getFirst(), null); // Paper - command function tags are not implemented yet
|
||||
},
|
||||
applyExecutor
|
||||
);
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/server/ServerFunctionManager.java
|
||||
+++ b/net/minecraft/server/ServerFunctionManager.java
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
|
||||
public CommandDispatcher<CommandSourceStack> getDispatcher() {
|
||||
- return this.server.getCommands().getDispatcher();
|
||||
+ return this.server.getCommands().getDispatcher(); // CraftBukkit // Paper - Don't override command dispatcher
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
@@ -0,0 +1,168 @@
|
||||
--- a/net/minecraft/server/ServerScoreboard.java
|
||||
+++ b/net/minecraft/server/ServerScoreboard.java
|
||||
@@ -42,7 +42,7 @@
|
||||
protected void onScoreChanged(ScoreHolder scoreHolder, Objective objective, Score score) {
|
||||
super.onScoreChanged(scoreHolder, objective, score);
|
||||
if (this.trackedObjectives.contains(objective)) {
|
||||
- this.server.getPlayerList().broadcastAll(new ClientboundSetScorePacket(scoreHolder.getScoreboardName(), objective.getName(), score.value(), Optional.ofNullable(score.display()), Optional.ofNullable(score.numberFormat())));
|
||||
+ this.broadcastAll(new ClientboundSetScorePacket(scoreHolder.getScoreboardName(), objective.getName(), score.value(), Optional.ofNullable(score.display()), Optional.ofNullable(score.numberFormat()))); // CraftBukkit
|
||||
}
|
||||
|
||||
this.setDirty();
|
||||
@@ -57,7 +57,7 @@
|
||||
@Override
|
||||
public void onPlayerRemoved(ScoreHolder scoreHolder) {
|
||||
super.onPlayerRemoved(scoreHolder);
|
||||
- this.server.getPlayerList().broadcastAll(new ClientboundResetScorePacket(scoreHolder.getScoreboardName(), (String) null));
|
||||
+ this.broadcastAll(new ClientboundResetScorePacket(scoreHolder.getScoreboardName(), (String) null)); // CraftBukkit
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
public void onPlayerScoreRemoved(ScoreHolder scoreHolder, Objective objective) {
|
||||
super.onPlayerScoreRemoved(scoreHolder, objective);
|
||||
if (this.trackedObjectives.contains(objective)) {
|
||||
- this.server.getPlayerList().broadcastAll(new ClientboundResetScorePacket(scoreHolder.getScoreboardName(), objective.getName()));
|
||||
+ this.broadcastAll(new ClientboundResetScorePacket(scoreHolder.getScoreboardName(), objective.getName())); // CraftBukkit
|
||||
}
|
||||
|
||||
this.setDirty();
|
||||
@@ -78,7 +78,7 @@
|
||||
super.setDisplayObjective(slot, objective);
|
||||
if (scoreboardobjective1 != objective && scoreboardobjective1 != null) {
|
||||
if (this.getObjectiveDisplaySlotCount(scoreboardobjective1) > 0) {
|
||||
- this.server.getPlayerList().broadcastAll(new ClientboundSetDisplayObjectivePacket(slot, objective));
|
||||
+ this.broadcastAll(new ClientboundSetDisplayObjectivePacket(slot, objective)); // CraftBukkit
|
||||
} else {
|
||||
this.stopTrackingObjective(scoreboardobjective1);
|
||||
}
|
||||
@@ -86,7 +86,7 @@
|
||||
|
||||
if (objective != null) {
|
||||
if (this.trackedObjectives.contains(objective)) {
|
||||
- this.server.getPlayerList().broadcastAll(new ClientboundSetDisplayObjectivePacket(slot, objective));
|
||||
+ this.broadcastAll(new ClientboundSetDisplayObjectivePacket(slot, objective)); // CraftBukkit
|
||||
} else {
|
||||
this.startTrackingObjective(objective);
|
||||
}
|
||||
@@ -98,7 +98,7 @@
|
||||
@Override
|
||||
public boolean addPlayerToTeam(String scoreHolderName, PlayerTeam team) {
|
||||
if (super.addPlayerToTeam(scoreHolderName, team)) {
|
||||
- this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, scoreHolderName, ClientboundSetPlayerTeamPacket.Action.ADD));
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, scoreHolderName, ClientboundSetPlayerTeamPacket.Action.ADD)); // CraftBukkit
|
||||
this.setDirty();
|
||||
return true;
|
||||
} else {
|
||||
@@ -106,13 +106,43 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Multiple Entries with Scoreboards
|
||||
+ public boolean addPlayersToTeam(java.util.Collection<String> players, PlayerTeam team) {
|
||||
+ boolean anyAdded = false;
|
||||
+ for (String playerName : players) {
|
||||
+ if (super.addPlayerToTeam(playerName, team)) {
|
||||
+ anyAdded = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (anyAdded) {
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createMultiplePlayerPacket(team, players, ClientboundSetPlayerTeamPacket.Action.ADD));
|
||||
+ this.setDirty();
|
||||
+ return true;
|
||||
+ } else {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Multiple Entries with Scoreboards
|
||||
+
|
||||
@Override
|
||||
public void removePlayerFromTeam(String scoreHolderName, PlayerTeam team) {
|
||||
super.removePlayerFromTeam(scoreHolderName, team);
|
||||
- this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, scoreHolderName, ClientboundSetPlayerTeamPacket.Action.REMOVE));
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, scoreHolderName, ClientboundSetPlayerTeamPacket.Action.REMOVE)); // CraftBukkit
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
+ // Paper start - Multiple Entries with Scoreboards
|
||||
+ public void removePlayersFromTeam(java.util.Collection<String> players, PlayerTeam team) {
|
||||
+ for (String playerName : players) {
|
||||
+ super.removePlayerFromTeam(playerName, team);
|
||||
+ }
|
||||
+
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createMultiplePlayerPacket(team, players, ClientboundSetPlayerTeamPacket.Action.REMOVE));
|
||||
+ this.setDirty();
|
||||
+ }
|
||||
+ // Paper end - Multiple Entries with Scoreboards
|
||||
+
|
||||
@Override
|
||||
public void onObjectiveAdded(Objective objective) {
|
||||
super.onObjectiveAdded(objective);
|
||||
@@ -123,7 +153,7 @@
|
||||
public void onObjectiveChanged(Objective objective) {
|
||||
super.onObjectiveChanged(objective);
|
||||
if (this.trackedObjectives.contains(objective)) {
|
||||
- this.server.getPlayerList().broadcastAll(new ClientboundSetObjectivePacket(objective, 2));
|
||||
+ this.broadcastAll(new ClientboundSetObjectivePacket(objective, 2)); // CraftBukkit
|
||||
}
|
||||
|
||||
this.setDirty();
|
||||
@@ -142,21 +172,21 @@
|
||||
@Override
|
||||
public void onTeamAdded(PlayerTeam team) {
|
||||
super.onTeamAdded(team);
|
||||
- this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true));
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true)); // CraftBukkit
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTeamChanged(PlayerTeam team) {
|
||||
super.onTeamChanged(team);
|
||||
- this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, false));
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, false)); // CraftBukkit
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTeamRemoved(PlayerTeam team) {
|
||||
super.onTeamRemoved(team);
|
||||
- this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createRemovePacket(team));
|
||||
+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createRemovePacket(team)); // CraftBukkit
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
@@ -207,6 +237,7 @@
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||
+ if (entityplayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board
|
||||
Iterator iterator1 = list.iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
@@ -243,6 +274,7 @@
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||
+ if (entityplayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board
|
||||
Iterator iterator1 = list.iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
@@ -287,6 +319,16 @@
|
||||
return this.createData().load(nbt, registries);
|
||||
}
|
||||
|
||||
+ // CraftBukkit start - Send to players
|
||||
+ private void broadcastAll(Packet packet) {
|
||||
+ for (ServerPlayer entityplayer : (List<ServerPlayer>) this.server.getPlayerList().players) {
|
||||
+ if (entityplayer.getBukkitEntity().getScoreboard().getHandle() == this) {
|
||||
+ entityplayer.connection.send(packet);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
public static enum Method {
|
||||
|
||||
CHANGE, REMOVE;
|
||||
@@ -0,0 +1,53 @@
|
||||
--- a/net/minecraft/server/ServerTickRateManager.java
|
||||
+++ b/net/minecraft/server/ServerTickRateManager.java
|
||||
@@ -59,8 +59,14 @@
|
||||
}
|
||||
|
||||
public boolean stopSprinting() {
|
||||
+ // CraftBukkit start, add sendLog parameter
|
||||
+ return this.stopSprinting(true);
|
||||
+ }
|
||||
+
|
||||
+ public boolean stopSprinting(boolean sendLog) {
|
||||
+ // CraftBukkit end
|
||||
if (this.remainingSprintTicks > 0L) {
|
||||
- this.finishTickSprint();
|
||||
+ this.finishTickSprint(sendLog); // CraftBukkit - add sendLog parameter
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -78,7 +84,7 @@
|
||||
return flag;
|
||||
}
|
||||
|
||||
- private void finishTickSprint() {
|
||||
+ private void finishTickSprint(boolean sendLog) { // CraftBukkit - add sendLog parameter
|
||||
long i = this.scheduledCurrentSprintTicks - this.remainingSprintTicks;
|
||||
double d0 = Math.max(1.0D, (double) this.sprintTimeSpend) / (double) TimeUtil.NANOSECONDS_PER_MILLISECOND;
|
||||
int j = (int) ((double) (TimeUtil.MILLISECONDS_PER_SECOND * i) / d0);
|
||||
@@ -86,9 +92,13 @@
|
||||
|
||||
this.scheduledCurrentSprintTicks = 0L;
|
||||
this.sprintTimeSpend = 0L;
|
||||
- this.server.createCommandSourceStack().sendSuccess(() -> {
|
||||
- return Component.translatable("commands.tick.sprint.report", j, s);
|
||||
- }, true);
|
||||
+ // CraftBukkit start - add sendLog parameter
|
||||
+ if (sendLog) {
|
||||
+ this.server.createCommandSourceStack().sendSuccess(() -> {
|
||||
+ return Component.translatable("commands.tick.sprint.report", j, s);
|
||||
+ }, true);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.remainingSprintTicks = 0L;
|
||||
this.setFrozen(this.previousIsFrozen);
|
||||
this.server.onTickRateChanged();
|
||||
@@ -102,7 +112,7 @@
|
||||
--this.remainingSprintTicks;
|
||||
return true;
|
||||
} else {
|
||||
- this.finishTickSprint();
|
||||
+ this.finishTickSprint(true); // CraftBukkit - add sendLog parameter
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user