mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-12-05 03:02:37 +01:00
Add create debuginfo command (#5531)
This commit is contained in:
parent
2e1bcdb619
commit
7eea02854e
12 changed files with 545 additions and 8 deletions
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -93,6 +93,8 @@ import com.simibubi.create.foundation.utility.ServerSpeedProvider;
|
||||||
import com.simibubi.create.infrastructure.command.HighlightPacket;
|
import com.simibubi.create.infrastructure.command.HighlightPacket;
|
||||||
import com.simibubi.create.infrastructure.command.SConfigureConfigPacket;
|
import com.simibubi.create.infrastructure.command.SConfigureConfigPacket;
|
||||||
|
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.ServerDebugInfoPacket;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
@ -203,7 +205,7 @@ public enum AllPackets {
|
||||||
CONTRAPTION_ACTOR_TOGGLE(ContraptionDisableActorPacket.class, ContraptionDisableActorPacket::new, PLAY_TO_CLIENT),
|
CONTRAPTION_ACTOR_TOGGLE(ContraptionDisableActorPacket.class, ContraptionDisableActorPacket::new, PLAY_TO_CLIENT),
|
||||||
CONTRAPTION_COLLIDER_LOCK(ContraptionColliderLockPacket.class, ContraptionColliderLockPacket::new, PLAY_TO_CLIENT),
|
CONTRAPTION_COLLIDER_LOCK(ContraptionColliderLockPacket.class, ContraptionColliderLockPacket::new, PLAY_TO_CLIENT),
|
||||||
ATTACHED_COMPUTER(AttachedComputerPacket.class, AttachedComputerPacket::new, PLAY_TO_CLIENT),
|
ATTACHED_COMPUTER(AttachedComputerPacket.class, AttachedComputerPacket::new, PLAY_TO_CLIENT),
|
||||||
|
SERVER_DEBUG_INFO(ServerDebugInfoPacket.class, ServerDebugInfoPacket::new, PLAY_TO_CLIENT)
|
||||||
;
|
;
|
||||||
|
|
||||||
public static final ResourceLocation CHANNEL_NAME = Create.asResource("main");
|
public static final ResourceLocation CHANNEL_NAME = Create.asResource("main");
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.simibubi.create.foundation.mixin.accessor;
|
||||||
|
|
||||||
|
import net.minecraft.SystemReport;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mixin(SystemReport.class)
|
||||||
|
public interface SystemReportAccessor {
|
||||||
|
@Accessor
|
||||||
|
static String getOPERATING_SYSTEM() {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
static String getJAVA_VERSION() {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
Map<String, String> getEntries();
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ public class AllCommands {
|
||||||
.then(OverlayConfigCommand.register())
|
.then(OverlayConfigCommand.register())
|
||||||
.then(DumpRailwaysCommand.register())
|
.then(DumpRailwaysCommand.register())
|
||||||
.then(FixLightingCommand.register())
|
.then(FixLightingCommand.register())
|
||||||
|
.then(DebugInfoCommand.register())
|
||||||
.then(HighlightCommand.register())
|
.then(HighlightCommand.register())
|
||||||
.then(KillTrainCommand.register())
|
.then(KillTrainCommand.register())
|
||||||
.then(PassengerCommand.register())
|
.then(PassengerCommand.register())
|
||||||
|
@ -39,6 +40,7 @@ public class AllCommands {
|
||||||
.then(CloneCommand.register())
|
.then(CloneCommand.register())
|
||||||
.then(GlueCommand.register())
|
.then(GlueCommand.register())
|
||||||
|
|
||||||
|
|
||||||
// utility
|
// utility
|
||||||
.then(util);
|
.then(util);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.simibubi.create.infrastructure.command;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.Command;
|
||||||
|
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||||
|
|
||||||
|
import com.simibubi.create.AllPackets;
|
||||||
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.DebugInformation;
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.ServerDebugInfoPacket;
|
||||||
|
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.network.chat.ClickEvent;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraftforge.network.PacketDistributor;
|
||||||
|
|
||||||
|
import static net.minecraft.commands.Commands.literal;
|
||||||
|
|
||||||
|
public class DebugInfoCommand {
|
||||||
|
public static ArgumentBuilder<CommandSourceStack, ?> register() {
|
||||||
|
return literal("debuginfo").executes(ctx -> {
|
||||||
|
CommandSourceStack source = ctx.getSource();
|
||||||
|
ServerPlayer player = source.getPlayerOrException();
|
||||||
|
source.sendSuccess(
|
||||||
|
Components.literal("Sending server debug information to your client..."), true
|
||||||
|
);
|
||||||
|
AllPackets.getChannel().send(
|
||||||
|
PacketDistributor.PLAYER.with(() -> player),
|
||||||
|
new ServerDebugInfoPacket(player)
|
||||||
|
);
|
||||||
|
return Command.SINGLE_SUCCESS;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package com.simibubi.create.infrastructure.debugInfo;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.foundation.mixin.accessor.SystemReportAccessor;
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.element.DebugInfoSection;
|
||||||
|
|
||||||
|
import net.minecraft.SharedConstants;
|
||||||
|
import net.minecraft.SystemReport;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.element.InfoElement;
|
||||||
|
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.element.InfoEntry;
|
||||||
|
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.fml.DistExecutor;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.forgespi.language.IModInfo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.GlUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows for providing easily accessible debugging information.
|
||||||
|
* This info can be retrieved with the "/create debuginfo" command.
|
||||||
|
* This command copies all information to the clipboard, formatted for a GitHub issue.
|
||||||
|
* Addons are welcome to add their own sections. Registration must occur synchronously.
|
||||||
|
*/
|
||||||
|
public class DebugInformation {
|
||||||
|
private static final List<DebugInfoSection> client = new ArrayList<>();
|
||||||
|
private static final List<DebugInfoSection> server = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final ImmutableMap<String, String> mcSystemInfo = Util.make(() -> {
|
||||||
|
SystemReport systemReport = new SystemReport();
|
||||||
|
SystemReportAccessor access = (SystemReportAccessor) systemReport;
|
||||||
|
return ImmutableMap.copyOf(access.getEntries());
|
||||||
|
});
|
||||||
|
|
||||||
|
public static void registerClientInfo(DebugInfoSection section) {
|
||||||
|
client.add(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerServerInfo(DebugInfoSection section) {
|
||||||
|
server.add(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerBothInfo(DebugInfoSection section) {
|
||||||
|
registerClientInfo(section);
|
||||||
|
registerServerInfo(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DebugInfoSection> getClientInfo() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DebugInfoSection> getServerInfo() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
DebugInfoSection.builder(Create.NAME)
|
||||||
|
.put("Mod Version", Create.VERSION)
|
||||||
|
.put("Forge Version", getVersionOfMod("forge"))
|
||||||
|
.put("Minecraft Version", SharedConstants.getCurrentVersion().getName())
|
||||||
|
.buildTo(DebugInformation::registerBothInfo);
|
||||||
|
|
||||||
|
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
|
||||||
|
DebugInfoSection.builder("Graphics")
|
||||||
|
.put("Flywheel Version", Flywheel.getVersion().toString())
|
||||||
|
.put("Flywheel Backend", () -> Backend.getBackendType().toString())
|
||||||
|
.put("OpenGL Renderer", GlUtil::getRenderer)
|
||||||
|
.put("OpenGL Version", GlUtil::getOpenGLVersion)
|
||||||
|
.put("Graphics Mode", () -> Minecraft.getInstance().options.graphicsMode.toString())
|
||||||
|
.buildTo(DebugInformation::registerClientInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
DebugInfoSection.builder("System Information")
|
||||||
|
.put("Operating System", SystemReportAccessor.getOPERATING_SYSTEM())
|
||||||
|
.put("Java Version", SystemReportAccessor.getJAVA_VERSION())
|
||||||
|
.put("JVM Flags", getMcSystemInfo("JVM Flags"))
|
||||||
|
.put("Memory", () -> getMcSystemInfo("Memory"))
|
||||||
|
.put("CPU", getCpuInfo())
|
||||||
|
.putAll(listAllGraphicsCards())
|
||||||
|
.buildTo(DebugInformation::registerBothInfo);
|
||||||
|
|
||||||
|
DebugInfoSection.builder("Other Mods")
|
||||||
|
.putAll(listAllOtherMods())
|
||||||
|
.buildTo(DebugInformation::registerBothInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getVersionOfMod(String id) {
|
||||||
|
return ModList.get().getModContainerById(id)
|
||||||
|
.map(mod -> mod.getModInfo().getVersion().toString())
|
||||||
|
.orElse("None");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<InfoElement> listAllOtherMods() {
|
||||||
|
List<InfoElement> mods = new ArrayList<>();
|
||||||
|
ModList.get().forEachModContainer((id, mod) -> {
|
||||||
|
if (!id.equals(Create.ID) && !id.equals("forge") && !id.equals("minecraft") && !id.equals("flywheel")) {
|
||||||
|
IModInfo info = mod.getModInfo();
|
||||||
|
String name = info.getDisplayName();
|
||||||
|
String version = info.getVersion().toString();
|
||||||
|
mods.add(new InfoEntry(name, version));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<InfoElement> listAllGraphicsCards() {
|
||||||
|
List<InfoElement> cards = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 10; i++) { // there won't be more than 10, right? right??
|
||||||
|
String name = getMcSystemInfo("Graphics card #" + i + " name");
|
||||||
|
String vendor = getMcSystemInfo("Graphics card #" + i + " vendor");
|
||||||
|
String vram = getMcSystemInfo("Graphics card #" + i + " VRAM (MB)");
|
||||||
|
if (name == null || vendor == null || vram == null)
|
||||||
|
break;
|
||||||
|
String key = "Graphics card #" + i;
|
||||||
|
String value = String.format("%s (%s); %s MB of VRAM", name, vendor, vram);
|
||||||
|
cards.add(new InfoEntry(key, value));
|
||||||
|
}
|
||||||
|
return cards.isEmpty() ? List.of(new InfoEntry("Graphics cards", "none")) : cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCpuInfo() {
|
||||||
|
String name = tryTrim(getMcSystemInfo("Processor Name"));
|
||||||
|
String freq = getMcSystemInfo("Frequency (GHz)");
|
||||||
|
String sockets = getMcSystemInfo("Number of physical packages");
|
||||||
|
String cores = getMcSystemInfo("Number of physical CPUs");
|
||||||
|
String threads = getMcSystemInfo("Number of logical CPUs");
|
||||||
|
return String.format("%s @ %s GHz; %s cores / %s threads on %s socket(s)", name, freq, cores, threads, sockets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a system attribute provided by Minecraft.
|
||||||
|
* They can be found in the constructor of {@link SystemReport}.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static String getMcSystemInfo(String key) {
|
||||||
|
return mcSystemInfo.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getIndent(int depth) {
|
||||||
|
return Stream.generate(() -> "\t").limit(depth).collect(Collectors.joining());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static String tryTrim(@Nullable String s) {
|
||||||
|
return s == null ? null : s.trim();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.simibubi.create.infrastructure.debugInfo;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A supplier of debug information. May be queried on the client or server.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface InfoProvider {
|
||||||
|
/**
|
||||||
|
* @param player the player requesting the data. May be null
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
String getInfo(@Nullable Player player);
|
||||||
|
|
||||||
|
default String getInfoSafe(Player player) {
|
||||||
|
try {
|
||||||
|
return Objects.toString(getInfo(player));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
StringBuilder builder = new StringBuilder("Error getting information!");
|
||||||
|
builder.append(' ').append(t.getMessage());
|
||||||
|
for (StackTraceElement element : t.getStackTrace()) {
|
||||||
|
builder.append('\n').append("\t").append(element.toString());
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package com.simibubi.create.infrastructure.debugInfo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||||
|
|
||||||
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.element.DebugInfoSection;
|
||||||
|
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import net.minecraftforge.fml.DistExecutor;
|
||||||
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
|
|
||||||
|
public class ServerDebugInfoPacket extends SimplePacketBase {
|
||||||
|
public static final Component COPIED = Components.literal(
|
||||||
|
"Debug information has been copied to your clipboard."
|
||||||
|
).withStyle(ChatFormatting.GREEN);
|
||||||
|
|
||||||
|
private final List<DebugInfoSection> serverInfo;
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public ServerDebugInfoPacket(Player player) {
|
||||||
|
this.serverInfo = DebugInformation.getServerInfo();
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerDebugInfoPacket(FriendlyByteBuf buffer) {
|
||||||
|
this.serverInfo = buffer.readList(DebugInfoSection::readDirect);
|
||||||
|
this.player = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(FriendlyByteBuf buffer) {
|
||||||
|
buffer.writeCollection(this.serverInfo, (buf, section) -> section.write(player, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handle(NetworkEvent.Context context) {
|
||||||
|
context.enqueueWork(() -> DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::handleOnClient));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printInfo(String side, Player player, List<DebugInfoSection> sections, StringBuilder output) {
|
||||||
|
output.append("<details>");
|
||||||
|
output.append('\n');
|
||||||
|
output.append("<summary>").append(side).append(" Info").append("</summary>");
|
||||||
|
output.append('\n').append('\n');
|
||||||
|
output.append("```");
|
||||||
|
output.append('\n');
|
||||||
|
|
||||||
|
for (int i = 0; i < sections.size(); i++) {
|
||||||
|
if (i != 0) {
|
||||||
|
output.append('\n');
|
||||||
|
}
|
||||||
|
sections.get(i).print(player, line -> output.append(line).append('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
output.append("```");
|
||||||
|
output.append('\n').append('\n');
|
||||||
|
output.append("</details>");
|
||||||
|
output.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
private void handleOnClient() {
|
||||||
|
Player player = Objects.requireNonNull(Minecraft.getInstance().player);
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
List<DebugInfoSection> clientInfo = DebugInformation.getClientInfo();
|
||||||
|
|
||||||
|
printInfo("Client", player, clientInfo, output);
|
||||||
|
output.append("\n\n");
|
||||||
|
printInfo("Server", player, serverInfo, output);
|
||||||
|
|
||||||
|
String text = output.toString();
|
||||||
|
Minecraft.getInstance().keyboardHandler.setClipboard(text);
|
||||||
|
player.displayClientMessage(COPIED, true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package com.simibubi.create.infrastructure.debugInfo.element;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.DebugInformation;
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.InfoProvider;
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A section for organizing debug information. Can contain both information and other sections.
|
||||||
|
* To create one, use the {@link #builder(String) builder} method.
|
||||||
|
*/
|
||||||
|
public record DebugInfoSection(String name, ImmutableList<InfoElement> elements) implements InfoElement {
|
||||||
|
@Override
|
||||||
|
public void write(Player player, FriendlyByteBuf buffer) {
|
||||||
|
buffer.writeBoolean(true);
|
||||||
|
buffer.writeUtf(name);
|
||||||
|
buffer.writeCollection(elements, (buf, element) -> element.write(player, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder builder() {
|
||||||
|
return builder(name).putAll(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(int depth, @Nullable Player player, Consumer<String> lineConsumer) {
|
||||||
|
String indent = DebugInformation.getIndent(depth);
|
||||||
|
lineConsumer.accept(indent + name + ":");
|
||||||
|
elements.forEach(element -> element.print(depth + 1, player, lineConsumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DebugInfoSection read(FriendlyByteBuf buffer) {
|
||||||
|
String name = buffer.readUtf();
|
||||||
|
ArrayList<InfoElement> elements = buffer.readCollection(ArrayList::new, InfoElement::read);
|
||||||
|
return new DebugInfoSection(name, ImmutableList.copyOf(elements));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DebugInfoSection readDirect(FriendlyByteBuf buf) {
|
||||||
|
buf.readBoolean(); // discard type marker
|
||||||
|
return read(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder(String name) {
|
||||||
|
return new Builder(null, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DebugInfoSection of(String name, Collection<DebugInfoSection> children) {
|
||||||
|
return builder(name).putAll(children).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private final Builder parent;
|
||||||
|
private final String name;
|
||||||
|
private final ImmutableList.Builder<InfoElement> elements;
|
||||||
|
|
||||||
|
public Builder(Builder parent, String name) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.name = name;
|
||||||
|
this.elements = ImmutableList.builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder put(InfoElement element) {
|
||||||
|
this.elements.add(element);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder put(String key, InfoProvider provider) {
|
||||||
|
return put(new InfoEntry(key, provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder put(String key, Supplier<String> value) {
|
||||||
|
return put(key, player -> value.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder put(String key, String value) {
|
||||||
|
return put(key, player -> value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder putAll(Collection<? extends InfoElement> elements) {
|
||||||
|
elements.forEach(this::put);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder section(String name) {
|
||||||
|
return new Builder(this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder finishSection() {
|
||||||
|
if (parent == null) {
|
||||||
|
throw new IllegalStateException("Cannot finish the root section");
|
||||||
|
}
|
||||||
|
parent.elements.add(this.build());
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugInfoSection build() {
|
||||||
|
return new DebugInfoSection(name, elements.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildTo(Consumer<DebugInfoSection> consumer) {
|
||||||
|
consumer.accept(this.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.simibubi.create.infrastructure.debugInfo.element;
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public sealed interface InfoElement permits DebugInfoSection, InfoEntry {
|
||||||
|
void write(Player player, FriendlyByteBuf buffer);
|
||||||
|
|
||||||
|
void print(int depth, @Nullable Player player, Consumer<String> lineConsumer);
|
||||||
|
|
||||||
|
default void print(@Nullable Player player, Consumer<String> lineConsumer) {
|
||||||
|
print(0, player, lineConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static InfoElement read(FriendlyByteBuf buffer) {
|
||||||
|
boolean section = buffer.readBoolean();
|
||||||
|
if (section) {
|
||||||
|
return DebugInfoSection.read(buffer);
|
||||||
|
} else {
|
||||||
|
return InfoEntry.read(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.simibubi.create.infrastructure.debugInfo.element;
|
||||||
|
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.DebugInformation;
|
||||||
|
import com.simibubi.create.infrastructure.debugInfo.InfoProvider;
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public record InfoEntry(String name, InfoProvider provider) implements InfoElement {
|
||||||
|
public InfoEntry(String name, String info) {
|
||||||
|
this(name, player -> info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(Player player, FriendlyByteBuf buffer) {
|
||||||
|
buffer.writeBoolean(false);
|
||||||
|
buffer.writeUtf(name);
|
||||||
|
buffer.writeUtf(provider.getInfoSafe(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(int depth, @Nullable Player player, Consumer<String> lineConsumer) {
|
||||||
|
String value = provider.getInfoSafe(player);
|
||||||
|
String indent = DebugInformation.getIndent(depth);
|
||||||
|
if (value.contains("\n")) {
|
||||||
|
String[] lines = value.split("\n");
|
||||||
|
String firstLine = lines[0];
|
||||||
|
String lineStart = name + ": ";
|
||||||
|
lineConsumer.accept(indent + lineStart + firstLine);
|
||||||
|
String extraIndent = Stream.generate(() -> " ").limit(lineStart.length()).collect(Collectors.joining());
|
||||||
|
|
||||||
|
for (int i = 1; i < lines.length; i++) {
|
||||||
|
lineConsumer.accept(indent + extraIndent + lines[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lineConsumer.accept(indent + name + ": " + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InfoEntry read(FriendlyByteBuf buffer) {
|
||||||
|
String name = buffer.readUtf();
|
||||||
|
String value = buffer.readUtf();
|
||||||
|
return new InfoEntry(name, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,9 +22,14 @@
|
||||||
"accessor.GameTestHelperAccessor",
|
"accessor.GameTestHelperAccessor",
|
||||||
"accessor.LivingEntityAccessor",
|
"accessor.LivingEntityAccessor",
|
||||||
"accessor.NbtAccounterAccessor",
|
"accessor.NbtAccounterAccessor",
|
||||||
"accessor.ServerLevelAccessor"
|
"accessor.ServerLevelAccessor",
|
||||||
|
"accessor.SystemReportAccessor"
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
|
"accessor.AgeableListModelAccessor",
|
||||||
|
"accessor.GameRendererAccessor",
|
||||||
|
"accessor.HumanoidArmorLayerAccessor",
|
||||||
|
"accessor.ParticleEngineAccessor",
|
||||||
"client.BlockDestructionProgressMixin",
|
"client.BlockDestructionProgressMixin",
|
||||||
"client.CameraMixin",
|
"client.CameraMixin",
|
||||||
"client.EntityContraptionInteractionMixin",
|
"client.EntityContraptionInteractionMixin",
|
||||||
|
@ -35,11 +40,7 @@
|
||||||
"client.MapRendererMapInstanceMixin",
|
"client.MapRendererMapInstanceMixin",
|
||||||
"client.ModelDataRefreshMixin",
|
"client.ModelDataRefreshMixin",
|
||||||
"client.PlayerRendererMixin",
|
"client.PlayerRendererMixin",
|
||||||
"client.WindowResizeMixin",
|
"client.WindowResizeMixin"
|
||||||
"accessor.AgeableListModelAccessor",
|
|
||||||
"accessor.GameRendererAccessor",
|
|
||||||
"accessor.HumanoidArmorLayerAccessor",
|
|
||||||
"accessor.ParticleEngineAccessor"
|
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
|
|
Loading…
Reference in a new issue