Don't add bluntly redundant block change entries.

Match vs. the last entry of existing entries for those coordinates.
Seems to be better with piston retract events.
This commit is contained in:
asofold 2016-12-07 22:11:45 +01:00
parent 9a6a370f1d
commit 9dca93e650
3 changed files with 63 additions and 35 deletions

View File

@ -250,6 +250,24 @@ public class BlockChangeTracker {
|| tick <= other.nextEntryTick && other.tick <= nextEntryTick;
}
/**
* Test for redundancy between this entry and the given data
* (coordinates are not regarded, assuming handling the queue for the
* same coordinates).
*
* @param tick
* @param direction
* @param previousState
* @return
*/
public boolean isRedundant(final int tick, final Direction direction,
final IBlockCacheNode previousState) {
return tick == this.tick && direction == this.direction && (
previousState == null && this.previousState == null
|| previousState.equals(this.previousState)
);
}
}
/** Change id/count, increasing with each entry added internally. */
@ -438,47 +456,37 @@ public class BlockChangeTracker {
*/
private void addBlockChange(final long changeId, final int tick, final WorldNode worldNode,
final int x, final int y, final int z, final Direction direction, final IBlockCacheNode previousState) {
worldNode.lastChangeTick = tick;
final BlockChangeEntry entry = new BlockChangeEntry(changeId, tick, x, y, z, direction, previousState);
LinkedList<BlockChangeEntry> entries = worldNode.blocks.get(x, y, z, MoveOrder.END);
ActivityNode activityNode = worldNode.getActivityNode(x, y, z, activityResolution);
final ActivityNode activityNode = worldNode.getActivityNode(x, y, z, activityResolution);
if (entries != null && !entries.isEmpty()) {
// Lazy expiration check for this block.
if (entries.getFirst().tick < tick - expirationAgeTicks) {
final int expired = expireEntries(tick - expirationAgeTicks, entries);
worldNode.size -= expired;
activityNode.count -= expired;
}
// Re-check in case of invalidation.
if (!entries.isEmpty()) {
// Update the nextEntryTick for the last entry in the list.
final BlockChangeEntry lastEntry = entries.getLast();
if (lastEntry.isRedundant(tick, direction, previousState)) {
// Do not add.
return;
}
else {
lastEntry.nextEntryTick = tick;
}
}
// TODO: Other redundancy checks / simplifications for often changing states?
}
if (entries == null) {
entries = new LinkedList<BlockChangeTracker.BlockChangeEntry>();
worldNode.blocks.put(x, y, z, entries, MoveOrder.END); // Add to end.
}
else {
// Lazy expiration check for this block.
if (!entries.isEmpty()) {
if (entries.getFirst().tick < tick - expirationAgeTicks) {
final int expired = expireEntries(tick - expirationAgeTicks, entries);
worldNode.size -= expired;
activityNode.count -= expired;
}
// Re-check in case of invalidation.
if (!entries.isEmpty()) {
// Update the nextEntryTick for the last entry in the list.
entries.getLast().nextEntryTick = tick;
}
}
}
/*
* TODO: Prevent too fast changing states (of similar nature) to cause
* slowed down checking (isOnGround). Means could be to search for past
* states that exactly match the current state, within some timing
* margin, and then increase the duration of validity for that past
* entry instead of adding a new entry. Needs some care, to not have
* that entry invalidated by tick, but could be interesting for the case
* of tick-clock-driven piston/door setups, such as survivalfly-traps
* and derp-o-matic machines (those could be detected by other means,
* however NCP shouldn't impose such restrictions on servers by
* default). Thinking of having to use often redundant physics events
* (falling blocks?), this will probably be necessary to add, and this
* kind of compressing mechanism could still be controlled by further
* parameters (both configuration + always for physics).
*/
entries.add(entry); // Add latest to the end always.
entries.add(new BlockChangeEntry(changeId, tick, x, y, z, direction, previousState)); // Add to end.
activityNode.count ++;
worldNode.size ++;
worldNode.lastChangeTick = tick;
//DebugUtil.debug("Add block change: " + x + "," + y + "," + z + " " + direction + " " + changeId); // TODO: REMOVE
}

View File

@ -172,6 +172,21 @@ public abstract class BlockCache {
fetched |= FETCHED_BOUNDS;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof IBlockCacheNode) {
final IBlockCacheNode other = (IBlockCacheNode) obj;
return id == other.getId()
&& (!isDataFetched() && !other.isDataFetched()
|| isDataFetched() && other.isDataFetched() && data == other.getData())
&& (!isBoundsFetched() && !other.isBoundsFetched()
|| isBoundsFetched() && other.isBoundsFetched()
&& BlockProperties.isSameShape(bounds, other.getBounds())
);
}
return false;
}
}
// Instance

View File

@ -3854,7 +3854,7 @@ public class BlockProperties {
}
/**
* Check if the bounds are the same. No null checks.
* Check if the bounds are the same. With null checks.
*
* @param bounds1
* the bounds1
@ -3865,6 +3865,11 @@ public class BlockProperties {
public static final boolean isSameShape(final double[] bounds1, final double[] bounds2) {
// TODO: further exclude simple full shape blocks, or confine to itchy block types
// TODO: make flags for it.
if (bounds1 == null || bounds2 == null) {
if (bounds1 != null || bounds2 != null) {
return false;
}
}
// Allow as ground for differing shapes.
for (int i = 0; i < 6; i++) {
if (bounds1[i] != bounds2[i]) {