26ed99fda6
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
263 lines
12 KiB
Go
263 lines
12 KiB
Go
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()
|
|
}
|