Merge branch 'crashreports'
This commit is contained in:
commit
a7eb90865f
@ -17,10 +17,23 @@
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.progressia;
|
||||
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
|
||||
import ru.windcorp.progressia.common.util.crash.providers.OSContextProvider;
|
||||
|
||||
public class ProgressiaLauncher {
|
||||
|
||||
public static void launch(String[] args, Proxy proxy) {
|
||||
setupCrashReports();
|
||||
proxy.initialize();
|
||||
}
|
||||
|
||||
private static void setupCrashReports() {
|
||||
CrashReports.registerProvider(new OSContextProvider());
|
||||
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
||||
Thread.setDefaultUncaughtExceptionHandler((Thread thread, Throwable t)-> {
|
||||
CrashReports.report(t,"Uncaught exception in thread %s", thread.getName());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import ru.windcorp.progressia.client.graphics.font.Typefaces;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Atlases;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.server.ServerState;
|
||||
import ru.windcorp.progressia.test.TestContent;
|
||||
|
||||
@ -39,8 +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) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
CrashReports.report(e, "ClientProxy failed");
|
||||
}
|
||||
|
||||
TestContent.registerContent();
|
||||
|
@ -1,6 +1,8 @@
|
||||
package ru.windcorp.progressia.client.comms;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ru.windcorp.jputil.chars.StringUtil;
|
||||
import ru.windcorp.progressia.client.Client;
|
||||
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
||||
@ -8,6 +10,7 @@ import ru.windcorp.progressia.common.comms.CommsListener;
|
||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer;
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.entity.PacketEntityChange;
|
||||
|
||||
@ -41,7 +44,11 @@ public class DefaultClientCommsListener implements CommsListener {
|
||||
);
|
||||
|
||||
if (entity == null) {
|
||||
throw new RuntimeException("");
|
||||
CrashReports.report(
|
||||
null,
|
||||
"Player entity with ID %s not found",
|
||||
new String(StringUtil.toFullHex(packet.getLocalPlayerEntityId()))
|
||||
);
|
||||
}
|
||||
|
||||
getClient().setLocalPlayer(entity);
|
||||
|
@ -24,6 +24,7 @@ import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker;
|
||||
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
||||
import ru.windcorp.progressia.client.graphics.backend.shaders.attributes.Attribute;
|
||||
import ru.windcorp.progressia.client.graphics.backend.shaders.uniforms.Uniform;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Program implements OpenGLDeletable {
|
||||
|
||||
@ -39,7 +40,7 @@ public class Program implements OpenGLDeletable {
|
||||
glLinkProgram(handle);
|
||||
|
||||
if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) {
|
||||
throw new RuntimeException("Bad program:\n" + glGetProgramInfoLog(handle));
|
||||
CrashReports.report(null, "Bad program:\n%s", glGetProgramInfoLog(handle));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,12 @@ import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker;
|
||||
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
||||
import ru.windcorp.progressia.common.resource.Resource;
|
||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Shader implements OpenGLDeletable {
|
||||
|
||||
public static enum ShaderType {
|
||||
VERTEX(GL_VERTEX_SHADER),
|
||||
FRAGMENT(GL_FRAGMENT_SHADER);
|
||||
VERTEX(GL_VERTEX_SHADER), FRAGMENT(GL_FRAGMENT_SHADER);
|
||||
|
||||
private final int glCode;
|
||||
|
||||
@ -79,7 +79,7 @@ public class Shader implements OpenGLDeletable {
|
||||
if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) {
|
||||
System.out.println("***************** ERROR ******************");
|
||||
System.out.println(source);
|
||||
throw new RuntimeException("Bad shader:\n" + glGetShaderInfoLog(handle));
|
||||
CrashReports.report(null, "Bad shader:\n %s", glGetShaderInfoLog(handle));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
package ru.windcorp.progressia.client.graphics.backend.shaders.attributes;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.backend.shaders.Program;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Attribute {
|
||||
|
||||
@ -26,7 +27,7 @@ public class Attribute {
|
||||
|
||||
public Attribute(int handle, Program program) {
|
||||
if (handle < 0) {
|
||||
throw new RuntimeException("Bad handle: " + handle);
|
||||
CrashReports.report(null, "Bad handle: %d", handle);
|
||||
}
|
||||
|
||||
this.handle = handle;
|
||||
|
@ -18,6 +18,7 @@
|
||||
package ru.windcorp.progressia.client.graphics.backend.shaders.uniforms;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.backend.shaders.Program;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Uniform {
|
||||
|
||||
@ -26,7 +27,7 @@ public class Uniform {
|
||||
|
||||
public Uniform(int handle, Program program) {
|
||||
if (handle < 0) {
|
||||
throw new RuntimeException("Bad handle: " + handle);
|
||||
CrashReports.report(null, "Bad handle: %d", handle);
|
||||
}
|
||||
|
||||
this.handle = handle;
|
||||
|
@ -21,14 +21,13 @@ import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
import ru.windcorp.progressia.client.graphics.texture.TextureDataEditor;
|
||||
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
||||
import ru.windcorp.progressia.common.resource.Resource;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class GNUUnifontLoader {
|
||||
|
||||
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT =
|
||||
new AtlasGroup("GNUUnifont", 1 << 12);
|
||||
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT = new AtlasGroup("GNUUnifont", 1 << 12);
|
||||
|
||||
private static final TextureSettings TEXTURE_SETTINGS =
|
||||
new TextureSettings(false);
|
||||
private static final TextureSettings TEXTURE_SETTINGS = new TextureSettings(false);
|
||||
|
||||
private static final int BITS_PER_HEX_DIGIT = 4;
|
||||
private static final int PREFIX_LENGTH = "0000:".length();
|
||||
@ -55,29 +54,17 @@ public class GNUUnifontLoader {
|
||||
|
||||
public static GNUUnifont load(Resource resource) {
|
||||
try (BufferedReader reader = createReader(resource)) {
|
||||
return createStream(reader)
|
||||
.map(GNUUnifontLoader::parse)
|
||||
.map(GNUUnifontLoader::addToAtlas)
|
||||
.collect(Collectors.collectingAndThen(
|
||||
createMapper(),
|
||||
GNUUnifont::new
|
||||
));
|
||||
return createStream(reader).map(GNUUnifontLoader::parse).map(GNUUnifontLoader::addToAtlas)
|
||||
.collect(Collectors.collectingAndThen(createMapper(), GNUUnifont::new));
|
||||
} catch (IOException | UncheckedIOException e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not load GNUUnifont");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BufferedReader createReader(Resource resource)
|
||||
throws IOException
|
||||
{
|
||||
private static BufferedReader createReader(Resource resource) throws IOException {
|
||||
return new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new GZIPInputStream(
|
||||
resource.getInputStream()
|
||||
),
|
||||
StandardCharsets.UTF_8
|
||||
)
|
||||
);
|
||||
new InputStreamReader(new GZIPInputStream(resource.getInputStream()), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private static Stream<String> createStream(BufferedReader reader) {
|
||||
@ -85,38 +72,37 @@ public class GNUUnifontLoader {
|
||||
}
|
||||
|
||||
private static ParsedGlyph parse(String declar) {
|
||||
try {
|
||||
|
||||
int width = getWidth(declar);
|
||||
checkDeclaration(declar, width);
|
||||
|
||||
char c = getChar(declar);
|
||||
|
||||
TextureDataEditor editor = new TextureDataEditor(
|
||||
width, GNUUnifont.HEIGHT,
|
||||
width, GNUUnifont.HEIGHT,
|
||||
TEXTURE_SETTINGS
|
||||
);
|
||||
TextureDataEditor editor = new TextureDataEditor(width, GNUUnifont.HEIGHT, width, GNUUnifont.HEIGHT,
|
||||
TEXTURE_SETTINGS);
|
||||
|
||||
for (int y = 0; y < GNUUnifont.HEIGHT; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
int bit = x + y * width;
|
||||
|
||||
editor.setPixel(
|
||||
x, GNUUnifont.HEIGHT - y - 1,
|
||||
getBit(declar, bit) ? 0xFFFFFFFF : 0x00000000
|
||||
);
|
||||
editor.setPixel(x, GNUUnifont.HEIGHT - y - 1, getBit(declar, bit) ? 0xFFFFFFFF : 0x00000000);
|
||||
}
|
||||
}
|
||||
|
||||
return new ParsedGlyph(c, editor);
|
||||
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static char getChar(String declar) {
|
||||
int result = 0;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
result =
|
||||
(result << BITS_PER_HEX_DIGIT) |
|
||||
getHexValue(declar.charAt(i));
|
||||
result = (result << BITS_PER_HEX_DIGIT) | getHexValue(declar.charAt(i));
|
||||
}
|
||||
|
||||
return (char) result;
|
||||
@ -138,35 +124,39 @@ public class GNUUnifontLoader {
|
||||
return meaningfulChars / charsPerColumn;
|
||||
}
|
||||
|
||||
private static void checkDeclaration(String declar, int width) {
|
||||
private static void checkDeclaration(String declar, int width) throws IOException {
|
||||
if (!GNUUnifont.WIDTHS.contains(width)) {
|
||||
throw new RuntimeException("Width " + width + " is not supported (in declar \"" + declar + "\")");
|
||||
throw new IOException("Width " + width + " is not supported (in declar \"" + declar + "\")");
|
||||
}
|
||||
|
||||
if ((declar.length() - PREFIX_LENGTH) % width != 0) {
|
||||
throw new RuntimeException("Declar \"" + declar + "\" has invalid length");
|
||||
throw new IOException("Declar \"" + declar + "\" has invalid length");
|
||||
}
|
||||
|
||||
for (int i = 0; i < declar.length(); ++i) {
|
||||
if (i == BITS_PER_HEX_DIGIT) {
|
||||
if (declar.charAt(i) != ':') {
|
||||
throw new RuntimeException("No colon ':' found in declar \"" + declar + "\" at index 4");
|
||||
throw new IOException("No colon ':' found in declar \"" + declar + "\" at index 4");
|
||||
}
|
||||
} else {
|
||||
char c = declar.charAt(i);
|
||||
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) {
|
||||
throw new RuntimeException("Illegal char in declar \"" + declar + "\" at index " + i + "; expected 0-9A-F");
|
||||
throw new IOException("Illegal char in declar \"" + declar + "\" at index " + i + "; expected 0-9A-F");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getHexValue(char digit) {
|
||||
if (digit < '0') throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||
if (digit <= '9') return digit - '0';
|
||||
if (digit < 'A') throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||
if (digit <= 'F') return digit - 'A' + 0xA;
|
||||
if (digit < '0')
|
||||
throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||
if (digit <= '9')
|
||||
return digit - '0';
|
||||
if (digit < 'A')
|
||||
throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||
if (digit <= 'F')
|
||||
return digit - 'A' + 0xA;
|
||||
throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||
}
|
||||
|
||||
@ -175,11 +165,8 @@ public class GNUUnifontLoader {
|
||||
return new AtlasGlyph(glyph.c, new SimpleTexture(sprite));
|
||||
}
|
||||
|
||||
private static
|
||||
Collector<AtlasGlyph, ?, TCharObjectMap<Texture>>
|
||||
createMapper() {
|
||||
return Collector.of(
|
||||
TCharObjectHashMap<Texture>::new,
|
||||
private static Collector<AtlasGlyph, ?, TCharObjectMap<Texture>> createMapper() {
|
||||
return Collector.of(TCharObjectHashMap<Texture>::new,
|
||||
|
||||
(map, glyph) -> map.put(glyph.c, glyph.texture),
|
||||
|
||||
@ -188,10 +175,10 @@ public class GNUUnifontLoader {
|
||||
return a;
|
||||
},
|
||||
|
||||
Characteristics.UNORDERED
|
||||
);
|
||||
Characteristics.UNORDERED);
|
||||
}
|
||||
|
||||
private GNUUnifontLoader() {}
|
||||
private GNUUnifontLoader() {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,11 +39,11 @@ import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
||||
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
|
||||
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
|
||||
import ru.windcorp.progressia.common.util.Named;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Component extends Named {
|
||||
|
||||
private final List<Component> children =
|
||||
Collections.synchronizedList(new CopyOnWriteArrayList<>());
|
||||
private final List<Component> children = Collections.synchronizedList(new CopyOnWriteArrayList<>());
|
||||
|
||||
private Component parent = null;
|
||||
|
||||
@ -88,7 +88,8 @@ public class Component extends Named {
|
||||
|
||||
public Component getChild(int index) {
|
||||
synchronized (getChildren()) {
|
||||
if (index < 0 || index >= getChildren().size()) return null;
|
||||
if (index < 0 || index >= getChildren().size())
|
||||
return null;
|
||||
return getChildren().get(index);
|
||||
}
|
||||
}
|
||||
@ -107,7 +108,8 @@ public class Component extends Named {
|
||||
}
|
||||
|
||||
public void moveChild(Component child, int newIndex) {
|
||||
if (newIndex == -1) newIndex = getChildren().size() - 1;
|
||||
if (newIndex == -1)
|
||||
newIndex = getChildren().size() - 1;
|
||||
|
||||
if (getChildren().remove(child)) {
|
||||
getChildren().add(newIndex, child);
|
||||
@ -123,7 +125,8 @@ public class Component extends Named {
|
||||
}
|
||||
|
||||
public Component addChild(Component child, int index) {
|
||||
if (index == -1) index = getChildren().size();
|
||||
if (index == -1)
|
||||
index = getChildren().size();
|
||||
|
||||
invalidate();
|
||||
getChildren().add(index, child);
|
||||
@ -232,7 +235,7 @@ public class Component extends Named {
|
||||
|
||||
valid = true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not layout Component %s", this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,7 +248,7 @@ public class Component extends Named {
|
||||
try {
|
||||
return getLayout().calculatePreferredSize(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not calculate preferred size for Component %s", this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,7 +345,8 @@ public class Component extends Named {
|
||||
}
|
||||
|
||||
private Component getNextFocusCandidate(boolean canUseChildren) {
|
||||
if (canUseChildren) synchronized (getChildren()) {
|
||||
if (canUseChildren)
|
||||
synchronized (getChildren()) {
|
||||
if (!getChildren().isEmpty()) {
|
||||
return getChild(0);
|
||||
}
|
||||
@ -458,20 +462,19 @@ public class Component extends Named {
|
||||
}
|
||||
|
||||
public void removeListener(Object listener) {
|
||||
if (eventBus == null) return;
|
||||
if (eventBus == null)
|
||||
return;
|
||||
eventBus.unregister(listener);
|
||||
}
|
||||
|
||||
public void dispatchEvent(Object event) {
|
||||
if (eventBus == null) return;
|
||||
if (eventBus == null)
|
||||
return;
|
||||
eventBus.post(event);
|
||||
}
|
||||
|
||||
public <T extends InputEvent> void addListener(
|
||||
Class<? extends T> type,
|
||||
boolean handlesConsumed,
|
||||
InputListener<T> listener
|
||||
) {
|
||||
public <T extends InputEvent> void addListener(Class<? extends T> type, boolean handlesConsumed,
|
||||
InputListener<T> listener) {
|
||||
if (inputBus == null) {
|
||||
inputBus = new InputBus();
|
||||
}
|
||||
@ -479,10 +482,7 @@ public class Component extends Named {
|
||||
inputBus.register(type, handlesConsumed, listener);
|
||||
}
|
||||
|
||||
public <T extends InputEvent> void addListener(
|
||||
Class<? extends T> type,
|
||||
InputListener<T> listener
|
||||
) {
|
||||
public <T extends InputEvent> void addListener(Class<? extends T> type, InputListener<T> listener) {
|
||||
if (inputBus == null) {
|
||||
inputBus = new InputBus();
|
||||
}
|
||||
@ -517,15 +517,17 @@ public class Component extends Named {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not dispatch input to Component %s", this);
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchInputToFocused(Input input) {
|
||||
Component c = findFocused();
|
||||
|
||||
if (c == null) return;
|
||||
if (attemptFocusTransfer(input, c)) return;
|
||||
if (c == null)
|
||||
return;
|
||||
if (attemptFocusTransfer(input, c))
|
||||
return;
|
||||
|
||||
while (c != null) {
|
||||
c.handleInput(input);
|
||||
@ -555,8 +557,10 @@ public class Component extends Named {
|
||||
}
|
||||
|
||||
private boolean attemptFocusTransfer(Input input, Component focused) {
|
||||
if (input.isConsumed()) return false;
|
||||
if (!(input.getEvent() instanceof KeyEvent)) return false;
|
||||
if (input.isConsumed())
|
||||
return false;
|
||||
if (!(input.getEvent() instanceof KeyEvent))
|
||||
return false;
|
||||
|
||||
KeyEvent keyInput = (KeyEvent) input.getEvent();
|
||||
|
||||
@ -574,16 +578,11 @@ public class Component extends Named {
|
||||
}
|
||||
|
||||
public synchronized boolean contains(int x, int y) {
|
||||
return
|
||||
x >= getX() && x < getX() + getWidth() &&
|
||||
y >= getY() && y < getY() + getHeight();
|
||||
return x >= getX() && x < getX() + getWidth() && y >= getY() && y < getY() + getHeight();
|
||||
}
|
||||
|
||||
public boolean containsCursor() {
|
||||
return contains(
|
||||
(int) InputTracker.getCursorX(),
|
||||
(int) InputTracker.getCursorY()
|
||||
);
|
||||
return contains((int) InputTracker.getCursorX(), (int) InputTracker.getCursorY());
|
||||
}
|
||||
|
||||
public void requestReassembly() {
|
||||
@ -610,7 +609,7 @@ public class Component extends Named {
|
||||
try {
|
||||
assembleSelf(target);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not assemble Component %s", this);
|
||||
}
|
||||
|
||||
assembleChildren(target);
|
||||
@ -618,7 +617,7 @@ public class Component extends Named {
|
||||
try {
|
||||
postAssembleSelf(target);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Post-assembly failed for Component %s", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,26 +31,23 @@ import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import gnu.trove.map.hash.TObjectIntHashMap;
|
||||
import gnu.trove.set.TIntSet;
|
||||
import gnu.trove.set.hash.TIntHashSet;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Keys {
|
||||
|
||||
private static final TIntObjectMap<String> CODES_TO_NAMES =
|
||||
new TIntObjectHashMap<>();
|
||||
private static final TIntObjectMap<String> CODES_TO_NAMES = new TIntObjectHashMap<>();
|
||||
|
||||
private static final TObjectIntMap<String> NAMES_TO_CODES =
|
||||
new TObjectIntHashMap<>();
|
||||
private static final TObjectIntMap<String> NAMES_TO_CODES = new TObjectIntHashMap<>();
|
||||
|
||||
private static final TIntSet MOUSE_BUTTONS = new TIntHashSet();
|
||||
|
||||
private static final String KEY_PREFIX = "GLFW_KEY_";
|
||||
private static final String MOUSE_BUTTON_PREFIX = "GLFW_MOUSE_BUTTON_";
|
||||
|
||||
private static final Set<String> IGNORE_FIELDS =
|
||||
new HashSet<>(Arrays.asList(
|
||||
"GLFW_KEY_UNKNOWN",
|
||||
"GLFW_KEY_LAST",
|
||||
"GLFW_MOUSE_BUTTON_LAST",
|
||||
"GLFW_MOUSE_BUTTON_1", // Alias for LEFT
|
||||
private static final Set<String> IGNORE_FIELDS = new HashSet<>(
|
||||
Arrays.asList("GLFW_KEY_UNKNOWN", "GLFW_KEY_LAST", "GLFW_MOUSE_BUTTON_LAST", "GLFW_MOUSE_BUTTON_1", // Alias
|
||||
// for
|
||||
// LEFT
|
||||
"GLFW_MOUSE_BUTTON_2", // Alias for RIGHT
|
||||
"GLFW_MOUSE_BUTTON_3" // Alias for MIDDLE
|
||||
));
|
||||
@ -63,28 +60,28 @@ public class Keys {
|
||||
try {
|
||||
|
||||
for (Field field : GLFW.class.getFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) continue;
|
||||
if (!Modifier.isFinal(field.getModifiers())) continue;
|
||||
if (!Modifier.isStatic(field.getModifiers()))
|
||||
continue;
|
||||
if (!Modifier.isFinal(field.getModifiers()))
|
||||
continue;
|
||||
|
||||
String name = field.getName();
|
||||
|
||||
if (
|
||||
!name.startsWith(KEY_PREFIX) &&
|
||||
!name.startsWith(MOUSE_BUTTON_PREFIX)
|
||||
) continue;
|
||||
if (!name.startsWith(KEY_PREFIX) && !name.startsWith(MOUSE_BUTTON_PREFIX))
|
||||
continue;
|
||||
|
||||
if (IGNORE_FIELDS.contains(name)) continue;
|
||||
if (IGNORE_FIELDS.contains(name))
|
||||
continue;
|
||||
|
||||
addToDictionary(field);
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Cannot access GLFW constants");
|
||||
}
|
||||
}
|
||||
|
||||
private static void addToDictionary(Field field)
|
||||
throws IllegalAccessException {
|
||||
private static void addToDictionary(Field field) throws IllegalAccessException {
|
||||
|
||||
int value = field.getInt(null);
|
||||
String name = field.getName();
|
||||
@ -97,11 +94,8 @@ public class Keys {
|
||||
}
|
||||
|
||||
if (CODES_TO_NAMES.containsKey(value)) {
|
||||
throw new RuntimeException(
|
||||
"Duplicate keys: " + CODES_TO_NAMES.get(value) +
|
||||
" and " + name + " both map to " + value +
|
||||
"(0x" + Integer.toHexString(value) + ")"
|
||||
);
|
||||
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.put(value, name);
|
||||
@ -125,8 +119,7 @@ public class Keys {
|
||||
name = "KEYPAD_" + name.substring("KP_".length());
|
||||
}
|
||||
|
||||
name = Character.toTitleCase(name.charAt(0)) +
|
||||
name.substring(1).toLowerCase();
|
||||
name = Character.toTitleCase(name.charAt(0)) + name.substring(1).toLowerCase();
|
||||
|
||||
name = name.replace('_', ' ');
|
||||
|
||||
|
@ -13,6 +13,7 @@ import ru.windcorp.progressia.client.graphics.backend.RenderTaskQueue;
|
||||
import ru.windcorp.progressia.common.resource.Resource;
|
||||
import ru.windcorp.progressia.common.util.BinUtil;
|
||||
import ru.windcorp.progressia.common.util.Named;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Atlases {
|
||||
|
||||
@ -24,10 +25,7 @@ public class Atlases {
|
||||
this.atlasSize = atlasSize;
|
||||
|
||||
if (!BinUtil.isPowerOf2(atlasSize)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Atlas size " + atlasSize
|
||||
+ " is not a power of 2"
|
||||
);
|
||||
throw new IllegalArgumentException("Atlas size " + atlasSize + " is not a power of 2");
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,11 +69,8 @@ public class Atlases {
|
||||
|
||||
editor.draw(data, nextX, nextY);
|
||||
|
||||
Sprite result = new Sprite(
|
||||
getPrimitive(),
|
||||
toPrimitiveCoords(nextX, nextY),
|
||||
toPrimitiveCoords(width, height)
|
||||
);
|
||||
Sprite result = new Sprite(getPrimitive(), toPrimitiveCoords(nextX, nextY),
|
||||
toPrimitiveCoords(width, height));
|
||||
|
||||
nextX += width;
|
||||
|
||||
@ -99,10 +94,7 @@ public class Atlases {
|
||||
}
|
||||
|
||||
private Vec2 toPrimitiveCoords(int x, int y) {
|
||||
return new Vec2(
|
||||
toPrimitiveCoord(x),
|
||||
toPrimitiveCoord(y)
|
||||
);
|
||||
return new Vec2(toPrimitiveCoord(x), toPrimitiveCoord(y));
|
||||
}
|
||||
|
||||
private float toPrimitiveCoord(int c) {
|
||||
@ -152,10 +144,8 @@ public class Atlases {
|
||||
|
||||
private static final TextureSettings SETTINGS = new TextureSettings(false);
|
||||
|
||||
private static final Map<Resource, Sprite> LOADED =
|
||||
new HashMap<>();
|
||||
private static final Multimap<AtlasGroup, Atlas> ATLASES =
|
||||
MultimapBuilder.hashKeys().arrayListValues().build();
|
||||
private static final Map<Resource, Sprite> LOADED = new HashMap<>();
|
||||
private static final Multimap<AtlasGroup, Atlas> ATLASES = MultimapBuilder.hashKeys().arrayListValues().build();
|
||||
|
||||
public static Sprite getSprite(Resource resource, AtlasGroup group) {
|
||||
return LOADED.computeIfAbsent(resource, k -> loadSprite(k, group));
|
||||
@ -163,12 +153,12 @@ public class Atlases {
|
||||
|
||||
private static Sprite loadSprite(Resource resource, AtlasGroup group) {
|
||||
try {
|
||||
TextureDataEditor data =
|
||||
TextureLoader.loadPixels(resource, SETTINGS);
|
||||
TextureDataEditor data = TextureLoader.loadPixels(resource, SETTINGS);
|
||||
|
||||
return loadSprite(data.getData(), group);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,14 +170,11 @@ public class Atlases {
|
||||
private static Atlas getReadyAtlas(AtlasGroup group, TextureData data) {
|
||||
List<Atlas> atlases = (List<Atlas>) ATLASES.get(group);
|
||||
|
||||
if (
|
||||
atlases.isEmpty() ||
|
||||
!(atlases.get(atlases.size() - 1).canAddSprite(data))
|
||||
) {
|
||||
if (atlases.isEmpty() || !(atlases.get(atlases.size() - 1).canAddSprite(data))) {
|
||||
Atlas newAtlas = new Atlas(group);
|
||||
|
||||
if (!newAtlas.canAddSprite(data)) {
|
||||
throw new RuntimeException("Could not fit tex into atlas of size " + newAtlas.getSize());
|
||||
CrashReports.report(null, "Could not fit texture into atlas of size %d", newAtlas.getSize());
|
||||
}
|
||||
|
||||
atlases.add(newAtlas);
|
||||
@ -202,6 +189,7 @@ public class Atlases {
|
||||
});
|
||||
}
|
||||
|
||||
private Atlases() {}
|
||||
private Atlases() {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import java.util.Map;
|
||||
|
||||
import ru.windcorp.progressia.common.resource.Resource;
|
||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class SimpleTextures {
|
||||
|
||||
@ -30,7 +31,8 @@ public class SimpleTextures {
|
||||
new Sprite(new TexturePrimitive(data.getData()))
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not load texture %s", resource);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.util.Arrays;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker;
|
||||
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class TexturePrimitive implements OpenGLDeletable {
|
||||
|
||||
@ -89,7 +90,7 @@ public class TexturePrimitive implements OpenGLDeletable {
|
||||
OpenGLObjectTracker.register(this);
|
||||
|
||||
if (handle < 0) {
|
||||
throw new RuntimeException("oops");
|
||||
CrashReports.report(null, "Failed to allocate texture");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@ package ru.windcorp.progressia.client.localization;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Localizer {
|
||||
private static final Localizer INSTANCE = new Localizer("assets/languages/", "en-US");
|
||||
|
||||
@ -41,7 +43,7 @@ public class Localizer {
|
||||
data = new Parser(langFolder + this.language + ".lang").parse();
|
||||
pokeListeners(language);
|
||||
} else {
|
||||
throw new RuntimeException("Language not found: " + language);
|
||||
CrashReports.report(null, "Language not found: %s", language);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package ru.windcorp.progressia.client.localization;
|
||||
import ru.windcorp.jputil.chars.EscapeException;
|
||||
import ru.windcorp.jputil.chars.Escaper;
|
||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
@ -80,7 +81,8 @@ public class Parser {
|
||||
}
|
||||
|
||||
} catch (IOException | EscapeException e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not parse language file %s", filePath);
|
||||
return null;
|
||||
}
|
||||
return parsedData;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
|
||||
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class EntityRenderRegistry extends NamespacedInstanceRegistry<EntityRender> {
|
||||
|
||||
@ -28,7 +29,8 @@ public class EntityRenderRegistry extends NamespacedInstanceRegistry<EntityRende
|
||||
).getData()
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not load entity texture %s", name);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import com.google.common.io.CharStreams;
|
||||
|
||||
import ru.windcorp.progressia.Progressia;
|
||||
import ru.windcorp.progressia.common.util.Named;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Resource extends Named {
|
||||
|
||||
@ -50,7 +51,8 @@ public class Resource extends Named {
|
||||
try (Reader reader = getReader()) {
|
||||
return CharStreams.toString(reader);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // TODO handle gracefully
|
||||
CrashReports.report(e, "Could not read resource %s as text", this);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +61,8 @@ public class Resource extends Named {
|
||||
try (InputStream stream = getInputStream()) {
|
||||
byteArray = ByteStreams.toByteArray(stream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // TODO handle gracefully
|
||||
CrashReports.report(e, "Could not read resource %s as bytes", this);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (output == null || output.remaining() < byteArray.length) {
|
||||
|
@ -0,0 +1,32 @@
|
||||
package ru.windcorp.progressia.common.util.crash;
|
||||
|
||||
/**
|
||||
* A crash report utility that performs analysis of a problem during crash
|
||||
* report generation and presents its conclusion to the user via the crash report.
|
||||
* Unlike {@link ContextProvider}s, Analyzers are provided with the reported problem
|
||||
* details.
|
||||
* @see ContextProvider
|
||||
* @author serega404
|
||||
*/
|
||||
public interface Analyzer {
|
||||
|
||||
/**
|
||||
* Provides a human-readable string describing this analyzer's conclusion
|
||||
* on the presented problem, or returns {@code null} if no conclusion
|
||||
* could be made.
|
||||
* @param throwable The reported throwable (may be {@code null})
|
||||
* @param messageFormat A {@linkplain java.util.Formatter#syntax format string} of a
|
||||
* human-readable description of the problem
|
||||
* @param args The arguments for the format string
|
||||
* @return a conclusion or {@code null}
|
||||
*/
|
||||
String analyze(Throwable throwable, String messageFormat, Object... args);
|
||||
|
||||
/**
|
||||
* Returns this analyzer's human-readable name.
|
||||
* It should be A String In Title Case With Spaces.
|
||||
* @return this analyzer's name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package ru.windcorp.progressia.common.util.crash;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A crash report utility that gathers information about game and system state
|
||||
* when a crash occurs and presents it to the user via the crash report.
|
||||
* ContextProviders are not aware of the nature of the problem, unlike {@link Analyzer}s.
|
||||
* @see Analyzer
|
||||
* @author serega404
|
||||
*/
|
||||
public interface ContextProvider {
|
||||
|
||||
/**
|
||||
* Provides human-readable description of the state of the game and the system.
|
||||
* This information is {@link Map#put(Object, Object) put} into the provided map
|
||||
* as key-value pairs. Keys are the characteristic being described, such as "OS Name",
|
||||
* and should be Strings In Title Case With Spaces.
|
||||
* If this provider cannot provide any information at this moment, the map is not
|
||||
* modified.
|
||||
* @param output the map to append output to
|
||||
*/
|
||||
void provideContext(Map<String, String> output);
|
||||
|
||||
/**
|
||||
* Returns this provider's human-readable name.
|
||||
* It should be A String In Title Case With Spaces.
|
||||
* @return this provider's name
|
||||
*/
|
||||
String getName();
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
package ru.windcorp.progressia.common.util.crash;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.util.StringBuilderWriter;
|
||||
|
||||
import ru.windcorp.jputil.chars.StringUtil;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class CrashReports {
|
||||
|
||||
private CrashReports() {
|
||||
}
|
||||
|
||||
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<Analyzer> ANALYZERS = Collections.synchronizedCollection(new ArrayList<>());
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("crash");
|
||||
|
||||
/**
|
||||
* <em>This method never returns.</em>
|
||||
* <p>
|
||||
* TODO document
|
||||
*
|
||||
* @param throwable
|
||||
* @param messageFormat
|
||||
* @param args
|
||||
*/
|
||||
public static void report(Throwable throwable, String messageFormat, Object... args) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
try {
|
||||
String.format(messageFormat, args);
|
||||
} catch (IllegalFormatException e) {
|
||||
messageFormat = StringUtil.replaceAll(messageFormat, "%", "%%");
|
||||
|
||||
if (args.length != 0) {
|
||||
messageFormat += "\nArgs:";
|
||||
for (Object arg : args) {
|
||||
try {
|
||||
messageFormat += " \"" + arg.toString() + "\"";
|
||||
} catch (Throwable t) {
|
||||
messageFormat += " exc: \"" + t.getClass().toString() + "\"";
|
||||
}
|
||||
}
|
||||
args = new Object[0]; // clear args
|
||||
}
|
||||
|
||||
messageFormat += "\nCould not format provided description";
|
||||
}
|
||||
|
||||
appendContextProviders(output);
|
||||
addSeparator(output);
|
||||
if (appendAnalyzers(output, throwable, messageFormat, args)) {
|
||||
addSeparator(output);
|
||||
}
|
||||
|
||||
appendMessageFormat(output, messageFormat, args);
|
||||
|
||||
appendStackTrace(output, throwable);
|
||||
|
||||
export(output.toString());
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static void appendContextProviders(StringBuilder output) {
|
||||
|
||||
// Do a local copy to avoid deadlocks -OLEGSHA
|
||||
ContextProvider[] localProvidersCopy = PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]);
|
||||
|
||||
for (ContextProvider provider : localProvidersCopy) {
|
||||
if (provider == null)
|
||||
continue;
|
||||
|
||||
addSeparator(output);
|
||||
|
||||
try {
|
||||
Map<String, String> buf = new HashMap<>();
|
||||
provider.provideContext(buf);
|
||||
|
||||
if (!buf.isEmpty()) {
|
||||
output.append("Provider name: ").append(provider.getName()).append("\n");
|
||||
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
||||
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
output.append("\n");
|
||||
|
||||
String providerName;
|
||||
|
||||
try {
|
||||
providerName = provider.getName();
|
||||
} catch (Throwable t1) {
|
||||
providerName = provider.getClass().getName();
|
||||
}
|
||||
|
||||
output.append(providerName).append(" is broken").append("\n");
|
||||
// ContextProvider is broken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean appendAnalyzers(StringBuilder output, Throwable throwable, String messageFormat,
|
||||
Object[] args) {
|
||||
boolean analyzerResponsesExist = false;
|
||||
|
||||
// Do a local copy to avoid deadlocks -OLEGSHA
|
||||
Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]);
|
||||
|
||||
for (Analyzer analyzer : localAnalyzersCopy) {
|
||||
if (analyzer == null)
|
||||
continue;
|
||||
|
||||
String answer;
|
||||
try {
|
||||
answer = analyzer.analyze(throwable, messageFormat, args);
|
||||
|
||||
if (answer != null && !answer.isEmpty()) {
|
||||
analyzerResponsesExist = true;
|
||||
output.append(analyzer.getName()).append(": ").append(answer).append("\n");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
analyzerResponsesExist = true;
|
||||
|
||||
output.append("\n");
|
||||
|
||||
String analyzerName;
|
||||
|
||||
try {
|
||||
analyzerName = analyzer.getName();
|
||||
} catch (Throwable t1) {
|
||||
analyzerName = analyzer.getClass().getName();
|
||||
}
|
||||
|
||||
output.append(analyzerName).append(" is broken").append("\n");
|
||||
// Analyzer is broken
|
||||
}
|
||||
}
|
||||
|
||||
return analyzerResponsesExist;
|
||||
}
|
||||
|
||||
private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) {
|
||||
output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n");
|
||||
|
||||
addSeparator(output);
|
||||
}
|
||||
|
||||
private static void appendStackTrace(StringBuilder output, Throwable throwable) {
|
||||
output.append("Stacktrace: \n");
|
||||
|
||||
if (throwable == null) {
|
||||
output.append("no Throwable provided").append("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Formatting to a human-readable string
|
||||
Writer sink = new StringBuilderWriter(output);
|
||||
try {
|
||||
throwable.printStackTrace(new PrintWriter(sink));
|
||||
} catch (Exception e) {
|
||||
// PLAK
|
||||
}
|
||||
output.append("\n");
|
||||
}
|
||||
|
||||
private static void export(String report) {
|
||||
try {
|
||||
LOGGER.fatal("/n" + report);
|
||||
} catch (Exception e) {
|
||||
// PLAK
|
||||
}
|
||||
|
||||
System.err.println(report);
|
||||
|
||||
generateCrashReportFiles(report);
|
||||
}
|
||||
|
||||
private static void generateCrashReportFiles(String output) {
|
||||
Date date = new Date();
|
||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
|
||||
|
||||
try {
|
||||
if (!Files.exists(CRASH_REPORTS_PATH))
|
||||
Files.createDirectory(CRASH_REPORTS_PATH);
|
||||
|
||||
createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log");
|
||||
createFileForCrashReport(output,
|
||||
CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log");
|
||||
} catch (Throwable t) {
|
||||
// Crash Report not created
|
||||
}
|
||||
}
|
||||
|
||||
private static void createFileForCrashReport(String buffer, String filename) {
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) {
|
||||
writer.write(buffer);
|
||||
} catch (IOException ex) {
|
||||
// Crash Report not created
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
// 80 chars
|
||||
"--------------------------------------------------------------------------------").append("\n");
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package ru.windcorp.progressia.common.util.crash.analyzers;
|
||||
|
||||
import ru.windcorp.progressia.common.util.crash.Analyzer;
|
||||
|
||||
public class OutOfMemoryAnalyzer implements Analyzer {
|
||||
@Override
|
||||
public String analyze(Throwable throwable, String messageFormat, Object... args) {
|
||||
if (throwable instanceof OutOfMemoryError)
|
||||
return "Try to add memory to the JVM";
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Out Of Memory Analyzer";
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package ru.windcorp.progressia.common.util.crash.providers;
|
||||
|
||||
import ru.windcorp.progressia.common.util.crash.ContextProvider;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class OSContextProvider implements ContextProvider {
|
||||
|
||||
@Override
|
||||
public void provideContext(Map<String, String> output) {
|
||||
output.put("OS Name", System.getProperty("os.name"));
|
||||
output.put("OS Version", System.getProperty("os.version"));
|
||||
output.put("OS Architecture", System.getProperty("os.arch"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "OS Context Provider";
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import java.io.IOException;
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.util.DataBuffer;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
|
||||
public class PacketEntityChange extends PacketWorldChange {
|
||||
@ -47,17 +48,13 @@ public class PacketEntityChange extends PacketWorldChange {
|
||||
EntityData entity = world.getEntity(getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
throw new RuntimeException(
|
||||
"Entity with ID " + getEntityId() + " not found"
|
||||
);
|
||||
CrashReports.report(null, "Entity with ID %d not found", getEntityId());
|
||||
}
|
||||
|
||||
try {
|
||||
entity.read(getReader(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Entity could not be read", e
|
||||
);
|
||||
CrashReports.report(e, "Entity could not be read");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.util.LowOverheadCache;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.Coordinates;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
@ -103,8 +104,7 @@ public class ImplementedChangeTracker implements Changer {
|
||||
Vec3i blockInChunk = Vectors.grab3i();
|
||||
Coordinates.convertInWorldToInChunk(position, blockInChunk);
|
||||
|
||||
List<TileData> tiles = world.getChunkByBlock(position)
|
||||
.getTiles(blockInChunk, face);
|
||||
List<TileData> tiles = world.getChunkByBlock(position).getTiles(blockInChunk, face);
|
||||
|
||||
if (shouldAdd) {
|
||||
tiles.add(tile);
|
||||
@ -142,7 +142,7 @@ public class ImplementedChangeTracker implements Changer {
|
||||
try {
|
||||
entity.write(packet.getWriter(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
CrashReports.report(e, "Could not write entity %s", entity);
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ public class ImplementedChangeTracker implements Changer {
|
||||
try {
|
||||
entity.write(packet.getWriter(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Entity could not be written", e);
|
||||
CrashReports.report(e, "Could not write entity %s", entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,18 +13,14 @@
|
||||
|
||||
<RollingFile name="FileLog" fileName="${APP_LOG_ROOT}/game.log"
|
||||
filePattern="${APP_LOG_ROOT}/game-%d{yyyy-MM-dd}-%i.log">
|
||||
<LevelRangeFilter minLevel="FATAL" maxLevel="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
<Policies>
|
||||
<SizeBasedTriggeringPolicy size="18MB" />
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="10"/>
|
||||
</RollingFile>
|
||||
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
|
||||
<Root level="info">
|
||||
<AppenderRef ref="FileLog" />
|
||||
<AppenderRef ref="Console" />
|
||||
|
Reference in New Issue
Block a user