    001package net.minecraft.crash;
    002
    003import cpw.mods.fml.common.FMLCommonHandler;
    004import cpw.mods.fml.relauncher.Side;
    005import cpw.mods.fml.relauncher.SideOnly;
    006import java.io.File;
    007import java.io.FileWriter;
    008import java.io.IOException;
    009import java.io.PrintWriter;
    010import java.io.StringWriter;
    011import java.text.SimpleDateFormat;
    012import java.util.ArrayList;
    013import java.util.Date;
    014import java.util.Iterator;
    015import java.util.List;
    016import net.minecraft.logging.ILogAgent;
    017import net.minecraft.util.ReportedException;
    018
    019public class CrashReport
    020{
    021    /** Description of the crash report. */
    022    private final String description;
    023
    024    /** The Throwable that is the "cause" for this crash and Crash Report. */
    025    private final Throwable cause;
    026    private final CrashReportCategory field_85061_c = new CrashReportCategory(this, "System Details");
    027
    028    /** Holds the keys and values of all crash report sections. */
    029    private final List crashReportSections = new ArrayList();
    030
    031    /** File of crash report. */
    032    private File crashReportFile = null;
    033    private boolean field_85059_f = true;
    034    private StackTraceElement[] field_85060_g = new StackTraceElement[0];
    035
    036    public CrashReport(String par1Str, Throwable par2Throwable)
    037    {
    038        this.description = par1Str;
    039        this.cause = par2Throwable;
    040        this.populateEnvironment();
    041    }
    042
    043    /**
    044     * Populates this crash report with initial information about the running server and operating system / java
    045     * environment
    046     */
    047    private void populateEnvironment()
    048    {
    049        this.field_85061_c.addCrashSectionCallable("Minecraft Version", new CallableMinecraftVersion(this));
    050        this.field_85061_c.addCrashSectionCallable("Operating System", new CallableOSInfo(this));
    051        this.field_85061_c.addCrashSectionCallable("Java Version", new CallableJavaInfo(this));
    052        this.field_85061_c.addCrashSectionCallable("Java VM Version", new CallableJavaInfo2(this));
    053        this.field_85061_c.addCrashSectionCallable("Memory", new CallableMemoryInfo(this));
    054        this.field_85061_c.addCrashSectionCallable("JVM Flags", new CallableJVMFlags(this));
    055        this.field_85061_c.addCrashSectionCallable("AABB Pool Size", new CallableCrashMemoryReport(this));
    056        this.field_85061_c.addCrashSectionCallable("Suspicious classes", new CallableSuspiciousClasses(this));
    057        this.field_85061_c.addCrashSectionCallable("IntCache", new CallableIntCache(this));
    058        FMLCommonHandler.instance().enhanceCrashReport(this, this.field_85061_c);
    059    }
    060
    061    /**
    062     * Returns the description of the Crash Report.
    063     */
    064    public String getDescription()
    065    {
    066        return this.description;
    067    }
    068
    069    /**
    070     * Returns the Throwable object that is the cause for the crash and Crash Report.
    071     */
    072    public Throwable getCrashCause()
    073    {
    074        return this.cause;
    075    }
    076
    077    @SideOnly(Side.CLIENT)
    078    public String func_90021_c()
    079    {
    080        StringBuilder stringbuilder = new StringBuilder();
    081        this.getSectionsInStringBuilder(stringbuilder);
    082        return stringbuilder.toString();
    083    }
    084
    085    /**
    086     * Gets the various sections of the crash report into the given StringBuilder
    087     */
    088    public void getSectionsInStringBuilder(StringBuilder par1StringBuilder)
    089    {
    090        if (this.field_85060_g != null && this.field_85060_g.length > 0)
    091        {
    092            par1StringBuilder.append("-- Head --\n");
    093            par1StringBuilder.append("Stacktrace:\n");
    094            StackTraceElement[] astacktraceelement = this.field_85060_g;
    095            int i = astacktraceelement.length;
    096
    097            for (int j = 0; j < i; ++j)
    098            {
    099                StackTraceElement stacktraceelement = astacktraceelement[j];
    100                par1StringBuilder.append("\t").append("at ").append(stacktraceelement.toString());
    101                par1StringBuilder.append("\n");
    102            }
    103
    104            par1StringBuilder.append("\n");
    105        }
    106
    107        Iterator iterator = this.crashReportSections.iterator();
    108
    109        while (iterator.hasNext())
    110        {
    111            CrashReportCategory crashreportcategory = (CrashReportCategory)iterator.next();
    112            crashreportcategory.func_85072_a(par1StringBuilder);
    113            par1StringBuilder.append("\n\n");
    114        }
    115
    116        this.field_85061_c.func_85072_a(par1StringBuilder);
    117    }
    118
    119    /**
    120     * Gets the stack trace of the Throwable that caused this crash report, or if that fails, the cause .toString().
    121     */
    122    public String getCauseStackTraceOrString()
    123    {
    124        StringWriter stringwriter = null;
    125        PrintWriter printwriter = null;
    126        String s = this.cause.toString();
    127
    128        try
    129        {
    130            stringwriter = new StringWriter();
    131            printwriter = new PrintWriter(stringwriter);
    132            this.cause.printStackTrace(printwriter);
    133            s = stringwriter.toString();
    134        }
    135        finally
    136        {
    137            try
    138            {
    139                if (stringwriter != null)
    140                {
    141                    stringwriter.close();
    142                }
    143
    144                if (printwriter != null)
    145                {
    146                    printwriter.close();
    147                }
    148            }
    149            catch (IOException ioexception)
    150            {
    151                ;
    152            }
    153        }
    154
    155        return s;
    156    }
    157
    158    /**
    159     * Gets the complete report with headers, stack trace, and different sections as a string.
    160     */
    161    public String getCompleteReport()
    162    {
    163        StringBuilder stringbuilder = new StringBuilder();
    164        stringbuilder.append("---- Minecraft Crash Report ----\n");
    165        stringbuilder.append("// ");
    166        stringbuilder.append(getWittyComment());
    167        stringbuilder.append("\n\n");
    168        stringbuilder.append("Time: ");
    169        stringbuilder.append((new SimpleDateFormat()).format(new Date()));
    170        stringbuilder.append("\n");
    171        stringbuilder.append("Description: ");
    172        stringbuilder.append(this.description);
    173        stringbuilder.append("\n\n");
    174        stringbuilder.append(this.getCauseStackTraceOrString());
    175        stringbuilder.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n");
    176
    177        for (int i = 0; i < 87; ++i)
    178        {
    179            stringbuilder.append("-");
    180        }
    181
    182        stringbuilder.append("\n\n");
    183        this.getSectionsInStringBuilder(stringbuilder);
    184        return stringbuilder.toString();
    185    }
    186
    187    @SideOnly(Side.CLIENT)
    188
    189    /**
    190     * Gets the file this crash report is saved into.
    191     */
    192    public File getFile()
    193    {
    194        return this.crashReportFile;
    195    }
    196
    197    /**
    198     * Saves the complete crash report to the given File.
    199     */
    200    public boolean saveToFile(File par1File, ILogAgent par2ILogAgent)
    201    {
    202        if (this.crashReportFile != null)
    203        {
    204            return false;
    205        }
    206        else
    207        {
    208            if (par1File.getParentFile() != null)
    209            {
    210                par1File.getParentFile().mkdirs();
    211            }
    212
    213            try
    214            {
    215                FileWriter filewriter = new FileWriter(par1File);
    216                filewriter.write(this.getCompleteReport());
    217                filewriter.close();
    218                this.crashReportFile = par1File;
    219                return true;
    220            }
    221            catch (Throwable throwable)
    222            {
    223                par2ILogAgent.logSevereException("Could not save crash report to " + par1File, throwable);
    224                return false;
    225            }
    226        }
    227    }
    228
    229    public CrashReportCategory func_85056_g()
    230    {
    231        return this.field_85061_c;
    232    }
    233
    234    /**
    235     * Creates a CrashReportCategory
    236     */
    237    public CrashReportCategory makeCategory(String par1Str)
    238    {
    239        return this.makeCategoryDepth(par1Str, 1);
    240    }
    241
    242    /**
    243     * Creates a CrashReportCategory for the given stack trace depth
    244     */
    245    public CrashReportCategory makeCategoryDepth(String par1Str, int par2)
    246    {
    247        CrashReportCategory crashreportcategory = new CrashReportCategory(this, par1Str);
    248
    249        if (this.field_85059_f)
    250        {
    251            int j = crashreportcategory.func_85073_a(par2);
    252            StackTraceElement[] astacktraceelement = this.cause.getStackTrace();
    253            StackTraceElement stacktraceelement = null;
    254            StackTraceElement stacktraceelement1 = null;
    255
    256            if (astacktraceelement != null && astacktraceelement.length - j < astacktraceelement.length)
    257            {
    258                stacktraceelement = astacktraceelement[astacktraceelement.length - j];
    259
    260                if (astacktraceelement.length + 1 - j < astacktraceelement.length)
    261                {
    262                    stacktraceelement1 = astacktraceelement[astacktraceelement.length + 1 - j];
    263                }
    264            }
    265
    266            this.field_85059_f = crashreportcategory.func_85069_a(stacktraceelement, stacktraceelement1);
    267
    268            if (j > 0 && !this.crashReportSections.isEmpty())
    269            {
    270                CrashReportCategory crashreportcategory1 = (CrashReportCategory)this.crashReportSections.get(this.crashReportSections.size() - 1);
    271                crashreportcategory1.func_85070_b(j);
    272            }
    273            else if (astacktraceelement != null && astacktraceelement.length >= j)
    274            {
    275                this.field_85060_g = new StackTraceElement[astacktraceelement.length - j];
    276                System.arraycopy(astacktraceelement, 0, this.field_85060_g, 0, this.field_85060_g.length);
    277            }
    278            else
    279            {
    280                this.field_85059_f = false;
    281            }
    282        }
    283
    284        this.crashReportSections.add(crashreportcategory);
    285        return crashreportcategory;
    286    }
    287
    288    /**
    289     * Gets a random witty comment for inclusion in this CrashReport
    290     */
    291    private static String getWittyComment()
    292    {
    293        String[] astring = new String[] {"Who set us up the TNT?", "Everything\'s going to plan. No, really, that was supposed to happen.", "Uh... Did I do that?", "Oops.", "Why did you do that?", "I feel sad now :(", "My bad.", "I\'m sorry, Dave.", "I let you down. Sorry :(", "On the bright side, I bought you a teddy bear!", "Daisy, daisy...", "Oh - I know what I did wrong!", "Hey, that tickles! Hehehe!", "I blame Dinnerbone.", "You should try our sister game, Minceraft!", "Don\'t be sad. I\'ll do better next time, I promise!", "Don\'t be sad, have a hug! <3", "I just don\'t know what went wrong :(", "Shall we play a game?", "Quite honestly, I wouldn\'t worry myself about that.", "I bet Cylons wouldn\'t have this problem.", "Sorry :(", "Surprise! Haha. Well, this is awkward.", "Would you like a cupcake?", "Hi. I\'m Minecraft, and I\'m a crashaholic.", "Ooh. Shiny.", "This doesn\'t make any sense!", "Why is it breaking :(", "Don\'t do that.", "Ouch. That hurt :(", "You\'re mean.", "This is a token for 1 free hug. Redeem at your nearest Mojangsta: [~~HUG~~]", "There are four lights!"};
    294
    295        try
    296        {
    297            return astring[(int)(System.nanoTime() % (long)astring.length)];
    298        }
    299        catch (Throwable throwable)
    300        {
    301            return "Witty comment unavailable :(";
    302        }
    303    }
    304
    305    /**
    306     * Creates a crash report for the exception
    307     */
    308    public static CrashReport makeCrashReport(Throwable par0Throwable, String par1Str)
    309    {
    310        CrashReport crashreport;
    311
    312        if (par0Throwable instanceof ReportedException)
    313        {
    314            crashreport = ((ReportedException)par0Throwable).getCrashReport();
    315        }
    316        else
    317        {
    318            crashreport = new CrashReport(par1Str, par0Throwable);
    319        }
    320
    321        return crashreport;
    322    }
    323}