block state
This commit is contained in:
+120
-13
@@ -1,6 +1,10 @@
|
|||||||
package data
|
package data
|
||||||
|
|
||||||
// {
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// {
|
||||||
// "id": 0,
|
// "id": 0,
|
||||||
// "name": "air",
|
// "name": "air",
|
||||||
// "displayName": "Air",
|
// "displayName": "Air",
|
||||||
@@ -35,29 +39,32 @@ type Block struct {
|
|||||||
DefaultState int32
|
DefaultState int32
|
||||||
MinStateId int32
|
MinStateId int32
|
||||||
MaxStateId int32
|
MaxStateId int32
|
||||||
States []struct {
|
States []BlockState
|
||||||
Name string
|
Drops []int
|
||||||
Type string
|
BoundingBox string
|
||||||
NumValues int
|
|
||||||
}
|
|
||||||
Drops []int
|
|
||||||
BoundingBox string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var BlocksCache = make(map[string][]Block)
|
type BlockState struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
NumValues int32 `json:"num_values"`
|
||||||
|
Values []string
|
||||||
|
}
|
||||||
|
|
||||||
func BlocksForVersion(v string) (ret []Block) {
|
var BlocksCache = make(map[string][]*Block)
|
||||||
|
|
||||||
|
func BlocksForVersion(v string) (ret []*Block) {
|
||||||
var ok bool
|
var ok bool
|
||||||
if ret, ok = BlocksCache[v]; ok {
|
if ret, ok = BlocksCache[v]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var b []Block
|
var b []*Block
|
||||||
must(LoadVersionedJson(v, "blocks", &b))
|
must(LoadVersionedJson(v, "blocks", &b))
|
||||||
BlocksCache[v] = b
|
BlocksCache[v] = b
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func LookupBlockByStateId(version string, stateId int32) (block Block, ok bool) {
|
func LookupBlockByStateId(version string, stateId int32) (block *Block, ok bool) {
|
||||||
for _, b := range BlocksForVersion(version) {
|
for _, b := range BlocksForVersion(version) {
|
||||||
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
|
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
|
||||||
block = b
|
block = b
|
||||||
@@ -67,7 +74,7 @@ func LookupBlockByStateId(version string, stateId int32) (block Block, ok bool)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func LookupBlockByName(version string, stateId int32) (block Block, ok bool) {
|
func LookupBlockByName(version string, stateId int32) (block *Block, ok bool) {
|
||||||
for _, b := range BlocksForVersion(version) {
|
for _, b := range BlocksForVersion(version) {
|
||||||
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
|
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
|
||||||
block = b
|
block = b
|
||||||
@@ -76,3 +83,103 @@ func LookupBlockByName(version string, stateId int32) (block Block, ok bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var BlockStateOutOfRangeErr = errors.New("stateId out of range")
|
||||||
|
var BlockNotFoundErr = errors.New("block not found")
|
||||||
|
var InvalidBoolValueErr = errors.New("invalid bool value")
|
||||||
|
var BadBlockStateTypeErr = errors.New("bad block state type")
|
||||||
|
var BadEnumErr = errors.New("bad enum")
|
||||||
|
var SupplyAllStatesErr = errors.New("supply all states")
|
||||||
|
|
||||||
|
func FromBlockState(version string, stateId int32) (b *Block, stateData map[string]any, err error) {
|
||||||
|
b, ok := LookupBlockByStateId(version, stateId)
|
||||||
|
if !ok {
|
||||||
|
err = BlockNotFoundErr
|
||||||
|
}
|
||||||
|
stateData, err = b.StateMapFromId(stateId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mcDataBlockStateTypeParse(n int32, s *BlockState) (v any, err error) {
|
||||||
|
switch s.Type {
|
||||||
|
case "int":
|
||||||
|
v = n
|
||||||
|
case "enum":
|
||||||
|
v = s.Values[n]
|
||||||
|
case "bool":
|
||||||
|
if n == 0 { // MOJANK: 0 is true, 1 is false
|
||||||
|
v = true
|
||||||
|
} else if n == 1 {
|
||||||
|
v = false
|
||||||
|
} else {
|
||||||
|
err = InvalidBoolValueErr
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = BadBlockStateTypeErr
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mcDataBlockStateTypeSerialize(v any, s *BlockState) (n int32, err error) {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case int32:
|
||||||
|
n = t
|
||||||
|
case bool:
|
||||||
|
if t {
|
||||||
|
n = 0
|
||||||
|
} else {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
for i, enumValue := range s.Values {
|
||||||
|
if enumValue == t {
|
||||||
|
return int32(i), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = BadEnumErr
|
||||||
|
default:
|
||||||
|
err = BadBlockStateTypeErr
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Block) StateMapFromId(stateId int32) (ret map[string]any, err error) {
|
||||||
|
if stateId < b.MinStateId || stateId > b.MaxStateId {
|
||||||
|
err = BlockStateOutOfRangeErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = make(map[string]any)
|
||||||
|
stateId -= b.MinStateId
|
||||||
|
|
||||||
|
a := int32(1)
|
||||||
|
for i := range len(b.States) {
|
||||||
|
s := b.States[len(b.States)-1-i]
|
||||||
|
ret[s.Name], err = mcDataBlockStateTypeParse((stateId/a)%s.NumValues, &s)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a *= s.NumValues
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Block) IdFromStateMap(m map[string]any) (ret int32, err error) {
|
||||||
|
a := int32(1)
|
||||||
|
for i := range len(b.States) {
|
||||||
|
s := b.States[len(b.States)-1-i]
|
||||||
|
v, found := m[s.Name]
|
||||||
|
if !found {
|
||||||
|
err = SupplyAllStatesErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var n int32
|
||||||
|
n, err = mcDataBlockStateTypeSerialize(v, &s)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret += n * a
|
||||||
|
a *= s.NumValues
|
||||||
|
}
|
||||||
|
ret += b.MinStateId
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,3 +14,21 @@ func TestLookupBlockByStateId(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fmt.Println(b)
|
fmt.Println(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockStates(t *testing.T) {
|
||||||
|
var stateId int32 = 8301
|
||||||
|
b, stateMap, err := data.FromBlockState("1.21.8", stateId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("Block state map: %v, block: %v", stateMap, b.Name)
|
||||||
|
stateId2, err := b.IdFromStateMap(stateMap)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if stateId2 != stateId {
|
||||||
|
t.Fatal("state id mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
var StatusServedError = errors.New("status served")
|
var StatusServedErr = errors.New("status served")
|
||||||
|
|
||||||
var UnexpectedStatusRequest = UnexpectedNextStateError{proto_base.Status}
|
var UnexpectedStatusRequest = UnexpectedNextStateError{proto_base.Status}
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ func (l *LoginServer) OnHandshake(packet *v1_21_8.HandshakingToServerPacketSetPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(l.CompatibleVersions) != 0 && !slices.Contains(l.CompatibleVersions, packet.ProtocolVersion) {
|
if len(l.CompatibleVersions) != 0 && !slices.Contains(l.CompatibleVersions, packet.ProtocolVersion) {
|
||||||
err = fmt.Errorf("incompatible protocol version please use one of %v you can look up the real coresponding version at https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol_version_numbers", l.CompatibleVersions)
|
err = fmt.Errorf("incompatible protocol version please use one of %v you can look up the real corresponding version at https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol_version_numbers", l.CompatibleVersions)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ func (l *LoginServer) OnStatusPing(packet *v1_21_8.StatusToServerPacketPing) (er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return event.HandlerDoneErr{Return: StatusServedError}
|
return event.HandlerDoneErr{Return: StatusServedErr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LoginServer) SetCompressionThreshold(threshold int32) (err error) {
|
func (l *LoginServer) SetCompressionThreshold(threshold int32) (err error) {
|
||||||
|
|||||||
+14
-7
@@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
@@ -21,13 +22,24 @@ func Servee(c net.Conn) (ret *proto.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServeClient(cNet net.Conn, factory func(c *proto.Conn) (h any, err error)) (err error) {
|
func ServeClient(cNet net.Conn, factory func(c *proto.Conn) (h any, err error)) {
|
||||||
c := Servee(cNet)
|
c := Servee(cNet)
|
||||||
|
defer c.Close()
|
||||||
h, err := factory(c)
|
h, err := factory(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = c.Start(h)
|
err = c.Start(h)
|
||||||
|
if errors.Is(err, StatusServedErr) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Error while handling client", "error", err, "client", c.Conn.RemoteAddr())
|
||||||
|
err = Kick(c, text.Pretty(err.Error()))
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Error while kicking client", "error", err, "client", c.Conn.RemoteAddr())
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +54,7 @@ func StartServerWithFactory(listenAddr string, factory func(c *proto.Conn) (h an
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func() {
|
go ServeClient(cNet, factory)
|
||||||
err := ServeClient(cNet, factory)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("error while serving client", "error", err, "client", cNet.RemoteAddr())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/admin-else/strom/client"
|
"github.com/admin-else/strom/client"
|
||||||
|
"github.com/admin-else/strom/data"
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/event"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/proto"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||||
@@ -67,10 +68,11 @@ var voidChunk = &v1_21_8.PlayToClientPacketMapChunk{X: 3, Z: 3, Heightmaps: []st
|
|||||||
|
|
||||||
func (s *Server) OnStart(_ event.OnStart) (err error) {
|
func (s *Server) OnStart(_ event.OnStart) (err error) {
|
||||||
//goland:noinspection GoResourceLeak captures the conn object which should NOT be closed
|
//goland:noinspection GoResourceLeak captures the conn object which should NOT be closed
|
||||||
_, err = server.ServeLogin(s.Conn, server.WithRawStatus(StatusResponse))
|
playerData, err := server.ServeLogin(s.Conn, server.WithRawStatus(StatusResponse), server.WithCompatibleVersions(772))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
slog.Info("player logged in", "name", playerData.Requested.Name)
|
||||||
err = server.ServeConfig(s.Conn)
|
err = server.ServeConfig(s.Conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -124,7 +126,7 @@ func (s *Server) OnStart(_ event.OnStart) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Timer.Every(time.Second*15, s.SendKeepAlive)
|
s.Timer.Every(time.Second*15, s.SendKeepAlive)
|
||||||
s.Timer.Every(time.Second, s.CycleBlockForTesting)
|
s.Timer.Every(time.Second*20, s.CycleBlockForTesting)
|
||||||
s.Timer.Active = true
|
s.Timer.Active = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -135,6 +137,12 @@ func (s *Server) OnTick(_ event.Tick) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) CycleBlockForTesting() (err error) {
|
func (s *Server) CycleBlockForTesting() (err error) {
|
||||||
|
_, stateData, err := data.FromBlockState(s.Version, s.Current)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.Info("cycle block", "state", stateData, "cycle", s.Current)
|
||||||
|
|
||||||
err = s.Send(&v1_21_8.PlayToClientPacketBlockChange{
|
err = s.Send(&v1_21_8.PlayToClientPacketBlockChange{
|
||||||
Location: v1_21_8.Position{
|
Location: v1_21_8.Position{
|
||||||
X: 1,
|
X: 1,
|
||||||
@@ -159,9 +167,9 @@ func (s *Server) SendKeepAlive() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
slog.SetLogLoggerLevel(slog.LevelInfo)
|
||||||
err := server.StartServerWithFactory(":25565", func(c *proto.Conn) (h any, err error) {
|
err := server.StartServerWithFactory(":25565", func(c *proto.Conn) (h any, err error) {
|
||||||
s := &Server{Conn: c, MinCycle: 2938, MaxCycle: 3017, Current: 2938}
|
s := &Server{Conn: c, MinCycle: 8297, MaxCycle: 8304, Current: 8297}
|
||||||
s.Timer = &client.Timer{}
|
s.Timer = &client.Timer{}
|
||||||
h = s
|
h = s
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user