diff --git a/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java b/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java index c8935fc..630f9c6 100644 --- a/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java +++ b/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java @@ -15,21 +15,32 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia; +import ru.windcorp.progressia.client.ClientProxy; +import ru.windcorp.progressia.client.graphics.GUI; 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.*; +import ru.windcorp.progressia.test.LayerTitle; public class ProgressiaLauncher { public static String[] arguments; + private static ClientProxy proxy; - public static void launch(String[] args, Proxy proxy) { + public static void launch(String[] args, ClientProxy inProxy) { arguments = args.clone(); setupCrashReports(); - proxy.initialize(); + + inProxy.initialize(); + proxy = inProxy; + GUI.addTopLayer(new LayerTitle("Title")); + } + + public static void play() { + proxy.setupServer(); } private static void setupCrashReports() { diff --git a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java index 1d154e7..ffb9bd7 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.client; import ru.windcorp.progressia.Proxy; @@ -38,7 +38,9 @@ public class ClientProxy implements Proxy { @Override public void initialize() { + GraphicsBackend.initialize(); + try { RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init); RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init); @@ -58,10 +60,12 @@ public class ClientProxy implements Proxy { AudioSystem.initialize(); - ServerState.startServer(); - ClientState.connectToLocalServer(); - TestMusicPlayer.start(); } + public void setupServer() { + ServerState.startServer(); + ClientState.connectToLocalServer(); + } + } diff --git a/src/main/java/ru/windcorp/progressia/client/ClientState.java b/src/main/java/ru/windcorp/progressia/client/ClientState.java index 31c366e..f473eb4 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientState.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientState.java @@ -15,15 +15,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.client; import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; import ru.windcorp.progressia.client.graphics.GUI; +import ru.windcorp.progressia.client.graphics.Layer; import ru.windcorp.progressia.client.graphics.world.LayerWorld; import ru.windcorp.progressia.common.world.DefaultWorldData; +import ru.windcorp.progressia.client.localization.MutableStringLocalized; import ru.windcorp.progressia.server.ServerState; import ru.windcorp.progressia.test.LayerAbout; +import ru.windcorp.progressia.test.LayerTestText; import ru.windcorp.progressia.test.LayerTestUI; import ru.windcorp.progressia.test.TestContent; @@ -52,11 +55,39 @@ public class ClientState { channel.connect(TestContent.PLAYER_LOGIN); setInstance(client); + displayLoadingScreen(); - GUI.addBottomLayer(new LayerWorld(client)); - GUI.addTopLayer(new LayerTestUI()); - GUI.addTopLayer(new LayerAbout()); + } + private static void displayLoadingScreen() { + GUI.addTopLayer(new LayerTestText("Text", new MutableStringLocalized("LayerText.Load"), layer -> { + Client client = ClientState.getInstance(); + + // TODO refacetor and remove + if (client != null) { + client.getComms().processPackets(); + } + + if (client != null && client.getLocalPlayer().hasEntity()) { + GUI.removeLayer(layer); + + // TODO refactor, this shouldn't be here + LayerWorld layerWorld = new LayerWorld(client); + LayerTestUI layerUI = new LayerTestUI(); + LayerAbout layerAbout = new LayerAbout(); + GUI.addBottomLayer(layerWorld); + GUI.addTopLayer(layerUI); + GUI.addTopLayer(layerAbout); + } + })); + } + + public static void disconnectFromLocalServer() { + getInstance().getComms().disconnect(); + + for (Layer layer : GUI.getLayers()) { + GUI.removeLayer(layer); + } } private ClientState() { diff --git a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalClient.java b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalClient.java index 816fba8..9fc0e47 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalClient.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalClient.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.client.comms.localhost; import java.io.IOException; diff --git a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java index 194a2a1..fcf8dd0 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java @@ -15,12 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.client.comms.localhost; import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.common.comms.packets.Packet; import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.ServerState; public class LocalServerCommsChannel extends ServerCommsChannel { @@ -54,7 +55,7 @@ public class LocalServerCommsChannel extends ServerCommsChannel { @Override public void disconnect() { - // Do nothing + ServerState.getInstance().getClientManager().disconnectClient(localClient); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/GUI.java b/src/main/java/ru/windcorp/progressia/client/graphics/GUI.java index bb4d85b..59b89da 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/GUI.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/GUI.java @@ -15,12 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.client.graphics; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import com.google.common.eventbus.Subscribe; @@ -58,6 +59,7 @@ public class GUI { } public static void addBottomLayer(Layer layer) { + Objects.requireNonNull(layer, "layer"); modify(layers -> { layers.add(layer); layer.onAdded(); @@ -65,6 +67,7 @@ public class GUI { } public static void addTopLayer(Layer layer) { + Objects.requireNonNull(layer, "layer"); modify(layers -> { layers.add(0, layer); layer.onAdded(); @@ -72,6 +75,7 @@ public class GUI { } public static void removeLayer(Layer layer) { + Objects.requireNonNull(layer, "layer"); modify(layers -> { layers.remove(layer); layer.onRemoved(); @@ -88,33 +92,33 @@ public class GUI { public static void render() { synchronized (LAYERS) { - + if (!MODIFICATION_QUEUE.isEmpty()) { MODIFICATION_QUEUE.forEach(action -> action.affect(LAYERS)); MODIFICATION_QUEUE.clear(); - + boolean isMouseCurrentlyCaptured = GraphicsInterface.isMouseCaptured(); Layer.CursorPolicy policy = Layer.CursorPolicy.REQUIRE; - + for (Layer layer : LAYERS) { Layer.CursorPolicy currentPolicy = layer.getCursorPolicy(); - + if (currentPolicy != Layer.CursorPolicy.INDIFFERENT) { policy = currentPolicy; break; } } - + boolean shouldCaptureMouse = (policy == Layer.CursorPolicy.FORBID); if (shouldCaptureMouse != isMouseCurrentlyCaptured) { GraphicsInterface.setMouseCaptured(shouldCaptureMouse); } } - + for (int i = LAYERS.size() - 1; i >= 0; --i) { LAYERS.get(i).render(); } - + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/BasicButton.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/BasicButton.java index cd30152..14c0e36 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/BasicButton.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/BasicButton.java @@ -37,15 +37,15 @@ import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; import ru.windcorp.progressia.client.graphics.input.KeyEvent; public abstract class BasicButton extends Component { - + private final Label label; private boolean isPressed = false; private final Collection> actions = Collections.synchronizedCollection(new ArrayList<>()); - public BasicButton(String name, String label, Font labelFont) { + public BasicButton(String name, Label label) { super(name); - this.label = new Label(name + ".Label", labelFont, label); + this.label = label; setLayout(new LayoutAlign(10)); addChild(this.label); @@ -59,8 +59,8 @@ public abstract class BasicButton extends Component { return false; } else if ( e.isLeftMouseButton() || - e.getKey() == GLFW.GLFW_KEY_SPACE || - e.getKey() == GLFW.GLFW_KEY_ENTER + e.getKey() == GLFW.GLFW_KEY_SPACE || + e.getKey() == GLFW.GLFW_KEY_ENTER ) { setPressed(e.isPress()); return true; @@ -68,9 +68,9 @@ public abstract class BasicButton extends Component { return false; } }); - + addListener(new Object() { - + // Release when losing focus @Subscribe public void onFocusChange(FocusEvent e) { @@ -78,7 +78,7 @@ public abstract class BasicButton extends Component { setPressed(false); } } - + // Release when hover ends @Subscribe public void onHoverEnded(HoverEvent e) { @@ -86,7 +86,7 @@ public abstract class BasicButton extends Component { setPressed(false); } } - + // Release when disabled @Subscribe public void onDisabled(EnableEvent e) { @@ -94,16 +94,20 @@ public abstract class BasicButton extends Component { setPressed(false); } } - + // Trigger virtualClick when button is released @Subscribe public void onRelease(ButtonEvent.Release e) { virtualClick(); } - + }); } + public BasicButton(String name, String label, Font labelFont) { + this(name, new Label(name + ".Label", labelFont, label)); + } + public BasicButton(String name, String label) { this(name, label, new Font()); } @@ -111,7 +115,7 @@ public abstract class BasicButton extends Component { public boolean isPressed() { return isPressed; } - + public void click() { setPressed(true); setPressed(false); @@ -120,7 +124,7 @@ public abstract class BasicButton extends Component { public void setPressed(boolean isPressed) { if (this.isPressed != isPressed) { this.isPressed = isPressed; - + if (isPressed) { takeFocus(); } @@ -128,16 +132,16 @@ public abstract class BasicButton extends Component { dispatchEvent(ButtonEvent.create(this, this.isPressed)); } } - + public BasicButton addAction(Consumer action) { this.actions.add(Objects.requireNonNull(action, "action")); return this; } - + public boolean removeAction(Consumer action) { return this.actions.remove(action); } - + public void virtualClick() { this.actions.forEach(action -> { action.accept(this); @@ -147,5 +151,5 @@ public abstract class BasicButton extends Component { public Label getLabel() { return label; } - + } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Button.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Button.java index bbeb361..5d42241 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Button.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Button.java @@ -28,7 +28,11 @@ public class Button extends BasicButton { public Button(String name, String label, Font labelFont) { super(name, label, labelFont); } - + + public Button(String name, Label label) { + super(name, label); + } + public Button(String name, String label) { this(name, label, new Font()); } @@ -36,7 +40,7 @@ public class Button extends BasicButton { @Override protected void assembleSelf(RenderTarget target) { // Border - + Vec4 borderColor; if (isPressed() || isHovered() || isFocused()) { borderColor = Colors.BLUE; @@ -44,9 +48,9 @@ public class Button extends BasicButton { borderColor = Colors.LIGHT_GRAY; } target.fill(getX(), getY(), getWidth(), getHeight(), borderColor); - + // Inside area - + if (isPressed()) { // Do nothing } else { @@ -58,20 +62,20 @@ public class Button extends BasicButton { } target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, backgroundColor); } - + // Change label font color - + if (isPressed()) { getLabel().setFont(getLabel().getFont().withColor(Colors.WHITE)); } else { getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK)); } } - + @Override protected void postAssembleSelf(RenderTarget target) { // Apply disable tint - + if (!isEnabled()) { target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF)); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Checkbox.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Checkbox.java index 5f9d0df..990ed43 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Checkbox.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Checkbox.java @@ -27,24 +27,24 @@ import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutHorizontal; public class Checkbox extends BasicButton { - + private class Tick extends Component { public Tick() { super(Checkbox.this.getName() + ".Tick"); - + setPreferredSize(new Vec2i(Typefaces.getDefault().getLineHeight() * 3 / 2)); } - + @Override protected void assembleSelf(RenderTarget target) { - + int size = getPreferredSize().x; int x = getX(); int y = getY() + (getHeight() - size) / 2; - + // Border - + Vec4 borderColor; if (Checkbox.this.isPressed() || Checkbox.this.isHovered() || Checkbox.this.isFocused()) { borderColor = Colors.BLUE; @@ -52,9 +52,9 @@ public class Checkbox extends BasicButton { borderColor = Colors.LIGHT_GRAY; } target.fill(x, y, size, size, borderColor); - + // Inside area - + if (Checkbox.this.isPressed()) { // Do nothing } else { @@ -66,9 +66,9 @@ public class Checkbox extends BasicButton { } target.fill(x + 2, y + 2, size - 4, size - 4, backgroundColor); } - + // "Tick" - + if (Checkbox.this.isChecked()) { target.fill(x + 4, y + 4, size - 8, size - 8, Colors.BLUE); } @@ -81,10 +81,10 @@ public class Checkbox extends BasicButton { public Checkbox(String name, String label, Font labelFont, boolean check) { super(name, label, labelFont); this.checked = check; - + assert getChildren().size() == 1 : "Checkbox expects that BasicButton contains exactly one child"; Component basicChild = getChild(0); - + Group group = new Group(getName() + ".LabelAndTick", new LayoutHorizontal(0, 10)); removeChild(basicChild); setLayout(new LayoutAlign(0, 0.5f, 10)); @@ -92,18 +92,18 @@ public class Checkbox extends BasicButton { group.addChild(new Tick()); group.addChild(basicChild); addChild(group); - + addAction(b -> switchState()); } - + public Checkbox(String name, String label, Font labelFont) { this(name, label, labelFont, false); } - + public Checkbox(String name, String label, boolean check) { this(name, label, new Font(), check); } - + public Checkbox(String name, String label) { this(name, label, false); } @@ -111,14 +111,14 @@ public class Checkbox extends BasicButton { public void switchState() { setChecked(!isChecked()); } - + /** * @return the checked */ public boolean isChecked() { return checked; } - + /** * @param checked the checked to set */ @@ -129,21 +129,21 @@ public class Checkbox extends BasicButton { @Override protected void assembleSelf(RenderTarget target) { // Change label font color - + if (isPressed()) { getLabel().setFont(getLabel().getFont().withColor(Colors.BLUE)); } else { getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK)); } } - + @Override protected void postAssembleSelf(RenderTarget target) { // Apply disable tint - + if (!isEnabled()) { target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF)); } } - + } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/RadioButton.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/RadioButton.java index 471efb6..1ee7f66 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/RadioButton.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/RadioButton.java @@ -30,30 +30,30 @@ import ru.windcorp.progressia.client.graphics.gui.layout.LayoutHorizontal; import ru.windcorp.progressia.client.graphics.input.KeyEvent; public class RadioButton extends BasicButton { - + private class Tick extends Component { public Tick() { super(RadioButton.this.getName() + ".Tick"); - + setPreferredSize(new Vec2i(Typefaces.getDefault().getLineHeight() * 3 / 2)); } - + private void cross(RenderTarget target, int x, int y, int size, Vec4 color) { target.fill(x + 4, y, size - 8, size, color); target.fill(x + 2, y + 2, size - 4, size - 4, color); target.fill(x, y + 4, size, size - 8, color); } - + @Override protected void assembleSelf(RenderTarget target) { - + int size = getPreferredSize().x; int x = getX(); int y = getY() + (getHeight() - size) / 2; - + // Border - + Vec4 borderColor; if (RadioButton.this.isPressed() || RadioButton.this.isHovered() || RadioButton.this.isFocused()) { borderColor = Colors.BLUE; @@ -61,9 +61,9 @@ public class RadioButton extends BasicButton { borderColor = Colors.LIGHT_GRAY; } cross(target, x, y, size, borderColor); - + // Inside area - + if (RadioButton.this.isPressed()) { // Do nothing } else { @@ -75,9 +75,9 @@ public class RadioButton extends BasicButton { } cross(target, x + 2, y + 2, size - 4, backgroundColor); } - + // "Tick" - + if (RadioButton.this.isChecked()) { cross(target, x + 4, y + 4, size - 8, Colors.BLUE); } @@ -86,16 +86,16 @@ public class RadioButton extends BasicButton { } private boolean checked; - + private RadioButtonGroup group = null; public RadioButton(String name, String label, Font labelFont, boolean check) { super(name, label, labelFont); this.checked = check; - + assert getChildren().size() == 1 : "RadioButton expects that BasicButton contains exactly one child"; Component basicChild = getChild(0); - + Group group = new Group(getName() + ".LabelAndTick", new LayoutHorizontal(0, 10)); removeChild(basicChild); setLayout(new LayoutAlign(0, 0.5f, 10)); @@ -103,16 +103,17 @@ public class RadioButton extends BasicButton { group.addChild(new Tick()); group.addChild(basicChild); addChild(group); - + addListener(KeyEvent.class, e -> { - if (e.isRelease()) return false; - + if (e.isRelease()) + return false; + if (e.getKey() == GLFW.GLFW_KEY_LEFT || e.getKey() == GLFW.GLFW_KEY_UP) { if (this.group != null) { this.group.selectPrevious(); this.group.getSelected().takeFocus(); } - + return true; } else if (e.getKey() == GLFW.GLFW_KEY_RIGHT || e.getKey() == GLFW.GLFW_KEY_DOWN) { if (this.group != null) { @@ -121,85 +122,87 @@ public class RadioButton extends BasicButton { } return true; } - + return false; }); - + addAction(b -> setChecked(true)); } - + public RadioButton(String name, String label, Font labelFont) { this(name, label, labelFont, false); } - + public RadioButton(String name, String label, boolean check) { this(name, label, new Font(), check); } - + public RadioButton(String name, String label) { this(name, label, false); } - + /** * @param group the group to set */ public RadioButton setGroup(RadioButtonGroup group) { - + if (this.group != null) { group.selectNext(); removeAction(group.listener); group.buttons.remove(this); - group.getSelected(); // Clear reference if this was the only button in the group + group.getSelected(); // Clear reference if this was the only button + // in the group } - + this.group = group; - + if (this.group != null) { group.buttons.add(this); addAction(group.listener); } - + setChecked(false); - + return this; } - + /** * @return the checked */ public boolean isChecked() { return checked; } - + /** * @param checked the checked to set */ public void setChecked(boolean checked) { this.checked = checked; - + if (group != null) { - group.listener.accept(this); // Failsafe for manual invocations of setChecked() + group.listener.accept(this); // Failsafe for manual invocations of + // setChecked() } } @Override protected void assembleSelf(RenderTarget target) { // Change label font color - + if (isPressed()) { getLabel().setFont(getLabel().getFont().withColor(Colors.BLUE)); } else { getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK)); } } - + @Override protected void postAssembleSelf(RenderTarget target) { // Apply disable tint - + if (!isEnabled()) { target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF)); } } - + } diff --git a/src/main/java/ru/windcorp/progressia/common/util/HashableVec3i.java b/src/main/java/ru/windcorp/progressia/common/util/HashableVec3i.java new file mode 100644 index 0000000..b94f79d --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/HashableVec3i.java @@ -0,0 +1,35 @@ +package ru.windcorp.progressia.common.util; + +import glm.vec._3.i.Vec3i; + +public class HashableVec3i { + + public Vec3i value; + + public HashableVec3i(Vec3i inValue) + { + value = inValue; + } + + @Override + public int hashCode() // Uses first 3 primes greater than 2**30 + { + return 1073741827 * value.x + 1073741831 * value.y + 1073741833 * value.z; + } + + @Override + public boolean equals(Object comparee) + { + if (comparee == null) + { + return false; + } + if (comparee.getClass() != HashableVec3i.class) + { + return false; + } + HashableVec3i compareeCast = (HashableVec3i) comparee; + return compareeCast.value == value; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java index f762778..a7184e9 100644 --- a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java +++ b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java @@ -27,6 +27,7 @@ import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityDataRegistry; import ru.windcorp.progressia.server.events.PlayerJoinedEvent; +import ru.windcorp.progressia.server.events.PlayerLeftEvent; import ru.windcorp.progressia.test.TestContent; public class PlayerManager { @@ -47,6 +48,11 @@ public class PlayerManager { this.players.add(player); getServer().postEvent(new PlayerJoinedEvent.Immutable(getServer(), player)); } + + public void removePlayer(Player player) { + this.players.remove(player); + getServer().postEvent(new PlayerLeftEvent.Immutable(getServer(), player)); + } public EntityData conjurePlayerEntity(String login) { // TODO Live up to the name diff --git a/src/main/java/ru/windcorp/progressia/server/ServerThread.java b/src/main/java/ru/windcorp/progressia/server/ServerThread.java index 90f3ce9..aa0ff67 100644 --- a/src/main/java/ru/windcorp/progressia/server/ServerThread.java +++ b/src/main/java/ru/windcorp/progressia/server/ServerThread.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.server; import java.util.concurrent.Executors; @@ -26,10 +26,13 @@ import org.apache.logging.log4j.LogManager; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.server.world.ticking.TickerCoordinator; +@SuppressWarnings("unused") public class ServerThread implements Runnable { private static final ThreadLocal SERVER_THREADS_MAP = new ThreadLocal<>(); + private static boolean isShuttingDown; + public static Server getCurrentServer() { return SERVER_THREADS_MAP.get(); } @@ -63,6 +66,7 @@ public class ServerThread implements Runnable { } public void start() { + isShuttingDown = false; ticker.start(); executor.scheduleAtFixedRate(this, 0, 1000 / 20, TimeUnit.MILLISECONDS); } @@ -70,6 +74,11 @@ public class ServerThread implements Runnable { @Override public void run() { try { + if (isShuttingDown) { + getTicker().stop(); + executor.shutdown(); + return; + } server.tick(); ticker.runOneTick(); } catch (Throwable e) { @@ -78,13 +87,10 @@ public class ServerThread implements Runnable { } public void stop() { - try { - executor.awaitTermination(10, TimeUnit.MINUTES); - } catch (InterruptedException e) { - LogManager.getLogger().warn("Received interrupt in ServerThread.stop(), aborting wait"); - } - getTicker().stop(); + isShuttingDown = true; + + // getTicker().stop(); } public Server getServer() { diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java index 935e846..fc55cbb 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -92,6 +92,9 @@ public class ClientManager { public void disconnectClient(Client client) { client.disconnect(); clientsById.remove(client.getId()); + if (client instanceof ClientPlayer) { + getServer().getPlayerManager().removePlayer(((ClientPlayer) client).getPlayer()); + } } public void processPackets() { diff --git a/src/main/java/ru/windcorp/progressia/test/LayerButtonTest.java b/src/main/java/ru/windcorp/progressia/test/LayerButtonTest.java index e505291..a9812ea 100644 --- a/src/main/java/ru/windcorp/progressia/test/LayerButtonTest.java +++ b/src/main/java/ru/windcorp/progressia/test/LayerButtonTest.java @@ -17,7 +17,11 @@ */ package ru.windcorp.progressia.test; +import java.util.Collection; + +import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.graphics.Colors; +import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.gui.Button; import ru.windcorp.progressia.client.graphics.gui.Checkbox; @@ -25,45 +29,71 @@ import ru.windcorp.progressia.client.graphics.gui.Label; import ru.windcorp.progressia.client.graphics.gui.RadioButton; import ru.windcorp.progressia.client.graphics.gui.RadioButtonGroup; import ru.windcorp.progressia.client.graphics.gui.menu.MenuLayer; +import ru.windcorp.progressia.client.localization.MutableStringLocalized; +import ru.windcorp.progressia.server.Player; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.ServerState; public class LayerButtonTest extends MenuLayer { + boolean alive = true; + public LayerButtonTest() { super("ButtonTest"); - + addTitle(); - + Button blockableButton; getContent().addChild((blockableButton = new Button("BlockableButton", "Blockable")).addAction(b -> { System.out.println("Button Blockable!"); })); blockableButton.setEnabled(false); - + getContent().addChild(new Checkbox("EnableButton", "Enable").addAction(b -> { blockableButton.setEnabled(((Checkbox) b).isChecked()); })); - + RadioButtonGroup group = new RadioButtonGroup().addAction(g -> { System.out.println("RBG! " + g.getSelected().getLabel().getCurrentText()); }); - + getContent().addChild(new RadioButton("RB1", "Moon").setGroup(group)); getContent().addChild(new RadioButton("RB2", "Type").setGroup(group)); getContent().addChild(new RadioButton("RB3", "Ice").setGroup(group)); getContent().addChild(new RadioButton("RB4", "Cream").setGroup(group)); - + getContent().getChild(getContent().getChildren().size() - 1).setEnabled(false); - + getContent().addChild(new Label("Hint", new Font().withColor(Colors.LIGHT_GRAY), "This is a MenuLayer")); - + getContent().addChild(new Button("Continue", "Continue").addAction(b -> { getCloseAction().run(); })); - - getContent().addChild(new Button("Quit", "Quit").addAction(b -> { - System.exit(0); + + getContent().addChild(new Button("Menu", "Back To Menu").addAction(b -> { + getCloseAction().run(); + + Collection players = ServerState.getInstance().getPlayerManager().getPlayers(); + players.clear(); + + ClientState.disconnectFromLocalServer(); + + GUI.addTopLayer(new LayerTestText("Text", new MutableStringLocalized("LayerText.Save"), layer -> { + Server server = ServerState.getInstance(); + if (server != null && server.getWorld().getChunks().isEmpty()) { + GUI.removeLayer(layer); + + // TODO Refactor, this shouldn't be here + GUI.addTopLayer(new LayerTitle("Title")); + ServerState.getInstance().shutdown("Safe Exit"); + ServerState.setInstance(null); + TestPlayerControls.resetInstance(); + } + })); + + ClientState.setInstance(null); })); - + getContent().takeFocus(); } diff --git a/src/main/java/ru/windcorp/progressia/test/LayerTestText.java b/src/main/java/ru/windcorp/progressia/test/LayerTestText.java new file mode 100644 index 0000000..a4d202b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/LayerTestText.java @@ -0,0 +1,29 @@ +package ru.windcorp.progressia.test; + +import java.util.function.Consumer; +import ru.windcorp.progressia.client.graphics.Colors; +import ru.windcorp.progressia.client.graphics.font.Font; +import ru.windcorp.progressia.client.graphics.gui.GUILayer; +import ru.windcorp.progressia.client.graphics.gui.Label; +import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; +import ru.windcorp.progressia.client.localization.MutableString; + +public class LayerTestText extends GUILayer { + + private final Consumer remover; + + public LayerTestText(String name, MutableString value, Consumer remover) { + super(name, new LayoutAlign(15)); + this.remover = remover; + + Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(0.5f); + getRoot().addChild(new Label(name + ".Text", titleFont, value)); + } + + @Override + protected void doRender() { + super.doRender(); + remover.accept(this); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/LayerTitle.java b/src/main/java/ru/windcorp/progressia/test/LayerTitle.java new file mode 100644 index 0000000..d2ba139 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/LayerTitle.java @@ -0,0 +1,37 @@ +package ru.windcorp.progressia.test; + +import ru.windcorp.progressia.ProgressiaLauncher; +import ru.windcorp.progressia.client.graphics.Colors; +import ru.windcorp.progressia.client.graphics.GUI; +import ru.windcorp.progressia.client.graphics.font.Font; +import ru.windcorp.progressia.client.graphics.gui.Button; +import ru.windcorp.progressia.client.graphics.gui.GUILayer; +import ru.windcorp.progressia.client.graphics.gui.Label; +import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical; +import ru.windcorp.progressia.client.localization.MutableString; +import ru.windcorp.progressia.client.localization.MutableStringLocalized; + +public class LayerTitle extends GUILayer { + + public LayerTitle(String name) { + super(name, new LayoutVertical(20, 10)); + + MutableString title = new MutableStringLocalized("Layer" + name + ".Title"); + Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(0.5f); + getRoot().addChild(new Label(name + ".Title", titleFont, title)); + + Font buttonFont = titleFont; + MutableString playText = new MutableStringLocalized("Layer" + name + ".Play"); + getRoot().addChild(new Button(name + ".Play", new Label(name + ".Play", buttonFont, playText)).addAction(b -> { + GUI.removeLayer(this); + + ProgressiaLauncher.play(); + })); + + MutableString quitText = new MutableStringLocalized("Layer" + name + ".Quit"); + getRoot().addChild(new Button(name + "Quit", new Label(name + ".Quit", buttonFont, quitText)).addAction(b -> { + System.exit(0); + })); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index ee42396..4ca0ea6 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -46,10 +46,10 @@ import ru.windcorp.progressia.server.ServerState; public class TestPlayerControls { - private static final TestPlayerControls INSTANCE = new TestPlayerControls(); + private static TestPlayerControls instance = new TestPlayerControls(); public static TestPlayerControls getInstance() { - return INSTANCE; + return instance; } private static final double MODE_SWITCH_MAX_DELAY = 300 * Units.MILLISECONDS; @@ -90,6 +90,10 @@ public class TestPlayerControls { private LayerTestGUI debugLayer = null; private Runnable updateCallback = null; + + public static void resetInstance() { + instance = new TestPlayerControls(); + } public void applyPlayerControls() { if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) { diff --git a/src/main/java/ru/windcorp/progressia/test/TestWorldDiskIO.java b/src/main/java/ru/windcorp/progressia/test/TestWorldDiskIO.java index ebfafde..b79a90e 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestWorldDiskIO.java +++ b/src/main/java/ru/windcorp/progressia/test/TestWorldDiskIO.java @@ -15,17 +15,30 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package ru.windcorp.progressia.test; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Scanner; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; @@ -34,6 +47,7 @@ import org.apache.logging.log4j.Logger; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.state.IOContext; +import ru.windcorp.progressia.common.util.HashableVec3i; import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.DecodingException; import ru.windcorp.progressia.common.world.DefaultWorldData; @@ -42,47 +56,342 @@ import ru.windcorp.progressia.server.Server; public class TestWorldDiskIO { - private static final Path SAVE_DIR = Paths.get("tmp_world"); + private static Path SAVE_DIR = Paths.get("tmp_world"); + private static final String formatFile = "world.format"; private static final Logger LOG = LogManager.getLogger("TestWorldDiskIO"); + + private static HashMap mappedByteMap; + private static final boolean ENABLE = true; - private static final boolean ENABLE = false; + private static int maxSize = 1048576; + private static int sectorSize = maxSize / 256; + + private static final int bestFormat = 65536; + + // private Map regions = new HashMap(); + private static Vec3i regionSize; + private static int chunksPerRegion; + private static int offsetBytes; + + private static int currentFormat = -1; + private static String extension = ".null"; + + private static int natFromInt(int loc) { + if (loc < 0) + return -2*loc - 1; + return 2*loc; + } + + /* + * private static int intFromNat(int loc) // Possibly unused + * { + * if ((loc & 1) == 1) + * return -loc >> 1; + * return loc >> 1; + * } + */ + + private static Vec3i getRegion(Vec3i chunkLoc) { + int x = chunkLoc.x; + if (x<0) + { + x /= regionSize.x; + x--; + } + else + { + x /= regionSize.x; + } + int y = chunkLoc.y; + if (y<0) + { + y /= regionSize.y; + y--; + } + else + { + y /= regionSize.y; + } + int z = chunkLoc.z; + if (z<0) + { + z /= regionSize.z; + z--; + } + else + { + z /= regionSize.z; + } + return new Vec3i( + natFromInt(x), + natFromInt(y), + natFromInt(z) + ); + } + + private static int mod(int a, int m) { + return ((a % m) + m) % m; + } + + private static Vec3i getRegionLoc(Vec3i chunkLoc) { + return new Vec3i(mod(chunkLoc.x, regionSize.x), mod(chunkLoc.y, regionSize.y), mod(chunkLoc.z, regionSize.z)); + } + + public static void initRegions() { + initRegions(null); + } + + public static void initRegions(Path worldPath) { + if (worldPath != null) { + SAVE_DIR = worldPath; + } + + // regions.put(new Vec3i(0,0,0), new Vec3i(1,1,1)); + } + + public static int getAvailableSector(MappedByteBuffer mbb) + { + int sectorsUsed = 0; + for (int i=offsetBytes; i<(offsetBytes+1)*chunksPerRegion; i+= (offsetBytes+1)) + { + sectorsUsed += mbb.get(i); + } + return sectorsUsed; + } + + private static void setRegionSize(int format) { + mappedByteMap = new HashMap(); + switch (format) { + case 0: + case 1: + regionSize = new Vec3i(1); + chunksPerRegion = 1; + currentFormat = format; + extension = ".progressia_chunk"; + break; + case 65536: + regionSize = new Vec3i(16); + chunksPerRegion = 16 * 16 * 16; + currentFormat = 65536; + offsetBytes = 3; + extension = ".progressia_region"; + break; + } + } + + /*private static void expand(int sectors) { + + }*/ public static void saveChunk(DefaultChunkData chunk, Server server) { if (!ENABLE) return; try { - LOG.debug( - "Saving {} {} {}", - chunk.getPosition().x, - chunk.getPosition().y, - chunk.getPosition().z - ); - Files.createDirectories(SAVE_DIR); - - Path path = SAVE_DIR.resolve( - String.format( - "chunk_%+d_%+d_%+d.progressia_chunk", + if (currentFormat == 0) { + LOG.debug( + "Saving {} {} {}", chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z - ) - ); + ); + + Files.createDirectories(SAVE_DIR); + + Path path = SAVE_DIR.resolve( + String.format( + "chunk_%+d_%+d_%+d" + extension, + chunk.getPosition().x, + chunk.getPosition().y, + chunk.getPosition().z + ) + ); + + try ( + DataOutputStream output = new DataOutputStream( + new DeflaterOutputStream(new BufferedOutputStream(Files.newOutputStream(path))) + ) + ) { + ChunkIO.save(chunk, output, IOContext.SAVE); + writeGenerationHint(chunk, output, server); + } + } else if (currentFormat == 1) { + LOG.debug( + "Saving {} {} {}", + chunk.getPosition().x, + chunk.getPosition().y, + chunk.getPosition().z + ); + + Files.createDirectories(SAVE_DIR); + + Vec3i saveCoords = getRegion(chunk.getPosition()); + + Path path = SAVE_DIR.resolve( + String.format( + "chunk_%d_%d_%d" + extension, + saveCoords.x, + saveCoords.y, + saveCoords.z + ) + ); + + try ( + DataOutputStream output = new DataOutputStream( + new DeflaterOutputStream(new BufferedOutputStream(Files.newOutputStream(path))) + ) + ) { + ChunkIO.save(chunk, output, IOContext.SAVE); + writeGenerationHint(chunk, output, server); + } + } else if (currentFormat == 65536) { + LOG.debug( + "Saving {} {} {}", + chunk.getPosition().x, + chunk.getPosition().y, + chunk.getPosition().z + ); + + Files.createDirectories(SAVE_DIR); + + Vec3i saveCoords = getRegion(chunk.getPosition()); + + Path path = SAVE_DIR.resolve( + String.format( + "%d_%d_%d" + extension, + saveCoords.x, + saveCoords.y, + saveCoords.z + ) + ); + + /* + * if (!dosave) + * { + * return; + * } + * dosave = false; + */ + + + MappedByteBuffer output = mappedByteMap.get(new HashableVec3i(saveCoords)); + LOG.info("saveCoords {},{},{}", saveCoords.x, saveCoords.y, saveCoords.z); + if (output == null) + { + output = makeNew(path, new HashableVec3i(saveCoords)); + } + // LOG.debug(output.read()); + if (output.get() < 0) { + LOG.info("Making header"); + ByteBuffer headerBytes = ByteBuffer.allocate((offsetBytes + 1) * chunksPerRegion); + for (int i=0;i<(offsetBytes + 1) * chunksPerRegion;i++) + { + headerBytes.put(i, (byte) 0); + } + output.put(headerBytes); + } + + Vec3i pos = getRegionLoc(chunk.getPosition()); + int shortOffset = (offsetBytes + 1) * (pos.z + regionSize.z * (pos.y + regionSize.y * pos.x)); + int fullOffset = (offsetBytes + 1) * (chunksPerRegion); + output.position(shortOffset); + int offset = 0; + for (int i = 0; i < offsetBytes; i++) { + offset *= 256; + offset += output.get(); + } + ByteBuffer readOffset = ByteBuffer.allocate(offsetBytes); + int sectorLength = output.get(); + if (sectorLength == 0) { + //int outputLen = (int) output.size(); + //offset = (int) (outputLen - fullOffset) / sectorSize + 1; + offset = getAvailableSector(output); + output.position(shortOffset); + + //readInt.putInt(offset<<8); + for (int i=0;i> 24); + LOG.debug(bfClone >> 24); + bfClone = bfClone << 8; + } + + /* + * bw.write( + * new char[] { + * (char) bestFormat / (256 * 256 * 256), + * (char) (bestFormat % 256) / (256 * 256), + * (char) (bestFormat % (256 * 256)) / (256), + * (char) (bestFormat % (256 * 256 * 256)) } + * ); + */ + + bw.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } } - try { - DefaultChunkData result = load(path, chunkPos, world, server); + if (currentFormat == 0) { - LOG.debug( - "Loaded {} {} {}", - chunkPos.x, - chunkPos.y, - chunkPos.z + Path path = SAVE_DIR.resolve( + String.format( + "chunk_%+d_%+d_%+d" + extension, + chunkPos.x, + chunkPos.y, + chunkPos.z + ) ); - return result; - } catch (Exception e) { - e.printStackTrace(); - LOG.debug( - "Could not load {} {} {}", - chunkPos.x, - chunkPos.y, - chunkPos.z + if (!Files.exists(path)) { + LOG.debug( + "Not found {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + + return null; + } + + try { + DefaultChunkData result = load(path, chunkPos, world, server); + + LOG.debug( + "Loaded {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + + return result; + } catch (Exception e) { + e.printStackTrace(); + LOG.debug( + "Could not load {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + return null; + } + } else if (currentFormat == 1) { + Vec3i saveCoords = getRegion(chunkPos); + + Path path = SAVE_DIR.resolve( + String.format( + "chunk_%d_%d_%d" + extension, + saveCoords.x, + saveCoords.y, + saveCoords.z + ) ); - return null; + + if (!Files.exists(path)) { + LOG.debug( + "Not found {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + + return null; + } + + try { + DefaultChunkData result = load(path, chunkPos, world, server); + + LOG.debug( + "Loaded {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + + return result; + } catch (Exception e) { + e.printStackTrace(); + LOG.debug( + "Could not load {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + return null; + } + } else if (currentFormat == 65536) { + Vec3i saveCoords = getRegion(chunkPos); + + Path path = SAVE_DIR.resolve( + String.format( + "%d_%d_%d" + extension, + saveCoords.x, + saveCoords.y, + saveCoords.z + ) + ); + + if (!Files.exists(path)) { + LOG.debug( + "Not found {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + + return null; + } + + try { + DefaultChunkData result = loadRegion(path, chunkPos, world, server); + + LOG.debug( + "Loaded {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + + return result; + } catch (Exception e) { + e.printStackTrace(); + LOG.debug( + "Could not load {} {} {}", + chunkPos.x, + chunkPos.y, + chunkPos.z + ); + return null; + } } + return null; } private static DefaultChunkData load(Path path, Vec3i chunkPos, DefaultWorldData world, Server server) @@ -149,6 +608,56 @@ public class TestWorldDiskIO { } } + private static DefaultChunkData loadRegion(Path path, Vec3i chunkPos, DefaultWorldData world, Server server) + throws IOException, + DecodingException { + try + { + Vec3i streamCoords = getRegion(chunkPos); + + MappedByteBuffer input = mappedByteMap.get(new HashableVec3i(streamCoords)); + LOG.info("streamCoords {},{},{}", streamCoords.x,streamCoords.y,streamCoords.z); + if (input == null) + { + //input = new RandomAccessFile(path.toFile(), "rw"); + //input = Files.newByteChannel(path); + input = makeNew(path, new HashableVec3i(streamCoords)); + } + + // LOG.info(path.toString()); + Vec3i pos = getRegionLoc(chunkPos); + + int shortOffset = (offsetBytes + 1) * (pos.z + regionSize.z * (pos.y + regionSize.y * pos.x)); + int fullOffset = (offsetBytes + 1) * (chunksPerRegion); + input.position(shortOffset); + int offset = 0; + for (int i = 0; i < offsetBytes; i++) { + offset *= 256; + offset += input.get(); + } + int sectorLength = input.get(); + input.position(fullOffset + sectorSize * offset); + + // LOG.info("Read {} sectors", sectorLength); + + byte tempData[] = new byte[sectorSize * sectorLength]; + input.get(tempData); + + DataInputStream trueInput = new DataInputStream( + new InflaterInputStream(new BufferedInputStream(new ByteArrayInputStream(tempData))) + ); + DefaultChunkData chunk = ChunkIO.load(world, chunkPos, trueInput, IOContext.SAVE); + readGenerationHint(chunk, trueInput, server); + return chunk; + } + catch (EOFException e) + { + LOG.warn("Reached end of file"); + e.printStackTrace(); + } + return null; + } + private static void readGenerationHint(DefaultChunkData chunk, DataInputStream input, Server server) throws IOException, DecodingException { diff --git a/src/main/resources/assets/languages/en-US.lang b/src/main/resources/assets/languages/en-US.lang index e2ff70b..e6ab183 100644 --- a/src/main/resources/assets/languages/en-US.lang +++ b/src/main/resources/assets/languages/en-US.lang @@ -21,4 +21,13 @@ LayerTestGUI.PlacementModeHint = (Blocks %s Tiles: Ctrl + Mouse Wheel) LayerTestGUI.IsFullscreen = Fullscreen: %5s (F11) LayerTestGUI.IsVSync = VSync: %5s (F12) -LayerButtonTest.Title = Button Test \ No newline at end of file +LayerButtonTest.Title = Button Test +LayerButtonTest.Return = Back To Menu + +LayerTitle.Title = Progressia +LayerTitle.Play = Play World +LayerTitle.Options = Options +LayerTitle.Quit = Quit + +LayerText.Load = Loading... +LayerText.Save = Saving... \ No newline at end of file diff --git a/src/main/resources/assets/languages/ru-RU.lang b/src/main/resources/assets/languages/ru-RU.lang index 97935d6..09c9c33 100644 --- a/src/main/resources/assets/languages/ru-RU.lang +++ b/src/main/resources/assets/languages/ru-RU.lang @@ -21,4 +21,13 @@ LayerTestGUI.PlacementModeHint = (Блок %s плитки: Ctrl + прокру LayerTestGUI.IsFullscreen = Полный экран: %5s (F11) LayerTestGUI.IsVSync = Верт. синхр.: %5s (F12) -LayerButtonTest.Title = Тест Кнопок \ No newline at end of file +LayerButtonTest.Title = Тест Кнопок +LayerButtonTest.Return = Back To Menu [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + +LayerTitle.Title = Прогрессия +LayerTitle.Play = ??????? +LayerTitle.Options = ???????? +LayerTitle.Quit = ???????? + +LayerText.Load = Loading... (Change) +LayerText.Save = Saving...(Cahnsf) \ No newline at end of file diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index ce70734..b249e72 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -26,6 +26,7 @@ --> +