From 01bf2a575cd8818c0b2a8dc63b7e2f58837c9e7d Mon Sep 17 00:00:00 2001 From: adman Date: Mon, 15 Dec 2025 21:44:09 +0100 Subject: [PATCH] Refactor: Add `StartServer` and `ServeClient` functions, improve palette unpacking with new helpers, and simplify the proxy example --- examples/chunks/main.go | 110 +++++++++++++++++++++++++++++++++---- examples/proxy/main.go | 118 ++-------------------------------------- modules/login_server.go | 16 ++++++ server.go | 28 ++++++++++ 4 files changed, 147 insertions(+), 125 deletions(-) create mode 100644 modules/login_server.go create mode 100644 server.go diff --git a/examples/chunks/main.go b/examples/chunks/main.go index 54a4b7a..f41d0de 100644 --- a/examples/chunks/main.go +++ b/examples/chunks/main.go @@ -10,7 +10,13 @@ import ( "github.com/admin-else/queser" ) -func UnpackIndirectPalette(r io.Reader, bitsPerEntry uint8, numberOfEntries int) (err error) { +const ( + BlocksPerChunkSection = 4096 + BiomesPerChunkSection = 64 + SectionsPerChunk = 24 +) + +func UnpackArrayPalette(r io.Reader, bitsPerEntry uint8, numberOfEntries int) (data []int32, err error) { var palletLen queser.VarInt palletLen, err = palletLen.Decode(r) if err != nil { @@ -25,50 +31,130 @@ func UnpackIndirectPalette(r io.Reader, bitsPerEntry uint8, numberOfEntries int) } pallet = append(pallet, int32(entry)) } + data, err = UnpackLongData(r, bitsPerEntry, numberOfEntries) + for i, b := range data { + data[i] = pallet[b] // maybe check bounds? + } + + return +} + +func UnpackLongData(r io.Reader, bitsPerEntry uint8, numberOfEntries int) (data []int32, err error) { entriesPerLong := int(64 / bitsPerEntry) numberOfLongs := int(math.Ceil(float64(numberOfEntries) / float64(entriesPerLong))) - var data []uint64 + var dataL []uint64 for range numberOfLongs { var entry uint64 err = binary.Read(r, binary.BigEndian, &entry) if err != nil { return } - data = append(data, entry) + dataL = append(dataL, entry) } - fmt.Println(data) + + data = LongsToData(dataL, int32(numberOfEntries), int32(bitsPerEntry)) return } -func GetBit(data []uint64, index, bpe int32) uint64 { - // net.minecraft.util.collection.PackedIntegerArray - return 1 +func UnpackSingleValuePalette(r io.Reader, numberOfEntries int) (data []int32, err error) { + entry, err := queser.VarInt(0).Decode(r) + if err != nil { + return + } + data = make([]int32, numberOfEntries) + for i := range data { + data[i] = int32(entry) + } + return } -func UnpackPalette(r io.Reader, numberOfEntries int) (err error) { +func LongsToData(data []uint64, n, bpe int32) (ret []int32) { + mask := (uint64(1) << uint32(bpe)) - 1 + elementsPerLong := 64 / bpe + + ret = make([]int32, n) + for i := range n { + ret[i] = int32((data[i/elementsPerLong] >> ((i % elementsPerLong) * bpe)) & mask) + } + return +} + +func UnpackBlockData(r io.Reader) (blocks [BlocksPerChunkSection]int32, err error) { var bitsPerEntry uint8 err = binary.Read(r, binary.BigEndian, &bitsPerEntry) if err != nil { return } - err = UnpackIndirectPalette(r, bitsPerEntry, numberOfEntries) + var blocksSlice []int32 + fmt.Println("blocks bpe", bitsPerEntry) + switch bitsPerEntry { + case 0: + blocksSlice, err = UnpackSingleValuePalette(r, BlocksPerChunkSection) + case 1, 2, 3, 4: + blocksSlice, err = UnpackArrayPalette(r, bitsPerEntry, BlocksPerChunkSection) + case 5, 6, 7, 8: + panic("unimplemented") + default: + blocksSlice, err = UnpackLongData(r, bitsPerEntry, BlocksPerChunkSection) + } + blocks = [BlocksPerChunkSection]int32(blocksSlice) return } -func UnpackSection(r io.Reader) (err error) { +func UnpackBiomeData(r io.Reader) (biomes [BiomesPerChunkSection]int32, err error) { + // net.minecraft.world.chunk.PaletteProvider#forBiomes + var bitsPerEntry uint8 + err = binary.Read(r, binary.BigEndian, &bitsPerEntry) + if err != nil { + return + } + var biomesSlice []int32 + fmt.Println("biome bpe", bitsPerEntry) + switch bitsPerEntry { + case 0: + biomesSlice, err = UnpackSingleValuePalette(r, BiomesPerChunkSection) + case 1, 2, 3: + biomesSlice, err = UnpackArrayPalette(r, bitsPerEntry, BiomesPerChunkSection) + default: + biomesSlice, err = UnpackLongData(r, bitsPerEntry, BiomesPerChunkSection) + } + if err != nil { + return + } + biomes = [BiomesPerChunkSection]int32(biomesSlice) + return +} + +type ChunkSection struct { + BlockData [BlocksPerChunkSection]int32 + BiomeData [BiomesPerChunkSection]int32 +} + +func UnpackSection(r io.Reader) (s ChunkSection, err error) { var blockCount int16 err = binary.Read(r, binary.BigEndian, &blockCount) if err != nil { return } - err = UnpackPalette(r, 4096) // 4096 is the number of blocks in a chunk + s.BlockData, err = UnpackBlockData(r) + if err != nil { + return + } + s.BiomeData, err = UnpackBiomeData(r) + if err != nil { + return + } + return } func mainE() (err error) { fmt.Println(packet.X*16, packet.Z*16) b := bytes.NewBuffer(packet.ChunkData.Val) - err = UnpackSection(b) + sections := make([]ChunkSection, SectionsPerChunk) + for i := range sections { + sections[i], err = UnpackSection(b) + } return } diff --git a/examples/proxy/main.go b/examples/proxy/main.go index d4ba285..3a3292b 100644 --- a/examples/proxy/main.go +++ b/examples/proxy/main.go @@ -1,94 +1,15 @@ package main import ( - "errors" "fmt" - "log" - "net" "github.com/admin-else/strom" - - "github.com/admin-else/queser" - "github.com/admin-else/queser/generated/v1_21_8" ) -type ProxyClient struct { - *strom.Conn - Proxy *Proxy -} - -func (p *ProxyClient) OnLoopCycle(event strom.OnLoopCycle) (err error) { - return -} - -func (p *ProxyClient) Default(event any) (err error) { - fmt.Printf("S2C %#v\n", event) - err = p.Proxy.Send(event) - if err != nil { - return - } - return -} - -func (p *ProxyClient) Compress(packet v1_21_8.LoginToClientPacketCompress) (err error) { - err = p.Proxy.Send(packet) - if err != nil { - return - } - p.CompressionThreshold = int32(packet.Threshold) - p.Proxy.CompressionThreshold = int32(packet.Threshold) - return -} - -func (p *ProxyClient) OnStart(_ strom.OnStart) (err error) { - return -} - -type Proxy struct { - *strom.Conn - ProxyClient *ProxyClient -} - -func (p *Proxy) OnLastTrigger(event strom.OnLoopCycle) (err error) { - return -} +type Proxy struct{ *strom.Conn } func (p *Proxy) Default(event any) (err error) { - fmt.Printf("C2S %#v\n", event) - err = p.ProxyClient.Send(event) - return -} - -func (p *Proxy) OnHandshake(packet v1_21_8.HandshakingToServerPacketSetProtocol) (err error) { - err = p.ProxyClient.Send(packet) - if err != nil { - return - } - if packet.NextState != queser.VarInt(queser.Status) && packet.NextState != queser.VarInt(queser.Login) { - err = errors.New("invalid next state") - } - p.State = queser.State(packet.NextState) - p.ProxyClient.State = queser.State(packet.NextState) - return -} - -func (p *Proxy) OnLoginAcknowledged(packet v1_21_8.LoginToServerPacketLoginAcknowledged) (err error) { - err = p.ProxyClient.Send(packet) - if err != nil { - return - } - p.State = queser.Configuration - p.ProxyClient.State = queser.Configuration - return -} - -func (p *Proxy) OnConfigFinish(packet v1_21_8.ConfigurationToServerPacketFinishConfiguration) (err error) { - err = p.ProxyClient.Send(packet) - if err != nil { - return - } - p.State = queser.Play - p.ProxyClient.State = queser.Play + fmt.Printf("%#v\n", event) return } @@ -96,40 +17,11 @@ func (p *Proxy) OnStart(_ strom.OnStart) (err error) { return } -func handleConn(conn net.Conn) { - defer conn.Close() - s := strom.Servee(conn) - p := &Proxy{Conn: s} - c, err := strom.Connect("127.0.0.1:25566") - if err != nil { - return - } - p.ProxyClient = &ProxyClient{Conn: c, Proxy: p} - errChan := make(chan error) - go func() { - errChan <- fmt.Errorf("client error: %v", c.Start(p.ProxyClient)) - }() - go func() { - errChan <- fmt.Errorf("server error: %v", s.Start(p)) - }() - err = <-errChan - if err != nil { - log.Println(err) - } - return -} - func main() { - l, err := net.Listen("tcp", ":25565") + err := strom.StartServer(":25565", func(c *strom.Conn) (h strom.HandlerInst, err error) { + return &Proxy{c}, nil + }) if err != nil { panic(err) } - for { - var c net.Conn - c, err = l.Accept() - if err != nil { - panic(err) - } - go handleConn(c) - } } diff --git a/modules/login_server.go b/modules/login_server.go new file mode 100644 index 0000000..9d561fd --- /dev/null +++ b/modules/login_server.go @@ -0,0 +1,16 @@ +package modules + +import ( + "fmt" + + "github.com/admin-else/strom" +) + +type LoginServer struct { + *strom.Conn +} + +func (l LoginServer) Default(event any) (err error) { + err = fmt.Errorf("unexpected event: %#v", event) + return +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..4731120 --- /dev/null +++ b/server.go @@ -0,0 +1,28 @@ +package strom + +import "net" + +func ServeClient(cNet net.Conn, factory func(c *Conn) (h HandlerInst, err error)) (err error) { + c := Servee(cNet) + h, err := factory(c) + if err != nil { + return + } + err = c.Start(h) + return +} + +func StartServer(listenAddr string, factory func(c *Conn) (h HandlerInst, err error)) (err error) { + l, err := net.Listen("tcp", listenAddr) + if err != nil { + return + } + var cNet net.Conn + for { + cNet, err = l.Accept() + if err != nil { + return + } + go ServeClient(cNet, factory) + } +}