Merge branch 'master' into multichunk

Conflicts:
	src/main/java/ru/windcorp/progressia/server/ServerThread.java
This commit is contained in:
OLEGSHA 2021-01-02 10:34:47 +03:00
commit 8c6794e7da
32 changed files with 266 additions and 90 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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());
}); });
} }

View File

@ -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();

View File

@ -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
} }

View File

@ -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));
} }
} }

View File

@ -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));
} }
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;
} }
} }

View 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);
} }
} }

View File

@ -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));
} }

View File

@ -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);

View File

@ -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;
} }
} }

View File

@ -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");
} }
} }

View File

@ -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);
} }
} }

View File

@ -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;
} }

View File

@ -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;
} }
} }

View File

@ -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());
} }
} }

View File

@ -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
); );

View File

@ -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) {

View File

@ -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() {
}
} }

View File

@ -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());

View File

@ -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");
} }
} }

View File

@ -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");
} }
} }

View File

@ -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);

View File

@ -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;
} }
} }

View File

@ -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");
} }
} }

View File

@ -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);
} }
} }

View File

@ -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++;

View File

@ -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.",

View File

@ -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;
// //