Merge branch 'master' into multichunk
Conflicts: src/main/java/ru/windcorp/progressia/server/ServerThread.java
This commit is contained in:
commit
8c6794e7da
@ -139,7 +139,9 @@ java {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileJava {
|
compileJava {
|
||||||
options.compilerArgs.addAll(['--release', '8'])
|
if (JavaVersion.current() != JavaVersion.VERSION_1_8) {
|
||||||
|
options.compilerArgs.addAll(['--release', '8'])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createPackages.dependsOn(build)
|
createPackages.dependsOn(build)
|
||||||
|
@ -690,6 +690,34 @@ public class StringUtil {
|
|||||||
return padToRight(src, length, ' ');
|
return padToRight(src, length, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String center(String src, int length) {
|
||||||
|
return center(src, length, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String center(String src, int length, char filler) {
|
||||||
|
if (length <= 0) {
|
||||||
|
throw new IllegalArgumentException("length must be positive (" + length + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src == null || length <= src.length()) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
char[] result = new char[length];
|
||||||
|
|
||||||
|
int leftPaddingLength = (length - src.length()) / 2;
|
||||||
|
|
||||||
|
Arrays.fill(result, 0, leftPaddingLength, filler);
|
||||||
|
|
||||||
|
for (int i = 0; i < src.length(); ++i) {
|
||||||
|
result[i + leftPaddingLength] = src.charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrays.fill(result, leftPaddingLength + src.length(), result.length, filler);
|
||||||
|
|
||||||
|
return new String(result);
|
||||||
|
}
|
||||||
|
|
||||||
public static int countWords(String src) {
|
public static int countWords(String src) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
boolean isWord = false;
|
boolean isWord = false;
|
||||||
|
@ -44,7 +44,7 @@ public class ProgressiaLauncher {
|
|||||||
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
||||||
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler((Thread thread, Throwable t)-> {
|
Thread.setDefaultUncaughtExceptionHandler((Thread thread, Throwable t)-> {
|
||||||
CrashReports.report(t,"Uncaught exception in thread %s", thread.getName());
|
CrashReports.crash(t, "Uncaught exception in thread %s", thread.getName());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public class ClientProxy implements Proxy {
|
|||||||
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||||
RenderTaskQueue.waitAndInvoke(() -> Typefaces.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz"))));
|
RenderTaskQueue.waitAndInvoke(() -> Typefaces.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz"))));
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
CrashReports.report(e, "ClientProxy failed");
|
throw CrashReports.report(e, "ClientProxy failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
TestContent.registerContent();
|
TestContent.registerContent();
|
||||||
|
@ -35,7 +35,7 @@ public class DefaultClientCommsListener implements CommsListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIOError(IOException reason) {
|
public void onIOError(IOException reason) {
|
||||||
CrashReports.report(reason, "An IOException has occurred in communications");
|
throw CrashReports.report(reason, "An IOException has occurred in communications");
|
||||||
// TODO implement
|
// TODO implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ public class Program implements OpenGLDeletable {
|
|||||||
glLinkProgram(handle);
|
glLinkProgram(handle);
|
||||||
|
|
||||||
if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) {
|
if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) {
|
||||||
CrashReports.report(null, "Bad program:\n%s", glGetProgramInfoLog(handle));
|
throw CrashReports.report(null, "Bad program:\n%s", glGetProgramInfoLog(handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ public class Shader implements OpenGLDeletable {
|
|||||||
if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) {
|
if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) {
|
||||||
System.out.println("***************** ERROR ******************");
|
System.out.println("***************** ERROR ******************");
|
||||||
System.out.println(source);
|
System.out.println(source);
|
||||||
CrashReports.report(null, "Bad shader:\n %s", glGetShaderInfoLog(handle));
|
throw CrashReports.report(null, "Bad shader:\n %s", glGetShaderInfoLog(handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ public class Attribute {
|
|||||||
|
|
||||||
public Attribute(int handle, Program program) {
|
public Attribute(int handle, Program program) {
|
||||||
if (handle < 0) {
|
if (handle < 0) {
|
||||||
CrashReports.report(null, "Bad handle: %d", handle);
|
throw CrashReports.report(null, "Bad handle: %d", handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
|
@ -27,7 +27,7 @@ public class Uniform {
|
|||||||
|
|
||||||
public Uniform(int handle, Program program) {
|
public Uniform(int handle, Program program) {
|
||||||
if (handle < 0) {
|
if (handle < 0) {
|
||||||
CrashReports.report(null, "Bad handle: %d", handle);
|
throw CrashReports.report(null, "Bad handle: %d", handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
|
@ -57,8 +57,7 @@ public class GNUUnifontLoader {
|
|||||||
return createStream(reader).map(GNUUnifontLoader::parse).map(GNUUnifontLoader::addToAtlas)
|
return createStream(reader).map(GNUUnifontLoader::parse).map(GNUUnifontLoader::addToAtlas)
|
||||||
.collect(Collectors.collectingAndThen(createMapper(), GNUUnifont::new));
|
.collect(Collectors.collectingAndThen(createMapper(), GNUUnifont::new));
|
||||||
} catch (IOException | UncheckedIOException e) {
|
} catch (IOException | UncheckedIOException e) {
|
||||||
CrashReports.report(e, "Could not load GNUUnifont");
|
throw CrashReports.report(e, "Could not load GNUUnifont");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +92,7 @@ public class GNUUnifontLoader {
|
|||||||
return new ParsedGlyph(c, editor);
|
return new ParsedGlyph(c, editor);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar);
|
throw CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java
Executable file → Normal file
10
src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java
Executable file → Normal file
@ -235,7 +235,7 @@ public class Component extends Named {
|
|||||||
|
|
||||||
valid = true;
|
valid = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not layout Component %s", this);
|
throw CrashReports.report(e, "Could not layout Component %s", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ public class Component extends Named {
|
|||||||
try {
|
try {
|
||||||
return getLayout().calculatePreferredSize(this);
|
return getLayout().calculatePreferredSize(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not calculate preferred size for Component %s", this);
|
throw CrashReports.report(e, "Could not calculate preferred size for Component %s", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,7 +517,7 @@ public class Component extends Named {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not dispatch input to Component %s", this);
|
throw CrashReports.report(e, "Could not dispatch input to Component %s", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,7 +609,7 @@ public class Component extends Named {
|
|||||||
try {
|
try {
|
||||||
assembleSelf(target);
|
assembleSelf(target);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not assemble Component %s", this);
|
throw CrashReports.report(e, "Could not assemble Component %s", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
assembleChildren(target);
|
assembleChildren(target);
|
||||||
@ -617,7 +617,7 @@ public class Component extends Named {
|
|||||||
try {
|
try {
|
||||||
postAssembleSelf(target);
|
postAssembleSelf(target);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Post-assembly failed for Component %s", this);
|
throw CrashReports.report(e, "Post-assembly failed for Component %s", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ public class Keys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
CrashReports.report(e, "Cannot access GLFW constants");
|
throw CrashReports.report(e, "Cannot access GLFW constants");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ public class Keys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (CODES_TO_NAMES.containsKey(value)) {
|
if (CODES_TO_NAMES.containsKey(value)) {
|
||||||
CrashReports.report(null, "Duplicate keys: %s and %s both map to %d(0x%s)",
|
throw CrashReports.report(null, "Duplicate keys: %s and %s both map to %d(0x%s)",
|
||||||
CODES_TO_NAMES.get(value), name, value, Integer.toHexString(value));
|
CODES_TO_NAMES.get(value), name, value, Integer.toHexString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,8 +157,7 @@ public class Atlases {
|
|||||||
|
|
||||||
return loadSprite(data.getData(), group);
|
return loadSprite(data.getData(), group);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group);
|
throw CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +173,7 @@ public class Atlases {
|
|||||||
Atlas newAtlas = new Atlas(group);
|
Atlas newAtlas = new Atlas(group);
|
||||||
|
|
||||||
if (!newAtlas.canAddSprite(data)) {
|
if (!newAtlas.canAddSprite(data)) {
|
||||||
CrashReports.report(null, "Could not fit texture into atlas of size %d", newAtlas.getSize());
|
throw CrashReports.report(null, "Could not fit texture into atlas of size %d", newAtlas.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
atlases.add(newAtlas);
|
atlases.add(newAtlas);
|
||||||
|
@ -31,8 +31,7 @@ public class SimpleTextures {
|
|||||||
new Sprite(new TexturePrimitive(data.getData()))
|
new Sprite(new TexturePrimitive(data.getData()))
|
||||||
);
|
);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Could not load texture %s", resource);
|
throw CrashReports.report(e, "Could not load texture %s", resource);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ public class TexturePrimitive implements OpenGLDeletable {
|
|||||||
OpenGLObjectTracker.register(this, GL11::glDeleteTextures);
|
OpenGLObjectTracker.register(this, GL11::glDeleteTextures);
|
||||||
|
|
||||||
if (handle < 0) {
|
if (handle < 0) {
|
||||||
CrashReports.report(null, "Failed to allocate texture");
|
throw CrashReports.report(null, "Failed to allocate texture");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ public class Localizer {
|
|||||||
data = new Parser(langFolder + this.language + ".lang").parse();
|
data = new Parser(langFolder + this.language + ".lang").parse();
|
||||||
pokeListeners(language);
|
pokeListeners(language);
|
||||||
} else {
|
} else {
|
||||||
CrashReports.report(null, "Language not found: %s", language);
|
throw CrashReports.report(null, "Language not found: %s", language);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,8 +81,7 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException | EscapeException e) {
|
} catch (IOException | EscapeException e) {
|
||||||
CrashReports.report(e, "Could not parse language file %s", filePath);
|
throw CrashReports.report(e, "Could not parse language file %s", filePath);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return parsedData;
|
return parsedData;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,7 @@ public class EntityRenderRegistry extends NamespacedInstanceRegistry<EntityRende
|
|||||||
).getData()
|
).getData()
|
||||||
);
|
);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Could not load entity texture %s", name);
|
throw CrashReports.report(e, "Could not load entity texture %s", name);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ public class Units {
|
|||||||
try {
|
try {
|
||||||
registerUnits(Units.class);
|
registerUnits(Units.class);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
CrashReports.report(e, "Could not register units declared in {}", Units.class.getName());
|
throw CrashReports.report(e, "Could not register units declared in {}", Units.class.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,11 +40,10 @@ public class ChunkIO {
|
|||||||
} catch (IOException | DecodingException e) {
|
} catch (IOException | DecodingException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
CrashReports.report(
|
throw CrashReports.report(
|
||||||
t, "Codec %s has failed to decode chunk (%d; %d; %d)",
|
t, "Codec %s has failed to decode chunk (%d; %d; %d)",
|
||||||
codec.getId(), position.x, position.y, position.z
|
codec.getId(), position.x, position.y, position.z
|
||||||
);
|
);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +58,7 @@ public class ChunkIO {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
CrashReports.report(
|
throw CrashReports.report(
|
||||||
t, "Codec %s has failed to encode chunk (%d; %d; %d)",
|
t, "Codec %s has failed to encode chunk (%d; %d; %d)",
|
||||||
codec.getId(), chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z
|
codec.getId(), chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z
|
||||||
);
|
);
|
||||||
|
@ -51,8 +51,7 @@ public class Resource extends Named {
|
|||||||
try (Reader reader = getReader()) {
|
try (Reader reader = getReader()) {
|
||||||
return CharStreams.toString(reader);
|
return CharStreams.toString(reader);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Could not read resource %s as text", this);
|
throw CrashReports.report(e, "Could not read resource %s as text", this);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,8 +60,7 @@ public class Resource extends Named {
|
|||||||
try (InputStream stream = getInputStream()) {
|
try (InputStream stream = getInputStream()) {
|
||||||
byteArray = ByteStreams.toByteArray(stream);
|
byteArray = ByteStreams.toByteArray(stream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Could not read resource %s as bytes", this);
|
throw CrashReports.report(e, "Could not read resource %s as bytes", this);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output == null || output.remaining() < byteArray.length) {
|
if (output == null || output.remaining() < byteArray.length) {
|
||||||
|
@ -18,11 +18,14 @@ import java.text.DateFormat;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility for reporting critical problems, gathering system context and terminating the application consequently (crashing).
|
||||||
|
* Do not hesitate to use {@link #report(Throwable, String, Object...)} at every other line.
|
||||||
|
*
|
||||||
|
* @author serega404
|
||||||
|
*/
|
||||||
public class CrashReports {
|
public class CrashReports {
|
||||||
|
|
||||||
private CrashReports() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports");
|
private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports");
|
||||||
|
|
||||||
private static final Collection<ContextProvider> PROVIDERS = Collections.synchronizedCollection(new ArrayList<>());
|
private static final Collection<ContextProvider> PROVIDERS = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
@ -30,17 +33,70 @@ public class CrashReports {
|
|||||||
private static final Collection<Analyzer> ANALYZERS = Collections.synchronizedCollection(new ArrayList<>());
|
private static final Collection<Analyzer> ANALYZERS = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger("crash");
|
private static final Logger LOGGER = LogManager.getLogger("crash");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ReportedException} that describes the provided problem so the program can crash later.
|
||||||
|
* This method is intended to be used like so:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* try {
|
||||||
|
* doSomethingDifficult(x);
|
||||||
|
* } catch (CouldntMakeItException e) {
|
||||||
|
* throw CrashReports.report(e, "We couldn't make it at x = %d", x);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>Such usage ensures that the report will be dealt with at the top of the call stack
|
||||||
|
* (at least in methods that have a properly set up
|
||||||
|
* {@linkplain #crash(Throwable, String, Object...) crash handler}). Not throwing the returned
|
||||||
|
* exception is pointless; using this in a thread without a crash handler will not produce a crash.
|
||||||
|
*
|
||||||
|
* <p>Avoid inserting variable information into {@code messageFormat} directly; use
|
||||||
|
* {@linkplain Formatter#summary format string} syntax and {@code args}. Different Strings
|
||||||
|
* in {@code messageFormat} may be interpreted as unrelated problems by {@linkplain Analyzer crash analyzers}.
|
||||||
|
*
|
||||||
|
* @param throwable a {@link Throwable} that caused the problem, if any; {@code null} otherwise
|
||||||
|
* @param messageFormat a human-readable description of the problem displayed in the crash report
|
||||||
|
* @param args an array of arguments for formatting {@code messageFormat}
|
||||||
|
* @return an exception containing the provided information that must be thrown
|
||||||
|
*/
|
||||||
|
public static ReportedException report(Throwable throwable, String messageFormat, Object... args) {
|
||||||
|
if (throwable instanceof ReportedException) return (ReportedException) throwable;
|
||||||
|
|
||||||
|
return new ReportedException(throwable, messageFormat, args);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <em>This method never returns.</em>
|
* Crashes the program due to the supplied problem.
|
||||||
* <p>
|
*
|
||||||
* TODO document
|
* <p><em>Use {@link #report(Throwable, String, Object...)} unless you are creating a catch-all handler for a
|
||||||
*
|
* thread.</em>
|
||||||
* @param throwable
|
*
|
||||||
* @param messageFormat
|
* <p>This method recovers information about the problem by casting {@code throwable} to {@link ReportedException},
|
||||||
* @param args
|
* or, failing that, uses the provided arguments as the information instead. It then constructs a full crash
|
||||||
|
* report, exports it and terminates the program by invoking {@link System#exit(int)}.
|
||||||
|
*
|
||||||
|
* <p>Such behavior can be dangerous or lead to unwanted consequences in the middle of the call stack, so it is
|
||||||
|
* necessary to invoke this method as high on the call stack as possible, usually in a {@code catch} clause
|
||||||
|
* of a {@code try} statement enveloping the thread's main method(s).
|
||||||
|
*
|
||||||
|
* @param throwable a {@link ReportedException} or another {@link Throwable} that caused the problem, if any;
|
||||||
|
* {@code null} otherwise
|
||||||
|
* @param messageFormat a human-readable description of the problem used when {@code throwable} is not a
|
||||||
|
* {@link ReportedException}. See {@link #report(Throwable, String, Object...)} for details.
|
||||||
|
* @param args an array of arguments for formatting {@code messageFormat}
|
||||||
|
* @return {@code null}, although this method never returns normally. Provided for convenience.
|
||||||
*/
|
*/
|
||||||
public static void report(Throwable throwable, String messageFormat, Object... args) {
|
public static RuntimeException crash(Throwable throwable, String messageFormat, Object... args) {
|
||||||
|
if (throwable instanceof ReportedException) {
|
||||||
|
ReportedException reportedException = (ReportedException) throwable;
|
||||||
|
|
||||||
|
// Discard provided arguments
|
||||||
|
throwable = reportedException.getCause();
|
||||||
|
messageFormat = reportedException.getMessageFormat();
|
||||||
|
args = reportedException.getArgs();
|
||||||
|
}
|
||||||
|
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -76,6 +132,7 @@ public class CrashReports {
|
|||||||
export(output.toString());
|
export(output.toString());
|
||||||
|
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void appendContextProviders(StringBuilder output) {
|
private static void appendContextProviders(StringBuilder output) {
|
||||||
@ -94,7 +151,7 @@ public class CrashReports {
|
|||||||
provider.provideContext(buf);
|
provider.provideContext(buf);
|
||||||
|
|
||||||
if (!buf.isEmpty()) {
|
if (!buf.isEmpty()) {
|
||||||
output.append(StringUtilsTemp.center(provider.getName(), 80)).append("\n");
|
output.append(StringUtil.center(provider.getName(), 80)).append("\n");
|
||||||
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
||||||
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||||
}
|
}
|
||||||
@ -122,7 +179,7 @@ public class CrashReports {
|
|||||||
Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]);
|
Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]);
|
||||||
|
|
||||||
if (localAnalyzersCopy.length > 0) {
|
if (localAnalyzersCopy.length > 0) {
|
||||||
output.append(StringUtilsTemp.center("Analyzers", 80)).append("\n");
|
output.append(StringUtil.center("Analyzers", 80)).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Analyzer analyzer : localAnalyzersCopy) {
|
for (Analyzer analyzer : localAnalyzersCopy) {
|
||||||
@ -222,36 +279,73 @@ public class CrashReports {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addSeparator(StringBuilder sb) {
|
||||||
|
sb.append(StringUtil.sequence('-', 80)).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the provided {@link ContextProvider} so it is consulted in the case of a crash.
|
||||||
|
* @param provider the provider to register
|
||||||
|
*/
|
||||||
public static void registerProvider(ContextProvider provider) {
|
public static void registerProvider(ContextProvider provider) {
|
||||||
PROVIDERS.add(provider);
|
PROVIDERS.add(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the provided {@link Analyzer} so it is consulted in the case of a crash.
|
||||||
|
* @param analyzer the analyzer to register
|
||||||
|
*/
|
||||||
public static void registerAnalyzer(Analyzer analyzer) {
|
public static void registerAnalyzer(Analyzer analyzer) {
|
||||||
ANALYZERS.add(analyzer);
|
ANALYZERS.add(analyzer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addSeparator(StringBuilder sb) {
|
/**
|
||||||
sb.append(StringUtil.sequence('-', 80)).append("\n");
|
* A wrapper used by {@link CrashReports} to transfer problem details from the place of
|
||||||
}
|
* occurrence to the handler at the top of the stack. Rethrow if caught
|
||||||
}
|
* (unless using {@link CrashReports#report(Throwable, String, Object...)}, which does
|
||||||
|
* so automatically).
|
||||||
|
*
|
||||||
|
* @author serega404
|
||||||
|
*/
|
||||||
|
public static class ReportedException extends RuntimeException {
|
||||||
|
|
||||||
class StringUtilsTemp {
|
private static final long serialVersionUID = 223720835231091533L;
|
||||||
public static String center(String s, int size) {
|
|
||||||
return center(s, size, ' ');
|
private final String messageFormat;
|
||||||
}
|
private final Object[] args;
|
||||||
|
|
||||||
public static String center(String s, int size, char pad) {
|
/**
|
||||||
if (s == null || size <= s.length())
|
* Constructs a {@link ReportedException}.
|
||||||
return s;
|
* @param throwable the reported {@link Throwable} or {@code null}
|
||||||
|
* @param messageFormat the reported message format.
|
||||||
StringBuilder sb = new StringBuilder(size);
|
* <em>This is not the message of the constructed Exception</em>.
|
||||||
for (int i = 0; i < (size - s.length()) / 2; i++) {
|
* @param args the reported message format arguments
|
||||||
sb.append(pad);
|
*/
|
||||||
|
public ReportedException(Throwable throwable, String messageFormat, Object... args) {
|
||||||
|
super(throwable);
|
||||||
|
this.messageFormat = messageFormat;
|
||||||
|
this.args = args;
|
||||||
}
|
}
|
||||||
sb.append(s);
|
|
||||||
while (sb.length() < size) {
|
/**
|
||||||
sb.append(pad);
|
* Returns the reported message format.
|
||||||
|
* @return message format
|
||||||
|
*/
|
||||||
|
public String getMessageFormat() {
|
||||||
|
return messageFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the reported message format arguments.
|
||||||
|
* @return message format arguments
|
||||||
|
*/
|
||||||
|
public Object[] getArgs() {
|
||||||
|
return args;
|
||||||
}
|
}
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CrashReports() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ public class StackTraceProvider implements ContextProvider {
|
|||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
for (int i = 4; i < stackTraceBuffer.length; i++) {
|
for (int i = 4; i < stackTraceBuffer.length; i++) {
|
||||||
sb.append(stackTraceBuffer[i].toString()).append("\n");
|
sb.append('\t').append(stackTraceBuffer[i].toString()).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
output.put("Reported from " + Thread.currentThread().getName(), sb.toString());
|
output.put("Reported from " + Thread.currentThread().getName(), sb.toString());
|
||||||
|
@ -53,7 +53,7 @@ public class PacketSendChunk extends PacketChunkChange {
|
|||||||
try {
|
try {
|
||||||
world.addChunk(ChunkIO.load(world, position, data.getReader(), IOContext.COMMS));
|
world.addChunk(ChunkIO.load(world, position, data.getReader(), IOContext.COMMS));
|
||||||
} catch (DecodingException | IOException e) {
|
} catch (DecodingException | IOException e) {
|
||||||
CrashReports.report(e, "Could not load chunk");
|
throw CrashReports.report(e, "Could not load chunk");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public class PacketEntityChange extends PacketWorldChange {
|
|||||||
try {
|
try {
|
||||||
entity.write(this.buffer.getWriter(), IOContext.COMMS);
|
entity.write(this.buffer.getWriter(), IOContext.COMMS);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Entity could not be written");
|
throw CrashReports.report(e, "Entity could not be written");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,13 +71,13 @@ public class PacketEntityChange extends PacketWorldChange {
|
|||||||
EntityData entity = world.getEntity(getEntityId());
|
EntityData entity = world.getEntity(getEntityId());
|
||||||
|
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
CrashReports.report(null, "Entity with ID %d not found", getEntityId());
|
throw CrashReports.report(null, "Entity with ID %d not found", getEntityId());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
entity.read(getReader(), IOContext.COMMS);
|
entity.read(getReader(), IOContext.COMMS);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Entity could not be read");
|
throw CrashReports.report(e, "Entity could not be read");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ public class PacketSendEntity extends PacketWorldChange {
|
|||||||
try {
|
try {
|
||||||
entity.write(this.buffer.getWriter(), IOContext.COMMS);
|
entity.write(this.buffer.getWriter(), IOContext.COMMS);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Could not write an entity into an internal buffer");
|
throw CrashReports.report(e, "Could not write an entity into an internal buffer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ public class PacketSendEntity extends PacketWorldChange {
|
|||||||
try {
|
try {
|
||||||
entity.read(this.buffer.getReader(), IOContext.COMMS);
|
entity.read(this.buffer.getReader(), IOContext.COMMS);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CrashReports.report(e, "Could not read an entity from an internal buffer");
|
throw CrashReports.report(e, "Could not read an entity from an internal buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
world.addEntity(entity);
|
world.addEntity(entity);
|
||||||
|
@ -44,8 +44,7 @@ public class PlayerManager {
|
|||||||
EntityData entity = spawnPlayerEntity(login);
|
EntityData entity = spawnPlayerEntity(login);
|
||||||
return entity;
|
return entity;
|
||||||
} else {
|
} else {
|
||||||
CrashReports.report(null, "Unknown login %s, javahorse stupid", login);
|
throw CrashReports.report(null, "Unknown login %s, javahorse stupid", login);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ public class ServerThread implements Runnable {
|
|||||||
server.tick();
|
server.tick();
|
||||||
ticker.runOneTick();
|
ticker.runOneTick();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
CrashReports.report(e, "Got a throwable in the server thread");
|
CrashReports.crash(e, "Got a throwable in the server thread");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ public class TickAndUpdateUtil {
|
|||||||
try {
|
try {
|
||||||
block.tick(context);
|
block.tick(context);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not tick block {}", block);
|
throw CrashReports.report(e, "Could not tick block {}", block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ public class TickAndUpdateUtil {
|
|||||||
try {
|
try {
|
||||||
tile.tick(context);
|
tile.tick(context);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not tick tile {}", tile);
|
throw CrashReports.report(e, "Could not tick tile {}", tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ public class TickAndUpdateUtil {
|
|||||||
try {
|
try {
|
||||||
block.update(context);
|
block.update(context);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not update block {}", block);
|
throw CrashReports.report(e, "Could not update block {}", block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ public class TickAndUpdateUtil {
|
|||||||
try {
|
try {
|
||||||
tile.update(context);
|
tile.update(context);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not update tile {}", tile);
|
throw CrashReports.report(e, "Could not update tile {}", tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ public class TickAndUpdateUtil {
|
|||||||
try {
|
try {
|
||||||
logic.tick(data, context);
|
logic.tick(data, context);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not tick entity {}", logic);
|
throw CrashReports.report(e, "Could not tick entity {}", logic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ class Ticker {
|
|||||||
try {
|
try {
|
||||||
task.run(srv);
|
task.run(srv);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
CrashReports.report(e, "Could not run %s task %s", task.getClass().getSimpleName(), task);
|
throw CrashReports.report(e, "Could not run %s task %s", task.getClass().getSimpleName(), task);
|
||||||
}
|
}
|
||||||
|
|
||||||
tasksCompleted++;
|
tasksCompleted++;
|
||||||
|
@ -260,8 +260,8 @@ public class TickerCoordinator {
|
|||||||
if (t instanceof ConcurrentModificationException) {
|
if (t instanceof ConcurrentModificationException) {
|
||||||
logger.debug("javahorse kill urself");
|
logger.debug("javahorse kill urself");
|
||||||
}
|
}
|
||||||
|
|
||||||
CrashReports.report(
|
throw CrashReports.crash(
|
||||||
t,
|
t,
|
||||||
"Something has gone horribly wrong in server ticker code "
|
"Something has gone horribly wrong in server ticker code "
|
||||||
+ "(thread %s) and it is (probably) not related to mods or devils.",
|
+ "(thread %s) and it is (probably) not related to mods or devils.",
|
||||||
|
@ -30,6 +30,8 @@ import ru.windcorp.progressia.client.graphics.gui.Label;
|
|||||||
import ru.windcorp.progressia.client.graphics.gui.Panel;
|
import ru.windcorp.progressia.client.graphics.gui.Panel;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||||
|
import ru.windcorp.progressia.common.Units;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
|
|
||||||
public class LayerTestGUI extends GUILayer {
|
public class LayerTestGUI extends GUILayer {
|
||||||
@ -63,13 +65,13 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
|
|
||||||
panel.addChild(new DynamicLabel(
|
panel.addChild(new DynamicLabel(
|
||||||
"FPSDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
|
"FPSDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
|
||||||
() -> String.format(Locale.US, "FPS: %5.1f", GraphicsInterface.getFPS()),
|
LayerTestGUI::getFPS,
|
||||||
128
|
128
|
||||||
));
|
));
|
||||||
|
|
||||||
panel.addChild(new DynamicLabel(
|
panel.addChild(new DynamicLabel(
|
||||||
"TPSDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
|
"TPSDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
|
||||||
() -> ServerState.getInstance() == null ? "TPS: n/a" : String.format(Locale.US, "TPS: %5.1f", ServerState.getInstance().getTPS()),
|
LayerTestGUI::getTPS,
|
||||||
128
|
128
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -89,6 +91,66 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
getRoot().addChild(panel);
|
getRoot().addChild(panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class Averager {
|
||||||
|
|
||||||
|
private static final int DISPLAY_INERTIA = 32;
|
||||||
|
private static final double UPDATE_INTERVAL = Units.get(50.0, "ms");
|
||||||
|
|
||||||
|
private final double[] values = new double[DISPLAY_INERTIA];
|
||||||
|
private int size;
|
||||||
|
private int head;
|
||||||
|
|
||||||
|
private long lastUpdate;
|
||||||
|
|
||||||
|
public void add(double value) {
|
||||||
|
if (size == values.length) {
|
||||||
|
values[head] = value;
|
||||||
|
head++;
|
||||||
|
if (head == values.length) head = 0;
|
||||||
|
} else {
|
||||||
|
values[size] = value;
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double average() {
|
||||||
|
double product = 1;
|
||||||
|
|
||||||
|
if (size == values.length) {
|
||||||
|
for (double d : values) product *= d;
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < size; ++i) product *= values[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.pow(product, 1.0 / size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double update(double value) {
|
||||||
|
long now = (long) (GraphicsInterface.getTime() / UPDATE_INTERVAL);
|
||||||
|
if (lastUpdate != now) {
|
||||||
|
lastUpdate = now;
|
||||||
|
add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return average();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Averager FPS_RECORD = new Averager();
|
||||||
|
private static final Averager TPS_RECORD = new Averager();
|
||||||
|
|
||||||
|
private static String getFPS() {
|
||||||
|
return String.format(Locale.US, "FPS: %5.1f", FPS_RECORD.update(GraphicsInterface.getFPS()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getTPS() {
|
||||||
|
Server server = ServerState.getInstance();
|
||||||
|
if (server == null) return "TPS: n/a";
|
||||||
|
|
||||||
|
return String.format(Locale.US, "TPS: %5.1f", TPS_RECORD.update(server.getTPS()));
|
||||||
|
}
|
||||||
|
|
||||||
// private static class DebugComponent extends Component {
|
// private static class DebugComponent extends Component {
|
||||||
// private final int color;
|
// private final int color;
|
||||||
//
|
//
|
||||||
|
Reference in New Issue
Block a user