replace cli interface, add dns and /setname command

This commit is contained in:
olebeck 2022-08-14 22:55:52 +02:00
parent 12b7ff0822
commit 1c68978e50
14 changed files with 478 additions and 182 deletions

View File

@ -2,18 +2,21 @@
## [releases](https://github.com/bedrock-tool/bedrocktool/releases)
<pre>
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)
</pre>
```
Usage: bedrocktool <flags> <subcommand> <subcommand args>
usage:
`./bedrocktool-<version>.exe <function> <address> [-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
```

View File

@ -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
}
}
}

BIN
chunk.bin Normal file

Binary file not shown.

28
chunk_test.go Normal file
View File

@ -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)
}
}

95
dns.go Normal file
View File

@ -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")
}
}()
}

7
go.mod
View File

@ -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

40
go.sum
View File

@ -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=

124
main.go
View File

@ -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 <command>' 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 <command>' 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))
}

View File

@ -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{})
}

Binary file not shown.

View File

@ -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{})
}

View File

@ -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{})
}

View File

@ -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
}

123
world.go
View File

@ -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 <worldname>\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 {