package blockinternal import ( "github.com/df-mc/dragonfly/server/item/category" "maps" "slices" ) // ComponentBuilder represents a builder that can be used to construct a block components map to be sent to a client. type ComponentBuilder struct { permutations map[string]map[string]any properties []map[string]any components map[string]any blockID int32 identifier string menuCategory category.Category } // NewComponentBuilder returns a new component builder with the provided block data, using the provided components map // as a base. func NewComponentBuilder(identifier string, components map[string]any, blockID int32) *ComponentBuilder { if components == nil { components = map[string]any{} } return &ComponentBuilder{ permutations: make(map[string]map[string]any), components: components, blockID: blockID, identifier: identifier, menuCategory: category.Construction(), } } // AddProperty adds the provided block property to the builder. func (builder *ComponentBuilder) AddProperty(name string, values []any) { builder.properties = append(builder.properties, map[string]any{ "name": name, "enum": values, }) } // AddComponent adds the provided component to the builder. If the component already exists, it will be overwritten. func (builder *ComponentBuilder) AddComponent(name string, value any) { builder.components[name] = value } // AddPermutation adds a permutation to the builder. If there is already an existing permutation for the provided // condition, the new components will be added to the existing permutation. func (builder *ComponentBuilder) AddPermutation(condition string, components map[string]any) { if len(builder.permutations) == 0 { // This trigger really does not matter at all, the component just needs to be set for custom block placements to // function as expected client-side, when permutations are applied. builder.AddComponent("minecraft:on_player_placing", map[string]any{ "triggerType": "placement_trigger", }) } if builder.permutations[condition] == nil { builder.permutations[condition] = map[string]any{} } for key, value := range components { builder.permutations[condition][key] = value } } // SetMenuCategory sets the creative category for the current block. func (builder *ComponentBuilder) SetMenuCategory(category category.Category) { builder.menuCategory = category } // Construct constructs the final block components map that is ready to be sent to the client. func (builder *ComponentBuilder) Construct() map[string]any { properties := slices.Clone(builder.properties) components := maps.Clone(builder.components) result := map[string]any{ "components": components, "molangVersion": int32(10), "menu_category": map[string]any{ "category": builder.menuCategory.String(), "group": builder.menuCategory.Group(), }, "vanilla_block_data": map[string]any{ "block_id": builder.blockID, }, } if len(properties) > 0 { result["properties"] = properties } permutations := maps.Clone(builder.permutations) if len(permutations) > 0 { result["permutations"] = []map[string]any{} for condition, values := range permutations { result["permutations"] = append(result["permutations"].([]map[string]any), map[string]any{ "condition": condition, "components": values, }) } } return result }