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
	}
}
RegisterBlock registers the Block passed. The EncodeBlock method will be used to encode and decode the
block passed. RegisterBlock panics if the block properties returned were not valid, existing properties.
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)