feat(blockstates): rotation profiles and placement tracking

This commit is contained in:
Jacobwasbeast
2026-03-11 19:56:21 -05:00
parent b47e3d2354
commit d4f7603390
15 changed files with 840 additions and 26 deletions

View File

@@ -52,6 +52,19 @@ public enum SoundType
Snow = 9
}
/// <summary>
/// Rotation/profile mapping used when resolving blockstate variants.
/// </summary>
public enum BlockRotationProfile
{
None = 0,
Facing = 1,
WallSign = 2,
StandingSign = 3,
Trapdoor = 4,
Door = 5
}
/// <summary>
/// Fluent builder for defining block properties.
/// </summary>
@@ -73,7 +86,10 @@ public class BlockProperties
internal ToolType RequiredToolValue = ToolType.None;
internal bool AcceptsRedstonePowerValue;
internal List<Assets.ModelBox>? ModelBoxes;
internal Dictionary<string, List<Assets.ModelBox>>? ModelVariants;
internal bool ModelIsFullCube;
internal BlockRotationProfile RotationProfileValue = BlockRotationProfile.None;
internal string? BlockStateValue;
public BlockProperties Material(MaterialType material) { MaterialValue = material; return this; }
public BlockProperties Hardness(float hardness) { HardnessValue = hardness; return this; }
@@ -106,4 +122,8 @@ public class BlockProperties
public BlockProperties RequiredTool(ToolType tool) { RequiredToolValue = tool; return this; }
/// <summary>Marks the block as one that can receive redstone power. Stored for future block callbacks.</summary>
public BlockProperties AcceptsRedstonePower(bool accepts = true) { AcceptsRedstonePowerValue = accepts; return this; }
/// <summary>Optional blockstate JSON name (e.g. "examplemod:ruby_chair").</summary>
public BlockProperties BlockState(string blockStateName) { BlockStateValue = blockStateName; return this; }
/// <summary>Rotation/profile mapping for blockstate variants.</summary>
public BlockProperties RotationProfile(BlockRotationProfile profile) { RotationProfileValue = profile; return this; }
}

View File

@@ -51,6 +51,7 @@ public static class BlockRegistry
public static RegisteredBlock Register(Identifier id, BlockProperties properties)
{
Assets.ModelResolver.ApplyBlockModel(id, properties);
Assets.ModelResolver.ApplyBlockStates(id, properties);
ApplyModelLightDefaults(properties);
int numericId = NativeInterop.native_register_block(
id.ToString(),
@@ -72,10 +73,7 @@ public static class BlockRegistry
throw new InvalidOperationException($"Failed to register block '{id}'. No free IDs or invalid parameters.");
}
if (properties.ModelBoxes != null && properties.ModelBoxes.Count > 0)
{
NativeInterop.native_register_block_model(numericId, properties.ModelBoxes.ToArray(), properties.ModelBoxes.Count);
}
RegisterBlockModels(numericId, properties);
AddToCreative(id, numericId, properties);
@@ -96,6 +94,7 @@ public static class BlockRegistry
return RegisterFalling(id, managedBlock, properties);
Assets.ModelResolver.ApplyBlockModel(id, properties);
Assets.ModelResolver.ApplyBlockStates(id, properties);
ApplyModelLightDefaults(properties);
int numericId = NativeInterop.native_register_managed_block(
id.ToString(),
@@ -117,10 +116,7 @@ public static class BlockRegistry
throw new InvalidOperationException($"Failed to register managed block '{id}'.");
}
if (properties.ModelBoxes != null && properties.ModelBoxes.Count > 0)
{
NativeInterop.native_register_block_model(numericId, properties.ModelBoxes.ToArray(), properties.ModelBoxes.Count);
}
RegisterBlockModels(numericId, properties);
AddToCreative(id, numericId, properties);
@@ -149,6 +145,7 @@ public static class BlockRegistry
private static RegisteredBlock RegisterFalling(Identifier id, Block? managedBlock, BlockProperties properties)
{
Assets.ModelResolver.ApplyBlockModel(id, properties);
Assets.ModelResolver.ApplyBlockStates(id, properties);
ApplyModelLightDefaults(properties);
int numericId = NativeInterop.native_register_falling_block(
id.ToString(),
@@ -170,10 +167,7 @@ public static class BlockRegistry
throw new InvalidOperationException($"Failed to register falling block '{id}'.");
}
if (properties.ModelBoxes != null && properties.ModelBoxes.Count > 0)
{
NativeInterop.native_register_block_model(numericId, properties.ModelBoxes.ToArray(), properties.ModelBoxes.Count);
}
RegisterBlockModels(numericId, properties);
AddToCreative(id, numericId, properties);
@@ -202,6 +196,7 @@ public static class BlockRegistry
public static RegisteredSlabBlock RegisterSlab(Identifier id, BlockProperties properties)
{
Assets.ModelResolver.ApplyBlockModel(id, properties);
Assets.ModelResolver.ApplyBlockStates(id, properties);
ApplyModelLightDefaults(properties);
Identifier doubleId = new($"{id}_double");
int numericId = NativeInterop.native_register_slab_block(
@@ -230,10 +225,7 @@ public static class BlockRegistry
throw new InvalidOperationException($"Failed to resolve generated slab pair '{doubleId}'.");
}
if (properties.ModelBoxes != null && properties.ModelBoxes.Count > 0)
{
NativeInterop.native_register_block_model(numericId, properties.ModelBoxes.ToArray(), properties.ModelBoxes.Count);
}
RegisterBlockModels(numericId, properties);
AddToCreative(id, numericId, properties);
@@ -257,6 +249,27 @@ public static class BlockRegistry
if (!properties.ModelIsFullCube)
properties.LightBlockValue = 0;
}
private static void RegisterBlockModels(int numericId, BlockProperties properties)
{
if (properties.ModelBoxes != null && properties.ModelBoxes.Count > 0)
{
NativeInterop.native_register_block_model(numericId, properties.ModelBoxes.ToArray(), properties.ModelBoxes.Count);
}
if (properties.ModelVariants != null)
{
foreach (var variant in properties.ModelVariants)
{
var boxes = variant.Value;
if (boxes.Count == 0)
continue;
NativeInterop.native_register_block_model_variant(numericId, variant.Key, boxes.ToArray(), boxes.Count);
}
}
NativeInterop.native_register_block_rotation_profile(numericId, (int)properties.RotationProfileValue);
}
internal static bool TryGetIdentifier(int numericId, out Identifier id)
{
lock (s_lock)