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.func_98234_c("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}