Skip to content

Commit

Permalink
Adds a command to export grids for debugging purposes (#7813)
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte committed Mar 31, 2024
1 parent 1924fcb commit b10ff95
Show file tree
Hide file tree
Showing 24 changed files with 875 additions and 79 deletions.
8 changes: 8 additions & 0 deletions src/main/java/appeng/api/networking/IGrid.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@

package appeng.api.networking;

import java.io.IOException;
import java.util.Set;

import com.google.gson.stream.JsonWriter;

import appeng.api.networking.crafting.ICraftingService;
import appeng.api.networking.energy.IEnergyService;
import appeng.api.networking.events.GridEvent;
Expand Down Expand Up @@ -177,4 +180,9 @@ default IPathingService getPathingService() {
default ISpatialService getSpatialService() {
return getService(ISpatialService.class);
}

/**
* Dump debug information about this grid to the given JSON writer.
*/
void export(JsonWriter jsonWriter) throws IOException;
}
10 changes: 10 additions & 0 deletions src/main/java/appeng/api/networking/IGridServiceProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@

package appeng.api.networking;

import java.io.IOException;

import com.google.gson.stream.JsonWriter;

import org.jetbrains.annotations.Nullable;

import net.minecraft.nbt.CompoundTag;
Expand Down Expand Up @@ -101,4 +105,10 @@ default void addNode(IGridNode gridNode, @Nullable CompoundTag savedData) {
*/
default void saveNodeData(IGridNode gridNode, CompoundTag savedData) {
}

/**
* Write debug information about this service to the given writer.
*/
default void debugDump(JsonWriter writer) throws IOException {
}
}
26 changes: 25 additions & 1 deletion src/main/java/appeng/blockentity/AEBaseBlockEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@

package appeng.blockentity;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

import com.google.gson.stream.JsonWriter;
import com.mojang.serialization.JsonOps;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.MustBeInvokedByOverriders;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -57,9 +61,12 @@
import net.minecraft.world.phys.BlockHitResult;
import net.neoforged.neoforge.client.model.data.ModelData;

import it.unimi.dsi.fastutil.objects.Reference2IntMap;

import appeng.api.inventories.ISegmentedInventory;
import appeng.api.inventories.InternalInventory;
import appeng.api.networking.GridHelper;
import appeng.api.networking.IGridNode;
import appeng.api.orientation.BlockOrientation;
import appeng.api.orientation.IOrientationStrategy;
import appeng.api.orientation.RelativeSide;
Expand All @@ -70,11 +77,13 @@
import appeng.hooks.ticking.TickHandler;
import appeng.items.tools.MemoryCardItem;
import appeng.util.CustomNameUtil;
import appeng.util.IDebugExportable;
import appeng.util.JsonStreamUtil;
import appeng.util.SettingsFrom;
import appeng.util.helpers.ItemComparisonHelper;

public class AEBaseBlockEntity extends BlockEntity
implements Nameable, ISegmentedInventory, Clearable {
implements Nameable, ISegmentedInventory, Clearable, IDebugExportable {

private static final Map<BlockEntityType<?>, Item> REPRESENTATIVE_ITEMS = new HashMap<>();
@Nullable
Expand Down Expand Up @@ -486,4 +495,19 @@ public void setBlockState(BlockState state) {
onOrientationChanged(newOrientation);
}
}

@Override
public void debugExport(JsonWriter writer, Reference2IntMap<Object> machineIds, Reference2IntMap<IGridNode> nodeIds)
throws IOException {
var data = new CompoundTag();
saveAdditional(data);

JsonStreamUtil.writeProperties(Map.of(
"blockState", BlockState.CODEC.encodeStart(JsonOps.INSTANCE, getBlockState()).getOrThrow(false, err -> {
}),
"level", level.dimension().location().toString(),
"pos", getBlockPos(),
"data", CompoundTag.CODEC.encodeStart(JsonOps.INSTANCE, data).getOrThrow(false, err -> {
})), writer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@

package appeng.blockentity.networking;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.google.gson.stream.JsonWriter;

import org.jetbrains.annotations.Nullable;

import net.minecraft.core.BlockPos;
Expand All @@ -41,6 +44,8 @@
import net.minecraft.world.phys.shapes.VoxelShape;
import net.neoforged.neoforge.client.model.data.ModelData;

import it.unimi.dsi.fastutil.objects.Reference2IntMap;

import appeng.api.networking.IGridNode;
import appeng.api.parts.IFacadeContainer;
import appeng.api.parts.IPart;
Expand All @@ -54,6 +59,7 @@
import appeng.core.AppEng;
import appeng.helpers.AEMultiBlockEntity;
import appeng.parts.CableBusContainer;
import appeng.util.IDebugExportable;
import appeng.util.Platform;

public class CableBusBlockEntity extends AEBaseBlockEntity implements AEMultiBlockEntity {
Expand Down Expand Up @@ -376,4 +382,25 @@ public InteractionResult disassembleWithWrench(Player player, Level level, Block
public VoxelShape getCollisionShape(CollisionContext context) {
return cb.getCollisionShape(context);
}

@Override
public void debugExport(JsonWriter writer, Reference2IntMap<Object> machineIds, Reference2IntMap<IGridNode> nodeIds)
throws IOException {
super.debugExport(writer, machineIds, nodeIds);

writer.name("parts");
writer.beginObject();
for (var side : Platform.DIRECTIONS_WITH_NULL) {
var part = getPart(side);
if (part != null) {
writer.name(side == null ? "center" : side.getSerializedName());
writer.beginObject();
if (part instanceof IDebugExportable exportable) {
exportable.debugExport(writer, machineIds, nodeIds);
}
writer.endObject();
}
}
writer.endObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;

Expand Down Expand Up @@ -51,6 +52,7 @@ public class NetworkStatusScreen extends AEBaseScreen<NetworkStatusMenu> {
// Dimensions of each table cell
private static final int CELL_WIDTH = 30;
private static final int CELL_HEIGHT = 18;
private final Button exportGridButton;

private NetworkStatus status = new NetworkStatus();

Expand All @@ -62,12 +64,18 @@ public NetworkStatusScreen(NetworkStatusMenu menu, Inventory playerInventory,
this.scrollbar = widgets.addScrollBar("scrollbar");

this.addToLeftToolbar(CommonButtons.togglePowerUnit());

exportGridButton = widgets.addButton("export_grid", Component.literal("Export Grid"), menu::exportGrid);
}

@Override
protected void updateBeforeRender() {
super.updateBeforeRender();

// Make the export button only visible if the command can actually be run. This allows tie-in with
// Prometheus or similar mods, which checking for op would not.
exportGridButton.visible = menu.canExportGrid();

setTextContent("dialog_title", GuiText.NetworkDetails.text(status.getChannelsUsed()));
setTextContent("stored_power", GuiText.StoredPower.text(Platform.formatPower(status.getStoredPower(), false)));
setTextContent("max_power", GuiText.MaxPower.text(Platform.formatPower(status.getMaxStoredPower(), false)));
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/appeng/core/network/InitNetwork.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import appeng.core.network.clientbound.CraftConfirmPlanPacket;
import appeng.core.network.clientbound.CraftingJobStatusPacket;
import appeng.core.network.clientbound.CraftingStatusPacket;
import appeng.core.network.clientbound.ExportedGridContent;
import appeng.core.network.clientbound.GuiDataSyncPacket;
import appeng.core.network.clientbound.ItemTransitionEffectPacket;
import appeng.core.network.clientbound.LightningPacket;
Expand Down Expand Up @@ -58,6 +59,7 @@ public static void init(RegisterPayloadHandlerEvent event) {
clientbound(registrar, NetworkStatusPacket.class, NetworkStatusPacket::decode);
clientbound(registrar, PatternAccessTerminalPacket.class, PatternAccessTerminalPacket::decode);
clientbound(registrar, SetLinkStatusPacket.class, SetLinkStatusPacket::decode);
clientbound(registrar, ExportedGridContent.class, ExportedGridContent::decode);

// Serverbound
serverbound(registrar, ColorApplicatorSelectColorPacket.class, ColorApplicatorSelectColorPacket::decode);
Expand Down
107 changes: 107 additions & 0 deletions src/main/java/appeng/core/network/clientbound/ExportedGridContent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package appeng.core.network.clientbound;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;

import appeng.core.network.ClientboundPacket;

/**
* Contains data produced by {@link appeng.server.subcommands.GridsCommand}
*/
public record ExportedGridContent(int serialNumber,
Type type,
byte[] compressedData) implements ClientboundPacket {

private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm");

private static final Logger LOG = LoggerFactory.getLogger(ExportedGridContent.class);

@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(serialNumber);
buffer.writeEnum(type);
buffer.writeByteArray(compressedData);
}

public static ExportedGridContent decode(FriendlyByteBuf buffer) {
var serialNumber = buffer.readInt();
var type = buffer.readEnum(Type.class);
var data = buffer.readByteArray();
return new ExportedGridContent(serialNumber, type, data);
}

@Override
public void handleOnClient(Player player) {
var saveDir = Minecraft.getInstance().gameDirectory.toPath();
String filename;
var spServer = Minecraft.getInstance().getSingleplayerServer();
var connection = Minecraft.getInstance().getConnection();
if (spServer != null) {
saveDir = spServer.getServerDirectory().toPath();
filename = "ae2_grid_";
} else if (connection != null) {
filename = "ae2_grid_from_server_";
} else {
LOG.error("Ignoring grid export without a connection to a server.");
return;
}

saveDir = saveDir.toAbsolutePath().normalize();

filename += serialNumber + "_" + TIMESTAMP_FORMATTER.format(LocalDateTime.now()) + ".zip";

OpenOption[] openOptions = new OpenOption[0];
if (type != Type.FIRST_CHUNK) {
openOptions = new OpenOption[] { StandardOpenOption.APPEND };
}

var tempPath = saveDir.resolve(filename + ".tmp");
var finalPath = saveDir.resolve(filename);
try (var out = Files.newOutputStream(tempPath, openOptions)) {
out.write(compressedData);
} catch (IOException e) {
player.sendSystemMessage(
Component.literal("Failed to write exported grid data to " + tempPath)
.withStyle(ChatFormatting.RED));
LOG.error("Failed to write exported grid data to {}", tempPath, e);
return;
}

if (type == Type.LAST_CHUNK) {
try {
Files.move(tempPath, finalPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
LOG.error("Failed to move grid export {} into place", finalPath, e);
}

player.sendSystemMessage(Component.literal("Saved grid data for grid #" + serialNumber + " from server to ")
.append(Component.literal(finalPath.toString()).withStyle(style -> {
return style.withUnderlined(true)
.withClickEvent(new ClickEvent(
ClickEvent.Action.OPEN_FILE,
finalPath.getParent().toString()));
})));
}
}

public enum Type {
FIRST_CHUNK,
CHUNK,
LAST_CHUNK
}
}
6 changes: 4 additions & 2 deletions src/main/java/appeng/hooks/ticking/ServerGridRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

package appeng.hooks.ticking;

import java.util.Collections;
import java.util.Objects;
import java.util.Set;

import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
Expand Down Expand Up @@ -86,8 +88,8 @@ synchronized void updateNetworks() {
/**
* Get all registered {@link Grid}s
*/
public Iterable<Grid> getNetworks() {
return networks;
public Set<Grid> getNetworks() {
return Collections.unmodifiableSet(networks);
}

}
3 changes: 2 additions & 1 deletion src/main/java/appeng/hooks/ticking/TickHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

Expand Down Expand Up @@ -174,7 +175,7 @@ public void removeNetwork(Grid grid) {
this.grids.removeNetwork(grid);
}

public Iterable<Grid> getGridList() {
public Set<Grid> getGridList() {
Platform.assertServerThread();
return this.grids.getNetworks();
}
Expand Down
Loading

0 comments on commit b10ff95

Please sign in to comment.