Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a CustomShape override, for changing shape based on blockstates. #902

Open
wants to merge 10 commits into
base: 2001
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,17 @@
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

@SuppressWarnings({"unused", "UnusedReturnValue"})
public abstract class BlockBuilder extends BuilderBase<Block> {
Expand All @@ -72,6 +77,7 @@ public abstract class BlockBuilder extends BuilderBase<Block> {
public transient String model;
public transient BlockItemBuilder itemBuilder;
public transient List<AABB> customShape;
public Map<Map<String, Object>, List<AABB>> shapeMap;
Copy link
Contributor

@ChiefArug ChiefArug Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mods/Vanilla typically use arrays to store shapes as each blockstate property value can be simplified down to an index.
I don't think that is feasible here, but it should be an IdentityHashMap and use BlockStates (because only one BlockState instance exists per combination of property values) as keys so that is has slightly better performance from use == rather than equals().

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will look into that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide more details? I don't understand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh whoops, I was reffering to the other shapeMap, here: https://github.com/KubeJS-Mods/KubeJS/pull/902/files#diff-0f8f0bc1e2c3614534f9f872111eca48e7d4e71d68c067e5db4e51506975e32bR102

Making that a Map<BlockState, VoxelShape> then when you initialize it making a new IdentityHashMap<>().

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like, how do I get all of the different blockstates, I played around with the code, but could not find how to do this. Or just pass on this one, how do I get this pr reviewed to add it to the real thing? Is there anything I need to do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be methods on Block for getting blockstates. Like I said, this comment was meant to be on the other shapeMap in the BasicBlockJS class where you do actually have access to those.

As for getting this added to KJS that could happen anytime Lat or Max feels like it, but that likely wont happen while there are pending reviews from other people. Sometimes PRs can stay open for a while with no activity as Lat doesn't touch Github much and he generally deals with feature PRs.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright

public transient boolean noCollision;
public transient boolean notSolid;
public transient float slipperiness = Float.NaN;
Expand Down Expand Up @@ -117,6 +123,7 @@ public BlockBuilder(ResourceLocation i) {
itemBuilder = getOrCreateItemBuilder();
itemBuilder.blockBuilder = this;
customShape = new ArrayList<>();
shapeMap = new HashMap<>();
noCollision = false;
notSolid = false;
randomTickCallback = null;
Expand Down Expand Up @@ -507,6 +514,23 @@ public BlockBuilder box(double x0, double y0, double z0, double x1, double y1, d
return box(x0, y0, z0, x1, y1, z1, true);
}

@Info("Set the shape of the block.")
public BlockBuilder box(Map<String, Object> condition, double x0, double y0, double z0, double x1, double y1, double z1) {
return box(condition, x0, y0, z0, x1, y1, z1, true);
}

@Info("Crates a callback for the shape of the block. '.box' will set be used if not present.")
public BlockBuilder box(Map<String, Object> condition, double x0, double y0, double z0, double x1, double y1, double z1, boolean scale16) {
List<AABB> cubes = shapeMap.getOrDefault(condition, new ArrayList<>());
if (scale16) {
cubes.add(new AABB(x0 / 16D, y0 / 16D, z0 / 16D, x1 / 16D, y1 / 16D, z1 / 16D));
} else {
cubes.add(new AABB(x0, y0, z0, x1, y1, z1));
}
shapeMap.put(condition, cubes);
return this;
}

public static VoxelShape createShape(List<AABB> boxes) {
if (boxes.isEmpty()) {
return Shapes.block();
Expand Down Expand Up @@ -833,4 +857,44 @@ public Block.Properties createProperties() {

return properties;
}

public Map<Map<String, Object>, VoxelShape> getShapeMap(Collection<Property<?>> properties) {
final Map<Map<String, Object>, List<AABB>>[] cubeMap = new Map[]{new HashMap<Map<String, Object>, List<AABB>>()};
properties.forEach(property -> {
if(cubeMap[0].isEmpty()) {
property.getPossibleValues().forEach(value -> {
Map<String, Object> propMap = new HashMap<>();
propMap.put(property.getName(), value);
cubeMap[0].put(propMap, new ArrayList<>());
});
}
else {
var oldMap = cubeMap[0];
cubeMap[0] = new HashMap<>();
oldMap.forEach((k,v) -> {
property.getPossibleValues().forEach(value -> {
Map<String, Object> propMap = new HashMap<>(k);
propMap.put(property.getName(), value);
cubeMap[0].put(propMap, v);
});
});
}
});
cubeMap[0].forEach((cubeMapKey,cubeMapValue) -> {
shapeMap.forEach((shapeMapKey,shapeMapValue) -> {
if(shapeMapKey.entrySet().stream().allMatch(entry -> compareValue(entry.getValue(),cubeMapKey.get(entry.getKey())))) cubeMapValue.addAll(shapeMapValue);
});
});
final Map<Map<String, Object>, VoxelShape> voxelShapeMap = new HashMap<>();
cubeMap[0].forEach((cubeMapKey,cubeMapValue) -> voxelShapeMap.put(cubeMapKey,BlockBuilder.createShape(cubeMapValue)));
return voxelShapeMap;
}
private boolean compareValue(Object o1, Object o2) {
if(o1.getClass() == Double.class || o1.getClass() == Float.class || o1.getClass() == Integer.class) {
if(o2.getClass() == Double.class || o2.getClass() == Float.class || o2.getClass() == Integer.class)
return ((Number) o1).doubleValue() == ((Number) o2).doubleValue();
}
else return o1.equals(o2);
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.latvian.mods.kubejs.block.custom;

import com.mojang.serialization.MapCodec;
import dev.latvian.mods.kubejs.block.BlockBuilder;
import dev.latvian.mods.kubejs.block.BlockRightClickedEventJS;
import dev.latvian.mods.kubejs.block.KubeJSBlockProperties;
Expand Down Expand Up @@ -37,6 +38,7 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
Expand All @@ -47,6 +49,7 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
Expand All @@ -57,7 +60,11 @@
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

public class BasicBlockJS extends Block implements BlockKJS, SimpleWaterloggedBlock {
Expand Down Expand Up @@ -92,13 +99,15 @@ public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, Block

public final BlockBuilder blockBuilder;
public final VoxelShape shape;
public Map<Map<String, Object>, VoxelShape> shapeMap = new HashMap<>();

public BasicBlockJS(BlockBuilder p) {
super(p.createProperties());
blockBuilder = p;
shape = BlockBuilder.createShape(p.customShape);

var blockState = stateDefinition.any();
this.shapeMap = p.getShapeMap(blockState.getProperties());
if (blockBuilder.defaultStateModification != null) {
var callbackJS = new BlockStateModifyCallbackJS(blockState);
if (safeCallback(blockBuilder.defaultStateModification, callbackJS, "Error while creating default blockState for block " + p.id)) {
Expand Down Expand Up @@ -126,7 +135,11 @@ public MutableComponent getName() {
@Override
@Deprecated
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return shape;
Map<String, Object> blockPropertyValues = new HashMap<>();
state.getProperties().forEach((property) -> {
blockPropertyValues.put(property.getName(), state.getValue(property));
});
return shapeMap.getOrDefault(blockPropertyValues, shape);
}

@Override
Expand Down