Merge remote-tracking branch 'github/master' into audio
This commit is contained in:
commit
3057910269
@ -27,6 +27,7 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||
import ru.windcorp.progressia.server.ServerState;
|
||||
import ru.windcorp.progressia.test.TestContent;
|
||||
|
||||
public class ClientProxy implements Proxy {
|
||||
|
||||
|
@ -1,9 +1,6 @@
|
||||
package ru.windcorp.progressia.client.comms;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import ru.windcorp.progressia.client.Client;
|
||||
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
|
||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
||||
@ -11,9 +8,10 @@ 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.world.ChunkData;
|
||||
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 {
|
||||
|
||||
private final Client client;
|
||||
@ -29,31 +27,18 @@ public class DefaultClientCommsListener implements CommsListener {
|
||||
getClient().getWorld().getData()
|
||||
);
|
||||
|
||||
if (!(packet instanceof PacketEntityChange)) {
|
||||
tmp_reassembleWorld();
|
||||
}
|
||||
} else if (packet instanceof PacketSetLocalPlayer) {
|
||||
setLocalPlayer((PacketSetLocalPlayer) packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void setLocalPlayer(PacketSetLocalPlayer packet) {
|
||||
UUID uuid = packet.getLocalPlayerEntityUUID();
|
||||
|
||||
Collection<ChunkData> chunks =
|
||||
getClient().getWorld().getData().getChunks();
|
||||
|
||||
EntityData entity = null;
|
||||
|
||||
synchronized (chunks) {
|
||||
chunkLoop:
|
||||
for (ChunkData chunk : chunks) {
|
||||
for (EntityData anEntity : chunk.getEntities()) {
|
||||
if (anEntity.getUUID().equals(uuid)) {
|
||||
entity = anEntity;
|
||||
break chunkLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EntityData entity = getClient().getWorld().getData().getEntity(
|
||||
packet.getLocalPlayerEntityId()
|
||||
);
|
||||
|
||||
if (entity == null) {
|
||||
throw new RuntimeException("");
|
||||
|
@ -18,6 +18,7 @@
|
||||
package ru.windcorp.progressia.client.graphics.world;
|
||||
|
||||
import static java.lang.Math.*;
|
||||
import static ru.windcorp.progressia.common.util.FloatMathUtils.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
@ -76,12 +77,33 @@ public class Camera {
|
||||
|
||||
private float fieldOfView;
|
||||
|
||||
/*
|
||||
* Cache
|
||||
*/
|
||||
|
||||
private final Vec3 lastAnchorPosition = new Vec3();
|
||||
private float lastAnchorYaw;
|
||||
private float lastAnchorPitch;
|
||||
|
||||
private final Mat4 lastCameraMatrix = new Mat4();
|
||||
|
||||
private final Vec3 lastAnchorLookingAt = new Vec3();
|
||||
private final Vec3 lastAnchorUp = new Vec3();
|
||||
|
||||
{
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
public Camera(float fieldOfView) {
|
||||
setFieldOfView(fieldOfView);
|
||||
}
|
||||
|
||||
public Camera() {}
|
||||
|
||||
/*
|
||||
* apply() and subroutines
|
||||
*/
|
||||
|
||||
public void apply(WorldRenderHelper helper) {
|
||||
SoundManager.update();
|
||||
applyPerspective(helper);
|
||||
@ -90,6 +112,8 @@ public class Camera {
|
||||
applyMode(helper);
|
||||
applyDirection(helper);
|
||||
applyPosition(helper);
|
||||
|
||||
cacheCameraTransform(helper);
|
||||
}
|
||||
|
||||
private void applyPerspective(WorldRenderHelper helper) {
|
||||
@ -124,22 +148,48 @@ public class Camera {
|
||||
}
|
||||
|
||||
private void applyDirection(WorldRenderHelper helper) {
|
||||
float pitch = anchor.getCameraPitch();
|
||||
float yaw = anchor.getCameraYaw();
|
||||
|
||||
helper.pushViewTransform()
|
||||
.rotateY(-anchor.getCameraPitch())
|
||||
.rotateZ(-anchor.getCameraYaw());
|
||||
.rotateY(-pitch)
|
||||
.rotateZ(-yaw);
|
||||
|
||||
this.lastAnchorYaw = yaw;
|
||||
this.lastAnchorPitch = pitch;
|
||||
|
||||
this.lastAnchorLookingAt.set(
|
||||
cos(pitch) * cos(yaw),
|
||||
cos(pitch) * sin(yaw),
|
||||
sin(pitch)
|
||||
);
|
||||
this.lastAnchorUp.set(
|
||||
cos(pitch + PI_F / 2) * cos(yaw),
|
||||
cos(pitch + PI_F / 2) * sin(yaw),
|
||||
sin(pitch + PI_F / 2)
|
||||
);
|
||||
}
|
||||
|
||||
private void applyPosition(WorldRenderHelper helper) {
|
||||
Vec3 v = Vectors.grab3();
|
||||
|
||||
anchor.getCameraPosition(v);
|
||||
v.negate();
|
||||
this.lastAnchorPosition.set(v);
|
||||
|
||||
v.negate();
|
||||
helper.pushViewTransform().translate(v);
|
||||
|
||||
Vectors.release(v);
|
||||
}
|
||||
|
||||
private void cacheCameraTransform(WorldRenderHelper helper) {
|
||||
this.lastCameraMatrix.set(helper.getViewTransform());
|
||||
}
|
||||
|
||||
/*
|
||||
* FOV management
|
||||
*/
|
||||
|
||||
private float computeFovY() {
|
||||
float widthOverHeight = GraphicsInterface.getAspectRatio();
|
||||
|
||||
@ -162,6 +212,10 @@ public class Camera {
|
||||
this.fieldOfView = fieldOfView;
|
||||
}
|
||||
|
||||
/*
|
||||
* Anchor management
|
||||
*/
|
||||
|
||||
public Anchor getAnchor() {
|
||||
return anchor;
|
||||
}
|
||||
@ -174,6 +228,7 @@ public class Camera {
|
||||
if (anchor == null) {
|
||||
this.anchor = null;
|
||||
this.modes = null;
|
||||
invalidateCache();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -192,6 +247,22 @@ public class Camera {
|
||||
this.currentModeIndex = 0;
|
||||
}
|
||||
|
||||
private void invalidateCache() {
|
||||
this.lastAnchorPosition.set(Float.NaN);
|
||||
this.lastAnchorYaw = Float.NaN;
|
||||
this.lastAnchorPitch = Float.NaN;
|
||||
|
||||
this.lastCameraMatrix.set(
|
||||
Float.NaN, Float.NaN, Float.NaN, Float.NaN,
|
||||
Float.NaN, Float.NaN, Float.NaN, Float.NaN,
|
||||
Float.NaN, Float.NaN, Float.NaN, Float.NaN,
|
||||
Float.NaN, Float.NaN, Float.NaN, Float.NaN
|
||||
);
|
||||
|
||||
this.lastAnchorLookingAt.set(Float.NaN);
|
||||
this.lastAnchorUp.set(Float.NaN);
|
||||
}
|
||||
|
||||
public Anchor.Mode getMode() {
|
||||
return modes[currentModeIndex];
|
||||
}
|
||||
@ -204,4 +275,28 @@ public class Camera {
|
||||
}
|
||||
}
|
||||
|
||||
public float getLastAnchorYaw() {
|
||||
return lastAnchorYaw;
|
||||
}
|
||||
|
||||
public float getLastAnchorPitch() {
|
||||
return lastAnchorPitch;
|
||||
}
|
||||
|
||||
public Vec3 getLastAnchorPosition() {
|
||||
return lastAnchorPosition;
|
||||
}
|
||||
|
||||
public Mat4 getLastCameraMatrix() {
|
||||
return lastCameraMatrix;
|
||||
}
|
||||
|
||||
public Vec3 getLastAnchorLookingAt() {
|
||||
return lastAnchorLookingAt;
|
||||
}
|
||||
|
||||
public Vec3 getLastAnchorUp() {
|
||||
return lastAnchorUp;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
package ru.windcorp.progressia.common.comms.packets;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PacketSetLocalPlayer extends Packet {
|
||||
|
||||
private final UUID localPlayerEntityUUID;
|
||||
private long localPlayerEntityId;
|
||||
|
||||
public PacketSetLocalPlayer(UUID uuid) {
|
||||
public PacketSetLocalPlayer(long entityId) {
|
||||
super("Core", "SetLocalPlayer");
|
||||
this.localPlayerEntityUUID = uuid;
|
||||
this.localPlayerEntityId = entityId;
|
||||
}
|
||||
|
||||
public UUID getLocalPlayerEntityUUID() {
|
||||
return localPlayerEntityUUID;
|
||||
public long getLocalPlayerEntityId() {
|
||||
return localPlayerEntityId;
|
||||
}
|
||||
|
||||
public void setLocalPlayerEntityId(long localPlayerEntityId) {
|
||||
this.localPlayerEntityId = localPlayerEntityId;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,83 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class AbstractStatefulObjectLayout
|
||||
extends StatefulObjectLayout {
|
||||
|
||||
public AbstractStatefulObjectLayout(String objectId) {
|
||||
super(objectId);
|
||||
}
|
||||
|
||||
protected abstract int getFieldCount();
|
||||
protected abstract StateField getField(int fieldIndex);
|
||||
|
||||
@Override
|
||||
public void read(
|
||||
StatefulObject object,
|
||||
DataInput input,
|
||||
IOContext context
|
||||
) throws IOException {
|
||||
|
||||
int fieldCount = getFieldCount();
|
||||
for (int i = 0; i < fieldCount; ++i) {
|
||||
|
||||
StateField field = getField(i);
|
||||
if (context == IOContext.COMMS && field.isLocal()) continue;
|
||||
field.read(object, input, context);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(
|
||||
StatefulObject object,
|
||||
DataOutput output,
|
||||
IOContext context
|
||||
) throws IOException {
|
||||
|
||||
int fieldCount = getFieldCount();
|
||||
for (int i = 0; i < fieldCount; ++i) {
|
||||
|
||||
StateField field = getField(i);
|
||||
if (context == IOContext.COMMS && field.isLocal()) continue;
|
||||
field.write(object, output, context);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(StatefulObject from, StatefulObject to) {
|
||||
int fieldCount = getFieldCount();
|
||||
for (int i = 0; i < fieldCount; ++i) {
|
||||
getField(i).copy(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHashCode(StatefulObject object) {
|
||||
int result = 1;
|
||||
|
||||
int fieldCount = getFieldCount();
|
||||
for (int i = 0; i < fieldCount; ++i) {
|
||||
|
||||
result = 31 * result + getField(i).computeHashCode(object);
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(StatefulObject a, StatefulObject b) {
|
||||
int fieldCount = getFieldCount();
|
||||
for (int i = 0; i < fieldCount; ++i) {
|
||||
if (!getField(i).areEqual(a, b)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import gnu.trove.map.TIntIntMap;
|
||||
import gnu.trove.map.hash.TIntIntHashMap;
|
||||
|
||||
public class HashMapStateStorage extends StateStorage {
|
||||
|
||||
private final TIntIntMap ints = new TIntIntHashMap();
|
||||
|
||||
@Override
|
||||
public int getInt(int index) {
|
||||
return ints.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInt(int index, int value) {
|
||||
ints.put(index, value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
public enum IOContext {
|
||||
|
||||
COMMS,
|
||||
SAVE,
|
||||
INTERNAL;
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class InspectingStatefulObjectLayout
|
||||
extends AbstractStatefulObjectLayout {
|
||||
|
||||
private final List<StateField> fields = new ArrayList<>();
|
||||
|
||||
private final PrimitiveCounters fieldIndexCounters =
|
||||
new PrimitiveCounters();
|
||||
|
||||
public InspectingStatefulObjectLayout(String objectId) {
|
||||
super(objectId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateStorage createStorage() {
|
||||
return new HashMapStateStorage();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFieldCount() {
|
||||
return fields.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StateField getField(int fieldIndex) {
|
||||
return fields.get(fieldIndex);
|
||||
}
|
||||
|
||||
public StatefulObjectLayout compile() {
|
||||
return new OptimizedStatefulObjectLayout(
|
||||
getObjectId(),
|
||||
fields, fieldIndexCounters
|
||||
);
|
||||
}
|
||||
|
||||
private <T extends StateField> T registerField(T field) {
|
||||
fields.add(field);
|
||||
return field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateFieldBuilder getBuilder(String namespace, String name) {
|
||||
return new InspectingStateFieldBuilder(
|
||||
namespace, name
|
||||
);
|
||||
}
|
||||
|
||||
private class InspectingStateFieldBuilder implements StateFieldBuilder {
|
||||
|
||||
private class Int implements StateFieldBuilder.Int {
|
||||
|
||||
@Override
|
||||
public IntStateField build() {
|
||||
return registerField(new IntStateField(
|
||||
namespace, name,
|
||||
isLocal,
|
||||
fieldIndexCounters.getIntsThenIncrement()
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final String namespace;
|
||||
private final String name;
|
||||
|
||||
private boolean isLocal = true;
|
||||
|
||||
public InspectingStateFieldBuilder(
|
||||
String namespace, String name
|
||||
) {
|
||||
this.namespace = namespace;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int ofInt() {
|
||||
return new Int();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateFieldBuilder setLocal(boolean isLocal) {
|
||||
this.isLocal = isLocal;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrdinal(int ordinal) {
|
||||
if (ordinal != fields.size()) {
|
||||
throw new IllegalStateException(
|
||||
"This field is going to receive ordinal "
|
||||
+ fields.size() + ", requested ordinal "
|
||||
+ ordinal
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class IntStateField extends StateField {
|
||||
|
||||
public IntStateField(
|
||||
String namespace, String name,
|
||||
boolean isLocal,
|
||||
int index
|
||||
) {
|
||||
super(namespace, name, isLocal, index);
|
||||
}
|
||||
|
||||
public int get(StatefulObject object) {
|
||||
return object.getStorage().getInt(getIndex());
|
||||
}
|
||||
|
||||
public void setNow(StatefulObject object, int value) {
|
||||
object.getStorage().setInt(getIndex(), value);
|
||||
}
|
||||
|
||||
public void set(StateChanger changer, int value) {
|
||||
changer.setInt(this, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(
|
||||
StatefulObject object,
|
||||
DataInput input,
|
||||
IOContext context
|
||||
) throws IOException {
|
||||
object.getStorage().setInt(getIndex(), input.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(
|
||||
StatefulObject object,
|
||||
DataOutput output,
|
||||
IOContext context
|
||||
) throws IOException {
|
||||
output.writeInt(object.getStorage().getInt(getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(StatefulObject from, StatefulObject to) {
|
||||
setNow(to, get(from));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHashCode(StatefulObject object) {
|
||||
return get(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(StatefulObject a, StatefulObject b) {
|
||||
return get(a) == get(b);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
public class OptimizedStateStorage extends StateStorage {
|
||||
|
||||
private final int[] ints;
|
||||
|
||||
public OptimizedStateStorage(PrimitiveCounters sizes) {
|
||||
this.ints = new int[sizes.getInts()];
|
||||
}
|
||||
|
||||
public int getInt(int index) {
|
||||
return ints[index];
|
||||
}
|
||||
|
||||
public void setInt(int index, int value) {
|
||||
ints[index] = value;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class OptimizedStatefulObjectLayout
|
||||
extends AbstractStatefulObjectLayout {
|
||||
|
||||
private final List<StateField> fields;
|
||||
private final PrimitiveCounters sizes;
|
||||
|
||||
public OptimizedStatefulObjectLayout(
|
||||
String objectId,
|
||||
List<StateField> fields, PrimitiveCounters counters
|
||||
) {
|
||||
super(objectId);
|
||||
this.fields = ImmutableList.copyOf(fields);
|
||||
this.sizes = new PrimitiveCounters(counters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFieldCount() {
|
||||
return fields.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StateField getField(int fieldIndex) {
|
||||
return fields.get(fieldIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateStorage createStorage() {
|
||||
return new OptimizedStateStorage(sizes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateFieldBuilder getBuilder(String namespace, String name) {
|
||||
return new RetrieverStateFieldBuilder();
|
||||
}
|
||||
|
||||
private class RetrieverStateFieldBuilder implements StateFieldBuilder {
|
||||
|
||||
private StateField result;
|
||||
|
||||
@Override
|
||||
public Int ofInt() {
|
||||
return new Int() {
|
||||
@Override
|
||||
public IntStateField build() {
|
||||
return (IntStateField) result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateFieldBuilder setLocal(boolean isLocal) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrdinal(int ordinal) {
|
||||
this.result = fields.get(ordinal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
class PrimitiveCounters {
|
||||
|
||||
private int ints = 0;
|
||||
|
||||
public PrimitiveCounters() {}
|
||||
|
||||
public PrimitiveCounters(PrimitiveCounters copyFrom) {
|
||||
this.ints = copyFrom.ints;
|
||||
}
|
||||
|
||||
public int getInts() {
|
||||
return ints;
|
||||
}
|
||||
|
||||
public int getIntsThenIncrement() {
|
||||
return this.ints++;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
public interface StateChanger {
|
||||
|
||||
void setInt(IntStateField field, int value);
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import ru.windcorp.progressia.common.util.Namespaced;
|
||||
|
||||
public abstract class StateField extends Namespaced {
|
||||
|
||||
private final boolean isLocal;
|
||||
private final int index;
|
||||
|
||||
public StateField(
|
||||
String namespace, String name,
|
||||
boolean isLocal,
|
||||
int index
|
||||
) {
|
||||
super(namespace, name);
|
||||
this.isLocal = isLocal;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public boolean isLocal() {
|
||||
return isLocal;
|
||||
}
|
||||
|
||||
protected int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public abstract void read(
|
||||
StatefulObject object,
|
||||
DataInput input,
|
||||
IOContext context
|
||||
) throws IOException;
|
||||
|
||||
public abstract void write(
|
||||
StatefulObject object,
|
||||
DataOutput output,
|
||||
IOContext context
|
||||
) throws IOException;
|
||||
|
||||
public abstract void copy(StatefulObject from, StatefulObject to);
|
||||
|
||||
public abstract int computeHashCode(StatefulObject object);
|
||||
|
||||
public abstract boolean areEqual(StatefulObject a, StatefulObject b);
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
public interface StateFieldBuilder {
|
||||
|
||||
public static interface Int {
|
||||
IntStateField build();
|
||||
}
|
||||
|
||||
Int ofInt();
|
||||
|
||||
StateFieldBuilder setLocal(boolean isLocal);
|
||||
|
||||
default StateFieldBuilder setLocal() {
|
||||
return setLocal(true);
|
||||
}
|
||||
|
||||
default StateFieldBuilder setShared() {
|
||||
return setLocal(false);
|
||||
}
|
||||
|
||||
void setOrdinal(int ordinal);
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
public abstract class StateStorage {
|
||||
|
||||
public abstract int getInt(int index);
|
||||
|
||||
public abstract void setInt(int index, int value);
|
||||
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import ru.windcorp.progressia.common.util.Namespaced;
|
||||
|
||||
/**
|
||||
* An abstract class describing objects that have trackable state,
|
||||
* such as blocks, tiles or entities. This class contains the declaration of
|
||||
* the state mechanics
|
||||
* (implementation of which is mostly delegated to
|
||||
* {@link StatefulObjectLayout}).
|
||||
*
|
||||
* <h1>Structure</h1>
|
||||
*
|
||||
* Stateful objects are characterized by their <i>likeness</i> and <i>state</i>.
|
||||
* <p>
|
||||
* An object's likeness is the combination of the object's runtime class (as in
|
||||
* {@link #getClass()}) and its ID. Objects that are "alike" share the same
|
||||
* internal structure, represented by a common
|
||||
* {@linkplain StatefulObjectLayout layout}. Likeness can be tested with
|
||||
* {@link #isLike(Object)}.
|
||||
* <p>
|
||||
* An object's state is the combination of the values of an object's
|
||||
* {@linkplain StateField state fields}. State fields are different from object
|
||||
* fields as described by the Java language: not every object field is a part
|
||||
* of its state, although state fields are usually implemented as object fields.
|
||||
* Each state field is, in its turn, has the following characteristics:
|
||||
* <ul>
|
||||
* <li>ID, distinct from the ID of the stateful object to which it belongs.
|
||||
* State field IDs are only unique within fields of one likeness.</li>
|
||||
* <li>data type, which is one of Java primitive types or a compound (Object)
|
||||
* type.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public abstract class StatefulObject extends Namespaced {
|
||||
|
||||
private final StatefulObjectLayout layout;
|
||||
|
||||
private final StateStorage storage;
|
||||
|
||||
public StatefulObject(
|
||||
StatefulObjectRegistry<?> type,
|
||||
String namespace,
|
||||
String name
|
||||
) {
|
||||
super(namespace, name);
|
||||
this.layout = type.getLayout(getId());
|
||||
this.storage = getLayout().createStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link StatefulObjectLayout} describing objects that are
|
||||
* {@linkplain #isLike(Object) "like"} this object.
|
||||
* You probably don't need this.
|
||||
*
|
||||
* @return this object's field layout
|
||||
*/
|
||||
public StatefulObjectLayout getLayout() {
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link StateStorage} used by this object to store its state in
|
||||
* memory. You probably don't need this.
|
||||
*
|
||||
* @return this object's state storage
|
||||
*/
|
||||
public StateStorage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
/*
|
||||
* Field construction
|
||||
*/
|
||||
|
||||
/**
|
||||
* Used to keep track of the ordinal number of the next field to be
|
||||
* requested.
|
||||
*/
|
||||
private int fieldOrdinal = 0;
|
||||
|
||||
/**
|
||||
* Returns a {@link StateFieldBuilder} set up to construct a field with the
|
||||
* specified ID.
|
||||
* <p>
|
||||
* This method must only be called from the constructor, and the same
|
||||
* sequence of invocations must occur during construction of each object
|
||||
* with the same ID.
|
||||
*
|
||||
* @param namespace the namespace of the new field
|
||||
* @param name the name of the new field
|
||||
*
|
||||
* @return a configured builder
|
||||
*/
|
||||
protected StateFieldBuilder field(String namespace, String name) {
|
||||
StateFieldBuilder builder = getLayout().getBuilder(namespace, name);
|
||||
|
||||
builder.setOrdinal(fieldOrdinal);
|
||||
fieldOrdinal++;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/*
|
||||
* IO
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets the state of this object according to the binary representation read
|
||||
* from {@code input}. If {@code context == COMMS}, the state of local
|
||||
* fields is unspecified after this operation.
|
||||
*
|
||||
* @param input a {@link DataInput} that a state can be read from
|
||||
* @param context the context
|
||||
*
|
||||
* @throws IOException if the state is encoded poorly or an error occurs
|
||||
* in {@code input}
|
||||
*/
|
||||
public void read(DataInput input, IOContext context) throws IOException {
|
||||
getLayout().read(this, input, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the binary representation of the state of this object to the
|
||||
* {@code output}.
|
||||
*
|
||||
* @param output a {@link DataOutput} that a state can be written to
|
||||
* @param context the context
|
||||
*
|
||||
* @throws IOException if an error occurs in {@code output}
|
||||
*/
|
||||
public void write(DataOutput output, IOContext context) throws IOException {
|
||||
getLayout().write(this, output, context);
|
||||
}
|
||||
|
||||
/*
|
||||
* Identity operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* Turns {@code destination} into a deep copy of this object.
|
||||
* <p>
|
||||
* Changes the provided object so that:
|
||||
* <ul>
|
||||
* <li>the provided object equals this object according to
|
||||
* {@link StatefulObject#equals(Object)}; and</li>
|
||||
* <li>the provided object is independent of this object, meaning no change
|
||||
* to {@code destination} can affect this object.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param destination the object to copy this object into.
|
||||
*/
|
||||
public StatefulObject copy(StatefulObject destination) {
|
||||
Objects.requireNonNull(destination, "destination");
|
||||
|
||||
if (destination == this) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot copy an object into itself"
|
||||
);
|
||||
}
|
||||
|
||||
if (destination.getClass() != this.getClass()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot copy from " + getClass()
|
||||
+ " (ID " + getId() + ") to " + destination.getClass()
|
||||
);
|
||||
}
|
||||
|
||||
getLayout().copy(this, destination);
|
||||
return destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #equals(Object)
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getLayout().computeHashCode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this object and {@code obj} have equal states.
|
||||
* Stateful objects are considered equal iff they are
|
||||
* {@linkplain #isLike(Object) "like"} and their binary representations
|
||||
* match exactly.
|
||||
*
|
||||
* @param obj the object to examine
|
||||
* @return {@code true} if {@code obj != null} and this object is equal to
|
||||
* {@code obj}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (!this.isLike(obj)) return false;
|
||||
|
||||
return getLayout().areEqual(this, (StatefulObject) obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the provided object is "like" this object.
|
||||
* <p>
|
||||
* Returns {@code true} iff this object and {@code obj} have the same ID and
|
||||
* are instances of the same class.
|
||||
*
|
||||
* @param obj the object to examine
|
||||
* @return {@code true} if {@code obj} is "like" this object
|
||||
*/
|
||||
public boolean isLike(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (obj == this) return true;
|
||||
if (obj.getClass() != this.getClass()) return false;
|
||||
|
||||
StatefulObject statefulObj = (StatefulObject) obj;
|
||||
|
||||
if (statefulObj.getId().equals(this.getId())) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class StatefulObjectLayout {
|
||||
|
||||
private final String objectId;
|
||||
|
||||
public StatefulObjectLayout(String objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public abstract StateStorage createStorage();
|
||||
|
||||
protected void checkObject(StatefulObject object) {
|
||||
if (!object.getId().equals(getObjectId())) {
|
||||
throw new IllegalArgumentException(
|
||||
object.getId() + " is not " + getObjectId()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void read(
|
||||
StatefulObject object,
|
||||
DataInput input,
|
||||
IOContext context
|
||||
) throws IOException;
|
||||
|
||||
public abstract void write(
|
||||
StatefulObject object,
|
||||
DataOutput output,
|
||||
IOContext context
|
||||
) throws IOException;
|
||||
|
||||
public abstract void copy(StatefulObject from, StatefulObject to);
|
||||
|
||||
public abstract int computeHashCode(StatefulObject object);
|
||||
public abstract boolean areEqual(StatefulObject a, StatefulObject b);
|
||||
|
||||
public abstract StateFieldBuilder getBuilder(String namespace, String name);
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package ru.windcorp.progressia.common.state;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import ru.windcorp.progressia.common.util.Namespaced;
|
||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
||||
|
||||
/**
|
||||
* Registry-like object for identification of various {@link StatefulObject}
|
||||
* types, such as blocks vs tiles. This object stores and manages various
|
||||
* {@linkplain StatefulObjectLayout layouts}.
|
||||
*/
|
||||
public class StatefulObjectRegistry<T extends StatefulObject> {
|
||||
|
||||
@FunctionalInterface
|
||||
public static interface Factory<T> {
|
||||
/**
|
||||
* Initializes a new, independent instance of the stateful object.
|
||||
* @return the created object
|
||||
*/
|
||||
T build();
|
||||
}
|
||||
|
||||
protected static class Type<T> extends Namespaced {
|
||||
|
||||
private final Factory<T> factory;
|
||||
|
||||
private final AtomicBoolean isRegistered = new AtomicBoolean(false);
|
||||
|
||||
public Type(String namespace, String name, Factory<T> factory) {
|
||||
super(namespace, name);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public T build() {
|
||||
return factory.build();
|
||||
}
|
||||
|
||||
public AtomicBoolean getRegistrationFlag() {
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final NamespacedRegistry<Type<T>> registry =
|
||||
new NamespacedRegistry<Type<T>>() {
|
||||
@Override
|
||||
public void register(Type<T> element) {
|
||||
super.register(element);
|
||||
StatefulObjectRegistry.this.register(element);
|
||||
};
|
||||
};
|
||||
|
||||
private final Map<String, StatefulObjectLayout> layouts =
|
||||
Collections.synchronizedMap(new WeakHashMap<>());
|
||||
|
||||
public StatefulObjectLayout getLayout(String id) {
|
||||
StatefulObjectLayout layout = layouts.get(id);
|
||||
|
||||
if (layout == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"ID " + id + " has not been registered"
|
||||
);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
protected void register(Type<T> type) {
|
||||
if (!type.getRegistrationFlag().compareAndSet(false, true)) {
|
||||
throw new IllegalStateException(
|
||||
"ID " + type.getId() + " is already registered"
|
||||
);
|
||||
}
|
||||
|
||||
InspectingStatefulObjectLayout inspector =
|
||||
new InspectingStatefulObjectLayout(type.getId());
|
||||
|
||||
layouts.put(type.getId(), inspector);
|
||||
|
||||
// During initialization inspector collects necessary data
|
||||
type.build();
|
||||
|
||||
layouts.put(type.getId(), inspector.compile());
|
||||
}
|
||||
|
||||
public T create(String id) {
|
||||
return registry.get(id).build();
|
||||
}
|
||||
|
||||
public void register(String namespace, String name, Factory<T> factory) {
|
||||
registry.register(new Type<>(namespace, name, factory));
|
||||
}
|
||||
|
||||
}
|
@ -17,6 +17,9 @@
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.progressia.common.util;
|
||||
|
||||
import glm.vec._2.i.Vec2i;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
|
||||
public class CoordinatePacker {
|
||||
|
||||
private static final int BITS_3_INTS_INTO_LONG;
|
||||
@ -53,6 +56,10 @@ public class CoordinatePacker {
|
||||
((c & MASK_3_INTS_INTO_LONG) << (0 * BITS_3_INTS_INTO_LONG));
|
||||
}
|
||||
|
||||
public static long pack3IntsIntoLong(Vec3i v) {
|
||||
return pack3IntsIntoLong(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
public static int unpack3IntsFromLong(long packed, int index) {
|
||||
if (index < 0 || index >= 3) {
|
||||
throw new IllegalArgumentException("Invalid index " + index);
|
||||
@ -72,12 +79,26 @@ public class CoordinatePacker {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Vec3i unpack3IntsFromLong(long packed, Vec3i output) {
|
||||
output.set(
|
||||
unpack3IntsFromLong(packed, 0),
|
||||
unpack3IntsFromLong(packed, 1),
|
||||
unpack3IntsFromLong(packed, 2)
|
||||
);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static long pack2IntsIntoLong(int a, int b) {
|
||||
return
|
||||
((a & MASK_2_INTS_INTO_LONG) << (1 * BITS_2_INTS_INTO_LONG)) |
|
||||
((b & MASK_2_INTS_INTO_LONG) << (0 * BITS_2_INTS_INTO_LONG));
|
||||
}
|
||||
|
||||
public static long pack2IntsIntoLong(Vec2i v) {
|
||||
return pack2IntsIntoLong(v.x, v.y);
|
||||
}
|
||||
|
||||
public static int unpack2IntsFromLong(long packed, int index) {
|
||||
if (index < 0 || index >= 2) {
|
||||
throw new IllegalArgumentException("Invalid index " + index);
|
||||
@ -91,4 +112,13 @@ public class CoordinatePacker {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Vec2i unpack2IntsFromLong(long packed, Vec2i output) {
|
||||
output.set(
|
||||
unpack2IntsFromLong(packed, 0),
|
||||
unpack2IntsFromLong(packed, 1)
|
||||
);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
121
src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java
Normal file
121
src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java
Normal file
@ -0,0 +1,121 @@
|
||||
package ru.windcorp.progressia.common.util;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import gnu.trove.list.array.TByteArrayList;
|
||||
|
||||
public class DataBuffer {
|
||||
|
||||
private static final int DEFAULT_CAPACITY = 1024;
|
||||
private static final int TRANSFER_BUFFER_SIZE = 1024;
|
||||
|
||||
private final TByteArrayList buffer;
|
||||
|
||||
private final byte[] transferBuffer = new byte[TRANSFER_BUFFER_SIZE];
|
||||
|
||||
private int position;
|
||||
|
||||
private final DataInput reader = new DataInputStream(
|
||||
new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (position >= buffer.size()) return -1;
|
||||
int result = buffer.getQuick(position);
|
||||
++position;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
private final DataOutput writer = new DataOutputStream(
|
||||
new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
buffer.add((byte) b);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
public DataBuffer(int capacity) {
|
||||
this.buffer = new TByteArrayList(capacity);
|
||||
}
|
||||
|
||||
public DataBuffer() {
|
||||
this(DEFAULT_CAPACITY);
|
||||
}
|
||||
|
||||
public DataBuffer(DataBuffer copyFrom) {
|
||||
this.buffer = new TByteArrayList(copyFrom.buffer);
|
||||
}
|
||||
|
||||
public DataInput getReader() {
|
||||
position = 0;
|
||||
return reader;
|
||||
}
|
||||
|
||||
public DataOutput getWriter() {
|
||||
buffer.resetQuick();
|
||||
return writer;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return buffer.size();
|
||||
}
|
||||
|
||||
public void fill(DataInput source, int length) throws IOException {
|
||||
buffer.resetQuick();
|
||||
buffer.ensureCapacity(length);
|
||||
|
||||
while (length > 0) {
|
||||
int currentLength = Math.min(transferBuffer.length, length);
|
||||
|
||||
source.readFully(transferBuffer, 0, currentLength);
|
||||
buffer.add(transferBuffer, 0, currentLength);
|
||||
|
||||
length -= currentLength;
|
||||
}
|
||||
}
|
||||
|
||||
public void flush(DataOutput sink) throws IOException {
|
||||
int position = 0;
|
||||
int length = buffer.size();
|
||||
|
||||
while (position < length) {
|
||||
int currentLength = Math.min(
|
||||
transferBuffer.length,
|
||||
length - position
|
||||
);
|
||||
|
||||
buffer.toArray(transferBuffer, position, 0, currentLength);
|
||||
sink.write(transferBuffer, 0, currentLength);
|
||||
|
||||
length -= currentLength;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return buffer.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
DataBuffer other = (DataBuffer) obj;
|
||||
if (!buffer.equals(other.buffer))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,18 @@ public class FloatMathUtils {
|
||||
return a - 2*PI_F * floor((a + PI_F) / (2*PI_F));
|
||||
}
|
||||
|
||||
public static float sin(float x) {
|
||||
return (float) Math.sin(x);
|
||||
}
|
||||
|
||||
public static float cos(float x) {
|
||||
return (float) Math.cos(x);
|
||||
}
|
||||
|
||||
public static float tan(float x) {
|
||||
return (float) Math.tan(x);
|
||||
}
|
||||
|
||||
private FloatMathUtils() {}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import com.google.errorprone.annotations.DoNotCall;
|
||||
|
||||
public class NamespacedRegistry<E extends Namespaced>
|
||||
@ -15,6 +17,7 @@ implements Map<String, E> {
|
||||
Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
public void register(E element) {
|
||||
LogManager.getLogger(getClass()).debug("Registering " + element.getId());
|
||||
backingMap.put(element.getId(), element);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,46 @@
|
||||
package ru.windcorp.progressia.common.util;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class NamespacedUtil {
|
||||
|
||||
public static final char SEPARATOR = ':';
|
||||
public static final int MAX_PART_LENGTH = 127;
|
||||
public static final int MAX_TOTAL_LENGTH = MAX_PART_LENGTH * 2 + 1;
|
||||
|
||||
private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$";
|
||||
|
||||
private static final Predicate<String> PART_CHECKER =
|
||||
Pattern.compile(PART_REGEX).asPredicate();
|
||||
|
||||
public static String getId(String namespace, String name) {
|
||||
checkPart(namespace, "Namespace");
|
||||
checkPart(name, "Name");
|
||||
|
||||
return namespace + SEPARATOR + name;
|
||||
}
|
||||
|
||||
private static void checkPart(String data, String name) {
|
||||
Objects.requireNonNull(data, name);
|
||||
|
||||
if (data.length() > MAX_PART_LENGTH) {
|
||||
throw new IllegalArgumentException(
|
||||
name + " \"" + data + "\" is too long. "
|
||||
+ "Expected at most " + MAX_PART_LENGTH
|
||||
+ " characters"
|
||||
);
|
||||
}
|
||||
|
||||
if (!PART_CHECKER.test(name)) {
|
||||
throw new IllegalArgumentException(
|
||||
name + " \"" + data + "\" is invalid. "
|
||||
+ "Allowed is: " + PART_REGEX
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private NamespacedUtil() {}
|
||||
|
||||
}
|
@ -22,7 +22,6 @@ import static ru.windcorp.progressia.common.world.block.BlockFace.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -39,6 +38,7 @@ import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityDataRegistry;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
|
||||
|
||||
@ -132,13 +132,18 @@ public class ChunkData {
|
||||
}
|
||||
}
|
||||
|
||||
EntityData javapony = new EntityData("Test", "Javapony");
|
||||
javapony.setUUID(UUID.nameUUIDFromBytes(new byte[] {42}));
|
||||
EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony");
|
||||
javapony.setEntityId(0x42);
|
||||
javapony.setPosition(new Vec3(-6, -6, 20));
|
||||
javapony.setDirection(new Vec2(
|
||||
(float) Math.toRadians(40), (float) Math.toRadians(45)
|
||||
));
|
||||
getEntities().add(javapony);
|
||||
|
||||
EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie");
|
||||
statie.setEntityId(0xDEADBEEF);
|
||||
statie.setPosition(new Vec3(0, 15, 16));
|
||||
getEntities().add(statie);
|
||||
}
|
||||
|
||||
public BlockData getBlock(Vec3i posInChunk) {
|
||||
|
@ -21,28 +21,59 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import gnu.trove.impl.sync.TSynchronizedLongObjectMap;
|
||||
import gnu.trove.map.TLongObjectMap;
|
||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
||||
import ru.windcorp.progressia.common.util.CoordinatePacker;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public class WorldData {
|
||||
|
||||
private final TLongObjectMap<ChunkData> chunks = new TLongObjectHashMap<>();
|
||||
private final TLongObjectMap<ChunkData> chunksByPos =
|
||||
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this);
|
||||
|
||||
private final Collection<ChunkData> chunks =
|
||||
Collections.unmodifiableCollection(chunksByPos.valueCollection());
|
||||
|
||||
private final TLongObjectMap<EntityData> entitiesById =
|
||||
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this);
|
||||
|
||||
private final Collection<EntityData> entities =
|
||||
Collections.unmodifiableCollection(entitiesById.valueCollection());
|
||||
|
||||
public WorldData() {
|
||||
final int size = 1;
|
||||
|
||||
for (int x = -(size / 2); x <= (size / 2); ++x) {
|
||||
for (int y = -(size / 2); y <= (size / 2); ++y) {
|
||||
chunks.put(CoordinatePacker.pack3IntsIntoLong(x, y, 0), new ChunkData(x, y, 0, this));
|
||||
addChunk(new ChunkData(x, y, 0, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void addChunk(ChunkData chunk) {
|
||||
chunksByPos.put(getChunkKey(chunk), chunk);
|
||||
|
||||
chunk.forEachEntity(entity ->
|
||||
entitiesById.put(entity.getEntityId(), entity)
|
||||
);
|
||||
}
|
||||
|
||||
// private synchronized void removeChunk(ChunkData chunk) {
|
||||
// chunksByPos.remove(getChunkKey(chunk));
|
||||
//
|
||||
// chunk.forEachEntity(entity ->
|
||||
// entitiesById.remove(entity.getEntityId())
|
||||
// );
|
||||
// }
|
||||
|
||||
private static long getChunkKey(ChunkData chunk) {
|
||||
return CoordinatePacker.pack3IntsIntoLong(chunk.getPosition());
|
||||
}
|
||||
|
||||
public ChunkData getChunk(Vec3i pos) {
|
||||
long key = CoordinatePacker.pack3IntsIntoLong(pos.x, pos.y, pos.z);
|
||||
return chunks.get(key);
|
||||
return chunksByPos.get(CoordinatePacker.pack3IntsIntoLong(pos));
|
||||
}
|
||||
|
||||
public ChunkData getChunkByBlock(Vec3i blockInWorld) {
|
||||
@ -54,7 +85,15 @@ public class WorldData {
|
||||
}
|
||||
|
||||
public Collection<ChunkData> getChunks() {
|
||||
return Collections.unmodifiableCollection(chunks.valueCollection());
|
||||
return chunks;
|
||||
}
|
||||
|
||||
public EntityData getEntity(long entityId) {
|
||||
return entitiesById.get(entityId);
|
||||
}
|
||||
|
||||
public Collection<EntityData> getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
package ru.windcorp.progressia.common.world.entity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import glm.vec._2.Vec2;
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.progressia.common.util.Namespaced;
|
||||
import ru.windcorp.progressia.common.state.StatefulObject;
|
||||
|
||||
public class EntityData extends Namespaced {
|
||||
public class EntityData extends StatefulObject {
|
||||
|
||||
private final Vec3 position = new Vec3();
|
||||
private final Vec3 velocity = new Vec3();
|
||||
|
||||
private final Vec2 direction = new Vec2();
|
||||
|
||||
private UUID uuid;
|
||||
private long entityId;
|
||||
|
||||
private double age = 0;
|
||||
|
||||
public EntityData(String namespace, String name) {
|
||||
super(namespace, name);
|
||||
super(EntityDataRegistry.getInstance(), namespace, name);
|
||||
}
|
||||
|
||||
public Vec3 getPosition() {
|
||||
@ -51,12 +51,24 @@ public class EntityData extends Namespaced {
|
||||
return getDirection().y;
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
public long getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
public void setUUID(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
public void setEntityId(long entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
public double getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(double age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public void incrementAge(double increment) {
|
||||
this.age += increment;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package ru.windcorp.progressia.common.world.entity;
|
||||
|
||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
||||
import ru.windcorp.progressia.common.state.StatefulObjectRegistry;
|
||||
|
||||
public class EntityDataRegistry extends NamespacedRegistry<EntityData> {
|
||||
public class EntityDataRegistry extends StatefulObjectRegistry<EntityData> {
|
||||
|
||||
private static final EntityDataRegistry INSTANCE = new EntityDataRegistry();
|
||||
|
||||
@ -10,4 +10,8 @@ public class EntityDataRegistry extends NamespacedRegistry<EntityData> {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public void register(String namespace, String name) {
|
||||
super.register(namespace, name, () -> new EntityData(namespace, name));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
package ru.windcorp.progressia.common.world.entity;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.util.DataBuffer;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
|
||||
public class PacketEntityChange extends PacketWorldChange {
|
||||
|
||||
private long entityId;
|
||||
private final DataBuffer buffer = new DataBuffer();
|
||||
|
||||
public PacketEntityChange() {
|
||||
super("Core", "EntityChange");
|
||||
}
|
||||
|
||||
public long getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
public void setEntityId(long entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
public DataBuffer getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public DataInput getReader() {
|
||||
return buffer.getReader();
|
||||
}
|
||||
|
||||
public DataOutput getWriter() {
|
||||
return buffer.getWriter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(WorldData world) {
|
||||
EntityData entity = world.getEntity(getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
throw new RuntimeException(
|
||||
"Entity with ID " + getEntityId() + " not found"
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
entity.read(getReader(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Entity could not be read", e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -4,6 +4,8 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import ru.windcorp.progressia.server.world.Ticker;
|
||||
|
||||
public class ServerThread implements Runnable {
|
||||
@ -50,8 +52,12 @@ public class ServerThread implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
server.tick();
|
||||
ticker.run();
|
||||
} catch (Exception e) {
|
||||
LogManager.getLogger(getClass()).error("Got an exception in server thread", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
|
@ -4,7 +4,6 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import gnu.trove.TCollections;
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
@ -39,10 +38,7 @@ public class ClientManager {
|
||||
|
||||
client.addListener(new DefaultServerCommsListener(this, client));
|
||||
|
||||
client.sendPacket(new PacketSetLocalPlayer(
|
||||
server.getWorld().getData().getChunk(new Vec3i(0, 0, 0))
|
||||
.getEntities().get(0).getUUID()
|
||||
));
|
||||
client.sendPacket(new PacketSetLocalPlayer(0x42));
|
||||
}
|
||||
|
||||
public void disconnectClient(Client client) {
|
||||
|
@ -3,14 +3,22 @@ package ru.windcorp.progressia.server.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.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
|
||||
public interface Changer {
|
||||
|
||||
@FunctionalInterface
|
||||
public static interface Change<T> {
|
||||
void change(T object);
|
||||
}
|
||||
|
||||
void setBlock(Vec3i pos, BlockData block);
|
||||
|
||||
void addTile(Vec3i block, BlockFace face, TileData tile);
|
||||
|
||||
void removeTile(Vec3i block, BlockFace face, TileData tile);
|
||||
|
||||
<T extends EntityData> void changeEntity(T entity, Change<T> change);
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ru.windcorp.progressia.server.world;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -7,25 +8,28 @@ import java.util.Objects;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
||||
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
||||
import ru.windcorp.progressia.common.state.IOContext;
|
||||
import ru.windcorp.progressia.common.util.LowOverheadCache;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.Coordinates;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.common.world.entity.PacketEntityChange;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
|
||||
public class ImplementedChangeTracker implements Changer {
|
||||
|
||||
public static interface Change {
|
||||
public static interface ChangeImplementation {
|
||||
void applyOnServer(WorldData world);
|
||||
Packet asPacket();
|
||||
}
|
||||
|
||||
private static class SetBlock
|
||||
extends PacketWorldChange
|
||||
implements Change {
|
||||
implements ChangeImplementation {
|
||||
|
||||
private final Vec3i position = new Vec3i();
|
||||
private BlockData block;
|
||||
@ -63,7 +67,7 @@ public class ImplementedChangeTracker implements Changer {
|
||||
|
||||
private static class AddOrRemoveTile
|
||||
extends PacketWorldChange
|
||||
implements Change {
|
||||
implements ChangeImplementation {
|
||||
|
||||
private final Vec3i position = new Vec3i();
|
||||
private BlockFace face;
|
||||
@ -115,7 +119,45 @@ public class ImplementedChangeTracker implements Changer {
|
||||
|
||||
}
|
||||
|
||||
private final List<Change> changes = new ArrayList<>(1024);
|
||||
private static class ChangeEntity implements ChangeImplementation {
|
||||
|
||||
private EntityData entity;
|
||||
private Change<?> change;
|
||||
|
||||
private final PacketEntityChange packet = new PacketEntityChange();
|
||||
|
||||
public <T extends EntityData> void set(T entity, Change<T> change) {
|
||||
this.entity = entity;
|
||||
this.change = change;
|
||||
|
||||
packet.setEntityId(entity.getEntityId());
|
||||
try {
|
||||
entity.write(packet.getWriter(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void applyOnServer(WorldData world) {
|
||||
((Change<EntityData>) change).change(entity);
|
||||
|
||||
try {
|
||||
entity.write(packet.getWriter(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Entity could not be written", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet asPacket() {
|
||||
return packet;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final List<ChangeImplementation> changes = new ArrayList<>(1024);
|
||||
|
||||
private final LowOverheadCache<SetBlock> setBlockCache =
|
||||
new LowOverheadCache<>(SetBlock::new);
|
||||
@ -123,6 +165,9 @@ public class ImplementedChangeTracker implements Changer {
|
||||
private final LowOverheadCache<AddOrRemoveTile> addOrRemoveTileCache =
|
||||
new LowOverheadCache<>(AddOrRemoveTile::new);
|
||||
|
||||
private final LowOverheadCache<ChangeEntity> changeEntityCache =
|
||||
new LowOverheadCache<>(ChangeEntity::new);
|
||||
|
||||
@Override
|
||||
public void setBlock(Vec3i pos, BlockData block) {
|
||||
SetBlock change = setBlockCache.grab();
|
||||
@ -144,12 +189,34 @@ public class ImplementedChangeTracker implements Changer {
|
||||
changes.add(change);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends EntityData> void changeEntity(
|
||||
T entity, Change<T> change
|
||||
) {
|
||||
ChangeEntity changeRecord = changeEntityCache.grab();
|
||||
changeRecord.set(entity, change);
|
||||
changes.add(changeRecord);
|
||||
}
|
||||
|
||||
public void applyChanges(Server server) {
|
||||
changes.forEach(c -> c.applyOnServer(server.getWorld().getData()));
|
||||
changes.stream().map(Change::asPacket).filter(Objects::nonNull).forEach(
|
||||
changes.stream().map(ChangeImplementation::asPacket).filter(Objects::nonNull).forEach(
|
||||
server.getClientManager()::broadcastGamePacket
|
||||
);
|
||||
changes.forEach(this::release);
|
||||
changes.clear();
|
||||
}
|
||||
|
||||
private void release(ChangeImplementation c) {
|
||||
if (c instanceof SetBlock) {
|
||||
setBlockCache.release((SetBlock) c);
|
||||
} else if (c instanceof AddOrRemoveTile) {
|
||||
addOrRemoveTileCache.release((AddOrRemoveTile) c);
|
||||
} else if (c instanceof ChangeEntity) {
|
||||
changeEntityCache.release((ChangeEntity) c);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not find cache for " + c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package ru.windcorp.progressia.server.world;
|
||||
|
||||
import ru.windcorp.progressia.server.Server;
|
||||
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.tile.TickableTile;
|
||||
|
||||
public class Ticker implements Runnable {
|
||||
@ -36,12 +38,32 @@ public class Ticker implements Runnable {
|
||||
blockContext.setServer(server);
|
||||
tileContext.setServer(server);
|
||||
|
||||
blockContext.setChunk(chunk);
|
||||
tileContext.setChunk(chunk);
|
||||
|
||||
tickRegularTickers(chunk, blockContext, tileContext);
|
||||
tickRandomBlocks(chunk, blockContext, tileContext);
|
||||
|
||||
tickEntities(chunk, blockContext);
|
||||
|
||||
flushChanges(chunk);
|
||||
}
|
||||
|
||||
private void tickEntities(
|
||||
ChunkLogic chunk,
|
||||
MutableChunkTickContext tickContext
|
||||
) {
|
||||
// TODO this is ugly
|
||||
|
||||
chunk.getData().getEntities().forEach(entity -> {
|
||||
EntityLogic logic = EntityLogicRegistry.getInstance().get(
|
||||
entity.getId()
|
||||
);
|
||||
|
||||
logic.tick(entity, tickContext, tracker);
|
||||
});
|
||||
}
|
||||
|
||||
private void tickRegularTickers(
|
||||
ChunkLogic chunk,
|
||||
MutableBlockTickContext blockContext,
|
||||
|
@ -1,6 +1,9 @@
|
||||
package ru.windcorp.progressia.server.world.entity;
|
||||
|
||||
import ru.windcorp.progressia.common.util.Namespaced;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.server.world.Changer;
|
||||
import ru.windcorp.progressia.server.world.TickContext;
|
||||
|
||||
public class EntityLogic extends Namespaced {
|
||||
|
||||
@ -8,4 +11,8 @@ public class EntityLogic extends Namespaced {
|
||||
super(namespace, name);
|
||||
}
|
||||
|
||||
public void tick(EntityData entity, TickContext context, Changer changer) {
|
||||
entity.incrementAge(context.getTickLength());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ru.windcorp.progressia.client;
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import static ru.windcorp.progressia.client.world.block.BlockRenderRegistry.getBlockTexture;
|
||||
import static ru.windcorp.progressia.client.world.tile.TileRenderRegistry.getTileTexture;
|
||||
@ -12,6 +12,7 @@ import ru.windcorp.progressia.client.world.block.*;
|
||||
import ru.windcorp.progressia.client.world.entity.*;
|
||||
import ru.windcorp.progressia.client.world.tile.*;
|
||||
import ru.windcorp.progressia.common.comms.controls.*;
|
||||
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.*;
|
||||
import ru.windcorp.progressia.common.world.entity.*;
|
||||
@ -77,9 +78,13 @@ public class TestContent {
|
||||
}
|
||||
|
||||
private static void registerEntities() {
|
||||
register(new EntityData("Test", "Javapony"));
|
||||
registerEntityData("Test", "Javapony");
|
||||
register(new TestEntityRenderJavapony());
|
||||
register(new EntityLogic("Test", "Javapony"));
|
||||
|
||||
register("Test", "Statie", TestEntityDataStatie::new);
|
||||
register(new TestEntityRenderStatie());
|
||||
register(new TestEntityLogicStatie());
|
||||
}
|
||||
|
||||
private static void regsiterControls() {
|
||||
@ -112,8 +117,17 @@ public class TestContent {
|
||||
TileDataRegistry.getInstance().register(x);
|
||||
}
|
||||
|
||||
private static void register(EntityData x) {
|
||||
EntityDataRegistry.getInstance().register(x);
|
||||
private static void register(
|
||||
String namespace, String name,
|
||||
Factory<EntityData> factory
|
||||
) {
|
||||
EntityDataRegistry.getInstance().register(namespace, name, factory);
|
||||
}
|
||||
|
||||
private static void registerEntityData(
|
||||
String namespace, String name
|
||||
) {
|
||||
EntityDataRegistry.getInstance().register(namespace, name);
|
||||
}
|
||||
|
||||
private static void register(BlockRender x) {
|
@ -0,0 +1,24 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import ru.windcorp.progressia.common.state.IntStateField;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public class TestEntityDataStatie extends EntityData {
|
||||
|
||||
private final IntStateField size =
|
||||
field("Test", "Size").setShared().ofInt().build();
|
||||
|
||||
public TestEntityDataStatie() {
|
||||
super("Test", "Statie");
|
||||
setSizeNow(16);
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size.get(this);
|
||||
}
|
||||
|
||||
public void setSizeNow(int size) {
|
||||
this.size.setNow(this, size);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
import ru.windcorp.progressia.server.world.Changer;
|
||||
import ru.windcorp.progressia.server.world.TickContext;
|
||||
import ru.windcorp.progressia.server.world.entity.EntityLogic;
|
||||
|
||||
public class TestEntityLogicStatie extends EntityLogic {
|
||||
|
||||
public TestEntityLogicStatie() {
|
||||
super("Test", "Statie");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(EntityData entity, TickContext context, Changer changer) {
|
||||
super.tick(entity, context, changer);
|
||||
|
||||
TestEntityDataStatie statie = (TestEntityDataStatie) entity;
|
||||
|
||||
int size = (int) (18 + 6 * Math.sin(entity.getAge()));
|
||||
changer.changeEntity(statie, e -> e.setSizeNow(size));
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ru.windcorp.progressia.client;
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -320,7 +320,7 @@ public class TestEntityRenderJavapony extends EntityRender {
|
||||
|
||||
new QuadripedModel.Body(body),
|
||||
new QuadripedModel.Head(
|
||||
head, new Vec3(12, 0, 20), 60, 45, new Vec3(16, 0, 20)
|
||||
head, new Vec3(12, 0, 20), 120, 45, new Vec3(16, 0, 20)
|
||||
),
|
||||
new QuadripedModel.Leg(
|
||||
leftForeLeg, new Vec3( 6, +8.1f, -16), 0.0f
|
@ -0,0 +1,42 @@
|
||||
package ru.windcorp.progressia.test;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.progressia.client.graphics.model.Shapes;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.client.world.entity.EntityRender;
|
||||
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
|
||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||
|
||||
public class TestEntityRenderStatie extends EntityRender {
|
||||
|
||||
private final Renderable cube =
|
||||
new Shapes.PppBuilder(
|
||||
WorldRenderProgram.getDefault(),
|
||||
(Texture) null
|
||||
)
|
||||
.setColorMultiplier(1, 1, 0)
|
||||
.create();
|
||||
|
||||
public TestEntityRenderStatie() {
|
||||
super("Test", "Statie");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderable createRenderable(EntityData entity) {
|
||||
return new EntityRenderable(entity) {
|
||||
@Override
|
||||
public void render(ShapeRenderHelper renderer) {
|
||||
renderer.pushTransform().scale(
|
||||
((TestEntityDataStatie) entity).getSize() / 24.0f
|
||||
);
|
||||
|
||||
cube.render(renderer);
|
||||
|
||||
renderer.popTransform();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user