Files

263 lines
12 KiB
Go
Raw Permalink Normal View History

2026-07-09 08:33:57 +08:00
package world
import (
"image"
"math/rand/v2"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/block/customblock"
)
// Block is a block that may be placed or found in a world. In addition, the block may also be added to an
// inventory: It is also an item.
// Every Block implementation must be able to be hashed as key in a map.
type Block interface {
// EncodeBlock encodes the block to a string ID such as 'minecraft:grass' and properties associated
// with the block.
EncodeBlock() (string, map[string]any)
// Hash returns two different identifiers for the block. The first is the base hash which is unique for
// each type of block at runtime. For vanilla blocks, this is an auto-incrementing constant and for custom
// blocks, you can call block.NextHash() to get a unique identifier. The second is the hash of the block's
// own state and does not need to worry about colliding with other types of blocks. This is later combined
// with the base hash to create a unique identifier for the full block.
Hash() (uint64, uint64)
// Model returns the BlockModel of the Block.
Model() BlockModel
}
// CustomBlock represents a block that is non-vanilla and requires a resource pack and extra steps to show it to the
// client.
type CustomBlock interface {
Block
Properties() customblock.Properties
}
type CustomBlockBuildable interface {
CustomBlock
// Name is the name displayed to clients using the block.
Name() string
// Geometry is the geometries for the block that define the shape of the block. If false is returned, no custom
// geometry will be applied. Permutation-specific geometry can be defined by returning a map of permutations to
// geometry.
Geometry() []byte
// Textures is a map of images indexed by their target, used to map textures on to the block. Permutation-specific
// textures can be defined by returning a map of permutations to textures.
Textures() map[string]image.Image
}
// Liquid represents a block that can be moved through and which can flow in the world after placement. There
// are two liquids in vanilla, which are lava and water.
type Liquid interface {
Block
// LiquidDepth returns the current depth of the liquid.
LiquidDepth() int
// SpreadDecay returns the amount of depth that is subtracted from the liquid's depth when it spreads to
// a next block.
SpreadDecay() int
// WithDepth returns the liquid with the depth passed.
WithDepth(depth int, falling bool) Liquid
// LiquidFalling checks if the liquid is currently considered falling down.
LiquidFalling() bool
// BlastResistance is the blast resistance of the liquid, which influences the liquid's ability to withstand an
// explosive blast.
BlastResistance() float64
// LiquidType returns an int unique for the liquid, used to check if two liquids are considered to be
// of the same type.
LiquidType() string
// Harden checks if the block should harden when looking at the surrounding blocks and sets the position
// to the hardened block when adequate. If the block was hardened, the method returns true.
Harden(pos cube.Pos, tx *Tx, flownIntoBy *cube.Pos) bool
// LiquidRemoveBlock is called when the liquid flows into and removes the block passed.
LiquidRemoveBlock(pos cube.Pos, tx *Tx, removed Block)
}
// Conductor represents a block that can conduct a redstone signal.
type Conductor interface {
Block
// RedstoneSource returns true if the conductor is a signal source.
RedstoneSource() bool
// WeakPower returns the weak power level emitted by this conductor toward a neighbouring receiver.
// The face argument is relative to the receiving block, not this conductor.
// Weak power can pass through a solid block to power redstone components on the other side, but
// cannot power solid blocks themselves or travel further.
// The accountForDust parameter indicates whether redstone dust should be considered when
// calculating power levels.
WeakPower(pos cube.Pos, face cube.Face, tx *Tx, accountForDust bool) int
// StrongPower returns the strong power level emitted by this conductor toward a neighbouring
// receiver. The face argument uses the same convention as WeakPower.
// Strong power can be transmitted through solid blocks. When a solid block receives strong power
// through one of its faces, it can provide weak power to adjacent redstone components on all other
// faces. Strong power can also directly power any redstone component.
// The accountForDust parameter indicates whether redstone dust should be considered when
// calculating power levels.
StrongPower(pos cube.Pos, face cube.Face, tx *Tx, accountForDust bool) int
}
// WeakBlockPowerer represents a conductor whose weak power may weakly power an adjacent conductive block. Weakly
// powered blocks may activate mechanisms and repeaters, but do not power adjacent redstone dust. For example,
// dust pointing into a stone block opens a door on the stone's far side, but a second stretch of dust there stays
// dark.
type WeakBlockPowerer interface {
Conductor
// WeaklyPowersBlocks returns true if this conductor's WeakPower can make an adjacent conductive block weakly powered.
WeaklyPowersBlocks() bool
}
// RedstonePowerRelayer represents a block with custom behaviour for whether
// neighbouring redstone power may be relayed through it by Tx.RedstonePower.
type RedstonePowerRelayer interface {
Block
// RelaysRedstonePowerThrough reports whether this non-conductor block may
// relay neighbouring redstone power through itself to receivers on its other sides.
RelaysRedstonePowerThrough() bool
}
// RedstoneUpdater represents a block that reacts to nearby redstone power changes.
type RedstoneUpdater interface {
Block
// RedstoneUpdate is called when a change in redstone signal is computed.
RedstoneUpdate(pos cube.Pos, tx *Tx)
}
// RegisterBlock registers the Block passed in the DefaultBlockRegistry.
//
// This function exists for backwards compatibility and works well for the common "single server per process" setup,
// where all worlds share the global default registry.
//
// If you run multiple servers/registries in a single process, prefer creating a registry using NewBlockRegistry() and
// registering blocks on that instance (e.g. conf.Blocks.RegisterBlock(...)) before calling Finalize().
func RegisterBlock(b Block) {
DefaultBlockRegistry.RegisterBlock(b)
}
// BlockHash returns a unique identifier of the block including the block states using the DefaultBlockRegistry.
// This function is used internally to convert a block to a single integer which can be used in map lookups. The hash
// produced therefore does not need to match anything in the game, but it must be unique among all registered blocks.
// The tool in `/cmd/blockhash` may be used to automatically generate block hashes of blocks in a package.
//
// If you use a non-default registry (NewBlockRegistry), use your registry instance's BlockHash(...) instead so the
// hash is consistent with that registry.
func BlockHash(b Block) uint64 {
return DefaultBlockRegistry.BlockHash(b)
}
// BlockRuntimeID attempts to return a runtime ID of a block previously registered using RegisterBlock() on the
// DefaultBlockRegistry.
// If the runtime ID cannot be found because the Block wasn't registered, BlockRuntimeID will panic.
// If you use a non-default registry (NewBlockRegistry), use your registry instance's BlockRuntimeID(...) instead.
func BlockRuntimeID(b Block) uint32 {
return DefaultBlockRegistry.BlockRuntimeID(b)
}
// BlockByRuntimeID attempts to return a Block by its runtime ID using the DefaultBlockRegistry. If not found, the bool
// returned is false. If found, the block is non-nil and the bool true.
// If you use a non-default registry (NewBlockRegistry), use your registry instance's BlockByRuntimeID(...) instead.
func BlockByRuntimeID(rid uint32) (Block, bool) {
return DefaultBlockRegistry.BlockByRuntimeID(rid)
}
// BlockByName attempts to return a Block by its name and properties using the DefaultBlockRegistry. If not found, the
// bool returned is false.
// If you use a non-default registry (NewBlockRegistry), use your registry instance's BlockByName(...) instead.
func BlockByName(name string, properties map[string]any) (Block, bool) {
return DefaultBlockRegistry.BlockByName(name, properties)
}
// Blocks returns a slice of all blocks registered in the DefaultBlockRegistry.
// If you use a non-default registry (NewBlockRegistry), use your registry instance's Blocks() instead.
func Blocks() []Block {
return DefaultBlockRegistry.Blocks()
}
// CustomBlocks returns a map of all custom blocks registered with their names as keys in the DefaultBlockRegistry.
// If you use a non-default registry (NewBlockRegistry), use your registry instance's CustomBlocks() instead.
func CustomBlocks() map[string]CustomBlock {
return DefaultBlockRegistry.CustomBlocks()
}
// RandomTicker represents a block that executes an action when it is ticked randomly. Every 20th of a second,
// one random block in each sub chunk are picked to receive a random tick.
type RandomTicker interface {
// RandomTick handles a random tick of the block at the position passed. Additionally, a rand.RandSource
// instance is passed which may be used to generate values randomly without locking.
RandomTick(pos cube.Pos, tx *Tx, r *rand.Rand)
}
// ScheduledTicker represents a block that executes an action when it has a block update scheduled, such as
// when a block adjacent to it is broken.
type ScheduledTicker interface {
// ScheduledTick handles a scheduled tick initiated by an event in one of the neighbouring blocks, such as
// when a block is placed or broken. Additionally, a rand.RandSource instance is passed which may be used to
// generate values randomly without locking.
ScheduledTick(pos cube.Pos, tx *Tx, r *rand.Rand)
}
// TickerBlock is an implementation of NBTer with an additional Tick method that is called on every world
// tick for loaded blocks that implement this interface.
type TickerBlock interface {
NBTer
Tick(currentTick int64, pos cube.Pos, tx *Tx)
}
// NeighbourUpdateTicker represents a block that is updated when a block adjacent to it is updated, either
// through placement or being broken.
type NeighbourUpdateTicker interface {
// NeighbourUpdateTick handles a neighbouring block being updated. The position of that block and the
// position of this block is passed.
NeighbourUpdateTick(pos, changedNeighbour cube.Pos, tx *Tx)
}
// NBTer represents either an item or a block which may decode NBT data and encode to NBT data. Typically,
// this is done to store additional data.
type NBTer interface {
// DecodeNBT returns the (new) item, block or Entity, depending on which of those the NBTer was, with the NBT data
// decoded into it.
DecodeNBT(data map[string]any) any
// EncodeNBT encodes the Entity into a map which can then be encoded as NBT to be written.
EncodeNBT() map[string]any
}
// LiquidDisplacer represents a block that is able to displace a liquid to a different world layer, without
// fully removing the liquid.
type LiquidDisplacer interface {
// CanDisplace specifies if the block is able to displace the liquid passed.
CanDisplace(b Liquid) bool
// SideClosed checks if a position on the side of the block placed in the world at a specific position is
// closed. When this returns true (for example, when the side is below the position and the block is a
// slab), liquid inside the displacer won't flow from pos into side.
SideClosed(pos, side cube.Pos, tx *Tx) bool
}
// lightEmitter is identical to a block.LightEmitter.
type lightEmitter interface {
LightEmissionLevel() uint8
}
// lightDiffuser is identical to a block.LightDiffuser.
type lightDiffuser interface {
LightDiffusionLevel() uint8
}
// replaceableBlock represents a block that may be replaced by another block automatically. An example is
// grass, which may be replaced by clicking it with another block.
type replaceableBlock interface {
// ReplaceableBy returns a bool which indicates if the block is replaceable by another block.
ReplaceableBy(b Block) bool
}
// replaceable checks if the block at the position passed is replaceable with the block passed.
func replaceable(w *World, c *Column, pos cube.Pos, with Block) bool {
if r, ok := w.blockInChunk(c, pos).(replaceableBlock); ok {
return r.ReplaceableBy(with)
}
return false
}
// BlockAction represents an action that may be performed by a block. Typically, these actions are sent to
// viewers in a world so that they can see these actions.
type BlockAction interface {
BlockAction()
}