diff --git a/Readme.md b/Readme.md index 2d6d1a2..13f0d29 100644 --- a/Readme.md +++ b/Readme.md @@ -2,18 +2,21 @@ ## [releases](https://github.com/bedrock-tool/bedrocktool/releases) -
-Available commands:
-        capture 		capture packets
-        realms-token    get xbl3.0 token
-        ~~packs			downloads resourcepacks from a server~~
-        skins-proxy     skin stealer (proxy)
-        skins   		skin stealer
-        world   		Launch world downloading proxy (NOT WORKING YET)
-
+``` +Usage: bedrocktool -usage: -`./bedrocktool-.exe
[-debug>` +Subcommands: + capture capture packets in a pcap file + help describe subcommands and their syntax + inject inject files into a minecraft install (USE WITH CAUTION) + packs download resource packs from a server + realms-token print xbl3.0 token for realms api + skins download all skins from players on a server + skins-proxy download skins from players on a server with proxy + worlds download a world from a server -example: -`./bedrocktool-v1.24.3.exe skins-proxy play.minecraft.net [-debug]` + +Top-level flags (use "bedrocktool flags" for a full list): + -debug=false: debug mode + -dns=false: enable dns server for consoles +``` \ No newline at end of file diff --git a/capture.go b/capture.go index 9a495ee..424286c 100644 --- a/capture.go +++ b/capture.go @@ -14,6 +14,7 @@ import ( "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcapgo" + "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" ) @@ -22,7 +23,7 @@ var SrcIp_client = net.IPv4(127, 0, 0, 1) var SrcIp_server = net.IPv4(243, 0, 0, 2) func init() { - register_command("capture", "capture packets", packets_main) + register_command(&CaptureCMD{}) } func dump_packet(from_client bool, w *pcapgo.Writer, pk packet.Packet) { @@ -73,31 +74,40 @@ func dump_packet(from_client bool, w *pcapgo.Writer, pk packet.Packet) { } } -func packets_main(ctx context.Context, args []string) error { - var server string - flag.StringVar(&server, "server", "", "target server") - flag.CommandLine.Parse(args) - if G_help { - flag.Usage() - return nil - } +type CaptureCMD struct { + server_address string +} - address, hostname, err := server_input(server) +func (*CaptureCMD) Name() string { return "capture" } +func (*CaptureCMD) Synopsis() string { return "capture packets in a pcap file" } + +func (p *CaptureCMD) SetFlags(f *flag.FlagSet) { + f.StringVar(&p.server_address, "address", "", "remote server address") +} +func (c *CaptureCMD) Usage() string { + return c.Name() + ": " + c.Synopsis() + "\n" +} + +func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + address, hostname, err := server_input(c.server_address) if err != nil { - return err + fmt.Fprintln(os.Stderr, err) + return 1 } listener, serverConn, clientConn, err := create_proxy(ctx, address) if err != nil { - return err + fmt.Fprintln(os.Stderr, err) + return 1 } - f, err := os.Create(hostname + "-" + time.Now().Format("2006-01-02_15-04-05") + ".pcap") + fio, err := os.Create(hostname + "-" + time.Now().Format("2006-01-02_15-04-05") + ".pcap") if err != nil { - return err + fmt.Fprintln(os.Stderr, err) + return 1 } - defer f.Close() - w := pcapgo.NewWriter(f) + defer fio.Close() + w := pcapgo.NewWriter(fio) w.WriteFileHeader(65536, layers.LinkTypeEthernet) _wl := sync.Mutex{} @@ -129,7 +139,7 @@ func packets_main(ctx context.Context, args []string) error { pk, err := serverConn.ReadPacket() if err != nil { fmt.Fprintf(os.Stderr, "Failed to read packet: %s\n", err) - return nil + return 1 } _wl.Lock() @@ -139,7 +149,7 @@ func packets_main(ctx context.Context, args []string) error { err = clientConn.WritePacket(pk) if err != nil { fmt.Fprintf(os.Stderr, "Failed to write packet: %s\n", err) - return nil + return 1 } } } diff --git a/chunk.bin b/chunk.bin new file mode 100644 index 0000000..be2cc50 Binary files /dev/null and b/chunk.bin differ diff --git a/chunk_test.go b/chunk_test.go new file mode 100644 index 0000000..a6e0bf1 --- /dev/null +++ b/chunk_test.go @@ -0,0 +1,28 @@ +package main + +import ( + "os" + "testing" + + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world/chunk" +) + +func Benchmark_chunk_decode(b *testing.B) { + data, _ := os.ReadFile("chunk.bin") + for i := 0; i < b.N; i++ { + _, err := chunk.NetworkDecode(33, data, 6, cube.Range{-64, 319}) + if err != nil { + b.Error(err) + } + } +} + +func Benchmark_render_chunk(b *testing.B) { + data, _ := os.ReadFile("chunk.bin") + ch, _ := chunk.NetworkDecode(33, data, 6, cube.Range{-64, 319}) + + for i := 0; i < b.N; i++ { + Chunk2Img(ch) + } +} diff --git a/dns.go b/dns.go new file mode 100644 index 0000000..6a1cec7 --- /dev/null +++ b/dns.go @@ -0,0 +1,95 @@ +package main + +import ( + "fmt" + "log" + "net" + + "github.com/miekg/dns" +) + +var override_dns = map[string]bool{ + "geo.hivebedrock.network.": true, +} + +func answerQuery(remote net.Addr, req *dns.Msg) (reply *dns.Msg) { + reply = new(dns.Msg) + + answered := false + for _, q := range req.Question { + switch q.Qtype { + case dns.TypeA: + log.Printf("Query for %s\n", q.Name) + + if override_dns[q.Name] { + host, _, _ := net.SplitHostPort(remote.String()) + remote_ip := net.ParseIP(host) + + addrs, _ := net.InterfaceAddrs() + var ip string + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.Contains(remote_ip) { + ip = ipnet.IP.String() + } + } + } + if ip == "" { + log.Panicf("query from outside of own network??") + } + + rr, err := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, ip)) + if err == nil { + reply.Answer = append(reply.Answer, rr) + answered = true + } + } + } + } + + // forward to 1.1.1.1 if not intercepted + if !answered { + c, err := dns.DialWithTLS("tcp4", "1.1.1.1:853", nil) + if err != nil { + panic(err) + } + if err = c.WriteMsg(req); err != nil { + panic(err) + } + if reply, err = c.ReadMsg(); err != nil { + panic(err) + } + c.Close() + } + + return reply +} + +func dns_handler(w dns.ResponseWriter, req *dns.Msg) { + var reply *dns.Msg + + switch req.Opcode { + case dns.OpcodeQuery: + reply = answerQuery(w.RemoteAddr(), req) + default: + reply = new(dns.Msg) + } + + reply.SetReply(req) + w.WriteMsg(reply) +} + +func init_dns() { + dns.HandleFunc(".", dns_handler) + + server := &dns.Server{Addr: ":53", Net: "udp"} + go func() { + fmt.Printf("Starting dns at %s:53\n", GetLocalIP()) + err := server.ListenAndServe() + defer server.Shutdown() + if err != nil { + log.Printf("Failed to start dns server: %s\n ", err.Error()) + println("you may have to use bedrockconnect") + } + }() +} diff --git a/go.mod b/go.mod index 64c468b..6df81f1 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,9 @@ require ( github.com/df-mc/goleveldb v1.1.9 github.com/go-gl/mathgl v1.0.0 github.com/google/gopacket v1.1.19 + github.com/google/subcommands v1.2.0 + github.com/google/uuid v1.3.0 + github.com/miekg/dns v1.1.50 github.com/sandertv/gophertunnel v1.24.0 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 @@ -26,16 +29,18 @@ require ( github.com/df-mc/atomic v1.10.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/klauspost/compress v1.15.9 // indirect github.com/muhammadmuzzammil1998/jsonc v1.0.0 // indirect github.com/sandertv/go-raknet v1.11.1 // indirect github.com/sirupsen/logrus v1.9.0 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.10 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect diff --git a/go.sum b/go.sum index 2915457..b3d4c49 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 h1:/G0ghZwrhou0Wq21qc1vXXMm/t/aKWkALWwITptKbE0= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9/go.mod h1:TOk10ahXejq9wkEaym3KPRNeuR/h5Jx+s8QRWIa2oTM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/df-mc/atomic v1.10.0 h1:0ZuxBKwR/hxcFGorKiHIp+hY7hgY+XBTzhCYD2NqSEg= github.com/df-mc/atomic v1.10.0/go.mod h1:Gw9rf+rPIbydMjA329Jn4yjd/O2c/qusw3iNp4tFGSc= @@ -18,32 +19,41 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs= github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= github.com/olebeck/dragonfly v0.8.2-1 h1:hC0iWH7WFOpsvR3XeTwJSRKtKO+GhEcMq+H+DHUwHWc= github.com/olebeck/dragonfly v0.8.2-1/go.mod h1:xgpCDhHoP03RygPaTrzzDwsSTcEZhxNPMV3CAxETj+I= -github.com/olebeck/dragonfly v0.8.2 h1:UIFDpktsif5pzQp0ewJIOtpzbNzuKHSMCPT69HHhKyo= -github.com/olebeck/dragonfly v0.8.2/go.mod h1:xgpCDhHoP03RygPaTrzzDwsSTcEZhxNPMV3CAxETj+I= github.com/olebeck/gophertunnel v1.24.4 h1:nX7Std61XpXW4VP7KKd2RvinRwx1nGB5l8QnbwrArLE= github.com/olebeck/gophertunnel v1.24.4/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sandertv/go-raknet v1.11.1 h1:0auvhHoZNyC/Z1l5xqniE3JE+w3MGO3n3JXEGHzdlRE= github.com/sandertv/go-raknet v1.11.1/go.mod h1:Gx+WgZBMQ0V2UoouGoJ8Wj6CDrMBQ4SB2F/ggpl5/+Y= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -57,30 +67,52 @@ golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw= golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= @@ -88,9 +120,13 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= diff --git a/main.go b/main.go index a90b7eb..96ed6d3 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "regexp" "syscall" + "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft/auth" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" @@ -25,7 +26,6 @@ const TOKEN_FILE = "token.json" var G_src oauth2.TokenSource var G_xbl_token *auth.XBLToken var G_debug bool -var G_help bool var G_exit []func() = []func(){} var pool = packet.NewPool() @@ -85,22 +85,6 @@ func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { fmt.Printf("%s 0x%x, %s\n", dir, pk.ID(), pk_name) } -type CMD struct { - Name string - Desc string - Main func(context.Context, []string) error -} - -var cmds map[string]CMD = make(map[string]CMD) - -func register_command(name, desc string, main_func func(context.Context, []string) error) { - cmds[name] = CMD{ - Name: name, - Desc: desc, - Main: main_func, - } -} - func exit() { fmt.Printf("\nExiting\n") for i := len(G_exit) - 1; i >= 0; i-- { // go through cleanup functions reversed @@ -109,12 +93,50 @@ func exit() { os.Exit(0) } -func main() { - flag.BoolVar(&G_debug, "debug", false, "debug mode") - flag.BoolVar(&G_help, "help", false, "show help") +var valid_cmds = make(map[string]string, 0) +func register_command(sub subcommands.Command) { + subcommands.Register(sub, "") + valid_cmds[sub.Name()] = sub.Synopsis() +} + +func main() { ctx, cancel := context.WithCancel(context.Background()) + flag.BoolVar(&G_debug, "debug", false, "debug mode") + enable_dns := flag.Bool("dns", false, "enable dns server for consoles") + subcommands.Register(subcommands.HelpCommand(), "") + subcommands.ImportantFlag("debug") + subcommands.ImportantFlag("dns") + + { // interactive input + if len(os.Args) < 2 { + select { + case <-ctx.Done(): + return + default: + fmt.Println("Available commands:") + for name, desc := range valid_cmds { + fmt.Printf("\t%s\t%s\n", name, desc) + } + fmt.Printf("Use '%s ' to run a command\n", os.Args[0]) + + fmt.Printf("Input Command: ") + reader := bufio.NewReader(os.Stdin) + target, _ := reader.ReadString('\n') + r, _ := regexp.Compile(`[\n\r]`) + target = string(r.ReplaceAll([]byte(target), []byte(""))) + os.Args = append(os.Args, target) + } + } + } + + flag.Parse() + + if *enable_dns { + init_dns() + } + // exit cleanup sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) @@ -124,58 +146,22 @@ func main() { exit() }() - // authenticate - token := get_token() - G_src = auth.RefreshTokenSource(&token) - { - _token, err := G_src.Token() - if err != nil { - panic(err) - } - G_xbl_token, err = auth.RequestXBLToken(ctx, _token, "https://pocket.realms.minecraft.net/") - if err != nil { - panic(err) - } - } - - if len(os.Args) < 2 { - select { - case <-ctx.Done(): - return - default: - fmt.Println("Available commands:") - for name, cmd := range cmds { - fmt.Printf("\t%s\t%s\n", name, cmd.Desc) + { // authenticate + token := get_token() + G_src = auth.RefreshTokenSource(&token) + { + _token, err := G_src.Token() + if err != nil { + panic(err) + } + G_xbl_token, err = auth.RequestXBLToken(ctx, _token, "https://pocket.realms.minecraft.net/") + if err != nil { + panic(err) } - fmt.Printf("Use '%s ' to run a command\n", os.Args[0]) - - fmt.Printf("Input Command: ") - reader := bufio.NewReader(os.Stdin) - target, _ := reader.ReadString('\n') - r, _ := regexp.Compile(`[\n\r]`) - target = string(r.ReplaceAll([]byte(target), []byte(""))) - os.Args = append(os.Args, target) } } - cmd := cmds[os.Args[1]] - if cmd.Main == nil { - fmt.Fprintf(os.Stderr, "Unknown command: %s\n", os.Args[1]) - os.Exit(1) - } - if err := cmd.Main(ctx, os.Args[2:]); err != nil { - fmt.Fprintf(os.Stderr, "Error: %s\n", err) - os.Exit(1) - } - + ret := subcommands.Execute(ctx) exit() -} - -func token_main(ctx context.Context, args []string) error { - fmt.Printf("%s\n", G_xbl_token.AuthorizationToken.Token) - return nil -} - -func init() { - register_command("realms-token", "get xbl3.0 token for pocket.realms.minecraft.net", token_main) + os.Exit(int(ret)) } diff --git a/realms.go b/realms.go index b7a182d..be54718 100644 --- a/realms.go +++ b/realms.go @@ -1,11 +1,15 @@ package main import ( + "context" "encoding/json" + "flag" "fmt" "io" "net/http" "strings" + + "github.com/google/subcommands" ) type Realm struct { @@ -106,3 +110,22 @@ func list_realms() error { } return nil } + +type TokenCMD struct{} + +func (*TokenCMD) Name() string { return "realms-token" } +func (*TokenCMD) Synopsis() string { return "print xbl3.0 token for realms api" } + +func (c *TokenCMD) SetFlags(f *flag.FlagSet) {} +func (c *TokenCMD) Usage() string { + return c.Name() + ": " + c.Synopsis() + "\n" +} + +func (c *TokenCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + fmt.Printf("%s\n", G_xbl_token.AuthorizationToken.Token) + return 0 +} + +func init() { + register_command(&TokenCMD{}) +} diff --git a/resourcepack-ace.go b/resourcepack-ace.go index 66f3378..fd1d03b 100644 Binary files a/resourcepack-ace.go and b/resourcepack-ace.go differ diff --git a/skins-proxy.go b/skins-proxy.go index d0ac9ac..97d4819 100644 --- a/skins-proxy.go +++ b/skins-proxy.go @@ -7,35 +7,37 @@ import ( "fmt" "os" + "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft" ) -func init() { - register_command("skins-proxy", "skin stealer (proxy)", skin_proxy_main) +type SkinProxyCMD struct { + server_address string + filter string } -func skin_proxy_main(ctx context.Context, args []string) error { - var server string - if len(args) >= 1 { - server = args[0] - args = args[1:] - } +func (*SkinProxyCMD) Name() string { return "skins-proxy" } +func (*SkinProxyCMD) Synopsis() string { return "download skins from players on a server with proxy" } - flag.StringVar(&skin_filter_player, "player", "", "only download the skin of this player") - flag.CommandLine.Parse(args) - if G_help { - flag.Usage() - return nil - } +func (c *SkinProxyCMD) SetFlags(f *flag.FlagSet) { + f.StringVar(&c.server_address, "address", "", "remote server address") + f.StringVar(&c.filter, "filter", "", "player name filter prefix") +} +func (c *SkinProxyCMD) Usage() string { + return c.Name() + ": " + c.Synopsis() + "\n" +} - address, hostname, err := server_input(server) +func (c *SkinProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + address, hostname, err := server_input(c.server_address) if err != nil { - return err + fmt.Fprintln(os.Stderr, err) + return 1 } listener, clientConn, serverConn, err := create_proxy(ctx, address) if err != nil { - return err + fmt.Fprintln(os.Stderr, err) + return 1 } defer listener.Close() @@ -85,9 +87,14 @@ func skin_proxy_main(ctx context.Context, args []string) error { for { select { case err := <-errs: - return err + fmt.Fprintln(os.Stderr, err) + return 1 case <-ctx.Done(): - return nil + return 0 } } } + +func init() { + register_command(&SkinProxyCMD{}) +} diff --git a/skins.go b/skins.go index 2fb689c..bce0244 100644 --- a/skins.go +++ b/skins.go @@ -14,6 +14,7 @@ import ( "regexp" "strings" + "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" @@ -101,10 +102,6 @@ func (skin *Skin) Write(output_path, name string) error { return err } -func init() { - register_command("skins", "skin stealer", skin_main) -} - var name_regexp = regexp.MustCompile(`§.`) func cleanup_name(name string) string { @@ -169,36 +166,41 @@ func process_packet_skins(conn *minecraft.Conn, out_path string, pk packet.Packe } } -func skin_main(ctx context.Context, args []string) error { - var server string +type SkinCMD struct { + server_address string + filter string +} - if len(args) >= 1 { - server = args[0] - args = args[1:] - } +func (*SkinCMD) Name() string { return "skins" } +func (*SkinCMD) Synopsis() string { return "download all skins from players on a server" } - flag.StringVar(&skin_filter_player, "player", "", "only download the skin of this player") - flag.CommandLine.Parse(args) - if G_help { - flag.Usage() - return nil - } +func (c *SkinCMD) SetFlags(f *flag.FlagSet) { + f.StringVar(&c.server_address, "address", "", "remote server address") + f.StringVar(&c.filter, "filter", "", "player name filter prefix") +} +func (c *SkinCMD) Usage() string { + return c.Name() + ": " + c.Synopsis() + "\n" +} - address, hostname, err := server_input(server) +func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + address, hostname, err := server_input(c.server_address) if err != nil { - return err + fmt.Fprint(os.Stderr, err) + return 1 } serverConn, err := connect_server(ctx, address, nil) if err != nil { - return err + fmt.Fprint(os.Stderr, err) + return 1 } defer serverConn.Close() out_path := fmt.Sprintf("skins/%s", hostname) if err := serverConn.DoSpawnContext(ctx); err != nil { - return err + fmt.Fprint(os.Stderr, err) + return 1 } println("Connected") @@ -209,8 +211,12 @@ func skin_main(ctx context.Context, args []string) error { for { pk, err := serverConn.ReadPacket() if err != nil { - return err + return 1 } process_packet_skins(nil, out_path, pk) } } + +func init() { + register_command(&SkinCMD{}) +} diff --git a/utils.go b/utils.go index 7b124dc..a1feaef 100644 --- a/utils.go +++ b/utils.go @@ -8,6 +8,7 @@ import ( "log" "net" "os" + "path" "regexp" "strings" @@ -192,6 +193,7 @@ var PrivateIPNetworks = []net.IPNet{ }, } +// check if ip is private func IPPrivate(ip net.IP) bool { for _, ipNet := range PrivateIPNetworks { if ipNet.Contains(ip) { @@ -200,3 +202,53 @@ func IPPrivate(ip net.IP) bool { } return false } + +// GetLocalIP returns the non loopback local IP of the host +func GetLocalIP() string { + addrs, err := net.InterfaceAddrs() + if err != nil { + return "" + } + for _, address := range addrs { + // check the address type and if it is not a loopback the display it + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String() + } + } + } + return "" +} + +// get longest line length +func max_len(lines []string) int { + o := 0 + for _, line := range lines { + if o < len(line) { + o = len(line) + } + } + return o +} + +// make text centered +func margin_lines(lines []string) string { + ret := "" + max := max_len(lines) + for _, line := range lines { + if len(line) != max { + ret += strings.Repeat(" ", max/2-len(line)/4) + } + ret += line + "\n" + } + return ret +} + +// split path to filename and extension +func splitext(filename string) (name, ext string) { + name, ext = path.Base(filename), path.Ext(filename) + if ext != "" { + name = strings.TrimSuffix(name, ext) + } + return +} diff --git a/world.go b/world.go index 228674d..82a7f5f 100644 --- a/world.go +++ b/world.go @@ -13,15 +13,19 @@ import ( "os" "path" "path/filepath" - "strconv" + "strings" "time" "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/entity" + "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/chunk" "github.com/df-mc/dragonfly/server/world/mcdb" "github.com/df-mc/goleveldb/leveldb/opt" "github.com/go-gl/mathgl/mgl32" + "github.com/go-gl/mathgl/mgl64" + "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" @@ -40,6 +44,7 @@ type TPlayerPos struct { type WorldState struct { chunks map[protocol.ChunkPos]*chunk.Chunk + entities map[int64]world.SaveableEntity Dim world.Dimension WorldName string ServerName string @@ -63,60 +68,58 @@ func NewWorldState() *WorldState { } } +var setname_command = protocol.Command{ + Name: "setname", + Description: "set user defined name for this world", + Overloads: []protocol.CommandOverload{ + { + Parameters: []protocol.CommandParameter{ + { + Name: "name", + Type: protocol.CommandArgTypeFilepath, + Optional: false, + }, + }, + }, + }, +} + var black_16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16)) func init() { draw.Draw(black_16x16, image.Rect(0, 0, 16, 16), image.Black, image.Point{}, draw.Src) - register_command("world", "Launch world downloading proxy", world_main) - register_command("test-chunk", "test chunk decode", test_chunk) + register_command(&WorldCMD{}) } -func test_chunk(ctx context.Context, args []string) error { - if len(args) == 0 { - return fmt.Errorf("not enough args") - } - fname, air, count := args[0], args[1], args[2] - _air, _ := strconv.Atoi(air) - _count, _ := strconv.Atoi(count) - data, err := os.ReadFile(fname) - if err != nil { - return err - } - - ch, err := chunk.NetworkDecode(uint32(_air), data, _count, cube.Range{-64, 319}) - if err != nil { - return err - } - ch.Range() - return nil +type WorldCMD struct { + server_address string } -func world_main(ctx context.Context, args []string) error { - var server_address string +func (*WorldCMD) Name() string { return "worlds" } +func (*WorldCMD) Synopsis() string { return "download a world from a server" } - if len(args) >= 1 { - server_address = args[0] - args = args[1:] - } +func (p *WorldCMD) SetFlags(f *flag.FlagSet) { + f.StringVar(&p.server_address, "address", "", "remote server address") +} +func (c *WorldCMD) Usage() string { + return c.Name() + ": " + c.Synopsis() + "\n" +} - flag.CommandLine.Parse(args) - if G_help { - flag.Usage() - return nil - } - - server_address, hostname, err := server_input(server_address) +func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + server_address, hostname, err := server_input(c.server_address) if err != nil { - return err + fmt.Fprintln(os.Stderr, err) + return 1 } listener, clientConn, serverConn, err := create_proxy(ctx, server_address) if err != nil { - return err + fmt.Fprintln(os.Stderr, err) + return 1 } handleConn(ctx, listener, clientConn, serverConn, hostname) - return nil + return 0 } var dimension_ids = map[int32]world.Dimension{ @@ -201,6 +204,19 @@ func (w *WorldState) ProcessAnimate(pk *packet.Animate) { } } +func (w *WorldState) ProcessAddItemActor(pk *packet.AddItemActor) { + it, ok := world.ItemByRuntimeID(pk.Item.StackNetworkID, int16(pk.Item.Stack.MetadataValue)) + if !ok { + return + } + stack := item.NewStack(it, int(pk.Item.Stack.Count)) + w.entities[pk.EntityUniqueID] = entity.NewItem(stack, mgl64.Vec3{ + float64(pk.Position[0]), + float64(pk.Position[1]), + float64(pk.Position[2]), + }) +} + func (w *WorldState) ProcessChangeDimension(pk *packet.ChangeDimension) { fmt.Printf("ChangeDimension %d\n", pk.Dimension) if len(w.chunks) > 0 { @@ -212,6 +228,23 @@ func (w *WorldState) ProcessChangeDimension(pk *packet.ChangeDimension) { w.Dim = dimension_ids[pk.Dimension] } +func (w *WorldState) SendMessage(text string) { + w.ClientConn.WritePacket(&packet.Text{ + TextType: packet.TextTypeSystem, + Message: "§8[§bBedrocktool§8]§r " + text, + }) +} + +func (w *WorldState) ProcessCommand(pk *packet.CommandRequest) bool { + cmd := strings.Split(pk.CommandLine, " ") + if cmd[0] == "/setname" && len(cmd) >= 2 { + w.WorldName = strings.Join(cmd[1:], " ") + w.SendMessage(fmt.Sprintf("worldName is now: %s", w.WorldName)) + return true + } + return false +} + func (w *WorldState) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) { w.PlayerPos = TPlayerPos{ Position: Position, @@ -223,13 +256,14 @@ func (w *WorldState) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float func (w *WorldState) Reset() { w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk) + w.WorldName = "world" w.ui.Reset() } // writes the world to a folder, resets all the chunks func (w *WorldState) SaveAndReset() { fmt.Println("Saving world") - folder := path.Join("worlds", fmt.Sprintf("%s/world-%d", w.ServerName, w.worldCounter)) + folder := path.Join("worlds", fmt.Sprintf("%s/%s-%d", w.ServerName, w.WorldName, w.worldCounter)) os.MkdirAll(folder, 0777) provider, err := mcdb.New(folder, opt.DefaultCompression) if err != nil { @@ -239,6 +273,9 @@ func (w *WorldState) SaveAndReset() { for cp, c := range w.chunks { provider.SaveChunk((world.ChunkPos)(cp), c, w.Dim) } + + //provider.SaveEntities(w.entities) + s := provider.Settings() s.Spawn = cube.Pos{ int(w.PlayerPos.Position[0]), @@ -356,6 +393,8 @@ func handleConn(ctx context.Context, l *minecraft.Listener, cc, sc *minecraft.Co w.ClientConn = cc w.ServerConn = sc + w.SendMessage("use /setname \nto set the world name") + G_exit = append(G_exit, func() { w.SaveAndReset() }) @@ -387,6 +426,8 @@ func handleConn(ctx context.Context, l *minecraft.Listener, cc, sc *minecraft.Co skip = true case *packet.Animate: w.ProcessAnimate(pk) + case *packet.CommandRequest: + skip = w.ProcessCommand(pk) } if !skip { @@ -438,9 +479,13 @@ func handleConn(ctx context.Context, l *minecraft.Listener, cc, sc *minecraft.Co case *packet.LevelChunk: w.ProcessLevelChunk(pk) w.ui.Send(w) - send_popup(w.ClientConn, fmt.Sprintf("%d chunks loaded\n", len(w.chunks))) + send_popup(w.ClientConn, fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName)) case *packet.SubChunk: w.ProcessSubChunk(pk) + case *packet.AddItemActor: + w.ProcessAddItemActor(pk) + case *packet.AvailableCommands: + pk.Commands = append(pk.Commands, setname_command) } if err := w.ClientConn.WritePacket(pk); err != nil {