github.com/df-mc/dragonfly@v0.9.13/server/internal/blockinternal/components.go (about)

     1  package blockinternal
     2  
     3  import (
     4  	"github.com/df-mc/dragonfly/server/block"
     5  	"github.com/df-mc/dragonfly/server/block/cube"
     6  	"github.com/df-mc/dragonfly/server/block/customblock"
     7  	"github.com/df-mc/dragonfly/server/world"
     8  	"github.com/go-gl/mathgl/mgl64"
     9  )
    10  
    11  // Components returns all the components for the custom block, including permutations and properties.
    12  func Components(identifier string, b world.CustomBlock, blockID int32) map[string]any {
    13  	components := componentsFromProperties(b.Properties())
    14  	builder := NewComponentBuilder(identifier, components, blockID)
    15  	if emitter, ok := b.(block.LightEmitter); ok {
    16  		builder.AddComponent("minecraft:block_light_emission", map[string]any{
    17  			"emission": float32(emitter.LightEmissionLevel() / 15),
    18  		})
    19  	}
    20  	if diffuser, ok := b.(block.LightDiffuser); ok {
    21  		builder.AddComponent("minecraft:block_light_filter", map[string]any{
    22  			"lightLevel": int32(diffuser.LightDiffusionLevel()),
    23  		})
    24  	}
    25  	if breakable, ok := b.(block.Breakable); ok {
    26  		info := breakable.BreakInfo()
    27  		builder.AddComponent("minecraft:destructible_by_mining", map[string]any{"value": float32(info.Hardness)})
    28  	}
    29  	if frictional, ok := b.(block.Frictional); ok {
    30  		builder.AddComponent("minecraft:friction", map[string]any{"value": float32(frictional.Friction())})
    31  	}
    32  	if flammable, ok := b.(block.Flammable); ok {
    33  		info := flammable.FlammabilityInfo()
    34  		builder.AddComponent("minecraft:flammable", map[string]any{
    35  			"flame_odds": int32(info.Encouragement),
    36  			"burn_odds":  int32(info.Flammability),
    37  		})
    38  	}
    39  	if permutable, ok := b.(block.Permutable); ok {
    40  		for name, values := range permutable.States() {
    41  			builder.AddProperty(name, values)
    42  		}
    43  		for _, permutation := range permutable.Permutations() {
    44  			builder.AddPermutation(permutation.Condition, componentsFromProperties(permutation.Properties))
    45  		}
    46  	}
    47  	if item, ok := b.(world.CustomItem); ok {
    48  		builder.SetMenuCategory(item.Category())
    49  	}
    50  	return builder.Construct()
    51  }
    52  
    53  // componentsFromProperties builds a base components map that includes all the common data between a regular block and
    54  // a custom permutation.
    55  func componentsFromProperties(props customblock.Properties) map[string]any {
    56  	components := make(map[string]any)
    57  	if props.CollisionBox != (cube.BBox{}) {
    58  		components["minecraft:collision_box"] = bboxComponent(props.CollisionBox)
    59  	}
    60  	if props.SelectionBox != (cube.BBox{}) {
    61  		components["minecraft:selection_box"] = bboxComponent(props.SelectionBox)
    62  	}
    63  	if props.Geometry != "" {
    64  		components["minecraft:geometry"] = map[string]any{"identifier": props.Geometry}
    65  	} else if props.Cube {
    66  		components["minecraft:unit_cube"] = map[string]any{}
    67  	}
    68  	if props.MapColour != "" {
    69  		components["minecraft:map_color"] = map[string]any{"value": props.MapColour}
    70  	}
    71  	if props.Textures != nil {
    72  		materials := map[string]any{}
    73  		for target, material := range props.Textures {
    74  			materials[target] = material.Encode()
    75  		}
    76  		components["minecraft:material_instances"] = map[string]any{
    77  			"mappings":  map[string]any{},
    78  			"materials": materials,
    79  		}
    80  	}
    81  	transformation := make(map[string]any)
    82  	if props.Rotation != (cube.Pos{}) {
    83  		transformation["RX"] = int32(props.Rotation.X())
    84  		transformation["RY"] = int32(props.Rotation.Y())
    85  		transformation["RZ"] = int32(props.Rotation.Z())
    86  	}
    87  	if props.Translation != (mgl64.Vec3{}) {
    88  		transformation["TX"] = float32(props.Translation.X())
    89  		transformation["TY"] = float32(props.Translation.Y())
    90  		transformation["TZ"] = float32(props.Translation.Z())
    91  	}
    92  	if props.Scale != (mgl64.Vec3{}) {
    93  		transformation["SX"] = float32(props.Scale.X())
    94  		transformation["SY"] = float32(props.Scale.Y())
    95  		transformation["SZ"] = float32(props.Scale.Z())
    96  	} else if len(transformation) > 0 {
    97  		transformation["SX"] = float32(1.0)
    98  		transformation["SY"] = float32(1.0)
    99  		transformation["SZ"] = float32(1.0)
   100  	}
   101  	if len(transformation) > 0 {
   102  		components["minecraft:transformation"] = transformation
   103  	}
   104  	return components
   105  }
   106  
   107  // bboxComponent returns the component data for a bounding box. It translates the coordinates to the origin and size
   108  // format that the client expects.
   109  func bboxComponent(box cube.BBox) map[string]any {
   110  	min, max := box.Min(), box.Max()
   111  	originX, originY, originZ := min.X()*16, min.Y()*16, min.Z()*16
   112  	sizeX, sizeY, sizeZ := (max.X()-min.X())*16, (max.Y()-min.Y())*16, (max.Z()-min.Z())*16
   113  	return map[string]any{
   114  		"enabled": true,
   115  		"origin":  []float32{float32(originX) - 8, float32(originY), float32(originZ) - 8},
   116  		"size":    []float32{float32(sizeX), float32(sizeY), float32(sizeZ)},
   117  	}
   118  }