diff --git a/libraries/branding/branding-mcin-20100206-2103-mca1.2.1_01/src/main/java/net/ornithemc/osl/branding/impl/BrandingPatchImpl.java b/libraries/branding/branding-mcin-20100206-2103-mca1.2.1_01/src/main/java/net/ornithemc/osl/branding/impl/BrandingPatchImpl.java index d7f8dd0a9..e0d06e0ca 100644 --- a/libraries/branding/branding-mcin-20100206-2103-mca1.2.1_01/src/main/java/net/ornithemc/osl/branding/impl/BrandingPatchImpl.java +++ b/libraries/branding/branding-mcin-20100206-2103-mca1.2.1_01/src/main/java/net/ornithemc/osl/branding/impl/BrandingPatchImpl.java @@ -20,15 +20,7 @@ public class BrandingPatchImpl implements ClientModInitializer { public static String getGameVersion() { if (gameVersion == null) { - gameVersion = MinecraftVersion.resolve().gameVersion(); - - if (gameVersion.charAt(0) == 'a') { - gameVersion = "Alpha v" + gameVersion.substring(1); - } else if (gameVersion.startsWith("inf")) { - gameVersion = "Infdev 0.31." + gameVersion.substring(4); - } else if (gameVersion.startsWith("in-")) { - gameVersion = "Indev 0.31." + gameVersion.substring(3); - } + gameVersion = MinecraftVersion.resolve().friendlyGameVersion(); } return gameVersion; diff --git a/libraries/core/src/main/java/net/ornithemc/osl/core/impl/util/MinecraftVersion.java b/libraries/core/src/main/java/net/ornithemc/osl/core/impl/util/MinecraftVersion.java index 583581cce..163f0b5e8 100644 --- a/libraries/core/src/main/java/net/ornithemc/osl/core/impl/util/MinecraftVersion.java +++ b/libraries/core/src/main/java/net/ornithemc/osl/core/impl/util/MinecraftVersion.java @@ -39,6 +39,8 @@ public static MinecraftVersion resolve() { // semantic versions for other game versions private final Map semanticVersions = new HashMap<>(); + private String friendlyGameVersion; + private MinecraftVersion(String gameVersion, SemanticVersion semanticVersion) { this.gameVersion = gameVersion; this.semanticVersion = semanticVersion; @@ -48,6 +50,39 @@ public String gameVersion() { return this.gameVersion; } + public String friendlyGameVersion() { + if (this.friendlyGameVersion == null) { + String friendlyVersion = this.gameVersion; + + int i = friendlyVersion.indexOf('+'); + if (i > 0) { + friendlyVersion = friendlyVersion.substring(i); + } + + char chr = friendlyVersion.charAt(0); + + if (chr == 'b') { + friendlyVersion = "Beta " + friendlyVersion.substring(1); + } else if (chr == 'a') { + friendlyVersion = "Alpha v" + friendlyVersion.substring(1); + } else if (chr == 'i') { + if (friendlyVersion.charAt(2) == 'f') { + friendlyVersion = "Infdev 0.31." + friendlyVersion.substring(4); + } else { + friendlyVersion = "Indev 0.31." + friendlyVersion.substring(3); + } + } else if (chr == 'c') { + friendlyVersion = "Classic " + friendlyVersion.substring(1); + } else if (chr == 'r' || chr == 'p') { + friendlyVersion = "Pre-Classic " + friendlyVersion.substring(3); + } + + this.friendlyGameVersion = friendlyVersion; + } + + return this.friendlyGameVersion; + } + public String semanticVersion() { return this.semanticVersion.toString(); } diff --git a/libraries/crash-reports/README.md b/libraries/crash-reports/README.md new file mode 100644 index 000000000..739764537 --- /dev/null +++ b/libraries/crash-reports/README.md @@ -0,0 +1,41 @@ +# Crash Reports API + +The Crash Reports API adds general mod information to crash reports and adds a convenient version-independent way for mods to throw crash exceptions and add details to crash reports. + +## Throwing Crash Exceptions + +The `CrashExceptions` class provides a utility method for constructing crash exceptions with a given error and title. You may also pass in a crash report builder in which you can add details to the crash report. + +```java +try { + cookieMonster.eatCookie(cookie); +} catch (Throwable error) { + throw CrashExceptions.from("Cookie monster encountered an error while eating a cookie", error, report -> { + CrashReportSection section = report.addSection("Cookie Monster"); + + section.addDetail("Name", () -> cookieMonster.getName()); + section.addDetail("Cookie Recipe", () -> cookie.getRecipe()); + }) +} + +``` + +## Populating Crash Reports + +It is possible to add general information to the system report and level details sections of a crash report. This is done by registering a crash listener to one of several classes that implement the `CrashReportPopulator` interface (`Minecraft`, `MinecraftServer`, `World`, and `WorldData`). In a crash listener you can add details to the given crash report section or add a new section to the crash report. + +```java +MinecraftClientEvents.START.register(minecraft -> { + World world = minecraft.world; + + minecraft.addCrashListener((report, section) -> { + CrashReportSection section = report.addSection("Cookie Management"); + + section.addDetail("Cookies made", "Not enough!"); + section.addDetail("Cookies eaten", "All of them!"); + }); + world.addCrashListener((report, section) -> { + section.addDetail("Cookie monsters", () -> world.cookieMonsters.size()); + }); +}); +``` diff --git a/libraries/crash-reports/build.gradle b/libraries/crash-reports/build.gradle new file mode 100644 index 000000000..e220ddc42 --- /dev/null +++ b/libraries/crash-reports/build.gradle @@ -0,0 +1,6 @@ +setUpLibrary(project) + +dependencies { + implementation 'org.jetbrains:annotations:24.0.1' + implementation 'it.unimi.dsi:fastutil:8.5.9' +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/build.gradle b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/build.gradle new file mode 100644 index 000000000..0a350fdb3 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/build.gradle @@ -0,0 +1 @@ +setUpModule(project) diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/gradle.properties b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/gradle.properties new file mode 100644 index 000000000..d119a3c92 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/gradle.properties @@ -0,0 +1,4 @@ +min_mc_version = 1.4.3 +max_mc_version = 1.14.4 + +minecraft_version = 1.14.4 diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java new file mode 100644 index 000000000..279e0876f --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java @@ -0,0 +1,37 @@ +package net.ornithemc.osl.crashreports.api; + +import java.util.function.Consumer; + +import net.ornithemc.osl.crashreports.api.crash.CrashReport; +import net.ornithemc.osl.crashreports.impl.CrashExceptionsImpl; + +/** + * Utility methods for constructing crash exceptions. + */ +public final class CrashExceptions { + + /** + * Constructs a crash exception with a crash report that is empty + * exception for system details. + * + * @param title the title of the crash report. + * @param error the error that caused the crash. + * @return a new crash exception + */ + public static RuntimeException from(String title, Throwable error) { + return CrashExceptionsImpl.from(title, error); + } + + /** + * Constructs a crash exception with a crash report that is populated + * by the given builder. + * + * @param title the title of the crash report. + * @param error the error that caused the crash. + * @param builder the builder that populates the crash report. + * @return a new crash exception. + */ + public static RuntimeException from(String title, Throwable error, Consumer builder) { + return CrashExceptionsImpl.from(title, error, builder); + } +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java new file mode 100644 index 000000000..cbdb5ff8f --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java @@ -0,0 +1,20 @@ +package net.ornithemc.osl.crashreports.impl; + +import java.util.function.Consumer; + +import net.minecraft.util.crash.CrashException; +import net.minecraft.util.crash.CrashReport; + +public final class CrashExceptionsImpl { + + public static RuntimeException from(String title, Throwable error) { + return from(title, error, report -> { }); + } + + public static RuntimeException from(String title, Throwable error, Consumer builder) { + CrashReport report = CrashReport.of(error, title); + builder.accept(report); + + return new CrashException(report); + } +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/MinecraftMixin.java b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/MinecraftMixin.java new file mode 100644 index 000000000..523ef8d21 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/MinecraftMixin.java @@ -0,0 +1,39 @@ +package net.ornithemc.osl.crashreports.impl.mixin.client; + +import java.util.ArrayList; +import java.util.List; + +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.CallbackInfoReturnable; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.crash.CrashReport; + +import net.ornithemc.osl.crashreports.api.crash.CrashListener; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; +import net.ornithemc.osl.crashreports.impl.CrashReports; + +@Mixin(Minecraft.class) +public class MinecraftMixin implements CrashReportPopulator { + + @Unique + private final List crashListeners = new ArrayList<>(); + + @Override + public void addCrashListener(CrashListener listener) { + this.crashListeners.add(listener); + } + + @Inject( + method = "populateCrashReport", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$populateCrashReport(CrashReport report, CallbackInfoReturnable cir) { + CrashReports.notifyListeners(report, this.crashListeners); + } +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportCategoryMixin.java b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportCategoryMixin.java new file mode 100644 index 000000000..fdda77bcc --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportCategoryMixin.java @@ -0,0 +1,135 @@ +package net.ornithemc.osl.crashreports.impl.mixin.common; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; + +import net.ornithemc.conditionalmixin.annotations.Conditional; +import net.ornithemc.conditionalmixin.annotations.Version; + +import net.ornithemc.osl.core.impl.util.MinecraftVersion; +import net.ornithemc.osl.crashreports.api.crash.CrashReportDetail; +import net.ornithemc.osl.crashreports.impl.crash.ModifiableCrashReportSection; + +@Mixin(net.minecraft.util.crash.CrashReportCategory.class) +public class CrashReportCategoryMixin implements ModifiableCrashReportSection { + + private static final boolean CRASH_REPORT_DETAIL_EXISTS = MinecraftVersion.resolve().compareTo("16w15b") >= 0; + private static final boolean ADD_RETURNS_REPORT_CATEGORY = MinecraftVersion.resolve().compareTo("1.14.4-pre4") >= 0; + + @Shadow + private List entries; + + @Unique + private Object2ReferenceMap entriesByKey; + + @Conditional(minecraftVersion = @Version("<16w15b")) + @Shadow + private void m_16534273(String key, Callable detail) { } + @Conditional(minecraftVersion = @Version(">=16w15b <1.14.4-pre4")) + @Shadow + private void m_16534273(String key, net.minecraft.util.crash.CrashReportDetail detail) { } + @Conditional(minecraftVersion = @Version(">=1.14.4-pre4")) + @Shadow + private net.minecraft.util.crash.CrashReportCategory add(String key, net.minecraft.util.crash.CrashReportDetail detail) { return null; } + @Conditional(minecraftVersion = @Version("<1.14.4-pre4")) + @Shadow + private void m_32997861(String key, Object detail) { } + @Conditional(minecraftVersion = @Version(">=1.14.4-pre4")) + @Shadow + private net.minecraft.util.crash.CrashReportCategory add(String key, Object detail) { return null; } + + @Inject( + method = "", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$init(CallbackInfo ci) { + this.entriesByKey = new Object2ReferenceOpenHashMap<>(); + } + + @Conditional(minecraftVersion = @Version("<1.14.4-pre4")) + @Inject( + method = "m_32997861", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$saveEntryIndex(String key, Object detail, CallbackInfo ci) { + this.saveLastEntry(key); + } + + @Conditional(minecraftVersion = @Version(">=1.14.4-pre4")) + @Inject( + method = "m_32997861", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$saveEntryIndex(String key, Object detail, CallbackInfoReturnable cir) { + this.saveLastEntry(key); + } + + @Unique + private void saveLastEntry(String key) { + this.entriesByKey.put(key, this.entries.get(this.entries.size() - 1)); + } + + @Override + public void addDetail(String key, CrashReportDetail detail) { + if (!CRASH_REPORT_DETAIL_EXISTS) { + this.m_16534273(key, (Callable) detail::call); + } else if (!ADD_RETURNS_REPORT_CATEGORY) { + this.m_16534273(key, (net.minecraft.util.crash.CrashReportDetail) detail::call); + } else { + this.add(key, detail::call); + } + } + @Override + public void addDetail(String key, Object detail) { + if (ADD_RETURNS_REPORT_CATEGORY) { + this.add(key, detail); + } else { + this.m_32997861(key, detail); + } + } + + @Override + public void addDetailError(String key, Throwable error) { + this.addDetail(key, error); + } + + @Override + public void removeDetail(String key) { + Object entry = this.entriesByKey.remove(key); + + if (entry != null) { + this.entries.remove(entry); + } + } + + @Override + public void replaceDetail(String oldKey, String newKey, CrashReportDetail detail) { + Object entry = this.entriesByKey.remove(oldKey); + + if (entry != null) { + int index = this.entries.indexOf(entry); + + this.addDetail(newKey, detail); // also updates the map + Collections.swap(this.entries, index, this.entries.size() - 1); + this.entries.remove(this.entries.size() - 1); + } + } +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportMixin.java b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportMixin.java new file mode 100644 index 000000000..4ce33a3ad --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportMixin.java @@ -0,0 +1,115 @@ +package net.ornithemc.osl.crashreports.impl.mixin.common; + +import java.io.File; +import java.nio.file.Path; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.ornithemc.conditionalmixin.annotations.Conditional; +import net.ornithemc.conditionalmixin.annotations.Version; + +import net.ornithemc.osl.core.impl.util.MinecraftVersion; +import net.ornithemc.osl.crashreports.api.crash.CrashReport; +import net.ornithemc.osl.crashreports.api.crash.CrashReportSection; +import net.ornithemc.osl.crashreports.impl.CrashReports; + +@Mixin(net.minecraft.util.crash.CrashReport.class) +public class CrashReportMixin implements CrashReport { + + private static final boolean SAVING_FORWARDING_SUPPORTED = MinecraftVersion.resolve().compareTo("13w07a") < 0 || MinecraftVersion.resolve().compareTo("13w37b") > 0; + + @Shadow + private String description; + @Shadow + private Throwable exception; + @Shadow + private net.minecraft.util.crash.CrashReportCategory systemDetails; + @Shadow + private File file; + + @Shadow + private net.minecraft.util.crash.CrashReportCategory addCategory(String name) { return null; } + @Shadow + private net.minecraft.util.crash.CrashReportCategory addCategory(String name, int ignoredStackTraceCallCount) { return null; } + @Shadow + private String getExceptionMessage() { return null; } + @Shadow + private String build() { return null; } + @Shadow + private void addDetails(StringBuilder sb) { } + @Conditional(minecraftVersion = @Version({ "<13w07a", ">13w37b" })) + @Shadow + private boolean writeToFile(File file) { return false; } + + @Inject( + method = "fillSystemDetails", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$addModsToSystemDetails(CallbackInfo ci) { + CrashReports.addModTree(this.systemDetails); + } + + @Override + public String getTitle() { + return this.description; + } + + @Override + public Throwable getError() { + return this.exception; + } + + @Override + public CrashReportSection getSystemReport() { + return this.systemDetails; + } + + @Override + public CrashReportSection addSection(String name) { + return this.addCategory(name, 2); + } + + @Override + public CrashReportSection addSection(String name, int stackTraceOffset) { + return this.addCategory(name, stackTraceOffset + 1); + } + + @Override + public String getErrorMessage() { + return this.getExceptionMessage(); + } + + @Override + public String buildFriendlyString() { + return this.build(); + } + + @Override + public String buildFriendlyStringWithoutHeader() { + StringBuilder sb = new StringBuilder(); + this.addDetails(sb); + return sb.toString(); + } + + @Override + public Path getSaveFile() { + return this.file.toPath(); + } + + @Override + public boolean saveToFile(Path file) { + if (SAVING_FORWARDING_SUPPORTED) { + return this.writeToFile(file.toFile()); + } else { + // TODO: figure how to shadow a method with a non existent param type + // in 13w07a-13w37b there's an extra net.minecraft.util.Logger parameter + throw new UnsupportedOperationException(); + } + } +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/MinecraftServerMixin.java b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/MinecraftServerMixin.java new file mode 100644 index 000000000..1e536bcd7 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/MinecraftServerMixin.java @@ -0,0 +1,39 @@ +package net.ornithemc.osl.crashreports.impl.mixin.common; + +import java.util.ArrayList; +import java.util.List; + +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.CallbackInfoReturnable; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.crash.CrashReport; + +import net.ornithemc.osl.crashreports.api.crash.CrashListener; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; +import net.ornithemc.osl.crashreports.impl.CrashReports; + +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin implements CrashReportPopulator { + + @Unique + private final List crashListeners = new ArrayList<>(); + + @Override + public void addCrashListener(CrashListener listener) { + this.crashListeners.add(listener); + } + + @Inject( + method = "populateCrashReport", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$populateCrashReport(CrashReport report, CallbackInfoReturnable cir) { + CrashReports.notifyListeners(report, this.crashListeners); + } +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldDataMixin.java b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldDataMixin.java new file mode 100644 index 000000000..3a6533fd1 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldDataMixin.java @@ -0,0 +1,39 @@ +package net.ornithemc.osl.crashreports.impl.mixin.common; + +import java.util.ArrayList; +import java.util.List; + +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 net.minecraft.util.crash.CrashReportCategory; +import net.minecraft.world.WorldData; + +import net.ornithemc.osl.crashreports.api.crash.CrashListener; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; +import net.ornithemc.osl.crashreports.impl.CrashReports; + +@Mixin(WorldData.class) +public class WorldDataMixin implements CrashReportPopulator { + + @Unique + private final List crashListeners = new ArrayList<>(); + + @Override + public void addCrashListener(CrashListener listener) { + this.crashListeners.add(listener); + } + + @Inject( + method = "populateCrashReport", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$populateCrashReport(CrashReportCategory category, CallbackInfo ci) { + CrashReports.notifyListeners(null, category, this.crashListeners); + } +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldMixin.java b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldMixin.java new file mode 100644 index 000000000..7bfd7f1a5 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldMixin.java @@ -0,0 +1,42 @@ +package net.ornithemc.osl.crashreports.impl.mixin.common; + +import java.util.ArrayList; +import java.util.List; + +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.CallbackInfoReturnable; + +import com.llamalad7.mixinextras.sugar.Local; + +import net.minecraft.util.crash.CrashReport; +import net.minecraft.util.crash.CrashReportCategory; +import net.minecraft.world.World; + +import net.ornithemc.osl.crashreports.api.crash.CrashListener; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; +import net.ornithemc.osl.crashreports.impl.CrashReports; + +@Mixin(World.class) +public class WorldMixin implements CrashReportPopulator { + + @Unique + private final List crashListeners = new ArrayList<>(); + + @Override + public void addCrashListener(CrashListener listener) { + this.crashListeners.add(listener); + } + + @Inject( + method = "populateCrashReport", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$populateCrashReport(CrashReport report, CallbackInfoReturnable cir, @Local CrashReportCategory category) { + CrashReports.notifyListeners(report, category, this.crashListeners); + } +} diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/fabric.mod.json b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/fabric.mod.json new file mode 100644 index 000000000..10b52be7e --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "osl-crash-reports", + "version": "0.1.0-alpha.1+mc1.4.3-mc1.14.4", + "environment": "*", + "mixins": [ + "osl.crash-reports.mixins.json" + ], + "accessWidener": "osl.crash-reports.classtweaker", + "depends": { + "fabricloader": "\u003e\u003d0.18.0", + "minecraft": "\u003e\u003d1.4.3 \u003c\u003d1.14.4", + "osl-core": "\u003e\u003d0.9.0", + "osl-entrypoints": "\u003e\u003d0.5.0" + }, + "name": "OSL Crash Reports", + "description": "Crash reports API and events.", + "authors": [ + "OrnitheMC" + ], + "contact": { + "homepage": "https://ornithemc.net/", + "issues": "https://github.com/OrnitheMC/ornithe-standard-libraries/issues", + "sources": "https://github.com/OrnitheMC/ornithe-standard-libraries" + }, + "license": "Apache-2.0" +} \ No newline at end of file diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/osl.crash-reports.classtweaker b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/osl.crash-reports.classtweaker new file mode 100644 index 000000000..397a03013 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/osl.crash-reports.classtweaker @@ -0,0 +1,8 @@ +classTweaker v1 named + +transitive-inject-interface net/minecraft/util/crash/CrashReport net/ornithemc/osl/crashreports/impl/crash/CrashReportExtensionImpl +transitive-inject-interface net/minecraft/util/crash/CrashReportCategory net/ornithemc/osl/crashreports/impl/crash/CrashReportCategoryExtensionImpl +transitive-inject-interface net/minecraft/client/Minecraft net/ornithemc/osl/crashreports/impl/crash/CrashReportPopulatorImpl +transitive-inject-interface net/minecraft/world/World net/ornithemc/osl/crashreports/impl/crash/CrashReportPopulatorImpl +transitive-inject-interface net/minecraft/world/WorldData net/ornithemc/osl/crashreports/impl/crash/CrashReportPopulatorImpl +transitive-inject-interface net/minecraft/server/MinecraftServer net/ornithemc/osl/crashreports/impl/crash/CrashReportPopulatorImpl diff --git a/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/osl.crash-reports.mixins.json b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/osl.crash-reports.mixins.json new file mode 100644 index 000000000..dd7363cae --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc1.4.3-mc1.14.4/src/main/resources/osl.crash-reports.mixins.json @@ -0,0 +1,22 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.ornithemc.osl.crashreports.impl.mixin", + "compatibilityLevel": "JAVA_8", + "plugin": "net.ornithemc.conditionalmixin.mixin.ConditionalMixinPlugin", + "mixins": [ + "common.CrashReportCategoryMixin", + "common.CrashReportMixin", + "common.MinecraftServerMixin", + "common.WorldDataMixin", + "common.WorldMixin" + ], + "client": [ + "client.MinecraftMixin" + ], + "server": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/build.gradle b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/build.gradle new file mode 100644 index 000000000..0a350fdb3 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/build.gradle @@ -0,0 +1 @@ +setUpModule(project) diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/gradle.properties b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/gradle.properties new file mode 100644 index 000000000..8453d2ec6 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/gradle.properties @@ -0,0 +1,6 @@ +min_mc_version = 12w15a +max_mc_version = 1.4.2 + +minecraft_version = 1.3.2 + +nests_build = 8 diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java new file mode 100644 index 000000000..279e0876f --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java @@ -0,0 +1,37 @@ +package net.ornithemc.osl.crashreports.api; + +import java.util.function.Consumer; + +import net.ornithemc.osl.crashreports.api.crash.CrashReport; +import net.ornithemc.osl.crashreports.impl.CrashExceptionsImpl; + +/** + * Utility methods for constructing crash exceptions. + */ +public final class CrashExceptions { + + /** + * Constructs a crash exception with a crash report that is empty + * exception for system details. + * + * @param title the title of the crash report. + * @param error the error that caused the crash. + * @return a new crash exception + */ + public static RuntimeException from(String title, Throwable error) { + return CrashExceptionsImpl.from(title, error); + } + + /** + * Constructs a crash exception with a crash report that is populated + * by the given builder. + * + * @param title the title of the crash report. + * @param error the error that caused the crash. + * @param builder the builder that populates the crash report. + * @return a new crash exception. + */ + public static RuntimeException from(String title, Throwable error, Consumer builder) { + return CrashExceptionsImpl.from(title, error, builder); + } +} diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java new file mode 100644 index 000000000..528611ec6 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java @@ -0,0 +1,25 @@ +package net.ornithemc.osl.crashreports.impl; + +import java.util.function.Consumer; + +import net.minecraft.util.crash.CrashException; +import net.minecraft.util.crash.CrashReport; + +public final class CrashExceptionsImpl { + + public static RuntimeException from(String title, Throwable error) { + return from(title, error, report -> { }); + } + + public static RuntimeException from(String title, Throwable error, Consumer builder) { + CrashReport report; + if (error instanceof CrashException) { + report = ((CrashException) error).getReport(); + } else { + report = new CrashReport(title, error); + } + builder.accept(report); + + return new CrashException(report); + } +} diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/ClientWorldMixin.java b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/ClientWorldMixin.java new file mode 100644 index 000000000..1d0441ea0 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/ClientWorldMixin.java @@ -0,0 +1,29 @@ +package net.ornithemc.osl.crashreports.impl.mixin.client; + +import java.util.concurrent.Callable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.crash.CrashReport; + +import net.ornithemc.osl.crashreports.impl.mixin.common.WorldMixin; + +@Mixin(ClientWorld.class) +public class ClientWorldMixin extends WorldMixin { + + @WrapOperation( + method = "populateCrashReport", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/util/crash/CrashReport;add(Ljava/lang/String;Ljava/util/concurrent/Callable;)V" + ) + ) + private void osl$crashreports$redirectDetailsToSection(CrashReport report, String key, Callable detail, Operation op) { + this.currentCrashReportSection.addDetail(key, detail::call); + } +} diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/MinecraftMixin.java b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/MinecraftMixin.java new file mode 100644 index 000000000..ffc7293be --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/MinecraftMixin.java @@ -0,0 +1,54 @@ +package net.ornithemc.osl.crashreports.impl.mixin.client; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +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.CallbackInfoReturnable; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.crash.CrashReport; + +import net.ornithemc.osl.crashreports.api.crash.CrashListener; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; +import net.ornithemc.osl.crashreports.impl.CrashReports; + +@Mixin(Minecraft.class) +public class MinecraftMixin implements CrashReportPopulator { + + @Unique + private final List crashListeners = new ArrayList<>(); + + @Override + public void addCrashListener(CrashListener listener) { + this.crashListeners.add(listener); + } + + @WrapOperation( + method = "populateCrashReport", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/util/crash/CrashReport;add(Ljava/lang/String;Ljava/util/concurrent/Callable;)V" + ) + ) + private void osl$crashreports$redirectDetailsToSystemReport(CrashReport report, String key, Callable detail, Operation op) { + report.getSystemReport().addDetail(key, detail::call); + } + + @Inject( + method = "populateCrashReport", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$populateCrashReport(CrashReport report, CallbackInfoReturnable cir) { + CrashReports.notifyListeners(report, this.crashListeners); + } +} diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportMixin.java b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportMixin.java new file mode 100644 index 000000000..278f62141 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/CrashReportMixin.java @@ -0,0 +1,243 @@ +package net.ornithemc.osl.crashreports.impl.mixin.common; + +import java.io.File; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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 com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import net.ornithemc.osl.crashreports.api.crash.CrashReport; +import net.ornithemc.osl.crashreports.api.crash.CrashReportSection; +import net.ornithemc.osl.crashreports.impl.CrashReports; +import net.ornithemc.osl.crashreports.impl.crash.CrashReportSectionImpl; + +@Mixin(net.minecraft.util.crash.CrashReport.class) +public class CrashReportMixin implements CrashReport { + + @Shadow + private String description; + @Shadow + private Throwable exception; + @Shadow + private Map details; + @Shadow + private File file; + + @Unique + private CrashReportSectionImpl systemReport; + @Unique + private List sections; + @Unique + private boolean trackingStackTrace; + @Unique + private StackTraceElement[] stackTrace; + + @Shadow + private String getExceptionMessage() { return null; } + @Shadow + private boolean save(File file) { return false; } + + @Inject( + method = "", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/util/crash/CrashReport;fillSystemDetails()V" + ) + ) + private void osl$crashreports$init(CallbackInfo ci) { + this.systemReport = new CrashReportSectionImpl("System Details"); + this.sections = new ArrayList<>(); + + this.trackingStackTrace = true; + } + + @WrapOperation( + method = "fillSystemDetails", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/util/crash/CrashReport;add(Ljava/lang/String;Ljava/util/concurrent/Callable;)V" + ) + ) + private void osl$crashreports$redirectDetailsToSystemReport(net.minecraft.util.crash.CrashReport report, String key, Callable detail, Operation op) { + this.systemReport.addDetail(key, detail::call); + } + + @Inject( + method = "fillSystemDetails", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$addModsToSystemDetails(CallbackInfo ci) { + CrashReports.addModTree(this.systemReport); + } + + @Inject( + method = "add(Ljava/lang/String;Ljava/lang/Object;)V", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$duplicateVanillaDetailToSystemDetails(String key, Object detail, CallbackInfo ci) { + this.systemReport.addDetail(key, detail); + } + + @Inject( + method = "addDetails", + cancellable = true, + at = @At( + value = "HEAD" + ) + ) + private void osl$crashreports$buildFriendlyString(StringBuilder sb, CallbackInfo ci) { + this.buildString(sb); + + if (this.details.isEmpty()) { + ci.cancel(); + } else { + sb.append("\n\n\n\n\n\n\n\n\n\n"); + sb.append("Miscellaneous details"); + sb.append("\n\n"); + } + } + + @Override + public String getTitle() { + return this.description; + } + + @Override + public Throwable getError() { + return this.exception; + } + + @Override + public CrashReportSection getSystemReport() { + return this.systemReport; + } + + @Override + public CrashReportSection addSection(String name) { + return this.addSection(name, 1); + } + + @Override + public CrashReportSection addSection(String name, int stackTraceOffset) { + CrashReportSectionImpl section = new CrashReportSectionImpl(name); + this.sections.add(section); + + if (this.trackingStackTrace) { + int length = section.fillStackTrace(stackTraceOffset); + + StackTraceElement[] fullTrace = this.exception.getStackTrace(); + StackTraceElement source = null; + StackTraceElement next = null; + + int index = fullTrace.length - length; + + if (index < fullTrace.length) { + source = fullTrace[index]; + + if (index + 1 < fullTrace.length) { + next = fullTrace[index + 1]; + } + } + + this.trackingStackTrace = section.validateStackTrace(source, next); + + if (fullTrace.length >= length && index >= 0 && index < fullTrace.length) { + this.stackTrace = new StackTraceElement[index]; + System.arraycopy(fullTrace, 0, this.stackTrace, 0, this.stackTrace.length); + } else { + this.trackingStackTrace = false; + } + } + + return section; + } + + @Override + public String getErrorMessage() { + return this.getExceptionMessage(); + } + + @Override + public String buildFriendlyString() { + StringBuilder sb = new StringBuilder(); + + sb.append("---- Minecraft Crash Report ----\n"); + sb.append("// "); + sb.append(CrashReports.getWittyComment()); + sb.append("\n\n"); + sb.append("Time: "); + sb.append(new SimpleDateFormat().format(new Date())); + sb.append("\n"); + sb.append("Description: "); + sb.append(this.description); + sb.append("\n\n"); + sb.append(this.getErrorMessage()); + sb.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n"); + + for (int i = 0; i < 87; i++) { + sb.append("-"); + } + + sb.append("\n\n"); + + this.buildString(sb); + + return sb.toString(); + } + + @Override + public String buildFriendlyStringWithoutHeader() { + StringBuilder sb = new StringBuilder(); + this.buildString(sb); + return sb.toString(); + } + + @Unique + private void buildString(StringBuilder sb) { + if (this.stackTrace != null && this.stackTrace.length > 0) { + sb.append("-- Head --\n"); + sb.append("Stacktrace:\n"); + + for (StackTraceElement element : this.stackTrace) { + sb.append("\t").append("at ").append(element.toString()); + sb.append("\n"); + } + + sb.append("\n"); + } + + for (CrashReportSectionImpl section : this.sections) { + section.buildString(sb); + sb.append("\n\n"); + } + + this.systemReport.buildString(sb); + } + + @Override + public Path getSaveFile() { + return this.file.toPath(); + } + + @Override + public boolean saveToFile(Path file) { + return this.save(file.toFile()); + } +} diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/MinecraftServerMixin.java b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/MinecraftServerMixin.java new file mode 100644 index 000000000..11d714f2a --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/MinecraftServerMixin.java @@ -0,0 +1,54 @@ +package net.ornithemc.osl.crashreports.impl.mixin.common; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +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.CallbackInfoReturnable; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.crash.CrashReport; + +import net.ornithemc.osl.crashreports.api.crash.CrashListener; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; +import net.ornithemc.osl.crashreports.impl.CrashReports; + +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin implements CrashReportPopulator { + + @Unique + private final List crashListeners = new ArrayList<>(); + + @Override + public void addCrashListener(CrashListener listener) { + this.crashListeners.add(listener); + } + + @WrapOperation( + method = "populateCrashReport", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/util/crash/CrashReport;add(Ljava/lang/String;Ljava/util/concurrent/Callable;)V" + ) + ) + private void osl$crashreports$redirectDetailsToSystemReport(CrashReport report, String key, Callable detail, Operation op) { + report.getSystemReport().addDetail(key, detail::call); + } + + @Inject( + method = "populateCrashReport", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$populateCrashReport(CrashReport report, CallbackInfoReturnable cir) { + CrashReports.notifyListeners(report, this.crashListeners); + } +} diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldMixin.java b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldMixin.java new file mode 100644 index 000000000..fe7a92ad8 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/common/WorldMixin.java @@ -0,0 +1,73 @@ +package net.ornithemc.osl.crashreports.impl.mixin.common; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.CallbackInfoReturnable; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import net.minecraft.util.crash.CrashReport; +import net.minecraft.world.World; +import net.minecraft.world.WorldData; + +import net.ornithemc.osl.crashreports.api.crash.CrashListener; +import net.ornithemc.osl.crashreports.api.crash.CrashReportSection; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; +import net.ornithemc.osl.crashreports.impl.CrashReports; + +@Mixin(World.class) +public class WorldMixin implements CrashReportPopulator { + + @Shadow + private WorldData data; + + @Unique + private final List crashListeners = new ArrayList<>(); + + @Unique + protected CrashReportSection currentCrashReportSection; + + @Override + public void addCrashListener(CrashListener listener) { + this.crashListeners.add(listener); + } + + @Inject( + method = "populateCrashReport", + at = @At( + value = "HEAD" + ) + ) + private void osl$crashreports$createSection(CrashReport report, CallbackInfoReturnable cir) { + this.currentCrashReportSection = report.addSection("Affected level"); + } + + @WrapOperation( + method = "populateCrashReport", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/util/crash/CrashReport;add(Ljava/lang/String;Ljava/util/concurrent/Callable;)V" + ) + ) + private void osl$crashreports$redirectDetailsToSection(CrashReport report, String key, Callable detail, Operation op) { + this.currentCrashReportSection.addDetail(key, detail::call); + } + + @Inject( + method = "populateCrashReport", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$populateCrashReport(CrashReport report, CallbackInfoReturnable cir) { + CrashReports.notifyListeners(report, this.currentCrashReportSection, this.crashListeners); + } +} diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/fabric.mod.json b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/fabric.mod.json new file mode 100644 index 000000000..c84d3449b --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "osl-crash-reports", + "version": "0.1.0-alpha.1+mc12w15a-mc1.4.2", + "environment": "*", + "mixins": [ + "osl.crash-reports.mixins.json" + ], + "accessWidener": "osl.crash-reports.classtweaker", + "depends": { + "fabricloader": "\u003e\u003d0.18.0", + "minecraft": "\u003e\u003d1.3-alpha.12.15.a \u003c\u003d1.4.2", + "osl-core": "\u003e\u003d0.9.0", + "osl-entrypoints": "\u003e\u003d0.5.0" + }, + "name": "OSL Crash Reports", + "description": "Crash reports API and events.", + "authors": [ + "OrnitheMC" + ], + "contact": { + "homepage": "https://ornithemc.net/", + "issues": "https://github.com/OrnitheMC/ornithe-standard-libraries/issues", + "sources": "https://github.com/OrnitheMC/ornithe-standard-libraries" + }, + "license": "Apache-2.0" +} \ No newline at end of file diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/osl.crash-reports.classtweaker b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/osl.crash-reports.classtweaker new file mode 100644 index 000000000..06b814171 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/osl.crash-reports.classtweaker @@ -0,0 +1,6 @@ +classTweaker v1 named + +transitive-inject-interface net/minecraft/util/crash/CrashReport net/ornithemc/osl/crashreports/impl/crash/CrashReportExtensionImpl +transitive-inject-interface net/minecraft/client/Minecraft net/ornithemc/osl/crashreports/impl/crash/CrashReportPopulatorImpl +transitive-inject-interface net/minecraft/world/World net/ornithemc/osl/crashreports/impl/crash/CrashReportPopulatorImpl +transitive-inject-interface net/minecraft/server/MinecraftServer net/ornithemc/osl/crashreports/impl/crash/CrashReportPopulatorImpl diff --git a/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/osl.crash-reports.mixins.json b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/osl.crash-reports.mixins.json new file mode 100644 index 000000000..78056aa7b --- /dev/null +++ b/libraries/crash-reports/crash-reports-mc12w15a-mc1.4.2/src/main/resources/osl.crash-reports.mixins.json @@ -0,0 +1,20 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.ornithemc.osl.crashreports.impl.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "common.CrashReportMixin", + "common.MinecraftServerMixin", + "common.WorldMixin" + ], + "client": [ + "client.ClientWorldMixin", + "client.MinecraftMixin" + ], + "server": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/build.gradle b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/build.gradle new file mode 100644 index 000000000..0a350fdb3 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/build.gradle @@ -0,0 +1 @@ +setUpModule(project) diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/gradle.properties b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/gradle.properties new file mode 100644 index 000000000..b11e0feba --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/gradle.properties @@ -0,0 +1,8 @@ +min_mc_version = a1.0.1_01 +max_mc_version = 1.2.5 + +minecraft_version = 1.2.5 + +client_raven_build = 1 +client_nests_build = 10 +server_nests_build = 4 diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java new file mode 100644 index 000000000..279e0876f --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/CrashExceptions.java @@ -0,0 +1,37 @@ +package net.ornithemc.osl.crashreports.api; + +import java.util.function.Consumer; + +import net.ornithemc.osl.crashreports.api.crash.CrashReport; +import net.ornithemc.osl.crashreports.impl.CrashExceptionsImpl; + +/** + * Utility methods for constructing crash exceptions. + */ +public final class CrashExceptions { + + /** + * Constructs a crash exception with a crash report that is empty + * exception for system details. + * + * @param title the title of the crash report. + * @param error the error that caused the crash. + * @return a new crash exception + */ + public static RuntimeException from(String title, Throwable error) { + return CrashExceptionsImpl.from(title, error); + } + + /** + * Constructs a crash exception with a crash report that is populated + * by the given builder. + * + * @param title the title of the crash report. + * @param error the error that caused the crash. + * @param builder the builder that populates the crash report. + * @return a new crash exception. + */ + public static RuntimeException from(String title, Throwable error, Consumer builder) { + return CrashExceptionsImpl.from(title, error, builder); + } +} diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/client/MinecraftExtension.java b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/client/MinecraftExtension.java new file mode 100644 index 000000000..fdf363db3 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/client/MinecraftExtension.java @@ -0,0 +1,11 @@ +package net.ornithemc.osl.crashreports.api.client; + +import net.minecraft.util.crash.CrashReport; + +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; + +public interface MinecraftExtension extends CrashReportPopulator { + + CrashReport populateCrashReport(CrashReport report); + +} diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/server/MinecraftServerExtension.java b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/server/MinecraftServerExtension.java new file mode 100644 index 000000000..51f14b3a9 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/server/MinecraftServerExtension.java @@ -0,0 +1,10 @@ +package net.ornithemc.osl.crashreports.api.server; + +import net.ornithemc.osl.crashreports.api.crash.CrashReport; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; + +public interface MinecraftServerExtension extends CrashReportPopulator { + + CrashReport populateCrashReport(CrashReport report); + +} diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/world/WorldExtension.java b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/world/WorldExtension.java new file mode 100644 index 000000000..de3797ebd --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/api/world/WorldExtension.java @@ -0,0 +1,11 @@ +package net.ornithemc.osl.crashreports.api.world; + +import net.ornithemc.osl.crashreports.api.crash.CrashReport; +import net.ornithemc.osl.crashreports.api.crash.CrashReportSection; +import net.ornithemc.osl.crashreports.api.crash.CrashReportPopulator; + +public interface WorldExtension extends CrashReportPopulator { + + CrashReportSection populateCrashReport(CrashReport report); + +} diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java new file mode 100644 index 000000000..6cc83fef0 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/CrashExceptionsImpl.java @@ -0,0 +1,32 @@ +package net.ornithemc.osl.crashreports.impl; + +import java.util.function.Consumer; + +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; + +import net.ornithemc.osl.crashreports.api.crash.CrashException; +import net.ornithemc.osl.crashreports.api.crash.CrashReport; + +public final class CrashExceptionsImpl { + + private static final boolean USE_VANILLA_CRASH_REPORT = FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT; + + public static RuntimeException from(String title, Throwable error) { + return from(title, error, report -> { }); + } + + public static RuntimeException from(String title, Throwable error, Consumer builder) { + CrashReport report; + if (error instanceof CrashException) { + report = ((CrashException) error).getReport(); + } else if (!USE_VANILLA_CRASH_REPORT) { + report = CrashReport.of(title, error); + } else { + report = new net.minecraft.util.crash.CrashReport(title, error); + } + builder.accept(report); + + return new CrashException(report); + } +} diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/client/MinecraftExtensionImpl.java b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/client/MinecraftExtensionImpl.java new file mode 100644 index 000000000..a3a54cbb5 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/client/MinecraftExtensionImpl.java @@ -0,0 +1,14 @@ +package net.ornithemc.osl.crashreports.impl.client; + +import net.minecraft.util.crash.CrashReport; + +import net.ornithemc.osl.crashreports.api.client.MinecraftExtension; +import net.ornithemc.osl.crashreports.impl.crash.CrashReportPopulatorImpl; + +public interface MinecraftExtensionImpl extends MinecraftExtension, CrashReportPopulatorImpl { + + @Override + default CrashReport populateCrashReport(CrashReport report) { + throw new AbstractMethodError(); + } +} diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/CrashReportMixin.java b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/CrashReportMixin.java new file mode 100644 index 000000000..f525837e6 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/CrashReportMixin.java @@ -0,0 +1,239 @@ +package net.ornithemc.osl.crashreports.impl.mixin.client; + +import java.io.BufferedWriter; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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 net.ornithemc.osl.crashreports.api.crash.CrashReport; +import net.ornithemc.osl.crashreports.api.crash.CrashReportSection; +import net.ornithemc.osl.crashreports.impl.CrashReports; +import net.ornithemc.osl.crashreports.impl.crash.CrashReportSectionImpl; + +@Mixin(net.minecraft.util.crash.CrashReport.class) +public class CrashReportMixin implements CrashReport { + + @Shadow + private String description; + @Shadow + private Throwable exception; + + @Unique + private CrashReportSectionImpl systemDetails; + @Unique + private List sections; + @Unique + private boolean trackingStackTrace; + @Unique + private StackTraceElement[] stackTrace; + @Unique + private Path file; + + @Inject( + method = "", + at = @At( + value = "TAIL" + ) + ) + private void osl$crashreports$init(CallbackInfo ci) { + this.systemDetails = new CrashReportSectionImpl("System Details"); + this.sections = new ArrayList<>(); + + this.trackingStackTrace = true; + + CrashReports.addSystemDetails(this.systemDetails); + } + + @Override + public String getTitle() { + return this.description; + } + + @Override + public Throwable getError() { + return this.exception; + } + + @Override + public CrashReportSection getSystemReport() { + return this.systemDetails; + } + + @Override + public CrashReportSection addSection(String name) { + return this.addSection(name, 1); + } + + @Override + public CrashReportSection addSection(String name, int stackTraceOffset) { + CrashReportSectionImpl section = new CrashReportSectionImpl(name); + this.sections.add(section); + + if (this.trackingStackTrace) { + int length = section.fillStackTrace(stackTraceOffset); + + StackTraceElement[] fullTrace = this.exception.getStackTrace(); + StackTraceElement source = null; + StackTraceElement next = null; + + int index = fullTrace.length - length; + + if (index < fullTrace.length) { + source = fullTrace[index]; + + if (index + 1 < fullTrace.length) { + next = fullTrace[index + 1]; + } + } + + this.trackingStackTrace = section.validateStackTrace(source, next); + + if (fullTrace.length >= length && index >= 0 && index < fullTrace.length) { + this.stackTrace = new StackTraceElement[index]; + System.arraycopy(fullTrace, 0, this.stackTrace, 0, this.stackTrace.length); + } else { + this.trackingStackTrace = false; + } + } + + return section; + } + + @Override + public String getErrorMessage() { + Throwable exception = this.exception; + + if (exception.getMessage() == null) { + if (exception instanceof NullPointerException) { + exception = replaceException(exception, new NullPointerException(this.description)); + } else if (exception instanceof StackOverflowError) { + exception = replaceException(exception, new StackOverflowError(this.description)); + } else if (exception instanceof OutOfMemoryError) { + exception = replaceException(exception, new OutOfMemoryError(this.description)); + } + } + + StringWriter writer = new StringWriter(); + + try (PrintWriter printer = new PrintWriter(writer)) { + exception.printStackTrace(new PrintWriter(writer)); + return writer.toString(); + } + } + + private static Throwable replaceException(Throwable original, Throwable copy) { + try { + Throwable cause = original.getCause(); + + if (cause != null) { + copy.initCause(cause); + } + + Throwable[] suppresseds = original.getSuppressed(); + + for (Throwable suppressed : suppresseds) { + copy.addSuppressed(suppressed); + } + + copy.setStackTrace(original.getStackTrace()); + + return copy; + } catch (Throwable t) { + return original; + } + } + + @Override + public String buildFriendlyString() { + StringBuilder sb = new StringBuilder(); + + sb.append("---- Minecraft Crash Report ----\n"); + sb.append("// "); + sb.append(CrashReports.getWittyComment()); + sb.append("\n\n"); + sb.append("Time: "); + sb.append(new SimpleDateFormat().format(new Date())); + sb.append("\n"); + sb.append("Description: "); + sb.append(this.description); + sb.append("\n\n"); + sb.append(this.getErrorMessage()); + sb.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n"); + + for (int i = 0; i < 87; i++) { + sb.append("-"); + } + + sb.append("\n\n"); + + this.buildString(sb); + + return sb.toString(); + } + + @Override + public String buildFriendlyStringWithoutHeader() { + StringBuilder sb = new StringBuilder(); + this.buildString(sb); + return sb.toString(); + } + + @Unique + private void buildString(StringBuilder sb) { + if (this.stackTrace != null && this.stackTrace.length > 0) { + sb.append("-- Head --\n"); + sb.append("Stacktrace:\n"); + + for (StackTraceElement element : this.stackTrace) { + sb.append("\t").append("at ").append(element.toString()); + sb.append("\n"); + } + + sb.append("\n"); + } + + for (CrashReportSectionImpl section : this.sections) { + section.buildString(sb); + sb.append("\n\n"); + } + + this.systemDetails.buildString(sb); + } + + @Override + public Path getSaveFile() { + return this.file; + } + + @Override + public boolean saveToFile(Path file) { + if (this.file != null) { + return false; + } + + if (file.getParent() != null) { + file.getParent().toFile().mkdirs(); + } + + try (BufferedWriter bw = Files.newBufferedWriter(file)) { + bw.write(this.buildFriendlyString()); + this.file = file; + return true; + } catch (Throwable t) { + CrashReports.LOGGER.error("Could not save crash report to {}", file, t); + return false; + } + } +} diff --git a/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/CrashReportPanelMixin.java b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/CrashReportPanelMixin.java new file mode 100644 index 000000000..584d192a3 --- /dev/null +++ b/libraries/crash-reports/crash-reports-mca1.0.1_01-mc1.2.5/src/main/java/net/ornithemc/osl/crashreports/impl/mixin/client/CrashReportPanelMixin.java @@ -0,0 +1,78 @@ +package net.ornithemc.osl.crashreports.impl.mixin.client; + +import java.awt.TextArea; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; + +import net.minecraft.client.CrashReportPanel; +import net.minecraft.client.Minecraft; +import net.minecraft.util.crash.CrashReport; + +@Mixin(CrashReportPanel.class) +public class CrashReportPanelMixin { + + private static final String GENERATED = "Generated "; + private static final String OPENGL = "OpenGL: "; + private static final String PROBLEM = "Minecraft has stopped running because it encountered a problem."; + + @WrapOperation( + method = "", + at = @At( + value = "NEW", + target = "java/awt/TextArea" + ) + ) + private TextArea osl$crashreports$displayCrashReport(String text, int row, int columns, int scrollbars, Operation