Added chunk transfer concept
- Added PacketLoadChunk - Added ChunkIO to handle converting ChunkData to/from bytes - Client no longer generates chunks by default - It still does generate them on request by server - DataBuffer now exposes InputStream and OutputStream
This commit is contained in:
parent
8977f460ca
commit
32ba0c78e5
@ -30,7 +30,7 @@ public class ClientState {
|
||||
|
||||
Client client = new Client(world, channel);
|
||||
|
||||
world.tmp_generate();
|
||||
// world.tmp_generate();
|
||||
|
||||
channel.connect();
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
package ru.windcorp.progressia.common.comms.packets;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.io.ChunkIO;
|
||||
import ru.windcorp.progressia.common.util.DataBuffer;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
|
||||
public class PacketLoadChunk extends PacketWorldChange {
|
||||
|
||||
private final DataBuffer data = new DataBuffer();
|
||||
private final Vec3i position = new Vec3i();
|
||||
|
||||
public PacketLoadChunk(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(WorldData world) {
|
||||
try {
|
||||
world.addChunk(ChunkIO.load(world, position, data.getInputStream()));
|
||||
} catch (DecodingException | IOException e) {
|
||||
CrashReports.report(e, "Could not load chunk");
|
||||
}
|
||||
}
|
||||
|
||||
public Vec3i getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public DataBuffer getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package ru.windcorp.progressia.common.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
|
||||
public abstract class ChunkCodec extends Namespaced {
|
||||
|
||||
private final byte signature;
|
||||
|
||||
public ChunkCodec(String id, byte signature) {
|
||||
super(id);
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public ChunkCodec(String id, int signature) {
|
||||
this(id, (byte) signature);
|
||||
}
|
||||
|
||||
public byte getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public abstract ChunkData decode(WorldData world, Vec3i position, InputStream data) throws DecodingException, IOException;
|
||||
|
||||
public abstract boolean shouldEncode(ChunkData chunk);
|
||||
|
||||
public abstract void encode(ChunkData chunk, OutputStream output) throws IOException;
|
||||
|
||||
}
|
99
src/main/java/ru/windcorp/progressia/common/io/ChunkIO.java
Normal file
99
src/main/java/ru/windcorp/progressia/common/io/ChunkIO.java
Normal file
@ -0,0 +1,99 @@
|
||||
package ru.windcorp.progressia.common.io;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import gnu.trove.map.TByteObjectMap;
|
||||
import gnu.trove.map.hash.TByteObjectHashMap;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
|
||||
public class ChunkIO {
|
||||
|
||||
private static final TByteObjectMap<ChunkCodec> CODECS_BY_ID = new TByteObjectHashMap<>();
|
||||
private static final List<ChunkCodec> CODECS_BY_PRIORITY = new ArrayList<>();
|
||||
|
||||
public static ChunkData load(WorldData world, Vec3i position, InputStream data)
|
||||
throws DecodingException, IOException
|
||||
{
|
||||
if (CODECS_BY_ID.isEmpty()) throw new IllegalStateException("No codecs registered");
|
||||
|
||||
int signature = data.read();
|
||||
if (signature < 0) throw new EOFException("Expected codec signature, got EOF");
|
||||
|
||||
ChunkCodec codec = getCodec((byte) signature);
|
||||
if (codec == null) {
|
||||
throw new DecodingException("Unknown codec signature " + Integer.toHexString(signature) + "; is it from the future?");
|
||||
}
|
||||
|
||||
try {
|
||||
return codec.decode(world, position, data);
|
||||
} catch (IOException | DecodingException e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
CrashReports.report(
|
||||
t, "Codec %s has failed to decode chunk (%d; %d; %d)",
|
||||
codec.getId(), position.x, position.y, position.z
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void save(ChunkData chunk, OutputStream output)
|
||||
throws IOException
|
||||
{
|
||||
ChunkCodec codec = getCodec(chunk);
|
||||
|
||||
try {
|
||||
output.write(codec.getSignature());
|
||||
codec.encode(chunk, output);
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
CrashReports.report(
|
||||
t, "Codec %s has failed to encode chunk (%d; %d; %d)",
|
||||
codec.getId(), chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static ChunkCodec getCodec(byte signature) {
|
||||
if (CODECS_BY_ID.isEmpty()) throw new IllegalStateException("No codecs registered");
|
||||
return CODECS_BY_ID.get(signature);
|
||||
}
|
||||
|
||||
public static ChunkCodec getCodec(ChunkData chunk) {
|
||||
for (ChunkCodec codec : CODECS_BY_PRIORITY) {
|
||||
if (codec.shouldEncode(chunk)) {
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
||||
if (CODECS_BY_ID.isEmpty()) throw new IllegalStateException("No codecs registered");
|
||||
return CODECS_BY_PRIORITY.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorted is order of decreasing priority
|
||||
* @return
|
||||
*/
|
||||
public static List<ChunkCodec> getCodecs() {
|
||||
return Collections.unmodifiableList(CODECS_BY_PRIORITY);
|
||||
}
|
||||
|
||||
public static void registerCodec(ChunkCodec codec) {
|
||||
CODECS_BY_PRIORITY.add(0, codec); // Add to the front
|
||||
CODECS_BY_ID.put(codec.getSignature(), codec);
|
||||
}
|
||||
|
||||
private ChunkIO() {}
|
||||
|
||||
}
|
@ -21,26 +21,25 @@ public class DataBuffer {
|
||||
|
||||
private int position;
|
||||
|
||||
private final DataInput reader = new DataInputStream(
|
||||
new InputStream() {
|
||||
private final InputStream inputStream = new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (position >= buffer.size()) return -1;
|
||||
int result = buffer.getQuick(position);
|
||||
++position;
|
||||
if (DataBuffer.this.position >= buffer.size()) return -1;
|
||||
int result = buffer.getQuick(DataBuffer.this.position);
|
||||
++DataBuffer.this.position;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
private final DataOutput writer = new DataOutputStream(
|
||||
new OutputStream() {
|
||||
private final OutputStream outputStream = new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
buffer.add((byte) b);
|
||||
DataBuffer.this.buffer.add((byte) b);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
private final DataInput reader = new DataInputStream(inputStream);
|
||||
private final DataOutput writer = new DataOutputStream(outputStream);
|
||||
|
||||
public DataBuffer(int capacity) {
|
||||
this.buffer = new TByteArrayList(capacity);
|
||||
@ -54,6 +53,16 @@ public class DataBuffer {
|
||||
this.buffer = new TByteArrayList(copyFrom.buffer);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
position = 0;
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
buffer.resetQuick();
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
public DataInput getReader() {
|
||||
position = 0;
|
||||
return reader;
|
||||
|
@ -0,0 +1,31 @@
|
||||
package ru.windcorp.progressia.common.world;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that some data could not be properly decoded.
|
||||
* @author javapony
|
||||
*/
|
||||
public class DecodingException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 3200709153311801198L;
|
||||
|
||||
public DecodingException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public DecodingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
public DecodingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DecodingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DecodingException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -62,7 +62,6 @@ public class WorldData {
|
||||
for (cursor.y = -(size / 2); cursor.y <= (size / 2); ++cursor.y) {
|
||||
ChunkData chunk = new ChunkData(cursor, this);
|
||||
TestContent.generateChunk(chunk);
|
||||
addChunkListeners(chunk);
|
||||
addChunk(chunk);
|
||||
}
|
||||
}
|
||||
@ -72,8 +71,20 @@ public class WorldData {
|
||||
getListeners().forEach(l -> l.getChunkListeners(this, chunk.getPosition(), chunk::addListener));
|
||||
}
|
||||
|
||||
private synchronized void addChunk(ChunkData chunk) {
|
||||
chunksByPos.put(getChunkKey(chunk), chunk);
|
||||
public synchronized void addChunk(ChunkData chunk) {
|
||||
addChunkListeners(chunk);
|
||||
|
||||
long key = getChunkKey(chunk);
|
||||
|
||||
ChunkData previous = chunksByPos.get(key);
|
||||
if (previous != null) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Chunk at (%d; %d; %d) already exists",
|
||||
chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z
|
||||
));
|
||||
}
|
||||
|
||||
chunksByPos.put(key, chunk);
|
||||
|
||||
chunk.forEachEntity(entity ->
|
||||
entitiesById.put(entity.getEntityId(), entity)
|
||||
@ -83,16 +94,16 @@ public class WorldData {
|
||||
getListeners().forEach(l -> l.onChunkLoaded(this, chunk));
|
||||
}
|
||||
|
||||
// private synchronized void removeChunk(ChunkData chunk) {
|
||||
// getListeners().forEach(l -> l.beforeChunkUnloaded(this, chunk));
|
||||
// chunk.beforeUnloaded();
|
||||
//
|
||||
// chunk.forEachEntity(entity ->
|
||||
// entitiesById.remove(entity.getEntityId())
|
||||
// );
|
||||
//
|
||||
// chunksByPos.remove(getChunkKey(chunk));
|
||||
// }
|
||||
public synchronized void removeChunk(ChunkData chunk) {
|
||||
getListeners().forEach(l -> l.beforeChunkUnloaded(this, chunk));
|
||||
chunk.beforeUnloaded();
|
||||
|
||||
chunk.forEachEntity(entity ->
|
||||
entitiesById.remove(entity.getEntityId())
|
||||
);
|
||||
|
||||
chunksByPos.remove(getChunkKey(chunk));
|
||||
}
|
||||
|
||||
private static long getChunkKey(ChunkData chunk) {
|
||||
return CoordinatePacker.pack3IntsIntoLong(chunk.getPosition());
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.windcorp.progressia.server.comms;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -10,7 +11,11 @@ import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import ru.windcorp.progressia.common.comms.CommsChannel.Role;
|
||||
import ru.windcorp.progressia.common.comms.CommsChannel.State;
|
||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketLoadChunk;
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer;
|
||||
import ru.windcorp.progressia.common.io.ChunkIO;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
|
||||
public class ClientManager {
|
||||
@ -34,12 +39,30 @@ public class ClientManager {
|
||||
}
|
||||
|
||||
public void addClient(Client client) {
|
||||
synchronized (client) {
|
||||
clientsById.put(client.getId(), client);
|
||||
|
||||
client.addListener(new DefaultServerCommsListener(this, client));
|
||||
|
||||
for (ChunkData chunk : server.getWorld().getData().getChunks()) {
|
||||
PacketLoadChunk packet = new PacketLoadChunk("Core:LoadChunk");
|
||||
packet.getPosition().set(
|
||||
chunk.getPosition().x,
|
||||
chunk.getPosition().y,
|
||||
chunk.getPosition().z
|
||||
);
|
||||
|
||||
try {
|
||||
ChunkIO.save(chunk, packet.getData().getOutputStream());
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "ClientManager fjcked up. javahorse stupid");
|
||||
}
|
||||
client.sendPacket(packet);
|
||||
}
|
||||
|
||||
client.sendPacket(new PacketSetLocalPlayer(0x42));
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnectClient(Client client) {
|
||||
client.disconnect();
|
||||
|
@ -0,0 +1,35 @@
|
||||
package ru.windcorp.progressia.server.world;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
||||
import ru.windcorp.progressia.common.world.Coordinates;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
|
||||
public class UpdateTriggerer implements ChunkDataListener {
|
||||
|
||||
private final Server server;
|
||||
|
||||
public UpdateTriggerer(Server server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkBlockChanged(
|
||||
ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current
|
||||
) {
|
||||
server.getWorldAccessor().triggerUpdates(Coordinates.getInWorld(chunk.getPosition(), blockInChunk, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkTilesChanged(
|
||||
ChunkData chunk, Vec3i blockInChunk, BlockFace face, TileData tile,
|
||||
boolean wasAdded
|
||||
) {
|
||||
server.getWorldAccessor().triggerUpdates(Coordinates.getInWorld(chunk.getPosition(), blockInChunk, null), face);
|
||||
}
|
||||
|
||||
}
|
@ -7,17 +7,15 @@ import java.util.Map;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
||||
import ru.windcorp.progressia.common.world.ChunkDataListeners;
|
||||
import ru.windcorp.progressia.common.world.Coordinates;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
import ru.windcorp.progressia.common.world.WorldDataListener;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||
import ru.windcorp.progressia.server.world.tile.TileLogic;
|
||||
import ru.windcorp.progressia.test.TestChunkSender;
|
||||
|
||||
public class WorldLogic {
|
||||
|
||||
@ -42,22 +40,8 @@ public class WorldLogic {
|
||||
}
|
||||
});
|
||||
|
||||
data.addListener(ChunkDataListeners.createAdder(new ChunkDataListener() {
|
||||
@Override
|
||||
public void onChunkBlockChanged(
|
||||
ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current
|
||||
) {
|
||||
getServer().getWorldAccessor().triggerUpdates(Coordinates.getInWorld(chunk.getPosition(), blockInChunk, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkTilesChanged(
|
||||
ChunkData chunk, Vec3i blockInChunk, BlockFace face, TileData tile,
|
||||
boolean wasAdded
|
||||
) {
|
||||
getServer().getWorldAccessor().triggerUpdates(Coordinates.getInWorld(chunk.getPosition(), blockInChunk, null), face);
|
||||
}
|
||||
}));
|
||||
data.addListener(ChunkDataListeners.createAdder(new UpdateTriggerer(server)));
|
||||
data.addListener(new TestChunkSender(server));
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
|
@ -0,0 +1,36 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.io.ChunkCodec;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
|
||||
public class TestChunkCodec extends ChunkCodec {
|
||||
|
||||
public TestChunkCodec() {
|
||||
super("Test:TestCodec", 0x00);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkData decode(WorldData world, Vec3i position, InputStream data) throws DecodingException, IOException {
|
||||
ChunkData chunk = new ChunkData(position, world);
|
||||
TestContent.generateChunk(chunk);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEncode(ChunkData chunk) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ChunkData chunk, OutputStream output) throws IOException {
|
||||
// Do nothing. Heh.
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketLoadChunk;
|
||||
import ru.windcorp.progressia.common.io.ChunkIO;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
import ru.windcorp.progressia.common.world.WorldDataListener;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
|
||||
public class TestChunkSender implements WorldDataListener {
|
||||
|
||||
private final Server server;
|
||||
|
||||
public TestChunkSender(Server server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkLoaded(WorldData world, ChunkData chunk) {
|
||||
PacketLoadChunk packet = new PacketLoadChunk("Core:LoadChunk");
|
||||
try {
|
||||
ChunkIO.save(chunk, packet.getData().getOutputStream());
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "TestChunkSender fjcked up. javahorse stupid");
|
||||
}
|
||||
server.getClientManager().broadcastGamePacket(packet);
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ import ru.windcorp.progressia.client.world.tile.*;
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||
import ru.windcorp.progressia.common.comms.controls.*;
|
||||
import ru.windcorp.progressia.common.io.ChunkIO;
|
||||
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
@ -41,6 +42,7 @@ public class TestContent {
|
||||
public static void registerContent() {
|
||||
registerWorldContent();
|
||||
regsiterControls();
|
||||
registerMisc();
|
||||
}
|
||||
|
||||
private static void registerWorldContent() {
|
||||
@ -328,4 +330,8 @@ public class TestContent {
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerMisc() {
|
||||
ChunkIO.registerCodec(new TestChunkCodec());
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user