Compare commits
37 Commits
falling-sa
...
save-world
Author | SHA1 | Date | |
---|---|---|---|
0c41350ae7 | |||
a633c8324e
|
|||
6b33f231b4 | |||
e2308b825d
|
|||
e0f6a08740 | |||
2820e01974 | |||
46bcb85044 | |||
c5dfe3d0b7 | |||
0100c8791d
|
|||
e967a64401 | |||
d2ffe1fe0e
|
|||
f4300558d5
|
|||
cd16334db8
|
|||
41a2909f7c
|
|||
a222ea8f67
|
|||
98250cd524 | |||
9dcb3a7748 | |||
0ccc108ddd
|
|||
c7e7d3bdac
|
|||
62729f5873
|
|||
84864f8947
|
|||
d01ef3654f
|
|||
fae09edb16 | |||
e4ced6507e | |||
b7dcbb0f30 | |||
9c26418354 | |||
6891d3a095 | |||
8167c40f64 | |||
8bc23acb61 | |||
254faca0a5 | |||
0c66f1751e | |||
c88dea6030 | |||
6521cb5749 | |||
94db44e443 | |||
53f72b068a | |||
a9ca5f6b17 | |||
4ab7cb738e |
@ -18,18 +18,28 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia;
|
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.CrashReports;
|
||||||
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
|
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
|
||||||
import ru.windcorp.progressia.common.util.crash.providers.*;
|
import ru.windcorp.progressia.common.util.crash.providers.*;
|
||||||
|
import ru.windcorp.progressia.test.LayerTitle;
|
||||||
|
|
||||||
public class ProgressiaLauncher {
|
public class ProgressiaLauncher {
|
||||||
|
|
||||||
public static String[] arguments;
|
public static String[] arguments;
|
||||||
|
private static Proxy proxy;
|
||||||
|
|
||||||
public static void launch(String[] args, Proxy proxy) {
|
public static void launch(String[] args, Proxy proxy) {
|
||||||
arguments = args.clone();
|
arguments = args.clone();
|
||||||
setupCrashReports();
|
setupCrashReports();
|
||||||
|
|
||||||
proxy.initialize();
|
proxy.initialize();
|
||||||
|
ProgressiaLauncher.proxy = proxy;
|
||||||
|
GUI.addTopLayer(new LayerTitle("Title"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Proxy getProxy() {
|
||||||
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setupCrashReports() {
|
private static void setupCrashReports() {
|
||||||
|
@ -30,7 +30,6 @@ import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
|||||||
import ru.windcorp.progressia.client.localization.Localizer;
|
import ru.windcorp.progressia.client.localization.Localizer;
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
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.TestContent;
|
||||||
import ru.windcorp.progressia.test.TestMusicPlayer;
|
import ru.windcorp.progressia.test.TestMusicPlayer;
|
||||||
|
|
||||||
@ -38,7 +37,9 @@ public class ClientProxy implements Proxy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
|
||||||
GraphicsBackend.initialize();
|
GraphicsBackend.initialize();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
|
||||||
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||||
@ -58,10 +59,6 @@ public class ClientProxy implements Proxy {
|
|||||||
|
|
||||||
AudioSystem.initialize();
|
AudioSystem.initialize();
|
||||||
|
|
||||||
ServerState.startServer();
|
|
||||||
ClientState.connectToLocalServer();
|
|
||||||
|
|
||||||
TestMusicPlayer.start();
|
TestMusicPlayer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,13 @@ package ru.windcorp.progressia.client;
|
|||||||
|
|
||||||
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
|
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
|
||||||
import ru.windcorp.progressia.client.graphics.GUI;
|
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.client.graphics.world.LayerWorld;
|
||||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||||
|
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
import ru.windcorp.progressia.test.LayerAbout;
|
import ru.windcorp.progressia.test.LayerAbout;
|
||||||
|
import ru.windcorp.progressia.test.LayerTestText;
|
||||||
import ru.windcorp.progressia.test.LayerTestUI;
|
import ru.windcorp.progressia.test.LayerTestUI;
|
||||||
import ru.windcorp.progressia.test.TestContent;
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
|
||||||
@ -52,11 +55,39 @@ public class ClientState {
|
|||||||
channel.connect(TestContent.PLAYER_LOGIN);
|
channel.connect(TestContent.PLAYER_LOGIN);
|
||||||
|
|
||||||
setInstance(client);
|
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() {
|
private ClientState() {
|
||||||
|
@ -21,6 +21,7 @@ package ru.windcorp.progressia.client.comms.localhost;
|
|||||||
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
|
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
|
||||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
|
|
||||||
public class LocalServerCommsChannel extends ServerCommsChannel {
|
public class LocalServerCommsChannel extends ServerCommsChannel {
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ public class LocalServerCommsChannel extends ServerCommsChannel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
// Do nothing
|
ServerState.getInstance().getClientManager().disconnectClient(localClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ package ru.windcorp.progressia.client.graphics;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ public class GUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void addBottomLayer(Layer layer) {
|
public static void addBottomLayer(Layer layer) {
|
||||||
|
Objects.requireNonNull(layer, "layer");
|
||||||
modify(layers -> {
|
modify(layers -> {
|
||||||
layers.add(layer);
|
layers.add(layer);
|
||||||
layer.onAdded();
|
layer.onAdded();
|
||||||
@ -65,6 +67,7 @@ public class GUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void addTopLayer(Layer layer) {
|
public static void addTopLayer(Layer layer) {
|
||||||
|
Objects.requireNonNull(layer, "layer");
|
||||||
modify(layers -> {
|
modify(layers -> {
|
||||||
layers.add(0, layer);
|
layers.add(0, layer);
|
||||||
layer.onAdded();
|
layer.onAdded();
|
||||||
@ -72,6 +75,7 @@ public class GUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void removeLayer(Layer layer) {
|
public static void removeLayer(Layer layer) {
|
||||||
|
Objects.requireNonNull(layer, "layer");
|
||||||
modify(layers -> {
|
modify(layers -> {
|
||||||
layers.remove(layer);
|
layers.remove(layer);
|
||||||
layer.onRemoved();
|
layer.onRemoved();
|
||||||
|
@ -43,9 +43,9 @@ public abstract class BasicButton extends Component {
|
|||||||
private boolean isPressed = false;
|
private boolean isPressed = false;
|
||||||
private final Collection<Consumer<BasicButton>> actions = Collections.synchronizedCollection(new ArrayList<>());
|
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);
|
super(name);
|
||||||
this.label = new Label(name + ".Label", labelFont, label);
|
this.label = label;
|
||||||
|
|
||||||
setLayout(new LayoutAlign(10));
|
setLayout(new LayoutAlign(10));
|
||||||
addChild(this.label);
|
addChild(this.label);
|
||||||
@ -104,6 +104,10 @@ public abstract class BasicButton extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BasicButton(String name, String label, Font labelFont) {
|
||||||
|
this(name, new Label(name + ".Label", labelFont, label));
|
||||||
|
}
|
||||||
|
|
||||||
public BasicButton(String name, String label) {
|
public BasicButton(String name, String label) {
|
||||||
this(name, label, new Font());
|
this(name, label, new Font());
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,10 @@ public class Button extends BasicButton {
|
|||||||
super(name, label, labelFont);
|
super(name, label, labelFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Button(String name, Label label) {
|
||||||
|
super(name, label);
|
||||||
|
}
|
||||||
|
|
||||||
public Button(String name, String label) {
|
public Button(String name, String label) {
|
||||||
this(name, label, new Font());
|
this(name, label, new Font());
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,8 @@ public class RadioButton extends BasicButton {
|
|||||||
addChild(group);
|
addChild(group);
|
||||||
|
|
||||||
addListener(KeyEvent.class, e -> {
|
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 (e.getKey() == GLFW.GLFW_KEY_LEFT || e.getKey() == GLFW.GLFW_KEY_UP) {
|
||||||
if (this.group != null) {
|
if (this.group != null) {
|
||||||
@ -149,7 +150,8 @@ public class RadioButton extends BasicButton {
|
|||||||
group.selectNext();
|
group.selectNext();
|
||||||
removeAction(group.listener);
|
removeAction(group.listener);
|
||||||
group.buttons.remove(this);
|
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;
|
this.group = group;
|
||||||
@ -178,7 +180,8 @@ public class RadioButton extends BasicButton {
|
|||||||
this.checked = checked;
|
this.checked = checked;
|
||||||
|
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
group.listener.accept(this); // Failsafe for manual invocations of setChecked()
|
group.listener.accept(this); // Failsafe for manual invocations of
|
||||||
|
// setChecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,8 @@ public class LayerWorld extends Layer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRender() {
|
protected void doRender() {
|
||||||
|
client.getComms().processPackets();
|
||||||
|
|
||||||
Camera camera = client.getCamera();
|
Camera camera = client.getCamera();
|
||||||
if (camera.hasAnchor()) {
|
if (camera.hasAnchor()) {
|
||||||
renderWorld();
|
renderWorld();
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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.client.world.cro;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.ShapePart;
|
||||||
|
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||||
|
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||||
|
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||||
|
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.rels.RelFace;
|
||||||
|
|
||||||
|
public class ChunkRenderOptimizerSimple extends ChunkRenderOptimizer {
|
||||||
|
|
||||||
|
public interface BlockOptimizedSimple {
|
||||||
|
|
||||||
|
void getShapeParts(
|
||||||
|
DefaultChunkData chunk,
|
||||||
|
Vec3i relBlockInChunk,
|
||||||
|
Consumer<ShapePart> output
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface TileOptimizedCustom {
|
||||||
|
|
||||||
|
void getShapeParts(
|
||||||
|
DefaultChunkData chunk,
|
||||||
|
Vec3i relBlockInChunk,
|
||||||
|
RelFace blockFace,
|
||||||
|
Consumer<ShapePart> output
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Collection<ShapePart> parts = new ArrayList<>();
|
||||||
|
private final Consumer<ShapePart> partAdder = parts::add;
|
||||||
|
|
||||||
|
public ChunkRenderOptimizerSimple(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startRender() {
|
||||||
|
parts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addBlock(BlockRender block, Vec3i relBlockInChunk) {
|
||||||
|
if (block instanceof BlockOptimizedSimple) {
|
||||||
|
((BlockOptimizedSimple) block).getShapeParts(chunk.getData(), relBlockInChunk, partAdder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTile(TileRender tile, Vec3i relBlockInChunk, RelFace blockFace) {
|
||||||
|
if (tile instanceof TileOptimizedCustom) {
|
||||||
|
((TileOptimizedCustom) tile).getShapeParts(chunk.getData(), relBlockInChunk, blockFace, partAdder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Renderable endRender() {
|
||||||
|
|
||||||
|
if (parts.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Shape(
|
||||||
|
Usage.STATIC,
|
||||||
|
WorldRenderProgram.getDefault(),
|
||||||
|
parts.toArray(new ShapePart[parts.size()])
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* 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.client.world.tile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.mat._3.Mat3;
|
||||||
|
import glm.mat._4.Mat4;
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import glm.vec._4.Vec4;
|
||||||
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.ShapePart;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.ShapeParts;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
|
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||||
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSimple.TileOptimizedCustom;
|
||||||
|
import ru.windcorp.progressia.common.util.Matrices;
|
||||||
|
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.rels.AbsFace;
|
||||||
|
import ru.windcorp.progressia.common.world.rels.AxisRotations;
|
||||||
|
import ru.windcorp.progressia.common.world.rels.RelFace;
|
||||||
|
|
||||||
|
public class TileRenderCross extends TileRender implements TileOptimizedCustom {
|
||||||
|
|
||||||
|
private static final float SQRT_2_OVER_2 = (float) Math.sqrt(2) / 2;
|
||||||
|
private static final float[] ONE_AND_NEGATIVE_ONE = new float[] { 1, -1 };
|
||||||
|
|
||||||
|
private final Texture texture;
|
||||||
|
private final float width;
|
||||||
|
|
||||||
|
public TileRenderCross(String id, Texture texture, boolean allowStretching) {
|
||||||
|
super(id);
|
||||||
|
this.texture = texture;
|
||||||
|
this.width = allowStretching ? 1 : SQRT_2_OVER_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture getTexture(RelFace blockFace) {
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec4 getColorMultiplier(RelFace blockFace) {
|
||||||
|
return Colors.WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getShapeParts(
|
||||||
|
DefaultChunkData chunk,
|
||||||
|
Vec3i bic,
|
||||||
|
RelFace blockFace,
|
||||||
|
Consumer<ShapePart> output
|
||||||
|
) {
|
||||||
|
Mat4 transform = Matrices.grab4();
|
||||||
|
Vec3 origin = Vectors.grab3();
|
||||||
|
Vec3 width = Vectors.grab3();
|
||||||
|
Vec3 height = Vectors.grab3();
|
||||||
|
|
||||||
|
Mat3 resolutionMatrix = AxisRotations.getResolutionMatrix3(blockFace.resolve(AbsFace.POS_Z));
|
||||||
|
|
||||||
|
Vec4 color = getColorMultiplier(blockFace);
|
||||||
|
Texture texture = getTexture(blockFace);
|
||||||
|
float originOffset = (1 - this.width) / 2;
|
||||||
|
|
||||||
|
WorldRenderProgram program = WorldRenderProgram.getDefault();
|
||||||
|
|
||||||
|
for (int i = 0; getTransform(chunk, bic, blockFace, i, transform); i++) {
|
||||||
|
|
||||||
|
for (float flip : ONE_AND_NEGATIVE_ONE) {
|
||||||
|
origin.set(flip * (originOffset - 0.5f), originOffset - 0.5f, 0);
|
||||||
|
width.set(flip * this.width, this.width, 0);
|
||||||
|
height.set(0, 0, 1);
|
||||||
|
|
||||||
|
VectorUtil.applyMat4(origin, transform);
|
||||||
|
VectorUtil.rotateOnly(width, transform);
|
||||||
|
VectorUtil.rotateOnly(height, transform);
|
||||||
|
|
||||||
|
origin.z += 1 - 0.5f;
|
||||||
|
|
||||||
|
if (blockFace != RelFace.UP) {
|
||||||
|
resolutionMatrix.mul(origin);
|
||||||
|
resolutionMatrix.mul(width);
|
||||||
|
resolutionMatrix.mul(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
origin.add(bic.x, bic.y, bic.z);
|
||||||
|
|
||||||
|
output.accept(
|
||||||
|
ShapeParts.createRectangle(
|
||||||
|
program,
|
||||||
|
texture,
|
||||||
|
color,
|
||||||
|
origin,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
output.accept(
|
||||||
|
ShapeParts.createRectangle(
|
||||||
|
program,
|
||||||
|
texture,
|
||||||
|
color,
|
||||||
|
origin,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrices.release(transform);
|
||||||
|
Vectors.release(origin);
|
||||||
|
Vectors.release(width);
|
||||||
|
Vectors.release(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean getTransform(
|
||||||
|
DefaultChunkData chunk,
|
||||||
|
Vec3i relBlockInChunk,
|
||||||
|
RelFace blockFace,
|
||||||
|
int count,
|
||||||
|
Mat4 output
|
||||||
|
) {
|
||||||
|
output.identity();
|
||||||
|
return count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk, RelFace blockFace) {
|
||||||
|
Collection<ShapePart> parts = new ArrayList<>(4);
|
||||||
|
|
||||||
|
getShapeParts(chunk, blockInChunk, blockFace, parts::add);
|
||||||
|
|
||||||
|
return new Shape(
|
||||||
|
Usage.STATIC,
|
||||||
|
WorldRenderProgram.getDefault(),
|
||||||
|
parts.toArray(new ShapePart[parts.size()])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsOwnRenderable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||||
|
|
||||||
@ -54,6 +55,8 @@ public abstract class CommsChannel {
|
|||||||
|
|
||||||
private final Collection<CommsListener> listeners = Collections.synchronizedCollection(new ArrayList<>());
|
private final Collection<CommsListener> listeners = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
|
private final List<Packet> pendingPackets = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
protected abstract void doSendPacket(Packet packet) throws IOException;
|
protected abstract void doSendPacket(Packet packet) throws IOException;
|
||||||
|
|
||||||
private synchronized void sendPacket(
|
private synchronized void sendPacket(
|
||||||
@ -101,9 +104,20 @@ public abstract class CommsChannel {
|
|||||||
public abstract void disconnect();
|
public abstract void disconnect();
|
||||||
|
|
||||||
protected void onPacketReceived(Packet packet) {
|
protected void onPacketReceived(Packet packet) {
|
||||||
|
pendingPackets.add(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void forwardPacketToListeners(Packet packet) {
|
||||||
listeners.forEach(l -> l.onPacketReceived(packet));
|
listeners.forEach(l -> l.onPacketReceived(packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void processPackets() {
|
||||||
|
synchronized (pendingPackets) {
|
||||||
|
pendingPackets.forEach(this::forwardPacketToListeners);
|
||||||
|
pendingPackets.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addListener(CommsListener listener) {
|
public void addListener(CommsListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
@ -180,6 +180,25 @@ public class VectorUtil {
|
|||||||
return applyMat4(inOut, mat, inOut);
|
return applyMat4(inOut, mat, inOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vec3 rotateOnly(Vec3 in, Mat4 mat, Vec3 out) {
|
||||||
|
if (out == null) {
|
||||||
|
out = new Vec3();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat3 mat3 = Matrices.grab3();
|
||||||
|
mat3.set(mat);
|
||||||
|
|
||||||
|
mat3.mul(in, out);
|
||||||
|
|
||||||
|
Matrices.release(mat3);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec3 rotateOnly(Vec3 inOut, Mat4 mat) {
|
||||||
|
return rotateOnly(inOut, mat, inOut);
|
||||||
|
}
|
||||||
|
|
||||||
public static Vec3 rotate(Vec3 in, Vec3 axis, float angle, Vec3 out) {
|
public static Vec3 rotate(Vec3 in, Vec3 axis, float angle, Vec3 out) {
|
||||||
if (out == null) {
|
if (out == null) {
|
||||||
out = new Vec3();
|
out = new Vec3();
|
||||||
|
@ -163,4 +163,71 @@ public class Coordinates {
|
|||||||
return blockInChunk == 0 || blockInChunk == BLOCKS_PER_CHUNK - 1;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,17 +18,18 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.server;
|
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.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
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 {
|
public class PlayerManager {
|
||||||
|
|
||||||
private final Server server;
|
private final Server server;
|
||||||
@ -48,17 +49,26 @@ public class PlayerManager {
|
|||||||
getServer().postEvent(new PlayerJoinedEvent.Immutable(getServer(), player));
|
getServer().postEvent(new PlayerJoinedEvent.Immutable(getServer(), player));
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityData conjurePlayerEntity(String login) {
|
public void removePlayer(Player player) {
|
||||||
// TODO Live up to the name
|
server.getWorld().getContainer().savePlayer(player, server);
|
||||||
if (TestContent.PLAYER_LOGIN.equals(login)) {
|
this.players.remove(player);
|
||||||
EntityData entity = spawnPlayerEntity(login);
|
getServer().postEvent(new PlayerLeftEvent.Immutable(getServer(), player));
|
||||||
return entity;
|
|
||||||
} else {
|
|
||||||
throw CrashReports.report(null, "Unknown login %s, javahorse stupid", login);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
||||||
|
|
||||||
player.setEntityId(TestContent.PLAYER_ENTITY_ID);
|
player.setEntityId(TestContent.PLAYER_ENTITY_ID);
|
||||||
@ -67,8 +77,6 @@ public class PlayerManager {
|
|||||||
player.setUpVector(new Vec3(0, 0, 1));
|
player.setUpVector(new Vec3(0, 0, 1));
|
||||||
player.setLookingAt(new Vec3(2, 1, 0));
|
player.setLookingAt(new Vec3(2, 1, 0));
|
||||||
|
|
||||||
getServer().getWorld().getData().addEntity(player);
|
|
||||||
|
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,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.ReportingServerContext;
|
||||||
import ru.windcorp.progressia.server.world.context.impl.RotatingServerContext;
|
import ru.windcorp.progressia.server.world.context.impl.RotatingServerContext;
|
||||||
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
|
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.tasks.WorldAccessor;
|
||||||
import ru.windcorp.progressia.server.world.ticking.Change;
|
import ru.windcorp.progressia.server.world.ticking.Change;
|
||||||
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||||
@ -78,11 +79,16 @@ public class Server {
|
|||||||
|
|
||||||
private final TickingSettings tickingSettings = new TickingSettings();
|
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(
|
this.world = new DefaultWorldLogic(
|
||||||
world,
|
world,
|
||||||
this,
|
this,
|
||||||
generatorCreator.apply(this),
|
generatorCreator.apply(this),
|
||||||
|
worldContainer,
|
||||||
worldAccessor
|
worldAccessor
|
||||||
);
|
);
|
||||||
this.serverThread = new ServerThread(this);
|
this.serverThread = new ServerThread(this);
|
||||||
@ -91,6 +97,7 @@ public class Server {
|
|||||||
this.playerManager = new PlayerManager(this);
|
this.playerManager = new PlayerManager(this);
|
||||||
this.loadManager = new LoadManager(this);
|
this.loadManager = new LoadManager(this);
|
||||||
|
|
||||||
|
schedule(getClientManager()::processPackets);
|
||||||
schedule(new ChunkRequestDaemon(loadManager.getChunkManager())::tick);
|
schedule(new ChunkRequestDaemon(loadManager.getChunkManager())::tick);
|
||||||
schedule(new EntityRequestDaemon(loadManager.getEntityManager())::tick);
|
schedule(new EntityRequestDaemon(loadManager.getEntityManager())::tick);
|
||||||
|
|
||||||
@ -348,6 +355,7 @@ public class Server {
|
|||||||
public void shutdown(String message) {
|
public void shutdown(String message) {
|
||||||
LogManager.getLogger().warn("Server.shutdown() is not yet implemented");
|
LogManager.getLogger().warn("Server.shutdown() is not yet implemented");
|
||||||
serverThread.stop();
|
serverThread.stop();
|
||||||
|
getWorld().getContainer().close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleWorldTicks(Server server) {
|
private void scheduleWorldTicks(Server server) {
|
||||||
|
@ -18,7 +18,14 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.server;
|
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.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;
|
import ru.windcorp.progressia.test.gen.TestGenerationConfig;
|
||||||
|
|
||||||
public class ServerState {
|
public class ServerState {
|
||||||
@ -33,10 +40,15 @@ public class ServerState {
|
|||||||
ServerState.instance = instance;
|
ServerState.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void startServer() {
|
public static void startServer() throws IOException {
|
||||||
Server server = new Server(new DefaultWorldData(), TestGenerationConfig.createGenerator());
|
|
||||||
|
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);
|
setInstance(server);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServerState() {
|
private ServerState() {
|
||||||
|
@ -26,10 +26,13 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.server.world.ticking.TickerCoordinator;
|
import ru.windcorp.progressia.server.world.ticking.TickerCoordinator;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class ServerThread implements Runnable {
|
public class ServerThread implements Runnable {
|
||||||
|
|
||||||
private static final ThreadLocal<Server> SERVER_THREADS_MAP = new ThreadLocal<>();
|
private static final ThreadLocal<Server> SERVER_THREADS_MAP = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private static boolean isShuttingDown;
|
||||||
|
|
||||||
public static Server getCurrentServer() {
|
public static Server getCurrentServer() {
|
||||||
return SERVER_THREADS_MAP.get();
|
return SERVER_THREADS_MAP.get();
|
||||||
}
|
}
|
||||||
@ -63,6 +66,7 @@ public class ServerThread implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
isShuttingDown = false;
|
||||||
ticker.start();
|
ticker.start();
|
||||||
executor.scheduleAtFixedRate(this, 0, 1000 / 20, TimeUnit.MILLISECONDS);
|
executor.scheduleAtFixedRate(this, 0, 1000 / 20, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
@ -70,6 +74,11 @@ public class ServerThread implements Runnable {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
if (isShuttingDown) {
|
||||||
|
getTicker().stop();
|
||||||
|
executor.shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
server.tick();
|
server.tick();
|
||||||
ticker.runOneTick();
|
ticker.runOneTick();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@ -78,13 +87,10 @@ public class ServerThread implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
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() {
|
public Server getServer() {
|
||||||
|
@ -26,11 +26,11 @@ import glm.vec._3.i.Vec3i;
|
|||||||
import gnu.trove.TCollections;
|
import gnu.trove.TCollections;
|
||||||
import gnu.trove.map.TIntObjectMap;
|
import gnu.trove.map.TIntObjectMap;
|
||||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||||
|
import ru.windcorp.progressia.common.comms.CommsChannel;
|
||||||
import ru.windcorp.progressia.common.comms.CommsChannel.State;
|
import ru.windcorp.progressia.common.comms.CommsChannel.State;
|
||||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||||
import ru.windcorp.progressia.common.world.PacketSetGravityModel;
|
import ru.windcorp.progressia.common.world.PacketSetGravityModel;
|
||||||
import ru.windcorp.progressia.common.world.PacketSetLocalPlayer;
|
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.Player;
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
@ -79,18 +79,24 @@ public class ClientManager {
|
|||||||
setGravityModelPacket.set(getServer().getWorld().getData().getGravityModel());
|
setGravityModelPacket.set(getServer().getWorld().getData().getGravityModel());
|
||||||
client.sendPacket(setGravityModelPacket);
|
client.sendPacket(setGravityModelPacket);
|
||||||
|
|
||||||
EntityData entity = getServer().getPlayerManager().conjurePlayerEntity(login);
|
Player player = getServer().getPlayerManager().conjurePlayer(client, login);
|
||||||
Player player = new Player(entity, getServer(), client);
|
|
||||||
getServer().getPlayerManager().addPlayer(player);
|
getServer().getPlayerManager().addPlayer(player);
|
||||||
|
|
||||||
PacketSetLocalPlayer packet = new PacketSetLocalPlayer();
|
PacketSetLocalPlayer packet = new PacketSetLocalPlayer();
|
||||||
packet.set(entity.getEntityId());
|
packet.set(player.getEntity().getEntityId());
|
||||||
client.sendPacket(packet);
|
client.sendPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnectClient(Client client) {
|
public void disconnectClient(Client client) {
|
||||||
client.disconnect();
|
client.disconnect();
|
||||||
clientsById.remove(client.getId());
|
clientsById.remove(client.getId());
|
||||||
|
if (client instanceof ClientPlayer) {
|
||||||
|
getServer().getPlayerManager().removePlayer(((ClientPlayer) client).getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processPackets() {
|
||||||
|
getClients().forEach(CommsChannel::processPackets);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,7 @@ import ru.windcorp.progressia.common.world.PacketSendChunk;
|
|||||||
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
import ru.windcorp.progressia.common.world.DefaultWorldData;
|
||||||
import ru.windcorp.progressia.server.Player;
|
import ru.windcorp.progressia.server.Player;
|
||||||
import ru.windcorp.progressia.server.Server;
|
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
|
* Chunk manager provides facilities to load, unload and generate chunks for a
|
||||||
@ -52,6 +52,10 @@ public class ChunkManager {
|
|||||||
return getLoadManager().getServer();
|
return getLoadManager().getServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WorldContainer getContainer() {
|
||||||
|
return getServer().getWorld().getContainer();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the result of an attempt to load a chunk.
|
* Describes the result of an attempt to load a chunk.
|
||||||
*/
|
*/
|
||||||
@ -115,7 +119,7 @@ public class ChunkManager {
|
|||||||
|
|
||||||
DefaultWorldData world = getServer().getWorld().getData();
|
DefaultWorldData world = getServer().getWorld().getData();
|
||||||
|
|
||||||
DefaultChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world, getServer());
|
DefaultChunkData chunk = getServer().getWorld().getContainer().load(chunkPos, world, getServer());
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
world.addChunk(chunk);
|
world.addChunk(chunk);
|
||||||
return LoadResult.LOADED_FROM_DISK;
|
return LoadResult.LOADED_FROM_DISK;
|
||||||
@ -141,7 +145,7 @@ public class ChunkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
world.removeChunk(chunk);
|
world.removeChunk(chunk);
|
||||||
TestWorldDiskIO.saveChunk(chunk, getServer());
|
getContainer().save(chunk, getServer().getWorld().getData(), getServer());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import ru.windcorp.progressia.common.world.WorldDataListener;
|
|||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
|
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.TickEntitiesTask;
|
||||||
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
|
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
|
||||||
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||||
@ -42,18 +43,21 @@ public class DefaultWorldLogic implements WorldLogic {
|
|||||||
private final Server server;
|
private final Server server;
|
||||||
|
|
||||||
private final WorldGenerator generator;
|
private final WorldGenerator generator;
|
||||||
|
private final WorldContainer container;
|
||||||
|
|
||||||
private final Map<DefaultChunkData, DefaultChunkLogic> chunks = new HashMap<>();
|
private final Map<DefaultChunkData, DefaultChunkLogic> chunks = new HashMap<>();
|
||||||
|
|
||||||
private final Evaluation tickEntitiesTask = new TickEntitiesTask();
|
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.data = data;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
|
|
||||||
this.generator = generator;
|
this.generator = generator;
|
||||||
data.setGravityModel(getGenerator().getGravityModel());
|
data.setGravityModel(getGenerator().getGravityModel());
|
||||||
|
|
||||||
|
this.container = container;
|
||||||
|
|
||||||
data.addListener(new WorldDataListener() {
|
data.addListener(new WorldDataListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onChunkLoaded(DefaultWorldData world, DefaultChunkData chunk) {
|
public void onChunkLoaded(DefaultWorldData world, DefaultChunkData chunk) {
|
||||||
@ -106,6 +110,10 @@ public class DefaultWorldLogic implements WorldLogic {
|
|||||||
return generator;
|
return generator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WorldContainer getContainer() {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
public DefaultChunkData generate(Vec3i chunkPos) {
|
public DefaultChunkData generate(Vec3i chunkPos) {
|
||||||
DefaultChunkData chunk = getGenerator().generate(chunkPos);
|
DefaultChunkData chunk = getGenerator().generate(chunkPos);
|
||||||
|
|
||||||
|
@ -405,7 +405,7 @@ class DefaultServerContextImpl extends DefaultServerContext
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getTileCount(Vec3i location, RelFace face) {
|
public int getTileCount(Vec3i location, RelFace face) {
|
||||||
assert requireContextRole(Role.TILE_STACK);
|
assert requireContextRole(Role.WORLD);
|
||||||
TileDataStack stack = world.getTilesOrNull(location, face.resolve(AbsFace.POS_Z));
|
TileDataStack stack = world.getTilesOrNull(location, face.resolve(AbsFace.POS_Z));
|
||||||
if (stack == null)
|
if (stack == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -48,12 +48,11 @@ class PlanetTerrainGenerator {
|
|||||||
this.parent = generator;
|
this.parent = generator;
|
||||||
|
|
||||||
int seaLevel = (int) parent.getPlanet().getRadius();
|
int seaLevel = (int) parent.getPlanet().getRadius();
|
||||||
SurfaceFloatField adjustedHeightMap = (f, n, w) -> heightMap.get(f, n, w) + generator.getPlanet().getRadius();
|
|
||||||
|
|
||||||
this.surfaceGenerators = AbsFace.mapToFaces(
|
this.surfaceGenerators = AbsFace.mapToFaces(
|
||||||
face -> new SurfaceTerrainGenerator(
|
face -> new SurfaceTerrainGenerator(
|
||||||
new Surface(face, seaLevel),
|
new Surface(face, seaLevel),
|
||||||
adjustedHeightMap,
|
heightMap,
|
||||||
layers
|
layers
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -47,6 +47,7 @@ public class SurfaceTerrainGenerator {
|
|||||||
|
|
||||||
Vec3 offset = new Vec3(chunk.getMinX(), chunk.getMinY(), chunk.getMinZ());
|
Vec3 offset = new Vec3(chunk.getMinX(), chunk.getMinY(), chunk.getMinZ());
|
||||||
AxisRotations.relativize(offset, chunk.getUp(), offset);
|
AxisRotations.relativize(offset, chunk.getUp(), offset);
|
||||||
|
offset.z -= surface.getSeaLevel();
|
||||||
|
|
||||||
SurfaceWorldContext context = surface.createContext(server, chunk, 0);
|
SurfaceWorldContext context = surface.createContext(server, chunk, 0);
|
||||||
|
|
||||||
|
@ -20,7 +20,9 @@ package ru.windcorp.progressia.server.world.generation.surface.context;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.world.generic.TileGenericStackRO;
|
||||||
import ru.windcorp.progressia.common.world.rels.RelFace;
|
import ru.windcorp.progressia.common.world.rels.RelFace;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
import ru.windcorp.progressia.server.world.context.ServerTileContext;
|
import ru.windcorp.progressia.server.world.context.ServerTileContext;
|
||||||
import ru.windcorp.progressia.server.world.context.impl.RotatingServerContext;
|
import ru.windcorp.progressia.server.world.context.impl.RotatingServerContext;
|
||||||
import ru.windcorp.progressia.server.world.generation.surface.Surface;
|
import ru.windcorp.progressia.server.world.generation.surface.Surface;
|
||||||
@ -111,4 +113,11 @@ public class SurfaceContextImpl extends RotatingServerContext implements Surface
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTile(Vec3i userLocation, RelFace userFace, TileData tile) {
|
||||||
|
if (getTileCount(userLocation, userFace) < TileGenericStackRO.TILES_PER_FACE) {
|
||||||
|
super.addTile(userLocation, userFace, tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
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.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.GUI;
|
||||||
import ru.windcorp.progressia.client.graphics.font.Font;
|
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Button;
|
import ru.windcorp.progressia.client.graphics.gui.Button;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Checkbox;
|
import ru.windcorp.progressia.client.graphics.gui.Checkbox;
|
||||||
@ -25,9 +29,15 @@ import ru.windcorp.progressia.client.graphics.gui.Label;
|
|||||||
import ru.windcorp.progressia.client.graphics.gui.RadioButton;
|
import ru.windcorp.progressia.client.graphics.gui.RadioButton;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.RadioButtonGroup;
|
import ru.windcorp.progressia.client.graphics.gui.RadioButtonGroup;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.menu.MenuLayer;
|
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 {
|
public class LayerButtonTest extends MenuLayer {
|
||||||
|
|
||||||
|
boolean alive = true;
|
||||||
|
|
||||||
public LayerButtonTest() {
|
public LayerButtonTest() {
|
||||||
super("ButtonTest");
|
super("ButtonTest");
|
||||||
|
|
||||||
@ -60,8 +70,28 @@ public class LayerButtonTest extends MenuLayer {
|
|||||||
getCloseAction().run();
|
getCloseAction().run();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
getContent().addChild(new Button("Quit", "Quit").addAction(b -> {
|
getContent().addChild(new Button("Menu", "Back To Menu").addAction(b -> {
|
||||||
System.exit(0);
|
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();
|
getContent().takeFocus();
|
||||||
|
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
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -83,6 +83,10 @@ public class Rocks {
|
|||||||
return blocks.get(variant);
|
return blocks.get(variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<BlockData> getBlocks() {
|
||||||
|
return blocks.values();
|
||||||
|
}
|
||||||
|
|
||||||
private void register() {
|
private void register() {
|
||||||
for (RockVariant variant : RockVariant.values()) {
|
for (RockVariant variant : RockVariant.values()) {
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import static ru.windcorp.progressia.client.world.block.BlockRenderRegistry.getB
|
|||||||
import static ru.windcorp.progressia.client.world.tile.TileRenderRegistry.getTileTexture;
|
import static ru.windcorp.progressia.client.world.tile.TileRenderRegistry.getTileTexture;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -38,6 +39,7 @@ import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
|||||||
import ru.windcorp.progressia.client.graphics.world.Selection;
|
import ru.windcorp.progressia.client.graphics.world.Selection;
|
||||||
import ru.windcorp.progressia.client.world.block.*;
|
import ru.windcorp.progressia.client.world.block.*;
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
|
||||||
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSimple;
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface;
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface;
|
||||||
import ru.windcorp.progressia.client.world.entity.*;
|
import ru.windcorp.progressia.client.world.entity.*;
|
||||||
import ru.windcorp.progressia.client.world.tile.*;
|
import ru.windcorp.progressia.client.world.tile.*;
|
||||||
@ -54,6 +56,9 @@ import ru.windcorp.progressia.common.world.tile.*;
|
|||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.comms.controls.*;
|
import ru.windcorp.progressia.server.comms.controls.*;
|
||||||
import ru.windcorp.progressia.server.world.block.*;
|
import ru.windcorp.progressia.server.world.block.*;
|
||||||
|
import ru.windcorp.progressia.server.world.context.ServerBlockContext;
|
||||||
|
import ru.windcorp.progressia.server.world.context.ServerTileContext;
|
||||||
|
import ru.windcorp.progressia.server.world.context.ServerTileStackContext;
|
||||||
import ru.windcorp.progressia.server.world.entity.*;
|
import ru.windcorp.progressia.server.world.entity.*;
|
||||||
import ru.windcorp.progressia.server.world.generation.planet.PlanetGravityModel;
|
import ru.windcorp.progressia.server.world.generation.planet.PlanetGravityModel;
|
||||||
import ru.windcorp.progressia.server.world.tile.*;
|
import ru.windcorp.progressia.server.world.tile.*;
|
||||||
@ -96,36 +101,21 @@ public class TestContent {
|
|||||||
register(new TestBlockLogicAir("Test:Air"));
|
register(new TestBlockLogicAir("Test:Air"));
|
||||||
placeableBlacklist.add("Test:Air");
|
placeableBlacklist.add("Test:Air");
|
||||||
|
|
||||||
register(new BlockData("Test:Dirt"));
|
registerSimplestBlock("Dirt");
|
||||||
register(new BlockRenderOpaqueCube("Test:Dirt", getBlockTexture("Dirt")));
|
registerSimplestBlock("Chernozem");
|
||||||
register(new BlockLogic("Test:Dirt"));
|
registerSimplestBlock("Stone");
|
||||||
|
registerSimplestBlock("Brick");
|
||||||
register(new BlockData("Test:Stone"));
|
registerSimplestBlock("BrickWhite");
|
||||||
register(new BlockRenderOpaqueCube("Test:Stone", getBlockTexture("Stone")));
|
registerSimplestBlock("Sand");
|
||||||
register(new BlockLogic("Test:Stone"));
|
registerSimplestBlock("Concrete");
|
||||||
|
registerSimplestBlock("WoodenPlank");
|
||||||
|
|
||||||
registerRocks();
|
registerRocks();
|
||||||
|
|
||||||
register(new BlockData("Test:Brick"));
|
|
||||||
register(new BlockRenderOpaqueCube("Test:Brick", getBlockTexture("Brick")));
|
|
||||||
register(new BlockLogic("Test:Brick"));
|
|
||||||
|
|
||||||
register(new BlockData("Test:BrickWhite"));
|
|
||||||
register(new BlockRenderOpaqueCube("Test:BrickWhite", getBlockTexture("BrickWhite")));
|
|
||||||
register(new BlockLogic("Test:BrickWhite"));
|
|
||||||
|
|
||||||
register(new BlockData("Test:Glass"));
|
register(new BlockData("Test:Glass"));
|
||||||
register(new BlockRenderTransparentCube("Test:Glass", getBlockTexture("Glass")));
|
register(new BlockRenderTransparentCube("Test:Glass", getBlockTexture("Glass")));
|
||||||
register(new TestBlockLogicGlass("Test:Glass"));
|
register(new TestBlockLogicGlass("Test:Glass"));
|
||||||
|
|
||||||
register(new BlockData("Test:Sand"));
|
|
||||||
register(new BlockRenderOpaqueCube("Test:Sand", getBlockTexture("Sand")));
|
|
||||||
register(new BlockLogic("Test:Sand"));
|
|
||||||
|
|
||||||
register(new BlockData("Test:Concrete"));
|
|
||||||
register(new BlockRenderOpaqueCube("Test:Concrete", getBlockTexture("ConcreteBlock")));
|
|
||||||
register(new BlockLogic("Test:Concrete"));
|
|
||||||
|
|
||||||
register(new BlockData("Test:Log"));
|
register(new BlockData("Test:Log"));
|
||||||
register(
|
register(
|
||||||
new BlockRenderOpaqueCube(
|
new BlockRenderOpaqueCube(
|
||||||
@ -136,13 +126,11 @@ public class TestContent {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
register(new BlockLogic("Test:Log"));
|
register(new BlockLogic("Test:Log"));
|
||||||
|
|
||||||
register(new BlockData("Test:TemporaryLeaves"));
|
register(new BlockData("Test:TemporaryLeaves"));
|
||||||
register(new BlockRenderTransparentCube("Test:TemporaryLeaves", getBlockTexture("TemporaryLeaves")));
|
register(new BlockRenderTransparentCube("Test:TemporaryLeaves", getBlockTexture("TemporaryLeaves")));
|
||||||
register(new TestBlockLogicGlass("Test:TemporaryLeaves")); // Sic, using Glass logic for leaves because Test
|
// Sic, using Glass logic for leaves because Test
|
||||||
|
register(new TestBlockLogicGlass("Test:TemporaryLeaves"));
|
||||||
register(new BlockData("Test:WoodenPlank"));
|
|
||||||
register(new BlockRenderOpaqueCube("Test:WoodenPlank", getBlockTexture("WoodenPlank")));
|
|
||||||
register(new BlockLogic("Test:WoodenPlank"));
|
|
||||||
|
|
||||||
BlockDataRegistry.getInstance().values().forEach(PLACEABLE_BLOCKS::add);
|
BlockDataRegistry.getInstance().values().forEach(PLACEABLE_BLOCKS::add);
|
||||||
PLACEABLE_BLOCKS.removeIf(b -> placeableBlacklist.contains(b.getId()));
|
PLACEABLE_BLOCKS.removeIf(b -> placeableBlacklist.contains(b.getId()));
|
||||||
@ -166,87 +154,112 @@ public class TestContent {
|
|||||||
private static void registerTiles() {
|
private static void registerTiles() {
|
||||||
Set<String> placeableBlacklist = new HashSet<>();
|
Set<String> placeableBlacklist = new HashSet<>();
|
||||||
|
|
||||||
register(new TileData("Test:Grass"));
|
Arrays.asList(
|
||||||
register(new TestTileRenderGrass("Test:Grass", getTileTexture("GrassTop"), getTileTexture("GrassSide")));
|
"Opaque",
|
||||||
register(new TestTileLogicGrass("Test:Grass"));
|
"Patches",
|
||||||
|
"Web",
|
||||||
|
"Threads"
|
||||||
|
).forEach(variant -> {
|
||||||
|
String fullName = "Grass" + variant;
|
||||||
|
String id = "Test:" + fullName;
|
||||||
|
|
||||||
register(new TileData("Test:Stones"));
|
register(new TileData(id));
|
||||||
register(new TileRenderTransparentSurface("Test:Stones", getTileTexture("Stones")));
|
register(
|
||||||
register(new HangingTileLogic("Test:Stones"));
|
new TestTileRenderGrass(
|
||||||
|
id,
|
||||||
|
getTileTexture(fullName + "Top"),
|
||||||
|
getTileTexture(fullName + "Side"),
|
||||||
|
variant.equals("Opaque")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
register(new TestTileLogicGrass(id));
|
||||||
|
});
|
||||||
|
|
||||||
register(new TileData("Test:YellowFlowers"));
|
Arrays.asList(
|
||||||
register(new TileRenderTransparentSurface("Test:YellowFlowers", getTileTexture("YellowFlowers")));
|
"Yellow",
|
||||||
register(new HangingTileLogic("Test:YellowFlowers"));
|
"White",
|
||||||
|
"Purple",
|
||||||
|
"Blue"
|
||||||
|
).forEach(color -> {
|
||||||
|
registerSimplestTransparentTile(color + "Flowers");
|
||||||
|
});
|
||||||
|
|
||||||
register(new TileData("Test:Sand"));
|
registerSimplestTransparentTile("Stones");
|
||||||
register(new TileRenderTransparentSurface("Test:Sand", getTileTexture("Sand")));
|
registerSimplestTransparentTile("Sand");
|
||||||
register(new HangingTileLogic("Test:Sand"));
|
|
||||||
|
|
||||||
register(new TileData("Test:SnowOpaque"));
|
registerSimplestOpaqueTile("SnowOpaque");
|
||||||
register(new TileRenderOpaqueSurface("Test:SnowOpaque", getTileTexture("SnowOpaque")));
|
Arrays.asList(
|
||||||
register(new HangingTileLogic("Test:SnowOpaque"));
|
"Half",
|
||||||
|
"Quarter"
|
||||||
|
).forEach(variant -> {
|
||||||
|
registerSimplestTransparentTile("Snow" + variant);
|
||||||
|
});
|
||||||
|
|
||||||
register(new TileData("Test:SnowHalf"));
|
registerSimplestTransparentTile("Clock");
|
||||||
register(new TileRenderTransparentSurface("Test:SnowHalf", getTileTexture("SnowHalf")));
|
registerSimplestOpaqueTile("CeilingTile1");
|
||||||
register(new HangingTileLogic("Test:SnowHalf"));
|
registerSimplestOpaqueTile("CeilingTile2");
|
||||||
|
registerSimplestOpaqueTile("WoodenPlank");
|
||||||
|
registerSimplestOpaqueTile("ParquetFloor");
|
||||||
|
registerSimplestOpaqueTile("Wallpaper");
|
||||||
|
registerSimplestOpaqueTile("WhitePaint");
|
||||||
|
registerSimplestOpaqueTile("RoughPaint");
|
||||||
|
registerSimplestOpaqueTile("DecorativeBricks");
|
||||||
|
registerSimplestTransparentTile("Painting");
|
||||||
|
registerSimplestOpaqueTile("TilesLarge");
|
||||||
|
registerSimplestOpaqueTile("TilesSmall");
|
||||||
|
|
||||||
register(new TileData("Test:SnowQuarter"));
|
registerHerb("LowGrass", 6);
|
||||||
register(new TileRenderTransparentSurface("Test:SnowQuarter", getTileTexture("SnowQuarter")));
|
registerHerb("MediumGrass", 6);
|
||||||
register(new HangingTileLogic("Test:SnowQuarter"));
|
registerHerb("TallGrass", 6);
|
||||||
|
|
||||||
register(new TileData("Test:Clock"));
|
Arrays.asList(
|
||||||
register(new TileRenderTransparentSurface("Test:Clock", getTileTexture("Clock")));
|
"Dandelion",
|
||||||
register(new HangingTileLogic("Test:Clock"));
|
"Lavander"
|
||||||
|
).forEach(variant -> {
|
||||||
|
String fullName = "Tiny" + variant + "Flowers";
|
||||||
|
String id = "Test:" + fullName;
|
||||||
|
|
||||||
register(new TileData("Test:CeilingTile1"));
|
register(new TileData(id));
|
||||||
register(new TileRenderOpaqueSurface("Test:CeilingTile1", getTileTexture("CeilingTile1")));
|
register(new TileRenderTinyFlower(id, getTileTexture(fullName), 8, 0.5f));
|
||||||
register(new HangingTileLogic("Test:CeilingTile1"));
|
register(new HangingTileLogic(id));
|
||||||
|
});
|
||||||
|
|
||||||
register(new TileData("Test:CeilingTile2"));
|
registerHerb("Bush", 1);
|
||||||
register(new TileRenderOpaqueSurface("Test:CeilingTile2", getTileTexture("CeilingTile2")));
|
registerHerb("Fern", 3);
|
||||||
register(new HangingTileLogic("Test:CeilingTile2"));
|
|
||||||
|
|
||||||
register(new TileData("Test:WoodenPlank"));
|
|
||||||
register(new TileRenderOpaqueSurface("Test:WoodenPlank", getTileTexture("WoodenPlank")));
|
|
||||||
register(new HangingTileLogic("Test:WoodenPlank"));
|
|
||||||
|
|
||||||
register(new TileData("Test:ParquetFloor"));
|
|
||||||
register(new TileRenderOpaqueSurface("Test:ParquetFloor", getTileTexture("ParquetFloor")));
|
|
||||||
register(new HangingTileLogic("Test:ParquetFloor"));
|
|
||||||
|
|
||||||
register(new TileData("Test:Wallpaper"));
|
|
||||||
register(new TileRenderOpaqueSurface("Test:Wallpaper", getTileTexture("Wallpaper")));
|
|
||||||
register(new HangingTileLogic("Test:Wallpaper"));
|
|
||||||
|
|
||||||
register(new TileData("Test:WhitePaint"));
|
|
||||||
register(new TileRenderOpaqueSurface("Test:WhitePaint", getTileTexture("WhitePaint")));
|
|
||||||
register(new HangingTileLogic("Test:WhitePaint"));
|
|
||||||
|
|
||||||
register(new TileData("Test:RoughPaint"));
|
|
||||||
register(new TileRenderOpaqueSurface("Test:RoughPaint", getTileTexture("RoughPaint")));
|
|
||||||
register(new HangingTileLogic("Test:RoughPaint"));
|
|
||||||
|
|
||||||
register(new TileData("Test:DecorativeBricks"));
|
|
||||||
register(new TileRenderOpaqueSurface("Test:DecorativeBricks", getTileTexture("DecorativeBricks")));
|
|
||||||
register(new HangingTileLogic("Test:DecorativeBricks"));
|
|
||||||
|
|
||||||
register(new TileData("Test:Painting"));
|
|
||||||
register(new TileRenderTransparentSurface("Test:Painting", getTileTexture("Painting")));
|
|
||||||
register(new HangingTileLogic("Test:Painting"));
|
|
||||||
|
|
||||||
register(new TileData("Test:TilesLarge"));
|
|
||||||
register(new TileRenderOpaqueSurface("Test:TilesLarge", getTileTexture("TilesLarge")));
|
|
||||||
register(new HangingTileLogic("Test:TilesLarge"));
|
|
||||||
|
|
||||||
register(new TileData("Test:TilesSmall"));
|
|
||||||
register(new TileRenderOpaqueSurface("Test:TilesSmall", getTileTexture("TilesSmall")));
|
|
||||||
register(new HangingTileLogic("Test:TilesSmall"));
|
|
||||||
|
|
||||||
TileDataRegistry.getInstance().values().forEach(PLACEABLE_TILES::add);
|
TileDataRegistry.getInstance().values().forEach(PLACEABLE_TILES::add);
|
||||||
PLACEABLE_TILES.removeIf(b -> placeableBlacklist.contains(b.getId()));
|
PLACEABLE_TILES.removeIf(b -> placeableBlacklist.contains(b.getId()));
|
||||||
PLACEABLE_TILES.sort(Comparator.comparing(TileData::getId));
|
PLACEABLE_TILES.sort(Comparator.comparing(TileData::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void registerSimplestBlock(String name) {
|
||||||
|
String id = "Test:" + name;
|
||||||
|
register(new BlockData(id));
|
||||||
|
register(new BlockRenderOpaqueCube(id, getBlockTexture(name)));
|
||||||
|
register(new BlockLogic(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerSimplestOpaqueTile(String name) {
|
||||||
|
String id = "Test:" + name;
|
||||||
|
register(new TileData(id));
|
||||||
|
register(new TileRenderOpaqueSurface(id, getTileTexture(name)));
|
||||||
|
register(new HangingTileLogic(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerSimplestTransparentTile(String name) {
|
||||||
|
String id = "Test:" + name;
|
||||||
|
register(new TileData(id));
|
||||||
|
register(new TileRenderTransparentSurface(id, getTileTexture(name)));
|
||||||
|
register(new HangingTileLogic(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerHerb(String name, int maxCount) {
|
||||||
|
String id = "Test:" + name;
|
||||||
|
register(new TileData(id));
|
||||||
|
register(new TileRenderHerb(id, getTileTexture(name), maxCount));
|
||||||
|
register(new HangingTileLogic(id));
|
||||||
|
}
|
||||||
|
|
||||||
private static void registerEntities() {
|
private static void registerEntities() {
|
||||||
float scale = 1.8f / 8;
|
float scale = 1.8f / 8;
|
||||||
registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4 * scale, 0.8f, 0.8f, 1.8f)));
|
registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4 * scale, 0.8f, 0.8f, 1.8f)));
|
||||||
@ -285,6 +298,7 @@ public class TestContent {
|
|||||||
i -> isAnythingSelected() && TestPlayerControls.getInstance().isBlockSelected()
|
i -> isAnythingSelected() && TestPlayerControls.getInstance().isBlockSelected()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
logic.register(ControlLogic.of("Test:PlaceBlock", TestContent::onBlockPlaceReceived));
|
logic.register(ControlLogic.of("Test:PlaceBlock", TestContent::onBlockPlaceReceived));
|
||||||
|
|
||||||
data.register("Test:PlaceTile", ControlPlaceTileData::new);
|
data.register("Test:PlaceTile", ControlPlaceTileData::new);
|
||||||
@ -433,16 +447,30 @@ public class TestContent {
|
|||||||
Vec3i blockInWorld = controlData.getBlockInWorld();
|
Vec3i blockInWorld = controlData.getBlockInWorld();
|
||||||
AbsFace face = controlData.getFace();
|
AbsFace face = controlData.getFace();
|
||||||
|
|
||||||
if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null)
|
if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null) {
|
||||||
return;
|
return;
|
||||||
if (server.getWorld().getData().getTiles(blockInWorld, face).isFull())
|
}
|
||||||
|
if (server.getWorld().getData().getTiles(blockInWorld, face).isFull()) {
|
||||||
return;
|
return;
|
||||||
server.createAbsoluteContext().addTile(blockInWorld, face.relativize(AbsFace.POS_Z), tile);
|
}
|
||||||
|
|
||||||
|
ServerBlockContext context = server.createContext(blockInWorld);
|
||||||
|
ServerTileStackContext tsContext = context.push(context.toContext(face));
|
||||||
|
ServerTileContext tileContext = tsContext.push(tsContext.getTileCount());
|
||||||
|
|
||||||
|
TileLogic logic = TileLogicRegistry.getInstance().get(tile.getId());
|
||||||
|
if (!logic.canOccupyFace(tileContext)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tileContext.addTile(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerMisc() {
|
private static void registerMisc() {
|
||||||
ChunkIO.registerCodec(new TestChunkCodec());
|
ChunkIO.registerCodec(new TestChunkCodec());
|
||||||
|
|
||||||
ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new);
|
ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new);
|
||||||
|
ChunkRenderOptimizerRegistry.getInstance().register("Core:SimpleOptimizer", ChunkRenderOptimizerSimple::new);
|
||||||
|
|
||||||
GravityModelRegistry.getInstance().register("Test:TheGravityModel", TestGravityModel::new);
|
GravityModelRegistry.getInstance().register("Test:TheGravityModel", TestGravityModel::new);
|
||||||
GravityModelRegistry.getInstance().register("Test:PlanetGravityModel", PlanetGravityModel::new);
|
GravityModelRegistry.getInstance().register("Test:PlanetGravityModel", PlanetGravityModel::new);
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,10 @@ import ru.windcorp.progressia.server.ServerState;
|
|||||||
|
|
||||||
public class TestPlayerControls {
|
public class TestPlayerControls {
|
||||||
|
|
||||||
private static final TestPlayerControls INSTANCE = new TestPlayerControls();
|
private static TestPlayerControls instance = new TestPlayerControls();
|
||||||
|
|
||||||
public static TestPlayerControls getInstance() {
|
public static TestPlayerControls getInstance() {
|
||||||
return INSTANCE;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final double MODE_SWITCH_MAX_DELAY = 300 * Units.MILLISECONDS;
|
private static final double MODE_SWITCH_MAX_DELAY = 300 * Units.MILLISECONDS;
|
||||||
@ -91,6 +91,10 @@ public class TestPlayerControls {
|
|||||||
private LayerTestGUI debugLayer = null;
|
private LayerTestGUI debugLayer = null;
|
||||||
private Runnable updateCallback = null;
|
private Runnable updateCallback = null;
|
||||||
|
|
||||||
|
public static void resetInstance() {
|
||||||
|
instance = new TestPlayerControls();
|
||||||
|
}
|
||||||
|
|
||||||
public void applyPlayerControls() {
|
public void applyPlayerControls() {
|
||||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||||
return;
|
return;
|
||||||
|
@ -26,15 +26,18 @@ public class TestTileRenderGrass extends TileRenderSurface {
|
|||||||
|
|
||||||
private final Texture topTexture;
|
private final Texture topTexture;
|
||||||
private final Texture sideTexture;
|
private final Texture sideTexture;
|
||||||
|
private final boolean isOpaque;
|
||||||
|
|
||||||
public TestTileRenderGrass(
|
public TestTileRenderGrass(
|
||||||
String id,
|
String id,
|
||||||
Texture top,
|
Texture top,
|
||||||
Texture side
|
Texture side,
|
||||||
|
boolean isOpaque
|
||||||
) {
|
) {
|
||||||
super(id);
|
super(id);
|
||||||
this.topTexture = top;
|
this.topTexture = top;
|
||||||
this.sideTexture = side;
|
this.sideTexture = side;
|
||||||
|
this.isOpaque = isOpaque;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -44,7 +47,7 @@ public class TestTileRenderGrass extends TileRenderSurface {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOpaque(RelFace face) {
|
public boolean isOpaque(RelFace face) {
|
||||||
return face == RelFace.UP;
|
return isOpaque && face == RelFace.UP;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* 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 glm.mat._4.Mat4;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
|
import ru.windcorp.progressia.client.world.tile.TileRenderCross;
|
||||||
|
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.rels.RelFace;
|
||||||
|
|
||||||
|
public class TileRenderHerb extends TileRenderCross {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stolen from OpenJDK's Random implementation
|
||||||
|
* *evil cackling*
|
||||||
|
*/
|
||||||
|
private static final long MULTIPLIER = 0x5DEECE66DL;
|
||||||
|
private static final long ADDEND = 0xBL;
|
||||||
|
private static final long MASK = (1L << 48) - 1;
|
||||||
|
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
|
||||||
|
|
||||||
|
private static long permute(long seed) {
|
||||||
|
return (seed * MULTIPLIER + ADDEND) & MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getDouble(long seed) {
|
||||||
|
final int mask26bits = (1 << 26) - 1;
|
||||||
|
final int mask27bits = (1 << 27) - 1;
|
||||||
|
|
||||||
|
int randomBitsX26 = (int) (seed & 0xFFFFFFFF);
|
||||||
|
int randomBitsX27 = (int) ((seed >>> Integer.SIZE) & 0xFFFFFFFF);
|
||||||
|
|
||||||
|
randomBitsX26 = randomBitsX26 & mask26bits;
|
||||||
|
randomBitsX27 = randomBitsX27 & mask27bits;
|
||||||
|
|
||||||
|
return (((long) (randomBitsX26) << 27) + randomBitsX27) * DOUBLE_UNIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int maxCount;
|
||||||
|
|
||||||
|
public TileRenderHerb(String id, Texture texture, int maxCount) {
|
||||||
|
super(id, texture, false);
|
||||||
|
this.maxCount = maxCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean getTransform(
|
||||||
|
DefaultChunkData chunk,
|
||||||
|
Vec3i relBlockInChunk,
|
||||||
|
RelFace blockFace,
|
||||||
|
int count,
|
||||||
|
Mat4 output
|
||||||
|
) {
|
||||||
|
|
||||||
|
long seed = permute(count ^ getId().hashCode());
|
||||||
|
seed = permute(seed + relBlockInChunk.x);
|
||||||
|
seed = permute(seed + relBlockInChunk.y);
|
||||||
|
seed = permute(seed + relBlockInChunk.z);
|
||||||
|
seed = permute(seed + blockFace.getId());
|
||||||
|
|
||||||
|
float x = (float) getDouble(seed) * 0.8f - 0.4f;
|
||||||
|
seed = permute(seed);
|
||||||
|
float y = (float) getDouble(seed) * 0.8f - 0.4f;
|
||||||
|
seed = permute(seed);
|
||||||
|
float size = (float) getDouble(seed) * 0.5f + 0.5f;
|
||||||
|
seed = permute(seed);
|
||||||
|
double rotation = getDouble(seed) * Math.PI / 8;
|
||||||
|
|
||||||
|
output.identity().translate(x, y, 0).scale(size).rotateZ(rotation);
|
||||||
|
return (count == 0) || ((count < maxCount) && (seed % 3 != 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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 glm.mat._4.Mat4;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
|
import ru.windcorp.progressia.common.world.DefaultChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.rels.RelFace;
|
||||||
|
|
||||||
|
public class TileRenderTinyFlower extends TileRenderHerb {
|
||||||
|
|
||||||
|
private final float size;
|
||||||
|
|
||||||
|
public TileRenderTinyFlower(String id, Texture texture, int maxCount, float size) {
|
||||||
|
super(id, texture, maxCount);
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean getTransform(
|
||||||
|
DefaultChunkData chunk,
|
||||||
|
Vec3i relBlockInChunk,
|
||||||
|
RelFace blockFace,
|
||||||
|
int count,
|
||||||
|
Mat4 output
|
||||||
|
) {
|
||||||
|
boolean result = super.getTransform(chunk, relBlockInChunk, blockFace, count, output);
|
||||||
|
output.scale(size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -215,20 +215,26 @@ public class Fields {
|
|||||||
return tweak(f, 1, 1, bias);
|
return tweak(f, 1, 1, bias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Field anti(Field f) {
|
||||||
|
return tweak(f, 1, -1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
public static Field octaves(Field f, double scaleFactor, double amplitudeFactor, int octaves) {
|
public static Field octaves(Field f, double scaleFactor, double amplitudeFactor, int octaves) {
|
||||||
return (x, y) -> {
|
return (x, y) -> {
|
||||||
double result = 0;
|
double result = 0;
|
||||||
|
|
||||||
double scale = 1;
|
double scale = 1;
|
||||||
double amplitude = 1;
|
double amplitude = 1;
|
||||||
|
double cumulativeAmplitude = 0;
|
||||||
|
|
||||||
for (int i = 0; i < octaves; ++i) {
|
for (int i = 0; i < octaves; ++i) {
|
||||||
result += f.compute(x * scale, y * scale) * amplitude;
|
result += f.compute(x * scale, y * scale) * amplitude;
|
||||||
|
cumulativeAmplitude += amplitude;
|
||||||
scale *= scaleFactor;
|
scale *= scaleFactor;
|
||||||
amplitude /= amplitudeFactor;
|
amplitude /= amplitudeFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result / cumulativeAmplitude;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* 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.gen;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.world.rels.RelFace;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
|
||||||
|
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
|
||||||
|
import ru.windcorp.progressia.server.world.generation.surface.SurfaceTopLayerFeature;
|
||||||
|
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
|
||||||
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
|
||||||
|
public class TestFlowerFeature extends SurfaceTopLayerFeature {
|
||||||
|
|
||||||
|
private static class FlowerGenerator {
|
||||||
|
private final TileData tile;
|
||||||
|
private final SurfaceFloatField floweriness;
|
||||||
|
|
||||||
|
public FlowerGenerator(TileData tile, Function<String, SurfaceFloatField> flowerinessGenerator) {
|
||||||
|
this.tile = tile;
|
||||||
|
this.floweriness = flowerinessGenerator.apply(tile.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generate(SurfaceBlockContext context) {
|
||||||
|
if (context.getRandom().nextDouble() < floweriness.get(context)) {
|
||||||
|
context.addTile(RelFace.UP, tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Set<String> soilWhitelist;
|
||||||
|
{
|
||||||
|
ImmutableSet.Builder<String> b = ImmutableSet.builder();
|
||||||
|
b.add("Test:Dirt", "Test:Stone");
|
||||||
|
TestContent.ROCKS.getRocks().forEach(rock -> rock.getBlocks().forEach(block -> b.add(block.getId())));
|
||||||
|
soilWhitelist = b.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final FlowerGenerator[] flowers;
|
||||||
|
|
||||||
|
public TestFlowerFeature(String id, Function<String, SurfaceFloatField> flowerinessGenerator) {
|
||||||
|
super(id);
|
||||||
|
|
||||||
|
this.flowers = TileDataRegistry.getInstance().values().stream()
|
||||||
|
.filter(tile -> tile.getName().endsWith("Flowers"))
|
||||||
|
.map(tile -> new FlowerGenerator(tile, flowerinessGenerator))
|
||||||
|
.toArray(FlowerGenerator[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processTopBlock(SurfaceBlockContext context) {
|
||||||
|
if (context.getLocation().z < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!soilWhitelist.contains(context.getBlock().getId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.pushRelative(RelFace.UP).logic().getBlock().isTransparent()) {
|
||||||
|
context.pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context.pop();
|
||||||
|
|
||||||
|
for (FlowerGenerator flower : flowers) {
|
||||||
|
flower.generate(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isSolid(SurfaceBlockContext context) {
|
||||||
|
return context.logic().getBlock().isSolid(RelFace.UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -49,9 +49,18 @@ public class TestGenerationConfig {
|
|||||||
private static final float CURVATURE = Units.get("100 m");
|
private static final float CURVATURE = Units.get("100 m");
|
||||||
private static final float INNER_RADIUS = Units.get("200 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 static Function<Server, WorldGenerator> createGenerator() {
|
public TestGenerationConfig() {
|
||||||
|
this.generator = createGenerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function<Server, WorldGenerator> getGenerator() {
|
||||||
|
return generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<Server, WorldGenerator> createGenerator() {
|
||||||
|
|
||||||
Planet planet = new Planet(
|
Planet planet = new Planet(
|
||||||
((int) PLANET_RADIUS) / Coordinates.CHUNK_SIZE,
|
((int) PLANET_RADIUS) / Coordinates.CHUNK_SIZE,
|
||||||
@ -60,7 +69,7 @@ public class TestGenerationConfig {
|
|||||||
INNER_RADIUS
|
INNER_RADIUS
|
||||||
);
|
);
|
||||||
|
|
||||||
TestHeightMap heightMap = new TestHeightMap(planet, planet.getRadius() / 4, FIELDS);
|
TestHeightMap heightMap = new TestHeightMap(planet, planet.getRadius() / 4, fields);
|
||||||
|
|
||||||
FloatRangeMap<TerrainLayer> layers = new ArrayFloatRangeMap<>();
|
FloatRangeMap<TerrainLayer> layers = new ArrayFloatRangeMap<>();
|
||||||
registerTerrainLayers(layers);
|
registerTerrainLayers(layers);
|
||||||
@ -72,11 +81,11 @@ public class TestGenerationConfig {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerTerrainLayers(FloatRangeMap<TerrainLayer> layers) {
|
private void registerTerrainLayers(FloatRangeMap<TerrainLayer> layers) {
|
||||||
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
|
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
|
||||||
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
|
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
|
||||||
|
|
||||||
SurfaceFloatField cliffs = FIELDS.get("Test:CliffSelector");
|
SurfaceFloatField cliffs = fields.get("Test:Cliff");
|
||||||
|
|
||||||
WorleyProceduralNoise.Builder<TerrainLayer> builder = WorleyProceduralNoise.builder();
|
WorleyProceduralNoise.Builder<TerrainLayer> builder = WorleyProceduralNoise.builder();
|
||||||
TestContent.ROCKS.getRocks().forEach(rock -> {
|
TestContent.ROCKS.getRocks().forEach(rock -> {
|
||||||
@ -88,9 +97,9 @@ public class TestGenerationConfig {
|
|||||||
}
|
}
|
||||||
}, 1);
|
}, 1);
|
||||||
});
|
});
|
||||||
SurfaceFloatField rockDepthOffsets = FIELDS.register(
|
SurfaceFloatField rockDepthOffsets = fields.register(
|
||||||
"Test:RockDepthOffsets",
|
"Test:RockDepthOffsets",
|
||||||
() -> tweak(FIELDS.primitive(), 40, 5)
|
() -> tweak(fields.primitive(), 40, 5)
|
||||||
);
|
);
|
||||||
RockLayer rockLayer = new RockLayer(builder.build(SEED), rockDepthOffsets);
|
RockLayer rockLayer = new RockLayer(builder.build(SEED), rockDepthOffsets);
|
||||||
|
|
||||||
@ -105,24 +114,35 @@ public class TestGenerationConfig {
|
|||||||
layers.put(4, Float.POSITIVE_INFINITY, rockLayer);
|
layers.put(4, Float.POSITIVE_INFINITY, rockLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerFeatures(List<SurfaceFeature> features) {
|
private void registerFeatures(List<SurfaceFeature> features) {
|
||||||
|
|
||||||
SurfaceFloatField forestiness = FIELDS.register(
|
SurfaceFloatField forestiness = fields.register(
|
||||||
"Test:Forestiness",
|
"Test:Forest",
|
||||||
() -> squash(scale(FIELDS.primitive(), 200), 5)
|
() -> squash(scale(fields.primitive(), 200), 5)
|
||||||
);
|
);
|
||||||
|
|
||||||
SurfaceFloatField floweriness = FIELDS.register(
|
SurfaceFloatField grassiness = fields.register(
|
||||||
"Test:Floweriness",
|
"Test:Grass",
|
||||||
f -> multiply(
|
f -> multiply(
|
||||||
scale(octaves(FIELDS.primitive(), 2, 2), 40),
|
tweak(octaves(fields.primitive(), 2, 2), 40, 0.5, 1.2),
|
||||||
tweak(FIELDS.get("Test:Forestiness", f), 1, -1, 1.1)
|
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(
|
||||||
|
"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))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
features.add(new TestBushFeature("Test:BushFeature", forestiness));
|
features.add(new TestBushFeature("Test:BushFeature", forestiness));
|
||||||
features.add(new TestTreeFeature("Test:TreeFeature", forestiness));
|
features.add(new TestTreeFeature("Test:TreeFeature", forestiness));
|
||||||
features.add(new TestGrassFeature("Test:GrassFeature", FIELDS.get("Test:CliffSelector"), floweriness));
|
features.add(new TestGrassFeature("Test:GrassFeature", grassiness));
|
||||||
|
features.add(new TestFlowerFeature("Test:FlowerFeature", floweriness));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,40 +23,58 @@ import java.util.Set;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.util.ArrayFloatRangeMap;
|
||||||
|
import ru.windcorp.progressia.common.util.FloatRangeMap;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
|
||||||
import ru.windcorp.progressia.common.world.rels.RelFace;
|
import ru.windcorp.progressia.common.world.rels.RelFace;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
|
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
|
||||||
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
|
import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
|
||||||
import ru.windcorp.progressia.server.world.generation.surface.SurfaceTopLayerFeature;
|
import ru.windcorp.progressia.server.world.generation.surface.SurfaceTopLayerFeature;
|
||||||
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
|
import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
|
||||||
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
|
||||||
public class TestGrassFeature extends SurfaceTopLayerFeature {
|
public class TestGrassFeature extends SurfaceTopLayerFeature {
|
||||||
|
|
||||||
private static final Set<String> WHITELIST = ImmutableSet.of(
|
private final Set<String> soilWhitelist;
|
||||||
"Test:Dirt",
|
{
|
||||||
"Test:Stone",
|
ImmutableSet.Builder<String> b = ImmutableSet.builder();
|
||||||
"Test:GraniteMonolith",
|
b.add("Test:Dirt", "Test:Stone");
|
||||||
"Test:GraniteCracked",
|
TestContent.ROCKS.getRocks().forEach(rock -> rock.getBlocks().forEach(block -> b.add(block.getId())));
|
||||||
"Test:GraniteGravel"
|
soilWhitelist = b.build();
|
||||||
);
|
}
|
||||||
|
|
||||||
private final SurfaceFloatField grassiness;
|
private final SurfaceFloatField grassiness;
|
||||||
private final SurfaceFloatField floweriness;
|
|
||||||
private final double scatterDensity = 1.0 / (3 * 3);
|
private final double scatterDensity = 1.0 / (3 * 3);
|
||||||
|
|
||||||
private final TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
|
private final BlockData chernozem = BlockDataRegistry.getInstance().get("Test:Chernozem");
|
||||||
private final List<TileData> flowers = ImmutableList.of(
|
|
||||||
TileDataRegistry.getInstance().get("Test:YellowFlowers")
|
private final FloatRangeMap<TileData> flatGrasses = new ArrayFloatRangeMap<>();
|
||||||
);
|
{
|
||||||
|
flatGrasses.put(0.4f, Float.POSITIVE_INFINITY, TileDataRegistry.getInstance().get("Test:GrassOpaque"));
|
||||||
|
flatGrasses.put(0.2f, 0.4f, TileDataRegistry.getInstance().get("Test:GrassPatches"));
|
||||||
|
flatGrasses.put(0.1f, 0.2f, TileDataRegistry.getInstance().get("Test:GrassWeb"));
|
||||||
|
flatGrasses.put(0.05f, 0.1f, TileDataRegistry.getInstance().get("Test:GrassThreads"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final FloatRangeMap<TileData> herbGrasses = new ArrayFloatRangeMap<>();
|
||||||
|
{
|
||||||
|
herbGrasses.put(0.6f, 1, TileDataRegistry.getInstance().get("Test:TallGrass"));
|
||||||
|
herbGrasses.put(0.4f, 0.6f, TileDataRegistry.getInstance().get("Test:MediumGrass"));
|
||||||
|
herbGrasses.put(0.1f, 0.4f, TileDataRegistry.getInstance().get("Test:LowGrass"));
|
||||||
|
}
|
||||||
|
|
||||||
private final List<TileData> scatter = ImmutableList.of(
|
private final List<TileData> scatter = ImmutableList.of(
|
||||||
TileDataRegistry.getInstance().get("Test:Stones"),
|
TileDataRegistry.getInstance().get("Test:Stones"),
|
||||||
TileDataRegistry.getInstance().get("Test:Sand")
|
TileDataRegistry.getInstance().get("Test:Sand"),
|
||||||
|
TileDataRegistry.getInstance().get("Test:Bush"),
|
||||||
|
TileDataRegistry.getInstance().get("Test:Fern")
|
||||||
);
|
);
|
||||||
|
|
||||||
public TestGrassFeature(String id, SurfaceFloatField grassiness, SurfaceFloatField floweriness) {
|
public TestGrassFeature(String id, SurfaceFloatField grassiness) {
|
||||||
super(id);
|
super(id);
|
||||||
this.grassiness = grassiness;
|
this.grassiness = grassiness;
|
||||||
this.floweriness = floweriness;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -64,7 +82,7 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
|
|||||||
if (context.getLocation().z < 0) {
|
if (context.getLocation().z < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!WHITELIST.contains(context.getBlock().getId())) {
|
if (!soilWhitelist.contains(context.getBlock().getId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,16 +92,8 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
|
|||||||
}
|
}
|
||||||
context.pop();
|
context.pop();
|
||||||
|
|
||||||
double grassiness = this.grassiness.get(context);
|
growGrass(context, this.grassiness.get(context));
|
||||||
if (grassiness < 0.2) {
|
|
||||||
growGrass(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
placeScatter(context);
|
placeScatter(context);
|
||||||
|
|
||||||
if (grassiness < 0.2) {
|
|
||||||
growFlowers(context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void placeScatter(SurfaceBlockContext context) {
|
private void placeScatter(SurfaceBlockContext context) {
|
||||||
@ -93,24 +103,32 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void growGrass(SurfaceBlockContext context) {
|
private void growGrass(SurfaceBlockContext context, double grassiness) {
|
||||||
|
TileData flatGrass = flatGrasses.get((float) grassiness);
|
||||||
|
if (flatGrass != null) {
|
||||||
for (RelFace face : RelFace.getFaces()) {
|
for (RelFace face : RelFace.getFaces()) {
|
||||||
if (face == RelFace.DOWN) continue;
|
if (face == RelFace.DOWN)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (context.pushRelative(face).logic().getBlock().isTransparent()) {
|
if (context.pushRelative(face).logic().getBlock().isTransparent()) {
|
||||||
context.pop();
|
context.pop();
|
||||||
context.addTile(face, grass);
|
context.addTile(face, flatGrass);
|
||||||
} else {
|
} else {
|
||||||
context.pop();
|
context.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (grassiness > 0.8 && context.getBlock().getId().equals("Test:Dirt")) {
|
||||||
|
context.setBlock(chernozem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void growFlowers(SurfaceBlockContext context) {
|
if (context.getRandom().nextDouble() < grassiness) {
|
||||||
if (context.getRandom().nextDouble() < floweriness.get(context)) {
|
TileData herbGrass = herbGrasses.get((float) grassiness);
|
||||||
TileData tile = pickRandom(context, flowers);
|
if (herbGrass != null) {
|
||||||
context.addTile(RelFace.UP, tile);
|
context.addTile(RelFace.UP, herbGrass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +54,9 @@ public class TestHeightMap implements SurfaceFloatField {
|
|||||||
tweak(octaves(fields.primitive(), 2, 3), 50, 0.2)
|
tweak(octaves(fields.primitive(), 2, 3), 50, 0.2)
|
||||||
);
|
);
|
||||||
|
|
||||||
fields.register("Test:CliffSelector", face, multiply(
|
fields.register("Test:Cliff", face, multiply(
|
||||||
shoreCliffSelector,
|
shoreCliffSelector,
|
||||||
bias(select(shoreCliffs, 0, 0.07), 0)
|
select(shoreCliffs, 0, 0.07)
|
||||||
));
|
));
|
||||||
|
|
||||||
fields.register("Test:Height", face, cutoff(add(
|
fields.register("Test:Height", face, cutoff(add(
|
||||||
|
@ -22,3 +22,13 @@ LayerTestGUI.IsFullscreen = Fullscreen: %5s (F11)
|
|||||||
LayerTestGUI.IsVSync = VSync: %5s (F12)
|
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.IsFullscreen = Полный экран: %5s (F11)
|
||||||
LayerTestGUI.IsVSync = Верт. синхр.: %5s (F12)
|
LayerTestGUI.IsVSync = Верт. синхр.: %5s (F12)
|
||||||
|
|
||||||
LayerButtonTest.Title = Тест Кнопок
|
LayerButtonTest.Title = Тест кнопок
|
||||||
|
LayerButtonTest.Return = Главное меню
|
||||||
|
|
||||||
|
LayerTitle.Title = Прогрессия
|
||||||
|
LayerTitle.Play = Играть
|
||||||
|
LayerTitle.Reset = Сбросить мир
|
||||||
|
LayerTitle.Options = Настройки
|
||||||
|
LayerTitle.Quit = Выход
|
||||||
|
|
||||||
|
LayerText.Load = Загрузка...
|
||||||
|
LayerText.Save = Сохранение...
|
BIN
src/main/resources/assets/textures/blocks/Chernozem.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 8.1 KiB |
BIN
src/main/resources/assets/textures/tiles/BlueFlowers.png
Normal file
After Width: | Height: | Size: 481 B |
BIN
src/main/resources/assets/textures/tiles/Bush.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/main/resources/assets/textures/tiles/Fern.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/main/resources/assets/textures/tiles/GrassOpaqueSide.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
src/main/resources/assets/textures/tiles/GrassOpaqueTop.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
src/main/resources/assets/textures/tiles/GrassPatchesSide.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
src/main/resources/assets/textures/tiles/GrassPatchesTop.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 4.5 KiB |
BIN
src/main/resources/assets/textures/tiles/GrassThreadsSide.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
src/main/resources/assets/textures/tiles/GrassThreadsTop.png
Normal file
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 7.7 KiB |
BIN
src/main/resources/assets/textures/tiles/GrassWebSide.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
src/main/resources/assets/textures/tiles/GrassWebTop.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
src/main/resources/assets/textures/tiles/LowGrass.png
Normal file
After Width: | Height: | Size: 448 B |
BIN
src/main/resources/assets/textures/tiles/MediumGrass.png
Normal file
After Width: | Height: | Size: 690 B |
BIN
src/main/resources/assets/textures/tiles/PurpleFlowers.png
Normal file
After Width: | Height: | Size: 519 B |
BIN
src/main/resources/assets/textures/tiles/TallGrass.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 4.3 KiB |
BIN
src/main/resources/assets/textures/tiles/TinyLavanderFlowers.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
src/main/resources/assets/textures/tiles/WhiteFlowers.png
Normal file
After Width: | Height: | Size: 586 B |
@ -27,6 +27,10 @@
|
|||||||
<Logger name="Ticker 0" level="DEBUG" />
|
<Logger name="Ticker 0" level="DEBUG" />
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!-- Uncomment to enable Region file logger debugging
|
||||||
|
<Logger name="TestWorldDiskIO" level="DEBUG" />
|
||||||
|
-->
|
||||||
|
|
||||||
<Root level="info">
|
<Root level="info">
|
||||||
<AppenderRef ref="FileLog" />
|
<AppenderRef ref="FileLog" />
|
||||||
<AppenderRef ref="Console" />
|
<AppenderRef ref="Console" />
|
||||||
|