From 1727a2a4a113bea69e242c7960140dd1b50866d8 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Wed, 25 Aug 2021 16:47:56 +0300 Subject: [PATCH] Added Object fields to StatefulObjects and added some utility methods - Added ObjectStateField - Added WorldGenericContextRO.findClosestEntity, .forEachEntity - Added VectorUtil.lookAt, .distance{,Sq} --- .../state/AbstractStatefulObjectLayout.java | 14 +++ .../progressia/common/state/Encodable.java | 64 +++++++++++ .../common/state/HashMapStateStorage.java | 13 +++ .../state/InspectingStatefulObjectLayout.java | 35 +++++- .../common/state/IntStateField.java | 5 + .../common/state/ObjectStateField.java | 107 ++++++++++++++++++ .../common/state/OptimizedStateStorage.java | 12 ++ .../state/OptimizedStatefulObjectLayout.java | 16 ++- .../common/state/PrimitiveCounters.java | 10 ++ .../progressia/common/state/StateChanger.java | 1 + .../progressia/common/state/StateField.java | 2 + .../common/state/StateFieldBuilder.java | 45 ++++++++ .../progressia/common/state/StateStorage.java | 4 + .../common/state/StatefulObject.java | 10 +- .../common/state/codec/EncodableCodec.java | 52 +++++++++ .../state/codec/ImmutableObjectCodec.java | 43 +++++++ .../common/state/codec/ObjectCodec.java | 73 ++++++++++++ .../state/codec/ObjectCodecRegistry.java | 49 ++++++++ .../state/codec/ReusableObjectCodec.java | 42 +++++++ .../progressia/common/util/VectorUtil.java | 71 ++++++++++++ .../context/WorldGenericContextRO.java | 45 +++++++- 21 files changed, 704 insertions(+), 9 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/state/Encodable.java create mode 100644 src/main/java/ru/windcorp/progressia/common/state/ObjectStateField.java create mode 100644 src/main/java/ru/windcorp/progressia/common/state/codec/EncodableCodec.java create mode 100644 src/main/java/ru/windcorp/progressia/common/state/codec/ImmutableObjectCodec.java create mode 100644 src/main/java/ru/windcorp/progressia/common/state/codec/ObjectCodec.java create mode 100644 src/main/java/ru/windcorp/progressia/common/state/codec/ObjectCodecRegistry.java create mode 100644 src/main/java/ru/windcorp/progressia/common/state/codec/ReusableObjectCodec.java diff --git a/src/main/java/ru/windcorp/progressia/common/state/AbstractStatefulObjectLayout.java b/src/main/java/ru/windcorp/progressia/common/state/AbstractStatefulObjectLayout.java index b2feb80..c00e1de 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/AbstractStatefulObjectLayout.java +++ b/src/main/java/ru/windcorp/progressia/common/state/AbstractStatefulObjectLayout.java @@ -28,6 +28,20 @@ public abstract class AbstractStatefulObjectLayout public AbstractStatefulObjectLayout(String objectId) { super(objectId); } + + @Override + public StateStorage createStorage() { + StateStorage storage = instantiateStorage(); + + int fieldCount = getFieldCount(); + for (int i = 0; i < fieldCount; ++i) { + getField(i).setDefault(storage); + } + + return storage; + } + + protected abstract StateStorage instantiateStorage(); protected abstract int getFieldCount(); diff --git a/src/main/java/ru/windcorp/progressia/common/state/Encodable.java b/src/main/java/ru/windcorp/progressia/common/state/Encodable.java new file mode 100644 index 0000000..670a338 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/state/Encodable.java @@ -0,0 +1,64 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.common.state; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +public interface Encodable { + + /** + * 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} + */ + void read(DataInput input, IOContext context) throws IOException; + + /** + * 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} + */ + void write(DataOutput output, IOContext context) throws IOException; + + /** + * Turns {@code destination} into a deep copy of this object. + *

+ * Changes the provided object so that: + *

+ * + * @param destination the object to copy this object into. Runtime class + * must match this class + * @return {@code destination} + */ + void copy(Encodable destination); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/state/HashMapStateStorage.java b/src/main/java/ru/windcorp/progressia/common/state/HashMapStateStorage.java index 52d446f..8a1afc0 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/HashMapStateStorage.java +++ b/src/main/java/ru/windcorp/progressia/common/state/HashMapStateStorage.java @@ -19,11 +19,14 @@ package ru.windcorp.progressia.common.state; import gnu.trove.map.TIntIntMap; +import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntIntHashMap; +import gnu.trove.map.hash.TIntObjectHashMap; public class HashMapStateStorage extends StateStorage { private final TIntIntMap ints = new TIntIntHashMap(); + private final TIntObjectMap objects = new TIntObjectHashMap<>(); @Override public int getInt(int index) { @@ -34,5 +37,15 @@ public class HashMapStateStorage extends StateStorage { public void setInt(int index, int value) { ints.put(index, value); } + + @Override + public Object getObject(int index) { + return objects.get(index); + } + + @Override + public void setObject(int index, Object object) { + objects.put(index, object); + } } diff --git a/src/main/java/ru/windcorp/progressia/common/state/InspectingStatefulObjectLayout.java b/src/main/java/ru/windcorp/progressia/common/state/InspectingStatefulObjectLayout.java index 8b0a9fe..d45b6b8 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/InspectingStatefulObjectLayout.java +++ b/src/main/java/ru/windcorp/progressia/common/state/InspectingStatefulObjectLayout.java @@ -20,6 +20,9 @@ package ru.windcorp.progressia.common.state; import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; + +import ru.windcorp.progressia.common.state.codec.ObjectCodec; public class InspectingStatefulObjectLayout extends AbstractStatefulObjectLayout { @@ -33,7 +36,7 @@ public class InspectingStatefulObjectLayout } @Override - public StateStorage createStorage() { + public StateStorage instantiateStorage() { return new HashMapStateStorage(); } @@ -81,6 +84,31 @@ public class InspectingStatefulObjectLayout } } + + private class Obj implements StateFieldBuilder.Obj { + + private final ObjectCodec codec; + private final Supplier defaultValue; + + public Obj(ObjectCodec codec, Supplier defaultValue) { + this.codec = codec; + this.defaultValue = defaultValue; + } + + @Override + public ObjectStateField build() { + return registerField( + new ObjectStateField( + id, + isLocal, + fieldIndexCounters.getObjectsThenIncrement(), + codec, + defaultValue + ) + ); + } + + } private final String id; @@ -94,6 +122,11 @@ public class InspectingStatefulObjectLayout public Int ofInt() { return new Int(); } + + @Override + public Obj of(ObjectCodec codec, Supplier defaultValue) { + return new Obj(codec, defaultValue); + } @Override public StateFieldBuilder setLocal(boolean isLocal) { diff --git a/src/main/java/ru/windcorp/progressia/common/state/IntStateField.java b/src/main/java/ru/windcorp/progressia/common/state/IntStateField.java index abf8573..3b550c5 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/IntStateField.java +++ b/src/main/java/ru/windcorp/progressia/common/state/IntStateField.java @@ -78,5 +78,10 @@ public class IntStateField extends StateField { public boolean areEqual(StatefulObject a, StatefulObject b) { return get(a) == get(b); } + + @Override + public void setDefault(StateStorage storage) { + storage.setInt(getIndex(), 0); + } } diff --git a/src/main/java/ru/windcorp/progressia/common/state/ObjectStateField.java b/src/main/java/ru/windcorp/progressia/common/state/ObjectStateField.java new file mode 100644 index 0000000..823ce64 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/state/ObjectStateField.java @@ -0,0 +1,107 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package ru.windcorp.progressia.common.state; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.function.Supplier; + +import ru.windcorp.progressia.common.state.codec.ObjectCodec; + +public class ObjectStateField extends StateField { + + private final ObjectCodec codec; + private final Supplier defaultValue; + + public ObjectStateField( + String id, + boolean isLocal, + int index, + ObjectCodec codec, + Supplier defaultValue + ) { + super(id, isLocal, index); + + this.codec = codec; + this.defaultValue = defaultValue; + } + + public ObjectCodec getCodec() { + return codec; + } + + @SuppressWarnings("unchecked") + public T get(StatefulObject object) { + return (T) object.getStorage().getObject(getIndex()); + } + + public void setNow(StatefulObject object, T value) { + object.getStorage().setObject(getIndex(), value); + } + + public void set(StateChanger changer, T value) { + changer.setObject(this, value); + } + + @Override + public void read( + StatefulObject object, + DataInput input, + IOContext context + ) + throws IOException { + + T previous = get(object); + T result = codec.read(previous, input, context); + object.getStorage().setObject(getIndex(), result); + } + + @Override + public void write( + StatefulObject object, + DataOutput output, + IOContext context + ) + throws IOException { + + codec.write(object.getStorage().getObject(getIndex()), output, context); + } + + @Override + public void copy(StatefulObject from, StatefulObject to) { + setNow(to, get(from)); + } + + @Override + public int computeHashCode(StatefulObject object) { + return codec.computeHashCode(get(object)); + } + + @Override + public boolean areEqual(StatefulObject a, StatefulObject b) { + return codec.areEqual(get(a), get(b)); + } + + @Override + public void setDefault(StateStorage storage) { + storage.setObject(getIndex(), defaultValue.get()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/state/OptimizedStateStorage.java b/src/main/java/ru/windcorp/progressia/common/state/OptimizedStateStorage.java index 478fbac..0d75f61 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/OptimizedStateStorage.java +++ b/src/main/java/ru/windcorp/progressia/common/state/OptimizedStateStorage.java @@ -21,9 +21,11 @@ package ru.windcorp.progressia.common.state; public class OptimizedStateStorage extends StateStorage { private final int[] ints; + private final Object[] objects; public OptimizedStateStorage(PrimitiveCounters sizes) { this.ints = new int[sizes.getInts()]; + this.objects = new Object[sizes.getObjects()]; } @Override @@ -35,5 +37,15 @@ public class OptimizedStateStorage extends StateStorage { public void setInt(int index, int value) { ints[index] = value; } + + @Override + public Object getObject(int index) { + return objects[index]; + } + + @Override + public void setObject(int index, Object object) { + objects[index] = object; + } } diff --git a/src/main/java/ru/windcorp/progressia/common/state/OptimizedStatefulObjectLayout.java b/src/main/java/ru/windcorp/progressia/common/state/OptimizedStatefulObjectLayout.java index 1158ab0..14152f4 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/OptimizedStatefulObjectLayout.java +++ b/src/main/java/ru/windcorp/progressia/common/state/OptimizedStatefulObjectLayout.java @@ -19,9 +19,12 @@ package ru.windcorp.progressia.common.state; import java.util.List; +import java.util.function.Supplier; import com.google.common.collect.ImmutableList; +import ru.windcorp.progressia.common.state.codec.ObjectCodec; + public class OptimizedStatefulObjectLayout extends AbstractStatefulObjectLayout { @@ -49,7 +52,7 @@ public class OptimizedStatefulObjectLayout } @Override - public StateStorage createStorage() { + public StateStorage instantiateStorage() { return new OptimizedStateStorage(sizes); } @@ -71,6 +74,17 @@ public class OptimizedStatefulObjectLayout } }; } + + @Override + public Obj of(ObjectCodec codec, Supplier defaultValue) { + return new Obj() { + @SuppressWarnings("unchecked") + @Override + public ObjectStateField build() { + return (ObjectStateField) result; + } + }; + } @Override public StateFieldBuilder setLocal(boolean isLocal) { diff --git a/src/main/java/ru/windcorp/progressia/common/state/PrimitiveCounters.java b/src/main/java/ru/windcorp/progressia/common/state/PrimitiveCounters.java index d3e2dbb..09c71a8 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/PrimitiveCounters.java +++ b/src/main/java/ru/windcorp/progressia/common/state/PrimitiveCounters.java @@ -21,12 +21,14 @@ package ru.windcorp.progressia.common.state; class PrimitiveCounters { private int ints = 0; + private int objects = 0; public PrimitiveCounters() { } public PrimitiveCounters(PrimitiveCounters copyFrom) { this.ints = copyFrom.ints; + this.objects = copyFrom.objects; } public int getInts() { @@ -36,5 +38,13 @@ class PrimitiveCounters { public int getIntsThenIncrement() { return this.ints++; } + + public int getObjects() { + return objects; + } + + public int getObjectsThenIncrement() { + return this.objects++; + } } diff --git a/src/main/java/ru/windcorp/progressia/common/state/StateChanger.java b/src/main/java/ru/windcorp/progressia/common/state/StateChanger.java index cc8cd5a..f68ba43 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StateChanger.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StateChanger.java @@ -21,5 +21,6 @@ package ru.windcorp.progressia.common.state; public interface StateChanger { void setInt(IntStateField field, int value); + void setObject(ObjectStateField field, T value); } diff --git a/src/main/java/ru/windcorp/progressia/common/state/StateField.java b/src/main/java/ru/windcorp/progressia/common/state/StateField.java index ca88c4f..0fe3562 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StateField.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StateField.java @@ -66,5 +66,7 @@ public abstract class StateField extends Namespaced { public abstract int computeHashCode(StatefulObject object); public abstract boolean areEqual(StatefulObject a, StatefulObject b); + + public abstract void setDefault(StateStorage storage); } diff --git a/src/main/java/ru/windcorp/progressia/common/state/StateFieldBuilder.java b/src/main/java/ru/windcorp/progressia/common/state/StateFieldBuilder.java index 97c7176..05a877d 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StateFieldBuilder.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StateFieldBuilder.java @@ -18,13 +18,58 @@ package ru.windcorp.progressia.common.state; +import java.util.function.Supplier; + +import ru.windcorp.progressia.common.state.codec.ObjectCodec; +import ru.windcorp.progressia.common.state.codec.ObjectCodecRegistry; + public interface StateFieldBuilder { public static interface Int { IntStateField build(); } + + public static interface Obj { + ObjectStateField build(); + } Int ofInt(); + + Obj of(ObjectCodec codec, Supplier defaultValue); + + default Obj of(Class clazz, Supplier defaultValue) { + ObjectCodec codec = ObjectCodecRegistry.get(clazz); + return of(codec, defaultValue); + } + + default Obj of(ObjectCodec codec, T defaultValue) { + return of(codec, (Supplier) () -> codec.copy(defaultValue, null)); + } + + default Obj of(Class clazz, T defaultValue) { + ObjectCodec codec = ObjectCodecRegistry.get(clazz); + return of(codec, (Supplier) () -> codec.copy(defaultValue, null)); + } + + default Obj of(ObjectCodec codec) { + return of(codec, (Supplier) () -> null); + } + + default Obj of(Class clazz) { + ObjectCodec codec = ObjectCodecRegistry.get(clazz); + return of(codec, (Supplier) () -> null); + } + + @SuppressWarnings("unchecked") + default Obj def(Supplier defaultValue) { + Class clazz = (Class) defaultValue.get().getClass(); + return of(clazz, defaultValue); + } + + @SuppressWarnings("unchecked") + default Obj def(T defaultValue) { + return of((Class) defaultValue.getClass(), defaultValue); + } StateFieldBuilder setLocal(boolean isLocal); diff --git a/src/main/java/ru/windcorp/progressia/common/state/StateStorage.java b/src/main/java/ru/windcorp/progressia/common/state/StateStorage.java index 2f999f0..7b0ee5e 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StateStorage.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StateStorage.java @@ -23,5 +23,9 @@ public abstract class StateStorage { public abstract int getInt(int index); public abstract void setInt(int index, int value); + + public abstract Object getObject(int index); + + public abstract void setObject(int index, Object object); } diff --git a/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java b/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java index 39f5b58..2feb7e6 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java @@ -52,7 +52,7 @@ import ru.windcorp.progressia.common.util.namespaces.Namespaced; * type. * */ -public abstract class StatefulObject extends Namespaced { +public abstract class StatefulObject extends Namespaced implements Encodable { private final StatefulObjectLayout layout; @@ -133,6 +133,7 @@ public abstract class StatefulObject extends Namespaced { * @throws IOException if the state is encoded poorly or an error occurs * in {@code input} */ + @Override public void read(DataInput input, IOContext context) throws IOException { getLayout().read(this, input, context); } @@ -145,6 +146,7 @@ public abstract class StatefulObject extends Namespaced { * @param context the context * @throws IOException if an error occurs in {@code output} */ + @Override public void write(DataOutput output, IOContext context) throws IOException { getLayout().write(this, output, context); } @@ -166,7 +168,8 @@ public abstract class StatefulObject extends Namespaced { * * @param destination the object to copy this object into. */ - public StatefulObject copy(StatefulObject destination) { + @Override + public void copy(Encodable destination) { Objects.requireNonNull(destination, "destination"); if (destination == this) { @@ -182,8 +185,7 @@ public abstract class StatefulObject extends Namespaced { ); } - getLayout().copy(this, destination); - return destination; + getLayout().copy(this, (StatefulObject) destination); } /** diff --git a/src/main/java/ru/windcorp/progressia/common/state/codec/EncodableCodec.java b/src/main/java/ru/windcorp/progressia/common/state/codec/EncodableCodec.java new file mode 100644 index 0000000..0ff9f1e --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/state/codec/EncodableCodec.java @@ -0,0 +1,52 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.common.state.codec; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Objects; + +import ru.windcorp.progressia.common.state.Encodable; +import ru.windcorp.progressia.common.state.IOContext; + +public class EncodableCodec extends ReusableObjectCodec { + + public EncodableCodec() { + super(Encodable.class); + } + + @Override + protected Encodable doRead(Encodable previous, DataInput input, IOContext context) throws IOException { + previous.read(input, context); + return previous; + } + + @Override + protected void doWrite(Encodable obj, DataOutput output, IOContext context) throws IOException { + obj.write(output, context); + } + + @Override + protected Encodable doCopy(Encodable object, Encodable previous) { + Objects.requireNonNull(previous, "previous"); + object.copy(previous); + return previous; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/state/codec/ImmutableObjectCodec.java b/src/main/java/ru/windcorp/progressia/common/state/codec/ImmutableObjectCodec.java new file mode 100644 index 0000000..64b30a7 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/state/codec/ImmutableObjectCodec.java @@ -0,0 +1,43 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.common.state.codec; + +import java.io.DataInput; +import java.io.IOException; + +import ru.windcorp.progressia.common.state.IOContext; + +public abstract class ImmutableObjectCodec extends ObjectCodec { + + public ImmutableObjectCodec(Class clazz) { + super(clazz); + } + + @Override + public final T copy(T object, T previous) { + return object; + } + + @Override + protected final T doRead(T previous, DataInput input, IOContext context) throws IOException { + return doRead(input, context); + } + + protected abstract T doRead(DataInput input, IOContext context) throws IOException; + +} diff --git a/src/main/java/ru/windcorp/progressia/common/state/codec/ObjectCodec.java b/src/main/java/ru/windcorp/progressia/common/state/codec/ObjectCodec.java new file mode 100644 index 0000000..e5d363c --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/state/codec/ObjectCodec.java @@ -0,0 +1,73 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.common.state.codec; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Objects; + +import ru.windcorp.progressia.common.state.IOContext; + +public abstract class ObjectCodec { + + private final Class clazz; + + public ObjectCodec(Class clazz) { + this.clazz = clazz; + } + + public Class getDataType() { + return clazz; + } + + @SuppressWarnings("unchecked") + public final T read(Object previous, DataInput input, IOContext context) throws IOException { + assert previous == null || clazz.isInstance(previous) + : "Cannot use codec " + this + " on object of type " + previous.getClass(); + + T result = doRead((T) previous, input, context); + + assert result == null || clazz.isInstance(previous) + : "Codec " + this + " read object of type " + previous.getClass(); + return result; + } + + protected abstract T doRead(T previous, DataInput input, IOContext context) throws IOException; + + @SuppressWarnings("unchecked") + public final void write(Object value, DataOutput output, IOContext context) throws IOException { + assert value == null || clazz.isInstance(value) + : "Cannot use codec " + this + " on object of type " + value.getClass(); + + doWrite((T) value, output, context); + } + + protected abstract void doWrite(T obj, DataOutput output, IOContext context) throws IOException; + + public abstract T copy(T object, T previous); + + public int computeHashCode(T object) { + return Objects.hashCode(object); + } + + public boolean areEqual(T a, T b) { + return Objects.equals(a, b); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/state/codec/ObjectCodecRegistry.java b/src/main/java/ru/windcorp/progressia/common/state/codec/ObjectCodecRegistry.java new file mode 100644 index 0000000..582571d --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/state/codec/ObjectCodecRegistry.java @@ -0,0 +1,49 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.common.state.codec; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import ru.windcorp.progressia.common.state.Encodable; + +public class ObjectCodecRegistry { + + private static final Map, ObjectCodec> CODECS = Collections.synchronizedMap(new HashMap<>()); + private static final EncodableCodec ENCODABLE_FALLBACK = new EncodableCodec(); + + public static void register(ObjectCodec codec) { + CODECS.put(codec.getDataType(), codec); + } + + @SuppressWarnings("unchecked") + public static ObjectCodec get(Class clazz) { + ObjectCodec codec = CODECS.get(clazz); + if (codec != null) { + return (ObjectCodec) codec; + } + + if (Encodable.class.isAssignableFrom(clazz)) { + return (ObjectCodec) ENCODABLE_FALLBACK; + } + + throw new IllegalArgumentException("No codec registered for class " + clazz); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/state/codec/ReusableObjectCodec.java b/src/main/java/ru/windcorp/progressia/common/state/codec/ReusableObjectCodec.java new file mode 100644 index 0000000..bb70fcb --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/state/codec/ReusableObjectCodec.java @@ -0,0 +1,42 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.common.state.codec; + +public abstract class ReusableObjectCodec extends ObjectCodec { + + public ReusableObjectCodec(Class clazz) { + super(clazz); + } + + @Override + public final T copy(T object, T previous) { + if (object == null) { + return null; + } + + T result = doCopy(object, previous); + + assert result != null : "copy() returned null"; + assert areEqual(object, result) : "copy() does not equal original: " + result + " != " + object; + + return result; + } + + protected abstract T doCopy(T object, T previous); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java index 3f33e50..a8a0386 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java +++ b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java @@ -199,6 +199,63 @@ public class VectorUtil { return rotateOnly(inOut, mat, inOut); } + public static Mat4 lookAt(Mat4 applyTo, Vec3 target, Vec3 up, Mat4 output) { + if (output == null) { + output = new Mat4(); + } + + Mat4 lookAtTransform = Matrices.grab4(); + + // Adapted from Glm.lookAt - we use Z as up + + // f(normalize(target)) + float fX = target.x; + float fY = target.y; + float fZ = target.z; + float inverseSqrt = 1f / (float) Math.sqrt(fX * fX + fY * fY + fZ * fZ); + fX *= inverseSqrt; + fY *= inverseSqrt; + fZ *= inverseSqrt; + // s(normalize(cross(up, f))) + float sX = up.y * fZ - up.z * fY; + float sY = up.z * fX - up.x * fZ; + float sZ = up.x * fY - up.y * fX; + inverseSqrt = 1.0f / (float) Math.sqrt(sX * sX + sY * sY + sZ * sZ); + sX *= inverseSqrt; + sY *= inverseSqrt; + sZ *= inverseSqrt; + // u(cross(f, s)) + float uX = fY * sZ - fZ * sY; + float uY = fZ * sX - fX * sZ; + float uZ = fX * sY - fY * sX; + + lookAtTransform.m00 = fX; + lookAtTransform.m01 = fY; + lookAtTransform.m02 = fZ; + lookAtTransform.m03 = 0f; + lookAtTransform.m10 = sX; + lookAtTransform.m11 = sY; + lookAtTransform.m12 = sZ; + lookAtTransform.m13 = 0f; + lookAtTransform.m20 = uX; + lookAtTransform.m21 = uY; + lookAtTransform.m22 = uZ; + lookAtTransform.m23 = 0f; + lookAtTransform.m30 = 0; + lookAtTransform.m31 = 0; + lookAtTransform.m32 = 0; + lookAtTransform.m33 = 1f; + + applyTo.mul(lookAtTransform, output); + Matrices.release(lookAtTransform); + + return output; + } + + public static Mat4 lookAt(Vec3 center, Vec3 up, Mat4 inOut) { + return lookAt(inOut, center, up, inOut); + } + public static Vec3 rotate(Vec3 in, Vec3 axis, float angle, Vec3 out) { if (out == null) { out = new Vec3(); @@ -269,6 +326,20 @@ public class VectorUtil { public static Vec3 projectOnVector(Vec3 inOut, Vec3 vector) { return projectOnVector(inOut, vector); } + + public static float distanceSq(Vec3 a, Vec3 b) { + float x = a.x - b.x; + float y = a.y - b.y; + float z = a.z - b.z; + return x * x + y * y + z * z; + } + + public static float distance(Vec3 a, Vec3 b) { + float x = a.x - b.x; + float y = a.y - b.y; + float z = a.z - b.z; + return (float) Math.sqrt(x * x + y * y + z * z); + } public static Vec3 linearCombination( Vec3 va, diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldGenericContextRO.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldGenericContextRO.java index ec188d1..1001708 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldGenericContextRO.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldGenericContextRO.java @@ -19,8 +19,11 @@ package ru.windcorp.progressia.common.world.generic.context; import java.util.Collection; import java.util.function.Consumer; +import java.util.function.Predicate; +import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.world.context.Context; import ru.windcorp.progressia.common.world.generic.*; import ru.windcorp.progressia.common.world.rels.RelFace; @@ -141,16 +144,52 @@ public interface WorldGenericContextRO< */ E getEntity(long entityId); + /* + * Convenience methods + */ + + default void forEachEntity(Consumer action) { + getEntities().forEach(action); + } + + default E findClosestEntity(Vec3 location, Predicate filter, float maxDistance) { + if (maxDistance <= 0) { + return null; + } + + E result = getEntities().stream().filter(filter).min((a, b) -> { + float aDistance = VectorUtil.distanceSq(location, a.getPosition()); + float bDistance = VectorUtil.distanceSq(location, b.getPosition()); + return Float.compare(aDistance, bDistance); + }).orElse(null); + + if (result == null) { + return null; + } + if (Float.isInfinite(maxDistance)) { + return result; + } + if (VectorUtil.distanceSq(location, result.getPosition()) > maxDistance * maxDistance) { + return null; + } + + return result; + } + + default E findClosestEntity(Vec3 location, float maxDistance) { + return findClosestEntity(location, e -> true, maxDistance); + } + /* * Subcontexting */ - + @Override BlockGenericContextRO push(Vec3i location); - + @Override TileStackGenericContextRO push(Vec3i location, RelFace face); - + @Override TileGenericContextRO push(Vec3i location, RelFace face, int layer);