From 00773d4f8b7ac706e733df601ee52d25c516e957 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Fri, 1 Jan 2021 20:25:47 +0300 Subject: [PATCH 1/4] Fixed compilation on JDK 8 --- build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 30bc9da..d70458b 100644 --- a/build.gradle +++ b/build.gradle @@ -139,7 +139,9 @@ java { } compileJava { - options.compilerArgs.addAll(['--release', '8']) + if (JavaVersion.current() != JavaVersion.VERSION_1_8) { + options.compilerArgs.addAll(['--release', '8']) + } } createPackages.dependsOn(build) From bedc9fc729130dc0def77c8b5d86689c3798e760 Mon Sep 17 00:00:00 2001 From: serega404 Date: Fri, 1 Jan 2021 22:32:07 +0300 Subject: [PATCH 2/4] Enhanced crash report interface - CrashReports.report now throws an exception instead of invoking System.exit() - Reduced chance of deadlocks - Improved code readability - added CrashReports.crash to handle exceptions thrown by report() in top-level catch blocks --- .../progressia/ProgressiaLauncher.java | 2 +- .../progressia/client/ClientProxy.java | 2 +- .../comms/DefaultClientCommsListener.java | 2 +- .../graphics/backend/shaders/Program.java | 2 +- .../graphics/backend/shaders/Shader.java | 2 +- .../backend/shaders/attributes/Attribute.java | 2 +- .../backend/shaders/uniforms/Uniform.java | 2 +- .../graphics/font/GNUUnifontLoader.java | 6 +-- .../client/graphics/gui/Component.java | 10 ++-- .../client/graphics/input/Keys.java | 4 +- .../client/graphics/texture/Atlases.java | 5 +- .../graphics/texture/SimpleTextures.java | 3 +- .../graphics/texture/TexturePrimitive.java | 2 +- .../client/localization/Localizer.java | 2 +- .../client/localization/Parser.java | 3 +- .../world/entity/EntityRenderRegistry.java | 3 +- .../ru/windcorp/progressia/common/Units.java | 2 +- .../progressia/common/io/ChunkIO.java | 5 +- .../progressia/common/resource/Resource.java | 6 +-- .../common/util/crash/CrashReports.java | 48 +++++++++++++++++-- .../common/world/PacketSendChunk.java | 2 +- .../world/entity/PacketEntityChange.java | 6 +-- .../progressia/server/PacketSendEntity.java | 4 +- .../progressia/server/PlayerManager.java | 3 +- .../progressia/server/ServerThread.java | 2 +- .../server/world/TickAndUpdateUtil.java | 10 ++-- .../server/world/ticking/Ticker.java | 2 +- .../world/ticking/TickerCoordinator.java | 4 +- 28 files changed, 87 insertions(+), 59 deletions(-) mode change 100755 => 100644 src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java diff --git a/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java b/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java index 330fa16..b313255 100644 --- a/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java +++ b/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java @@ -44,7 +44,7 @@ public class ProgressiaLauncher { CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer()); 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()); }); } diff --git a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java index 921fce5..6f59223 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java @@ -40,7 +40,7 @@ public class ClientProxy implements Proxy { RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init); RenderTaskQueue.waitAndInvoke(() -> Typefaces.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz")))); } catch (InterruptedException e) { - CrashReports.report(e, "ClientProxy failed"); + throw CrashReports.report(e, "ClientProxy failed"); } TestContent.registerContent(); diff --git a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java index 697a0ac..c44ff36 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java @@ -35,7 +35,7 @@ public class DefaultClientCommsListener implements CommsListener { @Override 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 } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java index 970fa10..2a62cd8 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java @@ -41,7 +41,7 @@ public class Program implements OpenGLDeletable { glLinkProgram(handle); 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)); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java index 394c32f..a34c30a 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java @@ -80,7 +80,7 @@ public class Shader implements OpenGLDeletable { if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) { System.out.println("***************** ERROR ******************"); System.out.println(source); - CrashReports.report(null, "Bad shader:\n %s", glGetShaderInfoLog(handle)); + throw CrashReports.report(null, "Bad shader:\n %s", glGetShaderInfoLog(handle)); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/attributes/Attribute.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/attributes/Attribute.java index 9075085..09060dd 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/attributes/Attribute.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/attributes/Attribute.java @@ -27,7 +27,7 @@ public class Attribute { public Attribute(int handle, Program program) { if (handle < 0) { - CrashReports.report(null, "Bad handle: %d", handle); + throw CrashReports.report(null, "Bad handle: %d", handle); } this.handle = handle; diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/uniforms/Uniform.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/uniforms/Uniform.java index 996b1fe..3a28983 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/uniforms/Uniform.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/uniforms/Uniform.java @@ -27,7 +27,7 @@ public class Uniform { public Uniform(int handle, Program program) { if (handle < 0) { - CrashReports.report(null, "Bad handle: %d", handle); + throw CrashReports.report(null, "Bad handle: %d", handle); } this.handle = handle; diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java b/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java index 72f359c..8cf4575 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java @@ -57,8 +57,7 @@ public class GNUUnifontLoader { return createStream(reader).map(GNUUnifontLoader::parse).map(GNUUnifontLoader::addToAtlas) .collect(Collectors.collectingAndThen(createMapper(), GNUUnifont::new)); } catch (IOException | UncheckedIOException e) { - CrashReports.report(e, "Could not load GNUUnifont"); - return null; + throw CrashReports.report(e, "Could not load GNUUnifont"); } } @@ -93,8 +92,7 @@ public class GNUUnifontLoader { return new ParsedGlyph(c, editor); } catch (IOException e) { - CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar); - return null; + throw CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java old mode 100755 new mode 100644 index 1a1b1f4..7424bd9 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java @@ -235,7 +235,7 @@ public class Component extends Named { valid = true; } 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 { return getLayout().calculatePreferredSize(this); } 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; } } 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 { assembleSelf(target); } catch (Exception e) { - CrashReports.report(e, "Could not assemble Component %s", this); + throw CrashReports.report(e, "Could not assemble Component %s", this); } assembleChildren(target); @@ -617,7 +617,7 @@ public class Component extends Named { try { postAssembleSelf(target); } catch (Exception e) { - CrashReports.report(e, "Post-assembly failed for Component %s", this); + throw CrashReports.report(e, "Post-assembly failed for Component %s", this); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/Keys.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/Keys.java index f97c4b0..f8e2dd9 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/input/Keys.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/input/Keys.java @@ -77,7 +77,7 @@ public class Keys { } } 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)) { - 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)); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java index 0ce64d4..489d922 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java @@ -157,8 +157,7 @@ public class Atlases { return loadSprite(data.getData(), group); } catch (IOException e) { - CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group); - return null; + throw CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group); } } @@ -174,7 +173,7 @@ public class Atlases { Atlas newAtlas = new Atlas(group); 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); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java index 51a1235..4b5ea61 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java @@ -31,8 +31,7 @@ public class SimpleTextures { new Sprite(new TexturePrimitive(data.getData())) ); } catch (IOException e) { - CrashReports.report(e, "Could not load texture %s", resource); - return null; + throw CrashReports.report(e, "Could not load texture %s", resource); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java index 71afc2d..be05ac7 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java @@ -94,7 +94,7 @@ public class TexturePrimitive implements OpenGLDeletable { OpenGLObjectTracker.register(this, GL11::glDeleteTextures); if (handle < 0) { - CrashReports.report(null, "Failed to allocate texture"); + throw CrashReports.report(null, "Failed to allocate texture"); } } diff --git a/src/main/java/ru/windcorp/progressia/client/localization/Localizer.java b/src/main/java/ru/windcorp/progressia/client/localization/Localizer.java index 16f7b05..f780065 100644 --- a/src/main/java/ru/windcorp/progressia/client/localization/Localizer.java +++ b/src/main/java/ru/windcorp/progressia/client/localization/Localizer.java @@ -43,7 +43,7 @@ public class Localizer { data = new Parser(langFolder + this.language + ".lang").parse(); pokeListeners(language); } else { - CrashReports.report(null, "Language not found: %s", language); + throw CrashReports.report(null, "Language not found: %s", language); } } diff --git a/src/main/java/ru/windcorp/progressia/client/localization/Parser.java b/src/main/java/ru/windcorp/progressia/client/localization/Parser.java index 239c779..95fa83c 100644 --- a/src/main/java/ru/windcorp/progressia/client/localization/Parser.java +++ b/src/main/java/ru/windcorp/progressia/client/localization/Parser.java @@ -81,8 +81,7 @@ public class Parser { } } catch (IOException | EscapeException e) { - CrashReports.report(e, "Could not parse language file %s", filePath); - return null; + throw CrashReports.report(e, "Could not parse language file %s", filePath); } return parsedData; } diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java index 4cb1966..df67f94 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java @@ -29,8 +29,7 @@ public class EntityRenderRegistry extends NamespacedInstanceRegistry ANALYZERS = Collections.synchronizedCollection(new ArrayList<>()); private static final Logger LOGGER = LogManager.getLogger("crash"); + + public static ReportedException report(Throwable throwable, String messageFormat, Object... args) { + if (throwable instanceof ReportedException) return (ReportedException) throwable; + + return new ReportedException(throwable, messageFormat, args); + } + + public static RuntimeException crash(Throwable throwable, String messageFormat, Object... args) { + if (throwable instanceof ReportedException) { + throw crash((ReportedException) throwable); + } else { + throw crash(report(throwable, messageFormat, args)); + } + } /** * This method never returns. *

* TODO document * - * @param throwable - * @param messageFormat - * @param args + * @param reportException */ - public static void report(Throwable throwable, String messageFormat, Object... args) { + public static RuntimeException crash(ReportedException reportException) { + Throwable throwable = reportException.getCause(); + String messageFormat = reportException.getMessageFormat(); + Object[] args = reportException.getArgs(); + StringBuilder output = new StringBuilder(); try { @@ -76,6 +92,7 @@ public class CrashReports { export(output.toString()); System.exit(0); + return reportException; } private static void appendContextProviders(StringBuilder output) { @@ -233,6 +250,27 @@ public class CrashReports { private static void addSeparator(StringBuilder sb) { sb.append(StringUtil.sequence('-', 80)).append("\n"); } + + public static class ReportedException extends RuntimeException { + + private String messageFormat; + private Object[] args; + + public ReportedException(Throwable throwable, String messageFormat, Object... args){ + super(throwable); + this.messageFormat = messageFormat; + this.args = args; + } + + public String getMessageFormat() { + return messageFormat; + } + + public Object[] getArgs() { + return args; + } + } + } class StringUtilsTemp { @@ -254,4 +292,4 @@ class StringUtilsTemp { } return sb.toString(); } -} +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java b/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java index a0fe418..25897d4 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java @@ -52,7 +52,7 @@ public class PacketSendChunk extends PacketChunkChange { try { world.addChunk(ChunkIO.load(world, position, data.getInputStream())); } catch (DecodingException | IOException e) { - CrashReports.report(e, "Could not load chunk"); + throw CrashReports.report(e, "Could not load chunk"); } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java index bb07297..aea1d06 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java @@ -49,7 +49,7 @@ public class PacketEntityChange extends PacketWorldChange { try { entity.write(this.buffer.getWriter(), IOContext.COMMS); } 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()); 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 { entity.read(getReader(), IOContext.COMMS); } catch (IOException e) { - CrashReports.report(e, "Entity could not be read"); + throw CrashReports.report(e, "Entity could not be read"); } } diff --git a/src/main/java/ru/windcorp/progressia/server/PacketSendEntity.java b/src/main/java/ru/windcorp/progressia/server/PacketSendEntity.java index d1da490..c0abca7 100644 --- a/src/main/java/ru/windcorp/progressia/server/PacketSendEntity.java +++ b/src/main/java/ru/windcorp/progressia/server/PacketSendEntity.java @@ -34,7 +34,7 @@ public class PacketSendEntity extends PacketWorldChange { try { entity.write(this.buffer.getWriter(), IOContext.COMMS); } 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 { entity.read(this.buffer.getReader(), IOContext.COMMS); } 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); diff --git a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java index 4a4bb65..7b2e69b 100644 --- a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java +++ b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java @@ -44,8 +44,7 @@ public class PlayerManager { EntityData entity = spawnPlayerEntity(login); return entity; } else { - CrashReports.report(null, "Unknown login %s, javahorse stupid", login); - return null; + throw CrashReports.report(null, "Unknown login %s, javahorse stupid", login); } } diff --git a/src/main/java/ru/windcorp/progressia/server/ServerThread.java b/src/main/java/ru/windcorp/progressia/server/ServerThread.java index ca9498b..d677f69 100644 --- a/src/main/java/ru/windcorp/progressia/server/ServerThread.java +++ b/src/main/java/ru/windcorp/progressia/server/ServerThread.java @@ -57,7 +57,7 @@ public class ServerThread implements Runnable { server.tick(); ticker.runOneTick(); } catch (Exception e) { - CrashReports.report(e, "Got an exception in the server thread"); + throw CrashReports.crash(e, "Got an exception in the server thread"); } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java b/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java index a38ccd7..411c3ab 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java +++ b/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java @@ -22,7 +22,7 @@ public class TickAndUpdateUtil { try { block.tick(context); } 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 { tile.tick(context); } 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 { block.update(context); } 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 { tile.update(context); } 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 { logic.tick(data, context); } catch (Exception e) { - CrashReports.report(e, "Could not tick entity {}", logic); + throw CrashReports.report(e, "Could not tick entity {}", logic); } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/ticking/Ticker.java b/src/main/java/ru/windcorp/progressia/server/world/ticking/Ticker.java index 32502ab..366dead 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ticking/Ticker.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ticking/Ticker.java @@ -147,7 +147,7 @@ class Ticker { try { task.run(srv); } 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++; diff --git a/src/main/java/ru/windcorp/progressia/server/world/ticking/TickerCoordinator.java b/src/main/java/ru/windcorp/progressia/server/world/ticking/TickerCoordinator.java index e47e878..84d3a2a 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ticking/TickerCoordinator.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ticking/TickerCoordinator.java @@ -260,8 +260,8 @@ public class TickerCoordinator { if (t instanceof ConcurrentModificationException) { logger.debug("javahorse kill urself"); } - - CrashReports.report( + + throw CrashReports.crash( t, "Something has gone horribly wrong in server ticker code " + "(thread %s) and it is (probably) not related to mods or devils.", From 01645f5c6e322044b3d57d70c26ce796f8829a5f Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sat, 2 Jan 2021 00:08:32 +0300 Subject: [PATCH 3/4] Documented and prettified CrashReports --- .../ru/windcorp/jputil/chars/StringUtil.java | 28 +++ .../common/util/crash/CrashReports.java | 168 ++++++++++++------ .../crash/providers/StackTraceProvider.java | 2 +- 3 files changed, 141 insertions(+), 57 deletions(-) diff --git a/src/main/java/ru/windcorp/jputil/chars/StringUtil.java b/src/main/java/ru/windcorp/jputil/chars/StringUtil.java index de5c37b..0aaf7a6 100644 --- a/src/main/java/ru/windcorp/jputil/chars/StringUtil.java +++ b/src/main/java/ru/windcorp/jputil/chars/StringUtil.java @@ -690,6 +690,34 @@ public class StringUtil { 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) { int i = 0; boolean isWord = false; diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java index d11acd5..9aa2e32 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java @@ -18,11 +18,14 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; 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 { - private CrashReports() { - } - private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports"); private static final Collection PROVIDERS = Collections.synchronizedCollection(new ArrayList<>()); @@ -31,31 +34,68 @@ public class CrashReports { 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: + * + *

+     * try {
+     *     doSomethingDifficult(x);
+     * } catch (CouldntMakeItException e) {
+     *     throw CrashReports.report(e, "We couldn't make it at x = %d", x);
+     * }
+     * 
+ * + *

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. + * + *

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; + if (throwable instanceof ReportedException) return (ReportedException) throwable; return new ReportedException(throwable, messageFormat, args); } - public static RuntimeException crash(Throwable throwable, String messageFormat, Object... args) { - if (throwable instanceof ReportedException) { - throw crash((ReportedException) throwable); - } else { - throw crash(report(throwable, messageFormat, args)); - } - } - /** - * This method never returns. - *

- * TODO document - * - * @param reportException + * Crashes the program due to the supplied problem. + * + *

Use {@link #report(Throwable, String, Object...)} unless you are creating a catch-all handler for a + * thread. + * + *

This method recovers information about the problem by casting {@code throwable} to {@link ReportedException}, + * 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)}. + * + *

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 RuntimeException crash(ReportedException reportException) { - Throwable throwable = reportException.getCause(); - String messageFormat = reportException.getMessageFormat(); - Object[] args = reportException.getArgs(); + 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(); @@ -92,7 +132,7 @@ public class CrashReports { export(output.toString()); System.exit(0); - return reportException; + return null; } private static void appendContextProviders(StringBuilder output) { @@ -111,7 +151,7 @@ public class CrashReports { provider.provideContext(buf); if (!buf.isEmpty()) { - output.append(StringUtilsTemp.center(provider.getName(), 80)).append("\n"); + output.append(StringUtil.center(provider.getName(), 80)).append("\n"); for (Map.Entry entry : buf.entrySet()) { output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } @@ -139,7 +179,7 @@ public class CrashReports { Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]); if (localAnalyzersCopy.length > 0) { - output.append(StringUtilsTemp.center("Analyzers", 80)).append("\n"); + output.append(StringUtil.center("Analyzers", 80)).append("\n"); } for (Analyzer analyzer : localAnalyzersCopy) { @@ -239,57 +279,73 @@ public class CrashReports { } } - public static void registerProvider(ContextProvider provider) { - PROVIDERS.add(provider); - } - - public static void registerAnalyzer(Analyzer analyzer) { - ANALYZERS.add(analyzer); - } - 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) { + 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) { + ANALYZERS.add(analyzer); + } + + /** + * 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 { - private String messageFormat; - private Object[] args; + private static final long serialVersionUID = 223720835231091533L; + + private final String messageFormat; + private final Object[] args; - public ReportedException(Throwable throwable, String messageFormat, Object... args){ + /** + * Constructs a {@link ReportedException}. + * @param throwable the reported {@link Throwable} or {@code null} + * @param messageFormat the reported message format. + * This is not the message of the constructed Exception. + * @param args the reported message format arguments + */ + public ReportedException(Throwable throwable, String messageFormat, Object... args) { super(throwable); this.messageFormat = messageFormat; this.args = args; } + /** + * 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; } } + private CrashReports() { + + } + } - -class StringUtilsTemp { - public static String center(String s, int size) { - return center(s, size, ' '); - } - - public static String center(String s, int size, char pad) { - if (s == null || size <= s.length()) - return s; - - StringBuilder sb = new StringBuilder(size); - for (int i = 0; i < (size - s.length()) / 2; i++) { - sb.append(pad); - } - sb.append(s); - while (sb.length() < size) { - sb.append(pad); - } - return sb.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/StackTraceProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/StackTraceProvider.java index 0469dca..083ac13 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/StackTraceProvider.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/StackTraceProvider.java @@ -11,7 +11,7 @@ public class StackTraceProvider implements ContextProvider { StringBuilder sb = new StringBuilder(); sb.append("\n"); 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()); From 65820040ce997a960f44f5167284f336f2bcc7e6 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sat, 2 Jan 2021 00:37:12 +0300 Subject: [PATCH 4/4] Added FPS and TPS display averaging --- .../progressia/test/LayerTestGUI.java | 66 ++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java b/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java index 0395f35..1a513bd 100755 --- a/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java +++ b/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java @@ -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.layout.LayoutAlign; 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; public class LayerTestGUI extends GUILayer { @@ -63,13 +65,13 @@ public class LayerTestGUI extends GUILayer { panel.addChild(new DynamicLabel( "FPSDisplay", new Font().withColor(0x37A3E6).deriveShadow(), - () -> String.format(Locale.US, "FPS: %5.1f", GraphicsInterface.getFPS()), + LayerTestGUI::getFPS, 128 )); panel.addChild(new DynamicLabel( "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 )); @@ -89,6 +91,66 @@ public class LayerTestGUI extends GUILayer { 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 final int color; //