diff --git a/paper-server/src/main/java/io/papermc/paper/command/PaperCommand.java b/paper-server/src/main/java/io/papermc/paper/command/PaperCommand.java index 33b10d4eb634..14adabba963c 100644 --- a/paper-server/src/main/java/io/papermc/paper/command/PaperCommand.java +++ b/paper-server/src/main/java/io/papermc/paper/command/PaperCommand.java @@ -8,6 +8,7 @@ import io.papermc.paper.command.subcommands.HeapDumpCommand; import io.papermc.paper.command.subcommands.MobcapsCommand; import io.papermc.paper.command.subcommands.ReloadCommand; +import io.papermc.paper.command.subcommands.ResendChunksCommand; import io.papermc.paper.command.subcommands.SyncLoadInfoCommand; import io.papermc.paper.command.subcommands.VersionCommand; import it.unimi.dsi.fastutil.Pair; @@ -51,6 +52,7 @@ public final class PaperCommand extends Command { commands.put(Set.of("dumpitem"), new DumpItemCommand()); commands.put(Set.of("mobcaps", "playermobcaps"), new MobcapsCommand()); commands.put(Set.of("dumplisteners"), new DumpListenersCommand()); + commands.put(Set.of("resendchunks"), new ResendChunksCommand()); FeatureHooks.registerPaperCommands(commands); return commands.entrySet().stream() diff --git a/paper-server/src/main/java/io/papermc/paper/command/subcommands/ResendChunksCommand.java b/paper-server/src/main/java/io/papermc/paper/command/subcommands/ResendChunksCommand.java new file mode 100644 index 000000000000..dc821733a8f9 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/command/subcommands/ResendChunksCommand.java @@ -0,0 +1,42 @@ +package io.papermc.paper.command.subcommands; + +import io.papermc.paper.command.PaperSubcommand; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.PlayerChunkSender; +import net.minecraft.world.level.ChunkPos; +import org.bukkit.Chunk; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.jetbrains.annotations.NotNull; + +import java.util.stream.Collectors; + +@DefaultQualifier(NonNull.class) +public final class ResendChunksCommand implements PaperSubcommand { + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { + if (sender instanceof Player player) { + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + PlayerChunkSender chunkSender = serverPlayer.connection.chunkSender; + int resent = 0; + + for (ChunkPos chunkPos : serverPlayer.getBukkitEntity().getSentChunks().stream().map(ResendChunksCommand::bukkitChunk2ChunkPos).collect(Collectors.toSet())) { + chunkSender.dropChunk(serverPlayer, chunkPos); + PlayerChunkSender.sendChunk(serverPlayer.connection, serverPlayer.level(), serverPlayer.level().getChunk(chunkPos.x, chunkPos.z)); + resent++; + } + serverPlayer.sendSystemMessage(Component.literal("Resent " + resent + " chunks to client")); + } else { + sender.sendMessage("Only a player can execute this command."); + } + return true; + } + + public static @NotNull ChunkPos bukkitChunk2ChunkPos(@NotNull Chunk chunk) { + return new ChunkPos(chunk.getX(), chunk.getZ()); + } +}