block state
This commit is contained in:
+117
-10
@@ -1,5 +1,9 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// {
|
||||
// "id": 0,
|
||||
// "name": "air",
|
||||
@@ -35,29 +39,32 @@ type Block struct {
|
||||
DefaultState int32
|
||||
MinStateId int32
|
||||
MaxStateId int32
|
||||
States []struct {
|
||||
Name string
|
||||
Type string
|
||||
NumValues int
|
||||
}
|
||||
States []BlockState
|
||||
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
|
||||
if ret, ok = BlocksCache[v]; ok {
|
||||
return
|
||||
}
|
||||
var b []Block
|
||||
var b []*Block
|
||||
must(LoadVersionedJson(v, "blocks", &b))
|
||||
BlocksCache[v] = 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) {
|
||||
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
|
||||
block = b
|
||||
@@ -67,7 +74,7 @@ func LookupBlockByStateId(version string, stateId int32) (block Block, ok bool)
|
||||
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) {
|
||||
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
|
||||
block = b
|
||||
@@ -76,3 +83,103 @@ func LookupBlockByName(version string, stateId int32) (block Block, ok bool) {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
var StatusServedError = errors.New("status served")
|
||||
var StatusServedErr = errors.New("status served")
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (l *LoginServer) OnStatusPing(packet *v1_21_8.StatusToServerPacketPing) (er
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return event.HandlerDoneErr{Return: StatusServedError}
|
||||
return event.HandlerDoneErr{Return: StatusServedErr}
|
||||
}
|
||||
|
||||
func (l *LoginServer) SetCompressionThreshold(threshold int32) (err error) {
|
||||
|
||||
+14
-7
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net"
|
||||
|
||||
@@ -21,13 +22,24 @@ func Servee(c net.Conn) (ret *proto.Conn) {
|
||||
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)
|
||||
defer c.Close()
|
||||
h, err := factory(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -42,12 +54,7 @@ func StartServerWithFactory(listenAddr string, factory func(c *proto.Conn) (h an
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
err := ServeClient(cNet, factory)
|
||||
if err != nil {
|
||||
slog.Error("error while serving client", "error", err, "client", cNet.RemoteAddr())
|
||||
}
|
||||
}()
|
||||
go ServeClient(cNet, factory)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/admin-else/strom/client"
|
||||
"github.com/admin-else/strom/data"
|
||||
"github.com/admin-else/strom/event"
|
||||
"github.com/admin-else/strom/proto"
|
||||
"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) {
|
||||
//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 {
|
||||
return
|
||||
}
|
||||
slog.Info("player logged in", "name", playerData.Requested.Name)
|
||||
err = server.ServeConfig(s.Conn)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -124,7 +126,7 @@ func (s *Server) OnStart(_ event.OnStart) (err error) {
|
||||
return
|
||||
}
|
||||
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
|
||||
return
|
||||
}
|
||||
@@ -135,6 +137,12 @@ func (s *Server) OnTick(_ event.Tick) (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{
|
||||
Location: v1_21_8.Position{
|
||||
X: 1,
|
||||
@@ -159,9 +167,9 @@ func (s *Server) SendKeepAlive() (err error) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
slog.SetLogLoggerLevel(slog.LevelInfo)
|
||||
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{}
|
||||
h = s
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user