All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.parzivail.errorman.ErrorManager Maven / Gradle / Ivy

package com.parzivail.errorman;

import ;
import I;
import Z;
import com.google.gson.Gson;
import com.parzivail.errorman.model.*;
import com.parzivail.pswg.Resources;
import com.parzivail.util.Lumberjack;
import com.parzivail.util.client.model.compat.FmlCompat;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.CustomValue;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.api.metadata.ModOrigin;
import net.minecraft.class_128;
import net.minecraft.class_129;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.lwjgl.util.tinyfd.TinyFileDialogs;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;

public class ErrorManager
{
	public static final Lumberjack LOG = new Lumberjack("errorman");

	private static final Gson GSON = new Gson();
	private static final String ROLLBAR_API_ENDPOINT = "https://api.rollbar.com/api/1/item/";
	private static final String ROLLBAR_CLIENT_TOKEN = "d9b378407e67416bad536678502410d1";

	public static void onCrash(class_128 report)
	{
		LOG.warn("Error manager triggered");

		// Do not ask to report errors if the user has disabled it
		if (!Resources.CONFIG.get().askToSendCrashReports)
			return;

		LOG.warn("Crash report system enabled");

		// Do not report errors if a new version of PSWG is known
		if (Resources.REMOTE_VERSION != null)
			return;

		LOG.warn("Running on latest version");

		// Do not ask to report errors in development environments
		if (FabricLoader.getInstance().isDevelopmentEnvironment())
			return;

		LOG.warn("Not in a development environment");

		// Do not report errors if Forge is loaded in any capacity
		if (FmlCompat.isForge())
			return;

		var cause = report.method_564();
		var atFault = false;

		var depth = 0;
		while (cause != null && depth < 10)
		{
			var trace = cause.getStackTrace();

			if (Arrays.stream(trace).map(StackTraceElement::getClassName).anyMatch(s -> s.startsWith("com.parzivail")))
			{
				atFault = true;
				break;
			}

			cause = cause.getCause();

			// Prevent infinite loops for poorly-formed
			// exception objects
			depth++;
		}

		LOG.warn("PSWG at fault: %s", atFault);

		// Do not ask to report errors if PSWG's package
		// isn't directly referenced in the stack trace
		if (!atFault)
			return;

		getConsentToDispatch(report);
	}

	private static void getConsentToDispatch(class_128 report)
	{
		LOG.warn("Getting consent to dispatch error");

		// Do not report errors without the user's consent
		var message = "PSWG has crashed! Send this crash report to the developers?";
		if (TinyFileDialogs.tinyfd_messageBox("PSWG Error", message, "yesno", "error", true))
		{
			try
			{
				dispatchError(report);
			}
			catch (Throwable t)
			{
				LOG.error("Failed to dispatch error");
				t.printStackTrace();
			}
		}
		else
			LOG.warn("No consent, abandoning report");
	}

	private static void dispatchError(class_128 report)
	{
		LOG.warn("Dispatching error");

		var modSet = new HashMap();

		String pswgVersion = "";
		for (var mod : FabricLoader.getInstance().getAllMods())
		{
			var meta = mod.getMetadata();

			if (meta.getId().equals(Resources.MODID))
				pswgVersion = meta.getVersion().getFriendlyString();

			// Exclude Fabric API modules
			var lifecycle = meta.getCustomValue("fabric-api:module-lifecycle");
			if (lifecycle != null)
				continue;

			var mo = mod.getOrigin();

			// Exclude nested mods
			if (mo.getKind() == ModOrigin.Kind.NESTED)
				continue;

			// Exclude mods without paths
			var paths = mo.getPaths();
			if (paths.isEmpty())
				continue;

			var filename = paths.get(0).getFileName().toString();
			modSet.put(meta.getId(), String.format("%s!%s", meta.getVersion().getFriendlyString(), filename));
		}

		LOG.warn("Found %s mods (pswg: %s)", modSet.size(), pswgVersion);

		// Do not report errors if the version field is
		// known but unpopulated
		if ("${version}".equals(pswgVersion))
			return;

		LOG.warn("Creating stack trace");

		var traceStack = new Stack();

		var cause = report.method_564();
		var depth = 0;
		while (cause != null && depth < 10)
		{
			var exc = new RollbarException(cause.getClass().getName(), cause.getMessage());

			var trace = cause.getStackTrace();
			var frames = new RollbarFrame[trace.length];

			for (var i = 0; i < trace.length; i++)
				frames[trace.length - i - 1] = new RollbarFrame(trace[i].getFileName(), trace[i].getClassName(), trace[i].getMethodName(), trace[i].getLineNumber());

			traceStack.push(new RollbarTrace(exc, frames));

			cause = cause.getCause();

			// Prevent infinite loops for poorly-formed
			// exception objects
			depth++;
		}

		LOG.warn("Stack trace contains %s entries", traceStack.size());

		var traceChain = new RollbarTrace[traceStack.size()];
		var i = 0;
		while (!traceStack.empty())
			traceChain[i++] = traceStack.pop();

		LOG.warn("Creating Rollbar request");

		var indexOfDelimiter = pswgVersion.indexOf('+');
		if (indexOfDelimiter < 0)
			indexOfDelimiter = pswgVersion.length();

		var request = new RollbarRequest(new RollbarData(
				pswgVersion.contains("-dev-") ? "dev" : "prod",
				pswgVersion.substring(0, indexOfDelimiter),
				"client",
				"java",
				new RollbarBody(traceChain),
				new RollbarRequestInfo(modSet)
		));
		var requestJson = GSON.toJson(request);

		LOG.warn("Posting error to Rollbar");

		try (var httpclient = HttpClients.createDefault())
		{
			HttpPost httppost = new HttpPost(ROLLBAR_API_ENDPOINT);
			httppost.addHeader("X-Rollbar-Access-Token", ROLLBAR_CLIENT_TOKEN);
			httppost.addHeader("Accept", "application/json");
			httppost.setEntity(new StringEntity(requestJson, ContentType.APPLICATION_JSON));

			LOG.warn("Executing request to Rollbar");

			HttpResponse response = httpclient.execute(httppost);

			LOG.warn("Executed post");

			HttpEntity entity = response.getEntity();

			LOG.warn("Response entity: %s", entity);

			if (entity != null)
			{
				LOG.warn("Reading entity stream");

				try (InputStream instream = entity.getContent())
				{
					var rollbarResponse = GSON.fromJson(new InputStreamReader(instream), RollbarResponse.class);

					LOG.warn("Response: %s", rollbarResponse);

					var section = report.method_562("PSWG Crash Submission Details");
					section.method_578("Status", rollbarResponse.err());
					if (rollbarResponse.result() != null)
						section.method_578("Report ID", rollbarResponse.result().uuid());
					else if (rollbarResponse.message() != null)
						section.method_578("Error Message", rollbarResponse.message());
				}
			}
		}
		catch (Throwable t)
		{
			LOG.error("Failed to post error");
			t.printStackTrace();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy