Added localization, LayerTestGUI hiding and fixed two bugs

- Re-added localization
  - Currently en-US, ru-RU
  - Switch with L
  - Used in LayerTestGUI and the new LayerAbout
- Added LayerAbout
  - Contains game named and version
  - Localized
- TestLayerGUI is hidden by default
  - Switch visiblity with F3
- Fixed alignment of text inside Labels
- Fixed crash reports not showing suppressed exceptions and causes
This commit is contained in:
OLEGSHA 2021-01-12 00:36:18 +03:00
parent 9a9823be0d
commit 70c213fe0d
12 changed files with 238 additions and 63 deletions

View File

@ -26,6 +26,7 @@ import ru.windcorp.progressia.client.graphics.font.GNUUnifontLoader;
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.client.localization.Localizer;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.server.ServerState;
@ -44,6 +45,8 @@ public class ClientProxy implements Proxy {
throw CrashReports.report(e, "ClientProxy failed");
}
Localizer.getInstance().setLanguage("en-US");
TestContent.registerContent();
Atlases.loadAllAtlases();

View File

@ -5,7 +5,7 @@ import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.common.world.WorldData;
import ru.windcorp.progressia.server.ServerState;
import ru.windcorp.progressia.test.LayerTestGUI;
import ru.windcorp.progressia.test.LayerAbout;
import ru.windcorp.progressia.test.LayerTestUI;
import ru.windcorp.progressia.test.TestContent;
@ -37,7 +37,7 @@ public class ClientState {
GUI.addBottomLayer(new LayerWorld(client));
GUI.addTopLayer(new LayerTestUI());
GUI.addTopLayer(new LayerTestGUI());
GUI.addTopLayer(new LayerAbout());
}

View File

@ -18,6 +18,7 @@
package ru.windcorp.progressia.client.graphics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.common.eventbus.Subscribe;
@ -31,7 +32,15 @@ import ru.windcorp.progressia.client.graphics.input.bus.Input;
public class GUI {
private static final List<Layer> LAYERS = new ArrayList<>();
private static final List<Layer> LAYERS = Collections.synchronizedList(new ArrayList<>());
private static final List<Layer> UNMODIFIABLE_LAYERS = Collections.unmodifiableList(LAYERS);
@FunctionalInterface
private interface LayerStackModification {
void affect(List<Layer> layers);
}
private static final List<LayerStackModification> MODIFICATION_QUEUE = Collections.synchronizedList(new ArrayList<>());
private static class ModifiableInput extends Input {
@Override
@ -44,23 +53,36 @@ public class GUI {
private GUI() {}
public synchronized static void addBottomLayer(Layer layer) {
LAYERS.add(layer);
public static void addBottomLayer(Layer layer) {
modify(layers -> layers.add(layer));
}
public synchronized static void addTopLayer(Layer layer) {
LAYERS.add(0, layer);
public static void addTopLayer(Layer layer) {
modify(layers -> layers.add(0, layer));
}
public synchronized static void removeLayer(Layer layer) {
LAYERS.remove(layer);
public static void removeLayer(Layer layer) {
modify(layers -> layers.remove(layer));
}
public synchronized static void render() {
private static void modify(LayerStackModification mod) {
MODIFICATION_QUEUE.add(mod);
}
public static List<Layer> getLayers() {
return UNMODIFIABLE_LAYERS;
}
public static void render() {
synchronized (LAYERS) {
MODIFICATION_QUEUE.forEach(action -> action.affect(LAYERS));
MODIFICATION_QUEUE.clear();
for (int i = LAYERS.size() - 1; i >= 0; --i) {
LAYERS.get(i).render();
}
}
}
public static void invalidateEverything() {
LAYERS.forEach(Layer::invalidate);

View File

@ -4,6 +4,7 @@ import glm.mat._4.Mat4;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.localization.MutableString;
import java.util.function.Supplier;
@ -14,6 +15,8 @@ public class Label extends Component {
private Vec2i currentSize;
private Supplier<String> contents;
private MutableString.Listener mutableStringListener = null;
private float maxWidth = Float.POSITIVE_INFINITY;
public Label(String name, Font font, Supplier<String> contents) {
@ -27,6 +30,13 @@ public class Label extends Component {
this(name, font, () -> contents);
}
public Label(String name, Font font, MutableString contents) {
this(name, font, contents::get);
this.mutableStringListener = this::update;
contents.addListener(mutableStringListener);
}
public void update() {
currentText = contents.get();
currentSize = font.getSize(currentText, maxWidth, null).mul(2);
@ -52,8 +62,10 @@ public class Label extends Component {
@Override
protected void assembleSelf(RenderTarget target) {
float startX = getX() + font.getAlign() * (getWidth() - currentSize.x);
target.pushTransform(
new Mat4().identity().translate(getX(), getY(), -1000) // TODO wtf is this magic <---
new Mat4().identity().translate(startX, getY(), -1000) // TODO wtf is this magic <---
.scale(2)
);

View File

@ -56,6 +56,10 @@ public class Localizer {
}
public synchronized String getValue(String key) {
if (data == null) {
throw new IllegalStateException("Localizer not yet initialized");
}
if (data.containsKey(key)) {
return data.get(key);
} else if (fallBackData.containsKey(key)) {

View File

@ -1,6 +1,7 @@
package ru.windcorp.progressia.client.localization;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@ -17,6 +18,8 @@ public abstract class MutableString {
protected final Collection<WeakReference<Listener>> listeners =
Collections.synchronizedCollection(new LinkedList<>());
private Collection<Listener> myListeners = null;
protected void pokeListeners() {
//TODO extract as weak bus listener class
synchronized (listeners) {
@ -44,7 +47,13 @@ public abstract class MutableString {
protected void listen(Object obj) {
if (obj instanceof MutableString) {
((MutableString) obj).addListener(this::update);
if (myListeners == null) {
myListeners = new ArrayList<>();
}
Listener listener = this::update;
myListeners.add(listener);
((MutableString) obj).addListener(listener);
}
}

View File

@ -2,11 +2,14 @@ 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;
@ -244,14 +247,22 @@ public class CrashReports {
}
output.append("Reported Throwable:\n");
appendStackTrace(output, throwable.getStackTrace(), throwable.toString());
// 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 appendStackTrace(StringBuilder output, StackTraceElement[] stackTrace, String header) {
output.append(header).append('\n');
for (StackTraceElement element : stackTrace) {
output.append(" ").append(element).append('\n');
output.append("\tat ").append(element).append('\n');
}
}

View File

@ -0,0 +1,42 @@
package ru.windcorp.progressia.test;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typeface;
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
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.client.localization.MutableStringLocalized;
public class LayerAbout extends GUILayer {
public LayerAbout() {
super("LayerAbout", new LayoutAlign(1, 1, 5));
Panel panel = new Panel("ControlDisplays", new LayoutVertical(5));
Font font = new Font().withColor(Colors.WHITE).deriveOutlined().withAlign(Typeface.ALIGN_RIGHT);
Font aboutFont = font.withColor(0xFF37A3E6).deriveBold();
panel.addChild(new Label(
"About", aboutFont,
new MutableStringLocalized("LayerAbout.Title")
));
panel.addChild(new Label(
"Version", font,
new MutableStringLocalized("LayerAbout.Version").format("pre-TechDemo")
));
panel.addChild(new Label(
"DebugHint", font,
new MutableStringLocalized("LayerAbout.DebugHint")
));
getRoot().addChild(panel);
}
}

View File

@ -34,6 +34,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.client.localization.Localizer;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.util.dynstr.DynamicStrings;
import ru.windcorp.progressia.server.Server;
@ -46,48 +48,47 @@ public class LayerTestGUI extends GUILayer {
Panel panel = new Panel("ControlDisplays", new LayoutVertical(5));
Collection<Label> labels = new ArrayList<>();
Vec4 color = Colors.WHITE;
Font font = new Font().withColor(color).deriveOutlined();
Font aboutFont = font.withColor(0xFF37A3E6).deriveBold();
panel.addChild(new Label(
"About", aboutFont,
"\u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441\u0438\u044F / Progressia"
));
panel.addChild(new Label(
"About", font,
"Version: pre-TechDemo"
));
TestPlayerControls tpc = TestPlayerControls.getInstance();
panel.addChild(new Label(
"IsFlyingDisplay", font,
() -> String.format("Flying: %5s (Space bar x2)", TestPlayerControls.getInstance().isFlying())
new MutableStringLocalized("LayerTestGUI.IsFlyingDisplay").format(tpc.isFlying())
));
panel.addChild(new Label(
"IsSprintingDisplay", font,
() -> String.format("Sprinting: %5s (W x2)", TestPlayerControls.getInstance().isSprinting())
new MutableStringLocalized("LayerTestGUI.IsSprintingDisplay").format(tpc.isSprinting())
));
panel.addChild(new Label(
"IsMouseCapturedDisplay", font,
() -> String.format("Mouse captured: %5s (esc)", TestPlayerControls.getInstance().isMouseCaptured())
new MutableStringLocalized("LayerTestGUI.IsMouseCapturedDisplay").format(tpc.isMouseCaptured())
));
panel.addChild(new Label(
"CameraModeDisplay", font,
() -> String.format("Camera mode: %5d (F5)", ClientState.getInstance().getCamera().getCurrentModeIndex())
new MutableStringLocalized("LayerTestGUI.CameraModeDisplay").format(ClientState.getInstance().getCamera().getCurrentModeIndex())
));
panel.addChild(new Label(
"GravityModeDisplay", font,
() -> String.format("Gravity: %9s (G)", TestPlayerControls.getInstance().useMinecraftGravity() ? "Minecraft" : "Realistic")
new MutableStringLocalized("LayerTestGUI.GravityModeDisplay").format(tpc.useMinecraftGravity() ? "Minecraft" : "Realistic")
));
panel.addChild(new Label(
"LanguageDisplay", font,
new MutableStringLocalized("LayerTestGUI.LanguageDisplay").apply(s -> String.format(s, Localizer.getInstance().getLanguage()))
));
panel.addChild(new DynamicLabel(
"FPSDisplay", font,
DynamicStrings.builder().add("FPS: ").addDyn(() -> FPS_RECORD.update(GraphicsInterface.getFPS()), 5, 1).buildSupplier(),
DynamicStrings.builder()
.addDyn(new MutableStringLocalized("LayerTestGUI.FPSDisplay"))
.addDyn(() -> FPS_RECORD.update(GraphicsInterface.getFPS()), 5, 1)
.buildSupplier(),
128
));
@ -99,7 +100,10 @@ public class LayerTestGUI extends GUILayer {
panel.addChild(new DynamicLabel(
"ChunkUpdatesDisplay", font,
DynamicStrings.builder().addConst("Pending updates: ").addDyn(ClientState.getInstance().getWorld()::getPendingChunkUpdates).buildSupplier(),
DynamicStrings.builder()
.addDyn(new MutableStringLocalized("LayerTestGUI.ChunkUpdatesDisplay"))
.addDyn(ClientState.getInstance().getWorld()::getPendingChunkUpdates)
.buildSupplier(),
128
));
@ -111,34 +115,34 @@ public class LayerTestGUI extends GUILayer {
panel.addChild(new Label(
"SelectedBlockDisplay", font,
() -> String.format(
"%s Block: %s",
new MutableStringLocalized("LayerTestGUI.SelectedBlockDisplay").format(
TestPlayerControls.getInstance().isBlockSelected() ? ">" : " ",
TestPlayerControls.getInstance().getSelectedBlock().getId()
)
));
panel.addChild(new Label(
"SelectedTileDisplay", font,
() -> String.format(
"%s Tile: %s",
new MutableStringLocalized("LayerTestGUI.SelectedTileDisplay").format(
TestPlayerControls.getInstance().isBlockSelected() ? " " : ">",
TestPlayerControls.getInstance().getSelectedTile().getId()
)
));
panel.addChild(new Label(
"PlacementModeDisplay", font,
"(Blocks \u2B04 Tiles: Ctrl + Mouse Wheel)"
"PlacementModeHint", font,
new MutableStringLocalized("LayerTestGUI.PlacementModeHint").format("\u2B04")
));
getRoot().addChild(panel);
}
panel.getChildren().forEach(c -> {
public Runnable getUpdateCallback() {
Collection<Label> labels = new ArrayList<>();
getRoot().getChild(0).getChildren().forEach(c -> {
if (c instanceof Label) {
labels.add((Label) c);
}
});
TestPlayerControls.getInstance().setUpdateCallback(() -> labels.forEach(Label::update));
getRoot().addChild(panel);
return () -> labels.forEach(Label::update);
}
private static class Averager {
@ -191,12 +195,12 @@ public class LayerTestGUI extends GUILayer {
private static final Averager TPS_RECORD = new Averager();
private static final Supplier<CharSequence> TPS_STRING = DynamicStrings.builder()
.add("TPS: ")
.addDyn(new MutableStringLocalized("LayerTestGUI.TPSDisplay"))
.addDyn(() -> TPS_RECORD.update(ServerState.getInstance().getTPS()), 5, 1)
.buildSupplier();
private static final Supplier<CharSequence> POS_STRING = DynamicStrings.builder()
.add("Pos: ")
.addDyn(new MutableStringLocalized("LayerTestGUI.PosDisplay"))
.addDyn(() -> ClientState.getInstance().getCamera().getLastAnchorPosition().x, 7, 1)
.addDyn(() -> ClientState.getInstance().getCamera().getLastAnchorPosition().y, 7, 1)
.addDyn(() -> ClientState.getInstance().getCamera().getLastAnchorPosition().z, 7, 1)
@ -204,18 +208,18 @@ public class LayerTestGUI extends GUILayer {
private static CharSequence getTPS() {
Server server = ServerState.getInstance();
if (server == null) return "TPS: n/a";
if (server == null) return Localizer.getInstance().getValue("LayerTestGUI.TPSDisplay.NA");
return TPS_STRING.get();
}
private static CharSequence getPos() {
Client client = ClientState.getInstance();
if (client == null) return "Pos: client n/a";
if (client == null) return Localizer.getInstance().getValue("LayerTestGUI.PosDisplay.NA.Client");
Vec3 pos = client.getCamera().getLastAnchorPosition();
if (Float.isNaN(pos.x)) {
return "Pos: entity n/a";
return Localizer.getInstance().getValue("LayerTestGUI.PosDisplay.NA.Entity");
} else {
return POS_STRING.get();
}

View File

@ -6,6 +6,7 @@ import glm.vec._2.Vec2;
import glm.vec._3.Vec3;
import org.lwjgl.glfw.GLFW;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
@ -15,6 +16,7 @@ import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent;
import ru.windcorp.progressia.client.graphics.input.bus.Input;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.localization.Localizer;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.util.FloatMathUtils;
import ru.windcorp.progressia.common.world.block.BlockData;
@ -30,9 +32,6 @@ public class TestPlayerControls {
return INSTANCE;
}
private TestPlayerControls() {
}
private static final double MODE_SWITCH_MAX_DELAY = 300 * Units.MILLISECONDS;
private static final double MODE_SPRINT_SWITCH_MAX_DELAY = 100 * Units.MILLISECONDS;
private static final double MIN_JUMP_DELAY = 300 * Units.MILLISECONDS;
@ -72,6 +71,7 @@ public class TestPlayerControls {
private int selectedTile = 0;
private boolean isBlockSelected = true;
private LayerTestGUI debugLayer = null;
private Runnable updateCallback = null;
public void applyPlayerControls() {
@ -164,6 +164,11 @@ public class TestPlayerControls {
handleEscape();
break;
case GLFW.GLFW_KEY_F3:
if (!event.isPress()) return false;
handleDebugLayerSwitch();
break;
case GLFW.GLFW_KEY_F5:
if (!event.isPress()) return false;
handleCameraMode();
@ -174,6 +179,11 @@ public class TestPlayerControls {
handleGravitySwitch();
break;
case GLFW.GLFW_KEY_L:
if (!event.isPress()) return false;
handleLanguageSwitch();
break;
case GLFW.GLFW_MOUSE_BUTTON_3:
if (!event.isPress()) return false;
switchPlacingMode();
@ -251,6 +261,19 @@ public class TestPlayerControls {
updateGUI();
}
private void handleDebugLayerSwitch() {
if (debugLayer == null) {
this.debugLayer = new LayerTestGUI();
this.updateCallback = debugLayer.getUpdateCallback();
}
if (GUI.getLayers().contains(debugLayer)) {
GUI.removeLayer(debugLayer);
} else {
GUI.addTopLayer(debugLayer);
}
}
private void handleCameraMode() {
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
return;
@ -267,6 +290,17 @@ public class TestPlayerControls {
updateGUI();
}
private void handleLanguageSwitch() {
Localizer localizer = Localizer.getInstance();
if (localizer.getLanguage().equals("ru-RU")) {
localizer.setLanguage("en-US");
} else {
localizer.setLanguage("ru-RU");
}
updateGUI();
}
private void onMouseMoved(CursorMoveEvent event) {
if (!captureMouse) return;
@ -342,10 +376,6 @@ public class TestPlayerControls {
return ClientState.getInstance().getLocalPlayer();
}
public void setUpdateCallback(Runnable updateCallback) {
this.updateCallback = updateCallback;
}
private void updateGUI() {
if (this.updateCallback != null) {
this.updateCallback.run();

View File

@ -1,3 +1,22 @@
# ru-RU
#test
Epsilon = Hooray?..
# en-US
LayerAbout.Title = Progressia
LayerAbout.Version = Version: %s
LayerAbout.DebugHint = Debug GUI: F3
LayerTestGUI.IsFlyingDisplay = Flying: %5s (Space bar x2)
LayerTestGUI.IsSprintingDisplay = Sprinting: %5s (W x2)
LayerTestGUI.IsMouseCapturedDisplay = Mouse captured: %5s (Esc)
LayerTestGUI.CameraModeDisplay = Camera mode: %5d (F5)
LayerTestGUI.GravityModeDisplay = Gravity: %9s (G)
LayerTestGUI.LanguageDisplay = Language: %5s (L)
LayerTestGUI.FPSDisplay = FPS:
LayerTestGUI.TPSDisplay = TPS:
LayerTestGUI.TPSDisplay.NA = TPS: n/a
LayerTestGUI.ChunkUpdatesDisplay = Pending updates:
LayerTestGUI.PosDisplay = Pos:
LayerTestGUI.PosDisplay.NA.Client = Pos: client n/a
LayerTestGUI.PosDisplay.NA.Entity = Pos: entity n/a
LayerTestGUI.SelectedBlockDisplay = %s Block: %s
LayerTestGUI.SelectedTileDisplay = %s Tile: %s
LayerTestGUI.PlacementModeHint = (Blocks %s Tiles: Ctrl + Mouse Wheel)

View File

@ -1,3 +1,22 @@
# ru-RU
#test
Epsilon = Ура?
LayerAbout.Title = Прогрессия
LayerAbout.Version = Версия: %s
LayerAbout.DebugHint = Отладочный GUI: F3
LayerTestGUI.IsFlyingDisplay = Полёт: %5s (Пробел x2)
LayerTestGUI.IsSprintingDisplay = Бег: %5s (W x2)
LayerTestGUI.IsMouseCapturedDisplay = Захват мыши: %5s (Esc)
LayerTestGUI.CameraModeDisplay = Камера: %5d (F5)
LayerTestGUI.GravityModeDisplay = Гравитация: %9s (G)
LayerTestGUI.LanguageDisplay = Язык: %5s (L)
LayerTestGUI.FPSDisplay = FPS:
LayerTestGUI.TPSDisplay = TPS:
LayerTestGUI.TPSDisplay.NA = TPS: н/д
LayerTestGUI.ChunkUpdatesDisplay = Обновления в очереди:
LayerTestGUI.PosDisplay = Поз:
LayerTestGUI.PosDisplay.NA.Client = Поз: клиент н/д
LayerTestGUI.PosDisplay.NA.Entity = Поз: сущность н/д
LayerTestGUI.SelectedBlockDisplay = %s Блок: %s
LayerTestGUI.SelectedTileDisplay = %s Плитка: %s
LayerTestGUI.PlacementModeHint = (Блок %s плитки: Ctrl + прокрутка)