Added world generation hints, altered ChunkIO slightly, fixed some bugs
- A custom Object can be appended to any ChunkData to save its generation status - Handled by WorldGenerator - Persistent - Used to determine whether a chunk is ready to be sent to client - ChunkIO now requires an IOConext - ChunkIO now requires Data{In,Out}putStreams (as a combination of Data{In,Out}put and {In,Out}putStream) - Fixed some minor bugs
This commit is contained in:
parent
77599e857d
commit
06f4b4637d
@ -1,10 +1,11 @@
|
|||||||
package ru.windcorp.progressia.common.io;
|
package ru.windcorp.progressia.common.io;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.state.IOContext;
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.DecodingException;
|
import ru.windcorp.progressia.common.world.DecodingException;
|
||||||
@ -27,10 +28,10 @@ public abstract class ChunkCodec extends Namespaced {
|
|||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract ChunkData decode(WorldData world, Vec3i position, InputStream data) throws DecodingException, IOException;
|
public abstract ChunkData decode(WorldData world, Vec3i position, DataInputStream input, IOContext context) throws DecodingException, IOException;
|
||||||
|
|
||||||
public abstract boolean shouldEncode(ChunkData chunk);
|
public abstract boolean shouldEncode(ChunkData chunk, IOContext context);
|
||||||
|
|
||||||
public abstract void encode(ChunkData chunk, OutputStream output) throws IOException;
|
public abstract void encode(ChunkData chunk, DataOutputStream output, IOContext context) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package ru.windcorp.progressia.common.io;
|
package ru.windcorp.progressia.common.io;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -11,6 +11,7 @@ import java.util.List;
|
|||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import gnu.trove.map.TByteObjectMap;
|
import gnu.trove.map.TByteObjectMap;
|
||||||
import gnu.trove.map.hash.TByteObjectHashMap;
|
import gnu.trove.map.hash.TByteObjectHashMap;
|
||||||
|
import ru.windcorp.progressia.common.state.IOContext;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.DecodingException;
|
import ru.windcorp.progressia.common.world.DecodingException;
|
||||||
@ -21,7 +22,7 @@ public class ChunkIO {
|
|||||||
private static final TByteObjectMap<ChunkCodec> CODECS_BY_ID = new TByteObjectHashMap<>();
|
private static final TByteObjectMap<ChunkCodec> CODECS_BY_ID = new TByteObjectHashMap<>();
|
||||||
private static final List<ChunkCodec> CODECS_BY_PRIORITY = new ArrayList<>();
|
private static final List<ChunkCodec> CODECS_BY_PRIORITY = new ArrayList<>();
|
||||||
|
|
||||||
public static ChunkData load(WorldData world, Vec3i position, InputStream data)
|
public static ChunkData load(WorldData world, Vec3i position, DataInputStream data, IOContext context)
|
||||||
throws DecodingException, IOException
|
throws DecodingException, IOException
|
||||||
{
|
{
|
||||||
if (CODECS_BY_ID.isEmpty()) throw new IllegalStateException("No codecs registered");
|
if (CODECS_BY_ID.isEmpty()) throw new IllegalStateException("No codecs registered");
|
||||||
@ -35,7 +36,7 @@ public class ChunkIO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return codec.decode(world, position, data);
|
return codec.decode(world, position, data, context);
|
||||||
} catch (IOException | DecodingException e) {
|
} catch (IOException | DecodingException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
@ -47,14 +48,14 @@ public class ChunkIO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void save(ChunkData chunk, OutputStream output)
|
public static void save(ChunkData chunk, DataOutputStream output, IOContext context)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
ChunkCodec codec = getCodec(chunk);
|
ChunkCodec codec = getCodec(chunk, context);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
output.write(codec.getSignature());
|
output.write(codec.getSignature());
|
||||||
codec.encode(chunk, output);
|
codec.encode(chunk, output, context);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
@ -70,9 +71,9 @@ public class ChunkIO {
|
|||||||
return CODECS_BY_ID.get(signature);
|
return CODECS_BY_ID.get(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChunkCodec getCodec(ChunkData chunk) {
|
public static ChunkCodec getCodec(ChunkData chunk, IOContext context) {
|
||||||
for (ChunkCodec codec : CODECS_BY_PRIORITY) {
|
for (ChunkCodec codec : CODECS_BY_PRIORITY) {
|
||||||
if (codec.shouldEncode(chunk)) {
|
if (codec.shouldEncode(chunk, context)) {
|
||||||
return codec;
|
return codec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +83,7 @@ public class ChunkIO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorted is order of decreasing priority
|
* Sorted in order of decreasing priority
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static List<ChunkCodec> getCodecs() {
|
public static List<ChunkCodec> getCodecs() {
|
||||||
|
@ -38,8 +38,8 @@ public class DataBuffer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final DataInput reader = new DataInputStream(inputStream);
|
private final DataInputStream reader = new DataInputStream(inputStream);
|
||||||
private final DataOutput writer = new DataOutputStream(outputStream);
|
private final DataOutputStream writer = new DataOutputStream(outputStream);
|
||||||
|
|
||||||
public DataBuffer(int capacity) {
|
public DataBuffer(int capacity) {
|
||||||
this.buffer = new TByteArrayList(capacity);
|
this.buffer = new TByteArrayList(capacity);
|
||||||
@ -53,25 +53,23 @@ public class DataBuffer {
|
|||||||
this.buffer = new TByteArrayList(copyFrom.buffer);
|
this.buffer = new TByteArrayList(copyFrom.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public DataInputStream getReader() {
|
||||||
position = 0;
|
|
||||||
return inputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OutputStream getOutputStream() {
|
|
||||||
buffer.resetQuick();
|
|
||||||
return outputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataInput getReader() {
|
|
||||||
position = 0;
|
position = 0;
|
||||||
return reader;
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataOutput getWriter() {
|
public InputStream getInputStream() {
|
||||||
|
return getReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataOutputStream getWriter() {
|
||||||
buffer.resetQuick();
|
buffer.resetQuick();
|
||||||
return writer;
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OutputStream getOutputStream() {
|
||||||
|
return getWriter();
|
||||||
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return buffer.size();
|
return buffer.size();
|
||||||
|
@ -59,6 +59,8 @@ implements GenericChunk<
|
|||||||
BLOCK_FACE_COUNT
|
BLOCK_FACE_COUNT
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private Object generationHint = null;
|
||||||
|
|
||||||
private final Collection<ChunkDataListener> listeners =
|
private final Collection<ChunkDataListener> listeners =
|
||||||
Collections.synchronizedCollection(new ArrayList<>());
|
Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
@ -238,6 +240,14 @@ implements GenericChunk<
|
|||||||
getListeners().forEach(l -> l.beforeChunkUnloaded(this));
|
getListeners().forEach(l -> l.beforeChunkUnloaded(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getGenerationHint() {
|
||||||
|
return generationHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGenerationHint(Object generationHint) {
|
||||||
|
this.generationHint = generationHint;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link TileDataStack} used internally by {@link ChunkData} to
|
* Implementation of {@link TileDataStack} used internally by {@link ChunkData} to
|
||||||
* actually store the tiles. This is basically an array wrapper with reporting
|
* actually store the tiles. This is basically an array wrapper with reporting
|
||||||
|
@ -6,6 +6,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.io.ChunkIO;
|
import ru.windcorp.progressia.common.io.ChunkIO;
|
||||||
|
import ru.windcorp.progressia.common.state.IOContext;
|
||||||
import ru.windcorp.progressia.common.util.DataBuffer;
|
import ru.windcorp.progressia.common.util.DataBuffer;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ public class PacketSendChunk extends PacketChunkChange {
|
|||||||
this.position.set(chunk.getX(), chunk.getY(), chunk.getZ());
|
this.position.set(chunk.getX(), chunk.getY(), chunk.getZ());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ChunkIO.save(chunk, this.data.getOutputStream());
|
ChunkIO.save(chunk, this.data.getWriter(), IOContext.COMMS);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Impossible
|
// Impossible
|
||||||
}
|
}
|
||||||
@ -50,7 +51,7 @@ public class PacketSendChunk extends PacketChunkChange {
|
|||||||
@Override
|
@Override
|
||||||
public void apply(WorldData world) {
|
public void apply(WorldData world) {
|
||||||
try {
|
try {
|
||||||
world.addChunk(ChunkIO.load(world, position, data.getInputStream()));
|
world.addChunk(ChunkIO.load(world, position, data.getReader(), IOContext.COMMS));
|
||||||
} catch (DecodingException | IOException e) {
|
} catch (DecodingException | IOException e) {
|
||||||
CrashReports.report(e, "Could not load chunk");
|
CrashReports.report(e, "Could not load chunk");
|
||||||
}
|
}
|
||||||
|
@ -101,8 +101,8 @@ public interface GenericWorld<
|
|||||||
return stack.get(layer);
|
return stack.get(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isChunkLoaded(Vec3i pos) {
|
default boolean isChunkLoaded(Vec3i chunkPos) {
|
||||||
return getChunk(pos) != null;
|
return getChunk(chunkPos) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isBlockLoaded(Vec3i blockInWorld) {
|
default boolean isBlockLoaded(Vec3i blockInWorld) {
|
||||||
|
@ -33,9 +33,12 @@ public class ChunkManager {
|
|||||||
|
|
||||||
public void updateQueues(Player player) {
|
public void updateQueues(Player player) {
|
||||||
toSend.clear();
|
toSend.clear();
|
||||||
toSend.addAll(requested);
|
|
||||||
toSend.removeAll(visible);
|
requested.forEachIn(server.getWorld(), chunk -> {
|
||||||
toSend.retainAll(loaded);
|
if (!chunk.isReady()) return;
|
||||||
|
if (visible.contains(chunk)) return;
|
||||||
|
toSend.add(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
toRevoke.clear();
|
toRevoke.clear();
|
||||||
toRevoke.addAll(visible);
|
toRevoke.addAll(visible);
|
||||||
@ -120,13 +123,13 @@ public class ChunkManager {
|
|||||||
|
|
||||||
WorldData world = getServer().getWorld().getData();
|
WorldData world = getServer().getWorld().getData();
|
||||||
|
|
||||||
ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world);
|
ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world, getServer());
|
||||||
if (chunk == null) {
|
if (chunk != null) {
|
||||||
chunk = getServer().getWorld().generate(chunkPos);
|
world.addChunk(chunk);
|
||||||
|
} else {
|
||||||
|
getServer().getWorld().generate(chunkPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
world.addChunk(chunk);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unloadChunk(Vec3i chunkPos) {
|
public void unloadChunk(Vec3i chunkPos) {
|
||||||
@ -143,7 +146,7 @@ public class ChunkManager {
|
|||||||
|
|
||||||
world.removeChunk(chunk);
|
world.removeChunk(chunk);
|
||||||
|
|
||||||
TestWorldDiskIO.saveChunk(chunk);
|
TestWorldDiskIO.saveChunk(chunk, getServer());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ public class ServerThread implements Runnable {
|
|||||||
try {
|
try {
|
||||||
server.tick();
|
server.tick();
|
||||||
ticker.runOneTick();
|
ticker.runOneTick();
|
||||||
} catch (Exception e) {
|
} catch (Throwable e) {
|
||||||
CrashReports.report(e, "Got an exception in the server thread");
|
CrashReports.report(e, "Got a throwable in the server thread");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,6 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import gnu.trove.TCollections;
|
import gnu.trove.TCollections;
|
||||||
import gnu.trove.map.TIntObjectMap;
|
import gnu.trove.map.TIntObjectMap;
|
||||||
@ -65,7 +63,6 @@ public class ClientManager {
|
|||||||
getServer().getPlayerManager().getPlayers().add(player);
|
getServer().getPlayerManager().getPlayers().add(player);
|
||||||
|
|
||||||
PacketSetLocalPlayer packet = new PacketSetLocalPlayer();
|
PacketSetLocalPlayer packet = new PacketSetLocalPlayer();
|
||||||
LogManager.getLogger().info("Sending local player ID {}", EntityData.formatEntityId(entity.getEntityId()));
|
|
||||||
packet.set(entity.getEntityId());
|
packet.set(entity.getEntityId());
|
||||||
client.sendPacket(packet);
|
client.sendPacket(packet);
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,10 @@ public class ChunkLogic implements GenericChunk<
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isReady() {
|
||||||
|
return getWorld().getGenerator().isChunkReady(getData().getGenerationHint());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasTickingBlocks() {
|
public boolean hasTickingBlocks() {
|
||||||
return !tickingBlocks.isEmpty();
|
return !tickingBlocks.isEmpty();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.generation;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.DecodingException;
|
||||||
|
|
||||||
|
public abstract class AbstractWorldGenerator<H> extends WorldGenerator {
|
||||||
|
|
||||||
|
private final Class<H> hintClass;
|
||||||
|
|
||||||
|
public AbstractWorldGenerator(String id, Class<H> hintClass) {
|
||||||
|
super(id);
|
||||||
|
this.hintClass = Objects.requireNonNull(hintClass, "hintClass");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object readGenerationHint(DataInputStream input) throws IOException, DecodingException {
|
||||||
|
return doReadGenerationHint(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void writeGenerationHint(DataOutputStream output, Object hint) throws IOException {
|
||||||
|
doWriteGenerationHint(output, hintClass.cast(hint));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract H doReadGenerationHint(DataInputStream input) throws IOException, DecodingException;
|
||||||
|
protected abstract void doWriteGenerationHint(DataOutputStream output, H hint) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isChunkReady(Object hint) {
|
||||||
|
return checkIsChunkReady(hintClass.cast(hint));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean checkIsChunkReady(H hint);
|
||||||
|
|
||||||
|
protected H getHint(ChunkData chunk) {
|
||||||
|
return hintClass.cast(chunk.getGenerationHint());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setHint(ChunkData chunk, H hint) {
|
||||||
|
chunk.setGenerationHint(hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,16 +1,25 @@
|
|||||||
package ru.windcorp.progressia.server.world.generation;
|
package ru.windcorp.progressia.server.world.generation;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.DecodingException;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
|
|
||||||
public abstract class WorldGenerator extends Namespaced {
|
public abstract class WorldGenerator extends Namespaced {
|
||||||
|
|
||||||
public WorldGenerator(String id) {
|
WorldGenerator(String id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
// package-private constructor; extend AbstractWorldGeneration
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract ChunkData generate(Vec3i chunkPos, WorldData world);
|
public abstract ChunkData generate(Vec3i chunkPos, WorldData world);
|
||||||
|
public abstract Object readGenerationHint(DataInputStream input) throws IOException, DecodingException;
|
||||||
|
public abstract void writeGenerationHint(DataOutputStream output, Object hint) throws IOException;
|
||||||
|
public abstract boolean isChunkReady(Object hint);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,5 +15,11 @@ public abstract class CachedChunkChange<P extends PacketChunkChange> extends Cac
|
|||||||
public void getRelevantChunk(Vec3i output) {
|
public void getRelevantChunk(Vec3i output) {
|
||||||
getPacket().getAffectedChunk(output);
|
getPacket().getAffectedChunk(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Vec3i getAffectedChunk(Vec3i output) {
|
||||||
|
getPacket().getAffectedChunk(output);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ import java.io.DataInputStream;
|
|||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -17,6 +15,7 @@ import gnu.trove.map.TObjectIntMap;
|
|||||||
import gnu.trove.map.hash.TObjectIntHashMap;
|
import gnu.trove.map.hash.TObjectIntHashMap;
|
||||||
import ru.windcorp.jputil.functions.ThrowingConsumer;
|
import ru.windcorp.jputil.functions.ThrowingConsumer;
|
||||||
import ru.windcorp.progressia.common.io.ChunkCodec;
|
import ru.windcorp.progressia.common.io.ChunkCodec;
|
||||||
|
import ru.windcorp.progressia.common.state.IOContext;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.DecodingException;
|
import ru.windcorp.progressia.common.world.DecodingException;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
@ -57,7 +56,7 @@ public class TestChunkCodec extends ChunkCodec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldEncode(ChunkData chunk) {
|
public boolean shouldEncode(ChunkData chunk, IOContext context) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,9 +65,7 @@ public class TestChunkCodec extends ChunkCodec {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChunkData decode(WorldData world, Vec3i position, InputStream inputStream) throws DecodingException, IOException {
|
public ChunkData decode(WorldData world, Vec3i position, DataInputStream input, IOContext context) throws DecodingException, IOException {
|
||||||
DataInput input = new DataInputStream(inputStream);
|
|
||||||
|
|
||||||
BlockData[] blockPalette = readBlockPalette(input);
|
BlockData[] blockPalette = readBlockPalette(input);
|
||||||
TileData[] tilePalette = readTilePalette(input);
|
TileData[] tilePalette = readTilePalette(input);
|
||||||
|
|
||||||
@ -136,10 +133,7 @@ public class TestChunkCodec extends ChunkCodec {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(ChunkData chunk, OutputStream outputStream) throws IOException {
|
public void encode(ChunkData chunk, DataOutputStream output, IOContext context) throws IOException {
|
||||||
|
|
||||||
DataOutput output = new DataOutputStream(outputStream);
|
|
||||||
|
|
||||||
Palette<BlockData> blockPalette = createBlockPalette(chunk);
|
Palette<BlockData> blockPalette = createBlockPalette(chunk);
|
||||||
Palette<TileData> tilePalette = createTilePalette(chunk);
|
Palette<TileData> tilePalette = createTilePalette(chunk);
|
||||||
|
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
package ru.windcorp.progressia.test.gen;
|
package ru.windcorp.progressia.test.gen;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.Coordinates;
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
|
import ru.windcorp.progressia.common.world.DecodingException;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
|
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
|
||||||
@ -10,28 +18,46 @@ import ru.windcorp.progressia.common.world.block.BlockFace;
|
|||||||
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.WorldLogic;
|
import ru.windcorp.progressia.server.world.WorldLogic;
|
||||||
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
|
import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator;
|
||||||
|
|
||||||
public class TestWorldGenerator extends WorldGenerator {
|
|
||||||
|
|
||||||
|
public class TestWorldGenerator extends AbstractWorldGenerator<Boolean> {
|
||||||
|
|
||||||
public TestWorldGenerator(WorldLogic world) {
|
public TestWorldGenerator(WorldLogic world) {
|
||||||
super("Test:WorldGenerator");
|
super("Test:WorldGenerator", Boolean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doReadGenerationHint(DataInputStream input) throws IOException, DecodingException {
|
||||||
|
return input.readBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doWriteGenerationHint(DataOutputStream output, Boolean hint) throws IOException {
|
||||||
|
output.writeBoolean(hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean checkIsChunkReady(Boolean hint) {
|
||||||
|
return hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChunkData generate(Vec3i chunkPos, WorldData world) {
|
public ChunkData generate(Vec3i chunkPos, WorldData world) {
|
||||||
|
ChunkData chunk = generateUnpopulated(chunkPos, world);
|
||||||
|
world.addChunk(chunk);
|
||||||
|
findAndPopulate(chunkPos, world);
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChunkData generateUnpopulated(Vec3i chunkPos, WorldData world) {
|
||||||
ChunkData chunk = new ChunkData(chunkPos, world);
|
ChunkData chunk = new ChunkData(chunkPos, world);
|
||||||
|
chunk.setGenerationHint(false);
|
||||||
|
|
||||||
final int bpc = ChunkData.BLOCKS_PER_CHUNK;
|
final int bpc = ChunkData.BLOCKS_PER_CHUNK;
|
||||||
|
|
||||||
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
|
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
|
||||||
BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone");
|
BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone");
|
||||||
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
|
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
|
||||||
|
|
||||||
TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
|
|
||||||
TileData stones = TileDataRegistry.getInstance().get("Test:Stones");
|
|
||||||
TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers");
|
|
||||||
TileData sand = TileDataRegistry.getInstance().get("Test:Sand");
|
|
||||||
|
|
||||||
final float maxHeight = 32;
|
final float maxHeight = 32;
|
||||||
final float rho = 2000;
|
final float rho = 2000;
|
||||||
@ -48,70 +74,134 @@ public class TestWorldGenerator extends WorldGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3i pos = new Vec3i();
|
VectorUtil.iterateCuboid(0, 0, 0, bpc, bpc, bpc, pos -> {
|
||||||
|
int layer = pos.z - heightMap[pos.x][pos.y];
|
||||||
for (pos.x = 0; pos.x < bpc; ++pos.x) {
|
|
||||||
for (pos.y = 0; pos.y < bpc; ++pos.y) {
|
if (layer < -4) {
|
||||||
for (pos.z = 0; pos.z < bpc; ++pos.z) {
|
chunk.setBlock(pos, stone, false);
|
||||||
|
} else if (layer < 0) {
|
||||||
int layer = pos.z - heightMap[pos.x][pos.y];
|
chunk.setBlock(pos, dirt, false);
|
||||||
|
} else {
|
||||||
if (layer < -4) {
|
chunk.setBlock(pos, air, false);
|
||||||
chunk.setBlock(pos, stone, false);
|
|
||||||
} else if (layer < 0) {
|
|
||||||
chunk.setBlock(pos, dirt, false);
|
|
||||||
} else {
|
|
||||||
chunk.setBlock(pos, air, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
for (int x = 0; x < bpc; ++x) {
|
|
||||||
for (int y = 0; y < bpc; ++y) {
|
|
||||||
|
|
||||||
// int z = heightMap[x][y];
|
|
||||||
|
|
||||||
for (int z = 0; z < bpc; ++z) {
|
|
||||||
|
|
||||||
pos.set(x, y, z);
|
|
||||||
int layer = pos.z - heightMap[x][y];
|
|
||||||
|
|
||||||
if (layer == -1) {
|
|
||||||
chunk.getTiles(pos, BlockFace.TOP).add(grass);
|
|
||||||
for (BlockFace face : BlockFace.getFaces()) {
|
|
||||||
if (face.getVector().z != 0) continue;
|
|
||||||
pos.add(face.getVector());
|
|
||||||
|
|
||||||
if (!ChunkData.isInBounds(pos) || (chunk.getBlock(pos) == air)) {
|
|
||||||
pos.sub(face.getVector());
|
|
||||||
chunk.getTiles(pos, face).add(grass);
|
|
||||||
} else {
|
|
||||||
pos.sub(face.getVector());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int hash = x*x * 19 ^ y*y * 41 ^ pos.z*pos.z * 147;
|
|
||||||
if (hash % 5 == 0) {
|
|
||||||
chunk.getTiles(pos, BlockFace.TOP).addFarthest(sand);
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = x*x * 13 ^ y*y * 37 ^ pos.z*pos.z * 129;
|
|
||||||
if (hash % 5 == 0) {
|
|
||||||
chunk.getTiles(pos, BlockFace.TOP).addFarthest(stones);
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = x*x * 17 ^ y*y * 39 ^ pos.z*pos.z * 131;
|
|
||||||
if (hash % 9 == 0) {
|
|
||||||
chunk.getTiles(pos, BlockFace.TOP).addFarthest(flowers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void findAndPopulate(Vec3i changePos, WorldData world) {
|
||||||
|
VectorUtil.iterateCuboidAround(changePos, 3, candidatePos -> {
|
||||||
|
if (canBePopulated(candidatePos, world)) {
|
||||||
|
populate(candidatePos, world);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canBePopulated(Vec3i candidatePos, WorldData world) {
|
||||||
|
Vec3i cursor = Vectors.grab3i();
|
||||||
|
|
||||||
|
ChunkData candidate = world.getChunk(candidatePos);
|
||||||
|
if (candidate == null || isChunkReady(candidate.getGenerationHint())) return false;
|
||||||
|
|
||||||
|
for (int dx = -1; dx <= 1; ++dx) {
|
||||||
|
cursor.x = candidatePos.x + dx;
|
||||||
|
for (int dy = -1; dy <= 1; ++dy) {
|
||||||
|
cursor.y = candidatePos.y + dy;
|
||||||
|
for (int dz = -1; dz <= 1; ++dz) {
|
||||||
|
|
||||||
|
if ((dx | dy | dz) == 0) continue;
|
||||||
|
|
||||||
|
cursor.z = candidatePos.z + dz;
|
||||||
|
|
||||||
|
ChunkData chunk = world.getChunk(cursor);
|
||||||
|
if (chunk == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vectors.release(cursor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populate(Vec3i chunkPos, WorldData world) {
|
||||||
|
Random random = new Random(chunkPos.x + chunkPos.y + chunkPos.z);
|
||||||
|
|
||||||
|
ChunkData chunk = world.getChunk(chunkPos);
|
||||||
|
assert chunk != null : "Something went wrong when populating chunk at (" + chunkPos.x + "; " + chunkPos.y + "; " + chunkPos.z + ")";
|
||||||
|
|
||||||
|
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
|
||||||
|
|
||||||
|
Vec3i biw = new Vec3i();
|
||||||
|
|
||||||
|
int minX = Coordinates.getInWorld(chunkPos.x, 0);
|
||||||
|
int maxX = Coordinates.getInWorld(chunkPos.x + 1, 0);
|
||||||
|
int minY = Coordinates.getInWorld(chunkPos.y, 0);
|
||||||
|
int maxY = Coordinates.getInWorld(chunkPos.y + 1, 0);
|
||||||
|
int minZ = Coordinates.getInWorld(chunkPos.z, 0);
|
||||||
|
int maxZ = Coordinates.getInWorld(chunkPos.z + 1, 0);
|
||||||
|
|
||||||
|
for (biw.x = minX; biw.x < maxX; ++biw.x) {
|
||||||
|
for (biw.y = minY; biw.y < maxY; ++biw.y) {
|
||||||
|
|
||||||
|
for (biw.z = minZ; biw.z < maxZ + 1 && world.getBlock(biw) != air; ++biw.z);
|
||||||
|
biw.z -= 1;
|
||||||
|
|
||||||
|
if (biw.z == maxZ) continue;
|
||||||
|
if (biw.z < minZ) continue;
|
||||||
|
|
||||||
|
addTiles(chunk, biw, world, random);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.setGenerationHint(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTiles(ChunkData chunk, Vec3i biw, WorldData world, Random random) {
|
||||||
|
addGrass(chunk, biw, world, random);
|
||||||
|
addDecor(chunk, biw, world, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addGrass(ChunkData chunk, Vec3i biw, WorldData world, Random random) {
|
||||||
|
BlockData air = BlockDataRegistry.getInstance().get("Test:Air");
|
||||||
|
TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
|
||||||
|
|
||||||
|
world.getTiles(biw, BlockFace.TOP).add(grass);
|
||||||
|
|
||||||
|
for (BlockFace face : BlockFace.getFaces()) {
|
||||||
|
if (face.getVector().z != 0) continue;
|
||||||
|
biw.add(face.getVector());
|
||||||
|
|
||||||
|
if (world.getBlock(biw) == air) {
|
||||||
|
biw.sub(face.getVector());
|
||||||
|
world.getTiles(biw, face).add(grass);
|
||||||
|
} else {
|
||||||
|
biw.sub(face.getVector());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDecor(ChunkData chunk, Vec3i biw, WorldData world, Random random) {
|
||||||
|
if (random.nextInt(8) == 0) {
|
||||||
|
world.getTiles(biw, BlockFace.TOP).addFarthest(
|
||||||
|
TileDataRegistry.getInstance().get("Test:Sand")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (random.nextInt(8) == 0) {
|
||||||
|
world.getTiles(biw, BlockFace.TOP).addFarthest(
|
||||||
|
TileDataRegistry.getInstance().get("Test:Stones")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (random.nextInt(8) == 0) {
|
||||||
|
world.getTiles(biw, BlockFace.TOP).addFarthest(
|
||||||
|
TileDataRegistry.getInstance().get("Test:YellowFlowers")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user