Entities are now transferred from server to client
- Entities are no longer internally bound to chunks - Entity visibility for a player is now unique for each entity - Entities are send/revoked just like chunks by EntityManager - LocalPlayer no longer stores an entity; instead it stores entity ID and looks up the entity on demand
This commit is contained in:
parent
cde854346c
commit
b44540999b
@ -1,8 +1,11 @@
|
|||||||
package ru.windcorp.progressia.client;
|
package ru.windcorp.progressia.client;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.comms.DefaultClientCommsListener;
|
import ru.windcorp.progressia.client.comms.DefaultClientCommsListener;
|
||||||
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
|
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
|
||||||
import ru.windcorp.progressia.client.graphics.world.Camera;
|
import ru.windcorp.progressia.client.graphics.world.Camera;
|
||||||
|
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
||||||
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
|
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
|
||||||
import ru.windcorp.progressia.client.world.WorldRender;
|
import ru.windcorp.progressia.client.world.WorldRender;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
@ -11,7 +14,7 @@ import ru.windcorp.progressia.common.world.entity.EntityData;
|
|||||||
public class Client {
|
public class Client {
|
||||||
|
|
||||||
private final WorldRender world;
|
private final WorldRender world;
|
||||||
private LocalPlayer localPlayer;
|
private final LocalPlayer localPlayer = new LocalPlayer(this);
|
||||||
|
|
||||||
private final Camera camera = new Camera((float) Math.toRadians(70));
|
private final Camera camera = new Camera((float) Math.toRadians(70));
|
||||||
|
|
||||||
@ -32,8 +35,8 @@ public class Client {
|
|||||||
return localPlayer;
|
return localPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocalPlayer(EntityData localPlayer) {
|
public boolean isReady() {
|
||||||
this.localPlayer = new LocalPlayer(localPlayer);
|
return localPlayer.hasEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Camera getCamera() {
|
public Camera getCamera() {
|
||||||
@ -44,4 +47,17 @@ public class Client {
|
|||||||
return comms;
|
return comms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onLocalPlayerEntityChanged(EntityData entity, EntityData lastKnownEntity) {
|
||||||
|
LogManager.getLogger().info("LocalPlayer entity changed from {} to {}", lastKnownEntity, entity);
|
||||||
|
|
||||||
|
if (entity == null) {
|
||||||
|
getCamera().setAnchor(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCamera().setAnchor(new EntityAnchor(
|
||||||
|
getWorld().getEntityRenderable(entity)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,12 @@ package ru.windcorp.progressia.client.comms;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import ru.windcorp.jputil.chars.StringUtil;
|
|
||||||
import ru.windcorp.progressia.client.Client;
|
import ru.windcorp.progressia.client.Client;
|
||||||
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
|
||||||
import ru.windcorp.progressia.common.comms.CommsListener;
|
import ru.windcorp.progressia.common.comms.CommsListener;
|
||||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.common.world.PacketSetLocalPlayer;
|
import ru.windcorp.progressia.common.world.PacketSetLocalPlayer;
|
||||||
import ru.windcorp.progressia.common.world.PacketWorldChange;
|
import ru.windcorp.progressia.common.world.PacketWorldChange;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
|
||||||
|
|
||||||
// TODO refactor with no mercy
|
// TODO refactor with no mercy
|
||||||
public class DefaultClientCommsListener implements CommsListener {
|
public class DefaultClientCommsListener implements CommsListener {
|
||||||
@ -33,26 +30,12 @@ public class DefaultClientCommsListener implements CommsListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setLocalPlayer(PacketSetLocalPlayer packet) {
|
private void setLocalPlayer(PacketSetLocalPlayer packet) {
|
||||||
EntityData entity = getClient().getWorld().getData().getEntity(
|
getClient().getLocalPlayer().setEntityId(packet.getEntityId());
|
||||||
packet.getEntityId()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (entity == null) {
|
|
||||||
CrashReports.report(
|
|
||||||
null,
|
|
||||||
"Player entity with ID %s not found",
|
|
||||||
new String(StringUtil.toFullHex(packet.getEntityId()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getClient().setLocalPlayer(entity);
|
|
||||||
getClient().getCamera().setAnchor(new EntityAnchor(
|
|
||||||
getClient().getWorld().getEntityRenderable(entity)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIOError(IOException reason) {
|
public void onIOError(IOException reason) {
|
||||||
|
CrashReports.report(reason, "An IOException has occurred in communications");
|
||||||
// TODO implement
|
// TODO implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,9 @@ public class LayerWorld extends Layer {
|
|||||||
renderWorld();
|
renderWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client.getLocalPlayer() != null) {
|
client.getLocalPlayer().getEntity();
|
||||||
|
|
||||||
|
if (client.isReady()) {
|
||||||
client.getLocalPlayer().update(client.getWorld());
|
client.getLocalPlayer().update(client.getWorld());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,10 +141,9 @@ public class LayerWorld extends Layer {
|
|||||||
private static final Renderable SELECTION_BOX = tmp_createSelectionBox();
|
private static final Renderable SELECTION_BOX = tmp_createSelectionBox();
|
||||||
|
|
||||||
private void tmp_drawSelectionBox() {
|
private void tmp_drawSelectionBox() {
|
||||||
LocalPlayer player = client.getLocalPlayer();
|
if (!client.isReady()) return;
|
||||||
if (player == null) return;
|
|
||||||
|
|
||||||
Vec3i selection = player.getSelection().getBlock();
|
Vec3i selection = client.getLocalPlayer().getSelection().getBlock();
|
||||||
if (selection == null) return;
|
if (selection == null) return;
|
||||||
|
|
||||||
helper.pushTransform().translate(selection.x, selection.y, selection.z);
|
helper.pushTransform().translate(selection.x, selection.y, selection.z);
|
||||||
|
@ -1,16 +1,59 @@
|
|||||||
package ru.windcorp.progressia.client.graphics.world;
|
package ru.windcorp.progressia.client.graphics.world;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.Client;
|
||||||
import ru.windcorp.progressia.client.world.WorldRender;
|
import ru.windcorp.progressia.client.world.WorldRender;
|
||||||
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
|
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
|
||||||
import ru.windcorp.progressia.common.world.PlayerData;
|
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
public class LocalPlayer extends PlayerData {
|
public class LocalPlayer {
|
||||||
|
|
||||||
|
private final Client client;
|
||||||
|
|
||||||
|
private long entityId = EntityData.NULL_ENTITY_ID;
|
||||||
|
private EntityData lastKnownEntity = null;
|
||||||
|
|
||||||
private final Selection selection = new Selection();
|
private final Selection selection = new Selection();
|
||||||
|
|
||||||
public LocalPlayer(EntityData entity) {
|
public LocalPlayer(Client client) {
|
||||||
super(entity);
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getEntityId() {
|
||||||
|
return entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityId(long entityId) {
|
||||||
|
this.entityId = entityId;
|
||||||
|
|
||||||
|
this.lastKnownEntity = null;
|
||||||
|
getEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEntityId() {
|
||||||
|
return entityId != EntityData.NULL_ENTITY_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEntity() {
|
||||||
|
return getEntity() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityData getEntity() {
|
||||||
|
if (!hasEntityId()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityData entity = getClient().getWorld().getData().getEntity(getEntityId());
|
||||||
|
|
||||||
|
if (entity != lastKnownEntity) {
|
||||||
|
getClient().onLocalPlayerEntityChanged(entity, lastKnownEntity);
|
||||||
|
this.lastKnownEntity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Selection getSelection() {
|
public Selection getSelection() {
|
||||||
|
@ -116,6 +116,8 @@ public abstract class NPedModel extends EntityRenderable {
|
|||||||
this.body = body;
|
this.body = body;
|
||||||
this.head = head;
|
this.head = head;
|
||||||
this.scale = scale;
|
this.scale = scale;
|
||||||
|
|
||||||
|
evaluateAngles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,7 +23,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -32,7 +31,6 @@ import glm.vec._3.i.Vec3i;
|
|||||||
import ru.windcorp.progressia.common.util.VectorUtil;
|
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
|
||||||
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
||||||
@ -61,9 +59,6 @@ implements GenericChunk<
|
|||||||
BLOCK_FACE_COUNT
|
BLOCK_FACE_COUNT
|
||||||
];
|
];
|
||||||
|
|
||||||
private final List<EntityData> entities =
|
|
||||||
Collections.synchronizedList(new ArrayList<>());
|
|
||||||
|
|
||||||
private final Collection<ChunkDataListener> listeners =
|
private final Collection<ChunkDataListener> listeners =
|
||||||
Collections.synchronizedCollection(new ArrayList<>());
|
Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
@ -160,10 +155,6 @@ implements GenericChunk<
|
|||||||
face.getId();
|
face.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EntityData> getEntities() {
|
|
||||||
return entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkLocalCoordinates(Vec3i posInChunk) {
|
private static void checkLocalCoordinates(Vec3i posInChunk) {
|
||||||
if (!isInBounds(posInChunk)) {
|
if (!isInBounds(posInChunk)) {
|
||||||
throw new IllegalCoordinatesException(
|
throw new IllegalCoordinatesException(
|
||||||
@ -219,10 +210,6 @@ implements GenericChunk<
|
|||||||
forEachTileStack(stack -> stack.forEach(tileData -> action.accept(stack, tileData)));
|
forEachTileStack(stack -> stack.forEach(tileData -> action.accept(stack, tileData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forEachEntity(Consumer<EntityData> action) {
|
|
||||||
getEntities().forEach(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldData getWorld() {
|
public WorldData getWorld() {
|
||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,12 @@ package ru.windcorp.progressia.common.world;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import gnu.trove.impl.sync.TSynchronizedLongObjectMap;
|
import gnu.trove.TCollections;
|
||||||
import gnu.trove.map.TLongObjectMap;
|
import gnu.trove.map.TLongObjectMap;
|
||||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
import gnu.trove.map.hash.TLongObjectHashMap;
|
||||||
|
import gnu.trove.set.TLongSet;
|
||||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
@ -46,14 +47,14 @@ implements GenericWorld<
|
|||||||
> {
|
> {
|
||||||
|
|
||||||
private final ChunkMap<ChunkData> chunksByPos = new LongBasedChunkMap<>(
|
private final ChunkMap<ChunkData> chunksByPos = new LongBasedChunkMap<>(
|
||||||
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this)
|
TCollections.synchronizedMap(new TLongObjectHashMap<>())
|
||||||
);
|
);
|
||||||
|
|
||||||
private final Collection<ChunkData> chunks =
|
private final Collection<ChunkData> chunks =
|
||||||
Collections.unmodifiableCollection(chunksByPos.values());
|
Collections.unmodifiableCollection(chunksByPos.values());
|
||||||
|
|
||||||
private final TLongObjectMap<EntityData> entitiesById =
|
private final TLongObjectMap<EntityData> entitiesById =
|
||||||
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this);
|
TCollections.synchronizedMap(new TLongObjectHashMap<>());
|
||||||
|
|
||||||
private final Collection<EntityData> entities =
|
private final Collection<EntityData> entities =
|
||||||
Collections.unmodifiableCollection(entitiesById.valueCollection());
|
Collections.unmodifiableCollection(entitiesById.valueCollection());
|
||||||
@ -86,6 +87,10 @@ implements GenericWorld<
|
|||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TLongSet getLoadedEntities() {
|
||||||
|
return entitiesById.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
public void tmp_generate() {
|
public void tmp_generate() {
|
||||||
final int size = 1;
|
final int size = 1;
|
||||||
Vec3i cursor = new Vec3i(0, 0, 0);
|
Vec3i cursor = new Vec3i(0, 0, 0);
|
||||||
@ -118,10 +123,6 @@ implements GenericWorld<
|
|||||||
|
|
||||||
chunksByPos.put(chunk, chunk);
|
chunksByPos.put(chunk, chunk);
|
||||||
|
|
||||||
chunk.forEachEntity(entity ->
|
|
||||||
entitiesById.put(entity.getEntityId(), entity)
|
|
||||||
);
|
|
||||||
|
|
||||||
chunk.onLoaded();
|
chunk.onLoaded();
|
||||||
getListeners().forEach(l -> l.onChunkLoaded(this, chunk));
|
getListeners().forEach(l -> l.onChunkLoaded(this, chunk));
|
||||||
}
|
}
|
||||||
@ -130,10 +131,6 @@ implements GenericWorld<
|
|||||||
getListeners().forEach(l -> l.beforeChunkUnloaded(this, chunk));
|
getListeners().forEach(l -> l.beforeChunkUnloaded(this, chunk));
|
||||||
chunk.beforeUnloaded();
|
chunk.beforeUnloaded();
|
||||||
|
|
||||||
chunk.forEachEntity(entity ->
|
|
||||||
entitiesById.remove(entity.getEntityId())
|
|
||||||
);
|
|
||||||
|
|
||||||
chunksByPos.remove(chunk);
|
chunksByPos.remove(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,6 +150,45 @@ implements GenericWorld<
|
|||||||
return entitiesById.get(entityId);
|
return entitiesById.get(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addEntity(EntityData entity) {
|
||||||
|
Objects.requireNonNull(entity, "entity");
|
||||||
|
|
||||||
|
EntityData previous = entitiesById.putIfAbsent(entity.getEntityId(), entity);
|
||||||
|
|
||||||
|
if (previous != null) {
|
||||||
|
String message = "Cannot add entity " + entity + ": ";
|
||||||
|
|
||||||
|
if (previous == entity) {
|
||||||
|
message += "already present";
|
||||||
|
} else {
|
||||||
|
message += "entity with the same EntityID already present (" + previous + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
getListeners().forEach(l -> l.onEntityAdded(this, entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEntity(long entityId) {
|
||||||
|
synchronized (entitiesById) {
|
||||||
|
EntityData entity = entitiesById.get(entityId);
|
||||||
|
|
||||||
|
if (entity == null) {
|
||||||
|
throw new IllegalArgumentException("Entity with EntityID " + EntityData.formatEntityId(entityId) + " not present");
|
||||||
|
} else {
|
||||||
|
removeEntity(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEntity(EntityData entity) {
|
||||||
|
Objects.requireNonNull(entity, "entity");
|
||||||
|
|
||||||
|
getListeners().forEach(l -> l.beforeEntityRemoved(this, entity));
|
||||||
|
entitiesById.remove(entity.getEntityId());
|
||||||
|
}
|
||||||
|
|
||||||
public float getTime() {
|
public float getTime() {
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package ru.windcorp.progressia.common.world;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
|
||||||
public interface WorldDataListener {
|
public interface WorldDataListener {
|
||||||
|
|
||||||
@ -31,4 +32,18 @@ public interface WorldDataListener {
|
|||||||
*/
|
*/
|
||||||
default void beforeChunkUnloaded(WorldData world, ChunkData chunk) {}
|
default void beforeChunkUnloaded(WorldData world, ChunkData chunk) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked whenever an {@link EntityData} has been added.
|
||||||
|
* @param world the world instance
|
||||||
|
* @param entity the entity that has been added
|
||||||
|
*/
|
||||||
|
default void onEntityAdded(WorldData world, EntityData entity) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked whenever an {@link EntityData} is about to be removed.
|
||||||
|
* @param world the world instance
|
||||||
|
* @param entity the entity that is going to be removed
|
||||||
|
*/
|
||||||
|
default void beforeEntityRemoved(WorldData world, EntityData entity) {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package ru.windcorp.progressia.common.world.entity;
|
|||||||
|
|
||||||
import glm.vec._2.Vec2;
|
import glm.vec._2.Vec2;
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.jputil.chars.StringUtil;
|
||||||
import ru.windcorp.progressia.common.collision.Collideable;
|
import ru.windcorp.progressia.common.collision.Collideable;
|
||||||
import ru.windcorp.progressia.common.collision.CollisionModel;
|
import ru.windcorp.progressia.common.collision.CollisionModel;
|
||||||
import ru.windcorp.progressia.common.state.StatefulObject;
|
import ru.windcorp.progressia.common.state.StatefulObject;
|
||||||
@ -14,6 +15,12 @@ public class EntityData extends StatefulObject implements Collideable, GenericEn
|
|||||||
|
|
||||||
private final Vec2 direction = new Vec2();
|
private final Vec2 direction = new Vec2();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique {@code long} value guaranteed to never be assigned to an entity as its entity ID.
|
||||||
|
* This can safely be used as a placeholder or a sentinel value.
|
||||||
|
*/
|
||||||
|
public static final long NULL_ENTITY_ID = 0x0000_0000_0000_0000;
|
||||||
|
|
||||||
private long entityId;
|
private long entityId;
|
||||||
|
|
||||||
private CollisionModel collisionModel = null;
|
private CollisionModel collisionModel = null;
|
||||||
@ -69,6 +76,9 @@ public class EntityData extends StatefulObject implements Collideable, GenericEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setEntityId(long entityId) {
|
public void setEntityId(long entityId) {
|
||||||
|
if (entityId == NULL_ENTITY_ID) {
|
||||||
|
throw new IllegalArgumentException("Attempted to set entity ID to NULL_ENTITY_ID (" + entityId + ")");
|
||||||
|
}
|
||||||
this.entityId = entityId;
|
this.entityId = entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,4 +138,17 @@ public class EntityData extends StatefulObject implements Collideable, GenericEn
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringBuilder(super.toString())
|
||||||
|
.append(" (EntityID ")
|
||||||
|
.append(StringUtil.toFullHex(getEntityId()))
|
||||||
|
.append(")")
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatEntityId(long entityId) {
|
||||||
|
return new String(StringUtil.toFullHex(entityId));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package ru.windcorp.progressia.common.world.entity;
|
||||||
|
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.world.PacketWorldChange;
|
||||||
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
|
|
||||||
|
public class PacketRevokeEntity extends PacketWorldChange {
|
||||||
|
|
||||||
|
private long entityId;
|
||||||
|
|
||||||
|
public PacketRevokeEntity() {
|
||||||
|
this("Core:RevokeEntity");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PacketRevokeEntity(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(long entityId) {
|
||||||
|
this.entityId = entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(DataInput input) throws IOException {
|
||||||
|
this.entityId = input.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(DataOutput output) throws IOException {
|
||||||
|
output.writeLong(this.entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(WorldData world) {
|
||||||
|
world.removeEntity(this.entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
package ru.windcorp.progressia.common.world.generic;
|
package ru.windcorp.progressia.common.world.generic;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
import gnu.trove.impl.sync.TSynchronizedLongSet;
|
import gnu.trove.impl.sync.TSynchronizedLongSet;
|
||||||
import gnu.trove.set.hash.TLongHashSet;
|
import gnu.trove.set.hash.TLongHashSet;
|
||||||
|
|
||||||
@ -17,6 +21,78 @@ public class ChunkSets {
|
|||||||
return new LongBasedChunkSet(new TSynchronizedLongSet(new TLongHashSet()));
|
return new LongBasedChunkSet(new TSynchronizedLongSet(new TLongHashSet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static ChunkSet EMPTY_SET = new ChunkSet() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Vec3i> iterator() {
|
||||||
|
return new Iterator<Vec3i>() {
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Vec3i next() {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Vec3i pos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(Vec3i pos) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Vec3i pos) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(ChunkSet other) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAny(ChunkSet other) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAll(ChunkSet other) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll(ChunkSet other) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void retainAll(ChunkSet other) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
public static ChunkSet empty() {
|
||||||
|
return EMPTY_SET;
|
||||||
|
}
|
||||||
|
|
||||||
private ChunkSets() {}
|
private ChunkSets() {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,11 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.PacketRevokeChunk;
|
||||||
|
import ru.windcorp.progressia.common.world.PacketSendChunk;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
import ru.windcorp.progressia.common.world.generic.ChunkSet;
|
import ru.windcorp.progressia.common.world.generic.ChunkSet;
|
||||||
import ru.windcorp.progressia.common.world.generic.ChunkSets;
|
import ru.windcorp.progressia.common.world.generic.ChunkSets;
|
||||||
import ru.windcorp.progressia.test.TestChunkSender;
|
|
||||||
import ru.windcorp.progressia.test.TestContent;
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
|
||||||
public class ChunkManager {
|
public class ChunkManager {
|
||||||
@ -148,14 +149,29 @@ public class ChunkManager {
|
|||||||
|
|
||||||
public void sendChunk(Player player, Vec3i chunkPos) {
|
public void sendChunk(Player player, Vec3i chunkPos) {
|
||||||
LogManager.getLogger().info("Sending {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z);
|
LogManager.getLogger().info("Sending {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z);
|
||||||
TestChunkSender.sendChunk(server, player.getClient(), chunkPos);
|
|
||||||
|
ChunkData chunk = server.getWorld().getData().getChunk(chunkPos);
|
||||||
|
|
||||||
|
if (chunk == null) {
|
||||||
|
throw new IllegalStateException(String.format(
|
||||||
|
"Chunk (%d; %d; %d) is not loaded, cannot send",
|
||||||
|
chunkPos.x, chunkPos.y, chunkPos.z
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketSendChunk packet = new PacketSendChunk();
|
||||||
|
packet.set(chunk);
|
||||||
|
player.getClient().sendPacket(packet);
|
||||||
|
|
||||||
getVision(player, true).visible.add(chunkPos);
|
getVision(player, true).visible.add(chunkPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void revokeChunk(Player player, Vec3i chunkPos) {
|
public void revokeChunk(Player player, Vec3i chunkPos) {
|
||||||
LogManager.getLogger().info("Revoking {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z);
|
LogManager.getLogger().info("Revoking {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z);
|
||||||
TestChunkSender.revokeChunk(player.getClient(), chunkPos);
|
|
||||||
|
PacketRevokeChunk packet = new PacketRevokeChunk();
|
||||||
|
packet.set(chunkPos);
|
||||||
|
player.getClient().sendPacket(packet);
|
||||||
|
|
||||||
PlayerVision vision = getVision(player, false);
|
PlayerVision vision = getVision(player, false);
|
||||||
if (vision != null) {
|
if (vision != null) {
|
||||||
@ -173,6 +189,16 @@ public class ChunkManager {
|
|||||||
return vision.isChunkVisible(chunkPos);
|
return vision.isChunkVisible(chunkPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChunkSet getVisibleChunks(Player player) {
|
||||||
|
PlayerVision vision = getVision(player, false);
|
||||||
|
|
||||||
|
if (vision == null) {
|
||||||
|
return ChunkSets.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return vision.visible;
|
||||||
|
}
|
||||||
|
|
||||||
public Server getServer() {
|
public Server getServer() {
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
183
src/main/java/ru/windcorp/progressia/server/EntityManager.java
Normal file
183
src/main/java/ru/windcorp/progressia/server/EntityManager.java
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
package ru.windcorp.progressia.server;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import gnu.trove.TCollections;
|
||||||
|
import gnu.trove.iterator.TLongIterator;
|
||||||
|
import gnu.trove.set.TLongSet;
|
||||||
|
import gnu.trove.set.hash.TLongHashSet;
|
||||||
|
import ru.windcorp.jputil.chars.StringUtil;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.PacketRevokeEntity;
|
||||||
|
import ru.windcorp.progressia.common.world.generic.ChunkSet;
|
||||||
|
|
||||||
|
public class EntityManager {
|
||||||
|
|
||||||
|
private class PlayerVision {
|
||||||
|
|
||||||
|
private final TLongSet visible = TCollections.synchronizedSet(new TLongHashSet());
|
||||||
|
private final TLongSet requested = new TLongHashSet();
|
||||||
|
private final TLongSet toSend = new TLongHashSet();
|
||||||
|
private final TLongSet toRevoke = new TLongHashSet();
|
||||||
|
|
||||||
|
public boolean isEntityVisible(long entityId) {
|
||||||
|
return visible.contains(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void gatherRequests(Player player) {
|
||||||
|
requested.clear();
|
||||||
|
|
||||||
|
ChunkSet visibleChunks = player.getClient().getVisibleChunks();
|
||||||
|
Vec3i v = Vectors.grab3i();
|
||||||
|
|
||||||
|
getServer().getWorld().forEachEntity(entity -> {
|
||||||
|
if (visibleChunks.contains(entity.getChunkCoords(v))) {
|
||||||
|
requested.add(entity.getEntityId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Vectors.release(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateQueues(Player player) {
|
||||||
|
toSend.clear();
|
||||||
|
toSend.addAll(requested);
|
||||||
|
toSend.removeAll(visible);
|
||||||
|
toSend.retainAll(loaded);
|
||||||
|
|
||||||
|
toRevoke.clear();
|
||||||
|
|
||||||
|
for (TLongIterator it = visible.iterator(); it.hasNext();) {
|
||||||
|
long entityId = it.next();
|
||||||
|
if (!loaded.contains(entityId) || !requested.contains(entityId)) {
|
||||||
|
toRevoke.add(entityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processQueues(Player player) {
|
||||||
|
toRevoke.forEach(entityId -> {
|
||||||
|
revokeEntity(player, entityId);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
toRevoke.clear();
|
||||||
|
|
||||||
|
toSend.forEach(entityId -> {
|
||||||
|
sendEntity(player, entityId);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
toSend.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Server server;
|
||||||
|
|
||||||
|
private final TLongSet loaded;
|
||||||
|
|
||||||
|
// TODO replace with a normal Map managed by some sort of PlayerListener, weak maps are weak
|
||||||
|
private final Map<Player, PlayerVision> visions = Collections.synchronizedMap(new WeakHashMap<>());
|
||||||
|
|
||||||
|
public EntityManager(Server server) {
|
||||||
|
this.server = server;
|
||||||
|
this.loaded = server.getWorld().getData().getLoadedEntities();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
synchronized (getServer().getWorld().getData()) {
|
||||||
|
synchronized (visions) {
|
||||||
|
gatherRequests();
|
||||||
|
updateQueues();
|
||||||
|
processQueues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gatherRequests() {
|
||||||
|
server.getPlayerManager().getPlayers().forEach(p -> {
|
||||||
|
PlayerVision vision = getVision(p, true);
|
||||||
|
vision.gatherRequests(p);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateQueues() {
|
||||||
|
visions.forEach((p, v) -> {
|
||||||
|
v.updateQueues(p);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processQueues() {
|
||||||
|
visions.forEach((p, v) -> {
|
||||||
|
v.processQueues(p);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerVision getVision(Player player, boolean createIfMissing) {
|
||||||
|
return createIfMissing ? visions.computeIfAbsent(player, k -> new PlayerVision()) : visions.get(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendEntity(Player player, long entityId) {
|
||||||
|
|
||||||
|
EntityData entity = server.getWorld().getData().getEntity(entityId);
|
||||||
|
|
||||||
|
if (entity == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Entity with entity ID " + new String(StringUtil.toFullHex(entityId)) + " is not loaded, cannot send"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager.getLogger().info("Sending {}", entity);
|
||||||
|
|
||||||
|
PacketSendEntity packet = new PacketSendEntity();
|
||||||
|
packet.set(entity);
|
||||||
|
player.getClient().sendPacket(packet);
|
||||||
|
|
||||||
|
getVision(player, true).visible.add(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void revokeEntity(Player player, long entityId) {
|
||||||
|
LogManager.getLogger().info("Revoking {}", new String(StringUtil.toFullHex(entityId)));
|
||||||
|
|
||||||
|
PacketRevokeEntity packet = new PacketRevokeEntity();
|
||||||
|
packet.set(entityId);
|
||||||
|
player.getClient().sendPacket(packet);
|
||||||
|
|
||||||
|
PlayerVision vision = getVision(player, false);
|
||||||
|
if (vision != null) {
|
||||||
|
vision.visible.remove(entityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEntityVisible(long entityId, Player player) {
|
||||||
|
PlayerVision vision = getVision(player, false);
|
||||||
|
|
||||||
|
if (vision == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vision.isEntityVisible(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final TLongSet EMPTY_LONG_SET = TCollections.unmodifiableSet(new TLongHashSet());
|
||||||
|
|
||||||
|
public TLongSet getVisibleEntities(Player player) {
|
||||||
|
PlayerVision vision = getVision(player, false);
|
||||||
|
|
||||||
|
if (vision == null) {
|
||||||
|
return EMPTY_LONG_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vision.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Server getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package ru.windcorp.progressia.server;
|
||||||
|
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.state.IOContext;
|
||||||
|
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.PacketWorldChange;
|
||||||
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityDataRegistry;
|
||||||
|
|
||||||
|
public class PacketSendEntity extends PacketWorldChange {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private long entityId;
|
||||||
|
private final DataBuffer buffer = new DataBuffer();
|
||||||
|
|
||||||
|
public PacketSendEntity() {
|
||||||
|
this("Core:SendEntity");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PacketSendEntity(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(EntityData entity) {
|
||||||
|
this.id = entity.getId();
|
||||||
|
this.entityId = entity.getEntityId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
entity.write(this.buffer.getWriter(), IOContext.COMMS);
|
||||||
|
} catch (IOException e) {
|
||||||
|
CrashReports.report(e, "Could not write an entity into an internal buffer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(DataInput input) throws IOException, DecodingException {
|
||||||
|
this.id = input.readUTF();
|
||||||
|
this.entityId = input.readLong();
|
||||||
|
this.buffer.fill(input, input.readInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(DataOutput output) throws IOException {
|
||||||
|
output.writeUTF(this.id);
|
||||||
|
output.writeLong(this.entityId);
|
||||||
|
output.writeInt(this.buffer.getSize());
|
||||||
|
this.buffer.flush(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(WorldData world) {
|
||||||
|
EntityData entity = EntityDataRegistry.getInstance().create(this.id);
|
||||||
|
|
||||||
|
entity.setEntityId(this.entityId);
|
||||||
|
try {
|
||||||
|
entity.read(this.buffer.getReader(), IOContext.COMMS);
|
||||||
|
} catch (IOException e) {
|
||||||
|
CrashReports.report(e, "Could not read an entity from an internal buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
world.addEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -55,4 +55,8 @@ public class Player extends PlayerData implements ChunkLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return getClient().getLogin();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import glm.vec._2.Vec2;
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityDataRegistry;
|
||||||
import ru.windcorp.progressia.test.TestContent;
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
|
||||||
public class PlayerManager {
|
public class PlayerManager {
|
||||||
@ -38,13 +41,28 @@ public class PlayerManager {
|
|||||||
getServer().getChunkManager().loadChunk(chunkPos);
|
getServer().getChunkManager().loadChunk(chunkPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
return getServer().getWorld().getData().getEntity(TestContent.PLAYER_ENTITY_ID);
|
EntityData entity = spawnPlayerEntity(login);
|
||||||
|
return entity;
|
||||||
} else {
|
} else {
|
||||||
CrashReports.report(null, "Unknown login %s, javahorse stupid", login);
|
CrashReports.report(null, "Unknown login %s, javahorse stupid", login);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EntityData spawnPlayerEntity(String login) {
|
||||||
|
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
||||||
|
|
||||||
|
player.setEntityId(TestContent.PLAYER_ENTITY_ID);
|
||||||
|
player.setPosition(new Vec3(8, 8, 8));
|
||||||
|
player.setDirection(new Vec2(
|
||||||
|
Math.toRadians(40), Math.toRadians(10)
|
||||||
|
));
|
||||||
|
|
||||||
|
getServer().getWorld().getData().addEntity(player);
|
||||||
|
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
public Server getServer() {
|
public Server getServer() {
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ public class Server {
|
|||||||
private final ClientManager clientManager;
|
private final ClientManager clientManager;
|
||||||
private final PlayerManager playerManager;
|
private final PlayerManager playerManager;
|
||||||
private final ChunkManager chunkManager;
|
private final ChunkManager chunkManager;
|
||||||
|
private final EntityManager entityManager;
|
||||||
|
|
||||||
private final TaskQueue taskQueue = new TaskQueue(this::isServerThread);
|
private final TaskQueue taskQueue = new TaskQueue(this::isServerThread);
|
||||||
|
|
||||||
@ -44,9 +45,11 @@ public class Server {
|
|||||||
this.clientManager = new ClientManager(this);
|
this.clientManager = new ClientManager(this);
|
||||||
this.playerManager = new PlayerManager(this);
|
this.playerManager = new PlayerManager(this);
|
||||||
this.chunkManager = new ChunkManager(this);
|
this.chunkManager = new ChunkManager(this);
|
||||||
|
this.entityManager = new EntityManager(this);
|
||||||
|
|
||||||
schedule(this::scheduleChunkTicks);
|
schedule(this::scheduleWorldTicks);
|
||||||
schedule(chunkManager::tick);
|
schedule(chunkManager::tick);
|
||||||
|
schedule(entityManager::tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,8 +200,9 @@ public class Server {
|
|||||||
serverThread.stop();
|
serverThread.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleChunkTicks(Server server) {
|
private void scheduleWorldTicks(Server server) {
|
||||||
server.getWorld().getChunks().forEach(chunk -> requestEvaluation(chunk.getTickTask()));
|
server.getWorld().getChunks().forEach(chunk -> requestEvaluation(chunk.getTickTask()));
|
||||||
|
requestEvaluation(server.getWorld().getTickEntitiesTask());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,7 @@ public class ServerState {
|
|||||||
|
|
||||||
public static void startServer() {
|
public static void startServer() {
|
||||||
Server server = new Server(new WorldData());
|
Server server = new Server(new WorldData());
|
||||||
server.getWorld().getData().tmp_generate();
|
// server.getWorld().getData().tmp_generate();
|
||||||
setInstance(server);
|
setInstance(server);
|
||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ 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;
|
||||||
@ -41,10 +43,10 @@ public class ClientManager {
|
|||||||
|
|
||||||
if (client instanceof ClientChat) {
|
if (client instanceof ClientChat) {
|
||||||
addClientChat((ClientChat) client);
|
addClientChat((ClientChat) client);
|
||||||
}
|
|
||||||
|
|
||||||
if (client instanceof ClientPlayer) {
|
if (client instanceof ClientPlayer) {
|
||||||
addClientPlayer((ClientPlayer) client);
|
addClientPlayer((ClientPlayer) client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.addListener(new DefaultServerCommsListener(this, client));
|
client.addListener(new DefaultServerCommsListener(this, client));
|
||||||
@ -58,18 +60,12 @@ public class ClientManager {
|
|||||||
private void addClientPlayer(ClientPlayer client) {
|
private void addClientPlayer(ClientPlayer client) {
|
||||||
String login = client.getLogin();
|
String login = client.getLogin();
|
||||||
|
|
||||||
EntityData entity;
|
EntityData entity = getServer().getPlayerManager().conjurePlayerEntity(login);
|
||||||
synchronized (getServer().getWorld().getData()) {
|
Player player = new Player(entity, getServer(), client);
|
||||||
entity = getServer().getPlayerManager().conjurePlayerEntity(login);
|
getServer().getPlayerManager().getPlayers().add(player);
|
||||||
|
|
||||||
Player player = new Player(entity, getServer(), client);
|
|
||||||
|
|
||||||
getServer().getPlayerManager().getPlayers().add(player);
|
|
||||||
|
|
||||||
getServer().getChunkManager().sendChunk(player, entity.getChunkCoords(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package ru.windcorp.progressia.server.comms;
|
package ru.windcorp.progressia.server.comms;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.world.generic.ChunkSet;
|
||||||
|
import ru.windcorp.progressia.common.world.generic.ChunkSets;
|
||||||
import ru.windcorp.progressia.server.Player;
|
import ru.windcorp.progressia.server.Player;
|
||||||
|
|
||||||
public abstract class ClientPlayer extends ClientChat {
|
public abstract class ClientPlayer extends ClientChat {
|
||||||
@ -26,6 +28,11 @@ public abstract class ClientPlayer extends ClientChat {
|
|||||||
return player.getServer().getChunkManager().isChunkVisible(chunkPos, player);
|
return player.getServer().getChunkManager().isChunkVisible(chunkPos, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChunkSet getVisibleChunks() {
|
||||||
|
if (player == null) return ChunkSets.empty();
|
||||||
|
return player.getServer().getChunkManager().getVisibleChunks(player);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isChunkVisible(long entityId) {
|
public boolean isChunkVisible(long entityId) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -11,15 +11,12 @@ import glm.vec._3.i.Vec3i;
|
|||||||
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.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
|
||||||
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileReference;
|
import ru.windcorp.progressia.common.world.tile.TileReference;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockLogicRegistry;
|
import ru.windcorp.progressia.server.world.block.BlockLogicRegistry;
|
||||||
import ru.windcorp.progressia.server.world.block.TickableBlock;
|
import ru.windcorp.progressia.server.world.block.TickableBlock;
|
||||||
import ru.windcorp.progressia.server.world.entity.EntityLogic;
|
|
||||||
import ru.windcorp.progressia.server.world.entity.EntityLogicRegistry;
|
|
||||||
import ru.windcorp.progressia.server.world.tasks.TickChunk;
|
import ru.windcorp.progressia.server.world.tasks.TickChunk;
|
||||||
import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
|
import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
|
||||||
import ru.windcorp.progressia.server.world.tile.TickableTile;
|
import ru.windcorp.progressia.server.world.tile.TickableTile;
|
||||||
@ -112,15 +109,6 @@ public class ChunkLogic implements GenericChunk<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forEachEntity(BiConsumer<EntityLogic, EntityData> action) {
|
|
||||||
getData().forEachEntity(data -> {
|
|
||||||
action.accept(
|
|
||||||
EntityLogicRegistry.getInstance().get(data.getId()),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public TickChunk getTickTask() {
|
public TickChunk getTickTask() {
|
||||||
return tickTask;
|
return tickTask;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import ru.windcorp.progressia.common.world.entity.EntityData;
|
|||||||
import ru.windcorp.progressia.common.world.generic.GenericWorld;
|
import ru.windcorp.progressia.common.world.generic.GenericWorld;
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.tasks.TickEntitiesTask;
|
||||||
|
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||||
import ru.windcorp.progressia.server.world.tile.TileLogic;
|
import ru.windcorp.progressia.server.world.tile.TileLogic;
|
||||||
import ru.windcorp.progressia.server.world.tile.TileLogicStack;
|
import ru.windcorp.progressia.server.world.tile.TileLogicStack;
|
||||||
|
|
||||||
@ -30,6 +32,8 @@ implements GenericWorld<
|
|||||||
|
|
||||||
private final Map<ChunkData, ChunkLogic> chunks = new HashMap<>();
|
private final Map<ChunkData, ChunkLogic> chunks = new HashMap<>();
|
||||||
|
|
||||||
|
private final Evaluation tickEntitiesTask = new TickEntitiesTask();
|
||||||
|
|
||||||
public WorldLogic(WorldData data, Server server) {
|
public WorldLogic(WorldData data, Server server) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
@ -64,6 +68,10 @@ implements GenericWorld<
|
|||||||
return getData().getEntities();
|
return getData().getEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Evaluation getTickEntitiesTask() {
|
||||||
|
return tickEntitiesTask;
|
||||||
|
}
|
||||||
|
|
||||||
public Server getServer() {
|
public Server getServer() {
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import ru.windcorp.progressia.common.world.block.BlockFace;
|
|||||||
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.world.ChunkLogic;
|
import ru.windcorp.progressia.server.world.ChunkLogic;
|
||||||
import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
|
|
||||||
import ru.windcorp.progressia.server.world.TickContextMutable;
|
import ru.windcorp.progressia.server.world.TickContextMutable;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
import ru.windcorp.progressia.server.world.block.TickableBlock;
|
import ru.windcorp.progressia.server.world.block.TickableBlock;
|
||||||
@ -55,7 +54,6 @@ public class TickChunk extends Evaluation {
|
|||||||
public void evaluate(Server server) {
|
public void evaluate(Server server) {
|
||||||
tickRegulars(server);
|
tickRegulars(server);
|
||||||
tickRandom(server);
|
tickRandom(server);
|
||||||
tickEntities(server);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tickRegulars(Server server) {
|
private void tickRegulars(Server server) {
|
||||||
@ -165,12 +163,6 @@ public class TickChunk extends Evaluation {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tickEntities(Server server) {
|
|
||||||
chunk.getData().forEachEntity(entity -> {
|
|
||||||
TickAndUpdateUtil.tickEntity(entity, server);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getRelevantChunk(Vec3i output) {
|
public void getRelevantChunk(Vec3i output) {
|
||||||
Vec3i p = chunk.getData().getPosition();
|
Vec3i p = chunk.getData().getPosition();
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
|
||||||
|
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||||
|
|
||||||
|
public class TickEntitiesTask extends Evaluation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluate(Server server) {
|
||||||
|
server.getWorld().forEachEntity(entity -> {
|
||||||
|
TickAndUpdateUtil.tickEntity(entity, server);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getRelevantChunk(Vec3i output) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isThreadSensitive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
package ru.windcorp.progressia.test;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
|
||||||
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.PacketRevokeChunk;
|
|
||||||
import ru.windcorp.progressia.common.world.PacketSendChunk;
|
|
||||||
import ru.windcorp.progressia.server.Server;
|
|
||||||
import ru.windcorp.progressia.server.comms.ClientPlayer;
|
|
||||||
|
|
||||||
public class TestChunkSender {
|
|
||||||
|
|
||||||
public static void sendChunk(Server server, ClientPlayer receiver, Vec3i chunkPos) {
|
|
||||||
ChunkData chunk = server.getWorld().getData().getChunk(chunkPos);
|
|
||||||
|
|
||||||
if (chunk == null) {
|
|
||||||
throw new IllegalStateException(String.format(
|
|
||||||
"Chunk (%d; %d; %d) is not loaded, cannot send",
|
|
||||||
chunkPos.x, chunkPos.y, chunkPos.z
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
PacketSendChunk packet = new PacketSendChunk();
|
|
||||||
packet.getPosition().set(chunkPos.x, chunkPos.y, chunkPos.z);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ChunkIO.save(chunk, packet.getData().getOutputStream());
|
|
||||||
} catch (IOException e) {
|
|
||||||
CrashReports.report(e, "TestChunkSender fjcked up. javahorse stupid");
|
|
||||||
}
|
|
||||||
|
|
||||||
receiver.sendPacket(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void revokeChunk(ClientPlayer receiver, Vec3i chunkPos) {
|
|
||||||
PacketRevokeChunk packet = new PacketRevokeChunk();
|
|
||||||
packet.set(chunkPos);
|
|
||||||
receiver.sendPacket(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -7,16 +7,12 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import glm.Glm;
|
|
||||||
import glm.vec._2.Vec2;
|
|
||||||
import glm.vec._3.Vec3;
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.ClientState;
|
import ru.windcorp.progressia.client.ClientState;
|
||||||
import ru.windcorp.progressia.client.audio.SoundEffect;
|
import ru.windcorp.progressia.client.audio.SoundEffect;
|
||||||
import ru.windcorp.progressia.client.comms.controls.*;
|
import ru.windcorp.progressia.client.comms.controls.*;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||||
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
|
|
||||||
import ru.windcorp.progressia.client.graphics.world.Selection;
|
import ru.windcorp.progressia.client.graphics.world.Selection;
|
||||||
import ru.windcorp.progressia.client.world.block.*;
|
import ru.windcorp.progressia.client.world.block.*;
|
||||||
import ru.windcorp.progressia.client.world.entity.*;
|
import ru.windcorp.progressia.client.world.entity.*;
|
||||||
@ -26,7 +22,6 @@ import ru.windcorp.progressia.common.collision.CollisionModel;
|
|||||||
import ru.windcorp.progressia.common.comms.controls.*;
|
import ru.windcorp.progressia.common.comms.controls.*;
|
||||||
import ru.windcorp.progressia.common.io.ChunkIO;
|
import ru.windcorp.progressia.common.io.ChunkIO;
|
||||||
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
|
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
|
||||||
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.block.*;
|
import ru.windcorp.progressia.common.world.block.*;
|
||||||
@ -214,12 +209,9 @@ public class TestContent {
|
|||||||
|
|
||||||
private static Selection getSelection() {
|
private static Selection getSelection() {
|
||||||
ru.windcorp.progressia.client.Client client = ClientState.getInstance();
|
ru.windcorp.progressia.client.Client client = ClientState.getInstance();
|
||||||
if (client == null) return null;
|
if (client == null || !client.isReady()) return null;
|
||||||
|
|
||||||
LocalPlayer player = client.getLocalPlayer();
|
return client.getLocalPlayer().getSelection();
|
||||||
if (player == null) return null;
|
|
||||||
|
|
||||||
return player.getSelection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void onBlockBreakTrigger(ControlData control) {
|
private static void onBlockBreakTrigger(ControlData control) {
|
||||||
@ -336,21 +328,6 @@ public class TestContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Glm.equals(chunk.getPosition(), Vectors.ZERO_3i)) {
|
|
||||||
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
|
|
||||||
player.setEntityId(PLAYER_ENTITY_ID);
|
|
||||||
player.setPosition(new Vec3(8, 8, 8));
|
|
||||||
player.setDirection(new Vec2(
|
|
||||||
(float) Math.toRadians(40), (float) Math.toRadians(45)
|
|
||||||
));
|
|
||||||
chunk.getEntities().add(player);
|
|
||||||
|
|
||||||
// EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie");
|
|
||||||
// statie.setEntityId(STATIE_ENTITY_ID);
|
|
||||||
// statie.setPosition(new Vec3(0, 15, 16));
|
|
||||||
// chunk.getEntities().add(statie);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerMisc() {
|
private static void registerMisc() {
|
||||||
|
@ -61,7 +61,7 @@ public class TestPlayerControls {
|
|||||||
private Runnable updateCallback = null;
|
private Runnable updateCallback = null;
|
||||||
|
|
||||||
public void applyPlayerControls() {
|
public void applyPlayerControls() {
|
||||||
if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) {
|
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ public class TestPlayerControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void handleInput(Input input) {
|
public void handleInput(Input input) {
|
||||||
if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) {
|
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user