func BlockByName(name string, properties map[string]interface{}) (Block, bool) {
	rid, ok := stateRuntimeIDs[stateHash{name: name, properties: hashProperties(properties)}]
	if !ok {
		return nil, false
	}
	return blocks[rid], true
}
BlockByName attempts to return a Block by its name and properties. If not found, the bool returned is
false.
package world
import (
	"fmt"
	"github.com/brentp/intintmap"
	"github.com/df-mc/dragonfly/server/block/cube"
	"github.com/df-mc/dragonfly/server/world/chunk"
	"math"
	"math/rand"
)
type Block interface {
	EncodeBlock() (string, map[string]interface{})
	Hash() uint64
	Model() BlockModel
}
type Liquid interface {
	Block
	LiquidDepth() int
	SpreadDecay() int
	WithDepth(depth int, falling bool) Liquid
	LiquidFalling() bool
	LiquidType() string
	Harden(pos cube.Pos, w *World, flownIntoBy *cube.Pos) bool
}
var hashes = intintmap.New(7000, 0.999)
func RegisterBlock(b Block) {
	name, properties := b.EncodeBlock()
	h := stateHash{name: name, properties: hashProperties(properties)}
	rid, ok := stateRuntimeIDs[h]
	if !ok {
		panic(fmt.Sprintf("block state returned is not registered (%v {%#v})", name, properties))
	}
	if _, ok := blocks[rid].(unknownBlock); !ok {
		panic(fmt.Sprintf("block with name and properties %v {%#v} already registered", name, properties))
	}
	hash := int64(b.Hash())
	if _, ok := hashes.Get(hash); ok {
		panic(fmt.Sprintf("block %#v with hash %v already registered", b, hash))
	}
	blocks[rid] = b
	hashes.Put(hash, int64(rid))
	if diffuser, ok := b.(lightDiffuser); ok {
		chunk.FilteringBlocks[rid] = diffuser.LightDiffusionLevel()
	}
	if emitter, ok := b.(lightEmitter); ok {
		chunk.LightBlocks[rid] = emitter.LightEmissionLevel()
	}
	if _, ok := b.(NBTer); ok {
		nbtBlocks[rid] = true
	}
	if _, ok := b.(RandomTicker); ok {
		randomTickBlocks[rid] = true
	}
}
func BlockRuntimeID(b Block) (uint32, bool) {
	if b == nil {
		return airRID, true
	}
	if h := b.Hash(); h != math.MaxUint64 {
		rid, ok := hashes.Get(int64(h))
		if !ok {
			panic(fmt.Sprintf("cannot find block by non-0 hash of block %#v", b))
		}
		return uint32(rid), ok
	}
	return slowBlockRuntimeID(b)
}
func slowBlockRuntimeID(b Block) (uint32, bool) {
	name, properties := b.EncodeBlock()
	rid, ok := stateRuntimeIDs[stateHash{name: name, properties: hashProperties(properties)}]
	return rid, ok
}
func BlockByRuntimeID(rid uint32) (Block, bool) {
	if rid >= uint32(len(blocks)) {
		return air(), false
	}
	return blocks[rid], true
}