Moved TestWorldDiskIO to a subpackage and introduced some abstractions

This commit is contained in:
OLEGSHA 2021-08-28 23:31:50 +03:00
parent 41a2909f7c
commit cd16334db8
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
9 changed files with 204 additions and 50 deletions

View File

@ -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);
@ -349,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) {

View File

@ -18,8 +18,14 @@
package ru.windcorp.progressia.server; package ru.windcorp.progressia.server;
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.test.gen.TestGenerationConfig; import ru.windcorp.progressia.test.gen.TestGenerationConfig;
import ru.windcorp.progressia.test.region.RegionFormat;
public class ServerState { public class ServerState {
@ -34,9 +40,14 @@ public class ServerState {
} }
public static void startServer() { public static void startServer() {
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() {

View File

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

View File

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

View File

@ -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;
import java.nio.file.Path;
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.Server;
public interface WorldContainer {
Path getPath();
DefaultChunkData load(Vec3i position, DefaultWorldData world, Server server);
void save(DefaultChunkData chunk, DefaultWorldData world, Server server);
void close();
}

View File

@ -0,0 +1,32 @@
/*
* 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.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);
}

View File

@ -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:Cliff"); 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,28 +114,28 @@ 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:Forest", "Test:Forest",
() -> squash(scale(FIELDS.primitive(), 200), 5) () -> squash(scale(fields.primitive(), 200), 5)
); );
SurfaceFloatField grassiness = FIELDS.register( SurfaceFloatField grassiness = fields.register(
"Test:Grass", "Test:Grass",
f -> multiply( f -> multiply(
tweak(octaves(FIELDS.primitive(), 2, 2), 40, 0.5, 1.2), tweak(octaves(fields.primitive(), 2, 2), 40, 0.5, 1.2),
squash(tweak(FIELDS.get("Test:Forest", f), 1, -1, 1), 10), squash(tweak(fields.get("Test:Forest", f), 1, -1, 1), 10),
anti(squash(FIELDS.get("Test:Cliff", f), 10)) anti(squash(fields.get("Test:Cliff", f), 10))
) )
); );
Function<String, SurfaceFloatField> floweriness = flowerName -> FIELDS.register( Function<String, SurfaceFloatField> floweriness = flowerName -> fields.register(
"Test:Flower" + flowerName, "Test:Flower" + flowerName,
f -> multiply( f -> multiply(
selectPositive(squash(scale(octaves(FIELDS.primitive(), 2, 3), 100), 2), 1, 0.5), selectPositive(squash(scale(octaves(fields.primitive(), 2, 3), 100), 2), 1, 0.5),
tweak(FIELDS.get("Test:Forest", f), 1, -1, 1.1), tweak(fields.get("Test:Forest", f), 1, -1, 1.1),
anti(squash(FIELDS.get("Test:Cliff", f), 10)) anti(squash(fields.get("Test:Cliff", f), 10))
) )
); );

View File

@ -0,0 +1,36 @@
/*
* 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.region;
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) {
return new TestWorldDiskIO(directory);
}
}

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package ru.windcorp.progressia.test; package ru.windcorp.progressia.test.region;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -52,12 +52,13 @@ import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.DefaultWorldData; import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.common.world.io.ChunkIO; import ru.windcorp.progressia.common.world.io.ChunkIO;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.io.WorldContainer;
public class TestWorldDiskIO { public class TestWorldDiskIO implements WorldContainer {
private static final boolean resetCorrupted = true; private static final boolean resetCorrupted = true;
public static class RandomFileMapped { public class RandomFileMapped {
public RandomAccessFile file; public RandomAccessFile file;
public HashMap<HashableVec3i, Integer> offsets; public HashMap<HashableVec3i, Integer> offsets;
public HashMap<HashableVec3i, Integer> lengths; public HashMap<HashableVec3i, Integer> lengths;
@ -121,11 +122,11 @@ public class TestWorldDiskIO {
} }
} }
private static Path SAVE_DIR = Paths.get("tmp_world"); private Path SAVE_DIR = Paths.get("tmp_world");
private static final String formatFile = "world.format"; private static final String formatFile = "world.format";
private static final Logger LOG = LogManager.getLogger("TestWorldDiskIO"); private static final Logger LOG = LogManager.getLogger("TestWorldDiskIO");
private static HashMap<HashableVec3i, RandomFileMapped> inOutMap; private HashMap<HashableVec3i, RandomFileMapped> inOutMap;
private static final boolean ENABLE = false; private static final boolean ENABLE = false;
private static int maxSize = 1048576; private static int maxSize = 1048576;
@ -134,11 +135,11 @@ public class TestWorldDiskIO {
private static final int bestFormat = 65536; private static final int bestFormat = 65536;
// private Map<Vec3i,Vec3i> regions = new HashMap<Vec3i,Vec3i>(); // private Map<Vec3i,Vec3i> regions = new HashMap<Vec3i,Vec3i>();
private static Vec3i regionSize; private Vec3i regionSize;
private static int chunksPerRegion; private int chunksPerRegion;
private static int currentFormat = -1; private int currentFormat = -1;
private static String extension = ".null"; private String extension = ".null";
private static int natFromInt(int loc) { private static int natFromInt(int loc) {
if (loc < 0) if (loc < 0)
@ -155,7 +156,7 @@ public class TestWorldDiskIO {
* } * }
*/ */
private static Vec3i getRegion(Vec3i chunkLoc) { private Vec3i getRegion(Vec3i chunkLoc) {
int x = chunkLoc.x; int x = chunkLoc.x;
if (x<0) if (x<0)
{ {
@ -197,15 +198,11 @@ public class TestWorldDiskIO {
return ((a % m) + m) % m; return ((a % m) + m) % m;
} }
private static Vec3i getRegionLoc(Vec3i chunkLoc) { private Vec3i getRegionLoc(Vec3i chunkLoc) {
return new Vec3i(mod(chunkLoc.x, regionSize.x), mod(chunkLoc.y, regionSize.y), mod(chunkLoc.z, regionSize.z)); return new Vec3i(mod(chunkLoc.x, regionSize.x), mod(chunkLoc.y, regionSize.y), mod(chunkLoc.z, regionSize.z));
} }
public static void initRegions() { public TestWorldDiskIO(Path worldPath) {
initRegions(null);
}
public static void initRegions(Path worldPath) {
if (worldPath != null) { if (worldPath != null) {
SAVE_DIR = worldPath; SAVE_DIR = worldPath;
} }
@ -224,7 +221,7 @@ public class TestWorldDiskIO {
return sectorsUsed; return sectorsUsed;
}*/ }*/
private static void setRegionSize(int format) { private void setRegionSize(int format) {
inOutMap = new HashMap<HashableVec3i, RandomFileMapped>(); inOutMap = new HashMap<HashableVec3i, RandomFileMapped>();
switch (format) { switch (format) {
case 65536: case 65536:
@ -243,7 +240,7 @@ public class TestWorldDiskIO {
} }
} }
public static boolean confirmHeaderHealth(RandomAccessFile input, HashMap<HashableVec3i, Integer> offsets, HashMap<HashableVec3i, Integer> length) throws IOException public boolean confirmHeaderHealth(RandomAccessFile input, HashMap<HashableVec3i, Integer> offsets, HashMap<HashableVec3i, Integer> length) throws IOException
{ {
Set<Integer> used = new HashSet<Integer>(); Set<Integer> used = new HashSet<Integer>();
input.seek(0); input.seek(0);
@ -282,7 +279,8 @@ public class TestWorldDiskIO {
return true; return true;
} }
public static void saveChunk(DefaultChunkData chunk, Server server) { @Override
public void save(DefaultChunkData chunk, DefaultWorldData world, Server server) {
if (!ENABLE) if (!ENABLE)
return; return;
@ -472,7 +470,7 @@ public class TestWorldDiskIO {
} }
} }
private static RandomFileMapped makeNew(Path path, Object hashObj) { private RandomFileMapped makeNew(Path path, Object hashObj) {
try try
{ {
RandomAccessFile raf = new RandomAccessFile(path.toFile(), "rw"); RandomAccessFile raf = new RandomAccessFile(path.toFile(), "rw");
@ -490,12 +488,13 @@ public class TestWorldDiskIO {
return null; return null;
} }
private static void writeGenerationHint(DefaultChunkData chunk, DataOutputStream output, Server server) private void writeGenerationHint(DefaultChunkData chunk, DataOutputStream output, Server server)
throws IOException { throws IOException {
server.getWorld().getGenerator().writeGenerationHint(output, chunk.getGenerationHint()); server.getWorld().getGenerator().writeGenerationHint(output, chunk.getGenerationHint());
} }
public static DefaultChunkData tryToLoad(Vec3i chunkPos, DefaultWorldData world, Server server) { @Override
public DefaultChunkData load(Vec3i chunkPos, DefaultWorldData world, Server server) {
if (!ENABLE) if (!ENABLE)
return null; return null;
@ -650,7 +649,7 @@ public class TestWorldDiskIO {
return null; return null;
} }
private static DefaultChunkData loadRegion(Path path, Vec3i chunkPos, DefaultWorldData world, Server server) private DefaultChunkData loadRegion(Path path, Vec3i chunkPos, DefaultWorldData world, Server server)
throws IOException, throws IOException,
DecodingException { DecodingException {
int offset = 0; int offset = 0;
@ -724,7 +723,7 @@ public class TestWorldDiskIO {
return null; return null;
} }
private static DefaultChunkData loadRegionX(Path path, Vec3i chunkPos, DefaultWorldData world, Server server) private DefaultChunkData loadRegionX(Path path, Vec3i chunkPos, DefaultWorldData world, Server server)
throws IOException, throws IOException,
DecodingException { DecodingException {
int offset = 0; int offset = 0;
@ -810,4 +809,15 @@ public class TestWorldDiskIO {
chunk.setGenerationHint(server.getWorld().getGenerator().readGenerationHint(input)); chunk.setGenerationHint(server.getWorld().getGenerator().readGenerationHint(input));
} }
@Override
public Path getPath() {
return SAVE_DIR;
}
@Override
public void close() {
// TODO Auto-generated method stub
}
} }