more patches updated
This commit is contained in:
		
					parent
					
						
							
								2b25404eca
							
						
					
				
			
			
				commit
				
					
						f629237348
					
				
			
		
					 8 changed files with 50 additions and 85 deletions
				
			
		
							
								
								
									
										430
									
								
								patches/server/Chunk-debug-command.patch
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										430
									
								
								patches/server/Chunk-debug-command.patch
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,430 @@ | |||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Spottedleaf <Spottedleaf@users.noreply.github.com> | ||||
| Date: Sat, 1 Jun 2019 13:00:55 -0700 | ||||
| Subject: [PATCH] Chunk debug command | ||||
| 
 | ||||
| Prints all chunk information to a text file into the debug | ||||
| folder in the root server folder. The format is in JSON, and | ||||
| the data format is described in MCUtil#dumpChunks(File) | ||||
| 
 | ||||
| The command will output server version and all online players to the | ||||
| file as well. We do not log anything but the location, world and | ||||
| username of the player. | ||||
| 
 | ||||
| Also logs the value of these config values (note not all are paper's): | ||||
| - keep spawn loaded value
 | ||||
| - spawn radius
 | ||||
| - view distance
 | ||||
| 
 | ||||
| Each chunk has the following logged: | ||||
| - Coordinate
 | ||||
| - Ticket level & its corresponding state
 | ||||
| - Whether it is queued for unload
 | ||||
| - Chunk status (may be unloaded)
 | ||||
| - All tickets on the chunk
 | ||||
| 
 | ||||
| Example log: | ||||
| https://gist.githubusercontent.com/Spottedleaf/0131e7710ffd5d531e5fd246c3367380/raw/169ae1b2e240485f99bc7a6bd8e78d90e3af7397/chunks-2019-06-01_19.57.05.txt | ||||
| 
 | ||||
| For references on certain keywords (ticket, status, etc), please see: | ||||
| 
 | ||||
| https://bugs.mojang.com/browse/MC-141484?focusedCommentId=528273&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-528273 | ||||
| https://bugs.mojang.com/browse/MC-141484?focusedCommentId=528577&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-528577 | ||||
| 
 | ||||
| diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
 | ||||
| index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | ||||
| --- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
 | ||||
| +++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
 | ||||
| @@ -0,0 +0,0 @@ import com.google.common.collect.ImmutableSet;
 | ||||
|  import com.google.common.collect.Iterables; | ||||
|  import com.google.common.collect.Lists; | ||||
|  import com.google.common.collect.Maps; | ||||
| -import net.minecraft.resources.ResourceLocation;
 | ||||
|  import net.minecraft.server.MinecraftServer; | ||||
| +import net.minecraft.server.level.ChunkHolder;
 | ||||
|  import net.minecraft.server.level.ServerChunkCache; | ||||
|  import net.minecraft.server.level.ServerLevel; | ||||
|  import net.minecraft.world.entity.Entity; | ||||
|  import net.minecraft.world.entity.EntityType; | ||||
|  import net.minecraft.world.level.ChunkPos; | ||||
| +import net.minecraft.resources.ResourceLocation;
 | ||||
| +import net.minecraft.server.MCUtil;
 | ||||
|  import org.apache.commons.lang3.tuple.MutablePair; | ||||
|  import org.apache.commons.lang3.tuple.Pair; | ||||
|  import org.bukkit.Bukkit; | ||||
| @@ -0,0 +0,0 @@ import java.util.stream.Collectors;
 | ||||
|   | ||||
|  public class PaperCommand extends Command { | ||||
|      private static final String BASE_PERM = "bukkit.command.paper."; | ||||
| -    private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version").build();
 | ||||
| +    private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo").build();
 | ||||
|   | ||||
|      public PaperCommand(String name) { | ||||
|          super(name); | ||||
| @@ -0,0 +0,0 @@ public class PaperCommand extends Command {
 | ||||
|                  if (args.length == 3) | ||||
|                      return getListMatchingLast(sender, args, EntityType.getEntityNameList().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new)); | ||||
|                  break; | ||||
| +            case "debug":
 | ||||
| +                if (args.length == 2) {
 | ||||
| +                    return getListMatchingLast(sender, args, "help", "chunks");
 | ||||
| +                }
 | ||||
| +                break;
 | ||||
| +            case "chunkinfo":
 | ||||
| +                List<String> worldNames = new ArrayList<>();
 | ||||
| +                worldNames.add("*");
 | ||||
| +                for (org.bukkit.World world : Bukkit.getWorlds()) {
 | ||||
| +                    worldNames.add(world.getName());
 | ||||
| +                }
 | ||||
| +                if (args.length == 2) {
 | ||||
| +                    return getListMatchingLast(sender, args, worldNames);
 | ||||
| +                }
 | ||||
| +                break;
 | ||||
|          } | ||||
|          return Collections.emptyList(); | ||||
|      } | ||||
| @@ -0,0 +0,0 @@ public class PaperCommand extends Command {
 | ||||
|              case "reload": | ||||
|                  doReload(sender); | ||||
|                  break; | ||||
| +            case "debug":
 | ||||
| +                doDebug(sender, args);
 | ||||
| +                break;
 | ||||
| +            case "chunkinfo":
 | ||||
| +                doChunkInfo(sender, args);
 | ||||
| +                break;
 | ||||
|              case "ver": | ||||
|                  if (!testPermission(sender, "version")) break; // "ver" needs a special check because it's an alias. All other commands are checked up before the switch statement (because they are present in the SUBCOMMANDS set) | ||||
|              case "version": | ||||
| @@ -0,0 +0,0 @@ public class PaperCommand extends Command {
 | ||||
|          return true; | ||||
|      } | ||||
|   | ||||
| +    private void doChunkInfo(CommandSender sender, String[] args) {
 | ||||
| +        List<org.bukkit.World> worlds;
 | ||||
| +        if (args.length < 2 || args[1].equals("*")) {
 | ||||
| +            worlds = Bukkit.getWorlds();
 | ||||
| +        } else {
 | ||||
| +            worlds = new ArrayList<>(args.length - 1);
 | ||||
| +            for (int i = 1; i < args.length; ++i) {
 | ||||
| +                org.bukkit.World world = Bukkit.getWorld(args[i]);
 | ||||
| +                if (world == null) {
 | ||||
| +                    sender.sendMessage(ChatColor.RED + "World '" + args[i] + "' is invalid");
 | ||||
| +                    return;
 | ||||
| +                }
 | ||||
| +                worlds.add(world);
 | ||||
| +            }
 | ||||
| +        }
 | ||||
| +
 | ||||
| +        int accumulatedTotal = 0;
 | ||||
| +        int accumulatedInactive = 0;
 | ||||
| +        int accumulatedBorder = 0;
 | ||||
| +        int accumulatedTicking = 0;
 | ||||
| +        int accumulatedEntityTicking = 0;
 | ||||
| +
 | ||||
| +        for (org.bukkit.World bukkitWorld : worlds) {
 | ||||
| +            ServerLevel world = ((CraftWorld)bukkitWorld).getHandle();
 | ||||
| +
 | ||||
| +            int total = 0;
 | ||||
| +            int inactive = 0;
 | ||||
| +            int border = 0;
 | ||||
| +            int ticking = 0;
 | ||||
| +            int entityTicking = 0;
 | ||||
| +
 | ||||
| +            for (ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) {
 | ||||
| +                if (chunk.getFullChunkUnchecked() == null) {
 | ||||
| +                    continue;
 | ||||
| +                }
 | ||||
| +
 | ||||
| +                ++total;
 | ||||
| +
 | ||||
| +                ChunkHolder.FullChunkStatus state = ChunkHolder.getFullChunkStatus(chunk.getTicketLevel());
 | ||||
| +
 | ||||
| +                switch (state) {
 | ||||
| +                    case INACCESSIBLE:
 | ||||
| +                        ++inactive;
 | ||||
| +                        continue;
 | ||||
| +                    case BORDER:
 | ||||
| +                        ++border;
 | ||||
| +                        continue;
 | ||||
| +                    case TICKING:
 | ||||
| +                        ++ticking;
 | ||||
| +                        continue;
 | ||||
| +                    case ENTITY_TICKING:
 | ||||
| +                        ++entityTicking;
 | ||||
| +                        continue;
 | ||||
| +                }
 | ||||
| +            }
 | ||||
| +
 | ||||
| +            accumulatedTotal += total;
 | ||||
| +            accumulatedInactive += inactive;
 | ||||
| +            accumulatedBorder += border;
 | ||||
| +            accumulatedTicking += ticking;
 | ||||
| +            accumulatedEntityTicking += entityTicking;
 | ||||
| +
 | ||||
| +            sender.sendMessage(ChatColor.BLUE + "Chunks in " + ChatColor.GREEN + bukkitWorld.getName() + ChatColor.DARK_AQUA + ":");
 | ||||
| +            sender.sendMessage(ChatColor.BLUE + "Total: " + ChatColor.DARK_AQUA + total + ChatColor.BLUE + " Inactive: " + ChatColor.DARK_AQUA
 | ||||
| +                               + inactive + ChatColor.BLUE + " Border: " + ChatColor.DARK_AQUA + border + ChatColor.BLUE + " Ticking: "
 | ||||
| +                               + ChatColor.DARK_AQUA + ticking + ChatColor.BLUE + " Entity: " + ChatColor.DARK_AQUA + entityTicking);
 | ||||
| +        }
 | ||||
| +        if (worlds.size() > 1) {
 | ||||
| +            sender.sendMessage(ChatColor.BLUE + "Chunks in " + ChatColor.GREEN + "all listed worlds" + ChatColor.DARK_AQUA + ":");
 | ||||
| +            sender.sendMessage(ChatColor.BLUE + "Total: " + ChatColor.DARK_AQUA + accumulatedTotal + ChatColor.BLUE + " Inactive: " + ChatColor.DARK_AQUA
 | ||||
| +                               + accumulatedInactive + ChatColor.BLUE + " Border: " + ChatColor.DARK_AQUA + accumulatedBorder + ChatColor.BLUE + " Ticking: "
 | ||||
| +                               + ChatColor.DARK_AQUA + accumulatedTicking + ChatColor.BLUE + " Entity: " + ChatColor.DARK_AQUA + accumulatedEntityTicking);
 | ||||
| +        }
 | ||||
| +    }
 | ||||
| +
 | ||||
| +    private void doDebug(CommandSender sender, String[] args) {
 | ||||
| +        if (args.length < 2) {
 | ||||
| +            sender.sendMessage(ChatColor.RED + "Use /paper debug [chunks] help for more information on a specific command");
 | ||||
| +            return;
 | ||||
| +        }
 | ||||
| +
 | ||||
| +        String debugType = args[1].toLowerCase(Locale.ENGLISH);
 | ||||
| +        switch (debugType) {
 | ||||
| +            case "chunks":
 | ||||
| +                if (args.length >= 3 && args[2].toLowerCase(Locale.ENGLISH).equals("help")) {
 | ||||
| +                    sender.sendMessage(ChatColor.RED + "Use /paper debug chunks to dump loaded chunk information to a file");
 | ||||
| +                    break;
 | ||||
| +                }
 | ||||
| +                File file = new File(new File(new File("."), "debug"),
 | ||||
| +                    "chunks-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt");
 | ||||
| +                sender.sendMessage(ChatColor.GREEN + "Writing chunk information dump to " + file.toString());
 | ||||
| +                try {
 | ||||
| +                    MCUtil.dumpChunks(file);
 | ||||
| +                    sender.sendMessage(ChatColor.GREEN + "Successfully written chunk information!");
 | ||||
| +                } catch (Throwable thr) {
 | ||||
| +                    MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
 | ||||
| +                    sender.sendMessage(ChatColor.RED + "Failed to dump chunk information, see console");
 | ||||
| +                }
 | ||||
| +
 | ||||
| +                break;
 | ||||
| +            case "help":
 | ||||
| +                // fall through to default
 | ||||
| +            default:
 | ||||
| +                sender.sendMessage(ChatColor.RED + "Use /paper debug [chunks] help for more information on a specific command");
 | ||||
| +                return;
 | ||||
| +        }
 | ||||
| +    }
 | ||||
| +
 | ||||
|      /* | ||||
|       * Ported from MinecraftForge - author: LexManos <LexManos@gmail.com> - License: LGPLv2.1 | ||||
|       */ | ||||
| diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
 | ||||
| index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | ||||
| --- a/src/main/java/net/minecraft/server/MCUtil.java
 | ||||
| +++ b/src/main/java/net/minecraft/server/MCUtil.java
 | ||||
| @@ -0,0 +0,0 @@ import net.minecraft.core.BlockPos;
 | ||||
|  import net.minecraft.core.Direction; | ||||
|  import net.minecraft.nbt.CompoundTag; | ||||
|  import net.minecraft.network.chat.Component; | ||||
| +import net.minecraft.server.level.ChunkHolder;
 | ||||
| +import net.minecraft.server.level.ChunkMap;
 | ||||
| +import net.minecraft.server.level.DistanceManager;
 | ||||
| +import net.minecraft.server.level.ServerChunkCache;
 | ||||
|  import net.minecraft.server.level.ServerLevel; | ||||
| +import net.minecraft.server.level.ServerPlayer;
 | ||||
| +import net.minecraft.server.level.Ticket;
 | ||||
|  import net.minecraft.world.entity.Entity; | ||||
|  import net.minecraft.world.level.ChunkPos; | ||||
|  import net.minecraft.world.level.ClipContext; | ||||
|  import net.minecraft.world.level.Level; | ||||
| +import net.minecraft.world.level.chunk.ChunkAccess;
 | ||||
| +import net.minecraft.world.level.chunk.ChunkStatus;
 | ||||
|  import org.apache.commons.lang.exception.ExceptionUtils; | ||||
| +import com.google.gson.JsonArray;
 | ||||
| +import com.google.gson.JsonObject;
 | ||||
| +import com.google.gson.internal.Streams;
 | ||||
| +import com.google.gson.stream.JsonWriter;
 | ||||
|  import com.mojang.authlib.GameProfile; | ||||
| +import com.mojang.datafixers.util.Either;
 | ||||
| +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
 | ||||
|  import org.bukkit.Location; | ||||
|  import org.bukkit.block.BlockFace; | ||||
|  import org.bukkit.craftbukkit.CraftWorld; | ||||
| @@ -0,0 +0,0 @@ import org.spigotmc.AsyncCatcher;
 | ||||
|   | ||||
|  import javax.annotation.Nonnull; | ||||
|  import javax.annotation.Nullable; | ||||
| +import java.io.*;
 | ||||
| +import java.util.ArrayList;
 | ||||
|  import java.util.List; | ||||
|  import java.util.Queue; | ||||
| +import java.util.Set;
 | ||||
|  import java.util.concurrent.CompletableFuture; | ||||
|  import java.util.concurrent.ExecutionException; | ||||
|  import java.util.concurrent.LinkedBlockingQueue; | ||||
| @@ -0,0 +0,0 @@ public final class MCUtil {
 | ||||
|          return null; | ||||
|      } | ||||
|   | ||||
| +    public static ChunkStatus getChunkStatus(ChunkHolder chunk) {
 | ||||
| +        List<ChunkStatus> statuses = net.minecraft.server.level.ServerChunkCache.CHUNK_STATUSES;
 | ||||
| +        for (int i = statuses.size() - 1; i >= 0; --i) {
 | ||||
| +            ChunkStatus curr = statuses.get(i);
 | ||||
| +            CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = chunk.getFutureIfPresentUnchecked(curr);
 | ||||
| +            if (future != ChunkHolder.UNLOADED_CHUNK_FUTURE) {
 | ||||
| +                return curr;
 | ||||
| +            }
 | ||||
| +        }
 | ||||
| +        return null; // unloaded
 | ||||
| +    }
 | ||||
| +
 | ||||
| +    public static void dumpChunks(File file) throws IOException {
 | ||||
| +        file.getParentFile().mkdirs();
 | ||||
| +        file.createNewFile();
 | ||||
| +        /*
 | ||||
| +         * Json format:
 | ||||
| +         *
 | ||||
| +         * Main data format:
 | ||||
| +         *  -server-version:<string>
 | ||||
| +         *  -data-version:<int>
 | ||||
| +         *  -worlds:
 | ||||
| +         *    -name:<world name>
 | ||||
| +         *    -view-distance:<int>
 | ||||
| +         *    -keep-spawn-loaded:<boolean>
 | ||||
| +         *    -keep-spawn-loaded-range:<int>
 | ||||
| +         *    -visible-chunk-count:<int>
 | ||||
| +         *    -loaded-chunk-count:<int>
 | ||||
| +         *    -verified-fully-loaded-chunks:<int>
 | ||||
| +         *    -players:<array of player>
 | ||||
| +         *    -chunk-data:<array of chunks>
 | ||||
| +         *
 | ||||
| +         * Player format:
 | ||||
| +         *  -name:<string>
 | ||||
| +         *  -x:<double>
 | ||||
| +         *  -y:<double>
 | ||||
| +         *  -z:<double>
 | ||||
| +         *
 | ||||
| +         * Chunk Format:
 | ||||
| +         *  -x:<integer>
 | ||||
| +         *  -z:<integer>
 | ||||
| +         *  -ticket-level:<integer>
 | ||||
| +         *  -state:<string>
 | ||||
| +         *  -queued-for-unload:<boolean>
 | ||||
| +         *  -status:<string>
 | ||||
| +         *  -tickets:<array of tickets>
 | ||||
| +         *
 | ||||
| +         *
 | ||||
| +         * Ticket format:
 | ||||
| +         *  -ticket-type:<string>
 | ||||
| +         *  -ticket-level:<int>
 | ||||
| +         *  -add-tick:<long>
 | ||||
| +         *  -object-reason:<string> // This depends on the type of ticket. ie POST_TELEPORT -> entity id
 | ||||
| +         */
 | ||||
| +        List<org.bukkit.World> worlds = org.bukkit.Bukkit.getWorlds();
 | ||||
| +        JsonObject data = new JsonObject();
 | ||||
| +
 | ||||
| +        data.addProperty("server-version", org.bukkit.Bukkit.getVersion());
 | ||||
| +        data.addProperty("data-version", 0);
 | ||||
| +
 | ||||
| +        JsonArray worldsData = new JsonArray();
 | ||||
| +
 | ||||
| +        for (org.bukkit.World bukkitWorld : worlds) {
 | ||||
| +            JsonObject worldData = new JsonObject();
 | ||||
| +
 | ||||
| +            ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle();
 | ||||
| +            ChunkMap chunkMap = world.getChunkSource().chunkMap;
 | ||||
| +            Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunks = chunkMap.visibleChunkMap;
 | ||||
| +            DistanceManager chunkMapDistance = chunkMap.distanceManager;
 | ||||
| +            List<ChunkHolder> allChunks = new ArrayList<>(visibleChunks.values());
 | ||||
| +            List<ServerPlayer> players = world.players;
 | ||||
| +
 | ||||
| +            int fullLoadedChunks = 0;
 | ||||
| +
 | ||||
| +            for (ChunkHolder chunk : allChunks) {
 | ||||
| +                if (chunk.getFullChunkUnchecked() != null) {
 | ||||
| +                    ++fullLoadedChunks;
 | ||||
| +                }
 | ||||
| +            }
 | ||||
| +
 | ||||
| +            // sorting by coordinate makes the log easier to read
 | ||||
| +            allChunks.sort((ChunkHolder v1, ChunkHolder v2) -> {
 | ||||
| +                if (v1.pos.x != v2.pos.x) {
 | ||||
| +                    return Integer.compare(v1.pos.x, v2.pos.x);
 | ||||
| +                }
 | ||||
| +                return Integer.compare(v1.pos.z, v2.pos.z);
 | ||||
| +            });
 | ||||
| +
 | ||||
| +            worldData.addProperty("name", world.getWorld().getName());
 | ||||
| +            worldData.addProperty("view-distance", world.spigotConfig.viewDistance);
 | ||||
| +            worldData.addProperty("keep-spawn-loaded", world.keepSpawnInMemory);
 | ||||
| +            worldData.addProperty("keep-spawn-loaded-range", world.paperConfig.keepLoadedRange);
 | ||||
| +            worldData.addProperty("visible-chunk-count", visibleChunks.size());
 | ||||
| +            worldData.addProperty("loaded-chunk-count", chunkMap.entitiesInLevel.size());
 | ||||
| +            worldData.addProperty("verified-fully-loaded-chunks", fullLoadedChunks);
 | ||||
| +
 | ||||
| +            JsonArray playersData = new JsonArray();
 | ||||
| +
 | ||||
| +            for (ServerPlayer player : players) {
 | ||||
| +                JsonObject playerData = new JsonObject();
 | ||||
| +
 | ||||
| +                playerData.addProperty("name", player.getScoreboardName());
 | ||||
| +                playerData.addProperty("x", player.getX());
 | ||||
| +                playerData.addProperty("y", player.getY());
 | ||||
| +                playerData.addProperty("z", player.getZ());
 | ||||
| +
 | ||||
| +                playersData.add(playerData);
 | ||||
| +
 | ||||
| +            }
 | ||||
| +
 | ||||
| +            worldData.add("players", playersData);
 | ||||
| +
 | ||||
| +            JsonArray chunksData = new JsonArray();
 | ||||
| +
 | ||||
| +            for (ChunkHolder playerChunk : allChunks) {
 | ||||
| +                JsonObject chunkData = new JsonObject();
 | ||||
| +
 | ||||
| +                Set<Ticket<?>> tickets = chunkMapDistance.tickets.get(playerChunk.pos.longKey);
 | ||||
| +                ChunkStatus status = getChunkStatus(playerChunk);
 | ||||
| +
 | ||||
| +                chunkData.addProperty("x", playerChunk.pos.x);
 | ||||
| +                chunkData.addProperty("z", playerChunk.pos.z);
 | ||||
| +                chunkData.addProperty("ticket-level", playerChunk.getTicketLevel());
 | ||||
| +                chunkData.addProperty("state", ChunkHolder.getFullChunkStatus(playerChunk.getTicketLevel()).toString());
 | ||||
| +                chunkData.addProperty("queued-for-unload", chunkMap.toDrop.contains(playerChunk.pos.longKey));
 | ||||
| +                chunkData.addProperty("status", status == null ? "unloaded" : status.toString());
 | ||||
| +
 | ||||
| +                JsonArray ticketsData = new JsonArray();
 | ||||
| +
 | ||||
| +                if (tickets != null) {
 | ||||
| +                    for (Ticket<?> ticket : tickets) {
 | ||||
| +                        JsonObject ticketData = new JsonObject();
 | ||||
| +
 | ||||
| +                        ticketData.addProperty("ticket-type", ticket.getType().toString());
 | ||||
| +                        ticketData.addProperty("ticket-level", ticket.getTicketLevel());
 | ||||
| +                        ticketData.addProperty("object-reason", String.valueOf(ticket.key));
 | ||||
| +                        ticketData.addProperty("add-tick", ticket.createdTick);
 | ||||
| +
 | ||||
| +                        ticketsData.add(ticketData);
 | ||||
| +                    }
 | ||||
| +                }
 | ||||
| +
 | ||||
| +                chunkData.add("tickets", ticketsData);
 | ||||
| +                chunksData.add(chunkData);
 | ||||
| +            }
 | ||||
| +
 | ||||
| +
 | ||||
| +            worldData.add("chunk-data", chunksData);
 | ||||
| +            worldsData.add(worldData);
 | ||||
| +        }
 | ||||
| +
 | ||||
| +        data.add("worlds", worldsData);
 | ||||
| +
 | ||||
| +        StringWriter stringWriter = new StringWriter();
 | ||||
| +        JsonWriter jsonWriter = new JsonWriter(stringWriter);
 | ||||
| +        jsonWriter.setIndent(" ");
 | ||||
| +        jsonWriter.setLenient(false);
 | ||||
| +        Streams.write(data, jsonWriter);
 | ||||
| +
 | ||||
| +        String fileData = stringWriter.toString();
 | ||||
| +
 | ||||
| +        try (PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8")) {
 | ||||
| +            out.print(fileData);
 | ||||
| +        }
 | ||||
| +    }
 | ||||
| +
 | ||||
|      public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) { | ||||
|          return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status); | ||||
|      } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jake Potrebic
				Jake Potrebic