big bang
This commit is contained in:
commit
48c2fd2977
23 changed files with 1293 additions and 0 deletions
13
src/main/java/net/moonleay/quickcrashreports/Main.kt
Normal file
13
src/main/java/net/moonleay/quickcrashreports/Main.kt
Normal file
|
@ -0,0 +1,13 @@
|
|||
package net.moonleay.quickcrashreports
|
||||
|
||||
import net.moonleay.quickcrashreports.build.BuildConstants
|
||||
import net.fabricmc.api.ModInitializer
|
||||
import org.apache.logging.log4j.LogManager
|
||||
|
||||
internal object Main : ModInitializer {
|
||||
internal val LOGGER = LogManager.getLogger(BuildConstants.modName)
|
||||
|
||||
override fun onInitialize() {
|
||||
LOGGER.info("Main has been initialized")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package net.moonleay.quickcrashreports.client
|
||||
|
||||
import net.moonleay.quickcrashreports.build.BuildConstants
|
||||
import net.fabricmc.api.ClientModInitializer
|
||||
import org.apache.logging.log4j.LogManager
|
||||
|
||||
internal object ClientMain : ClientModInitializer {
|
||||
internal val LOGGER = LogManager.getLogger(BuildConstants.modName)
|
||||
|
||||
override fun onInitializeClient() {
|
||||
LOGGER.info("ClientMain has been initialized")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package net.moonleay.quickcrashreports.client.ui
|
||||
|
||||
import java.awt.GridLayout
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.JSeparator
|
||||
import javax.swing.border.EmptyBorder
|
||||
|
||||
class BottomBar : JPanel() {
|
||||
init {
|
||||
this.layout = GridLayout(2, 4)
|
||||
this.border = EmptyBorder(0, 10, 10, 10)
|
||||
|
||||
val label = JLabel("(c) 2024 moonleay, Licensed under GPL-3.0")
|
||||
|
||||
this.add(JSeparator())
|
||||
this.add(label)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package net.moonleay.quickcrashreports.client.ui
|
||||
|
||||
import com.formdev.flatlaf.FlatDarculaLaf
|
||||
import com.mashape.unirest.http.Unirest
|
||||
import com.mashape.unirest.http.exceptions.UnirestException
|
||||
import java.io.IOException
|
||||
|
||||
object CrashUi {
|
||||
fun start (crashReport: String) {
|
||||
println("Launching gui")
|
||||
FlatDarculaLaf.setup()
|
||||
JustCrashedGUI()
|
||||
}
|
||||
|
||||
@Throws(IOException::class, InterruptedException::class, UnirestException::class)
|
||||
private fun sendPaste(text: String, burnAfterReading: Boolean): String {
|
||||
val body = StringBuilder("{\"text\": \"")
|
||||
body.append(text.replace("[\\u-0001-\\u001F]", "<br>")).append("\",\n")
|
||||
body.append("\"\"").append("\",\n")
|
||||
body.append("\"burn_after_reading\": ").append(burnAfterReading)
|
||||
body.append("}")
|
||||
|
||||
Unirest.setTimeouts(-1, 0)
|
||||
val response = Unirest.post("https://bin.moonleay.net/")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(body.toString())
|
||||
.asString()
|
||||
return response.body
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package net.moonleay.quickcrashreports.client.ui
|
||||
|
||||
import net.moonleay.quickcrashreports.build.BuildConstants
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JFrame
|
||||
import javax.swing.WindowConstants
|
||||
|
||||
class JustCrashedGUI : JFrame("QuickCrashReports " + BuildConstants.modVersion) {
|
||||
init {
|
||||
this.isResizable = false
|
||||
this.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
|
||||
|
||||
val contentPane = this.contentPane
|
||||
contentPane.layout = BorderLayout()
|
||||
|
||||
// contentPane.add(MainPanel, BorderLayout.CENTER)
|
||||
contentPane.add(BottomBar(), BorderLayout.SOUTH)
|
||||
|
||||
this.pack()
|
||||
this.setLocationRelativeTo(null)
|
||||
this.isVisible = true
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package net.moonleay.quickcrashreports.client.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class LaunchGui {
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 1) {
|
||||
System.err.println("Missing crash report path argument");
|
||||
System.exit(255);
|
||||
}
|
||||
|
||||
try {
|
||||
final var report = Files.readString(Path.of(args[0]));
|
||||
System.out.println(report);
|
||||
// TODO: Add GUI
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package net.moonleay.quickcrashreports.client.ui
|
||||
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.GridLayout
|
||||
import javax.swing.*
|
||||
import javax.swing.border.EmptyBorder
|
||||
|
||||
object MainPanel : JPanel() {
|
||||
|
||||
init {
|
||||
// val layout = GridLayout(6, 1)
|
||||
//
|
||||
// this.layout = layout
|
||||
// border = EmptyBorder(10, 10, 10, 10)
|
||||
//
|
||||
// val info = JLabel("Modpack Installer")
|
||||
// info.horizontalAlignment = SwingConstants.CENTER
|
||||
// info.font = Font(font.name, font.style, font.size + 6)
|
||||
// this.add(info, BorderLayout.NORTH)
|
||||
//
|
||||
// val flayout = FlowLayout()
|
||||
// flayout.hgap = 10
|
||||
// flayout.alignment = SwingConstants.VERTICAL
|
||||
//
|
||||
// fabricLabel = JLabel("Fabric mod loader")
|
||||
// modpackLabel = JLabel("Modpack")
|
||||
//
|
||||
// this.add(fabricLabel)
|
||||
// this.add(modpackLabel)
|
||||
//
|
||||
// dotMcLocationBar = JTextField(dotMcLocation)
|
||||
// dotMcLocationBar.isEditable = false
|
||||
// this.add(dotMcLocationBar)
|
||||
//
|
||||
// val openFileChooser = JButton("Select .minecraft Folder")
|
||||
// openFileChooser.addActionListener {
|
||||
// dotMcLocation = promptForFolder(openFileChooser)
|
||||
// dotMcLocationBar.text = dotMcLocation
|
||||
// }
|
||||
// this.add(openFileChooser)
|
||||
// generatorButton.addActionListener {
|
||||
// val dotMcMods = File(dotMcLocation + File.separator + "mods")
|
||||
// if (dotMcMods.exists()) {
|
||||
// val bool = PopupFactory.getYesOrNoError(
|
||||
// "Warning! .minecraft/mods folder found",
|
||||
// "This installer will clear your mods folder in order to install the right Modpack."
|
||||
// )
|
||||
// if (bool) {
|
||||
// generatorButton.isEnabled = false
|
||||
// Manager.startDownloadAndInstall(dotMcLocation)
|
||||
// }
|
||||
// } else {
|
||||
// generatorButton.isEnabled = false
|
||||
// Manager.startDownloadAndInstall(dotMcLocation)
|
||||
// }
|
||||
// }
|
||||
// this.add(generatorButton)
|
||||
}
|
||||
|
||||
// private fun promptForFolder(parent: Component?): String {
|
||||
// val fc = JFileChooser()
|
||||
// fc.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
|
||||
// return if (fc.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) {
|
||||
// fc.selectedFile.absolutePath
|
||||
// } else "Error"
|
||||
// }
|
||||
//
|
||||
// fun updateFabricModloader(tx: String) {
|
||||
// fabricLabel.text = "Fabric mod loader $tx"
|
||||
// }
|
||||
//
|
||||
// fun updateModpack(tx: String) {
|
||||
// modpackLabel.text = "Modpack $tx"
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Code by mostlymagic
|
||||
* src: https://github.com/mostlymagic/hacking-java
|
||||
* */
|
||||
package net.moonleay.quickcrashreports.client.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class CompilationResult {
|
||||
|
||||
private final boolean success;
|
||||
private final List<String> warnings;
|
||||
private final List<String> errors;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public List<String> getWarnings() {
|
||||
return Collections.unmodifiableList(warnings);
|
||||
}
|
||||
|
||||
public List<String> getErrors() {
|
||||
return Collections.unmodifiableList(errors);
|
||||
}
|
||||
|
||||
public CompilationResult(final boolean success, final List<String> warnings, final List<String> errors) {
|
||||
this.success = success;
|
||||
this.warnings = new ArrayList<>(warnings);
|
||||
this.errors = new ArrayList<>(errors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(success, warnings, errors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final CompilationResult other = (CompilationResult) obj;
|
||||
return Objects.equals(this.success, other.success) && Objects.equals(this.warnings, other.warnings)
|
||||
&& Objects.equals(this.errors, other.errors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CompilationResult{" + "success=" + success + ", warnings=" + warnings + ", errors=" + errors + '}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* Code by mostlymagic
|
||||
* src: https://github.com/mostlymagic/hacking-java
|
||||
* */
|
||||
|
||||
package net.moonleay.quickcrashreports.client.util;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static com.google.common.collect.Sets.newLinkedHashSet;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* This class encapsulates a call to the {@code java} executable (or optionally {@code javac}) in a separate, forked
|
||||
* process, with the classpath copied from the local process. Additionally, it allows you to move classpath entries to
|
||||
* the bootclasspath, in order to overwrite default JDK functionality. This class is stateful and not thread safe.
|
||||
*/
|
||||
public final class ForkedRun {
|
||||
private static final Joiner SEP_JOINER;
|
||||
private static final String MAVEN_REPO;
|
||||
// private static final String ROOT_PROJECT_PATH;
|
||||
|
||||
// by default, all classpath elements that come either from inside the project root or the local maven repository
|
||||
// are included in the forked classpath
|
||||
private final Set<JarPredicate> jarFilePredicates = EnumSet.of(
|
||||
JarPredicate.IN_LOCAL_MAVEN_REPO);
|
||||
private boolean useJavaC;
|
||||
private boolean outputCommand;
|
||||
|
||||
private final Set<String> bootClassPathMatchers = newHashSet();
|
||||
private final List<String> vmArgs = newArrayList();
|
||||
private final List<String> args = newArrayList();
|
||||
private final ClassLoader referenceClassLoader;
|
||||
private final Set<ClassLoader> additionalClassLoaders = newHashSet();
|
||||
|
||||
/**
|
||||
* Activate the flag to output the full command line statement to standard out before executing it.
|
||||
*/
|
||||
public ForkedRun printCommand() {
|
||||
this.outputCommand = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly add the tools.jar / classes.jar to the forked classpath. Since JDK 8, these are already present on the
|
||||
* classpath by default.
|
||||
*/
|
||||
public ForkedRun withToolsJar() {
|
||||
jarFilePredicates.add(JarPredicate.TOOLS_JAR);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the flag to use javac instead of java as target executable.
|
||||
*/
|
||||
public ForkedRun withJavaC() {
|
||||
this.useJavaC = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary directory and store its full path as command line argument.
|
||||
*/
|
||||
public ForkedRun tempDirAsArg() {
|
||||
this.args.add(Files.createTempDir().getAbsolutePath());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate debugging in the forked process, with the specified port.
|
||||
*/
|
||||
public ForkedRun withDebugPort(final int port) {
|
||||
vmArgs.addAll(
|
||||
asList("-Xdebug", "-Xnoagent",
|
||||
format("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%d", port)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vm argument in the forked process.
|
||||
*/
|
||||
public ForkedRun withArg(final String arg) {
|
||||
args.add(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a command line argument in the forked process.
|
||||
*/
|
||||
public ForkedRun withArg(final Class<?> arg) {
|
||||
args.add(arg.getName());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all class path elements that contain this partial path signature to bootclasspath elements. The
|
||||
* individual arguments should be parts of the directory path, usually Maven groupId and artifactId, e.g. "org",
|
||||
* "springframework", "spring-aop".
|
||||
*/
|
||||
public ForkedRun withBootClassPathMatcher(final String pathElement, final String... morePathElements) {
|
||||
bootClassPathMatchers.add(Joiner.on(File.separatorChar).join(Lists.asList(pathElement, morePathElements)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all classpath elements from the ClassLoader owning the referenced class.
|
||||
*/
|
||||
public ForkedRun withAdditionalClassLoaderFromClass(final Class<?> referenceClass) {
|
||||
return withAdditionalClassLoader(referenceClass.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all classpath elements from the referenced ClassLoader.
|
||||
*/
|
||||
public ForkedRun withAdditionalClassLoader(final ClassLoader additional) {
|
||||
additionalClassLoaders.add(additional);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate with reference ClassLoader.
|
||||
*/
|
||||
public ForkedRun(final ClassLoader referenceClassLoader) {
|
||||
if (referenceClassLoader instanceof URLClassLoader) {
|
||||
final URLClassLoader urlClassLoader = (URLClassLoader) referenceClassLoader;
|
||||
this.referenceClassLoader = urlClassLoader;
|
||||
} else throw new IllegalArgumentException("Unsupported ClassLoader: " + referenceClassLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate with ClassLoader from reference class.
|
||||
*/
|
||||
public ForkedRun(final Class<?> referenceClass) {
|
||||
this(referenceClass.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the actual forked process and create a CompilationResult object. This method should only be called once!
|
||||
*/
|
||||
public final CompilationResult run() {
|
||||
final Set<String> classPathElements = new LinkedHashSet<>();
|
||||
final Set<String> bootPathElements = new LinkedHashSet<>();
|
||||
|
||||
try {
|
||||
dispatchClassPath(classPathElements, bootPathElements);
|
||||
final String javaExe = getExecutable();
|
||||
final List<String> commands = buildCommands(classPathElements, bootPathElements, javaExe);
|
||||
final ProcessBuilder processBuilder = new ProcessBuilder().command(commands);
|
||||
if (outputCommand) System.out.println(Joiner.on(' ').join(commands));
|
||||
processBuilder.directory(Files.createTempDir());
|
||||
final Process proc = processBuilder.start();
|
||||
final List<String> errorMessages = gatherOutput(proc);
|
||||
final int statusCode = proc.waitFor();
|
||||
return new CompilationResult(statusCode == 0, Collections.<String>emptyList(), errorMessages);
|
||||
|
||||
} catch (InterruptedException | IOException | URISyntaxException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchClassPath(final Set<String> classPathElements, final Set<String> bootPathElements)
|
||||
throws URISyntaxException {
|
||||
for (final URL url : getUrLs()) {
|
||||
final File jarFile = new File(url.toURI());
|
||||
if (!jarFilePredicates.stream().anyMatch((it) -> it.test(jarFile))) continue;
|
||||
final String absolutePath = jarFile.getAbsolutePath();
|
||||
if (bootClassPathMatchers.stream().anyMatch((it) -> {
|
||||
return absolutePath.contains(it);
|
||||
})) bootPathElements.add(absolutePath);
|
||||
else classPathElements.add(absolutePath);
|
||||
}
|
||||
}
|
||||
|
||||
// collect all URLs from all ClassLoaders
|
||||
private Iterable<URL> getUrLs() {
|
||||
final Set<URL> urls = newLinkedHashSet();
|
||||
for (final ClassLoader referenceClassLoader : Lists.asList(this.referenceClassLoader,
|
||||
additionalClassLoaders.toArray(new ClassLoader[additionalClassLoaders.size()]))) {
|
||||
|
||||
ClassLoader current = referenceClassLoader;
|
||||
while (current != null) {
|
||||
if (current instanceof URLClassLoader) {
|
||||
final URLClassLoader urlClassLoader = (URLClassLoader) current;
|
||||
urls.addAll(asList(urlClassLoader.getURLs()));
|
||||
} else {
|
||||
throw new IllegalStateException("Bad ClassLoader: " + current);
|
||||
}
|
||||
current = current.getParent();
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
private String getExecutable() {
|
||||
return new File(
|
||||
System.getProperty("java.home"),
|
||||
useJavaC ? "../bin/javac" : "bin/java"
|
||||
).getAbsolutePath();
|
||||
}
|
||||
|
||||
private List<String> buildCommands(final Set<String> classPathElements, final Set<String> bootPathElements,
|
||||
final String javaExe) {
|
||||
final List<String> commands = newArrayList();
|
||||
if (!bootPathElements.isEmpty()) {
|
||||
vmArgs.add("-Xbootclasspath/p:" + SEP_JOINER.join(bootPathElements));
|
||||
}
|
||||
commands.add(javaExe);
|
||||
commands.addAll(vmArgs);
|
||||
if (!classPathElements.isEmpty()) {
|
||||
commands.add("-cp");
|
||||
commands.add(SEP_JOINER.join(classPathElements));
|
||||
}
|
||||
commands.addAll(args);
|
||||
return ImmutableList.copyOf(commands);
|
||||
}
|
||||
|
||||
private List<String> gatherOutput(final Process proc) throws IOException {
|
||||
try (BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
|
||||
final BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()))) {
|
||||
final List<String> errorMessages = newArrayList();
|
||||
String line = null;
|
||||
while ((line = stdInput.readLine()) != null) {
|
||||
System.out.println(line);
|
||||
}
|
||||
while ((line = stdError.readLine()) != null) {
|
||||
errorMessages.add(line);
|
||||
}
|
||||
return errorMessages;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
SEP_JOINER = Joiner.on(File.pathSeparatorChar);
|
||||
MAVEN_REPO = new File(System.getProperty("user.home"), ".m2/repository").getAbsolutePath();
|
||||
// ROOT_PROJECT_PATH = calculateProjectRoot();
|
||||
}
|
||||
|
||||
static String calculateProjectRoot() {
|
||||
File current = new File(System.getProperty("user.dir"));
|
||||
while (current != null) {
|
||||
if (new File(current, "x_common").isDirectory())
|
||||
return current.getAbsolutePath();
|
||||
current = current.getParentFile();
|
||||
}
|
||||
throw new IllegalStateException("Couldn't detect project root. Please fix this hacky logic");
|
||||
}
|
||||
|
||||
enum JarPredicate implements Predicate<File> {
|
||||
IN_LOCAL_MAVEN_REPO {
|
||||
@Override public boolean test(final File file) {
|
||||
return file.getAbsolutePath().startsWith(MAVEN_REPO);
|
||||
}
|
||||
},
|
||||
// IN_PROJECT_DIR {
|
||||
// @Override public boolean test(final File file) {
|
||||
// return file.getAbsolutePath().startsWith(ROOT_PROJECT_PATH);
|
||||
// }
|
||||
// },
|
||||
TOOLS_JAR {
|
||||
final Set<String> supportedNames = ImmutableSet.of("tools.jar", "classes.jar");
|
||||
|
||||
@Override public boolean test(final File file) {
|
||||
return supportedNames.contains(file.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package net.moonleay.quickcrashreports.client.util
|
||||
|
||||
object RunJavaProcess {
|
||||
@JvmStatic
|
||||
fun runInNewProcess(clazz: Class<*>, vararg args: String) {
|
||||
try {
|
||||
clazz.getDeclaredMethod("main", Array<String>::class.java)
|
||||
} catch (e: NoSuchMethodException) {
|
||||
error("Class ${clazz.name} has no main method")
|
||||
}
|
||||
|
||||
ForkedRun(clazz.classLoader).withArg(clazz).apply {
|
||||
args.forEach(::withArg)
|
||||
}.run()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.moonleay.quickcrashreports.datagen
|
||||
|
||||
import net.moonleay.quickcrashreports.build.BuildConstants
|
||||
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator
|
||||
import org.apache.logging.log4j.LogManager
|
||||
|
||||
internal class DataGenerator : DataGeneratorEntrypoint {
|
||||
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
|
||||
|
||||
override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) {
|
||||
LOGGER.info("Starting Data Generation")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package net.moonleay.quickcrashreports.mixin;
|
||||
|
||||
import com.mashape.unirest.http.HttpResponse;
|
||||
import com.mashape.unirest.http.Unirest;
|
||||
import com.mashape.unirest.http.exceptions.UnirestException;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.util.Session;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
import net.moonleay.quickcrashreports.client.ui.LaunchGui;
|
||||
import net.moonleay.quickcrashreports.client.util.RunJavaProcess;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Mixin(MinecraftClient.class)
|
||||
public class IJustCrashedMixin {
|
||||
|
||||
/* {
|
||||
"text": "<paste content>",
|
||||
"extension": "<file extension, optional>",
|
||||
"expires": <number of seconds from now, optional>,
|
||||
"burn_after_reading": <true/false, optional>
|
||||
}
|
||||
*/
|
||||
@Inject(method = "printCrashReport", at = @At("HEAD"), cancellable = false)
|
||||
private static void POSTCrashReportToWastebin(CrashReport report, CallbackInfo ci) {
|
||||
System.out.println("[QCR] Your game crashed!!");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("== Game crashed! ==\n");
|
||||
sb.append("Details:\n");
|
||||
MinecraftClient mc = MinecraftClient.getInstance();
|
||||
Session currentSession = mc.getSession();
|
||||
GameProfile profile = currentSession.getProfile();
|
||||
sb.append("User: ").append(profile.getName()).append(", UUID: ").append(profile.getId().toString());
|
||||
sb.append("\nCrash Reason: ").append(report.getMessage());
|
||||
sb.append("\nComplete report:\n").append(report.asString());
|
||||
|
||||
RunJavaProcess.runInNewProcess(LaunchGui.class, sb.toString());
|
||||
|
||||
String response = null;
|
||||
try {
|
||||
response = sendPaste(sb.toString(), true);
|
||||
} catch (IOException | InterruptedException | UnirestException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (response == null)
|
||||
return;
|
||||
// String id = response.replaceAll("{\"path\":\"", "");
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static String sendPaste(String text, boolean burnAfterReading) throws IOException, InterruptedException, UnirestException {
|
||||
StringBuilder body = new StringBuilder("{\"text\": \"");
|
||||
body.append(text.replaceAll("[\\u0000-\\u001F]", "<br>")).append("\",\n");
|
||||
// body.append("\"\"").append("\",\n");
|
||||
body.append("\"burn_after_reading\": ").append(burnAfterReading);
|
||||
body.append("}");
|
||||
|
||||
Unirest.setTimeouts(0, 0);
|
||||
HttpResponse<String> response = Unirest.post("https://bin.moonleay.net/")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(body.toString())
|
||||
.asString();
|
||||
return response.getBody();
|
||||
}
|
||||
|
||||
}
|
38
src/main/resources/fabric.mod.json
Normal file
38
src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "${modId}",
|
||||
"version": "${modVersion}",
|
||||
"name": "${modName}",
|
||||
"description": "Lets the user quickly upload crash reports to a wastebin instance.",
|
||||
"authors": [
|
||||
"The HuebCraft Team"
|
||||
],
|
||||
"contact": {},
|
||||
"license": "All rights reserved",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "net.moonleay.quickcrashreports.client.ClientMain"
|
||||
}
|
||||
],
|
||||
"main": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "net.moonleay.quickcrashreports.Main"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"${modId}.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=${fabricLoaderVersion}",
|
||||
"fabric": "*",
|
||||
"fabric-language-kotlin": ">=${fabricKotlinVersion}",
|
||||
"huebcraftconfiglib": "${configlibVersion}",
|
||||
"minecraft": "${minecraftVersion}"
|
||||
},
|
||||
"accessWidener": "${modId}.accesswidener"
|
||||
}
|
2
src/main/resources/quickcrashreports.accesswidener
Normal file
2
src/main/resources/quickcrashreports.accesswidener
Normal file
|
@ -0,0 +1,2 @@
|
|||
accessWidener v2 named
|
||||
|
14
src/main/resources/quickcrashreports.mixins.json
Normal file
14
src/main/resources/quickcrashreports.mixins.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "net.moonleay.quickcrashreports.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
],
|
||||
"client": [
|
||||
"IJustCrashedMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package net.moonleay.quickcrashreports.build
|
||||
|
||||
internal object BuildConstants {
|
||||
const val modId = "${modId}"
|
||||
const val modName = "${modName}"
|
||||
const val modVersion = "${modVersion}"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue