improve blockstate lookups
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
// Nitori Copyright (C) 2024 Gensokyo Reimagined
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.common.util.collections;
|
||||
|
||||
import it.unimi.dsi.fastutil.HashCommon;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A lossy hashtable implementation that stores a mapping between an object and a boolean.
|
||||
* <p>
|
||||
* Any hash collisions will result in an overwrite: this is safe because the correct value can always be recomputed,
|
||||
* given that the given operator is deterministic.
|
||||
* <p>
|
||||
* This implementation is safe to use from multiple threads
|
||||
*/
|
||||
public final class Object2BooleanCacheTable<T> {
|
||||
private final int mask;
|
||||
|
||||
private final Node<T>[] nodes;
|
||||
|
||||
private final Predicate<T> operator;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object2BooleanCacheTable(int capacity, Predicate<T> operator) {
|
||||
int capacity1 = Mth.smallestEncompassingPowerOfTwo(capacity);
|
||||
this.mask = capacity1 - 1;
|
||||
|
||||
this.nodes = (Node<T>[]) new Node[capacity1];
|
||||
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
private static <T> int hash(T key) {
|
||||
return HashCommon.mix(key.hashCode());
|
||||
}
|
||||
|
||||
public boolean get(T key) {
|
||||
int idx = hash(key) & this.mask;
|
||||
|
||||
Node<T> node = this.nodes[idx];
|
||||
if (node != null && key.equals(node.key)) {
|
||||
return node.value;
|
||||
}
|
||||
|
||||
boolean test = this.operator.test(key);
|
||||
this.nodes[idx] = new Node<>(key, test);
|
||||
|
||||
return test;
|
||||
}
|
||||
|
||||
static class Node<T> {
|
||||
final T key;
|
||||
final boolean value;
|
||||
|
||||
Node(T key, boolean value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Nitori Copyright (C) 2024 Gensokyo Reimagined
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.mixin.cached_hashcode;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Block.BlockStatePairKey.class)
|
||||
public class BlockNeighborGroupMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private BlockState first;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private BlockState second;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Direction direction;
|
||||
|
||||
private int hash;
|
||||
|
||||
/**
|
||||
* @reason Initialize the object's hashcode and cache it
|
||||
*/
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void generateHash(BlockState blockState_1, BlockState blockState_2, Direction direction_1, CallbackInfo ci) {
|
||||
int hash = this.first.hashCode();
|
||||
hash = 31 * hash + this.second.hashCode();
|
||||
hash = 31 * hash + this.direction.hashCode();
|
||||
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Uses the cached hashcode
|
||||
* @author JellySquid
|
||||
*/
|
||||
@Overwrite(remap = false)
|
||||
public int hashCode() {
|
||||
return this.hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Nitori Copyright (C) 2024 Gensokyo Reimagined
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.mixin.shapes.blockstate_cache;
|
||||
|
||||
import net.gensokyoreimagined.nitori.common.util.collections.Object2BooleanCacheTable;
|
||||
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
@Mixin(Block.class)
|
||||
public class BlockMixin {
|
||||
private static final Object2BooleanCacheTable<VoxelShape> FULL_CUBE_CACHE = new Object2BooleanCacheTable<>(
|
||||
512,
|
||||
shape -> !Shapes.joinIsNotEmpty(Shapes.block(), shape, BooleanOp.NOT_SAME)
|
||||
);
|
||||
|
||||
/**
|
||||
* @reason Use a faster cache implementation
|
||||
* @author gegy1000
|
||||
*/
|
||||
@Overwrite
|
||||
public static boolean isShapeFullBlock(VoxelShape shape) {
|
||||
return FULL_CUBE_CACHE.get(shape);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@
|
||||
"MixinWorldGenRegion",
|
||||
"alloc.ServerChunkManagerMixin",
|
||||
"alloc.StateMixin",
|
||||
"util.MixinLevelBlockEntityRetrieval"
|
||||
"util.MixinLevelBlockEntityRetrieval",
|
||||
"cached_hashcode.BlockNeighborGroupMixin",
|
||||
"shapes.blockstate_cache.BlockMixin"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user