Introduced ChunkData and WorldDataListeners
- Added ChunkDataListener and WorldDataListener - Moved chunk render update requests to clientside ChunkDataListeners - Moved ChunkRender and ChunkLogic initialization into WDListeners
This commit is contained in:
parent
7f381c7a1f
commit
372f173723
@ -30,6 +30,8 @@ public class ClientState {
|
||||
|
||||
Client client = new Client(world, channel);
|
||||
|
||||
world.tmp_generate();
|
||||
|
||||
channel.connect();
|
||||
|
||||
setInstance(client);
|
||||
|
@ -5,14 +5,12 @@ import java.io.IOException;
|
||||
import ru.windcorp.jputil.chars.StringUtil;
|
||||
import ru.windcorp.progressia.client.Client;
|
||||
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
||||
import ru.windcorp.progressia.common.comms.CommsListener;
|
||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer;
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.entity.PacketEntityChange;
|
||||
|
||||
// TODO refactor with no mercy
|
||||
public class DefaultClientCommsListener implements CommsListener {
|
||||
@ -29,10 +27,6 @@ public class DefaultClientCommsListener implements CommsListener {
|
||||
((PacketWorldChange) packet).apply(
|
||||
getClient().getWorld().getData()
|
||||
);
|
||||
|
||||
if (!(packet instanceof PacketEntityChange)) {
|
||||
tmp_reassembleWorld();
|
||||
}
|
||||
} else if (packet instanceof PacketSetLocalPlayer) {
|
||||
setLocalPlayer((PacketSetLocalPlayer) packet);
|
||||
}
|
||||
@ -57,10 +51,6 @@ public class DefaultClientCommsListener implements CommsListener {
|
||||
));
|
||||
}
|
||||
|
||||
private void tmp_reassembleWorld() {
|
||||
getClient().getWorld().getChunks().forEach(ChunkRender::markForUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIOError(IOException reason) {
|
||||
// TODO implement
|
||||
|
@ -0,0 +1,19 @@
|
||||
package ru.windcorp.progressia.client.world;
|
||||
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
||||
|
||||
class ChunkUpdateListener implements ChunkDataListener {
|
||||
|
||||
private final WorldRender world;
|
||||
|
||||
public ChunkUpdateListener(WorldRender world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkChanged(ChunkData chunk) {
|
||||
world.getChunk(chunk).markForUpdate();
|
||||
}
|
||||
|
||||
}
|
@ -29,7 +29,9 @@ import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry;
|
||||
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.ChunkDataListeners;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
import ru.windcorp.progressia.common.world.WorldDataListener;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public class WorldRender {
|
||||
@ -43,9 +45,18 @@ public class WorldRender {
|
||||
public WorldRender(WorldData data) {
|
||||
this.data = data;
|
||||
|
||||
for (ChunkData chunkData : data.getChunks()) {
|
||||
chunks.put(chunkData, new ChunkRender(this, chunkData));
|
||||
data.addListener(ChunkDataListeners.createAdder(new ChunkUpdateListener(this)));
|
||||
data.addListener(new WorldDataListener() {
|
||||
@Override
|
||||
public void onChunkLoaded(WorldData world, ChunkData chunk) {
|
||||
chunks.put(chunk, new ChunkRender(WorldRender.this, chunk));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeChunkUnloaded(WorldData world, ChunkData chunk) {
|
||||
chunks.remove(chunk);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public WorldData getData() {
|
||||
|
@ -20,6 +20,7 @@ package ru.windcorp.progressia.common.world;
|
||||
import static ru.windcorp.progressia.common.world.block.BlockFace.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
@ -63,8 +64,11 @@ public class ChunkData {
|
||||
private final List<EntityData> entities =
|
||||
Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
public ChunkData(int x, int y, int z, WorldData world) {
|
||||
this.position.set(x, y, z);
|
||||
private final Collection<ChunkDataListener> listeners =
|
||||
Collections.synchronizedCollection(new ArrayList<>());
|
||||
|
||||
public ChunkData(Vec3i position, WorldData world) {
|
||||
this.position.set(position.x, position.y, position.z);
|
||||
this.world = world;
|
||||
|
||||
tmp_generate();
|
||||
@ -92,11 +96,11 @@ public class ChunkData {
|
||||
pos.set(x, y, z);
|
||||
|
||||
if (f > 17) {
|
||||
setBlock(pos, stone);
|
||||
setBlock(pos, stone, false);
|
||||
} else if (f > 14) {
|
||||
setBlock(pos, dirt);
|
||||
setBlock(pos, dirt, false);
|
||||
} else {
|
||||
setBlock(pos, air);
|
||||
setBlock(pos, air, false);
|
||||
}
|
||||
|
||||
}
|
||||
@ -152,8 +156,16 @@ public class ChunkData {
|
||||
return blocks[getBlockIndex(posInChunk)];
|
||||
}
|
||||
|
||||
public void setBlock(Vec3i posInChunk, BlockData block) {
|
||||
public void setBlock(Vec3i posInChunk, BlockData block, boolean notify) {
|
||||
BlockData previous = blocks[getBlockIndex(posInChunk)];
|
||||
blocks[getBlockIndex(posInChunk)] = block;
|
||||
|
||||
if (notify) {
|
||||
getListeners().forEach(l -> {
|
||||
l.onChunkBlockChanged(this, posInChunk, previous, block);
|
||||
l.onChunkChanged(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public List<TileData> getTilesOrNull(Vec3i blockInChunk, BlockFace face) {
|
||||
@ -245,7 +257,7 @@ public class ChunkData {
|
||||
|
||||
private static void checkLocalCoordinates(Vec3i posInChunk) {
|
||||
if (!isInBounds(posInChunk)) {
|
||||
throw new IllegalArgumentException(
|
||||
throw new IllegalCoordinatesException(
|
||||
"Coordinates " + str(posInChunk) + " "
|
||||
+ "are not legal chunk coordinates"
|
||||
);
|
||||
@ -331,8 +343,28 @@ public class ChunkData {
|
||||
return world;
|
||||
}
|
||||
|
||||
public Collection<ChunkDataListener> getListeners() {
|
||||
return listeners;
|
||||
}
|
||||
|
||||
public void addListener(ChunkDataListener listener) {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(ChunkDataListener listener) {
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
private static String str(Vec3i v) {
|
||||
return "(" + v.x + "; " + v.y + "; " + v.z + ")";
|
||||
}
|
||||
|
||||
protected void onLoaded() {
|
||||
getListeners().forEach(l -> l.onChunkLoaded(this));
|
||||
}
|
||||
|
||||
protected void beforeUnloaded() {
|
||||
getListeners().forEach(l -> l.beforeChunkUnloaded(this));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
package ru.windcorp.progressia.common.world;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
|
||||
public interface ChunkDataListener {
|
||||
|
||||
/**
|
||||
* Invoked after a block has changed in a chunk.
|
||||
* This is not triggered when a change is caused by chunk loading or unloading.
|
||||
* @param chunk the chunk that has changed
|
||||
* @param blockInChunk the {@linkplain Coordinates#blockInChunk chunk coordinates} of the change
|
||||
* @param previous the previous occupant of {@code blockInChunk}
|
||||
* @param current the current (new) occupant of {@code blockInChunk}
|
||||
*/
|
||||
default void onChunkBlockChanged(ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current) {}
|
||||
|
||||
/**
|
||||
* Invoked after a tile has been added or removed from a chunk.
|
||||
* This is not triggered when a change is caused by chunk loading or unloading.
|
||||
* @param chunk the chunk that has changed
|
||||
* @param blockInChunk the {@linkplain Coordinates#blockInChunk chunk coordinates} of the change
|
||||
* @param face the face that the changed tile belongs or belonged to
|
||||
* @param tile the tile that has been added or removed
|
||||
* @param wasAdded {@code true} iff the tile has been added, {@code false} iff the tile has been removed
|
||||
*/
|
||||
default void onChunkTilesChanged(ChunkData chunk, Vec3i blockInChunk, BlockFace face, TileData tile, boolean wasAdded) {}
|
||||
|
||||
/**
|
||||
* Invoked whenever a chunk changes, loads or unloads. If some other method in this
|
||||
* {@code ChunkDataListener} are to be invoked, e.g. is the change was caused by a
|
||||
* block being removed, this method is called last.
|
||||
* @param chunk the chunk that has changed
|
||||
*/
|
||||
default void onChunkChanged(ChunkData chunk) {}
|
||||
|
||||
/**
|
||||
* Invoked whenever a chunk has been loaded.
|
||||
* @param chunk the chunk that has loaded
|
||||
*/
|
||||
default void onChunkLoaded(ChunkData chunk) {}
|
||||
|
||||
/**
|
||||
* Invoked whenever a chunk is about to be unloaded.
|
||||
* @param chunk the chunk that is going to be loaded
|
||||
*/
|
||||
default void beforeChunkUnloaded(ChunkData chunk) {}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package ru.windcorp.progressia.common.world;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
|
||||
public class ChunkDataListeners {
|
||||
|
||||
public static WorldDataListener createAdder(Supplier<ChunkDataListener> listenerSupplier) {
|
||||
return new WorldDataListener() {
|
||||
@Override
|
||||
public void getChunkListeners(WorldData world, Vec3i chunk, Consumer<ChunkDataListener> chunkListenerSink) {
|
||||
chunkListenerSink.accept(listenerSupplier.get());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static WorldDataListener createAdder(ChunkDataListener listener) {
|
||||
return createAdder(() -> listener);
|
||||
}
|
||||
|
||||
private ChunkDataListeners() {}
|
||||
|
||||
}
|
@ -11,18 +11,17 @@ import glm.vec._3.i.Vec3i;
|
||||
* Three types of coordinates are used in Progressia:
|
||||
* <ul>
|
||||
*
|
||||
* <li><em>World coordinates</em>, in code referred to as {@code blockInWorld} -
|
||||
* <li id="blockInWorld"><em>World coordinates</em>, in code referred to as {@code blockInWorld} -
|
||||
* coordinates relative to world origin. Every block in the world has unique
|
||||
* world coordinates.</li>
|
||||
*
|
||||
* <li><em>Chunk coordinates</em>, in code referred to as {@code blockInChunk} -
|
||||
* <li id="blockInChunk"><em>Chunk coordinates</em>, in code referred to as {@code blockInChunk} -
|
||||
* coordinates relative some chunk's origin. Every block in the chunk has unique
|
||||
* chunk coordinates, but blocks in different chunks may have identical chunk
|
||||
* coordinates. These coordinates are only useful in combination with a chunk
|
||||
* reference. Chunk coordinates are always <tt>[0; {@link #BLOCKS_PER_CHUNK})
|
||||
* </tt>.</li>
|
||||
* reference. Chunk coordinates are always <tt>[0; {@link #BLOCKS_PER_CHUNK})</tt>.</li>
|
||||
*
|
||||
* <li><em>Coordinates of chunk</em>, in code referred to as {@code chunk} -
|
||||
* <li id="chunk"><em>Coordinates of chunk</em>, in code referred to as {@code chunk} -
|
||||
* chunk coordinates relative to world origin. Every chunk in the world has
|
||||
* unique coordinates of chunk.</li>
|
||||
*
|
||||
|
@ -0,0 +1,28 @@
|
||||
package ru.windcorp.progressia.common.world;
|
||||
|
||||
public class IllegalCoordinatesException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1362481281554206710L;
|
||||
|
||||
public IllegalCoordinatesException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public IllegalCoordinatesException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public IllegalCoordinatesException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public IllegalCoordinatesException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public IllegalCoordinatesException(String message, Throwable cause, boolean enableSuppression,
|
||||
boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.progressia.common.world;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
@ -46,30 +47,50 @@ public class WorldData {
|
||||
|
||||
private float time = 0;
|
||||
|
||||
public WorldData() {
|
||||
final int size = 1;
|
||||
private final Collection<WorldDataListener> listeners =
|
||||
Collections.synchronizedCollection(new ArrayList<>());
|
||||
|
||||
for (int x = -(size / 2); x <= (size / 2); ++x) {
|
||||
for (int y = -(size / 2); y <= (size / 2); ++y) {
|
||||
addChunk(new ChunkData(x, y, 0, this));
|
||||
public WorldData() {
|
||||
|
||||
}
|
||||
|
||||
public void tmp_generate() {
|
||||
final int size = 1;
|
||||
Vec3i cursor = new Vec3i(0, 0, 0);
|
||||
|
||||
for (cursor.x = -(size / 2); cursor.x <= (size / 2); ++cursor.x) {
|
||||
for (cursor.y = -(size / 2); cursor.y <= (size / 2); ++cursor.y) {
|
||||
ChunkData chunk = new ChunkData(cursor, this);
|
||||
addChunkListeners(chunk);
|
||||
addChunk(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addChunkListeners(ChunkData chunk) {
|
||||
getListeners().forEach(l -> l.getChunkListeners(this, chunk.getPosition(), chunk::addListener));
|
||||
}
|
||||
|
||||
private synchronized void addChunk(ChunkData chunk) {
|
||||
chunksByPos.put(getChunkKey(chunk), chunk);
|
||||
|
||||
chunk.forEachEntity(entity ->
|
||||
entitiesById.put(entity.getEntityId(), entity)
|
||||
);
|
||||
|
||||
chunk.onLoaded();
|
||||
getListeners().forEach(l -> l.onChunkLoaded(this, chunk));
|
||||
}
|
||||
|
||||
// private synchronized void removeChunk(ChunkData chunk) {
|
||||
// chunksByPos.remove(getChunkKey(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) {
|
||||
@ -99,6 +120,20 @@ public class WorldData {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setBlock(Vec3i blockInWorld, BlockData block, boolean notify) {
|
||||
ChunkData chunk = getChunkByBlock(blockInWorld);
|
||||
if (chunk == null)
|
||||
throw new IllegalCoordinatesException(
|
||||
"Coordinates "
|
||||
+ "(" + blockInWorld.x + "; " + blockInWorld.y + "; " + blockInWorld.z + ") "
|
||||
+ "do not belong to a loaded chunk"
|
||||
);
|
||||
|
||||
Vec3i blockInChunk = Vectors.grab3i();
|
||||
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
|
||||
chunk.setBlock(blockInChunk, block, notify);
|
||||
}
|
||||
|
||||
public Collection<ChunkData> getChunks() {
|
||||
return chunks;
|
||||
}
|
||||
@ -132,4 +167,16 @@ public class WorldData {
|
||||
return block.getCollisionModel();
|
||||
}
|
||||
|
||||
public Collection<WorldDataListener> getListeners() {
|
||||
return listeners;
|
||||
}
|
||||
|
||||
public void addListener(WorldDataListener e) {
|
||||
listeners.add(e);
|
||||
}
|
||||
|
||||
public void removeListener(WorldDataListener o) {
|
||||
listeners.remove(o);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package ru.windcorp.progressia.common.world;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
|
||||
public interface WorldDataListener {
|
||||
|
||||
/**
|
||||
* Invoked when a new {@link ChunkData} instance is created. This method should be used to add
|
||||
* {@link ChunkDataListener}s to a new chunk. When listeners are added with this method,
|
||||
* their {@link ChunkDataListener#onChunkLoaded(ChunkData) onChunkLoaded} methods will be invoked.
|
||||
* @param world the world instance
|
||||
* @param chunk the {@linkplain Coordinates#chunk coordinates of chunk} of the chunk about to load
|
||||
* @param chunkListenerSink a sink for listeners. All listeners passed to its
|
||||
* {@link Consumer#accept(Object) accept} method will be added to the chunk.
|
||||
*/
|
||||
default void getChunkListeners(WorldData world, Vec3i chunk, Consumer<ChunkDataListener> chunkListenerSink) {}
|
||||
|
||||
/**
|
||||
* Invoked whenever a {@link Chunk} has been loaded.
|
||||
* @param world the world instance
|
||||
* @param chunk the chunk that has loaded
|
||||
*/
|
||||
default void onChunkLoaded(WorldData world, ChunkData chunk) {}
|
||||
|
||||
/**
|
||||
* Invoked whenever a {@link Chunk} is about to be unloaded.
|
||||
* @param world the world instance
|
||||
* @param chunk the chunk that is going to be unloaded
|
||||
*/
|
||||
default void beforeChunkUnloaded(WorldData world, ChunkData chunk) {}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@ public class ServerState {
|
||||
|
||||
public static void startServer() {
|
||||
Server server = new Server(new WorldData());
|
||||
server.getWorld().getData().tmp_generate();
|
||||
setInstance(server);
|
||||
server.start();
|
||||
}
|
||||
|
@ -5,8 +5,12 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
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.server.world.block.BlockLogic;
|
||||
|
||||
public class WorldLogic {
|
||||
|
||||
@ -17,9 +21,17 @@ public class WorldLogic {
|
||||
public WorldLogic(WorldData data) {
|
||||
this.data = data;
|
||||
|
||||
for (ChunkData chunkData : data.getChunks()) {
|
||||
chunks.put(chunkData, new ChunkLogic(this, chunkData));
|
||||
data.addListener(new WorldDataListener() {
|
||||
@Override
|
||||
public void onChunkLoaded(WorldData world, ChunkData chunk) {
|
||||
chunks.put(chunk, new ChunkLogic(WorldLogic.this, chunk));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeChunkUnloaded(WorldData world, ChunkData chunk) {
|
||||
chunks.remove(chunk);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public WorldData getData() {
|
||||
@ -34,6 +46,25 @@ public class WorldLogic {
|
||||
return chunks.get(getData().getChunk(pos));
|
||||
}
|
||||
|
||||
public ChunkLogic getChunkByBlock(Vec3i blockInWorld) {
|
||||
Vec3i chunkPos = Vectors.grab3i();
|
||||
Coordinates.convertInWorldToChunk(blockInWorld, chunkPos);
|
||||
ChunkLogic result = getChunk(chunkPos);
|
||||
Vectors.release(chunkPos);
|
||||
return result;
|
||||
}
|
||||
|
||||
public BlockLogic getBlock(Vec3i blockInWorld) {
|
||||
ChunkLogic chunk = getChunkByBlock(blockInWorld);
|
||||
if (chunk == null) return null;
|
||||
|
||||
Vec3i blockInChunk = Vectors.grab3i();
|
||||
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
|
||||
BlockLogic result = chunk.getBlock(blockInChunk);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Collection<ChunkLogic> getChunks() {
|
||||
return chunks.values();
|
||||
}
|
||||
|
Reference in New Issue
Block a user