Merge branch 'save-world'
This commit is contained in:
commit
409bbdb680
@ -15,21 +15,31 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package ru.windcorp.progressia;
|
||||
|
||||
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 Proxy proxy;
|
||||
|
||||
public static void launch(String[] args, Proxy proxy) {
|
||||
arguments = args.clone();
|
||||
setupCrashReports();
|
||||
|
||||
proxy.initialize();
|
||||
ProgressiaLauncher.proxy = proxy;
|
||||
GUI.addTopLayer(new LayerTitle("Title"));
|
||||
}
|
||||
|
||||
public static Proxy getProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
private static void setupCrashReports() {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package ru.windcorp.progressia.client;
|
||||
|
||||
import ru.windcorp.progressia.Proxy;
|
||||
@ -30,7 +30,6 @@ 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;
|
||||
import ru.windcorp.progressia.test.TestContent;
|
||||
import ru.windcorp.progressia.test.TestMusicPlayer;
|
||||
|
||||
@ -38,7 +37,9 @@ public class ClientProxy implements Proxy {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
|
||||
GraphicsBackend.initialize();
|
||||
|
||||
try {
|
||||
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
|
||||
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||
@ -58,10 +59,6 @@ public class ClientProxy implements Proxy {
|
||||
|
||||
AudioSystem.initialize();
|
||||
|
||||
ServerState.startServer();
|
||||
ClientState.connectToLocalServer();
|
||||
|
||||
TestMusicPlayer.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,15 +15,18 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
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() {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package ru.windcorp.progressia.client.comms.localhost;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -15,12 +15,13 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,12 +15,13 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Consumer<BasicButton>> 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);
|
||||
@ -121,7 +125,7 @@ public abstract class BasicButton extends Component {
|
||||
if (this.isPressed != isPressed) {
|
||||
this.isPressed = isPressed;
|
||||
requestReassembly();
|
||||
|
||||
|
||||
if (isPressed) {
|
||||
takeFocus();
|
||||
}
|
||||
@ -129,16 +133,16 @@ public abstract class BasicButton extends Component {
|
||||
dispatchEvent(ButtonEvent.create(this, this.isPressed));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BasicButton addAction(Consumer<BasicButton> action) {
|
||||
this.actions.add(Objects.requireNonNull(action, "action"));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public boolean removeAction(Consumer<BasicButton> action) {
|
||||
return this.actions.remove(action);
|
||||
}
|
||||
|
||||
|
||||
public void virtualClick() {
|
||||
this.actions.forEach(action -> {
|
||||
action.accept(this);
|
||||
@ -148,5 +152,5 @@ public abstract class BasicButton extends Component {
|
||||
public Label getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -162,5 +162,72 @@ public class Coordinates {
|
||||
public static boolean isOnChunkBorder(int blockInChunk) {
|
||||
return blockInChunk == 0 || blockInChunk == BLOCKS_PER_CHUNK - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generalized versions
|
||||
*/
|
||||
|
||||
public static int convertGlobalToCell(int bits, int global) {
|
||||
return global >> bits;
|
||||
}
|
||||
|
||||
public static Vec3i convertGlobalToCell(
|
||||
int bits,
|
||||
Vec3i global,
|
||||
Vec3i output
|
||||
) {
|
||||
if (output == null)
|
||||
output = new Vec3i();
|
||||
|
||||
output.x = convertGlobalToCell(bits, global.x);
|
||||
output.y = convertGlobalToCell(bits, global.y);
|
||||
output.z = convertGlobalToCell(bits, global.z);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static int convertGlobalToInCell(int bits, int global) {
|
||||
int mask = (1 << bits) - 1;
|
||||
return global & mask;
|
||||
}
|
||||
|
||||
public static Vec3i convertGlobalToInCell(
|
||||
int bits,
|
||||
Vec3i global,
|
||||
Vec3i output
|
||||
) {
|
||||
if (output == null)
|
||||
output = new Vec3i();
|
||||
|
||||
output.x = convertGlobalToInCell(bits, global.x);
|
||||
output.y = convertGlobalToInCell(bits, global.y);
|
||||
output.z = convertGlobalToInCell(bits, global.z);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static int getGlobal(int bits, int cell, int inCell) {
|
||||
return inCell | (cell << bits);
|
||||
}
|
||||
|
||||
public static Vec3i getGlobal(
|
||||
int bits,
|
||||
Vec3i cell,
|
||||
Vec3i inCell,
|
||||
Vec3i output
|
||||
) {
|
||||
if (output == null)
|
||||
output = new Vec3i();
|
||||
|
||||
output.x = getGlobal(bits, cell.x, inCell.x);
|
||||
output.y = getGlobal(bits, cell.y, inCell.y);
|
||||
output.z = getGlobal(bits, cell.z, inCell.z);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static boolean isOnCellBorder(int bits, int inCell) {
|
||||
return inCell == 0 || inCell == (1 << bits) - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,20 +15,21 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package ru.windcorp.progressia.server;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityDataRegistry;
|
||||
import ru.windcorp.progressia.server.comms.ClientPlayer;
|
||||
import ru.windcorp.progressia.server.events.PlayerJoinedEvent;
|
||||
import ru.windcorp.progressia.server.events.PlayerLeftEvent;
|
||||
import ru.windcorp.progressia.test.TestContent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
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.test.TestContent;
|
||||
|
||||
public class PlayerManager {
|
||||
|
||||
private final Server server;
|
||||
@ -48,30 +49,37 @@ public class PlayerManager {
|
||||
getServer().postEvent(new PlayerJoinedEvent.Immutable(getServer(), player));
|
||||
}
|
||||
|
||||
public EntityData conjurePlayerEntity(String login) {
|
||||
// TODO Live up to the name
|
||||
if (TestContent.PLAYER_LOGIN.equals(login)) {
|
||||
EntityData entity = spawnPlayerEntity(login);
|
||||
return entity;
|
||||
} else {
|
||||
throw CrashReports.report(null, "Unknown login %s, javahorse stupid", login);
|
||||
}
|
||||
public void removePlayer(Player player) {
|
||||
server.getWorld().getContainer().savePlayer(player, server);
|
||||
this.players.remove(player);
|
||||
getServer().postEvent(new PlayerLeftEvent.Immutable(getServer(), player));
|
||||
}
|
||||
|
||||
private EntityData spawnPlayerEntity(String login) {
|
||||
public Player conjurePlayer(ClientPlayer clientPlayer, String login) {
|
||||
|
||||
Player player = getServer().getWorld().getContainer().loadPlayer(login, clientPlayer, getServer());
|
||||
if (player == null) { // create new player
|
||||
EntityData entity = spawnPlayerEntity(clientPlayer, login);
|
||||
player = new Player(entity, getServer(), clientPlayer);
|
||||
}
|
||||
|
||||
getServer().getWorld().getData().addEntity(player.getEntity());
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
private EntityData spawnPlayerEntity(ClientPlayer clientPlayer, String login) {
|
||||
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
||||
|
||||
player.setEntityId(TestContent.PLAYER_ENTITY_ID);
|
||||
player.setPosition(getServer().getWorld().getGenerator().suggestSpawnLocation());
|
||||
|
||||
|
||||
player.setUpVector(new Vec3(0, 0, 1));
|
||||
player.setLookingAt(new Vec3(2, 1, 0));
|
||||
|
||||
getServer().getWorld().getData().addEntity(player);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
|
||||
public Object getMutex() {
|
||||
return players;
|
||||
}
|
||||
|
@ -21,8 +21,6 @@ package ru.windcorp.progressia.server;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
@ -46,6 +44,7 @@ import ru.windcorp.progressia.server.world.context.impl.DefaultServerContext;
|
||||
import ru.windcorp.progressia.server.world.context.impl.ReportingServerContext;
|
||||
import ru.windcorp.progressia.server.world.context.impl.RotatingServerContext;
|
||||
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
|
||||
import ru.windcorp.progressia.server.world.io.WorldContainer;
|
||||
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
|
||||
import ru.windcorp.progressia.server.world.ticking.Change;
|
||||
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||
@ -78,11 +77,16 @@ public class Server {
|
||||
|
||||
private final TickingSettings tickingSettings = new TickingSettings();
|
||||
|
||||
public Server(DefaultWorldData world, Function<Server, WorldGenerator> generatorCreator) {
|
||||
public Server(
|
||||
DefaultWorldData world,
|
||||
Function<Server, WorldGenerator> generatorCreator,
|
||||
WorldContainer worldContainer
|
||||
) {
|
||||
this.world = new DefaultWorldLogic(
|
||||
world,
|
||||
this,
|
||||
generatorCreator.apply(this),
|
||||
worldContainer,
|
||||
worldAccessor
|
||||
);
|
||||
this.serverThread = new ServerThread(this);
|
||||
@ -241,14 +245,14 @@ public class Server {
|
||||
public void scheduleChange(Change change) {
|
||||
serverThread.getTicker().requestChange(change);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delayed
|
||||
*/
|
||||
public void scheduleEvaluation(Evaluation evaluation) {
|
||||
serverThread.getTicker().requestEvaluation(evaluation);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Immediate if possible, otherwise delayed
|
||||
*/
|
||||
@ -347,8 +351,8 @@ public class Server {
|
||||
* reason
|
||||
*/
|
||||
public void shutdown(String message) {
|
||||
LogManager.getLogger().warn("Server.shutdown() is not yet implemented");
|
||||
serverThread.stop();
|
||||
getWorld().getContainer().close();
|
||||
}
|
||||
|
||||
private void scheduleWorldTicks(Server server) {
|
||||
|
@ -18,7 +18,14 @@
|
||||
|
||||
package ru.windcorp.progressia.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.function.Function;
|
||||
|
||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
|
||||
import ru.windcorp.progressia.server.world.io.WorldContainer;
|
||||
import ru.windcorp.progressia.server.world.io.region.RegionFormat;
|
||||
import ru.windcorp.progressia.test.gen.TestGenerationConfig;
|
||||
|
||||
public class ServerState {
|
||||
@ -33,10 +40,15 @@ public class ServerState {
|
||||
ServerState.instance = instance;
|
||||
}
|
||||
|
||||
public static void startServer() {
|
||||
Server server = new Server(new DefaultWorldData(), TestGenerationConfig.createGenerator());
|
||||
public static void startServer() throws IOException {
|
||||
|
||||
Function<Server, WorldGenerator> generator = new TestGenerationConfig().getGenerator();
|
||||
WorldContainer container = new RegionFormat("Test:Region").create(Paths.get("tmp_world"));
|
||||
|
||||
Server server = new Server(new DefaultWorldData(), generator, container);
|
||||
setInstance(server);
|
||||
server.start();
|
||||
|
||||
}
|
||||
|
||||
private ServerState() {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
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> 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() {
|
||||
|
@ -31,7 +31,6 @@ import ru.windcorp.progressia.common.comms.CommsChannel.State;
|
||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||
import ru.windcorp.progressia.common.world.PacketSetGravityModel;
|
||||
import ru.windcorp.progressia.common.world.PacketSetLocalPlayer;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.server.Player;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
|
||||
@ -80,18 +79,20 @@ public class ClientManager {
|
||||
setGravityModelPacket.set(getServer().getWorld().getData().getGravityModel());
|
||||
client.sendPacket(setGravityModelPacket);
|
||||
|
||||
EntityData entity = getServer().getPlayerManager().conjurePlayerEntity(login);
|
||||
Player player = new Player(entity, getServer(), client);
|
||||
Player player = getServer().getPlayerManager().conjurePlayer(client, login);
|
||||
getServer().getPlayerManager().addPlayer(player);
|
||||
|
||||
PacketSetLocalPlayer packet = new PacketSetLocalPlayer();
|
||||
packet.set(entity.getEntityId());
|
||||
packet.set(player.getEntity().getEntityId());
|
||||
client.sendPacket(packet);
|
||||
}
|
||||
|
||||
public void disconnectClient(Client client) {
|
||||
client.disconnect();
|
||||
clientsById.remove(client.getId());
|
||||
if (client instanceof ClientPlayer) {
|
||||
getServer().getPlayerManager().removePlayer(((ClientPlayer) client).getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
public void processPackets() {
|
||||
|
@ -28,7 +28,7 @@ import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||
import ru.windcorp.progressia.server.Player;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.test.TestWorldDiskIO;
|
||||
import ru.windcorp.progressia.server.world.io.WorldContainer;
|
||||
|
||||
/**
|
||||
* Chunk manager provides facilities to load, unload and generate chunks for a
|
||||
@ -55,6 +55,10 @@ public class ChunkManager {
|
||||
public Server getServer() {
|
||||
return getLoadManager().getServer();
|
||||
}
|
||||
|
||||
public WorldContainer getContainer() {
|
||||
return getServer().getWorld().getContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the result of an attempt to load a chunk.
|
||||
@ -119,7 +123,7 @@ public class ChunkManager {
|
||||
|
||||
DefaultWorldData world = getServer().getWorld().getData();
|
||||
|
||||
DefaultChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world, getServer());
|
||||
DefaultChunkData chunk = getServer().getWorld().getContainer().load(chunkPos, world, getServer());
|
||||
if (chunk != null) {
|
||||
world.addChunk(chunk);
|
||||
return LoadResult.LOADED_FROM_DISK;
|
||||
@ -150,7 +154,7 @@ public class ChunkManager {
|
||||
entitiesToRemove.forEach(world::removeEntity);
|
||||
|
||||
world.removeChunk(chunk);
|
||||
TestWorldDiskIO.saveChunk(chunk, getServer());
|
||||
getContainer().save(chunk, getServer().getWorld().getData(), getServer());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import ru.windcorp.progressia.common.world.WorldDataListener;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
|
||||
import ru.windcorp.progressia.server.world.io.WorldContainer;
|
||||
import ru.windcorp.progressia.server.world.tasks.TickEntitiesTask;
|
||||
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
|
||||
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||
@ -43,17 +44,20 @@ public class DefaultWorldLogic implements WorldLogic {
|
||||
private final Server server;
|
||||
|
||||
private final WorldGenerator generator;
|
||||
private final WorldContainer container;
|
||||
|
||||
private final Map<DefaultChunkData, DefaultChunkLogic> chunks = new HashMap<>();
|
||||
|
||||
private final Evaluation tickEntitiesTask = new TickEntitiesTask();
|
||||
|
||||
public DefaultWorldLogic(DefaultWorldData data, Server server, WorldGenerator generator, WorldAccessor accessor) {
|
||||
public DefaultWorldLogic(DefaultWorldData data, Server server, WorldGenerator generator, WorldContainer container, WorldAccessor accessor) {
|
||||
this.data = data;
|
||||
this.server = server;
|
||||
|
||||
this.generator = generator;
|
||||
data.setGravityModel(getGenerator().getGravityModel());
|
||||
|
||||
this.container = container;
|
||||
|
||||
data.addListener(new WorldDataListener() {
|
||||
@Override
|
||||
@ -127,6 +131,10 @@ public class DefaultWorldLogic implements WorldLogic {
|
||||
public WorldGenerator getGenerator() {
|
||||
return generator;
|
||||
}
|
||||
|
||||
public WorldContainer getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
public DefaultChunkData generate(Vec3i chunkPos) {
|
||||
DefaultChunkData chunk = getGenerator().generate(chunkPos);
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.server.world.io;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||
import ru.windcorp.progressia.server.Player;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.comms.ClientPlayer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface WorldContainer {
|
||||
|
||||
Path getPath();
|
||||
|
||||
DefaultChunkData load(Vec3i position, DefaultWorldData world, Server server);
|
||||
|
||||
void save(DefaultChunkData chunk, DefaultWorldData world, Server server);
|
||||
|
||||
Player loadPlayer(String login, ClientPlayer clientPlayer, Server server);
|
||||
|
||||
void savePlayer(Player player, Server server);
|
||||
|
||||
void close();
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.server.world.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
|
||||
public abstract class WorldContainerFormat extends Namespaced {
|
||||
|
||||
public WorldContainerFormat(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public abstract WorldContainer create(Path directory) throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.server.world.io.region;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMap;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMaps;
|
||||
import ru.windcorp.progressia.common.world.io.ChunkIO;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
|
||||
public class Region {
|
||||
|
||||
private static final boolean RESET_CORRUPTED = true;
|
||||
|
||||
public int loadedChunks;
|
||||
|
||||
private AtomicBoolean isUsing = new AtomicBoolean(false);
|
||||
private AtomicBoolean isClosed = new AtomicBoolean(false);
|
||||
|
||||
private final RegionFile file;
|
||||
|
||||
private final ChunkMap<Integer> offsets = ChunkMaps.newHashMap();
|
||||
|
||||
public Region(RandomAccessFile file, Vec3i regionCoords) throws IOException {
|
||||
this.file = new RegionFile(file);
|
||||
|
||||
try {
|
||||
this.file.confirmHeaderHealth(offsets, regionCoords);
|
||||
} catch (IOException e) {
|
||||
|
||||
RegionWorldContainer.LOG.debug("Uh the file broke");
|
||||
if (RESET_CORRUPTED) {
|
||||
this.file.makeHeader(regionCoords);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public RegionFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.file.close();
|
||||
isClosed.lazySet(true);
|
||||
}
|
||||
|
||||
public int getOffset(Vec3i chunkLoc) {
|
||||
return offsets.get(chunkLoc);
|
||||
}
|
||||
|
||||
public boolean hasOffset(Vec3i pos) {
|
||||
return offsets.containsKey(pos);
|
||||
}
|
||||
|
||||
public void putOffset(Vec3i pos, int offset) {
|
||||
offsets.put(pos, offset);
|
||||
}
|
||||
|
||||
public AtomicBoolean isClosed() {
|
||||
return isClosed;
|
||||
}
|
||||
|
||||
public AtomicBoolean isUsing() {
|
||||
return isUsing;
|
||||
}
|
||||
|
||||
public void save(DefaultChunkData chunk, Server server) throws IOException {
|
||||
isUsing.set(true);
|
||||
Vec3i pos = RegionWorldContainer.getInRegionCoords(chunk.getPosition());
|
||||
|
||||
if (!hasOffset(pos)) {
|
||||
putOffset(pos, file.allocateChunk(pos));
|
||||
}
|
||||
int dataOffset = getOffset(pos);
|
||||
|
||||
byte[] buffer = saveToBuffer(chunk, server);
|
||||
|
||||
file.writeBuffer(buffer, dataOffset, pos);
|
||||
isUsing.set(false);
|
||||
}
|
||||
|
||||
private byte[] saveToBuffer(DefaultChunkData chunk, Server server) throws IOException {
|
||||
ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
|
||||
try (
|
||||
DataOutputStream dataStream = new DataOutputStream(
|
||||
new DeflaterOutputStream(
|
||||
new BufferedOutputStream(arrayStream)
|
||||
)
|
||||
)
|
||||
) {
|
||||
ChunkIO.save(chunk, dataStream, IOContext.SAVE);
|
||||
RegionWorldContainer.writeGenerationHint(chunk, dataStream, server);
|
||||
}
|
||||
|
||||
return arrayStream.toByteArray();
|
||||
}
|
||||
|
||||
public DefaultChunkData load(Vec3i chunkPos, DefaultWorldData world, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
isUsing.set(true);
|
||||
|
||||
int dataOffset = 0;
|
||||
Vec3i pos = RegionWorldContainer.getInRegionCoords(chunkPos);
|
||||
|
||||
if (hasOffset(pos)) {
|
||||
dataOffset = getOffset(pos);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] buffer = file.readBuffer(dataOffset);
|
||||
DefaultChunkData result = loadFromBuffer(buffer, chunkPos, world, server);
|
||||
isUsing.set(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
private DefaultChunkData loadFromBuffer(byte[] buffer, Vec3i chunkPos, DefaultWorldData world, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
|
||||
DataInputStream dataStream = new DataInputStream(
|
||||
new InflaterInputStream(
|
||||
new BufferedInputStream(
|
||||
new ByteArrayInputStream(buffer)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
DefaultChunkData result = ChunkIO.load(world, chunkPos, dataStream, IOContext.SAVE);
|
||||
RegionWorldContainer.readGenerationHint(result, dataStream, server);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
package ru.windcorp.progressia.server.world.io.region;
|
||||
|
||||
import static ru.windcorp.progressia.server.world.io.region.RegionWorldContainer.REGION_DIAMETER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMap;
|
||||
|
||||
/**
|
||||
* Backend for the .progressia_region file.
|
||||
* Use similarly to a file object
|
||||
*/
|
||||
public class RegionFile {
|
||||
// 4 MiB
|
||||
private static final int MAX_CHUNK_SIZE = 4 * 1024 * 1024;
|
||||
private static final int SECTORS_BYTES = Short.BYTES;
|
||||
private static final int SECTOR_SIZE = MAX_CHUNK_SIZE >> (SECTORS_BYTES * 8);
|
||||
private static final int SECTOR_HEADER_SIZE = 1;
|
||||
private static final int ID_HEADER_SIZE = 16;
|
||||
private static final byte[] HEADER_ID = {'P','R','O','G'};
|
||||
|
||||
final byte endBytes[] = new byte[SECTOR_SIZE];
|
||||
|
||||
public static enum SectorType {
|
||||
Ending(0), // Just an empty block
|
||||
Data(1), // has a byte counting up in position 1, and then
|
||||
PartitionLink(2),
|
||||
BulkSaved(3); // TODO implement this
|
||||
|
||||
private final byte data;
|
||||
|
||||
SectorType(int i) {
|
||||
this.data = (byte) i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final int DEFINITION_SIZE = Integer.BYTES;
|
||||
|
||||
private static final int HEADER_SIZE = DEFINITION_SIZE * REGION_DIAMETER * REGION_DIAMETER * REGION_DIAMETER + ID_HEADER_SIZE;
|
||||
|
||||
private final RandomAccessFile file;
|
||||
|
||||
public RegionFile(RandomAccessFile inFile) {
|
||||
file = inFile;
|
||||
}
|
||||
|
||||
public void confirmHeaderHealth(ChunkMap<Integer> offsets, Vec3i regionCoords) throws IOException {
|
||||
|
||||
Set<Integer> used = new HashSet<Integer>();
|
||||
int maxUsed = 0;
|
||||
final int chunksPerRegion = REGION_DIAMETER * REGION_DIAMETER * REGION_DIAMETER;
|
||||
|
||||
if (file.length() < HEADER_SIZE) {
|
||||
throw new IOException("File is too short to contain a header");
|
||||
}
|
||||
|
||||
|
||||
char prog;
|
||||
for (int i=0;i<4;i++) {
|
||||
prog = file.readChar();
|
||||
if (prog != HEADER_ID[i])
|
||||
{
|
||||
throw new IOException("File is not a .progressia_chunk file");
|
||||
}
|
||||
}
|
||||
|
||||
int tempX = file.readInt();
|
||||
int tempY = file.readInt();
|
||||
int tempZ = file.readInt();
|
||||
|
||||
if (regionCoords.x != tempX || regionCoords.y != tempY || regionCoords.z != tempZ)
|
||||
{
|
||||
throw new IOException("Region file is in the wrong place/ has the wrong name.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < chunksPerRegion; i++) {
|
||||
file.seek(i * DEFINITION_SIZE + ID_HEADER_SIZE);
|
||||
int offset = file.readInt();
|
||||
|
||||
if (offset == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
offset--;
|
||||
|
||||
Vec3i pos = new Vec3i();
|
||||
pos.x = i / REGION_DIAMETER / REGION_DIAMETER;
|
||||
pos.y = (i / REGION_DIAMETER) % REGION_DIAMETER;
|
||||
pos.z = i % REGION_DIAMETER;
|
||||
|
||||
offsets.put(pos, offset);
|
||||
|
||||
boolean shouldEnd = false;
|
||||
byte counter = 0;
|
||||
while (!shouldEnd) {
|
||||
if (offset > maxUsed) {
|
||||
maxUsed = offset;
|
||||
}
|
||||
|
||||
if (!used.add(offset)) {
|
||||
throw new IOException("A sector is used twice");
|
||||
}
|
||||
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * offset);
|
||||
byte type = file.readByte();
|
||||
|
||||
if (type == SectorType.Data.data) {
|
||||
byte fileCounter = file.readByte();
|
||||
if (fileCounter != counter) {
|
||||
throw new IOException("An unexpected block was found");
|
||||
}
|
||||
counter++;
|
||||
offset++;
|
||||
} else if (type == SectorType.Ending.data) {
|
||||
shouldEnd = true;
|
||||
} else if (type == SectorType.PartitionLink.data) {
|
||||
offset = file.readInt();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
LogManager.getLogger("Region").debug("Efficiency of {}", (double) used.size() / maxUsed);
|
||||
}
|
||||
|
||||
public void makeHeader(Vec3i regionCoords) throws IOException {
|
||||
file.seek(0);
|
||||
file.write(HEADER_ID);
|
||||
file.writeInt(regionCoords.x);
|
||||
file.writeInt(regionCoords.y);
|
||||
file.writeInt(regionCoords.z);
|
||||
for (int i = 0; i < HEADER_SIZE; i++) {
|
||||
file.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeBuffer(byte[] buffer, int dataOffset, Vec3i pos) throws IOException {
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * dataOffset);
|
||||
int loc = 0;
|
||||
byte tempBuffer[] = new byte[SECTOR_SIZE];
|
||||
byte counter = 0;
|
||||
boolean isDone = false;
|
||||
while (!isDone) {
|
||||
if (file.length() > HEADER_SIZE + SECTOR_SIZE * (dataOffset + 1)) {
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * (dataOffset + 1));
|
||||
byte header = file.readByte();
|
||||
if (header == SectorType.Data.data) {
|
||||
byte fileCounter = file.readByte();
|
||||
if (fileCounter != counter + 1) // This makes the actual
|
||||
// partition place
|
||||
{
|
||||
int newOffset = allocateEmptySector();
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * dataOffset);
|
||||
file.write(2);
|
||||
file.writeInt(newOffset);
|
||||
dataOffset = newOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
tempBuffer[0] = 1;
|
||||
tempBuffer[1] = counter;
|
||||
counter++;
|
||||
for (int i = 0; i < (SECTOR_SIZE - SECTOR_HEADER_SIZE - 1); i++) {
|
||||
if (loc * (SECTOR_SIZE - SECTOR_HEADER_SIZE - 1) + i < buffer.length) {
|
||||
tempBuffer[i + SECTOR_HEADER_SIZE + 1] = buffer[loc * (SECTOR_SIZE - SECTOR_HEADER_SIZE - 1) + i];
|
||||
} else {
|
||||
isDone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
loc++;
|
||||
if (file.getFilePointer() < 256)
|
||||
LogManager.getLogger("Region")
|
||||
.debug("at {}, ({},{},{}), {}", file.getFilePointer(), pos.x, pos.y, pos.z, dataOffset);
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * dataOffset);
|
||||
dataOffset++;
|
||||
file.write(tempBuffer);
|
||||
}
|
||||
|
||||
file.write(endBytes);
|
||||
}
|
||||
|
||||
public int allocateChunk(Vec3i pos) throws IOException {
|
||||
int definitionOffset = ID_HEADER_SIZE + DEFINITION_SIZE * (pos.z + REGION_DIAMETER * (pos.y + REGION_DIAMETER * pos.x));
|
||||
|
||||
int outputLen = (int) file.length();
|
||||
|
||||
int dataOffset = (int) Math.ceil((double) (outputLen - HEADER_SIZE) / SECTOR_SIZE);
|
||||
|
||||
file.seek(definitionOffset);
|
||||
file.writeInt(dataOffset + 1);
|
||||
|
||||
file.setLength(HEADER_SIZE + dataOffset * SECTOR_SIZE);
|
||||
return dataOffset;
|
||||
}
|
||||
|
||||
private int allocateEmptySector() throws IOException {
|
||||
int outputLen = (int) file.length();
|
||||
|
||||
int dataOffset = (int) Math.ceil((double) (outputLen - HEADER_SIZE) / SECTOR_SIZE);
|
||||
|
||||
file.setLength(HEADER_SIZE + dataOffset * SECTOR_SIZE);
|
||||
|
||||
return dataOffset;
|
||||
}
|
||||
|
||||
public byte[] readBuffer(int dataOffset) throws IOException {
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * dataOffset);
|
||||
|
||||
int bufferPos = 0;
|
||||
byte buffer[] = new byte[SECTOR_SIZE * 16];
|
||||
byte tempBuffer[] = new byte[SECTOR_SIZE];
|
||||
|
||||
boolean reachedEnd = false;
|
||||
byte counter = 0;
|
||||
while (!reachedEnd) {
|
||||
int bytesRead = file.read(tempBuffer, 0, SECTOR_SIZE);
|
||||
if (bytesRead < 0) {
|
||||
reachedEnd = true;
|
||||
continue;
|
||||
}
|
||||
if (tempBuffer[0] == SectorType.Data.data) {
|
||||
if (tempBuffer[1] != counter) {
|
||||
throw new IOException(
|
||||
"Sectors were read out of order\nExpected chunk number " + Byte.toString(counter)
|
||||
+ " but encountered number " + Byte.toString(tempBuffer[1])
|
||||
);
|
||||
}
|
||||
counter++;
|
||||
if (buffer.length - bufferPos < SECTOR_SIZE - SECTOR_HEADER_SIZE - 1) {
|
||||
byte newBuffer[] = new byte[buffer.length + SECTOR_SIZE * 16];
|
||||
for (int i = 0; i < buffer.length; i++) // TODO dedicated
|
||||
// copy, java-y at
|
||||
// least
|
||||
{
|
||||
newBuffer[i] = buffer[i];
|
||||
}
|
||||
buffer = newBuffer;
|
||||
}
|
||||
for (int i = 0; i < SECTOR_SIZE - SECTOR_HEADER_SIZE - 1; i++) {
|
||||
buffer[bufferPos + i] = tempBuffer[i + 2];
|
||||
}
|
||||
bufferPos += SECTOR_SIZE - SECTOR_HEADER_SIZE - 1;
|
||||
} else if (tempBuffer[0] == SectorType.Ending.data) {
|
||||
reachedEnd = true;
|
||||
} else if (tempBuffer[0] == SectorType.PartitionLink.data) {
|
||||
ByteBuffer intBuffer = ByteBuffer.wrap(tempBuffer);
|
||||
int newOffset = intBuffer.getInt(1);
|
||||
file.seek(HEADER_SIZE + SECTOR_SIZE * newOffset);
|
||||
} else {
|
||||
throw new IOException("Invalid sector ID.");
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
file.close();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.server.world.io.region;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import ru.windcorp.progressia.server.world.io.WorldContainer;
|
||||
import ru.windcorp.progressia.server.world.io.WorldContainerFormat;
|
||||
|
||||
public class RegionFormat extends WorldContainerFormat {
|
||||
|
||||
public RegionFormat(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldContainer create(Path directory) throws IOException {
|
||||
return new RegionWorldContainer(directory);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.windcorp.progressia.server.world.io.region;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.Coordinates;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityDataRegistry;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMap;
|
||||
import ru.windcorp.progressia.common.world.generic.ChunkMaps;
|
||||
import ru.windcorp.progressia.server.Player;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.comms.ClientPlayer;
|
||||
import ru.windcorp.progressia.server.world.io.WorldContainer;
|
||||
import ru.windcorp.progressia.test.TestContent;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class RegionWorldContainer implements WorldContainer {
|
||||
|
||||
private static final boolean ENABLE = true;
|
||||
|
||||
private static final String REGION_FOLDER_NAME = "regions";
|
||||
private static final String PLAYERS_FOLDER_NAME = "players";
|
||||
private static final String REGION_NAME_FORMAT = REGION_FOLDER_NAME + "/" + "region_%d_%d_%d.progressia_region";
|
||||
private static final String PLAYER_NAME_FORMAT = PLAYERS_FOLDER_NAME + "/" + "%s.progressia_player";
|
||||
|
||||
private static final int BITS_IN_CHUNK_COORDS = 4;
|
||||
public static final int REGION_DIAMETER = 1 << BITS_IN_CHUNK_COORDS;
|
||||
|
||||
public static Vec3i getRegionCoords(Vec3i chunkCoords) {
|
||||
return Coordinates.convertGlobalToCell(BITS_IN_CHUNK_COORDS, chunkCoords, null);
|
||||
}
|
||||
|
||||
public static Vec3i getInRegionCoords(Vec3i chunkCoords) {
|
||||
return Coordinates.convertGlobalToInCell(BITS_IN_CHUNK_COORDS, chunkCoords, null);
|
||||
}
|
||||
|
||||
static final Logger LOG = LogManager.getLogger("TestWorldDiskIO");
|
||||
|
||||
private final Path path;
|
||||
private final ChunkMap<Region> regions = ChunkMaps.newHashMap();
|
||||
|
||||
public RegionWorldContainer(Path path) throws IOException {
|
||||
this.path = path;
|
||||
|
||||
Files.createDirectories(getPath());
|
||||
Files.createDirectories(getPath().resolve(REGION_FOLDER_NAME));
|
||||
Files.createDirectories(getPath().resolve(PLAYERS_FOLDER_NAME));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultChunkData load(Vec3i chunkPos, DefaultWorldData world, Server server) {
|
||||
if (!ENABLE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Region region = getRegion(chunkPos, false);
|
||||
if (region == null) {
|
||||
debug("Could not load chunk {} {} {}: region did not load", chunkPos);
|
||||
return null;
|
||||
}
|
||||
|
||||
DefaultChunkData result = region.load(chunkPos, world, server);
|
||||
return result;
|
||||
|
||||
} catch (IOException | DecodingException e) {
|
||||
warn("Failed to load chunk {} {} {}", chunkPos);
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(DefaultChunkData chunk, DefaultWorldData world, Server server) {
|
||||
if (!ENABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
debug("Saving chunk {} {} {}", chunk.getPosition());
|
||||
Region region = getRegion(chunk.getPosition(), true);
|
||||
region.save(chunk, server);
|
||||
} catch (IOException e) {
|
||||
warn("Failed to save chunk {} {} {}", chunk.getPosition());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player loadPlayer(String login, ClientPlayer clientPlayer, Server server) {
|
||||
|
||||
Path path = getPlayerPath(login);
|
||||
if (!Files.exists(path)) {
|
||||
LOG.debug("Could not load player {} because file {} does not exist", login, path);
|
||||
return null;
|
||||
}
|
||||
|
||||
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
||||
try (
|
||||
DataInputStream dataInputStream = new DataInputStream(
|
||||
new BufferedInputStream(
|
||||
Files.newInputStream(
|
||||
getPlayerPath(login)
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
player.read(dataInputStream, IOContext.SAVE);
|
||||
player.setEntityId(TestContent.PLAYER_ENTITY_ID);
|
||||
return new Player(player, server, clientPlayer);
|
||||
} catch (IOException ioException) {
|
||||
throw CrashReports.report(ioException, "Could not load player data: " + login);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePlayer(Player player, Server server) {
|
||||
Path path = getPlayerPath(player.getLogin());
|
||||
try (
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(
|
||||
new BufferedOutputStream(
|
||||
Files.newOutputStream(path)
|
||||
)
|
||||
)
|
||||
) {
|
||||
player.getEntity().
|
||||
|
||||
write(dataOutputStream, IOContext.SAVE);
|
||||
} catch (IOException ioException) {
|
||||
throw CrashReports.report(ioException, "Could not save player %s data in file ", player.getLogin(), path);
|
||||
}
|
||||
}
|
||||
|
||||
private Region getRegion(Vec3i position, boolean createIfMissing) throws IOException {
|
||||
|
||||
Vec3i regionCoords = getRegionCoords(position);
|
||||
|
||||
Region region = regions.get(regionCoords);
|
||||
if (region == null) {
|
||||
debug("Region {} {} {} is not loaded, loading", regionCoords);
|
||||
|
||||
Path path = getRegionPath(regionCoords);
|
||||
|
||||
if (!createIfMissing && !Files.exists(path)) {
|
||||
debug("Region {} {} {} does not exist on disk, aborting load", regionCoords);
|
||||
return null;
|
||||
}
|
||||
|
||||
region = openRegion(path, regionCoords);
|
||||
debug("Region {} {} {} loaded", regionCoords);
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
private Region openRegion(Path path, Vec3i regionCoords) throws IOException {
|
||||
RandomAccessFile raf = new RandomAccessFile(path.toFile(), "rw");
|
||||
Region region = new Region(raf, regionCoords);
|
||||
regions.put(regionCoords, region);
|
||||
return region;
|
||||
}
|
||||
|
||||
static void writeGenerationHint(DefaultChunkData chunk, DataOutputStream output, Server server)
|
||||
throws IOException {
|
||||
server.getWorld().getGenerator().writeGenerationHint(output, chunk.getGenerationHint());
|
||||
}
|
||||
|
||||
static void readGenerationHint(DefaultChunkData chunk, DataInputStream input, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
chunk.setGenerationHint(server.getWorld().getGenerator().readGenerationHint(input));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
private Path getRegionPath(Vec3i regionPos) {
|
||||
return getPath().resolve(
|
||||
String.format(
|
||||
REGION_NAME_FORMAT,
|
||||
regionPos.x,
|
||||
regionPos.y,
|
||||
regionPos.z
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private Path getPlayerPath(String login) {
|
||||
return getPath().resolve(
|
||||
String.format(
|
||||
PLAYER_NAME_FORMAT,
|
||||
login
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
ChunkMap<AtomicBoolean> isCloseds = ChunkMaps.newHashMap();
|
||||
ChunkMap<AtomicBoolean> isUsings = ChunkMaps.newHashMap();
|
||||
|
||||
for (Vec3i region : regions.keys()) {
|
||||
isCloseds.put(region, regions.get(region).isClosed());
|
||||
isUsings.put(region, regions.get(region).isUsing());
|
||||
}
|
||||
|
||||
boolean stillOpen = true;
|
||||
while (stillOpen) {
|
||||
stillOpen = false;
|
||||
for (Vec3i region : regions.keys()) {
|
||||
if (!isCloseds.get(region).get() && !isUsings.get(region).get()) {
|
||||
regions.get(region).close();
|
||||
} else if (isUsings.get(region).get()) {
|
||||
stillOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw CrashReports.report(e, "Could not close region files");
|
||||
}
|
||||
}
|
||||
|
||||
private static void debug(String message, Vec3i vector) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(message, vector.x, vector.y, vector.z);
|
||||
}
|
||||
}
|
||||
|
||||
private static void warn(String message, Vec3i vector) {
|
||||
LOG.warn(message, vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
}
|
@ -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<Player> 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();
|
||||
}
|
||||
|
||||
|
29
src/main/java/ru/windcorp/progressia/test/LayerTestText.java
Normal file
29
src/main/java/ru/windcorp/progressia/test/LayerTestText.java
Normal file
@ -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<LayerTestText> remover;
|
||||
|
||||
public LayerTestText(String name, MutableString value, Consumer<LayerTestText> 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);
|
||||
}
|
||||
|
||||
}
|
95
src/main/java/ru/windcorp/progressia/test/LayerTitle.java
Normal file
95
src/main/java/ru/windcorp/progressia/test/LayerTitle.java
Normal file
@ -0,0 +1,95 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
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.BasicButton;
|
||||
import ru.windcorp.progressia.client.graphics.gui.Button;
|
||||
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
||||
import ru.windcorp.progressia.client.graphics.gui.Group;
|
||||
import ru.windcorp.progressia.client.graphics.gui.Label;
|
||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||
import ru.windcorp.progressia.client.localization.MutableString;
|
||||
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.server.ServerState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
public class LayerTitle extends GUILayer {
|
||||
|
||||
private final BasicButton resetButton;
|
||||
|
||||
public LayerTitle(String name) {
|
||||
super(name, new LayoutAlign(0.5f, 0.7f, 15));
|
||||
Group content = new Group("Layer" + name + ".Group", new LayoutVertical(15));
|
||||
|
||||
MutableString title = new MutableStringLocalized("Layer" + name + ".Title");
|
||||
Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(0.5f);
|
||||
content.addChild(new Label(name + ".Title", titleFont, title));
|
||||
|
||||
Font buttonFont = titleFont.deriveNotBold();
|
||||
MutableString playText = new MutableStringLocalized("Layer" + name + ".Play");
|
||||
content.addChild(new Button(name + ".Play", new Label(name + ".Play", buttonFont, playText)).addAction(this::startGame));
|
||||
|
||||
MutableString resetText = new MutableStringLocalized("Layer" + name + ".Reset");
|
||||
this.resetButton = new Button(name + ".Reset", new Label(name + ".Reset", buttonFont, resetText)).addAction(this::resetWorld);
|
||||
content.addChild(resetButton);
|
||||
|
||||
updateResetButton();
|
||||
|
||||
MutableString quitText = new MutableStringLocalized("Layer" + name + ".Quit");
|
||||
content.addChild(new Button(name + "Quit", new Label(name + ".Quit", buttonFont, quitText)).addAction(b -> {
|
||||
System.exit(0);
|
||||
}));
|
||||
|
||||
getRoot().addChild(content);
|
||||
}
|
||||
|
||||
private void updateResetButton() {
|
||||
resetButton.setEnabled(Files.exists(Paths.get("tmp_world")));
|
||||
}
|
||||
|
||||
private void startGame(BasicButton basicButton) {
|
||||
GUI.removeLayer(this);
|
||||
try {
|
||||
ServerState.startServer();
|
||||
ClientState.connectToLocalServer();
|
||||
} catch (IOException e) {
|
||||
throw CrashReports.report(e, "Problem with loading server");
|
||||
}
|
||||
}
|
||||
|
||||
private void resetWorld(BasicButton basicButton) {
|
||||
Path rootPath = Paths.get("tmp_world");
|
||||
|
||||
try {
|
||||
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw CrashReports.report(e, "Could not reset world");
|
||||
}
|
||||
|
||||
updateResetButton();
|
||||
}
|
||||
|
||||
}
|
@ -390,7 +390,7 @@ public class TestContent {
|
||||
ru.windcorp.progressia.client.Client client = ClientState.getInstance();
|
||||
if (client == null || !client.isReady())
|
||||
return null;
|
||||
|
||||
|
||||
return client.getLocalPlayer().getSelection();
|
||||
}
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||
import ru.windcorp.progressia.common.world.io.ChunkIO;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
|
||||
public class TestWorldDiskIO {
|
||||
|
||||
private static final Path SAVE_DIR = Paths.get("tmp_world");
|
||||
private static final Logger LOG = LogManager.getLogger("TestWorldDiskIO");
|
||||
|
||||
private static final boolean ENABLE = false;
|
||||
|
||||
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",
|
||||
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);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeGenerationHint(DefaultChunkData chunk, DataOutputStream output, Server server)
|
||||
throws IOException {
|
||||
server.getWorld().getGenerator().writeGenerationHint(output, chunk.getGenerationHint());
|
||||
}
|
||||
|
||||
public static DefaultChunkData tryToLoad(Vec3i chunkPos, DefaultWorldData world, Server server) {
|
||||
if (!ENABLE)
|
||||
return null;
|
||||
|
||||
Path path = SAVE_DIR.resolve(
|
||||
String.format(
|
||||
"chunk_%+d_%+d_%+d.progressia_chunk",
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private static DefaultChunkData load(Path path, Vec3i chunkPos, DefaultWorldData world, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
try (
|
||||
DataInputStream input = new DataInputStream(
|
||||
new InflaterInputStream(new BufferedInputStream(Files.newInputStream(path)))
|
||||
)
|
||||
) {
|
||||
DefaultChunkData chunk = ChunkIO.load(world, chunkPos, input, IOContext.SAVE);
|
||||
readGenerationHint(chunk, input, server);
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
private static void readGenerationHint(DefaultChunkData chunk, DataInputStream input, Server server)
|
||||
throws IOException,
|
||||
DecodingException {
|
||||
chunk.setGenerationHint(server.getWorld().getGenerator().readGenerationHint(input));
|
||||
}
|
||||
|
||||
}
|
@ -46,9 +46,18 @@ public class TestGenerationConfig {
|
||||
private static final float CURVATURE = Units.get("100 m");
|
||||
private static final float INNER_RADIUS = Units.get("200 m");
|
||||
|
||||
private static final Fields FIELDS = new Fields(SEED);
|
||||
private final Fields fields = new Fields(SEED);
|
||||
private final Function<Server, WorldGenerator> generator;
|
||||
|
||||
public TestGenerationConfig() {
|
||||
this.generator = createGenerator();
|
||||
}
|
||||
|
||||
public Function<Server, WorldGenerator> getGenerator() {
|
||||
return generator;
|
||||
}
|
||||
|
||||
public static Function<Server, WorldGenerator> createGenerator() {
|
||||
private Function<Server, WorldGenerator> createGenerator() {
|
||||
|
||||
Planet planet = new Planet(
|
||||
((int) PLANET_RADIUS) / Coordinates.CHUNK_SIZE,
|
||||
@ -57,7 +66,7 @@ public class TestGenerationConfig {
|
||||
INNER_RADIUS
|
||||
);
|
||||
|
||||
TestHeightMap heightMap = new TestHeightMap(planet, planet.getRadius() / 4, FIELDS);
|
||||
TestHeightMap heightMap = new TestHeightMap(planet, planet.getRadius() / 4, fields);
|
||||
|
||||
LayeredTerrain terrain = new LayeredTerrain();
|
||||
registerTerrainLayers(terrain);
|
||||
@ -69,12 +78,12 @@ public class TestGenerationConfig {
|
||||
|
||||
}
|
||||
|
||||
private static void registerTerrainLayers(LayeredTerrain terrain) {
|
||||
SurfaceFloatField cliffs = FIELDS.get("Test:Cliff");
|
||||
SurfaceFloatField beaches = FIELDS.register(
|
||||
private void registerTerrainLayers(LayeredTerrain terrain) {
|
||||
SurfaceFloatField cliffs = fields.get("Test:Cliff");
|
||||
SurfaceFloatField beaches = fields.register(
|
||||
"Test:Beach",
|
||||
f -> multiply(
|
||||
anti(FIELDS.get("Test:Cliff", f))
|
||||
anti(fields.get("Test:Cliff", f))
|
||||
)
|
||||
);
|
||||
RockStrata rockStrata = createStrata();
|
||||
@ -88,40 +97,40 @@ public class TestGenerationConfig {
|
||||
terrain.addLayer(new BeachLayer("Test:Beaches", beaches, rockStrata));
|
||||
}
|
||||
|
||||
private static RockStrata createStrata() {
|
||||
private RockStrata createStrata() {
|
||||
WorleyProceduralNoise.Builder<Rock> builder = WorleyProceduralNoise.builder();
|
||||
TestContent.ROCKS.getRocks().forEach(rock -> builder.add(rock, 1));
|
||||
|
||||
SurfaceFloatField rockDepthOffsets = FIELDS.register(
|
||||
SurfaceFloatField rockDepthOffsets = fields.register(
|
||||
"Test:RockDepthOffsets",
|
||||
() -> tweak(FIELDS.primitive(), 40, 5)
|
||||
() -> tweak(fields.primitive(), 40, 5)
|
||||
);
|
||||
|
||||
return new RockStrata(builder.build(SEED), rockDepthOffsets);
|
||||
}
|
||||
|
||||
private static void registerFeatures(List<SurfaceFeature> features) {
|
||||
private void registerFeatures(List<SurfaceFeature> features) {
|
||||
|
||||
SurfaceFloatField forestiness = FIELDS.register(
|
||||
SurfaceFloatField forestiness = fields.register(
|
||||
"Test:Forest",
|
||||
() -> squash(scale(FIELDS.primitive(), 200), 5)
|
||||
() -> squash(scale(fields.primitive(), 200), 5)
|
||||
);
|
||||
|
||||
SurfaceFloatField grassiness = FIELDS.register(
|
||||
SurfaceFloatField grassiness = fields.register(
|
||||
"Test:Grass",
|
||||
f -> multiply(
|
||||
tweak(octaves(FIELDS.primitive(), 2, 2), 40, 0.5, 1.2),
|
||||
squash(tweak(FIELDS.get("Test:Forest", f), 1, -1, 1), 10),
|
||||
anti(squash(FIELDS.get("Test:Cliff", f), 10))
|
||||
tweak(octaves(fields.primitive(), 2, 2), 40, 0.5, 1.2),
|
||||
squash(tweak(fields.get("Test:Forest", f), 1, -1, 1), 10),
|
||||
anti(squash(fields.get("Test:Cliff", f), 10))
|
||||
)
|
||||
);
|
||||
|
||||
Function<String, SurfaceFloatField> floweriness = flowerName -> FIELDS.register(
|
||||
Function<String, SurfaceFloatField> floweriness = flowerName -> fields.register(
|
||||
"Test:Flower" + flowerName,
|
||||
f -> multiply(
|
||||
selectPositive(squash(scale(octaves(FIELDS.primitive(), 2, 3), 100), 2), 1, 0.5),
|
||||
tweak(FIELDS.get("Test:Forest", f), 1, -1, 1.1),
|
||||
anti(squash(FIELDS.get("Test:Cliff", f), 10))
|
||||
selectPositive(squash(scale(octaves(fields.primitive(), 2, 3), 100), 2), 1, 0.5),
|
||||
tweak(fields.get("Test:Forest", f), 1, -1, 1.1),
|
||||
anti(squash(fields.get("Test:Cliff", f), 10))
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -21,4 +21,14 @@ LayerTestGUI.PlacementModeHint = (Blocks %s Tiles: Ctrl + Mouse Wheel)
|
||||
LayerTestGUI.IsFullscreen = Fullscreen: %5s (F11)
|
||||
LayerTestGUI.IsVSync = VSync: %5s (F12)
|
||||
|
||||
LayerButtonTest.Title = Button Test
|
||||
LayerButtonTest.Title = Button Test
|
||||
LayerButtonTest.Return = Back To Menu
|
||||
|
||||
LayerTitle.Title = Progressia
|
||||
LayerTitle.Play = Play World
|
||||
LayerTitle.Reset = Reset World
|
||||
LayerTitle.Options = Options
|
||||
LayerTitle.Quit = Quit
|
||||
|
||||
LayerText.Load = Loading...
|
||||
LayerText.Save = Saving...
|
@ -21,4 +21,14 @@ LayerTestGUI.PlacementModeHint = (Блок %s плитки: Ctrl + прокру
|
||||
LayerTestGUI.IsFullscreen = Полный экран: %5s (F11)
|
||||
LayerTestGUI.IsVSync = Верт. синхр.: %5s (F12)
|
||||
|
||||
LayerButtonTest.Title = Тест Кнопок
|
||||
LayerButtonTest.Title = Тест кнопок
|
||||
LayerButtonTest.Return = Главное меню
|
||||
|
||||
LayerTitle.Title = Прогрессия
|
||||
LayerTitle.Play = Играть
|
||||
LayerTitle.Reset = Сбросить мир
|
||||
LayerTitle.Options = Настройки
|
||||
LayerTitle.Quit = Выход
|
||||
|
||||
LayerText.Load = Загрузка...
|
||||
LayerText.Save = Сохранение...
|
@ -27,6 +27,10 @@
|
||||
<Logger name="Ticker 0" level="DEBUG" />
|
||||
-->
|
||||
|
||||
<!-- Uncomment to enable Region file logger debugging
|
||||
<Logger name="TestWorldDiskIO" level="DEBUG" />
|
||||
-->
|
||||
|
||||
<Root level="info">
|
||||
<AppenderRef ref="FileLog" />
|
||||
<AppenderRef ref="Console" />
|
||||
|
Reference in New Issue
Block a user