Added ChunkMap and ChunkSet

This commit is contained in:
OLEGSHA 2020-12-25 18:12:14 +03:00
parent f921acf317
commit c6677ec8fd
6 changed files with 590 additions and 37 deletions

View File

@ -25,12 +25,13 @@ import glm.vec._3.i.Vec3i;
import gnu.trove.impl.sync.TSynchronizedLongObjectMap;
import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import gnu.trove.set.TLongSet;
import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.util.CoordinatePacker;
import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.generic.ChunkMap;
import ru.windcorp.progressia.common.world.generic.ChunkSet;
import ru.windcorp.progressia.common.world.generic.GenericWorld;
import ru.windcorp.progressia.common.world.generic.LongBasedChunkMap;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.common.world.tile.TileDataStack;
import ru.windcorp.progressia.test.TestContent;
@ -44,11 +45,12 @@ implements GenericWorld<
EntityData
>{
private final TLongObjectMap<ChunkData> chunksByPos =
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this);
private final ChunkMap<ChunkData> chunksByPos = new LongBasedChunkMap<>(
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this)
);
private final Collection<ChunkData> chunks =
Collections.unmodifiableCollection(chunksByPos.valueCollection());
Collections.unmodifiableCollection(chunksByPos.values());
private final TLongObjectMap<EntityData> entitiesById =
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this);
@ -67,7 +69,7 @@ implements GenericWorld<
@Override
public ChunkData getChunk(Vec3i pos) {
return chunksByPos.get(CoordinatePacker.pack3IntsIntoLong(pos));
return chunksByPos.get(pos);
}
@Override
@ -75,6 +77,10 @@ implements GenericWorld<
return chunks;
}
public ChunkSet getLoadedChunks() {
return chunksByPos.keys();
}
@Override
public Collection<EntityData> getEntities() {
return entities;
@ -102,9 +108,7 @@ implements GenericWorld<
public synchronized void addChunk(ChunkData chunk) {
addChunkListeners(chunk);
long key = getChunkKey(chunk);
ChunkData previous = chunksByPos.get(key);
ChunkData previous = chunksByPos.get(chunk);
if (previous != null) {
throw new IllegalArgumentException(String.format(
"Chunk at (%d; %d; %d) already exists",
@ -112,7 +116,7 @@ implements GenericWorld<
));
}
chunksByPos.put(key, chunk);
chunksByPos.put(chunk, chunk);
chunk.forEachEntity(entity ->
entitiesById.put(entity.getEntityId(), entity)
@ -130,11 +134,7 @@ implements GenericWorld<
entitiesById.remove(entity.getEntityId())
);
chunksByPos.remove(getChunkKey(chunk));
}
private static long getChunkKey(ChunkData chunk) {
return CoordinatePacker.pack3IntsIntoLong(chunk.getPosition());
chunksByPos.remove(chunk);
}
public void setBlock(Vec3i blockInWorld, BlockData block, boolean notify) {
@ -149,10 +149,6 @@ implements GenericWorld<
chunk.setBlock(Coordinates.convertInWorldToInChunk(blockInWorld, null), block, notify);
}
public TLongSet getChunkKeys() {
return chunksByPos.keySet();
}
public EntityData getEntity(long entityId) {
return entitiesById.get(entityId);
}

View File

@ -0,0 +1,113 @@
package ru.windcorp.progressia.common.world.generic;
import java.util.Collection;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import glm.vec._3.i.Vec3i;
public interface ChunkMap<V> {
/*
* Size
*/
int size();
default boolean isEmpty() {
return size() == 0;
}
/*
* Basic operations
*/
boolean containsKey(Vec3i pos);
V get(Vec3i pos);
V put(Vec3i pos, V obj);
V remove(Vec3i pos);
default boolean containsValue(V value) {
return values().contains(value);
}
default V getOrDefault(Vec3i pos, V def) {
return containsKey(pos) ? def : get(pos);
}
default V compute(Vec3i pos, BiFunction<? super Vec3i, ? super V, ? extends V> remappingFunction) {
V newValue = remappingFunction.apply(pos, get(pos));
if (newValue == null) {
remove(pos);
} else {
put(pos, newValue);
}
return newValue;
}
// TODO implement ALL default methods from Map
/*
* Basic operation wrappers
*/
// TODO implement (int, int, int) and GenericChunk versions of all of the above
default boolean containsChunk(GenericChunk<?, ?, ?, ?> chunk) {
return containsKey(chunk.getPosition());
}
default V get(GenericChunk<?, ?, ?, ?> chunk) {
return get(chunk.getPosition());
}
default V put(GenericChunk<?, ?, ?, ?> chunk, V obj) {
return put(chunk.getPosition(), obj);
}
default V remove(GenericChunk<?, ?, ?, ?> chunk) {
return remove(chunk.getPosition());
}
default V getOrDefault(GenericChunk<?, ?, ?, ?> chunk, V def) {
return containsChunk(chunk) ? def : get(chunk);
}
default <C extends GenericChunk<C, ?, ?, ?>> V compute(C chunk, BiFunction<? super C, ? super V, ? extends V> remappingFunction) {
V newValue = remappingFunction.apply(chunk, get(chunk));
if (newValue == null) {
remove(chunk);
} else {
put(chunk, newValue);
}
return newValue;
}
/*
* Views
*/
Collection<V> values();
ChunkSet keys();
/*
* Bulk operations
*/
boolean removeIf(BiPredicate<? super Vec3i, ? super V> condition);
void forEach(BiConsumer<? super Vec3i, ? super V> action);
default <C extends GenericChunk<C, ?, ?, ?>> void forEachIn(GenericWorld<?, ?, ?, C, ?> world, BiConsumer<? super C, ? super V> action) {
forEach((pos, value) -> {
C chunk = world.getChunk(pos);
if (chunk == null) return;
action.accept(chunk, value);
});
}
}

View File

@ -0,0 +1,212 @@
package ru.windcorp.progressia.common.world.generic;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import glm.vec._3.i.Vec3i;
import gnu.trove.set.hash.TLongHashSet;
import ru.windcorp.progressia.common.util.Vectors;
public interface ChunkSet extends Iterable<Vec3i> {
/*
* Size
*/
int size();
default boolean isEmpty() {
return size() == 0;
}
/*
* Basic operations
*/
boolean contains(Vec3i pos);
boolean add(Vec3i pos);
boolean remove(Vec3i pos);
/*
* Basic operation wrappers
*/
default boolean contains(int x, int y, int z) {
Vec3i v = Vectors.grab3i();
boolean result = contains(v);
Vectors.release(v);
return result;
}
default boolean add(int x, int y, int z) {
Vec3i v = Vectors.grab3i();
boolean result = add(v);
Vectors.release(v);
return result;
}
default boolean remove(int x, int y, int z) {
Vec3i v = Vectors.grab3i();
boolean result = remove(v);
Vectors.release(v);
return result;
}
default boolean contains(GenericChunk<?, ?, ?, ?> chunk) {
return contains(chunk.getPosition());
}
default boolean add(GenericChunk<?, ?, ?, ?> chunk) {
return add(chunk.getPosition());
}
default boolean remove(GenericChunk<?, ?, ?, ?> chunk) {
return remove(chunk.getPosition());
}
default <C extends GenericChunk<C, ?, ?, ?>> void forEachIn(GenericWorld<?, ?, ?, C, ?> world, Consumer<? super C> action) {
forEach(position -> {
C chunk = world.getChunk(position);
if (chunk == null) return;
action.accept(chunk);
});
}
/*
* Bulk operations on ChunkSets
*/
boolean containsAll(ChunkSet other);
boolean containsAny(ChunkSet other);
void addAll(ChunkSet other);
void removeAll(ChunkSet other);
void retainAll(ChunkSet other);
/*
* Other bulk operations
*/
void clear();
default boolean containsAll(Iterable<? extends Vec3i> other) {
boolean[] hasMissing = new boolean[] { false };
other.forEach(v -> {
if (!contains(v)) {
hasMissing[0] = true;
}
});
return hasMissing[0];
}
default boolean containsAny(Iterable<? extends Vec3i> other) {
boolean[] hasPresent = new boolean[] { false };
other.forEach(v -> {
if (contains(v)) {
hasPresent[0] = true;
}
});
return hasPresent[0];
}
default void addAll(Iterable<? extends Vec3i> other) {
other.forEach(this::add);
}
default void removeAll(Iterable<? extends Vec3i> other) {
other.forEach(this::remove);
}
default void retainAll(Iterable<? extends Vec3i> other) {
if (other instanceof ChunkSet) {
// We shouldn't invoke retainAll(ChunkSet) because we could be the fallback for it
removeIf(v -> !((ChunkSet) other).contains(v));
return;
}
final int threshold = 16; // Maximum size of other at which point creating a Set becomes faster than iterating
Collection<? extends Vec3i> collection = null;
int otherSize = -1;
if (other instanceof Set<?>) {
collection = (Set<? extends Vec3i>) other;
} else if (other instanceof Collection<?>) {
Collection<? extends Vec3i> otherAsCollection = ((Collection<? extends Vec3i>) other);
otherSize = otherAsCollection.size();
if (otherSize < threshold) {
collection = otherAsCollection;
}
}
if (collection != null) {
final Collection<? extends Vec3i> c = collection;
removeIf(v -> !c.contains(v));
return;
}
if (otherSize < 0) {
otherSize = gnu.trove.impl.Constants.DEFAULT_CAPACITY;
}
retainAll(new LongBasedChunkSet(new TLongHashSet(otherSize), other));
return;
}
default void removeIf(Predicate<? super Vec3i> condition) {
for (Iterator<? extends Vec3i> it = iterator(); it.hasNext();) {
if (condition.test(it.next())) {
it.remove();
}
}
}
default void retainIf(Predicate<? super Vec3i> condition) {
for (Iterator<? extends Vec3i> it = iterator(); it.hasNext();) {
if (!condition.test(it.next())) {
it.remove();
}
}
}
default boolean containsAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
boolean[] hasMissing = new boolean[] { false };
chunks.forEach(c -> {
if (!contains(c.getPosition())) {
hasMissing[0] = true;
}
});
return hasMissing[0];
}
default boolean containsAnyChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
boolean[] hasPresent = new boolean[] { false };
chunks.forEach(c -> {
if (contains(c.getPosition())) {
hasPresent[0] = true;
}
});
return hasPresent[0];
}
default void addAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
chunks.forEach(this::add);
}
default void removeAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
chunks.forEach(this::remove);
}
}

View File

@ -0,0 +1,89 @@
package ru.windcorp.progressia.common.world.generic;
import java.util.Collection;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import glm.vec._3.i.Vec3i;
import gnu.trove.map.TLongObjectMap;
import ru.windcorp.progressia.common.util.CoordinatePacker;
import ru.windcorp.progressia.common.util.Vectors;
public class LongBasedChunkMap<V> implements ChunkMap<V> {
protected final TLongObjectMap<V> impl;
private final ChunkSet keys;
public LongBasedChunkMap(TLongObjectMap<V> impl) {
this.impl = impl;
this.keys = new LongBasedChunkSet(impl.keySet());
}
private static long getKey(Vec3i v) {
return CoordinatePacker.pack3IntsIntoLong(v);
}
private static Vec3i getVector(long key, Vec3i output) {
return CoordinatePacker.unpack3IntsFromLong(key, output);
}
@Override
public int size() {
return impl.size();
}
@Override
public boolean containsKey(Vec3i pos) {
return impl.containsKey(getKey(pos));
}
@Override
public V get(Vec3i pos) {
return impl.get(getKey(pos));
}
@Override
public V put(Vec3i pos, V obj) {
return impl.put(getKey(pos), obj);
}
@Override
public V remove(Vec3i pos) {
return impl.remove(getKey(pos));
}
@Override
public Collection<V> values() {
return impl.valueCollection();
}
@Override
public ChunkSet keys() {
return keys;
}
@Override
public boolean removeIf(BiPredicate<? super Vec3i, ? super V> condition) {
Vec3i v = Vectors.grab3i();
boolean result = impl.retainEntries((key, value) -> {
return !condition.test(getVector(key, v), value);
});
Vectors.release(v);
return result;
}
@Override
public void forEach(BiConsumer<? super Vec3i, ? super V> action) {
Vec3i v = Vectors.grab3i();
impl.forEachEntry((key, value) -> {
action.accept(getVector(key, v), value);
return true;
});
Vectors.release(v);
}
}

View File

@ -0,0 +1,152 @@
package ru.windcorp.progressia.common.world.generic;
import java.util.Iterator;
import java.util.function.Consumer;
import glm.vec._3.i.Vec3i;
import gnu.trove.iterator.TLongIterator;
import gnu.trove.set.TLongSet;
import ru.windcorp.progressia.common.util.CoordinatePacker;
import ru.windcorp.progressia.common.util.Vectors;
public class LongBasedChunkSet implements ChunkSet {
protected final TLongSet impl;
public LongBasedChunkSet(TLongSet impl) {
this.impl = impl;
}
public LongBasedChunkSet(TLongSet impl, ChunkSet copyFrom) {
this(impl);
addAll(copyFrom);
}
public LongBasedChunkSet(TLongSet impl, Iterable<? extends Vec3i> copyFrom) {
this(impl);
addAll(copyFrom);
}
public LongBasedChunkSet(TLongSet impl, GenericWorld<?, ?, ?, ?, ?> copyFrom) {
this(impl);
addAllChunks(copyFrom.getChunks());
}
private static long getKey(Vec3i v) {
return CoordinatePacker.pack3IntsIntoLong(v);
}
private static Vec3i getVector(long key, Vec3i output) {
return CoordinatePacker.unpack3IntsFromLong(key, output);
}
@Override
public Iterator<Vec3i> iterator() {
return new IteratorImpl();
}
@Override
public int size() {
return impl.size();
}
@Override
public boolean contains(Vec3i pos) {
return impl.contains(getKey(pos));
}
@Override
public boolean add(Vec3i pos) {
return impl.add(getKey(pos));
}
@Override
public boolean remove(Vec3i pos) {
return impl.remove(getKey(pos));
}
@Override
public boolean containsAll(ChunkSet other) {
if (other instanceof LongBasedChunkSet) {
return impl.containsAll(((LongBasedChunkSet) other).impl);
}
return ChunkSet.super.containsAll((Iterable<? extends Vec3i>) other);
}
@Override
public boolean containsAny(ChunkSet other) {
return ChunkSet.super.containsAny((Iterable<? extends Vec3i>) other);
}
@Override
public void addAll(ChunkSet other) {
if (other instanceof LongBasedChunkSet) {
impl.addAll(((LongBasedChunkSet) other).impl);
return;
}
ChunkSet.super.addAll((Iterable<? extends Vec3i>) other);
}
@Override
public void removeAll(ChunkSet other) {
if (other instanceof LongBasedChunkSet) {
impl.removeAll(((LongBasedChunkSet) other).impl);
return;
}
ChunkSet.super.removeAll((Iterable<? extends Vec3i>) other);
}
@Override
public void retainAll(ChunkSet other) {
if (other instanceof LongBasedChunkSet) {
impl.retainAll(((LongBasedChunkSet) other).impl);
return;
}
ChunkSet.super.retainAll((Iterable<? extends Vec3i>) other);
}
@Override
public void clear() {
impl.clear();
}
@Override
public void forEach(Consumer<? super Vec3i> action) {
Vec3i v = Vectors.grab3i();
impl.forEach(key -> {
getVector(key, v);
action.accept(v);
return true;
});
Vectors.release(v);
}
private class IteratorImpl implements Iterator<Vec3i> {
private final Vec3i vector = new Vec3i();
private final TLongIterator parent = LongBasedChunkSet.this.impl.iterator();
@Override
public boolean hasNext() {
return parent.hasNext();
}
@Override
public Vec3i next() {
return getVector(parent.next(), vector);
}
@Override
public void remove() {
parent.remove();
}
}
}

View File

@ -5,10 +5,10 @@ import java.util.Collection;
import java.util.Collections;
import glm.vec._3.i.Vec3i;
import gnu.trove.set.TLongSet;
import gnu.trove.set.hash.TLongHashSet;
import ru.windcorp.progressia.common.util.CoordinatePacker;
import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.generic.ChunkSet;
import ru.windcorp.progressia.common.world.generic.LongBasedChunkSet;
import ru.windcorp.progressia.test.TestContent;
public class ChunkLoadManager {
@ -18,9 +18,9 @@ public class ChunkLoadManager {
private final Collection<Collection<? extends ChunkLoader>> allChunkLoaders =
Collections.synchronizedCollection(new ArrayList<>());
private final TLongSet requested = new TLongHashSet();
private final TLongSet toLoad = new TLongHashSet();
private final TLongSet toUnload = new TLongHashSet();
private final ChunkSet requested = new LongBasedChunkSet(new TLongHashSet());
private final ChunkSet toLoad = new LongBasedChunkSet(new TLongHashSet());
private final ChunkSet toUnload = new LongBasedChunkSet(new TLongHashSet());
public ChunkLoadManager(Server server) {
this.server = server;
@ -42,11 +42,11 @@ public class ChunkLoadManager {
}
private void gatherRequests(ChunkLoader loader) {
loader.requestChunksToLoad(v -> requested.add(CoordinatePacker.pack3IntsIntoLong(v)));
loader.requestChunksToLoad(requested::add);
}
private void updateQueues() {
TLongSet loaded = getServer().getWorld().getData().getChunkKeys();
ChunkSet loaded = getServer().getWorld().getData().getLoadedChunks();
toLoad.clear();
toLoad.addAll(requested);
@ -58,17 +58,8 @@ public class ChunkLoadManager {
}
private void processQueues() {
Vec3i v = new Vec3i();
toLoad.forEach(key -> {
loadChunk(CoordinatePacker.unpack3IntsFromLong(key, v));
return true;
});
toUnload.forEach(key -> {
unloadChunk(CoordinatePacker.unpack3IntsFromLong(key, v));
return true;
});
toUnload.forEach(this::unloadChunk);
toLoad.forEach(this::loadChunk);
}
public Server getServer() {