Testing system for NodeManager as well as completion of basic functionality of NodeManager
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
package me.cortex.voxy.client.core.rendering;
|
||||||
|
|
||||||
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
|
|
||||||
|
public interface ISectionWatcher {
|
||||||
|
default boolean watch(int lvl, int x, int y, int z, int types) {
|
||||||
|
return this.watch(WorldEngine.getWorldSectionId(lvl, x, y, z), types);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean watch(long position, int types);
|
||||||
|
|
||||||
|
default boolean unwatch(int lvl, int x, int y, int z, int types) {
|
||||||
|
return this.unwatch(WorldEngine.getWorldSectionId(lvl, x, y, z), types);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean unwatch(long position, int types);
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
|
|||||||
import me.cortex.voxy.client.core.model.ModelStore;
|
import me.cortex.voxy.client.core.model.ModelStore;
|
||||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||||
import me.cortex.voxy.client.core.rendering.building.SectionUpdateRouter;
|
|
||||||
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
|
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
|
||||||
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
|
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
|
||||||
import me.cortex.voxy.client.core.rendering.hierachical.NodeManager;
|
import me.cortex.voxy.client.core.rendering.hierachical.NodeManager;
|
||||||
@@ -78,7 +77,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
|||||||
|
|
||||||
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, this.nodeCleaner);
|
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, this.nodeCleaner);
|
||||||
|
|
||||||
world.setDirtyCallback(router::forward);
|
world.setDirtyCallback(router::forwardEvent);
|
||||||
|
|
||||||
Arrays.stream(world.getMapper().getBiomeEntries()).forEach(this.modelService::addBiome);
|
Arrays.stream(world.getMapper().getBiomeEntries()).forEach(this.modelService::addBiome);
|
||||||
world.getMapper().setBiomeCallback(this.modelService::addBiome);
|
world.getMapper().setBiomeCallback(this.modelService::addBiome);
|
||||||
@@ -154,7 +153,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
|||||||
private int q = -60;
|
private int q = -60;
|
||||||
public void setup(Camera camera) {
|
public void setup(Camera camera) {
|
||||||
final int W = 32;
|
final int W = 32;
|
||||||
final int H = 2;
|
final int H = 3;
|
||||||
boolean SIDED = false;
|
boolean SIDED = false;
|
||||||
for (int i = 0; i<64 && q<((W*2+1)*(W*2+1)*H)&&q++>=0;i++) {
|
for (int i = 0; i<64 && q<((W*2+1)*(W*2+1)*H)&&q++>=0;i++) {
|
||||||
this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, (q%(W*2+1))-(SIDED?0:W), ((q/(W*2+1))/(W*2+1))-1, ((q/(W*2+1))%(W*2+1))-(SIDED?0:W)));
|
this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, (q%(W*2+1))-(SIDED?0:W), ((q/(W*2+1))/(W*2+1))-1, ((q/(W*2+1))%(W*2+1))-(SIDED?0:W)));
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package me.cortex.voxy.client.core.rendering.building;
|
package me.cortex.voxy.client.core.rendering;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
import me.cortex.voxy.common.world.WorldSection;
|
import me.cortex.voxy.common.world.WorldSection;
|
||||||
|
|
||||||
@@ -10,7 +8,7 @@ import java.util.function.LongConsumer;
|
|||||||
|
|
||||||
import static me.cortex.voxy.common.world.WorldEngine.UPDATE_TYPE_BLOCK_BIT;
|
import static me.cortex.voxy.common.world.WorldEngine.UPDATE_TYPE_BLOCK_BIT;
|
||||||
|
|
||||||
public class SectionUpdateRouter {
|
public class SectionUpdateRouter implements ISectionWatcher {
|
||||||
private static final int SLICES = 1<<3;
|
private static final int SLICES = 1<<3;
|
||||||
public interface IChildUpdate {void accept(WorldSection section);}
|
public interface IChildUpdate {void accept(WorldSection section);}
|
||||||
|
|
||||||
@@ -78,7 +76,7 @@ public class SectionUpdateRouter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forward(WorldSection section, int type) {
|
public void forwardEvent(WorldSection section, int type) {
|
||||||
final long position = section.key;
|
final long position = section.key;
|
||||||
var set = this.slices[getSliceIndex(position)];
|
var set = this.slices[getSliceIndex(position)];
|
||||||
byte types = 0;
|
byte types = 0;
|
||||||
@@ -3,16 +3,20 @@ package me.cortex.voxy.client.core.rendering.hierachical;
|
|||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||||
|
import me.cortex.voxy.client.core.rendering.ISectionWatcher;
|
||||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||||
import me.cortex.voxy.client.core.rendering.building.SectionUpdateRouter;
|
|
||||||
import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager;
|
import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager;
|
||||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||||
import me.cortex.voxy.client.core.util.ExpandingObjectAllocationList;
|
import me.cortex.voxy.client.core.util.ExpandingObjectAllocationList;
|
||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
|
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
import me.cortex.voxy.commonImpl.VoxyCommon;
|
|
||||||
import net.caffeinemc.mods.sodium.client.util.MathUtil;
|
import net.caffeinemc.mods.sodium.client.util.MathUtil;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -26,7 +30,7 @@ import static me.cortex.voxy.common.world.WorldEngine.MAX_LOD_LAYERS;
|
|||||||
|
|
||||||
|
|
||||||
public class NodeManager {
|
public class NodeManager {
|
||||||
private static final boolean VERIFY_NODE_MANAGER_OPERATIONS = VoxyCommon.isVerificationFlagOn("nodeManager");
|
private static final boolean VERIFY_NODE_MANAGER_OPERATIONS = true;//VoxyCommon.isVerificationFlagOn("nodeManager");
|
||||||
//Assumptions:
|
//Assumptions:
|
||||||
// all nodes have children (i.e. all nodes have at least one child existence bit set at all times)
|
// all nodes have children (i.e. all nodes have at least one child existence bit set at all times)
|
||||||
// leaf nodes always contain geometry (empty geometry counts as geometry (it just doesnt take any memory to store))
|
// leaf nodes always contain geometry (empty geometry counts as geometry (it just doesnt take any memory to store))
|
||||||
@@ -50,6 +54,8 @@ public class NodeManager {
|
|||||||
|
|
||||||
public static final int NULL_GEOMETRY_ID = -1;
|
public static final int NULL_GEOMETRY_ID = -1;
|
||||||
public static final int EMPTY_GEOMETRY_ID = -2;
|
public static final int EMPTY_GEOMETRY_ID = -2;
|
||||||
|
public static final int NULL_REQUEST_ID = NodeStore.REQUEST_ID_MSK;
|
||||||
|
public static final int SENTINEL_EMPTY_CHILD_PTR = NodeStore.NODE_ID_MSK-1;
|
||||||
|
|
||||||
public static final int NODE_ID_MSK = ((1<<24)-1);
|
public static final int NODE_ID_MSK = ((1<<24)-1);
|
||||||
private static final int NODE_TYPE_MSK = 0b11<<30;
|
private static final int NODE_TYPE_MSK = 0b11<<30;
|
||||||
@@ -66,11 +72,12 @@ public class NodeManager {
|
|||||||
private final ExpandingObjectAllocationList<NodeChildRequest> childRequests = new ExpandingObjectAllocationList<>(NodeChildRequest[]::new);
|
private final ExpandingObjectAllocationList<NodeChildRequest> childRequests = new ExpandingObjectAllocationList<>(NodeChildRequest[]::new);
|
||||||
private final IntOpenHashSet nodeUpdates = new IntOpenHashSet();
|
private final IntOpenHashSet nodeUpdates = new IntOpenHashSet();
|
||||||
private final AbstractSectionGeometryManager geometryManager;
|
private final AbstractSectionGeometryManager geometryManager;
|
||||||
private final SectionUpdateRouter updateRouter;
|
private final ISectionWatcher watcher;
|
||||||
private final Long2IntOpenHashMap activeSectionMap = new Long2IntOpenHashMap();
|
private final Long2IntOpenHashMap activeSectionMap = new Long2IntOpenHashMap();
|
||||||
private final NodeStore nodeData;
|
private final NodeStore nodeData;
|
||||||
public final int maxNodeCount;
|
public final int maxNodeCount;
|
||||||
private final IntArrayList topLevelNodeIds = new IntArrayList();
|
private final IntArrayList topLevelNodeIds = new IntArrayList();
|
||||||
|
private final LongOpenHashSet topLevelNodes = new LongOpenHashSet();
|
||||||
private int activeNodeRequestCount;
|
private int activeNodeRequestCount;
|
||||||
|
|
||||||
public interface ClearIdCallback {void clearId(int id);}
|
public interface ClearIdCallback {void clearId(int id);}
|
||||||
@@ -78,7 +85,7 @@ public class NodeManager {
|
|||||||
public void setClearIdCallback(ClearIdCallback callback) {this.clearIdCallback = callback;}
|
public void setClearIdCallback(ClearIdCallback callback) {this.clearIdCallback = callback;}
|
||||||
private void clearId(int id) { if (this.clearIdCallback != null) this.clearIdCallback.clearId(id); }
|
private void clearId(int id) { if (this.clearIdCallback != null) this.clearIdCallback.clearId(id); }
|
||||||
|
|
||||||
public NodeManager(int maxNodeCount, AbstractSectionGeometryManager geometryManager, SectionUpdateRouter updateRouter) {
|
public NodeManager(int maxNodeCount, AbstractSectionGeometryManager geometryManager, ISectionWatcher watcher) {
|
||||||
if (!MathUtil.isPowerOfTwo(maxNodeCount)) {
|
if (!MathUtil.isPowerOfTwo(maxNodeCount)) {
|
||||||
throw new IllegalArgumentException("Max node count must be a power of 2");
|
throw new IllegalArgumentException("Max node count must be a power of 2");
|
||||||
}
|
}
|
||||||
@@ -86,7 +93,7 @@ public class NodeManager {
|
|||||||
throw new IllegalArgumentException("Max node count cannot exceed 2^24");
|
throw new IllegalArgumentException("Max node count cannot exceed 2^24");
|
||||||
}
|
}
|
||||||
this.activeSectionMap.defaultReturnValue(-1);
|
this.activeSectionMap.defaultReturnValue(-1);
|
||||||
this.updateRouter = updateRouter;
|
this.watcher = watcher;
|
||||||
this.maxNodeCount = maxNodeCount;
|
this.maxNodeCount = maxNodeCount;
|
||||||
this.nodeData = new NodeStore(maxNodeCount);
|
this.nodeData = new NodeStore(maxNodeCount);
|
||||||
this.geometryManager = geometryManager;
|
this.geometryManager = geometryManager;
|
||||||
@@ -103,8 +110,9 @@ public class NodeManager {
|
|||||||
|
|
||||||
var request = new SingleNodeRequest(pos);
|
var request = new SingleNodeRequest(pos);
|
||||||
int id = this.singleRequests.put(request);
|
int id = this.singleRequests.put(request);
|
||||||
this.updateRouter.watch(pos, WorldEngine.UPDATE_FLAGS);
|
this.watcher.watch(pos, WorldEngine.UPDATE_FLAGS);
|
||||||
this.activeSectionMap.put(pos, id|NODE_TYPE_REQUEST|REQUEST_TYPE_SINGLE);
|
this.activeSectionMap.put(pos, id|NODE_TYPE_REQUEST|REQUEST_TYPE_SINGLE);
|
||||||
|
this.topLevelNodes.add(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeTopLevelNode(long pos) {
|
public void removeTopLevelNode(long pos) {
|
||||||
@@ -119,6 +127,9 @@ public class NodeManager {
|
|||||||
// OR!! just ensure the list is always ordered?? maybe? idk i think hashmap is best
|
// OR!! just ensure the list is always ordered?? maybe? idk i think hashmap is best
|
||||||
// since the array list might get shuffled as nodes are removed
|
// since the array list might get shuffled as nodes are removed
|
||||||
// since need to move the entry at the end of the array to fill a hole made
|
// since need to move the entry at the end of the array to fill a hole made
|
||||||
|
|
||||||
|
// remove from topLevelNodes aswell
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -182,6 +193,8 @@ public class NodeManager {
|
|||||||
if (this.updateNodeGeometry(nodeId&NODE_ID_MSK, sectionResult) != 0) {
|
if (this.updateNodeGeometry(nodeId&NODE_ID_MSK, sectionResult) != 0) {
|
||||||
this.invalidateNode(nodeId&NODE_ID_MSK);
|
this.invalidateNode(nodeId&NODE_ID_MSK);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +294,7 @@ public class NodeManager {
|
|||||||
throw new IllegalStateException("Child pos was in a request but not in active section map");
|
throw new IllegalStateException("Child pos was in a request but not in active section map");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.updateRouter.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
if (!this.watcher.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Child pos was not being watched");
|
throw new IllegalStateException("Child pos was not being watched");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,7 +312,7 @@ public class NodeManager {
|
|||||||
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
|
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
|
||||||
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
|
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
|
||||||
}
|
}
|
||||||
if (!this.updateRouter.watch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
if (!this.watcher.watch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Child pos update router issue");
|
throw new IllegalStateException("Child pos update router issue");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,14 +335,14 @@ public class NodeManager {
|
|||||||
private void updateChildSectionsInner(long pos, int nodeId, byte childExistence) {
|
private void updateChildSectionsInner(long pos, int nodeId, byte childExistence) {
|
||||||
//Very complex and painful operation
|
//Very complex and painful operation
|
||||||
|
|
||||||
|
if (childExistence == 0) {
|
||||||
//TODO: operation of needing to create a request node to add new sections
|
Logger.warn("Inner node child existence is changing to 0, this is mild bad");
|
||||||
// (or modify the node to remove a child node (recursively probably ;-;))
|
}
|
||||||
|
|
||||||
|
|
||||||
//This works in 2 parts, adding and removing, adding is (surprisingly) much easier than removing
|
//This works in 2 parts, adding and removing, adding is (surprisingly) much easier than removing
|
||||||
// adding, either adds to a request, or creates a new request
|
// adding, either adds to a request, or creates a new request
|
||||||
byte existence = this.nodeData.getNodeChildExistence(nodeId);
|
byte existence = this.nodeData.getNodeChildExistence(nodeId);
|
||||||
|
|
||||||
byte add = (byte) ((existence^childExistence)&childExistence);
|
byte add = (byte) ((existence^childExistence)&childExistence);
|
||||||
if (add != 0) {//We have nodes to add
|
if (add != 0) {//We have nodes to add
|
||||||
if (!this.nodeData.isNodeRequestInFlight(nodeId)) {//If there is not an existing request, create it
|
if (!this.nodeData.isNodeRequestInFlight(nodeId)) {//If there is not an existing request, create it
|
||||||
@@ -356,7 +369,7 @@ public class NodeManager {
|
|||||||
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
|
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
|
||||||
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
|
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
|
||||||
}
|
}
|
||||||
if (!this.updateRouter.watch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
if (!this.watcher.watch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Child pos update router issue");
|
throw new IllegalStateException("Child pos update router issue");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,10 +378,10 @@ public class NodeManager {
|
|||||||
//Update the nodes existence msk to the new one
|
//Update the nodes existence msk to the new one
|
||||||
// this needs to be before the removal since that may invoke requestFinish, which expects updated node masks
|
// this needs to be before the removal since that may invoke requestFinish, which expects updated node masks
|
||||||
//TODO: verify this
|
//TODO: verify this
|
||||||
this.nodeData.setNodeChildExistence(nodeId&NODE_ID_MSK, childExistence);
|
this.nodeData.setNodeChildExistence(nodeId, childExistence);
|
||||||
|
|
||||||
// Do removals
|
// Do removals
|
||||||
byte rem = (byte) ((existence^childExistence)&existence);
|
int rem = ((existence^childExistence)&existence)&0xFF;
|
||||||
if (rem != 0) {
|
if (rem != 0) {
|
||||||
//If there is an inflight request, update it w.r.t removals
|
//If there is an inflight request, update it w.r.t removals
|
||||||
if (this.nodeData.isNodeRequestInFlight(nodeId)) {
|
if (this.nodeData.isNodeRequestInFlight(nodeId)) {
|
||||||
@@ -377,7 +390,7 @@ public class NodeManager {
|
|||||||
if (request.getPosition() != pos) throw new IllegalStateException("Request is not at pos");
|
if (request.getPosition() != pos) throw new IllegalStateException("Request is not at pos");
|
||||||
|
|
||||||
|
|
||||||
byte reqRem = (byte) (request.getMsk()&rem);
|
int reqRem =Byte.toUnsignedInt(request.getMsk())&rem;
|
||||||
if (reqRem != 0) {
|
if (reqRem != 0) {
|
||||||
//There are things in the request to remove
|
//There are things in the request to remove
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
@@ -392,37 +405,144 @@ public class NodeManager {
|
|||||||
if (this.activeSectionMap.remove(cPos) == -1) {//TODO: verify the removed section is a request type of child and the request id matches this
|
if (this.activeSectionMap.remove(cPos) == -1) {//TODO: verify the removed section is a request type of child and the request id matches this
|
||||||
throw new IllegalStateException("Child pos was in a request but not in active section map");
|
throw new IllegalStateException("Child pos was in a request but not in active section map");
|
||||||
}
|
}
|
||||||
if (!this.updateRouter.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
if (!this.watcher.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Child pos was not being watched");
|
throw new IllegalStateException("Child pos was not being watched");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO: FIXME: This isnt right, as we need to remove node + geometry if it was in a request aswell as a child?
|
|
||||||
// BUT dont think thats possible?
|
|
||||||
|
|
||||||
|
|
||||||
rem ^= reqRem;
|
rem ^= reqRem;
|
||||||
//If the request is satisfied, submit the result
|
}
|
||||||
|
|
||||||
|
if (rem != 0) {
|
||||||
|
//There are child node entries that need removing
|
||||||
|
// and of course still delete all the old data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Compact the node data with respect to what has been removed
|
||||||
|
int oldPtr = this.nodeData.getChildPtr(nodeId);
|
||||||
|
int oldCount = this.nodeData.getChildPtrCount(nodeId);
|
||||||
|
if (oldPtr == -1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
int oldExistence = 0;
|
||||||
|
for (int i = 0; i < oldCount; i++) {
|
||||||
|
if (!this.nodeData.nodeExists(i+oldPtr)) throw new IllegalStateException();
|
||||||
|
oldExistence |= 1<<getChildIdx(this.nodeData.nodePosition(i+oldPtr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rem&oldExistence)!=rem) {//If rem contains stuff that does not exist, is illegal
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int remaining = rem^oldExistence;
|
||||||
|
|
||||||
|
if (remaining == 0) {
|
||||||
|
//This state should only ever occur when a node is inflight, or... if an inner node has existance mask of 0... sigh
|
||||||
|
if (childExistence != 0 && !this.nodeData.isNodeRequestInFlight(nodeId)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
//TODO: TRIPPLY CHECK THIS IS RIGHT
|
||||||
|
//TODO: make new SENTINAL value for this!!! NodeStore.NODE_ID_MSK-1
|
||||||
|
// check in shader aswell!!!
|
||||||
|
|
||||||
|
this.nodeData.setChildPtr(nodeId, SENTINEL_EMPTY_CHILD_PTR);
|
||||||
|
this.nodeData.setChildPtrCount(nodeId, 8);
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if ((rem&(1<<i))==0) continue;
|
||||||
|
long cPos = makeChildPos(pos, i);
|
||||||
|
this.recurseRemoveNode(cPos);
|
||||||
|
}
|
||||||
|
//this.nodeData.free(oldPtr, oldCount);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
int newCnt = Integer.bitCount(remaining);
|
||||||
|
int newPtr = this.nodeData.allocate(newCnt);
|
||||||
|
int prevChildId = oldPtr - 1;
|
||||||
|
int newChildId = newPtr - 1;
|
||||||
|
|
||||||
|
//Need to compact the old into the new
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if ((oldExistence & (1 << i)) == 0) continue;
|
||||||
|
prevChildId++;
|
||||||
|
if ((rem & (1 << i)) != 0) {//If we removing
|
||||||
|
long cPos = makeChildPos(pos, i);
|
||||||
|
this.recurseRemoveNode(cPos);
|
||||||
|
} else {//We are compacting
|
||||||
|
newChildId++;
|
||||||
|
long cPos = this.nodeData.nodePosition(prevChildId);
|
||||||
|
if (cPos != makeChildPos(pos, i)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy the previous entry to its new location
|
||||||
|
this.nodeData.copyNode(prevChildId, newChildId);
|
||||||
|
|
||||||
|
int prevNodeId = this.activeSectionMap.get(cPos);
|
||||||
|
if ((prevNodeId & NODE_TYPE_MSK) == NODE_TYPE_REQUEST) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if ((prevNodeId & NODE_ID_MSK) != prevChildId) {
|
||||||
|
throw new IllegalStateException("State inconsistency");
|
||||||
|
}
|
||||||
|
this.activeSectionMap.put(cPos, (prevNodeId & NODE_TYPE_MSK) | newChildId);
|
||||||
|
//Release the old entry
|
||||||
|
this.nodeData.free(prevChildId);
|
||||||
|
//Need to invalidate the old and the new
|
||||||
|
this.invalidateNode(prevChildId);
|
||||||
|
this.invalidateNode(newChildId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Put the new childPtr into the map
|
||||||
|
this.nodeData.setChildPtr(nodeId, newPtr);
|
||||||
|
this.nodeData.setChildPtrCount(nodeId, newCnt);
|
||||||
|
|
||||||
|
if (VERIFY_NODE_MANAGER_OPERATIONS) {
|
||||||
|
//Verify all old is free
|
||||||
|
for (int i = 0; i < oldCount; i++) {
|
||||||
|
if (this.nodeData.nodeExists(i + oldPtr)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Invalidate the node as data has changed
|
||||||
|
this.invalidateNode(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: reuse requestId and obj from before (its faster)
|
||||||
|
//Only finish the request after so that compaction of the child msk is correct
|
||||||
|
if (this.nodeData.isNodeRequestInFlight(nodeId)) {//Also only need to do this after/if there are removals to be done
|
||||||
|
int requestId = this.nodeData.getNodeRequest(nodeId);
|
||||||
|
var request = this.childRequests.get(requestId);// TODO: do not assume request is childRequest (it will probably always be)
|
||||||
|
if (request.getPosition() != pos) throw new IllegalStateException("Request is not at pos");
|
||||||
|
|
||||||
if (request.isSatisfied()) {
|
if (request.isSatisfied()) {
|
||||||
this.finishRequest(requestId, request);
|
this.finishRequest(requestId, request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rem != 0) {
|
|
||||||
//"TODO"
|
|
||||||
//There are child node entries that need removing
|
|
||||||
// TODO: this should be ok to do before request is satisfied
|
|
||||||
Logger.error("UNFINISHED OPERATION TODO: FIXME");
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
if ((rem & (1 << i)) == 0) continue;
|
|
||||||
long cPos = makeChildPos(pos, i);
|
|
||||||
|
|
||||||
this.recurseRemoveNode(cPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: check this is ok and correct
|
|
||||||
this.nodeData.setNodeChildExistence(nodeId&NODE_ID_MSK, childExistence);
|
if (childExistence == 0) {
|
||||||
|
//We need to change the node from inner to leaf as it does not have any children
|
||||||
|
if (this.nodeData.isNodeRequestInFlight(nodeId))//Leaf nodes cannot have requests associated to them
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
if (this.nodeData.getNodeGeometry(nodeId) == -1)
|
||||||
|
throw new IllegalStateException("leaf nodes must have geometry");
|
||||||
|
|
||||||
|
if (this.nodeData.getChildPtr(nodeId) != SENTINEL_EMPTY_CHILD_PTR) {//This should only ever be the sentinal ptr
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.nodeData.setChildPtr(nodeId, -1);
|
||||||
|
int old = this.activeSectionMap.put(pos, NODE_TYPE_LEAF|nodeId);
|
||||||
|
|
||||||
|
this.invalidateNode(nodeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,13 +582,22 @@ public class NodeManager {
|
|||||||
|
|
||||||
//Unwatch the request position
|
//Unwatch the request position
|
||||||
long childPos = makeChildPos(pos, i);
|
long childPos = makeChildPos(pos, i);
|
||||||
if (!this.updateRouter.unwatch(childPos, WorldEngine.UPDATE_FLAGS)) {
|
//Remove from section tracker
|
||||||
|
int cId = this.activeSectionMap.remove(childPos);
|
||||||
|
if (cId == -1) {
|
||||||
|
throw new IllegalStateException("Child not in activeMap");
|
||||||
|
}
|
||||||
|
if ((cId&NODE_TYPE_MSK) != NODE_TYPE_REQUEST || (cId&REQUEST_TYPE_MSK) != REQUEST_TYPE_CHILD || (cId&NODE_ID_MSK) != reqId) {
|
||||||
|
throw new IllegalStateException("Invalid child active state map: " + cId);
|
||||||
|
}
|
||||||
|
if (!this.watcher.unwatch(childPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Pos was not being watched");
|
throw new IllegalStateException("Pos was not being watched");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.childRequests.release(reqId);//Release the request
|
this.childRequests.release(reqId);//Release the request
|
||||||
|
this.activeNodeRequestCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -476,7 +605,38 @@ public class NodeManager {
|
|||||||
// childRequest
|
// childRequest
|
||||||
// this is only valid if this node is an inner node
|
// this is only valid if this node is an inner node
|
||||||
|
|
||||||
Logger.error("UNFINISHED OPERATION TODO: FIXME2");
|
//Logger.error("UNFINISHED OPERATION TODO: FIXME2");
|
||||||
|
|
||||||
|
//Only recursively delete if the node is not a leaf
|
||||||
|
if (type == NODE_TYPE_INNER) {
|
||||||
|
//Verify child data
|
||||||
|
if (VERIFY_NODE_MANAGER_OPERATIONS) {
|
||||||
|
byte msk = 0;
|
||||||
|
int childPtr = this.nodeData.getChildPtr(nodeId);
|
||||||
|
if (childPtr == -1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (childPtr != SENTINEL_EMPTY_CHILD_PTR) {
|
||||||
|
int childCnt = this.nodeData.getChildPtrCount(nodeId);
|
||||||
|
if (Integer.bitCount(Byte.toUnsignedInt(childExistence)) != childCnt) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < childCnt; i++) {
|
||||||
|
if (!this.nodeData.nodeExists(i + childPtr)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
long cp = this.nodeData.nodePosition(i + childPtr);
|
||||||
|
if (makeParentPos(cp) != pos) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
msk |= (byte) (1 << getChildIdx(cp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msk != childExistence) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
if ((childExistence & (1 << i)) == 0) continue;
|
if ((childExistence & (1 << i)) == 0) continue;
|
||||||
|
|
||||||
@@ -484,6 +644,26 @@ public class NodeManager {
|
|||||||
this.recurseRemoveNode(childPos);
|
this.recurseRemoveNode(childPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Verify that all node children are free
|
||||||
|
if (VERIFY_NODE_MANAGER_OPERATIONS) {
|
||||||
|
int childPtr = this.nodeData.getChildPtr(nodeId);
|
||||||
|
if (childPtr == -1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (childPtr != SENTINEL_EMPTY_CHILD_PTR) {
|
||||||
|
int childCnt = this.nodeData.getChildPtrCount(nodeId);
|
||||||
|
if (Integer.bitCount(Byte.toUnsignedInt(childExistence)) != childCnt) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < childCnt; i++) {
|
||||||
|
if (this.nodeData.nodeExists(i+childPtr)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Free geometry and related memory for this node
|
//Free geometry and related memory for this node
|
||||||
int geometry = this.nodeData.getNodeGeometry(nodeId);
|
int geometry = this.nodeData.getNodeGeometry(nodeId);
|
||||||
if (geometry != EMPTY_GEOMETRY_ID && geometry != NULL_GEOMETRY_ID)
|
if (geometry != EMPTY_GEOMETRY_ID && geometry != NULL_GEOMETRY_ID)
|
||||||
@@ -491,9 +671,10 @@ public class NodeManager {
|
|||||||
|
|
||||||
this.nodeData.free(nodeId);
|
this.nodeData.free(nodeId);
|
||||||
this.clearId(nodeId);
|
this.clearId(nodeId);
|
||||||
|
this.invalidateNode(nodeId);
|
||||||
|
|
||||||
//Unwatch position
|
//Unwatch position
|
||||||
if (!this.updateRouter.unwatch(pos, WorldEngine.UPDATE_FLAGS)) {
|
if (!this.watcher.unwatch(pos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Pos was not being watched");
|
throw new IllegalStateException("Pos was not being watched");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -529,8 +710,28 @@ public class NodeManager {
|
|||||||
int parentNodeType = parentNodeId&NODE_TYPE_MSK;
|
int parentNodeType = parentNodeId&NODE_TYPE_MSK;
|
||||||
parentNodeId &= NODE_ID_MSK;
|
parentNodeId &= NODE_ID_MSK;
|
||||||
|
|
||||||
|
if (request.getMsk() == 0) {
|
||||||
|
//Request was "canceled" so remove the request
|
||||||
|
|
||||||
|
//Free request
|
||||||
|
this.childRequests.release(requestId);
|
||||||
|
|
||||||
|
//Update the parent
|
||||||
|
this.nodeData.setNodeRequest(parentNodeId, NULL_REQUEST_ID);//TODO: create a better null request
|
||||||
|
this.nodeData.unmarkRequestInFlight(parentNodeId);
|
||||||
|
this.activeNodeRequestCount--;
|
||||||
|
|
||||||
|
//Invalidate parent
|
||||||
|
this.invalidateNode(parentNodeId);
|
||||||
|
|
||||||
|
//TODO: verify things here
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (parentNodeType==NODE_TYPE_LEAF) {
|
if (parentNodeType==NODE_TYPE_LEAF) {
|
||||||
int msk = Byte.toUnsignedInt(request.getMsk());
|
int msk = Byte.toUnsignedInt(request.getMsk());
|
||||||
|
if (msk == 0) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
int base = this.nodeData.allocate(Integer.bitCount(msk));
|
int base = this.nodeData.allocate(Integer.bitCount(msk));
|
||||||
int offset = -1;
|
int offset = -1;
|
||||||
for (int childIdx = 0; childIdx < 8; childIdx++) {
|
for (int childIdx = 0; childIdx < 8; childIdx++) {
|
||||||
@@ -545,7 +746,12 @@ public class NodeManager {
|
|||||||
this.nodeData.setNodePosition(childNodeId, childPos);
|
this.nodeData.setNodePosition(childNodeId, childPos);
|
||||||
byte childExistence = request.getChildChildExistence(childIdx);
|
byte childExistence = request.getChildChildExistence(childIdx);
|
||||||
if (childExistence == 0) {
|
if (childExistence == 0) {
|
||||||
throw new IllegalStateException("Request result with child existence of 0");
|
//This is an ok error if it happens the request with a child state should never be zero
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: make into warning or log error
|
||||||
|
//throw new IllegalStateException("Request result with child existence of 0");
|
||||||
|
|
||||||
}
|
}
|
||||||
this.nodeData.setNodeChildExistence(childNodeId, childExistence);
|
this.nodeData.setNodeChildExistence(childNodeId, childExistence);
|
||||||
this.nodeData.setNodeGeometry(childNodeId, request.getChildMesh(childIdx));
|
this.nodeData.setNodeGeometry(childNodeId, request.getChildMesh(childIdx));
|
||||||
@@ -563,8 +769,8 @@ public class NodeManager {
|
|||||||
this.childRequests.release(requestId);
|
this.childRequests.release(requestId);
|
||||||
//Update the parent
|
//Update the parent
|
||||||
this.nodeData.setChildPtr(parentNodeId, base);
|
this.nodeData.setChildPtr(parentNodeId, base);
|
||||||
this.nodeData.setChildPtrCount(parentNodeId, offset+1);
|
this.nodeData.setChildPtrCount(parentNodeId, Integer.bitCount(msk));
|
||||||
this.nodeData.setNodeRequest(parentNodeId, 0);//TODO: create a better null request
|
this.nodeData.setNodeRequest(parentNodeId, NULL_REQUEST_ID);
|
||||||
this.activeNodeRequestCount--;
|
this.activeNodeRequestCount--;
|
||||||
this.nodeData.unmarkRequestInFlight(parentNodeId);
|
this.nodeData.unmarkRequestInFlight(parentNodeId);
|
||||||
|
|
||||||
@@ -580,25 +786,28 @@ public class NodeManager {
|
|||||||
|
|
||||||
this.invalidateNode(parentNodeId);
|
this.invalidateNode(parentNodeId);
|
||||||
} else if (parentNodeType==NODE_TYPE_INNER) {
|
} else if (parentNodeType==NODE_TYPE_INNER) {
|
||||||
//Logger.error("TODO: FIXME FINISH: finishRequest NODE_TYPE_INNER");
|
|
||||||
//For this, only need to add the nodes to the existing child set thing (shuffle around whatever) dont ever have to remove nodes
|
//For this, only need to add the nodes to the existing child set thing (shuffle around whatever) dont ever have to remove nodes
|
||||||
|
|
||||||
int childPtr = this.nodeData.getChildPtr(parentNodeId);
|
int oldChildPtr = this.nodeData.getChildPtr(parentNodeId);
|
||||||
int childCnt = this.nodeData.getChildPtrCount(parentNodeId);
|
int oldChildCnt = this.nodeData.getChildPtrCount(parentNodeId);
|
||||||
if (childPtr == -1) {
|
if (oldChildPtr == -1) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int existingChildMsk = 0;
|
||||||
|
|
||||||
|
//If the pointer is the empty ptr, dont check the count
|
||||||
|
if (oldChildPtr != SENTINEL_EMPTY_CHILD_PTR) {
|
||||||
//Ok so technically, it _is ok_ to just add to the end of the childPtr, however, imo that is stupid
|
//Ok so technically, it _is ok_ to just add to the end of the childPtr, however, imo that is stupid
|
||||||
// and it should follow the logical allocation with respect to the 8 child indices
|
// and it should follow the logical allocation with respect to the 8 child indices
|
||||||
// this means, need to extract the child indices already in the ptr (or technically could use the child existance? but having both and doing verification would be good)
|
// this means, need to extract the child indices already in the ptr (or technically could use the child existance? but having both and doing verification would be good)
|
||||||
|
|
||||||
int existingChildMsk = 0;
|
for (int i = 0; i < oldChildCnt; i++) {
|
||||||
for (int i = 0; i < childCnt; i++) {
|
if (!this.nodeData.nodeExists(i + oldChildPtr)) {
|
||||||
if (!this.nodeData.nodeExists(i+childPtr)) {
|
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
existingChildMsk |= 1<<getChildIdx(this.nodeData.nodePosition(i+childPtr));
|
existingChildMsk |= 1 << getChildIdx(this.nodeData.nodePosition(i + oldChildPtr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int reqMsk = Byte.toUnsignedInt(request.getMsk());
|
int reqMsk = Byte.toUnsignedInt(request.getMsk());
|
||||||
if ((byte) (existingChildMsk|reqMsk) != this.nodeData.getNodeChildExistence(parentNodeId)) {
|
if ((byte) (existingChildMsk|reqMsk) != this.nodeData.getNodeChildExistence(parentNodeId)) {
|
||||||
@@ -618,7 +827,7 @@ public class NodeManager {
|
|||||||
//Need to interlace the old and new data into the new allocation
|
//Need to interlace the old and new data into the new allocation
|
||||||
// FOR OLD ALLOCATIONS, NEED TO UPDATE POINTERS
|
// FOR OLD ALLOCATIONS, NEED TO UPDATE POINTERS
|
||||||
int childId = newChildPtr-1;
|
int childId = newChildPtr-1;
|
||||||
int prevChildId = childPtr-1;
|
int prevChildId = oldChildPtr-1;
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
if ((newMsk&(1<<i))==0) continue;
|
if ((newMsk&(1<<i))==0) continue;
|
||||||
childId++;
|
childId++;
|
||||||
@@ -630,7 +839,11 @@ public class NodeManager {
|
|||||||
this.nodeData.setNodePosition(childId, childPos);
|
this.nodeData.setNodePosition(childId, childPos);
|
||||||
byte childExistence = request.getChildChildExistence(i);
|
byte childExistence = request.getChildChildExistence(i);
|
||||||
if (childExistence == 0) {
|
if (childExistence == 0) {
|
||||||
throw new IllegalStateException("Request result with child existence of 0");
|
|
||||||
|
//TODO: make into warning or log error
|
||||||
|
//throw new IllegalStateException("Request result with child existence of 0");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
this.nodeData.setNodeChildExistence(childId, childExistence);
|
this.nodeData.setNodeChildExistence(childId, childExistence);
|
||||||
this.nodeData.setNodeGeometry(childId, request.getChildMesh(i));
|
this.nodeData.setNodeGeometry(childId, request.getChildMesh(i));
|
||||||
@@ -668,7 +881,9 @@ public class NodeManager {
|
|||||||
//Do final steps
|
//Do final steps
|
||||||
|
|
||||||
//Free the old child data
|
//Free the old child data
|
||||||
this.nodeData.free(childPtr, childCnt);
|
if (oldChildPtr != SENTINEL_EMPTY_CHILD_PTR) {
|
||||||
|
this.nodeData.free(oldChildPtr, oldChildCnt);
|
||||||
|
}
|
||||||
|
|
||||||
//Free request
|
//Free request
|
||||||
this.childRequests.release(requestId);
|
this.childRequests.release(requestId);
|
||||||
@@ -676,7 +891,7 @@ public class NodeManager {
|
|||||||
//Update the parent
|
//Update the parent
|
||||||
this.nodeData.setChildPtr(parentNodeId, newChildPtr);
|
this.nodeData.setChildPtr(parentNodeId, newChildPtr);
|
||||||
this.nodeData.setChildPtrCount(parentNodeId, Integer.bitCount(newMsk));
|
this.nodeData.setChildPtrCount(parentNodeId, Integer.bitCount(newMsk));
|
||||||
this.nodeData.setNodeRequest(parentNodeId, 0);//TODO: create a better null request
|
this.nodeData.setNodeRequest(parentNodeId, NULL_REQUEST_ID);
|
||||||
this.activeNodeRequestCount--;
|
this.activeNodeRequestCount--;
|
||||||
this.nodeData.unmarkRequestInFlight(parentNodeId);
|
this.nodeData.unmarkRequestInFlight(parentNodeId);
|
||||||
|
|
||||||
@@ -734,7 +949,7 @@ public class NodeManager {
|
|||||||
if (this.nodeData.getNodeGeometry(nodeId) == NULL_GEOMETRY_ID) {
|
if (this.nodeData.getNodeGeometry(nodeId) == NULL_GEOMETRY_ID) {
|
||||||
//Weird case that not sure how possible
|
//Weird case that not sure how possible
|
||||||
Logger.warn("Got request for leaf that doesnt have geometry, this should not be possible at pos " + WorldEngine.pprintPos(pos));
|
Logger.warn("Got request for leaf that doesnt have geometry, this should not be possible at pos " + WorldEngine.pprintPos(pos));
|
||||||
if (!this.updateRouter.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
if (!this.watcher.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
||||||
Logger.warn("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
|
Logger.warn("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -759,7 +974,7 @@ public class NodeManager {
|
|||||||
//Logger.error("TODO FINISH THIS");
|
//Logger.error("TODO FINISH THIS");
|
||||||
// THis shouldent result in markRequestInFlight afak
|
// THis shouldent result in markRequestInFlight afak
|
||||||
|
|
||||||
if (!this.updateRouter.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
if (!this.watcher.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
||||||
//FIXME: think this can occur accidently? when removing nodes or something creating leaf nodes
|
//FIXME: think this can occur accidently? when removing nodes or something creating leaf nodes
|
||||||
// or other, the node might be wanted to be watched by gpu, but cpu already started watching it a few frames ago
|
// or other, the node might be wanted to be watched by gpu, but cpu already started watching it a few frames ago
|
||||||
Logger.info("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
|
Logger.info("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
|
||||||
@@ -771,6 +986,15 @@ public class NodeManager {
|
|||||||
long pos = this.nodeData.nodePosition(nodeId);
|
long pos = this.nodeData.nodePosition(nodeId);
|
||||||
byte childExistence = this.nodeData.getNodeChildExistence(nodeId);
|
byte childExistence = this.nodeData.getNodeChildExistence(nodeId);
|
||||||
|
|
||||||
|
if (childExistence == 0) {
|
||||||
|
if (!this.topLevelNodes.contains(pos)) {//Top level nodes are special, as they can have a request with child existence of 0 for performance reasons
|
||||||
|
Logger.warn("Not creating a leaf request with existence mask of 0");
|
||||||
|
this.nodeData.unmarkRequestInFlight(nodeId);
|
||||||
|
this.invalidateNode(nodeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Enqueue a leaf expansion request
|
//Enqueue a leaf expansion request
|
||||||
var request = new NodeChildRequest(pos);
|
var request = new NodeChildRequest(pos);
|
||||||
int requestId = this.childRequests.put(request);
|
int requestId = this.childRequests.put(request);
|
||||||
@@ -796,7 +1020,7 @@ public class NodeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Watch and request the child node at the given position
|
//Watch and request the child node at the given position
|
||||||
if (!this.updateRouter.watch(childPos, WorldEngine.UPDATE_FLAGS)) {
|
if (!this.watcher.watch(childPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Failed to watch childPos");
|
throw new IllegalStateException("Failed to watch childPos");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -858,6 +1082,12 @@ public class NodeManager {
|
|||||||
|
|
||||||
//THIS IS MOST IMPORTANT
|
//THIS IS MOST IMPORTANT
|
||||||
//Logger.error("TODO: THIS 2");
|
//Logger.error("TODO: THIS 2");
|
||||||
|
|
||||||
|
//TODO: cancel any requests with the parent node
|
||||||
|
// update the parent nodes child existance with respect to the request if it had
|
||||||
|
// recurseRemoveNode(); on all the children (which will include us)
|
||||||
|
// update the type of the parent node
|
||||||
|
Logger.error("TODO: FINISH THIS");
|
||||||
}
|
}
|
||||||
//this.removeGeometryInternal(pos, nodeId);
|
//this.removeGeometryInternal(pos, nodeId);
|
||||||
return;
|
return;
|
||||||
@@ -869,7 +1099,7 @@ public class NodeManager {
|
|||||||
int geometryId = this.nodeData.getNodeGeometry(nodeId);
|
int geometryId = this.nodeData.getNodeGeometry(nodeId);
|
||||||
if (geometryId != NULL_GEOMETRY_ID && geometryId != EMPTY_GEOMETRY_ID) {
|
if (geometryId != NULL_GEOMETRY_ID && geometryId != EMPTY_GEOMETRY_ID) {
|
||||||
//Unwatch geometry updates
|
//Unwatch geometry updates
|
||||||
if (this.updateRouter.unwatch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
if (this.watcher.unwatch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
||||||
throw new IllegalStateException("Unwatching position for geometry removal at: " + WorldEngine.pprintPos(pos) + " resulted in full removal");
|
throw new IllegalStateException("Unwatching position for geometry removal at: " + WorldEngine.pprintPos(pos) + " resulted in full removal");
|
||||||
}
|
}
|
||||||
//Remove geometry and set to null
|
//Remove geometry and set to null
|
||||||
@@ -895,6 +1125,22 @@ public class NodeManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MemoryBuffer _generateChangeList() {
|
||||||
|
//For internal testing use only
|
||||||
|
if (this.nodeUpdates.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var buff = new MemoryBuffer(this.nodeUpdates.size()*20L);
|
||||||
|
int c = 0;
|
||||||
|
for (int i : this.nodeUpdates) {
|
||||||
|
long addr = buff.address + 20L * c++;
|
||||||
|
MemoryUtil.memPutInt(addr, i);
|
||||||
|
this.nodeData.writeNode(addr+4, i);
|
||||||
|
}
|
||||||
|
this.nodeUpdates.clear();
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
private void invalidateNode(int nodeId) {
|
private void invalidateNode(int nodeId) {
|
||||||
this.nodeUpdates.add(nodeId);
|
this.nodeUpdates.add(nodeId);
|
||||||
}
|
}
|
||||||
@@ -940,4 +1186,198 @@ public class NodeManager {
|
|||||||
public AbstractSectionGeometryManager getGeometryManager() {
|
public AbstractSectionGeometryManager getGeometryManager() {
|
||||||
return this.geometryManager;
|
return this.geometryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==================================================================================================================
|
||||||
|
|
||||||
|
//TODO: need to figure out what happens if an inner node gets marked with child existence of 0
|
||||||
|
// it should become a leaf node
|
||||||
|
// however, if the node doesnt have geometry attached that would put it in an invalid state so need to figure out
|
||||||
|
// a solution for this
|
||||||
|
|
||||||
|
private int verifyRequest(long pos, int node, int cActiveExistence, LongOpenHashSet seenPositions, IntOpenHashSet seenNodes) {
|
||||||
|
if (this.nodeData.isNodeRequestInFlight(node)) {
|
||||||
|
int requestId = this.nodeData.getNodeRequest(node);
|
||||||
|
var request = this.childRequests.get(requestId);//TODO: dont assume is a child request
|
||||||
|
if (request.getPosition() != pos)
|
||||||
|
throw new IllegalStateException();//Request position must be this position
|
||||||
|
int reqMsk = Byte.toUnsignedInt(request.getMsk());
|
||||||
|
if ((cActiveExistence&reqMsk)!=0)//Cannot have an active child and request for the same position
|
||||||
|
throw new IllegalStateException();
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if ((reqMsk&(1<<i))==0) continue;
|
||||||
|
long cPos = makeChildPos(pos, i);
|
||||||
|
int cNode = this.activeSectionMap.get(cPos);
|
||||||
|
if (cNode == -1)//Request pos must be in map
|
||||||
|
throw new IllegalStateException();
|
||||||
|
if ((cNode&NODE_TYPE_MSK)!=NODE_TYPE_REQUEST)//It must be a request type
|
||||||
|
throw new IllegalStateException();
|
||||||
|
if ((cNode&REQUEST_TYPE_MSK)!=REQUEST_TYPE_CHILD)//Must be a child request
|
||||||
|
throw new IllegalStateException();
|
||||||
|
if ((cNode&NODE_ID_MSK) != requestId)//Must be for this request
|
||||||
|
throw new IllegalStateException();
|
||||||
|
this.verifyNode(cPos, seenPositions, seenNodes);
|
||||||
|
}
|
||||||
|
return reqMsk;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyNode(long pos, LongOpenHashSet seenPositions, IntOpenHashSet seenNodes) {
|
||||||
|
int node = this.activeSectionMap.get(pos);
|
||||||
|
if (node == -1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (!seenPositions.add(pos))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
int type = node&NODE_TYPE_MSK;
|
||||||
|
if (type == NODE_TYPE_REQUEST) {
|
||||||
|
if ((node&REQUEST_TYPE_MSK)==REQUEST_TYPE_SINGLE) {
|
||||||
|
if (!this.topLevelNodes.contains(pos)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
//TODO
|
||||||
|
} else {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node &= NODE_ID_MSK;
|
||||||
|
if (!this.nodeData.nodeExists(node)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (this.nodeData.nodePosition(node) != pos) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if ((this.nodeData.getNodeRequest(node) != NULL_REQUEST_ID) != this.nodeData.isNodeRequestInFlight(node)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (this.nodeData.isNodeRequestInFlight(node)) {
|
||||||
|
var req = this.childRequests.get(this.nodeData.getNodeRequest(node));
|
||||||
|
if (req == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (req.getPosition() != pos) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (req.isSatisfied()) {//If a request is satisfied it should not be in the array
|
||||||
|
//The exception to this rule is the top level nodes, and only if they are leaf nodes
|
||||||
|
if (!(type == NODE_TYPE_LEAF && this.topLevelNodes.contains(pos)))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (this.nodeData.getNodeType(node) != type) {
|
||||||
|
// throw new IllegalStateException();
|
||||||
|
//}
|
||||||
|
if (!seenNodes.add(node))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
if (type == NODE_TYPE_INNER) {
|
||||||
|
int childPtr = this.nodeData.getChildPtr(node);
|
||||||
|
int childCount = this.nodeData.getChildPtrCount(node);
|
||||||
|
int cActiveExistence = 0;
|
||||||
|
|
||||||
|
if (childPtr == -1) {//Inner nodes cannot have null child ptrs
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
//TODO: check SENTINEL_EMPTY_CHILD_PTR
|
||||||
|
if (childPtr != SENTINEL_EMPTY_CHILD_PTR) {
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
if (!this.nodeData.nodeExists(i + childPtr))//All children must exist
|
||||||
|
throw new IllegalStateException();
|
||||||
|
long cPos = this.nodeData.nodePosition(i + childPtr);
|
||||||
|
if (makeParentPos(cPos) != pos)//Parent of child must be this position
|
||||||
|
throw new IllegalStateException();
|
||||||
|
cActiveExistence |= 1 << getChildIdx(cPos);
|
||||||
|
//Recurse into child
|
||||||
|
this.verifyNode(cPos, seenPositions, seenNodes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//TODO: verify SENTINEL_EMPTY_CHILD_PTR is valid
|
||||||
|
childCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int childExistence = cActiveExistence;
|
||||||
|
childExistence |= this.verifyRequest(pos, node, cActiveExistence, seenPositions, seenNodes);
|
||||||
|
|
||||||
|
if (childExistence != Byte.toUnsignedInt(this.nodeData.getNodeChildExistence(node))) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childExistence == 0) {//Inner nodes should always have children
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
} else if (type == NODE_TYPE_LEAF) {
|
||||||
|
if (this.nodeData.getChildPtr(node) != -1) {//Leafs cannot have child ptrs
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (this.nodeData.getNodeGeometry(node) == NULL_GEOMETRY_ID) {//Leafs cannot have null geometry
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WorldEngine.getLevel(pos) == 0) {
|
||||||
|
//TODO: this is a specialcase
|
||||||
|
if (this.nodeData.isNodeRequestInFlight(node)) {//Child nodes cannot have inflight requests
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Child existence only matters if there is a request in flight
|
||||||
|
if (this.nodeData.isNodeRequestInFlight(node)) {
|
||||||
|
int childExistence = this.verifyRequest(pos, node, 0, seenPositions, seenNodes);
|
||||||
|
if (childExistence != Byte.toUnsignedInt(this.nodeData.getNodeChildExistence(node))) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verifyIntegrity() {
|
||||||
|
this.verifyIntegrity(null);
|
||||||
|
}
|
||||||
|
public void verifyIntegrity(LongSet watchingPosSet) {
|
||||||
|
//Should verify integrity of node manager, everything
|
||||||
|
// should traverse from top (root positions) down
|
||||||
|
// after it should check if there is anything it hasnt tracked, if so thats badd
|
||||||
|
//It should check childPtr and childPtrCount match and align with the nodeMsk
|
||||||
|
// verify requests and positions
|
||||||
|
//it should verify everything is correct and as it should be
|
||||||
|
// it should verify geometry exists as well for nodes that should
|
||||||
|
|
||||||
|
LongOpenHashSet seenPositions = new LongOpenHashSet();
|
||||||
|
IntOpenHashSet seenNodes = new IntOpenHashSet();
|
||||||
|
|
||||||
|
for (long pos : this.topLevelNodes) {
|
||||||
|
this.verifyNode(pos, seenPositions, seenNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
var thisMap = this.activeSectionMap.keySet();
|
||||||
|
if (!(seenPositions.containsAll(thisMap)&&thisMap.containsAll(seenPositions))) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seenNodes.size() != this.nodeData.getNodeCount()) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
for (int i : seenNodes) {
|
||||||
|
if (!this.nodeData.nodeExists(i)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.activeNodeRequestCount != this.childRequests.count()) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (watchingPosSet != null) {
|
||||||
|
if (!watchingPosSet.containsAll(thisMap)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (!thisMap.containsAll(watchingPosSet)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ public final class NodeStore {
|
|||||||
public static final int MAX_GEOMETRY_ID = (1<<24)-3;
|
public static final int MAX_GEOMETRY_ID = (1<<24)-3;
|
||||||
private static final int SENTINEL_NULL_GEOMETRY_ID = (1<<24)-1;
|
private static final int SENTINEL_NULL_GEOMETRY_ID = (1<<24)-1;
|
||||||
private static final int SENTINEL_EMPTY_GEOMETRY_ID = (1<<24)-2;
|
private static final int SENTINEL_EMPTY_GEOMETRY_ID = (1<<24)-2;
|
||||||
private static final int SENTINEL_NULL_NODE_ID = NODE_ID_MSK -1;
|
private static final int SENTINEL_NULL_NODE_ID = NODE_ID_MSK;
|
||||||
private static final int SENTINEL_REQUEST_ID = REQUEST_ID_MSK -1;
|
|
||||||
private static final int LONGS_PER_NODE = 4;
|
private static final int LONGS_PER_NODE = 4;
|
||||||
private static final int INCREMENT_SIZE = 1<<16;
|
private static final int INCREMENT_SIZE = 1<<16;
|
||||||
private final HierarchicalBitSet allocationSet;
|
private final HierarchicalBitSet allocationSet;
|
||||||
@@ -42,7 +41,7 @@ public final class NodeStore {
|
|||||||
|
|
||||||
public int allocate(int count) {
|
public int allocate(int count) {
|
||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
throw new IllegalArgumentException("Count cannot be <= 0");
|
throw new IllegalArgumentException("Count cannot be <= 0 was " + count);
|
||||||
}
|
}
|
||||||
int id = this.allocationSet.allocateNextConsecutiveCounted(count);
|
int id = this.allocationSet.allocateNextConsecutiveCounted(count);
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
@@ -86,7 +85,7 @@ public final class NodeStore {
|
|||||||
int idx = id2idx(nodeId);
|
int idx = id2idx(nodeId);
|
||||||
this.localNodeData[idx] = -1;//Position
|
this.localNodeData[idx] = -1;//Position
|
||||||
this.localNodeData[idx+1] = GEOMETRY_ID_MSK|(((long)NODE_ID_MSK)<<24);
|
this.localNodeData[idx+1] = GEOMETRY_ID_MSK|(((long)NODE_ID_MSK)<<24);
|
||||||
this.localNodeData[idx+2] = 0;
|
this.localNodeData[idx+2] = REQUEST_ID_MSK;
|
||||||
this.localNodeData[idx+3] = 0;
|
this.localNodeData[idx+3] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,4 +276,7 @@ public final class NodeStore {
|
|||||||
public int getEndNodeId() {
|
public int getEndNodeId() {
|
||||||
return this.allocationSet.getMaxIndex();
|
return this.allocationSet.getMaxIndex();
|
||||||
}
|
}
|
||||||
|
public int getNodeCount() {
|
||||||
|
return this.allocationSet.getCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,537 @@
|
|||||||
|
package me.cortex.voxy.client.core.rendering.hierachical;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.Stack;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2IntFunction;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||||
|
import me.cortex.voxy.client.core.rendering.ISectionWatcher;
|
||||||
|
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||||
|
import me.cortex.voxy.client.core.rendering.SectionUpdateRouter;
|
||||||
|
import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager;
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
|
import me.cortex.voxy.common.util.HierarchicalBitSet;
|
||||||
|
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||||
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
|
import me.cortex.voxy.common.world.WorldSection;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.IntSupplier;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import static me.cortex.voxy.common.world.WorldEngine.*;
|
||||||
|
|
||||||
|
public class TestNodeManager {
|
||||||
|
private static final class MemoryGeometryManager extends AbstractSectionGeometryManager {
|
||||||
|
private record Entry(long pos, long size) {}
|
||||||
|
private long memoryInUse = 0;
|
||||||
|
private final HierarchicalBitSet allocation;
|
||||||
|
private final Int2ObjectOpenHashMap<Entry> sections = new Int2ObjectOpenHashMap<>();
|
||||||
|
public MemoryGeometryManager(int maxSections, long geometryCapacity) {
|
||||||
|
super(maxSections, geometryCapacity);
|
||||||
|
this.allocation = new HierarchicalBitSet(maxSections);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int uploadReplaceSection(int oldId, BuiltSection section) {
|
||||||
|
if (section.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (oldId != -1) {
|
||||||
|
this.removeSection(oldId);
|
||||||
|
}
|
||||||
|
int newId = this.allocation.allocateNext();
|
||||||
|
var entry = new Entry(section.position, section.geometryBuffer.size);
|
||||||
|
if (this.sections.put(newId, entry) != null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
this.memoryInUse += entry.size;
|
||||||
|
section.free();
|
||||||
|
|
||||||
|
Logger.info("Creating geometry with id", newId, "and size", entry.size, "at pos", WorldEngine.pprintPos(entry.pos));
|
||||||
|
|
||||||
|
return newId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeSection(int id) {
|
||||||
|
if (!this.allocation.free(id)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
var old = this.sections.remove(id);
|
||||||
|
if (old == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
this.memoryInUse -= old.size;
|
||||||
|
Logger.info("Removing geometry with id", id, "it was at pos", WorldEngine.pprintPos(old.pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadAndRemove(int id, Consumer<BuiltSection> callback) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUsedCapacity() {
|
||||||
|
return this.memoryInUse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Watcher implements ISectionWatcher {
|
||||||
|
private final Long2ByteOpenHashMap updateTypes = new Long2ByteOpenHashMap();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean watch(long position, int types) {
|
||||||
|
byte current = 0;
|
||||||
|
boolean had = false;
|
||||||
|
if (this.updateTypes.containsKey(position)) {
|
||||||
|
current = this.updateTypes.get(position);
|
||||||
|
had = true;
|
||||||
|
}
|
||||||
|
if (had && current == 0) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
this.updateTypes.put(position, (byte) (current | types));
|
||||||
|
byte delta = (byte) (types&(~current));
|
||||||
|
Logger.info("Watching pos", WorldEngine.pprintPos(position), "with types", getPrettyTypes(types), "was", getPrettyTypes(current));
|
||||||
|
return delta!=0;//returns true if new types where set
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean unwatch(long position, int types) {
|
||||||
|
if (!this.updateTypes.containsKey(position)) {
|
||||||
|
throw new IllegalStateException("Pos not in map: " + WorldEngine.pprintPos(position));
|
||||||
|
}
|
||||||
|
byte current = this.updateTypes.get(position);
|
||||||
|
byte newTypes = (byte) (current&(~types));
|
||||||
|
if (newTypes == 0) {
|
||||||
|
this.updateTypes.remove(position);
|
||||||
|
} else {
|
||||||
|
this.updateTypes.put(position, newTypes);
|
||||||
|
}
|
||||||
|
Logger.info("UnWatching pos", WorldEngine.pprintPos(position), "removing types", getPrettyTypes(types), "was watching", getPrettyTypes(current), "new types", getPrettyTypes(newTypes));
|
||||||
|
return newTypes == 0;//Returns true on removal
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] getPrettyTypes(int msk) {
|
||||||
|
if ((msk&~UPDATE_FLAGS)!=0) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
String[] types = new String[Integer.bitCount(msk)];
|
||||||
|
int i = 0;
|
||||||
|
if ((msk&UPDATE_TYPE_BLOCK_BIT)!=0) {
|
||||||
|
types[i++] = "BLOCK";
|
||||||
|
}
|
||||||
|
if ((msk&UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) {
|
||||||
|
types[i++] = "CHILD";
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestBase {
|
||||||
|
public final MemoryGeometryManager geometryManager;
|
||||||
|
public final NodeManager nodeManager;
|
||||||
|
public final Watcher watcher;
|
||||||
|
|
||||||
|
public TestBase() {
|
||||||
|
this.watcher = new Watcher();
|
||||||
|
this.geometryManager = new MemoryGeometryManager(1<<20, 1<<30);
|
||||||
|
this.nodeManager = new NodeManager(1 << 21, this.geometryManager, this.watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putTopPos(long pos) {
|
||||||
|
this.nodeManager.insertTopLevelNode(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void meshUpdate(long pos, int childExistence, int geometrySize) {
|
||||||
|
if (childExistence == -1) {
|
||||||
|
childExistence = 0xFF;
|
||||||
|
}
|
||||||
|
if (childExistence>255) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
MemoryBuffer buff = null;
|
||||||
|
if (geometrySize != 0) {
|
||||||
|
buff = new MemoryBuffer(geometrySize);
|
||||||
|
}
|
||||||
|
var builtGeometry = new BuiltSection(pos, (byte) childExistence, -2, buff, null);
|
||||||
|
this.nodeManager.processGeometryResult(builtGeometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void request(long pos) {
|
||||||
|
this.nodeManager.processRequest(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void childUpdate(long pos, int existence) {
|
||||||
|
if (existence == -1) {
|
||||||
|
existence = 0xFF;
|
||||||
|
}
|
||||||
|
if (existence>255) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.nodeManager.processChildChange(pos, (byte) existence);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean printNodeChanges() {
|
||||||
|
var changes = this.nodeManager._generateChangeList();
|
||||||
|
if (changes == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int c = 0; c < changes.size/20; c++) {
|
||||||
|
long ptr = changes.address+20L*c;
|
||||||
|
int nodeId = MemoryUtil.memGetInt(ptr); ptr+=4;
|
||||||
|
long pos = Integer.toUnsignedLong(MemoryUtil.memGetInt(ptr))<<32; ptr += 4;
|
||||||
|
pos |= Integer.toUnsignedLong(MemoryUtil.memGetInt(ptr)); ptr += 4;
|
||||||
|
int z = MemoryUtil.memGetInt(ptr); ptr += 4;
|
||||||
|
int w = MemoryUtil.memGetInt(ptr); ptr += 4;
|
||||||
|
|
||||||
|
int childPtr = w&0xFFFFFF;
|
||||||
|
int geometry = z&0xFFFFFF;
|
||||||
|
short flags = 0;
|
||||||
|
|
||||||
|
flags |= (short) ((z>>>24)&0xFF);
|
||||||
|
flags |= (short) (((w>>>24)&0xFF)<<8);
|
||||||
|
|
||||||
|
Logger.info("Node update, id:",nodeId,"pos:",WorldEngine.pprintPos(pos),"childPtr:",childPtr,"geometry:",geometry,"flags:",flags);
|
||||||
|
}
|
||||||
|
changes.free();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeNodeGeometry(long pos) {
|
||||||
|
this.nodeManager.removeNodeGeometry(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verifyIntegrity() {
|
||||||
|
this.nodeManager.verifyIntegrity(this.watcher.updateTypes.keySet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillInALl(TestBase test, long pos, Long2IntFunction converter) {
|
||||||
|
test.request(pos);
|
||||||
|
|
||||||
|
|
||||||
|
int ce = converter.get(pos);
|
||||||
|
//Satisfy request for all the children
|
||||||
|
for (int i = 0; i<8;i++) {
|
||||||
|
if ((ce&(1<<i))==0) continue;
|
||||||
|
long p = makeChildPos(pos, i);
|
||||||
|
test.meshUpdate(p, converter.get(p), 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WorldEngine.getLevel(pos) == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i<8;i++) {
|
||||||
|
if ((ce&(1<<i))==0) continue;
|
||||||
|
fillInALl(test, makeChildPos(pos, i), converter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Logger.INSERT_CLASS = false;
|
||||||
|
int ITER_COUNT = 5_000;
|
||||||
|
int INNER_ITER_COUNT = 100_000;
|
||||||
|
|
||||||
|
AtomicInteger finished = new AtomicInteger();
|
||||||
|
HashSet<List<StackTraceElement>> seenTraces = new HashSet<>();
|
||||||
|
|
||||||
|
Logger.SHUTUP = true;
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
for (int q = 0; q < ITER_COUNT; q++) {
|
||||||
|
//Logger.info("Iteration "+ q);
|
||||||
|
if (runTest(INNER_ITER_COUNT, q, seenTraces)) {
|
||||||
|
finished.incrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IntStream.range(0, ITER_COUNT).parallel().forEach(i->{
|
||||||
|
if (runTest(INNER_ITER_COUNT, i, seenTraces)) {
|
||||||
|
finished.incrementAndGet();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
System.out.println("Finished " + finished.get() + " iterations out of " + ITER_COUNT);
|
||||||
|
}
|
||||||
|
private static long rPos(Random r) {
|
||||||
|
int lvl = r.nextInt(5);
|
||||||
|
if (lvl==4) {
|
||||||
|
return WorldEngine.getWorldSectionId(4,0,0,0);
|
||||||
|
}
|
||||||
|
int bound = 16>>lvl;
|
||||||
|
return WorldEngine.getWorldSectionId(lvl, r.nextInt(bound), r.nextInt(bound), r.nextInt(bound));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean runTest(int ITERS, int testIdx, Set<List<StackTraceElement>> traces) {
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
|
||||||
|
Random r = new Random(testIdx * 1234L);
|
||||||
|
try {
|
||||||
|
var test = new TestBase();
|
||||||
|
//Fuzzy bruteforce everything
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
for (int i = 0; i < ITERS; i++) {
|
||||||
|
long pos = rPos(r);
|
||||||
|
int op = r.nextInt(3);
|
||||||
|
int extra = r.nextInt(256);
|
||||||
|
boolean hasGeometry = r.nextBoolean();
|
||||||
|
if (op == 0) {
|
||||||
|
test.request(pos);
|
||||||
|
}
|
||||||
|
if (op == 1) {
|
||||||
|
test.childUpdate(pos, extra);
|
||||||
|
}
|
||||||
|
if (op == 2) {
|
||||||
|
test.meshUpdate(pos, extra, hasGeometry ? 100 : 0);
|
||||||
|
}
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.verifyIntegrity();
|
||||||
|
}
|
||||||
|
test.childUpdate(POS_A, 0);
|
||||||
|
test.meshUpdate(POS_A, 0, 0);
|
||||||
|
if (test.geometryManager.memoryInUse != 0) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
var trace = new ArrayList<>(List.of(e.getStackTrace()));
|
||||||
|
while (!trace.getLast().getMethodName().equals("runTest")) trace.removeLast();//Very hacky budget filter
|
||||||
|
synchronized (traces) {
|
||||||
|
if (traces.add(trace)) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void main3(String[] args) {
|
||||||
|
Logger.INSERT_CLASS = false;
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
test.meshUpdate(POS_A, 3, 0);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.request(POS_A);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 0), -1, 100);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 1), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("TEST: Created full node");
|
||||||
|
test.childUpdate(POS_A, 0);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("BB");
|
||||||
|
test.meshUpdate(POS_A, 0, 0);
|
||||||
|
test.printNodeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
test.meshUpdate(POS_A, 1, 0);
|
||||||
|
test.request(POS_A);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 0), -1, 100);
|
||||||
|
test.childUpdate(POS_A, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (false) {//Crash E
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
test.meshUpdate(POS_A, 3, 0);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.request(POS_A);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 0), -1, 100);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 1), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("TEST: Created full node");
|
||||||
|
test.childUpdate(POS_A, 0b110);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.childUpdate(POS_A, 0b10);
|
||||||
|
test.printNodeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {//Crash D
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
test.meshUpdate(POS_A, 3, 0);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.request(POS_A);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 0), -1, 100);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 1), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("TEST: Created full node");
|
||||||
|
test.childUpdate(POS_A, 0b110);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 2), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {//Crash c
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
test.meshUpdate(POS_A, 3, 0);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.request(POS_A);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 0), -1, 100);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 1), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("TEST: Created full node");
|
||||||
|
test.childUpdate(POS_A, 0b1111);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 2), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("TEST: Executing funny");
|
||||||
|
test.childUpdate(POS_A, 0b0110);
|
||||||
|
test.printNodeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {//Crash b
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
test.meshUpdate(POS_A, 3, 0);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.request(POS_A);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 0), -1, 100);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 1), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("TEST: Created full node");
|
||||||
|
test.childUpdate(POS_A, 0b1111);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 2), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.childUpdate(POS_A, 0b1110);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.childUpdate(POS_A, 0b0110);
|
||||||
|
test.printNodeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {// Test case A, known crash
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
test.meshUpdate(POS_A, 7, 0);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.request(POS_A);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 0), -1, 100);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 1), -1, 100);
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 2), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.error("CHANGING CHILD A");
|
||||||
|
test.childUpdate(POS_A, 1);
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.error("CHANGING CHILD B");
|
||||||
|
test.childUpdate(POS_A, 3);
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.meshUpdate(makeChildPos(POS_A, 1), -1, 100);
|
||||||
|
test.printNodeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main2(String[] args) {
|
||||||
|
Logger.INSERT_CLASS = false;
|
||||||
|
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
|
||||||
|
test.meshUpdate(POS_A, -1, 0);
|
||||||
|
fillInALl(test, POS_A, a->-1);
|
||||||
|
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("\n\n");
|
||||||
|
|
||||||
|
test.removeNodeGeometry(WorldEngine.getWorldSectionId(0,0,0,0));
|
||||||
|
test.printNodeChanges();
|
||||||
|
test.removeNodeGeometry(WorldEngine.getWorldSectionId(3,0,0,0));
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("changing child existance");
|
||||||
|
test.childUpdate(WorldEngine.getWorldSectionId(4,0,0,0), 1);
|
||||||
|
test.childUpdate(WorldEngine.getWorldSectionId(3,0,0,0), 1);
|
||||||
|
test.childUpdate(WorldEngine.getWorldSectionId(2,0,0,0), 1);
|
||||||
|
test.childUpdate(WorldEngine.getWorldSectionId(1,0,0,0), 1);
|
||||||
|
test.printNodeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main1(String[] args) {
|
||||||
|
Logger.INSERT_CLASS = false;
|
||||||
|
|
||||||
|
Random r = new Random(1234);
|
||||||
|
Long2IntOpenHashMap aa = new Long2IntOpenHashMap();
|
||||||
|
Long2IntFunction cc = p-> aa.computeIfAbsent(p, poss->{int b = r.nextInt()&0xFF;
|
||||||
|
while (b==0) b = r.nextInt()&0xFF;
|
||||||
|
return b;});
|
||||||
|
|
||||||
|
var test = new TestBase();
|
||||||
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
test.putTopPos(POS_A);
|
||||||
|
|
||||||
|
test.meshUpdate(POS_A, cc.get(POS_A), 0);
|
||||||
|
fillInALl(test, POS_A, cc);
|
||||||
|
|
||||||
|
test.printNodeChanges();
|
||||||
|
Logger.info("\n\n");
|
||||||
|
|
||||||
|
var positions = new ArrayList<>(aa.keySet().stream().filter(k->{
|
||||||
|
return WorldEngine.getLevel(k)!=0;
|
||||||
|
}).toList());
|
||||||
|
positions.sort(Long::compareTo);
|
||||||
|
Collections.shuffle(positions, r);
|
||||||
|
|
||||||
|
Logger.info("Removing", WorldEngine.pprintPos(positions.get(0)));
|
||||||
|
test.removeNodeGeometry(positions.get(0));
|
||||||
|
test.printNodeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static int getChildIdx(long pos) {
|
||||||
|
int x = WorldEngine.getX(pos);
|
||||||
|
int y = WorldEngine.getY(pos);
|
||||||
|
int z = WorldEngine.getZ(pos);
|
||||||
|
return (x&1)|((y&1)<<2)|((z&1)<<1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long makeChildPos(long basePos, int addin) {
|
||||||
|
int lvl = WorldEngine.getLevel(basePos);
|
||||||
|
if (lvl == 0) {
|
||||||
|
throw new IllegalArgumentException("Cannot create a child lower than lod level 0");
|
||||||
|
}
|
||||||
|
return WorldEngine.getWorldSectionId(lvl-1,
|
||||||
|
(WorldEngine.getX(basePos)<<1)|(addin&1),
|
||||||
|
(WorldEngine.getY(basePos)<<1)|((addin>>2)&1),
|
||||||
|
(WorldEngine.getZ(basePos)<<1)|((addin>>1)&1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private long makeParentPos(long pos) {
|
||||||
|
int lvl = WorldEngine.getLevel(pos);
|
||||||
|
if (lvl == MAX_LOD_LAYERS-1) {
|
||||||
|
throw new IllegalArgumentException("Cannot create a parent higher than LoD " + (MAX_LOD_LAYERS-1));
|
||||||
|
}
|
||||||
|
return WorldEngine.getWorldSectionId(lvl+1,
|
||||||
|
WorldEngine.getX(pos)>>1,
|
||||||
|
WorldEngine.getY(pos)>>1,
|
||||||
|
WorldEngine.getZ(pos)>>1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,13 +5,19 @@ import net.minecraft.client.MinecraftClient;
|
|||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class Logger {
|
public class Logger {
|
||||||
|
public static boolean INSERT_CLASS = true;
|
||||||
|
public static boolean SHUTUP = false;
|
||||||
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger("Voxy");
|
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger("Voxy");
|
||||||
|
|
||||||
public static void error(Object... args) {
|
public static void error(Object... args) {
|
||||||
|
if (SHUTUP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Throwable throwable = null;
|
Throwable throwable = null;
|
||||||
for (var i : args) {
|
for (var i : args) {
|
||||||
if (i instanceof Throwable) {
|
if (i instanceof Throwable) {
|
||||||
@@ -19,14 +25,17 @@ public class Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stackEntry = new Throwable().getStackTrace()[1];
|
var stackEntry = new Throwable().getStackTrace()[1];
|
||||||
String error = "["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" "));
|
String error = (INSERT_CLASS?("["+stackEntry.getClassName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" "));
|
||||||
LOGGER.error(error, throwable);
|
LOGGER.error(error, throwable);
|
||||||
if (!VoxyCommon.IS_DEDICATED_SERVER) {
|
if (VoxyCommon.IS_IN_MINECRAFT && !VoxyCommon.IS_DEDICATED_SERVER) {
|
||||||
MinecraftClient.getInstance().executeSync(()->{var player = MinecraftClient.getInstance().player; if (player != null) player.sendMessage(Text.literal(error), true);});
|
MinecraftClient.getInstance().executeSync(()->{var player = MinecraftClient.getInstance().player; if (player != null) player.sendMessage(Text.literal(error), true);});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void warn(Object... args) {
|
public static void warn(Object... args) {
|
||||||
|
if (SHUTUP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Throwable throwable = null;
|
Throwable throwable = null;
|
||||||
for (var i : args) {
|
for (var i : args) {
|
||||||
if (i instanceof Throwable) {
|
if (i instanceof Throwable) {
|
||||||
@@ -34,10 +43,13 @@ public class Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stackEntry = new Throwable().getStackTrace()[1];
|
var stackEntry = new Throwable().getStackTrace()[1];
|
||||||
LOGGER.warn("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable);
|
LOGGER.warn((INSERT_CLASS?("["+stackEntry.getClassName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" ")), throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void info(Object... args) {
|
public static void info(Object... args) {
|
||||||
|
if (SHUTUP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Throwable throwable = null;
|
Throwable throwable = null;
|
||||||
for (var i : args) {
|
for (var i : args) {
|
||||||
if (i instanceof Throwable) {
|
if (i instanceof Throwable) {
|
||||||
@@ -45,6 +57,16 @@ public class Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stackEntry = new Throwable().getStackTrace()[1];
|
var stackEntry = new Throwable().getStackTrace()[1];
|
||||||
LOGGER.info("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable);
|
LOGGER.info((INSERT_CLASS?("["+stackEntry.getClassName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" ")), throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String objToString(Object obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return "NULL";
|
||||||
|
}
|
||||||
|
if (obj.getClass().isArray()) {
|
||||||
|
return Arrays.deepToString((Object[]) obj);
|
||||||
|
}
|
||||||
|
return obj.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class ActiveSectionTracker {
|
|||||||
return section;
|
return section;
|
||||||
} else {
|
} else {
|
||||||
while ((section = holder.obj) == null)
|
while ((section = holder.obj) == null)
|
||||||
Thread.onSpinWait();
|
Thread.yield();
|
||||||
|
|
||||||
synchronized (cache) {
|
synchronized (cache) {
|
||||||
if (section.tryAcquire()) {
|
if (section.tryAcquire()) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package me.cortex.voxy.commonImpl;
|
package me.cortex.voxy.commonImpl;
|
||||||
|
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.config.Serialization;
|
import me.cortex.voxy.common.config.Serialization;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
@@ -9,14 +10,17 @@ import net.fabricmc.loader.api.ModContainer;
|
|||||||
public class VoxyCommon implements ModInitializer {
|
public class VoxyCommon implements ModInitializer {
|
||||||
public static final String MOD_VERSION;
|
public static final String MOD_VERSION;
|
||||||
public static final boolean IS_DEDICATED_SERVER;
|
public static final boolean IS_DEDICATED_SERVER;
|
||||||
|
public static final boolean IS_IN_MINECRAFT;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ModContainer mod = (ModContainer) FabricLoader.getInstance().getModContainer("voxy").orElse(null);
|
ModContainer mod = (ModContainer) FabricLoader.getInstance().getModContainer("voxy").orElse(null);
|
||||||
if (mod == null) {
|
if (mod == null) {
|
||||||
System.err.println("RUNNING WITHOUT MOD");
|
IS_IN_MINECRAFT = false;
|
||||||
|
Logger.error("Running voxy without minecraft");
|
||||||
MOD_VERSION = "<UNKNOWN>";
|
MOD_VERSION = "<UNKNOWN>";
|
||||||
IS_DEDICATED_SERVER = false;
|
IS_DEDICATED_SERVER = false;
|
||||||
} else {
|
} else {
|
||||||
|
IS_IN_MINECRAFT = true;
|
||||||
var version = mod.getMetadata().getVersion().getFriendlyString();
|
var version = mod.getMetadata().getVersion().getFriendlyString();
|
||||||
var commit = mod.getMetadata().getCustomValue("commit").getAsString();
|
var commit = mod.getMetadata().getCustomValue("commit").getAsString();
|
||||||
MOD_VERSION = version + "-" + commit;
|
MOD_VERSION = version + "-" + commit;
|
||||||
|
|||||||
Reference in New Issue
Block a user