look at the code if you want to know what i did

This commit is contained in:
adman
2026-03-22 00:35:07 +01:00
parent a6102b7ec9
commit ea024fe808
21 changed files with 294 additions and 78 deletions
+2
View File
@@ -3,6 +3,7 @@ package client
import ( import (
"net" "net"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/proto" "github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base" "github.com/admin-else/strom/proto_base"
) )
@@ -30,6 +31,7 @@ func ConnectVersionLess(addr string) (ret *proto.Conn, err error) {
ret.SetState(proto_base.Handshaking) ret.SetState(proto_base.Handshaking)
ret.SetCompressionThreshold(-1) ret.SetCompressionThreshold(-1)
ret.Actor = proto_base.Client ret.Actor = proto_base.Client
ret.Loop = event.NewLoop()
ret.Conn, err = net.Dial("tcp", addr) ret.Conn, err = net.Dial("tcp", addr)
if err != nil { if err != nil {
return return
+6 -3
View File
@@ -19,8 +19,11 @@ func (c *ConfigIgnorer) OnStart() (err error) {
return return
} }
func (c *ConfigIgnorer) Default(event event.Anything) (err error) { func (c *ConfigIgnorer) Default(e event.Anything) (err error) {
slog.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", event)) if e.Val == (event.Tick{}) {
return
}
slog.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", e))
return return
} }
@@ -56,7 +59,7 @@ func IgnoreConfig(c *proto.Conn) (err error) {
ci.RegisterUntilLatest(ci.OnPing) ci.RegisterUntilLatest(ci.OnPing)
ci.RegisterUntilLatest(ci.OnKeepAlive) ci.RegisterUntilLatest(ci.OnKeepAlive)
err = ci.Start() err = ci.StartConn()
if err != nil { if err != nil {
err = errors.Join(err, errors.New("failed to ignore config")) err = errors.Join(err, errors.New("failed to ignore config"))
} }
+1 -1
View File
@@ -106,7 +106,7 @@ func DoDnsSimple(in string) (out string, err error) {
if err != nil { if err != nil {
return return
} }
out = in out = host + ":" + strconv.Itoa(int(portN))
if portN != 25565 { if portN != 25565 {
return return
} }
+1 -4
View File
@@ -152,7 +152,7 @@ func Login(c *proto.Conn, account *api.Account) (err error) {
lc.SetState(proto_base.Login) lc.SetState(proto_base.Login)
err = lc.Send(&v1_21_8.LoginToServerPacketLoginStart{Username: lc.Account.Name, PlayerUUID: lc.Account.Uuid}) err = lc.Send(&v1_21_8.LoginToServerPacketLoginStart{Username: lc.Account.Name, PlayerUUID: lc.Account.Uuid})
err = lc.Start() err = lc.StartConn()
if err != nil { if err != nil {
err = errors.Join(FailedToLoginErr, err) err = errors.Join(FailedToLoginErr, err)
} }
@@ -165,8 +165,5 @@ func ConnectAndLogin(connectTo string, account *api.Account) (c *proto.Conn, err
return return
} }
err = Login(c, account) err = Login(c, account)
if err != nil {
err = errors.Join(FailedToLoginErr, err)
}
return return
} }
+2 -7
View File
@@ -19,10 +19,6 @@ type StatusClient struct {
DoPingRoundTripTime bool DoPingRoundTripTime bool
} }
func (s *StatusClient) OnStart() (err error) {
return
}
func (s *StatusClient) OnStatus(p *v1_21_8.StatusToClientPacketServerInfo) (err error) { func (s *StatusClient) OnStatus(p *v1_21_8.StatusToClientPacketServerInfo) (err error) {
s.Status = p.Response s.Status = p.Response
s.PingSendTime = time.Now() s.PingSendTime = time.Now()
@@ -55,9 +51,8 @@ func StatusRaw(addr string) (s *StatusClient, err error) {
s = &StatusClient{ s = &StatusClient{
Conn: c, Conn: c,
} }
s.Loop = event.NewLoop() s.RegisterCriticalUntilLatest(s.OnStatus)
s.RegisterUntilLatest(s.OnStatus) s.RegisterCriticalUntilLatest(s.OnPong)
s.RegisterUntilLatest(s.OnPong)
p, err := MakeHandshakePacketAddr(s.Conn, proto_base.Status, addr) p, err := MakeHandshakePacketAddr(s.Conn, proto_base.Status, addr)
if err != nil { if err != nil {
+32 -26
View File
@@ -2,6 +2,7 @@ package data
import ( import (
"errors" "errors"
"strconv"
) )
// { // {
@@ -75,9 +76,9 @@ 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, name string) (block *Block, ok bool) {
for _, b := range BlocksForVersion(version) { for _, b := range BlocksForVersion(version) {
if b.MinStateId <= stateId && stateId <= b.MaxStateId { if b.Name == name {
block = b block = b
ok = true ok = true
} }
@@ -85,6 +86,16 @@ func LookupBlockByName(version string, stateId int32) (block *Block, ok bool) {
return return
} }
func StateIdFromBlocKAndStateMap(version string, name string, stateMap map[string]string) (stateId int32, err error) {
b, ok := LookupBlockByName(version, name)
if !ok {
err = BlockNotFoundErr
return
}
stateId, err = b.IdFromStateMap(stateMap)
return
}
var BlockStateOutOfRangeErr = errors.New("stateId out of range") var BlockStateOutOfRangeErr = errors.New("stateId out of range")
var BlockNotFoundErr = errors.New("block not found") var BlockNotFoundErr = errors.New("block not found")
var InvalidBoolValueErr = errors.New("invalid bool value") var InvalidBoolValueErr = errors.New("invalid bool value")
@@ -92,7 +103,7 @@ var BadBlockStateTypeErr = errors.New("bad block state type")
var BadEnumErr = errors.New("bad enum") var BadEnumErr = errors.New("bad enum")
var SupplyAllStatesErr = errors.New("supply all states") var SupplyAllStatesErr = errors.New("supply all states")
func FromBlockState(version string, stateId int32) (b *Block, stateData map[string]any, err error) { func FromBlockState(version string, stateId int32) (b *Block, stateData map[string]string, err error) {
b, ok := LookupBlockByStateId(version, stateId) b, ok := LookupBlockByStateId(version, stateId)
if !ok { if !ok {
err = BlockNotFoundErr err = BlockNotFoundErr
@@ -101,19 +112,17 @@ func FromBlockState(version string, stateId int32) (b *Block, stateData map[stri
return return
} }
// TODO: mc stores these as strings so we will do the same func mcDataBlockStateTypeParse(n int32, s *BlockState) (v string, err error) {
func mcDataBlockStateTypeParse(n int32, s *BlockState) (v any, err error) {
switch s.Type { switch s.Type {
case "int": case "int":
v = n v = strconv.Itoa(int(n))
case "enum": case "enum":
v = s.Values[n] v = s.Values[n]
case "bool": case "bool":
if n == 0 { // MOJANK: 0 is true, 1 is false if n == 0 { // MOJANK: 0 is true, 1 is false
v = true v = "true"
} else if n == 1 { } else if n == 1 {
v = false v = "false"
} else { } else {
err = InvalidBoolValueErr err = InvalidBoolValueErr
} }
@@ -123,35 +132,32 @@ func mcDataBlockStateTypeParse(n int32, s *BlockState) (v any, err error) {
return return
} }
func mcDataBlockStateTypeSerialize(v any, s *BlockState) (n int32, err error) { func mcDataBlockStateTypeSerialize(v string, s *BlockState) (n int32, err error) {
switch t := v.(type) { if v == "true" {
case int32: return 0, nil
n = t }
case bool: if v == "false" {
if t { return 1, nil
n = 0 }
} else { n1, errMabye := strconv.Atoi(v)
n = 1 if errMabye == nil {
return int32(n1), nil
} }
case string:
for i, enumValue := range s.Values { for i, enumValue := range s.Values {
if enumValue == t { if enumValue == v {
return int32(i), nil return int32(i), nil
} }
} }
err = BadEnumErr err = BadEnumErr
default:
err = BadBlockStateTypeErr
}
return return
} }
func (b *Block) StateMapFromId(stateId int32) (ret map[string]any, err error) { func (b *Block) StateMapFromId(stateId int32) (ret map[string]string, err error) {
if stateId < b.MinStateId || stateId > b.MaxStateId { if stateId < b.MinStateId || stateId > b.MaxStateId {
err = BlockStateOutOfRangeErr err = BlockStateOutOfRangeErr
return return
} }
ret = make(map[string]any) ret = make(map[string]string)
stateId -= b.MinStateId stateId -= b.MinStateId
a := int32(1) a := int32(1)
@@ -166,7 +172,7 @@ func (b *Block) StateMapFromId(stateId int32) (ret map[string]any, err error) {
return return
} }
func (b *Block) IdFromStateMap(m map[string]any) (ret int32, err error) { func (b *Block) IdFromStateMap(m map[string]string) (ret int32, err error) {
a := int32(1) a := int32(1)
for i := range len(b.States) { for i := range len(b.States) {
s := b.States[len(b.States)-1-i] s := b.States[len(b.States)-1-i]
+11 -1
View File
@@ -12,7 +12,7 @@ package data
type VersionInfo struct { type VersionInfo struct {
MinecraftVersion string MinecraftVersion string
Version int32 Version int32
DataVersion int DataVersion int32
UsesNetty bool UsesNetty bool
MajorVersion string MajorVersion string
ReleaseType string ReleaseType string
@@ -42,6 +42,16 @@ func LookUpVersionByProtocolVersion(version int32) (ret VersionInfo, err error)
return return
} }
func LookUpVersionByDataVersion(version int32) (ret VersionInfo, err error) {
for _, v := range ProtocolVersions {
if v.DataVersion == version {
ret = v
}
}
err = UnknownMinecraftVersionError
return
}
func init() { func init() {
must(LoadJson("minecraft-data/data/pc/common/protocolVersions.json", &ProtocolVersions)) must(LoadJson("minecraft-data/data/pc/common/protocolVersions.json", &ProtocolVersions))
} }
+12 -5
View File
@@ -53,10 +53,13 @@ func (e HandlerDoneErr) Unwrap() error {
return e.Return return e.Return
} }
// ErrDontForward will stop the event from being forwarded to other handlers, including anything handlers. // DontForwardErr will stop the event from being forwarded to other handlers, including anything handlers.
var ErrDontForward = errors.New("dont forward") var DontForwardErr = errors.New("dont forward")
var WhileClosingErr = errors.New("error while closing") var WhileClosingErr = errors.New("error while closing")
// CloseNonCriticalErr will close even when the event is non-critical
var CloseNonCriticalErr = errors.New("close non-critical")
func ValidateHandler(h any) (eventType reflect.Type, hv reflect.Value) { func ValidateHandler(h any) (eventType reflect.Type, hv reflect.Value) {
hv = reflect.ValueOf(h) hv = reflect.ValueOf(h)
if hv.Kind() != reflect.Func { if hv.Kind() != reflect.Func {
@@ -86,7 +89,11 @@ func (l *Loop) RegisterCustomType(eventType reflect.Type, hv reflect.Value) {
handleFunc := func(packet any) error { handleFunc := func(packet any) error {
v := hv.Call([]reflect.Value{reflect.ValueOf(packet)})[0] v := hv.Call([]reflect.Value{reflect.ValueOf(packet)})[0]
if !v.IsNil() { if !v.IsNil() {
slog.Error("handler failed", "err", v.Interface().(error)) err := v.Interface().(error)
if errors.Is(err, CloseNonCriticalErr) {
return err
}
slog.Error("handler failed", "err", err)
} }
return nil return nil
} }
@@ -124,7 +131,7 @@ func (l *Loop) FireFound(event any) (found bool, err error) {
func (l *Loop) Fire(event any) (err error) { func (l *Loop) Fire(event any) (err error) {
found, err := l.FireFound(event) found, err := l.FireFound(event)
if err != nil { if err != nil {
if errors.Is(err, ErrDontForward) { if errors.Is(err, DontForwardErr) {
err = nil err = nil
} }
return return
@@ -159,7 +166,7 @@ func (l *Loop) startLoop() (err error) {
return return
} }
func (l *Loop) Start() (err error) { func (l *Loop) StartLoop() (err error) {
go func() { go func() {
l.ErrChan <- l.startLoop() l.ErrChan <- l.startLoop()
}() }()
+71
View File
@@ -0,0 +1,71 @@
package main
import (
"github.com/admin-else/strom/data"
"github.com/go-viper/mapstructure/v2"
)
type Position struct {
X, Y, Z int32
}
type MetaData struct {
Author string
Description string
Name string
TimeCreated int64
TimeModified int64
TotalBlocks int32
TotalVolume int32
RegionCount int32
EnclosingSize Position
}
type BlockState struct {
Name string
Properties map[string]string
}
type Region struct {
Position Position
Size Position
BlockStatePalette []BlockState
BlockStates []int64
}
type File struct {
Version int32
SubVersion int32
MinecraftDataVersion int32
Metadata MetaData
Regions map[string]Region
}
type Structure struct {
File
BlockStatePalletRealized map[string][]int32
}
func Load(v any) (s *Structure, err error) {
s = &Structure{}
err = mapstructure.Decode(v, &s.File)
if err != nil {
return
}
ret, err := data.LookUpVersionByDataVersion(s.MinecraftDataVersion)
if err != nil {
return
}
for k, r := range s.Regions {
s.BlockStatePalletRealized[k] = make([]int32, len(r.BlockStatePalette))
for i, b := range r.BlockStatePalette {
var stateId int32
stateId, err = data.StateIdFromBlocKAndStateMap(ret.MinecraftVersion, b.Name, b.Properties)
if err != nil {
return
}
s.BlockStatePalletRealized[k][i] = stateId
}
}
return
}
@@ -1,11 +1,11 @@
package level package structure
import ( import (
"github.com/admin-else/strom/nbt" "github.com/admin-else/strom/nbt"
"github.com/go-viper/mapstructure/v2" "github.com/go-viper/mapstructure/v2"
) )
func LoadStructure(fileName string) (s *Structure, err error) { func Load(fileName string) (s *Structure, err error) {
n, err := nbt.ReadFilePath(fileName) n, err := nbt.ReadFilePath(fileName)
if err != nil { if err != nil {
return return
+3 -1
View File
@@ -291,6 +291,8 @@ func readPayload(id int8, r io.Reader) (ret any, err error) {
return return
} }
var UnknownTagTypeErr = errors.New("unknown nbt type")
func writePayload(v any, w io.Writer) (err error) { func writePayload(v any, w io.Writer) (err error) {
switch v := v.(type) { switch v := v.(type) {
case struct{}: case struct{}:
@@ -361,7 +363,7 @@ func writePayload(v any, w io.Writer) (err error) {
} }
err = binary.Write(w, Order, End) err = binary.Write(w, Order, End)
default: default:
err = errors.New("unknown nbt type") err = UnknownTagTypeErr
} }
return return
} }
+91
View File
@@ -0,0 +1,91 @@
package nbt
import (
"fmt"
"io"
)
// PrintSNBTAny documented at https://minecraft.wiki/w/NBT_format#SNBT_format
func PrintSNBTAny(t any, w io.Writer) (err error) {
switch t := t.(type) {
case int8:
_, err = fmt.Fprintf(w, "%vB", t)
case bool:
_, err = fmt.Fprintf(w, "%v", t)
case int16:
_, err = fmt.Fprintf(w, "%vS", t)
case int32:
_, err = fmt.Fprintf(w, "%vI", t)
case int64:
_, err = fmt.Fprintf(w, "%vL", t)
case float32:
_, err = fmt.Fprintf(w, "%vF", t)
case float64:
_, err = fmt.Fprintf(w, "%vD", t)
case string:
_, err = fmt.Fprintf(w, "%q", t)
case []any:
_, err = fmt.Fprintf(w, "[")
for _, v := range t {
err = PrintSNBTAny(v, w)
if err != nil {
return
}
_, err = fmt.Fprintf(w, ",")
}
_, err = fmt.Fprintf(w, "]")
case map[string]any:
_, err = fmt.Fprintf(w, "{")
for k, v := range t {
_, err = fmt.Fprintf(w, "%q:,", k)
err = PrintSNBTAny(v, w)
if err != nil {
return
}
_, err = fmt.Fprintf(w, ",")
}
_, err = fmt.Fprintf(w, "}")
case []int8:
_, err = fmt.Fprintf(w, "[B;")
if err != nil {
return
}
for _, v := range t {
err = PrintSNBTAny(v, w)
if err != nil {
return
}
_, err = fmt.Fprintf(w, ",")
}
_, err = fmt.Fprintf(w, "]")
case []int32:
_, err = fmt.Fprintf(w, "[I;")
if err != nil {
return
}
for _, v := range t {
err = PrintSNBTAny(v, w)
if err != nil {
return
}
_, err = fmt.Fprintf(w, ",")
}
_, err = fmt.Fprintf(w, "]")
case []int64:
_, err = fmt.Fprintf(w, "[L;")
if err != nil {
return
}
for _, v := range t {
err = PrintSNBTAny(v, w)
if err != nil {
return
}
_, err = fmt.Fprintf(w, ",")
}
_, err = fmt.Fprintf(w, "]")
default:
err = UnknownTagTypeErr
}
return
}
+23
View File
@@ -0,0 +1,23 @@
package nbt
import (
"bytes"
_ "embed"
"testing"
)
//go:embed testdata/top_0_20.nbt
var topSNBT []byte
func TestPrintSNBTAny(t *testing.T) {
n, err := ReadFile(bytes.NewReader(topSNBT))
if err != nil {
panic(err)
}
bb := bytes.NewBuffer(nil)
err = PrintSNBTAny(n.Value, bb)
if err != nil {
panic(err)
}
t.Log(bb.String())
}
BIN
View File
Binary file not shown.
+8 -9
View File
@@ -60,13 +60,19 @@ type Conn struct {
func (c *Conn) StartConn() (err error) { func (c *Conn) StartConn() (err error) {
c.RegisterCritical(c.OnTick) c.RegisterCritical(c.OnTick)
return c.Loop.Start() if c.state == proto_base.Play {
c.ActivateReceiveRoutine()
}
err = c.Loop.StartLoop()
c.Loop = event.NewLoop()
return
} }
func (c *Conn) ReceiveJob() { func (c *Conn) ReceiveJob() {
ctx := c.Ctx
for { for {
select { select {
case <-c.Ctx.Done(): case <-ctx.Done():
return return
default: default:
packet, err := c.Receive() packet, err := c.Receive()
@@ -233,10 +239,3 @@ func (c *Conn) OnTick(_ event.Tick) (err error) {
} }
return return
} }
func (c *Conn) Start() (err error) {
if c.Loop == nil {
c.Loop = &event.Loop{}
}
return c.Loop.Start()
}
+1 -1
View File
@@ -65,7 +65,7 @@ func ServeConfig(c *proto.Conn) (err error) {
} }
err = c.Send(&v1_21_8.ConfigurationToClientPacketFinishConfiguration{}) err = c.Send(&v1_21_8.ConfigurationToClientPacketFinishConfiguration{})
cs := &ConfigServer{c} cs := &ConfigServer{c}
return cs.Start() return cs.StartLoop()
} }
func (c *ConfigServer) OnFinishConfiguration(_ *v1_21_8.ConfigurationToServerPacketFinishConfiguration) (err error) { func (c *ConfigServer) OnFinishConfiguration(_ *v1_21_8.ConfigurationToServerPacketFinishConfiguration) (err error) {
+1 -1
View File
@@ -168,6 +168,6 @@ func ServeLogin(c *proto.Conn, settings ...LoginServerSetting) (ret *LoginServer
s(ret) s(ret)
} }
ret.CompressionThreshold = 256 ret.CompressionThreshold = 256
err = ret.Start() err = ret.StartLoop()
return return
} }
+1 -1
View File
@@ -67,6 +67,6 @@ func ServeStatus(c *proto.Conn, s StatusResponse) (err error) {
} }
server.RegisterUntilLatest(server.OnStatusRequest) server.RegisterUntilLatest(server.OnStatusRequest)
server.RegisterUntilLatest(server.OnStatusPing) server.RegisterUntilLatest(server.OnStatusPing)
err = server.Start() err = server.StartLoop()
return return
} }
+14 -1
View File
@@ -1,6 +1,7 @@
package print_nbt package print_nbt
import ( import (
"bytes"
"encoding/json" "encoding/json"
"flag" "flag"
"os" "os"
@@ -11,6 +12,7 @@ import (
var ( var (
cmd = flag.NewFlagSet("print-nbt", flag.ContinueOnError) cmd = flag.NewFlagSet("print-nbt", flag.ContinueOnError)
FileFlag = cmd.String("file", "", "The NBT file to print") FileFlag = cmd.String("file", "", "The NBT file to print")
HashFormatFlag = cmd.Bool("snbt", false, "will print with go %#v")
) )
func Run(args []string) (err error) { func Run(args []string) (err error) {
@@ -27,7 +29,18 @@ func Run(args []string) (err error) {
if err != nil { if err != nil {
return return
} }
b, err := json.MarshalIndent(n, "", " ") var b []byte
if *HashFormatFlag {
bb := bytes.NewBuffer(nil)
err = nbt.PrintSNBTAny(n.Value, bb)
if err != nil {
return
}
b = bb.Bytes()
//b = []byte(strings.ReplaceAll(string(b), ",", ",\n"))
} else {
b, err = json.MarshalIndent(n, "", " ")
}
if err != nil { if err != nil {
return return
} }
+3 -6
View File
@@ -116,10 +116,7 @@ func handleClient(c *proto.Conn) (err error) {
return return
} }
defer sockPuppet.Close() defer sockPuppet.Close()
err = sockPuppet.Start(&client.LoginClient{ err = sockPuppet.StartLoop()
Conn: sockPuppet,
Account: acc,
})
if err != nil { if err != nil {
return return
} }
@@ -127,10 +124,10 @@ func handleClient(c *proto.Conn) (err error) {
p := &Proxy{Client: sockPuppet, Servee: c} p := &Proxy{Client: sockPuppet, Servee: c}
p.Data.Value = map[string]any{"Registries": []any{}} p.Data.Value = map[string]any{"Registries": []any{}}
go func() { go func() {
errChan <- p.Client.Start(p) errChan <- p.Client.StartLoop()
}() }()
go func() { go func() {
errChan <- p.Servee.Start(p) errChan <- p.Servee.StartLoop()
}() }()
err = <-errChan err = <-errChan
f, err := os.Create("data/registry/" + SaveAs + ".nbt") f, err := os.Create("data/registry/" + SaveAs + ".nbt")
+2 -2
View File
@@ -154,10 +154,10 @@ func main() {
errChan := make(chan error) errChan := make(chan error)
defer pc.Close() defer pc.Close()
go func() { go func() {
errChan <- pc.Start(pc) errChan <- pc.StartConn()
}() }()
go func() { go func() {
errChan <- p.Start(p) errChan <- p.StartConn()
}() }()
err = <-errChan err = <-errChan
return return