Merge remote-tracking branch 'github/master' into audio

This commit is contained in:
OLEGSHA 2020-10-06 11:44:32 +03:00
commit 3057910269
40 changed files with 1541 additions and 71 deletions

View File

@ -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 {

View File

@ -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()
);
tmp_reassembleWorld();
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("");

View File

@ -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,11 +77,32 @@ 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();
@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,9 @@
package ru.windcorp.progressia.common.state;
public enum IOContext {
COMMS,
SAVE,
INTERNAL;
}

View File

@ -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
);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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++;
}
}

View File

@ -0,0 +1,7 @@
package ru.windcorp.progressia.common.state;
public interface StateChanger {
void setInt(IntStateField field, int value);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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));
}
}

View File

@ -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);
@ -90,5 +111,14 @@ public class CoordinatePacker {
return result;
}
public static Vec2i unpack2IntsFromLong(long packed, Vec2i output) {
output.set(
unpack2IntsFromLong(packed, 0),
unpack2IntsFromLong(packed, 1)
);
return output;
}
}

View 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;
}
}

View File

@ -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() {}
}

View File

@ -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);
}

View File

@ -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() {}
}

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -1,13 +1,17 @@
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();
public static EntityDataRegistry getInstance() {
return INSTANCE;
}
public void register(String namespace, String name) {
super.register(namespace, name, () -> new EntityData(namespace, name));
}
}

View File

@ -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
);
}
}
}

View File

@ -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() {
server.tick();
ticker.run();
try {
server.tick();
ticker.run();
} catch (Exception e) {
LogManager.getLogger(getClass()).error("Got an exception in server thread", e);
}
}
public Server getServer() {

View File

@ -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) {

View File

@ -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);
}

View File

@ -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;
@ -114,8 +118,46 @@ public class ImplementedChangeTracker implements Changer {
}
}
private static class ChangeEntity implements ChangeImplementation {
private EntityData entity;
private Change<?> change;
private final PacketEntityChange packet = new PacketEntityChange();
private final List<Change> changes = new ArrayList<>(1024);
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);
}
}
}

View File

@ -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,

View File

@ -1,11 +1,18 @@
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 {
public EntityLogic(String namespace, String name) {
super(namespace, name);
}
public void tick(EntityData entity, TickContext context, Changer changer) {
entity.incrementAge(context.getTickLength());
}
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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

View File

@ -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();
}
};
}
}