Collisions without BlockMapping

This commit is contained in:
Camotoy 2024-05-17 17:52:19 -04:00
parent 1cd0aad79f
commit 06dc0d1ca8
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
14 changed files with 100 additions and 126 deletions

View file

@ -69,7 +69,6 @@ import org.geysermc.geyser.event.GeyserEventBus;
import org.geysermc.geyser.extension.GeyserExtensionManager;
import org.geysermc.geyser.impl.MinecraftVersionImpl;
import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.network.netty.GeyserServer;
import org.geysermc.geyser.registry.BlockRegistries;
@ -258,7 +257,6 @@ public class GeyserImpl implements GeyserApi {
VersionCheckUtils.checkForOutdatedJava(logger);
Blocks.VAULT.javaId();
for (int i = 0; i < BlockRegistries.JAVA_BLOCKS.get().length; i++) {
String cleanIdentifier = BlockRegistries.JAVA_BLOCKS.get(i).getCleanJavaIdentifier();
String newIdentifier = BlockRegistries.BLOCK_STATES.get(i).block().javaIdentifier();

View file

@ -69,9 +69,6 @@ public final class BlockStateValues {
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
private static final IntSet UPPER_DOORS = new IntOpenHashSet();
public static final int JAVA_AIR_ID = 0;
public static int JAVA_COBWEB_ID;
public static int JAVA_FURNACE_ID;
public static int JAVA_FURNACE_LIT_ID;
public static int JAVA_HONEY_BLOCK_ID;

View file

@ -34,6 +34,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.geysermc.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.registry.loader.CollisionRegistryLoader;
@ -82,19 +83,13 @@ public class BlockRegistries {
/**
* A mapped registry containing which holds block IDs to its {@link BlockCollision}.
*/
public static final IntMappedRegistry<BlockCollision> COLLISIONS;
public static final ListRegistry<BlockCollision> COLLISIONS;
/**
* A mapped registry containing the Java identifiers to IDs.
*/
public static final MappedRegistry<String, Integer, Object2IntMap<String>> JAVA_IDENTIFIER_TO_ID = MappedRegistry.create(RegistryLoaders.empty(Object2IntOpenHashMap::new));
/**
* A registry which stores unique Java IDs to its clean identifier
* This is used in the statistics form.
*/
public static final ArrayRegistry<String> CLEAN_JAVA_IDENTIFIERS = ArrayRegistry.create(RegistryLoaders.uninitialized());
/**
* A registry containing all the waterlogged blockstates.
*/
@ -141,12 +136,13 @@ public class BlockRegistries {
public static final SimpleMappedRegistry<String, CustomSkull> CUSTOM_SKULLS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
static {
Blocks.VAULT.javaId(); // FIXME
CustomSkullRegistryPopulator.populate();
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.PRE_INIT);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.DEFINITION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_JAVA);
COLLISIONS = IntMappedRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collision.json"), CollisionRegistryLoader::new);
COLLISIONS = ListRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collisions.nbt"), CollisionRegistryLoader::new);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.VANILLA_REGISTRATION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK);

View file

@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.registry.loader.RegistryLoader;
import java.util.List;
import java.util.function.Supplier;
public class ListRegistry<M> extends Registry<List<M>> {
/**
@ -98,6 +99,18 @@ public class ListRegistry<M> extends Registry<List<M>> {
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, M> ListRegistry<M> create(RegistryLoader<I, List<M>> registryLoader) {
return new ListRegistry<M>(null, registryLoader);
return new ListRegistry<>(null, registryLoader);
}
/**
* Creates a new integer mapped registry with the given {@link RegistryLoader} and input.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <M> the type value
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, M> ListRegistry<M> create(I input, Supplier<RegistryLoader<I, List<M>>> registryLoader) {
return new ListRegistry<>(input, registryLoader.get());
}
}

View file

@ -25,18 +25,19 @@
package org.geysermc.geyser.registry.loader;
import com.fasterxml.jackson.databind.node.ArrayNode;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.AllArgsConstructor;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtList;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.nbt.NbtUtils;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.translator.collision.BlockCollision;
import org.geysermc.geyser.translator.collision.CollisionRemapper;
import org.geysermc.geyser.translator.collision.OtherCollision;
@ -51,41 +52,43 @@ import java.util.regex.Pattern;
/**
* Loads collision data from the given resource path.
*/
public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String, Int2ObjectMap<BlockCollision>> {
public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String, List<BlockCollision>> {
@Override
public Int2ObjectMap<BlockCollision> load(Pair<String, String> input) {
Int2ObjectMap<BlockCollision> collisions = new Int2ObjectOpenHashMap<>();
public List<BlockCollision> load(Pair<String, String> input) {
Map<Class<?>, CollisionInfo> annotationMap = new IdentityHashMap<>();
for (Class<?> clazz : FileUtils.getGeneratedClassesForAnnotation(CollisionRemapper.class.getName())) {
GeyserImpl.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName());
CollisionRemapper collisionRemapper = clazz.getAnnotation(CollisionRemapper.class);
annotationMap.put(clazz, new CollisionInfo(collisionRemapper, Pattern.compile(collisionRemapper.regex()), Pattern.compile(collisionRemapper.paramRegex())));
annotationMap.put(clazz, new CollisionInfo(collisionRemapper, Pattern.compile(collisionRemapper.regex())));
}
// Load collision mappings file
int[] indices;
List<BoundingBox[]> collisionList;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input.value())) {
ArrayNode collisionNode = (ArrayNode) GeyserImpl.JSON_MAPPER.readTree(stream);
collisionList = loadBoundingBoxes(collisionNode);
NbtMap collisionData = (NbtMap) NbtUtils.createGZIPReader(stream).readTag();
indices = collisionData.getIntArray("indices");
//SuppressWarnings unchecked
collisionList = loadBoundingBoxes(collisionData.getList("collisions", NbtType.LIST));
} catch (Exception e) {
throw new AssertionError("Unable to load collision data", e);
}
BlockMapping[] blockMappings = BlockRegistries.JAVA_BLOCKS.get();
List<BlockState> blockStates = BlockRegistries.BLOCK_STATES.get();
List<BlockCollision> collisions = new ObjectArrayList<>(blockStates.size());
// Map of unique collisions to its instance
Map<BlockCollision, BlockCollision> collisionInstances = new Object2ObjectOpenHashMap<>();
for (int i = 0; i < blockMappings.length; i++) {
BlockMapping blockMapping = blockMappings[i];
if (blockMapping == null) {
GeyserImpl.getInstance().getLogger().warning("Missing block mapping for Java block " + i);
for (int i = 0; i < blockStates.size(); i++) {
BlockState state = blockStates.get(i);
if (state == null) {
GeyserImpl.getInstance().getLogger().warning("Missing block state for Java block " + i);
continue;
}
BlockCollision newCollision = instantiateCollision(blockMapping, annotationMap, collisionList);
BlockCollision newCollision = instantiateCollision(state, annotationMap, indices[i], collisionList);
if (newCollision != null) {
// If there's an existing instance equal to this one, use that instead
@ -97,33 +100,27 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
}
}
collisions.put(i, newCollision);
collisions.add(newCollision);
}
return collisions;
}
private @Nullable BlockCollision instantiateCollision(BlockMapping mapping, Map<Class<?>, CollisionInfo> annotationMap, List<BoundingBox[]> collisionList) {
String[] blockIdParts = mapping.getJavaIdentifier().split("\\[");
String blockName = blockIdParts[0].replace("minecraft:", "");
String params = "";
if (blockIdParts.length == 2) {
params = "[" + blockIdParts[1];
}
int collisionIndex = mapping.getCollisionIndex();
private @Nullable BlockCollision instantiateCollision(BlockState state, Map<Class<?>, CollisionInfo> annotationMap, int collisionIndex, List<BoundingBox[]> collisionList) {
String blockName = state.block().javaIdentifier().substring("minecraft:".length());
for (Map.Entry<Class<?>, CollisionInfo> collisionRemappers : annotationMap.entrySet()) {
Class<?> type = collisionRemappers.getKey();
CollisionInfo collisionInfo = collisionRemappers.getValue();
CollisionRemapper annotation = collisionInfo.collisionRemapper;
if (collisionInfo.pattern.matcher(blockName).find() && collisionInfo.paramsPattern.matcher(params).find()) {
if (collisionInfo.pattern.matcher(blockName).find()) {
try {
if (annotation.passDefaultBoxes()) {
// Create an OtherCollision instance and get the bounding boxes
BoundingBox[] defaultBoxes = collisionList.get(collisionIndex);
return (BlockCollision) type.getDeclaredConstructor(String.class, BoundingBox[].class).newInstance(params, defaultBoxes);
return (BlockCollision) type.getDeclaredConstructor(BlockState.class, BoundingBox[].class).newInstance(state, defaultBoxes);
} else {
return (BlockCollision) type.getDeclaredConstructor(String.class).newInstance(params);
return (BlockCollision) type.getDeclaredConstructor(BlockState.class).newInstance(state);
}
} catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw new RuntimeException(e);
@ -138,25 +135,25 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
// Unless some of the low IDs are changed, which is unlikely, the second item should always be full collision
if (collisionIndex == 1) {
return new SolidCollision(params);
return new SolidCollision(state);
}
return new OtherCollision(collisionList.get(collisionIndex));
}
private List<BoundingBox[]> loadBoundingBoxes(ArrayNode collisionNode) {
private List<BoundingBox[]> loadBoundingBoxes(List<NbtList> collisionNode) {
List<BoundingBox[]> collisions = new ObjectArrayList<>();
for (int collisionIndex = 0; collisionIndex < collisionNode.size(); collisionIndex++) {
ArrayNode boundingBoxArray = (ArrayNode) collisionNode.get(collisionIndex);
@SuppressWarnings("unchecked") NbtList<NbtList<Double>> boundingBoxArray = (NbtList<NbtList<Double>>) collisionNode.get(collisionIndex);
BoundingBox[] boundingBoxes = new BoundingBox[boundingBoxArray.size()];
for (int i = 0; i < boundingBoxArray.size(); i++) {
ArrayNode boxProperties = (ArrayNode) boundingBoxArray.get(i);
boundingBoxes[i] = new BoundingBox(boxProperties.get(0).asDouble(),
boxProperties.get(1).asDouble(),
boxProperties.get(2).asDouble(),
boxProperties.get(3).asDouble(),
boxProperties.get(4).asDouble(),
boxProperties.get(5).asDouble());
NbtList<Double> boxProperties = boundingBoxArray.get(i);
boundingBoxes[i] = new BoundingBox(boxProperties.get(0),
boxProperties.get(1),
boxProperties.get(2),
boxProperties.get(3),
boxProperties.get(4),
boxProperties.get(5));
}
// Sorting by lowest Y first fixes some bugs
@ -173,6 +170,5 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
public static class CollisionInfo {
private final CollisionRemapper collisionRemapper;
private final Pattern pattern;
private final Pattern paramsPattern;
}
}

View file

@ -409,7 +409,6 @@ public final class BlockRegistryPopulator {
Deque<String> cleanIdentifiers = new ArrayDeque<>();
int javaRuntimeId = -1;
int cobwebBlockId = -1;
int furnaceRuntimeId = -1;
int furnaceLitRuntimeId = -1;
int honeyBlockRuntimeId = -1;
@ -485,10 +484,7 @@ public final class BlockRegistryPopulator {
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
if (javaId.contains("cobweb")) {
cobwebBlockId = uniqueJavaId;
} else if (javaId.startsWith("minecraft:furnace[facing=north")) {
if (javaId.startsWith("minecraft:furnace[facing=north")) {
if (javaId.contains("lit=true")) {
furnaceLitRuntimeId = javaRuntimeId;
} else {
@ -507,11 +503,6 @@ public final class BlockRegistryPopulator {
}
}
if (cobwebBlockId == -1) {
throw new AssertionError("Unable to find cobwebs in palette");
}
BlockStateValues.JAVA_COBWEB_ID = cobwebBlockId;
if (furnaceRuntimeId == -1) {
throw new AssertionError("Unable to find furnace in palette");
}

View file

@ -37,12 +37,6 @@ public @interface CollisionRemapper {
*/
String regex();
/**
* Regex of block state parameters to apply this collision to
* Defaults to matching any value
*/
String paramRegex() default ".*";
/**
* Signals if a new instance needs to created for every block state
*/

View file

@ -26,13 +26,14 @@
package org.geysermc.geyser.translator.collision;
import lombok.EqualsAndHashCode;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.level.physics.CollisionManager;
@EqualsAndHashCode(callSuper = true)
@CollisionRemapper(regex = "^dirt_path$", passDefaultBoxes = true)
public class DirtPathCollision extends BlockCollision {
public DirtPathCollision(String params, BoundingBox[] defaultBoxes) {
public DirtPathCollision(BlockState state, BoundingBox[] defaultBoxes) {
super(defaultBoxes);
}

View file

@ -26,6 +26,8 @@
package org.geysermc.geyser.translator.collision;
import lombok.EqualsAndHashCode;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.session.GeyserSession;
@ -40,20 +42,18 @@ public class DoorCollision extends BlockCollision {
*/
private int facing;
public DoorCollision(String params, BoundingBox[] defaultBoxes) {
public DoorCollision(BlockState state, BoundingBox[] defaultBoxes) {
super(defaultBoxes);
if (params.contains("facing=north")) {
facing = 1;
} else if (params.contains("facing=east")) {
facing = 2;
} else if (params.contains("facing=south")) {
facing = 3;
} else if (params.contains("facing=west")) {
facing = 4;
}
facing = switch (state.getValue(Properties.HORIZONTAL_FACING)) {
case NORTH -> 1;
case EAST -> 2;
case SOUTH -> 3;
case WEST -> 4;
default -> throw new IllegalStateException();
};
// If the door is open it changes direction
if (params.contains("open=true")) {
if (state.getValue(Properties.OPEN)) {
facing = facing % 2 + 1;
}
}

View file

@ -26,6 +26,8 @@
package org.geysermc.geyser.translator.collision;
import lombok.EqualsAndHashCode;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.session.GeyserSession;
@ -44,24 +46,23 @@ public class GlassPaneAndIronBarsCollision extends BlockCollision {
*/
private int facing;
public GlassPaneAndIronBarsCollision(String params, BoundingBox[] defaultBoxes) {
public GlassPaneAndIronBarsCollision(BlockState state, BoundingBox[] defaultBoxes) {
super(defaultBoxes);
//east=true,north=true,south=true,west=true
if (params.contains("north=true") && params.contains("east=true")) {
if (state.getValue(Properties.NORTH) && state.getValue(Properties.EAST)) {
facing = 5;
} else if (params.contains("east=true") && params.contains("south=true")) {
} else if (state.getValue(Properties.EAST) && state.getValue(Properties.SOUTH)) {
facing = 6;
} else if (params.contains("south=true") && params.contains("west=true")) {
} else if (state.getValue(Properties.SOUTH) && state.getValue(Properties.WEST)) {
facing = 7;
} else if (params.contains("west=true") && params.contains("north=true")) {
} else if (state.getValue(Properties.WEST) && state.getValue(Properties.NORTH)) {
facing = 8;
} else if (params.contains("north=true")) {
} else if (state.getValue(Properties.NORTH)) {
facing = 1;
} else if (params.contains("east=true")) {
} else if (state.getValue(Properties.EAST)) {
facing = 2;
} else if (params.contains("south=true")) {
} else if (state.getValue(Properties.SOUTH)) {
facing = 3;
} else if (params.contains("west=true")) {
} else if (state.getValue(Properties.WEST)) {
facing = 4;
}
}

View file

@ -26,6 +26,7 @@
package org.geysermc.geyser.translator.collision;
import lombok.EqualsAndHashCode;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.session.GeyserSession;
@ -35,7 +36,7 @@ import org.geysermc.geyser.session.GeyserSession;
@EqualsAndHashCode(callSuper = true)
@CollisionRemapper(regex = "^scaffolding$", usesParams = true, passDefaultBoxes = true)
public class ScaffoldingCollision extends BlockCollision {
public ScaffoldingCollision(String params, BoundingBox[] defaultBoxes) {
public ScaffoldingCollision(BlockState state, BoundingBox[] defaultBoxes) {
super(defaultBoxes);
}

View file

@ -26,12 +26,13 @@
package org.geysermc.geyser.translator.collision;
import lombok.EqualsAndHashCode;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox;
@EqualsAndHashCode(callSuper = true)
@CollisionRemapper(regex = "shulker_box$") // These have no collision in the mappings as it depends on the NBT data
public class SolidCollision extends BlockCollision {
public SolidCollision(String params) {
public SolidCollision(BlockState state) {
super(new BoundingBox[] {
new BoundingBox(0.5, 0.5, 0.5, 1, 1, 1)
});

View file

@ -26,42 +26,27 @@
package org.geysermc.geyser.translator.collision;
import lombok.EqualsAndHashCode;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.level.physics.CollisionManager;
import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.session.GeyserSession;
@EqualsAndHashCode(callSuper = true)
@CollisionRemapper(regex = "_trapdoor$", usesParams = true, passDefaultBoxes = true)
public class TrapdoorCollision extends BlockCollision {
/**
* 1 = north
* 2 = east
* 3 = south
* 4 = west
* 5 = up
* 6 = down
*/
private int facing;
private final Direction facing;
public TrapdoorCollision(String params, BoundingBox[] defaultBoxes) {
public TrapdoorCollision(BlockState state, BoundingBox[] defaultBoxes) {
super(defaultBoxes);
if (params.contains("open=true")) {
if (params.contains("facing=north")) {
facing = 1;
} else if (params.contains("facing=east")) {
facing = 2;
} else if (params.contains("facing=south")) {
facing = 3;
} else if (params.contains("facing=west")) {
facing = 4;
}
if (state.getValue(Properties.OPEN)) {
facing = state.getValue(Properties.HORIZONTAL_FACING);
} else {
if (params.contains("half=bottom")) {
// Up
facing = 5;
if (state.getValue(Properties.HALF).equals("bottom")) {
facing = Direction.UP;
} else {
// Down
facing = 6;
facing = Direction.DOWN;
}
}
}
@ -72,22 +57,22 @@ public class TrapdoorCollision extends BlockCollision {
// Check for door bug (doors are 0.1875 blocks thick on Java but 0.1825 blocks thick on Bedrock)
if (this.checkIntersection(x, y, z, playerCollision)) {
switch (facing) {
case 1: // North
case NORTH:
playerCollision.setMiddleZ(z + 0.5125);
break;
case 2: // East
case EAST:
playerCollision.setMiddleX(x + 0.5125);
break;
case 3: // South
case SOUTH:
playerCollision.setMiddleZ(z + 0.4875);
break;
case 4: // West
case WEST:
playerCollision.setMiddleX(x + 0.4875);
break;
case 5:
case UP:
// Up-facing trapdoors are handled by the step-up check
break;
case 6: // Down
case DOWN:
// (top y of trap door) - (trap door thickness) = top y of player
playerCollision.setMiddleY(y + 1 - (3.0 / 16.0) - playerCollision.getSizeY() / 2.0 - CollisionManager.COLLISION_TOLERANCE);
break;

@ -1 +1 @@
Subproject commit 7c01501ed6a0ee8848a66d729000539f2661f785
Subproject commit 6b661f0d517d895aebc1f55a25d2c86f033beb1d