Compare commits
7 Commits
796a846b8e
...
5149487c18
Author | SHA1 | Date |
---|---|---|
olebeck | 5149487c18 | |
olebeck | 87cd32e280 | |
olebeck | aa73c86d11 | |
olebeck | c20e017f0d | |
olebeck | 2c87715966 | |
olebeck | 7726c707c6 | |
olebeck | 2e920a7bf0 |
|
@ -8,7 +8,8 @@ assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is,
|
||||||
|
and what server it occurs on.
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
@ -24,15 +25,20 @@ A clear and concise description of what you expected to happen.
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Desktop (please complete the following information):**
|
||||||
- OS: [e.g. iOS]
|
- OS: [e.g. windows]
|
||||||
- Browser [e.g. chrome, safari]
|
- Version [e.g. 1.28.0-36]
|
||||||
- Version [e.g. 22]
|
- Minecraft Version [e.g. 1.19.73]
|
||||||
|
|
||||||
**Smartphone (please complete the following information):**
|
**Smartphone (please complete the following information):**
|
||||||
- Device: [e.g. iPhone6]
|
- Device: [e.g. iPhone6]
|
||||||
- OS: [e.g. iOS8.1]
|
- OS: [e.g. iOS12]
|
||||||
- Browser [e.g. stock browser, safari]
|
- Version [e.g. 1.28.0-36]
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|
||||||
|
|
||||||
|
**attach packets.log.gpg (not always necessary)**
|
||||||
|
this file can be helpful for debugging without having to connect to the server.
|
||||||
|
it can be created by running with -extra-debug [e.g. bedrocktool.exe -extra-debug worlds -address play.mojang.com ]
|
||||||
|
be sure to only attach the .gpg file which is not publicly readable.
|
|
@ -35,7 +35,6 @@ func (c *CLI) Init() bool {
|
||||||
|
|
||||||
func (c *CLI) Start(ctx context.Context, cancel context.CancelFunc) error {
|
func (c *CLI) Start(ctx context.Context, cancel context.CancelFunc) error {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
utils.InitDNS()
|
|
||||||
subcommands.Execute(ctx)
|
subcommands.Execute(ctx)
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
cancel()
|
cancel()
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -3,10 +3,10 @@ module github.com/bedrock-tool/bedrocktool
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
//replace github.com/sandertv/gophertunnel => ./gophertunnel
|
//replace github.com/sandertv/gophertunnel => ./gophertunnel
|
||||||
replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.28.1-1
|
replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.28.1-2
|
||||||
|
|
||||||
//replace github.com/df-mc/dragonfly => ./dragonfly
|
//replace github.com/df-mc/dragonfly => ./dragonfly
|
||||||
replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.9.4-7
|
replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.9.4-9
|
||||||
|
|
||||||
//replace gioui.org => ./gio
|
//replace gioui.org => ./gio
|
||||||
replace gioui.org => github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9
|
replace gioui.org => github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9
|
||||||
|
@ -51,6 +51,7 @@ require (
|
||||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 // indirect
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 // indirect
|
||||||
github.com/df-mc/atomic v1.10.0 // indirect
|
github.com/df-mc/atomic v1.10.0 // indirect
|
||||||
github.com/df-mc/worldupgrader v1.0.3 // indirect
|
github.com/df-mc/worldupgrader v1.0.3 // indirect
|
||||||
|
github.com/dlclark/regexp2 v1.9.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-text/typesetting v0.0.0-20221214153724-0399769901d5 // indirect
|
github.com/go-text/typesetting v0.0.0-20221214153724-0399769901d5 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -37,6 +37,8 @@ github.com/df-mc/goleveldb v1.1.9 h1:ihdosZyy5jkQKrxucTQmN90jq/2lUwQnJZjIYIC/9YU
|
||||||
github.com/df-mc/goleveldb v1.1.9/go.mod h1:+NHCup03Sci5q84APIA21z3iPZCuk6m6ABtg4nANCSk=
|
github.com/df-mc/goleveldb v1.1.9/go.mod h1:+NHCup03Sci5q84APIA21z3iPZCuk6m6ABtg4nANCSk=
|
||||||
github.com/df-mc/worldupgrader v1.0.3 h1:3nbthy6vfSNQZdqHBR+E5Fh3mCeWmCwLtqrYDiPUG5I=
|
github.com/df-mc/worldupgrader v1.0.3 h1:3nbthy6vfSNQZdqHBR+E5Fh3mCeWmCwLtqrYDiPUG5I=
|
||||||
github.com/df-mc/worldupgrader v1.0.3/go.mod h1:6ybkJ/BV9b0XkcPzcLmvgT9Nv/xgBXdDQTmRhu7b8zQ=
|
github.com/df-mc/worldupgrader v1.0.3/go.mod h1:6ybkJ/BV9b0XkcPzcLmvgT9Nv/xgBXdDQTmRhu7b8zQ=
|
||||||
|
github.com/dlclark/regexp2 v1.9.0 h1:pTK/l/3qYIKaRXuHnEnIf7Y5NxfRPfpb7dis6/gdlVI=
|
||||||
|
github.com/dlclark/regexp2 v1.9.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
github.com/flytam/filenamify v1.1.2 h1:dGlfWU4zrhDlsmvob4IFcfgjG5vIjfo4UwLyec6Wx94=
|
github.com/flytam/filenamify v1.1.2 h1:dGlfWU4zrhDlsmvob4IFcfgjG5vIjfo4UwLyec6Wx94=
|
||||||
|
@ -94,10 +96,14 @@ github.com/olebeck/dragonfly v0.9.4-6 h1:Pom7oMbUA/kFu6PCwr3mWtTOSPvgzD2/71+mUsq
|
||||||
github.com/olebeck/dragonfly v0.9.4-6/go.mod h1:k8OQvjmfj+JbrwQf1qHfMETlFHOp0WJLjILN+QVqh+c=
|
github.com/olebeck/dragonfly v0.9.4-6/go.mod h1:k8OQvjmfj+JbrwQf1qHfMETlFHOp0WJLjILN+QVqh+c=
|
||||||
github.com/olebeck/dragonfly v0.9.4-7 h1:xzSc9U9upx+mxayAHN1MxkD+PStVgqksJ4uls0o3g4w=
|
github.com/olebeck/dragonfly v0.9.4-7 h1:xzSc9U9upx+mxayAHN1MxkD+PStVgqksJ4uls0o3g4w=
|
||||||
github.com/olebeck/dragonfly v0.9.4-7/go.mod h1:k8OQvjmfj+JbrwQf1qHfMETlFHOp0WJLjILN+QVqh+c=
|
github.com/olebeck/dragonfly v0.9.4-7/go.mod h1:k8OQvjmfj+JbrwQf1qHfMETlFHOp0WJLjILN+QVqh+c=
|
||||||
|
github.com/olebeck/dragonfly v0.9.4-9 h1:VgcYFAyEZMo2VSm68fkUZ68iye7xmOWttt6gvjAG6Uw=
|
||||||
|
github.com/olebeck/dragonfly v0.9.4-9/go.mod h1:k8OQvjmfj+JbrwQf1qHfMETlFHOp0WJLjILN+QVqh+c=
|
||||||
github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9 h1:TqDsMHwjW5ZYfh+RE8ussT62m0qXqo+QjzSXb7BCVA4=
|
github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9 h1:TqDsMHwjW5ZYfh+RE8ussT62m0qXqo+QjzSXb7BCVA4=
|
||||||
github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9/go.mod h1:+W1Kpf96YcfissZocFqIp6O42FDTuphkObbEybp+Ffc=
|
github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9/go.mod h1:+W1Kpf96YcfissZocFqIp6O42FDTuphkObbEybp+Ffc=
|
||||||
github.com/olebeck/gophertunnel v1.28.1-1 h1:bw2jeMz94YHF5qQYhq1Yq/6fALkklGu7k26YbPI4DSs=
|
github.com/olebeck/gophertunnel v1.28.1-1 h1:bw2jeMz94YHF5qQYhq1Yq/6fALkklGu7k26YbPI4DSs=
|
||||||
github.com/olebeck/gophertunnel v1.28.1-1/go.mod h1:ekREo7U9TPHh86kbuPMaWA93NMyWsfVvP/iNT3XhAb8=
|
github.com/olebeck/gophertunnel v1.28.1-1/go.mod h1:ekREo7U9TPHh86kbuPMaWA93NMyWsfVvP/iNT3XhAb8=
|
||||||
|
github.com/olebeck/gophertunnel v1.28.1-2 h1:EoGyQNzmsXtg+ObOshCKyNffTqAfk9yRFNdJcHGjxmY=
|
||||||
|
github.com/olebeck/gophertunnel v1.28.1-2/go.mod h1:HxQfl/8mZzvjzhekEH8RO6xLAgan9i/wIyrQzw0tIPY=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
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 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -14,23 +13,22 @@ import (
|
||||||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dumpLock sync.Mutex
|
func (p *packetCapturer) dumpPacket(toServer bool, payload []byte) {
|
||||||
|
p.dumpLock.Lock()
|
||||||
func dumpPacket(f io.WriteCloser, toServer bool, payload []byte) {
|
defer p.dumpLock.Unlock()
|
||||||
dumpLock.Lock()
|
p.fio.Write([]byte{0xAA, 0xAA, 0xAA, 0xAA})
|
||||||
defer dumpLock.Unlock()
|
|
||||||
f.Write([]byte{0xAA, 0xAA, 0xAA, 0xAA})
|
|
||||||
packetSize := uint32(len(payload))
|
packetSize := uint32(len(payload))
|
||||||
binary.Write(f, binary.LittleEndian, packetSize)
|
binary.Write(p.fio, binary.LittleEndian, packetSize)
|
||||||
binary.Write(f, binary.LittleEndian, toServer)
|
binary.Write(p.fio, binary.LittleEndian, toServer)
|
||||||
binary.Write(f, binary.LittleEndian, time.Now().UnixMilli())
|
binary.Write(p.fio, binary.LittleEndian, time.Now().UnixMilli())
|
||||||
f.Write(payload)
|
p.fio.Write(payload)
|
||||||
f.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB})
|
p.fio.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB})
|
||||||
}
|
}
|
||||||
|
|
||||||
type packetCapturer struct {
|
type packetCapturer struct {
|
||||||
proxy *utils.ProxyContext
|
proxy *utils.ProxyContext
|
||||||
fio *os.File
|
fio *os.File
|
||||||
|
dumpLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *packetCapturer) AddressAndName(address, hostname string) error {
|
func (p *packetCapturer) AddressAndName(address, hostname string) error {
|
||||||
|
@ -45,12 +43,10 @@ func (p *packetCapturer) AddressAndName(address, hostname string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *packetCapturer) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) {
|
func (p *packetCapturer) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) {
|
||||||
IsfromClient := p.proxy.IsClient(src)
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
header.Write(buf)
|
header.Write(buf)
|
||||||
buf.Write(payload)
|
buf.Write(payload)
|
||||||
dumpPacket(p.fio, IsfromClient, buf.Bytes())
|
p.dumpPacket(p.proxy.IsClient(src), buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPacketCapturer() *utils.ProxyHandler {
|
func NewPacketCapturer() *utils.ProxyHandler {
|
||||||
|
@ -63,8 +59,8 @@ func NewPacketCapturer() *utils.ProxyHandler {
|
||||||
AddressAndName: p.AddressAndName,
|
AddressAndName: p.AddressAndName,
|
||||||
PacketFunc: p.PacketFunc,
|
PacketFunc: p.PacketFunc,
|
||||||
OnEnd: func() {
|
OnEnd: func() {
|
||||||
dumpLock.Lock()
|
p.dumpLock.Lock()
|
||||||
defer dumpLock.Unlock()
|
defer p.dumpLock.Unlock()
|
||||||
p.fio.Close()
|
p.fio.Close()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,11 +48,6 @@ var MutedPackets = []string{
|
||||||
"packet.PlaySound",
|
"packet.PlaySound",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
FLog io.Writer
|
|
||||||
dmpLock sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s string) {
|
func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s string) {
|
||||||
tBase := strings.Repeat("\t", level)
|
tBase := strings.Repeat("\t", level)
|
||||||
|
|
||||||
|
@ -173,15 +168,6 @@ func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s stri
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func DumpStruct(data interface{}) {
|
|
||||||
if FLog == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
FLog.Write([]byte(dmpStruct(0, data, true, false)))
|
|
||||||
FLog.Write([]byte("\n\n\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var dirS2C = color.GreenString("S") + "->" + color.CyanString("C")
|
var dirS2C = color.GreenString("S") + "->" + color.CyanString("C")
|
||||||
var dirC2S = color.CyanString("C") + "->" + color.GreenString("S")
|
var dirC2S = color.CyanString("C") + "->" + color.GreenString("S")
|
||||||
var pool = packet.NewPool()
|
var pool = packet.NewPool()
|
||||||
|
@ -189,6 +175,7 @@ var pool = packet.NewPool()
|
||||||
func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
||||||
var logPlain, logCrypt, logCryptEnc io.WriteCloser
|
var logPlain, logCrypt, logCryptEnc io.WriteCloser
|
||||||
var packetsLogF *bufio.Writer
|
var packetsLogF *bufio.Writer
|
||||||
|
var dmpLock sync.Mutex
|
||||||
|
|
||||||
if extraVerbose {
|
if extraVerbose {
|
||||||
// open plain text log
|
// open plain text log
|
||||||
|
@ -196,19 +183,14 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// open gpg log
|
// open gpg log
|
||||||
logCrypt, err := os.Create("packets.log.gpg")
|
logCryptEnc, err = crypt.Encer("packets.log.gpg")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
} else {
|
|
||||||
// encrypter for the log
|
|
||||||
logCryptEnc, err = crypt.Encer("packets.log", logCrypt)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
packetsLogF = bufio.NewWriter(io.MultiWriter(logPlain, logCryptEnc))
|
if logPlain != nil || logCryptEnc != nil {
|
||||||
|
packetsLogF = bufio.NewWriter(io.MultiWriter(logPlain, logCryptEnc))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var proxy *utils.ProxyContext
|
var proxy *utils.ProxyContext
|
||||||
|
@ -233,7 +215,7 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
||||||
}()
|
}()
|
||||||
pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0))
|
pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0))
|
||||||
|
|
||||||
if extraVerbose {
|
if packetsLogF != nil {
|
||||||
dmpLock.Lock()
|
dmpLock.Lock()
|
||||||
packetsLogF.Write([]byte(dmpStruct(0, pk, true, false) + "\n\n\n"))
|
packetsLogF.Write([]byte(dmpStruct(0, pk, true, false) + "\n\n\n"))
|
||||||
dmpLock.Unlock()
|
dmpLock.Unlock()
|
||||||
|
@ -249,6 +231,7 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
OnEnd: func() {
|
OnEnd: func() {
|
||||||
|
dmpLock.Lock()
|
||||||
if packetsLogF != nil {
|
if packetsLogF != nil {
|
||||||
packetsLogF.Flush()
|
packetsLogF.Flush()
|
||||||
}
|
}
|
||||||
|
@ -261,6 +244,7 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
||||||
if logCrypt != nil {
|
if logCrypt != nil {
|
||||||
logCrypt.Close()
|
logCrypt.Close()
|
||||||
}
|
}
|
||||||
|
dmpLock.Unlock()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,9 +80,8 @@ func (s *secondaryUser) processLevelChunk(pk *packet.LevelChunk) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *secondaryUser) processSubChunk(pk *packet.SubChunk) {
|
func (s *secondaryUser) processSubChunk(pk *packet.SubChunk) {
|
||||||
offsets := make([]protocol.SubChunkOffset, 0, len(pk.SubChunkEntries))
|
offsets := make(map[world.ChunkPos]bool, len(pk.SubChunkEntries))
|
||||||
for _, sub := range pk.SubChunkEntries {
|
for _, sub := range pk.SubChunkEntries {
|
||||||
offsets = append(offsets, sub.Offset)
|
|
||||||
var (
|
var (
|
||||||
absX = pk.Position[0] + int32(sub.Offset[0])
|
absX = pk.Position[0] + int32(sub.Offset[0])
|
||||||
absY = pk.Position[1] + int32(sub.Offset[1])
|
absY = pk.Position[1] + int32(sub.Offset[1])
|
||||||
|
@ -90,6 +89,7 @@ func (s *secondaryUser) processSubChunk(pk *packet.SubChunk) {
|
||||||
subPos = protocol.SubChunkPos{absX, absY, absZ}
|
subPos = protocol.SubChunkPos{absX, absY, absZ}
|
||||||
pos = world.ChunkPos{absX, absZ}
|
pos = world.ChunkPos{absX, absZ}
|
||||||
)
|
)
|
||||||
|
offsets[pos] = true
|
||||||
ch, ok := s.chunks[pos]
|
ch, ok := s.chunks[pos]
|
||||||
if !ok {
|
if !ok {
|
||||||
logrus.Error(locale.Loc("subchunk_before_chunk", nil))
|
logrus.Error(locale.Loc("subchunk_before_chunk", nil))
|
||||||
|
@ -107,6 +107,13 @@ func (s *secondaryUser) processSubChunk(pk *packet.SubChunk) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range s.server.Players() {
|
for _, p := range s.server.Players() {
|
||||||
p.Session().ViewSubChunks(world.SubChunkPos(pk.Position), offsets)
|
for pos := range offsets {
|
||||||
|
ch, ok := s.chunks[pos]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.Session().ViewChunk(pos, ch, nil)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ func NewSecondUser() *utils.ProxyHandler {
|
||||||
s.proxy = pc
|
s.proxy = pc
|
||||||
},
|
},
|
||||||
SecondaryClientCB: s.SecondaryClientCB,
|
SecondaryClientCB: s.SecondaryClientCB,
|
||||||
OnClientConnect: func(conn *minecraft.Conn) {
|
OnClientConnect: func(conn minecraft.IConn) {
|
||||||
id := conn.IdentityData()
|
id := conn.IdentityData()
|
||||||
s.mainPlayer = player.New(id.DisplayName, skin.New(64, 64), mgl64.Vec3{0, 00})
|
s.mainPlayer = player.New(id.DisplayName, skin.New(64, 64), mgl64.Vec3{0, 00})
|
||||||
s.server.World().AddEntity(s.mainPlayer)
|
s.server.World().AddEntity(s.mainPlayer)
|
||||||
|
@ -99,7 +99,7 @@ func NewSecondUser() *utils.ProxyHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *secondaryUser) SecondaryClientCB(conn *minecraft.Conn) {
|
func (s *secondaryUser) SecondaryClientCB(conn minecraft.IConn) {
|
||||||
s.listener.Conn <- conn
|
s.listener.Conn <- conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,13 +56,13 @@ func (s *SkinSaver) AddSkin(playerName string, playerID uuid.UUID, playerSkin *p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(playerName, s.PlayerNameFilter) {
|
if !strings.HasPrefix(playerName, s.PlayerNameFilter) {
|
||||||
return "", nil, false
|
return playerName, nil, false
|
||||||
}
|
}
|
||||||
s.playerNames[playerID] = playerName
|
s.playerNames[playerID] = playerName
|
||||||
|
|
||||||
skin := &utils.Skin{Skin: playerSkin}
|
skin := &utils.Skin{Skin: playerSkin}
|
||||||
if s.OnlyIfHasGeometry && !skin.HaveGeometry() {
|
if s.OnlyIfHasGeometry && !skin.HaveGeometry() {
|
||||||
return "", nil, false
|
return playerName, nil, false
|
||||||
}
|
}
|
||||||
wasAdded := s.AddPlayerSkin(playerID, playerName, skin)
|
wasAdded := s.AddPlayerSkin(playerID, playerName, skin)
|
||||||
|
|
||||||
|
|
|
@ -11,18 +11,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (w *worldsHandler) processChangeDimension(pk *packet.ChangeDimension) {
|
func (w *worldsHandler) processChangeDimension(pk *packet.ChangeDimension) {
|
||||||
if len(w.worldState.chunks) > 0 {
|
w.SaveAndReset()
|
||||||
w.SaveAndReset()
|
|
||||||
} else {
|
|
||||||
logrus.Info(locale.Loc("not_saving_empty", nil))
|
|
||||||
w.Reset(w.CurrentName())
|
|
||||||
}
|
|
||||||
dimensionID := pk.Dimension
|
dimensionID := pk.Dimension
|
||||||
if w.serverState.ispre118 && dimensionID == 0 {
|
if w.serverState.ispre118 && dimensionID == 0 {
|
||||||
dimensionID += 10
|
dimensionID += 10
|
||||||
}
|
}
|
||||||
d, _ := world.DimensionByID(int(dimensionID))
|
w.worldState.dimension, _ = world.DimensionByID(int(dimensionID))
|
||||||
w.worldState.dimension = d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worldsHandler) processLevelChunk(pk *packet.LevelChunk) {
|
func (w *worldsHandler) processLevelChunk(pk *packet.LevelChunk) {
|
||||||
|
@ -158,7 +152,7 @@ func (w *worldsHandler) ProcessChunkPackets(pk packet.Packet) packet.Packet {
|
||||||
if ok {
|
if ok {
|
||||||
x, y, z := blockPosInChunk(pk.Position)
|
x, y, z := blockPosInChunk(pk.Position)
|
||||||
c.SetBlock(x, y, z, uint8(pk.Layer), pk.NewBlockRuntimeID)
|
c.SetBlock(x, y, z, uint8(pk.Layer), pk.NewBlockRuntimeID)
|
||||||
w.mapUI.SetChunk(cp, w.worldState.chunks[cp], true)
|
w.mapUI.SetChunk(cp, c, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *packet.UpdateSubChunkBlocks:
|
case *packet.UpdateSubChunkBlocks:
|
||||||
|
@ -174,7 +168,7 @@ func (w *worldsHandler) ProcessChunkPackets(pk packet.Packet) packet.Packet {
|
||||||
c.SetBlock(x, y, z, 0, bce.BlockRuntimeID)
|
c.SetBlock(x, y, z, 0, bce.BlockRuntimeID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.mapUI.SetChunk(cp, w.worldState.chunks[cp], true)
|
w.mapUI.SetChunk(cp, c, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
func Test(t *testing.T) {
|
func Test(t *testing.T) {
|
||||||
data, _ := os.ReadFile("chunk.bin")
|
data, _ := os.ReadFile("chunk.bin")
|
||||||
ch, _, _ := chunk.NetworkDecode(33, data, 6, cube.Range{0, 255}, true, false)
|
ch, _, _ := chunk.NetworkDecode(33, data, 6, cube.Range{0, 255}, true, false)
|
||||||
i := utils.Chunk2Img(ch, true)
|
i := utils.Chunk2Img(ch)
|
||||||
f, _ := os.Create("chunk.png")
|
f, _ := os.Create("chunk.png")
|
||||||
png.Encode(f, i)
|
png.Encode(f, i)
|
||||||
f.Close()
|
f.Close()
|
||||||
|
@ -47,7 +47,7 @@ func Benchmark_render_chunk(b *testing.B) {
|
||||||
b.Error(err)
|
b.Error(err)
|
||||||
}
|
}
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
utils.Chunk2Img(ch, true)
|
utils.Chunk2Img(ch)
|
||||||
}
|
}
|
||||||
pprof.StopCPUProfile()
|
pprof.StopCPUProfile()
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (e serverEntity) Type() world.EntityType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worldsHandler) processAddActor(pk *packet.AddActor) {
|
func (w *worldsHandler) processAddActor(pk *packet.AddActor) {
|
||||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||||
if !ok {
|
if !ok {
|
||||||
e = &entityState{
|
e = &entityState{
|
||||||
RuntimeID: pk.EntityRuntimeID,
|
RuntimeID: pk.EntityRuntimeID,
|
||||||
|
@ -197,7 +197,7 @@ func (s *entityState) ToServerEntity() serverEntity {
|
||||||
Encoded: s.EntityType,
|
Encoded: s.EntityType,
|
||||||
NBT: map[string]any{
|
NBT: map[string]any{
|
||||||
"Pos": vec3float32(s.Position),
|
"Pos": vec3float32(s.Position),
|
||||||
"Rotation": []float32{s.Yaw, s.Pitch},
|
"Rotation": []float32{s.HeadYaw, s.Pitch},
|
||||||
"Motion": vec3float32(s.Velocity),
|
"Motion": vec3float32(s.Velocity),
|
||||||
"UniqueID": int64(s.UniqueID),
|
"UniqueID": int64(s.UniqueID),
|
||||||
},
|
},
|
||||||
|
@ -216,6 +216,11 @@ func (s *entityState) ToServerEntity() serverEntity {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *worldsHandler) getEntity(id uint64) (*entityState, bool) {
|
||||||
|
e, ok := w.worldState.entities[id]
|
||||||
|
return e, ok
|
||||||
|
}
|
||||||
|
|
||||||
func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
||||||
if !w.settings.SaveEntities {
|
if !w.settings.SaveEntities {
|
||||||
return pk
|
return pk
|
||||||
|
@ -226,7 +231,7 @@ func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
||||||
w.processAddActor(pk)
|
w.processAddActor(pk)
|
||||||
case *packet.RemoveActor:
|
case *packet.RemoveActor:
|
||||||
case *packet.SetActorData:
|
case *packet.SetActorData:
|
||||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||||
if ok {
|
if ok {
|
||||||
e.Metadata = pk.EntityMetadata
|
e.Metadata = pk.EntityMetadata
|
||||||
w.bp.AddEntity(behaviourpack.EntityIn{
|
w.bp.AddEntity(behaviourpack.EntityIn{
|
||||||
|
@ -236,12 +241,12 @@ func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case *packet.SetActorMotion:
|
case *packet.SetActorMotion:
|
||||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||||
if ok {
|
if ok {
|
||||||
e.Velocity = pk.Velocity
|
e.Velocity = pk.Velocity
|
||||||
}
|
}
|
||||||
case *packet.MoveActorDelta:
|
case *packet.MoveActorDelta:
|
||||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||||
if ok {
|
if ok {
|
||||||
if pk.Flags&packet.MoveActorDeltaFlagHasX != 0 {
|
if pk.Flags&packet.MoveActorDeltaFlagHasX != 0 {
|
||||||
e.Position[0] = pk.Position[0]
|
e.Position[0] = pk.Position[0]
|
||||||
|
@ -263,14 +268,14 @@ func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
case *packet.MoveActorAbsolute:
|
case *packet.MoveActorAbsolute:
|
||||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||||
if ok {
|
if ok {
|
||||||
e.Position = pk.Position
|
e.Position = pk.Position
|
||||||
e.Pitch = pk.Rotation.X()
|
e.Pitch = pk.Rotation.X()
|
||||||
e.Yaw = pk.Rotation.Y()
|
e.Yaw = pk.Rotation.Y()
|
||||||
}
|
}
|
||||||
case *packet.MobEquipment:
|
case *packet.MobEquipment:
|
||||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||||
if ok {
|
if ok {
|
||||||
w, ok := e.Inventory[pk.WindowID]
|
w, ok := e.Inventory[pk.WindowID]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -280,7 +285,7 @@ func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
||||||
w[pk.HotBarSlot] = pk.NewItem
|
w[pk.HotBarSlot] = pk.NewItem
|
||||||
}
|
}
|
||||||
case *packet.MobArmourEquipment:
|
case *packet.MobArmourEquipment:
|
||||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||||
if ok {
|
if ok {
|
||||||
e.Helmet = &pk.Helmet
|
e.Helmet = &pk.Helmet
|
||||||
e.Chestplate = &pk.Chestplate
|
e.Chestplate = &pk.Chestplate
|
||||||
|
|
|
@ -112,10 +112,6 @@ func (w *worldsHandler) processItemPacketsServer(pk packet.Packet) packet.Packet
|
||||||
|
|
||||||
case *packet.ItemComponent:
|
case *packet.ItemComponent:
|
||||||
w.bp.ApplyComponentEntries(pk.Items)
|
w.bp.ApplyComponentEntries(pk.Items)
|
||||||
case *packet.MobArmourEquipment:
|
|
||||||
if pk.EntityRuntimeID == w.proxy.Server.GameData().EntityRuntimeID {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return pk
|
return pk
|
||||||
}
|
}
|
||||||
|
@ -190,199 +186,3 @@ func stackToItem(it protocol.ItemStack) item.Stack {
|
||||||
s := item.NewStack(t, int(it.Count))
|
s := item.NewStack(t, int(it.Count))
|
||||||
return nbtconv.Item(it.NBTData, &s)
|
return nbtconv.Item(it.NBTData, &s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worldsHandler) playerData() (ret map[string]any) {
|
|
||||||
ret = map[string]any{
|
|
||||||
"format_version": "1.12.0",
|
|
||||||
"identifier": "minecraft:player",
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(w.serverState.playerInventory) > 0 {
|
|
||||||
inv := inventory.New(len(w.serverState.playerInventory), nil)
|
|
||||||
for i, ii := range w.serverState.playerInventory {
|
|
||||||
inv.SetItem(i, stackToItem(ii.Stack))
|
|
||||||
}
|
|
||||||
ret["Inventory"] = nbtconv.InvToNBT(inv)
|
|
||||||
}
|
|
||||||
|
|
||||||
ret["abilities"] = map[string]any{
|
|
||||||
"doorsandswitches": true,
|
|
||||||
"op": true,
|
|
||||||
"opencontainers": true,
|
|
||||||
"teleport": true,
|
|
||||||
"attackmobs": true,
|
|
||||||
"instabuild": true,
|
|
||||||
"permissionsLevel": int32(3),
|
|
||||||
"flying": false,
|
|
||||||
"lightning": false,
|
|
||||||
"playerPermissionsLevel": int32(2),
|
|
||||||
"attackplayers": true,
|
|
||||||
"build": true,
|
|
||||||
"flySpeed": float32(0.05),
|
|
||||||
"invulnerable": true,
|
|
||||||
"mayfly": true,
|
|
||||||
"mine": true,
|
|
||||||
"walkSpeed": float32(0.1),
|
|
||||||
}
|
|
||||||
|
|
||||||
type attribute struct {
|
|
||||||
Name string
|
|
||||||
Base float32
|
|
||||||
Current float32
|
|
||||||
DefaultMax float32
|
|
||||||
DefaultMin float32
|
|
||||||
Max float32
|
|
||||||
Min float32
|
|
||||||
}
|
|
||||||
|
|
||||||
ret["Attributes"] = []attribute{
|
|
||||||
{
|
|
||||||
Base: 0,
|
|
||||||
Current: 0,
|
|
||||||
DefaultMax: 1024,
|
|
||||||
DefaultMin: -1024,
|
|
||||||
Max: 1024,
|
|
||||||
Min: -1024,
|
|
||||||
Name: "minecraft:luck",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 20,
|
|
||||||
Current: 20,
|
|
||||||
DefaultMax: 20,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 20,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:health",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 0,
|
|
||||||
Current: 0,
|
|
||||||
DefaultMax: 16,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 16,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:absorption",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 0,
|
|
||||||
Current: 0,
|
|
||||||
DefaultMax: 1,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 1,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:knockback_resistance",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 0.1,
|
|
||||||
Current: 0.1,
|
|
||||||
DefaultMax: 3.4028235e+38,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 3.4028235e+38,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:movement",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 0.02,
|
|
||||||
Current: 0.02,
|
|
||||||
DefaultMax: 3.4028235e+38,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 3.4028235e+38,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:underwater_movement",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 0.02,
|
|
||||||
Current: 0.02,
|
|
||||||
DefaultMax: 3.4028235e+38,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 3.4028235e+38,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:lava_movement",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 16,
|
|
||||||
Current: 16,
|
|
||||||
DefaultMax: 2048,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 2048,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:follow_range",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 1,
|
|
||||||
Current: 1,
|
|
||||||
DefaultMax: 1,
|
|
||||||
DefaultMin: 1,
|
|
||||||
Max: 1,
|
|
||||||
Min: 1,
|
|
||||||
Name: "minecraft:attack_damage",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 20,
|
|
||||||
Current: 20,
|
|
||||||
DefaultMax: 20,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 20,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:player.hunger",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 0,
|
|
||||||
Current: 0,
|
|
||||||
DefaultMax: 20,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 20,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:player.exhaustion",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 5,
|
|
||||||
Current: 5,
|
|
||||||
DefaultMax: 20,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 20,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:player.saturation",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 0,
|
|
||||||
Current: 0,
|
|
||||||
DefaultMax: 24791,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 24791,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:player.level",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Base: 0,
|
|
||||||
Current: 0,
|
|
||||||
DefaultMax: 1,
|
|
||||||
DefaultMin: 0,
|
|
||||||
Max: 1,
|
|
||||||
Min: 0,
|
|
||||||
Name: "minecraft:player.experience",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ret["Tags"] = []string{}
|
|
||||||
ret["OnGround"] = true
|
|
||||||
|
|
||||||
spawn := w.proxy.Server.GameData().PlayerPosition
|
|
||||||
|
|
||||||
ret["SpawnX"] = int32(spawn.X())
|
|
||||||
ret["SpawnY"] = int32(spawn.Y())
|
|
||||||
ret["SpawnZ"] = int32(spawn.Z())
|
|
||||||
|
|
||||||
ret["Pos"] = []float32{
|
|
||||||
float32(spawn.X()),
|
|
||||||
float32(spawn.Y()),
|
|
||||||
float32(spawn.Z()),
|
|
||||||
}
|
|
||||||
|
|
||||||
ret["Rotation"] = []float32{
|
|
||||||
w.serverState.PlayerPos.Pitch,
|
|
||||||
w.serverState.PlayerPos.Yaw,
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
const ViewMapID = 0x424242
|
const ViewMapID = 0x424242
|
||||||
|
|
||||||
// MapItemPacket tells the client that it has a map with id 0x424242 in the offhand
|
// MapItemPacket tells the client that it has a map with id 0x424242 in the offhand
|
||||||
var MapItemPacket packet.InventoryContent = packet.InventoryContent{
|
var MapItemPacket = packet.InventoryContent{
|
||||||
WindowID: 119,
|
WindowID: 119,
|
||||||
Content: []protocol.ItemInstance{
|
Content: []protocol.ItemInstance{
|
||||||
{
|
{
|
||||||
|
@ -43,31 +43,37 @@ var MapItemPacket packet.InventoryContent = packet.InventoryContent{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func imin(a, b int32) int32 {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func imax(a, b int32) int32 {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MapUI) GetBounds() (min, max protocol.ChunkPos) {
|
func (m *MapUI) GetBounds() (min, max protocol.ChunkPos) {
|
||||||
// get the chunk coord bounds
|
if len(m.renderedChunks) == 0 {
|
||||||
i := 0
|
return
|
||||||
for _ch := range m.renderedChunks {
|
}
|
||||||
if _ch.X() < min.X() || i == 0 {
|
min = protocol.ChunkPos{math.MaxInt32, math.MaxInt32}
|
||||||
min[0] = _ch.X()
|
for chunk := range m.renderedChunks {
|
||||||
}
|
min[0] = imin(min[0], chunk[0])
|
||||||
if _ch.Z() < min.Z() || i == 0 {
|
min[1] = imin(min[1], chunk[1])
|
||||||
min[1] = _ch.Z()
|
max[0] = imax(max[0], chunk[0])
|
||||||
}
|
max[1] = imax(max[1], chunk[1])
|
||||||
if _ch.X() > max.X() || i == 0 {
|
|
||||||
max[0] = _ch.X()
|
|
||||||
}
|
|
||||||
if _ch.Z() > max.Z() || i == 0 {
|
|
||||||
max[1] = _ch.Z()
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type RenderElem struct {
|
type RenderElem struct {
|
||||||
pos protocol.ChunkPos
|
pos protocol.ChunkPos
|
||||||
ch *chunk.Chunk
|
ch *chunk.Chunk
|
||||||
complete bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MapUI struct {
|
type MapUI struct {
|
||||||
|
@ -106,9 +112,6 @@ func (m *MapUI) Start() {
|
||||||
MapID: ViewMapID,
|
MapID: ViewMapID,
|
||||||
Scale: 4,
|
Scale: 4,
|
||||||
MapsIncludedIn: []int64{ViewMapID},
|
MapsIncludedIn: []int64{ViewMapID},
|
||||||
Width: 0,
|
|
||||||
Height: 0,
|
|
||||||
Pixels: nil,
|
|
||||||
UpdateFlags: packet.MapUpdateFlagInitialisation,
|
UpdateFlags: packet.MapUpdateFlagInitialisation,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -190,9 +193,7 @@ func (m *MapUI) Redraw() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if r.ch != nil {
|
if r.ch != nil {
|
||||||
m.renderedChunks[r.pos] = utils.Chunk2Img(r.ch, !r.complete)
|
m.renderedChunks[r.pos] = utils.Chunk2Img(r.ch)
|
||||||
} else {
|
|
||||||
m.renderedChunks[r.pos] = black16x16
|
|
||||||
}
|
}
|
||||||
updatedChunks = append(updatedChunks, r.pos)
|
updatedChunks = append(updatedChunks, r.pos)
|
||||||
}
|
}
|
||||||
|
@ -244,7 +245,7 @@ func (m *MapUI) ToImage() *image.RGBA {
|
||||||
chunksX := int(max[0] - min[0] + 1) // how many chunk lengths is x coordinate
|
chunksX := int(max[0] - min[0] + 1) // how many chunk lengths is x coordinate
|
||||||
chunksY := int(max[1] - min[1] + 1)
|
chunksY := int(max[1] - min[1] + 1)
|
||||||
|
|
||||||
img2 := image.NewRGBA(image.Rect(0, 0, chunksX*16, chunksY*16))
|
img := image.NewRGBA(image.Rect(0, 0, chunksX*16, chunksY*16))
|
||||||
|
|
||||||
m.l.RLock()
|
m.l.RLock()
|
||||||
for pos, tile := range m.renderedChunks {
|
for pos, tile := range m.renderedChunks {
|
||||||
|
@ -252,17 +253,17 @@ func (m *MapUI) ToImage() *image.RGBA {
|
||||||
int((pos.X()-min.X())*16),
|
int((pos.X()-min.X())*16),
|
||||||
int((pos.Z()-min.Z())*16),
|
int((pos.Z()-min.Z())*16),
|
||||||
)
|
)
|
||||||
draw.Draw(img2, image.Rect(
|
draw.Draw(img, image.Rect(
|
||||||
px.X, px.Y,
|
px.X, px.Y,
|
||||||
px.X+16, px.Y+16,
|
px.X+16, px.Y+16,
|
||||||
), tile, image.Point{}, draw.Src)
|
), tile, image.Point{}, draw.Src)
|
||||||
}
|
}
|
||||||
m.l.RUnlock()
|
m.l.RUnlock()
|
||||||
return img2
|
return img
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk, complete bool) {
|
func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk, complete bool) {
|
||||||
m.renderQueue.Enqueue(&RenderElem{pos, ch, complete})
|
m.renderQueue.Enqueue(&RenderElem{pos, ch})
|
||||||
m.SchedRedraw()
|
m.SchedRedraw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
package worlds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bedrock-tool/bedrocktool/utils/nbtconv"
|
||||||
|
"github.com/df-mc/dragonfly/server/item/inventory"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (w *worldsHandler) playerData() (ret map[string]any) {
|
||||||
|
ret = map[string]any{
|
||||||
|
"format_version": "1.12.0",
|
||||||
|
"identifier": "minecraft:player",
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(w.serverState.playerInventory) > 0 {
|
||||||
|
inv := inventory.New(len(w.serverState.playerInventory), nil)
|
||||||
|
for i, ii := range w.serverState.playerInventory {
|
||||||
|
inv.SetItem(i, stackToItem(ii.Stack))
|
||||||
|
}
|
||||||
|
ret["Inventory"] = nbtconv.InvToNBT(inv)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret["abilities"] = map[string]any{
|
||||||
|
"doorsandswitches": true,
|
||||||
|
"op": true,
|
||||||
|
"opencontainers": true,
|
||||||
|
"teleport": true,
|
||||||
|
"attackmobs": true,
|
||||||
|
"instabuild": true,
|
||||||
|
"permissionsLevel": int32(3),
|
||||||
|
"flying": false,
|
||||||
|
"lightning": false,
|
||||||
|
"playerPermissionsLevel": int32(2),
|
||||||
|
"attackplayers": true,
|
||||||
|
"build": true,
|
||||||
|
"flySpeed": float32(0.05),
|
||||||
|
"invulnerable": true,
|
||||||
|
"mayfly": true,
|
||||||
|
"mine": true,
|
||||||
|
"walkSpeed": float32(0.1),
|
||||||
|
}
|
||||||
|
|
||||||
|
type attribute struct {
|
||||||
|
Name string
|
||||||
|
Base float32
|
||||||
|
Current float32
|
||||||
|
DefaultMax float32
|
||||||
|
DefaultMin float32
|
||||||
|
Max float32
|
||||||
|
Min float32
|
||||||
|
}
|
||||||
|
|
||||||
|
ret["Attributes"] = []attribute{
|
||||||
|
{
|
||||||
|
Base: 0,
|
||||||
|
Current: 0,
|
||||||
|
DefaultMax: 1024,
|
||||||
|
DefaultMin: -1024,
|
||||||
|
Max: 1024,
|
||||||
|
Min: -1024,
|
||||||
|
Name: "minecraft:luck",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 20,
|
||||||
|
Current: 20,
|
||||||
|
DefaultMax: 20,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 20,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:health",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 0,
|
||||||
|
Current: 0,
|
||||||
|
DefaultMax: 16,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 16,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:absorption",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 0,
|
||||||
|
Current: 0,
|
||||||
|
DefaultMax: 1,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 1,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:knockback_resistance",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 0.1,
|
||||||
|
Current: 0.1,
|
||||||
|
DefaultMax: 3.4028235e+38,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 3.4028235e+38,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:movement",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 0.02,
|
||||||
|
Current: 0.02,
|
||||||
|
DefaultMax: 3.4028235e+38,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 3.4028235e+38,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:underwater_movement",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 0.02,
|
||||||
|
Current: 0.02,
|
||||||
|
DefaultMax: 3.4028235e+38,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 3.4028235e+38,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:lava_movement",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 16,
|
||||||
|
Current: 16,
|
||||||
|
DefaultMax: 2048,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 2048,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:follow_range",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 1,
|
||||||
|
Current: 1,
|
||||||
|
DefaultMax: 1,
|
||||||
|
DefaultMin: 1,
|
||||||
|
Max: 1,
|
||||||
|
Min: 1,
|
||||||
|
Name: "minecraft:attack_damage",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 20,
|
||||||
|
Current: 20,
|
||||||
|
DefaultMax: 20,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 20,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:player.hunger",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 0,
|
||||||
|
Current: 0,
|
||||||
|
DefaultMax: 20,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 20,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:player.exhaustion",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 5,
|
||||||
|
Current: 5,
|
||||||
|
DefaultMax: 20,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 20,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:player.saturation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 0,
|
||||||
|
Current: 0,
|
||||||
|
DefaultMax: 24791,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 24791,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:player.level",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: 0,
|
||||||
|
Current: 0,
|
||||||
|
DefaultMax: 1,
|
||||||
|
DefaultMin: 0,
|
||||||
|
Max: 1,
|
||||||
|
Min: 0,
|
||||||
|
Name: "minecraft:player.experience",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ret["Tags"] = []string{}
|
||||||
|
ret["OnGround"] = true
|
||||||
|
|
||||||
|
spawn := w.serverState.PlayerPos.Position
|
||||||
|
|
||||||
|
ret["SpawnX"] = int32(spawn.X())
|
||||||
|
ret["SpawnY"] = int32(spawn.Y())
|
||||||
|
ret["SpawnZ"] = int32(spawn.Z())
|
||||||
|
|
||||||
|
ret["Pos"] = []float32{
|
||||||
|
float32(spawn.X()),
|
||||||
|
float32(spawn.Y()),
|
||||||
|
float32(spawn.Z()),
|
||||||
|
}
|
||||||
|
|
||||||
|
ret["Rotation"] = []float32{
|
||||||
|
w.serverState.PlayerPos.Pitch,
|
||||||
|
w.serverState.PlayerPos.Yaw,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -11,12 +11,14 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bedrock-tool/bedrocktool/locale"
|
"github.com/bedrock-tool/bedrocktool/locale"
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
||||||
"github.com/bedrock-tool/bedrocktool/utils"
|
"github.com/bedrock-tool/bedrocktool/utils"
|
||||||
"github.com/bedrock-tool/bedrocktool/utils/behaviourpack"
|
"github.com/bedrock-tool/bedrocktool/utils/behaviourpack"
|
||||||
|
"github.com/flytam/filenamify"
|
||||||
|
|
||||||
"github.com/df-mc/dragonfly/server/block/cube"
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||||||
"github.com/df-mc/dragonfly/server/world"
|
"github.com/df-mc/dragonfly/server/world"
|
||||||
|
@ -72,6 +74,7 @@ type serverState struct {
|
||||||
|
|
||||||
type worldsHandler struct {
|
type worldsHandler struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
wg sync.WaitGroup
|
||||||
proxy *utils.ProxyContext
|
proxy *utils.ProxyContext
|
||||||
mapUI *MapUI
|
mapUI *MapUI
|
||||||
gui utils.UI
|
gui utils.UI
|
||||||
|
@ -82,34 +85,22 @@ type worldsHandler struct {
|
||||||
settings WorldSettings
|
settings WorldSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
var black16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16))
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for i := 3; i < len(black16x16.Pix); i += 4 {
|
|
||||||
black16x16.Pix[i] = 255
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWorldsHandler(ctx context.Context, ui utils.UI, settings WorldSettings) *utils.ProxyHandler {
|
func NewWorldsHandler(ctx context.Context, ui utils.UI, settings WorldSettings) *utils.ProxyHandler {
|
||||||
w := &worldsHandler{
|
w := &worldsHandler{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
mapUI: nil,
|
gui: ui,
|
||||||
gui: ui,
|
|
||||||
bp: nil,
|
|
||||||
|
|
||||||
serverState: serverState{
|
serverState: serverState{
|
||||||
ispre118: false,
|
ispre118: false,
|
||||||
worldCounter: 0,
|
worldCounter: 0,
|
||||||
ChunkRadius: 0,
|
ChunkRadius: 0,
|
||||||
|
PlayerPos: TPlayerPos{},
|
||||||
playerInventory: nil,
|
|
||||||
PlayerPos: TPlayerPos{},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
settings: settings,
|
settings: settings,
|
||||||
}
|
}
|
||||||
w.mapUI = NewMapUI(w)
|
w.mapUI = NewMapUI(w)
|
||||||
w.Reset(w.CurrentName())
|
w.Reset()
|
||||||
|
|
||||||
return &utils.ProxyHandler{
|
return &utils.ProxyHandler{
|
||||||
Name: "Worlds",
|
Name: "Worlds",
|
||||||
|
@ -148,7 +139,7 @@ func NewWorldsHandler(ctx context.Context, ui utils.UI, settings WorldSettings)
|
||||||
w.serverState.Name = hostname
|
w.serverState.Name = hostname
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
OnClientConnect: func(conn *minecraft.Conn) {
|
OnClientConnect: func(conn minecraft.IConn) {
|
||||||
w.gui.Message(messages.SetUIState(messages.UIStateConnecting))
|
w.gui.Message(messages.SetUIState(messages.UIStateConnecting))
|
||||||
},
|
},
|
||||||
GameDataModifier: func(gd *minecraft.GameData) {
|
GameDataModifier: func(gd *minecraft.GameData) {
|
||||||
|
@ -181,6 +172,7 @@ func NewWorldsHandler(ctx context.Context, ui utils.UI, settings WorldSettings)
|
||||||
},
|
},
|
||||||
OnEnd: func() {
|
OnEnd: func() {
|
||||||
w.SaveAndReset()
|
w.SaveAndReset()
|
||||||
|
w.wg.Wait()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,22 +207,22 @@ func (w *worldsHandler) setWorldName(val string, fromUI bool) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worldsHandler) CurrentName() string {
|
func (w *worldsHandler) currentName() string {
|
||||||
worldName := "world"
|
worldName := "world"
|
||||||
if w.serverState.worldCounter > 1 {
|
if w.serverState.worldCounter > 0 {
|
||||||
worldName = fmt.Sprintf("world-%d", w.serverState.worldCounter)
|
worldName = fmt.Sprintf("world-%d", w.serverState.worldCounter)
|
||||||
}
|
}
|
||||||
return worldName
|
return worldName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worldsHandler) Reset(newName string) {
|
func (w *worldsHandler) Reset() {
|
||||||
w.worldState = worldState{
|
w.worldState = worldState{
|
||||||
dimension: w.worldState.dimension,
|
dimension: w.worldState.dimension,
|
||||||
chunks: make(map[protocol.ChunkPos]*chunk.Chunk),
|
chunks: make(map[protocol.ChunkPos]*chunk.Chunk),
|
||||||
blockNBT: make(map[protocol.SubChunkPos][]map[string]any),
|
blockNBT: make(map[protocol.SubChunkPos][]map[string]any),
|
||||||
entities: make(map[uint64]*entityState),
|
entities: make(map[uint64]*entityState),
|
||||||
openItemContainers: make(map[byte]*itemContainer),
|
openItemContainers: make(map[byte]*itemContainer),
|
||||||
Name: newName,
|
Name: w.currentName(),
|
||||||
}
|
}
|
||||||
w.mapUI.Reset()
|
w.mapUI.Reset()
|
||||||
}
|
}
|
||||||
|
@ -249,8 +241,11 @@ func (w *worldState) cullChunks() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worldState) Save(folder string) (*mcdb.Provider, error) {
|
func (w *worldState) Save(folder string) (*mcdb.DB, error) {
|
||||||
provider, err := mcdb.New(logrus.StandardLogger(), folder, opt.DefaultCompression)
|
provider, err := mcdb.Config{
|
||||||
|
Log: logrus.StandardLogger(),
|
||||||
|
Compression: opt.DefaultCompression,
|
||||||
|
}.New(folder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -289,138 +284,174 @@ func (w *worldState) Save(folder string) (*mcdb.Provider, error) {
|
||||||
return provider, err
|
return provider, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveAndReset writes the world to a folder, resets all the chunks
|
|
||||||
func (w *worldsHandler) SaveAndReset() {
|
func (w *worldsHandler) SaveAndReset() {
|
||||||
w.worldState.cullChunks()
|
w.worldState.cullChunks()
|
||||||
if len(w.worldState.chunks) == 0 {
|
if len(w.worldState.chunks) == 0 {
|
||||||
w.Reset(w.CurrentName())
|
w.Reset()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infof(locale.Loc("saving_world", locale.Strmap{"Name": w.worldState.Name, "Count": len(w.worldState.chunks)}))
|
worldStateCopy := w.worldState
|
||||||
w.gui.Message(messages.SavingWorld{
|
playerData := w.playerData()
|
||||||
Name: w.worldState.Name,
|
playerPos := w.serverState.PlayerPos.Position
|
||||||
Chunks: len(w.worldState.chunks),
|
|
||||||
})
|
|
||||||
|
|
||||||
// open world
|
|
||||||
folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.serverState.Name, w.worldState.Name))
|
|
||||||
os.RemoveAll(folder)
|
|
||||||
os.MkdirAll(folder, 0o777)
|
|
||||||
provider, err := w.worldState.Save(folder)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = provider.SaveLocalPlayerData(w.playerData())
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
playerPos := w.proxy.Server.GameData().PlayerPosition
|
|
||||||
spawnPos := cube.Pos{int(playerPos.X()), int(playerPos.Y()), int(playerPos.Z())}
|
spawnPos := cube.Pos{int(playerPos.X()), int(playerPos.Y()), int(playerPos.Z())}
|
||||||
|
|
||||||
// write metadata
|
var img image.Image
|
||||||
s := provider.Settings()
|
if w.settings.SaveImage {
|
||||||
s.Spawn = spawnPos
|
img = w.mapUI.ToImage()
|
||||||
s.Name = w.worldState.Name
|
|
||||||
|
|
||||||
// set gamerules
|
|
||||||
ld := provider.LevelDat()
|
|
||||||
gd := w.proxy.Server.GameData()
|
|
||||||
ld.RandomSeed = int64(gd.WorldSeed)
|
|
||||||
for _, gr := range gd.GameRules {
|
|
||||||
switch gr.Name {
|
|
||||||
case "commandblockoutput":
|
|
||||||
ld.CommandBlockOutput = gr.Value.(bool)
|
|
||||||
case "maxcommandchainlength":
|
|
||||||
ld.MaxCommandChainLength = int32(gr.Value.(uint32))
|
|
||||||
case "commandblocksenabled":
|
|
||||||
ld.CommandsEnabled = gr.Value.(bool)
|
|
||||||
case "dodaylightcycle":
|
|
||||||
ld.DoDayLightCycle = gr.Value.(bool)
|
|
||||||
case "doentitydrops":
|
|
||||||
ld.DoEntityDrops = gr.Value.(bool)
|
|
||||||
case "dofiretick":
|
|
||||||
ld.DoFireTick = gr.Value.(bool)
|
|
||||||
case "domobloot":
|
|
||||||
ld.DoMobLoot = gr.Value.(bool)
|
|
||||||
case "domobspawning":
|
|
||||||
ld.DoMobSpawning = gr.Value.(bool)
|
|
||||||
case "dotiledrops":
|
|
||||||
ld.DoTileDrops = gr.Value.(bool)
|
|
||||||
case "doweathercycle":
|
|
||||||
ld.DoWeatherCycle = gr.Value.(bool)
|
|
||||||
case "drowningdamage":
|
|
||||||
ld.DrowningDamage = gr.Value.(bool)
|
|
||||||
case "doinsomnia":
|
|
||||||
ld.DoInsomnia = gr.Value.(bool)
|
|
||||||
case "falldamage":
|
|
||||||
ld.FallDamage = gr.Value.(bool)
|
|
||||||
case "firedamage":
|
|
||||||
ld.FireDamage = gr.Value.(bool)
|
|
||||||
case "keepinventory":
|
|
||||||
ld.KeepInventory = gr.Value.(bool)
|
|
||||||
case "mobgriefing":
|
|
||||||
ld.MobGriefing = gr.Value.(bool)
|
|
||||||
case "pvp":
|
|
||||||
ld.PVP = gr.Value.(bool)
|
|
||||||
case "showcoordinates":
|
|
||||||
ld.ShowCoordinates = gr.Value.(bool)
|
|
||||||
case "naturalregeneration":
|
|
||||||
ld.NaturalRegeneration = gr.Value.(bool)
|
|
||||||
case "tntexplodes":
|
|
||||||
ld.TNTExplodes = gr.Value.(bool)
|
|
||||||
case "sendcommandfeedback":
|
|
||||||
ld.SendCommandFeedback = gr.Value.(bool)
|
|
||||||
case "randomtickspeed":
|
|
||||||
ld.RandomTickSpeed = int32(gr.Value.(uint32))
|
|
||||||
case "doimmediaterespawn":
|
|
||||||
ld.DoImmediateRespawn = gr.Value.(bool)
|
|
||||||
case "showdeathmessages":
|
|
||||||
ld.ShowDeathMessages = gr.Value.(bool)
|
|
||||||
case "functioncommandlimit":
|
|
||||||
ld.FunctionCommandLimit = int32(gr.Value.(uint32))
|
|
||||||
case "spawnradius":
|
|
||||||
ld.SpawnRadius = int32(gr.Value.(uint32))
|
|
||||||
case "showtags":
|
|
||||||
ld.ShowTags = gr.Value.(bool)
|
|
||||||
case "freezedamage":
|
|
||||||
ld.FreezeDamage = gr.Value.(bool)
|
|
||||||
case "respawnblocksexplode":
|
|
||||||
ld.RespawnBlocksExplode = gr.Value.(bool)
|
|
||||||
case "showbordereffect":
|
|
||||||
ld.ShowBorderEffect = gr.Value.(bool)
|
|
||||||
// todo
|
|
||||||
default:
|
|
||||||
logrus.Warnf(locale.Loc("unknown_gamerule", locale.Strmap{"Name": gr.Name}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// void world
|
|
||||||
if w.settings.VoidGen {
|
|
||||||
ld.FlatWorldLayers = `{"biome_id":1,"block_layers":[{"block_data":0,"block_id":0,"count":1},{"block_data":0,"block_id":0,"count":2},{"block_data":0,"block_id":0,"count":1}],"encoding_version":3,"structure_options":null}`
|
|
||||||
ld.Generator = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.bp.HasContent() {
|
|
||||||
if ld.Experiments == nil {
|
|
||||||
ld.Experiments = map[string]any{}
|
|
||||||
}
|
|
||||||
ld.Experiments["data_driven_items"] = true
|
|
||||||
ld.Experiments["experiments_ever_used"] = true
|
|
||||||
ld.Experiments["saved_with_toggled_experiments"] = true
|
|
||||||
}
|
|
||||||
ld.RandomTickSpeed = 0
|
|
||||||
s.CurrentTick = 0
|
|
||||||
provider.SaveSettings(s)
|
|
||||||
if err = provider.Close(); err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.serverState.worldCounter += 1
|
w.serverState.worldCounter += 1
|
||||||
|
w.Reset()
|
||||||
|
w.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer w.wg.Done()
|
||||||
|
logrus.Infof(locale.Loc("saving_world", locale.Strmap{"Name": worldStateCopy.Name, "Count": len(worldStateCopy.chunks)}))
|
||||||
|
w.gui.Message(messages.SavingWorld{
|
||||||
|
Name: w.worldState.Name,
|
||||||
|
Chunks: len(w.worldState.chunks),
|
||||||
|
})
|
||||||
|
|
||||||
|
// open world
|
||||||
|
folder := fmt.Sprintf("worlds/%s/%s", w.serverState.Name, worldStateCopy.Name)
|
||||||
|
os.RemoveAll(folder)
|
||||||
|
os.MkdirAll(folder, 0o777)
|
||||||
|
provider, err := worldStateCopy.Save(folder)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = provider.SaveLocalPlayerData(playerData)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// write metadata
|
||||||
|
s := provider.Settings()
|
||||||
|
s.Spawn = spawnPos
|
||||||
|
s.Name = worldStateCopy.Name
|
||||||
|
|
||||||
|
// set gamerules
|
||||||
|
ld := provider.LevelDat()
|
||||||
|
gd := w.proxy.Server.GameData()
|
||||||
|
ld.RandomSeed = int64(gd.WorldSeed)
|
||||||
|
for _, gr := range gd.GameRules {
|
||||||
|
switch gr.Name {
|
||||||
|
case "commandblockoutput":
|
||||||
|
ld.CommandBlockOutput = gr.Value.(bool)
|
||||||
|
case "maxcommandchainlength":
|
||||||
|
ld.MaxCommandChainLength = int32(gr.Value.(uint32))
|
||||||
|
case "commandblocksenabled":
|
||||||
|
ld.CommandsEnabled = gr.Value.(bool)
|
||||||
|
case "dodaylightcycle":
|
||||||
|
ld.DoDayLightCycle = gr.Value.(bool)
|
||||||
|
case "doentitydrops":
|
||||||
|
ld.DoEntityDrops = gr.Value.(bool)
|
||||||
|
case "dofiretick":
|
||||||
|
ld.DoFireTick = gr.Value.(bool)
|
||||||
|
case "domobloot":
|
||||||
|
ld.DoMobLoot = gr.Value.(bool)
|
||||||
|
case "domobspawning":
|
||||||
|
ld.DoMobSpawning = gr.Value.(bool)
|
||||||
|
case "dotiledrops":
|
||||||
|
ld.DoTileDrops = gr.Value.(bool)
|
||||||
|
case "doweathercycle":
|
||||||
|
ld.DoWeatherCycle = gr.Value.(bool)
|
||||||
|
case "drowningdamage":
|
||||||
|
ld.DrowningDamage = gr.Value.(bool)
|
||||||
|
case "doinsomnia":
|
||||||
|
ld.DoInsomnia = gr.Value.(bool)
|
||||||
|
case "falldamage":
|
||||||
|
ld.FallDamage = gr.Value.(bool)
|
||||||
|
case "firedamage":
|
||||||
|
ld.FireDamage = gr.Value.(bool)
|
||||||
|
case "keepinventory":
|
||||||
|
ld.KeepInventory = gr.Value.(bool)
|
||||||
|
case "mobgriefing":
|
||||||
|
ld.MobGriefing = gr.Value.(bool)
|
||||||
|
case "pvp":
|
||||||
|
ld.PVP = gr.Value.(bool)
|
||||||
|
case "showcoordinates":
|
||||||
|
ld.ShowCoordinates = gr.Value.(bool)
|
||||||
|
case "naturalregeneration":
|
||||||
|
ld.NaturalRegeneration = gr.Value.(bool)
|
||||||
|
case "tntexplodes":
|
||||||
|
ld.TNTExplodes = gr.Value.(bool)
|
||||||
|
case "sendcommandfeedback":
|
||||||
|
ld.SendCommandFeedback = gr.Value.(bool)
|
||||||
|
case "randomtickspeed":
|
||||||
|
ld.RandomTickSpeed = int32(gr.Value.(uint32))
|
||||||
|
case "doimmediaterespawn":
|
||||||
|
ld.DoImmediateRespawn = gr.Value.(bool)
|
||||||
|
case "showdeathmessages":
|
||||||
|
ld.ShowDeathMessages = gr.Value.(bool)
|
||||||
|
case "functioncommandlimit":
|
||||||
|
ld.FunctionCommandLimit = int32(gr.Value.(uint32))
|
||||||
|
case "spawnradius":
|
||||||
|
ld.SpawnRadius = int32(gr.Value.(uint32))
|
||||||
|
case "showtags":
|
||||||
|
ld.ShowTags = gr.Value.(bool)
|
||||||
|
case "freezedamage":
|
||||||
|
ld.FreezeDamage = gr.Value.(bool)
|
||||||
|
case "respawnblocksexplode":
|
||||||
|
ld.RespawnBlocksExplode = gr.Value.(bool)
|
||||||
|
case "showbordereffect":
|
||||||
|
ld.ShowBorderEffect = gr.Value.(bool)
|
||||||
|
// todo
|
||||||
|
default:
|
||||||
|
logrus.Warnf(locale.Loc("unknown_gamerule", locale.Strmap{"Name": gr.Name}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// void world
|
||||||
|
if w.settings.VoidGen {
|
||||||
|
ld.FlatWorldLayers = `{"biome_id":1,"block_layers":[{"block_data":0,"block_id":0,"count":1},{"block_data":0,"block_id":0,"count":2},{"block_data":0,"block_id":0,"count":1}],"encoding_version":3,"structure_options":null}`
|
||||||
|
ld.Generator = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
ld.RandomTickSpeed = 0
|
||||||
|
s.CurrentTick = 0
|
||||||
|
|
||||||
|
if w.bp.HasContent() {
|
||||||
|
if ld.Experiments == nil {
|
||||||
|
ld.Experiments = map[string]any{}
|
||||||
|
}
|
||||||
|
ld.Experiments["data_driven_items"] = true
|
||||||
|
ld.Experiments["experiments_ever_used"] = true
|
||||||
|
ld.Experiments["saved_with_toggled_experiments"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
provider.SaveSettings(s)
|
||||||
|
err = provider.Close()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.settings.SaveImage {
|
||||||
|
f, _ := os.Create(folder + ".png")
|
||||||
|
png.Encode(f, img)
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
w.AddPacks(folder)
|
||||||
|
|
||||||
|
// zip it
|
||||||
|
filename := folder + ".mcworld"
|
||||||
|
err = utils.ZipFolder(filename, folder)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logrus.Info(locale.Loc("saved", locale.Strmap{"Name": filename}))
|
||||||
|
//os.RemoveAll(folder)
|
||||||
|
w.gui.Message(messages.SetUIState(messages.UIStateMain))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *worldsHandler) AddPacks(folder string) {
|
||||||
type dep struct {
|
type dep struct {
|
||||||
PackID string `json:"pack_id"`
|
PackID string `json:"pack_id"`
|
||||||
Version [3]int `json:"version"`
|
Version [3]int `json:"version"`
|
||||||
|
@ -467,22 +498,31 @@ func (w *worldsHandler) SaveAndReset() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
} else {
|
} else {
|
||||||
|
packNames := make(map[string]int)
|
||||||
|
for _, pack := range packs {
|
||||||
|
packNames[pack.Name()] += 1
|
||||||
|
}
|
||||||
|
|
||||||
var rdeps []dep
|
var rdeps []dep
|
||||||
for k, p := range packs {
|
for _, pack := range packs {
|
||||||
if p.Encrypted() && !p.CanDecrypt() {
|
if pack.Encrypted() && !pack.CanDecrypt() {
|
||||||
logrus.Warnf("Cant add %s, it is encrypted", p.Name())
|
logrus.Warnf("Cant add %s, it is encrypted", pack.Name())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
logrus.Infof(locale.Loc("adding_pack", locale.Strmap{"Name": k}))
|
logrus.Infof(locale.Loc("adding_pack", locale.Strmap{"Name": pack.Name()}))
|
||||||
name := p.Name()
|
|
||||||
name = strings.ReplaceAll(name, ":", "_")
|
packName := pack.Name()
|
||||||
packFolder := path.Join(folder, "resource_packs", name)
|
if packNames[packName] > 1 {
|
||||||
|
packName += "_" + pack.UUID()
|
||||||
|
}
|
||||||
|
packName, _ = filenamify.FilenamifyV2(packName)
|
||||||
|
packFolder := path.Join(folder, "resource_packs", packName)
|
||||||
os.MkdirAll(packFolder, 0o755)
|
os.MkdirAll(packFolder, 0o755)
|
||||||
utils.UnpackZip(p, int64(p.Len()), packFolder)
|
utils.UnpackZip(pack, int64(pack.Len()), packFolder)
|
||||||
|
|
||||||
rdeps = append(rdeps, dep{
|
rdeps = append(rdeps, dep{
|
||||||
PackID: p.Manifest().Header.UUID,
|
PackID: pack.Manifest().Header.UUID,
|
||||||
Version: p.Manifest().Header.Version,
|
Version: pack.Manifest().Header.Version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(rdeps) > 0 {
|
if len(rdeps) > 0 {
|
||||||
|
@ -490,22 +530,6 @@ func (w *worldsHandler) SaveAndReset() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.settings.SaveImage {
|
|
||||||
f, _ := os.Create(folder + ".png")
|
|
||||||
png.Encode(f, w.mapUI.ToImage())
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// zip it
|
|
||||||
filename := folder + ".mcworld"
|
|
||||||
if err := utils.ZipFolder(filename, folder); err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
}
|
|
||||||
logrus.Info(locale.Loc("saved", locale.Strmap{"Name": filename}))
|
|
||||||
//os.RemoveAll(folder)
|
|
||||||
w.Reset(w.CurrentName())
|
|
||||||
w.gui.Message(messages.SetUIState(messages.UIStateMain))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worldsHandler) OnConnect(err error) bool {
|
func (w *worldsHandler) OnConnect(err error) bool {
|
||||||
|
|
|
@ -33,6 +33,7 @@ func (c *CaptureCMD) Execute(ctx context.Context, ui utils.UI) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
proxy.AlwaysGetPacks = true
|
||||||
proxy.AddHandler(handlers.NewPacketCapturer())
|
proxy.AddHandler(handlers.NewPacketCapturer())
|
||||||
return proxy.Run(ctx, address, hostname)
|
return proxy.Run(ctx, address, hostname)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/bedrock-tool/bedrocktool/locale"
|
"github.com/bedrock-tool/bedrocktool/locale"
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
||||||
"github.com/bedrock-tool/bedrocktool/utils"
|
"github.com/bedrock-tool/bedrocktool/utils"
|
||||||
|
|
||||||
"github.com/sandertv/gophertunnel/minecraft"
|
"github.com/sandertv/gophertunnel/minecraft"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,7 +42,7 @@ func (c *SkinCMD) Execute(ctx context.Context, ui utils.UI) error {
|
||||||
}))
|
}))
|
||||||
proxy.AddHandler(&utils.ProxyHandler{
|
proxy.AddHandler(&utils.ProxyHandler{
|
||||||
Name: "Skin CMD",
|
Name: "Skin CMD",
|
||||||
OnClientConnect: func(conn *minecraft.Conn) {
|
OnClientConnect: func(conn minecraft.IConn) {
|
||||||
ui.Message(messages.SetUIState(messages.UIStateConnecting))
|
ui.Message(messages.SetUIState(messages.UIStateConnecting))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,7 +22,7 @@ type Page interface {
|
||||||
NavItem() component.NavItem
|
NavItem() component.NavItem
|
||||||
|
|
||||||
// handle events from program
|
// handle events from program
|
||||||
Handler() HandlerFunc
|
Handler(data any) messages.MessageResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
|
@ -136,7 +136,7 @@ func (r *Router) Layout(gtx layout.Context, th *material.Theme) layout.Dimension
|
||||||
func (r *Router) Handler(data interface{}) messages.MessageResponse {
|
func (r *Router) Handler(data interface{}) messages.MessageResponse {
|
||||||
page, ok := r.pages[r.current]
|
page, ok := r.pages[r.current]
|
||||||
if ok {
|
if ok {
|
||||||
return page.Handler()(data)
|
return page.Handler(data)
|
||||||
}
|
}
|
||||||
return messages.MessageResponse{}
|
return messages.MessageResponse{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,6 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||||
p.Router.Wg.Add(1)
|
p.Router.Wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer p.Router.Wg.Done()
|
defer p.Router.Wg.Done()
|
||||||
utils.InitDNS()
|
|
||||||
|
|
||||||
err := cmd.Execute(p.Router.Ctx, utils.CurrentUI)
|
err := cmd.Execute(p.Router.Ctx, utils.CurrentUI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -174,11 +173,9 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) Handler() pages.HandlerFunc {
|
func (p *Page) Handler(any) messages.MessageResponse {
|
||||||
return func(data interface{}) messages.MessageResponse {
|
return messages.MessageResponse{
|
||||||
return messages.MessageResponse{
|
Ok: false,
|
||||||
Ok: false,
|
Data: nil,
|
||||||
Data: nil,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
"gioui.org/x/component"
|
"gioui.org/x/component"
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/gui"
|
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/gui/pages"
|
"github.com/bedrock-tool/bedrocktool/ui/gui/pages"
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
||||||
)
|
)
|
||||||
|
@ -96,7 +95,7 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||||
return layout.Flex{}.Layout(gtx)
|
return layout.Flex{}.Layout(gtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) handler(data interface{}) messages.MessageResponse {
|
func (p *Page) Handler(data interface{}) messages.MessageResponse {
|
||||||
r := messages.MessageResponse{
|
r := messages.MessageResponse{
|
||||||
Ok: false,
|
Ok: false,
|
||||||
Data: nil,
|
Data: nil,
|
||||||
|
@ -116,7 +115,3 @@ func (p *Page) handler(data interface{}) messages.MessageResponse {
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) Handler() gui.HandlerFunc {
|
|
||||||
return p.handler
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
"gioui.org/x/component"
|
"gioui.org/x/component"
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/gui"
|
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/gui/pages"
|
"github.com/bedrock-tool/bedrocktool/ui/gui/pages"
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
||||||
)
|
)
|
||||||
|
@ -115,7 +114,7 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||||
return layout.Dimensions{}
|
return layout.Dimensions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Page) handler(data interface{}) messages.MessageResponse {
|
func (u *Page) Handler(data any) messages.MessageResponse {
|
||||||
r := messages.MessageResponse{
|
r := messages.MessageResponse{
|
||||||
Ok: false,
|
Ok: false,
|
||||||
Data: nil,
|
Data: nil,
|
||||||
|
@ -148,7 +147,3 @@ func (u *Page) handler(data interface{}) messages.MessageResponse {
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) Handler() gui.HandlerFunc {
|
|
||||||
return p.handler
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package gui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gioui.org/layout"
|
|
||||||
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
|
||||||
)
|
|
||||||
|
|
||||||
type C = layout.Context
|
|
||||||
type D = layout.Dimensions
|
|
||||||
|
|
||||||
type HandlerFunc = func(data interface{}) messages.MessageResponse
|
|
|
@ -20,7 +20,7 @@ const (
|
||||||
UIStateFinished
|
UIStateFinished
|
||||||
)
|
)
|
||||||
|
|
||||||
type HandlerFunc = func(name string, data interface{}) MessageResponse
|
type HandlerFunc = func(data interface{}) MessageResponse
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
|
@ -72,11 +72,23 @@ func (bp *BehaviourPack) AddEntity(entity EntityIn) {
|
||||||
"value": scale,
|
"value": scale,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AlwaysShowName := entity.Meta.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagAlwaysShowName)
|
|
||||||
if AlwaysShowName {
|
width, widthOk := entity.Meta[protocol.EntityDataKeyWidth].(float32)
|
||||||
entry.MinecraftEntity.Components["minecraft:nameable"] = map[string]any{
|
height, heightOk := entity.Meta[protocol.EntityDataKeyHeight].(float32)
|
||||||
"always_show": true,
|
if widthOk || heightOk {
|
||||||
"allow_name_tag_renaming": false,
|
entry.MinecraftEntity.Components["minecraft:collision_box"] = map[string]any{
|
||||||
|
"width": width,
|
||||||
|
"height": height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := entity.Meta[protocol.EntityDataKeyFlags]; ok {
|
||||||
|
AlwaysShowName := entity.Meta.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagAlwaysShowName)
|
||||||
|
if AlwaysShowName {
|
||||||
|
entry.MinecraftEntity.Components["minecraft:nameable"] = map[string]any{
|
||||||
|
"always_show": true,
|
||||||
|
"allow_name_tag_renaming": false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA {
|
||||||
return blockColor
|
return blockColor
|
||||||
}
|
}
|
||||||
|
|
||||||
func Chunk2Img(c *chunk.Chunk, warn bool) *image.RGBA {
|
func Chunk2Img(c *chunk.Chunk) *image.RGBA {
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 16, 16))
|
img := image.NewRGBA(image.Rect(0, 0, 16, 16))
|
||||||
hm := c.HeightMapWithWater()
|
hm := c.HeightMapWithWater()
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
|
@ -42,9 +43,13 @@ func Enc(name string, data []byte) ([]byte, error) {
|
||||||
return w.Bytes(), nil
|
return w.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Encer(name string, w io.Writer) (io.WriteCloser, error) {
|
func Encer(filename string) (io.WriteCloser, error) {
|
||||||
|
w, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
wc, err := openpgp.Encrypt(w, recipients, nil, &openpgp.FileHints{
|
wc, err := openpgp.Encrypt(w, recipients, nil, &openpgp.FileHints{
|
||||||
IsBinary: true, FileName: name, ModTime: time.Now(),
|
IsBinary: true, FileName: filename, ModTime: time.Now(),
|
||||||
}, nil)
|
}, nil)
|
||||||
return wc, err
|
return wc, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build false
|
||||||
|
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -5,15 +5,11 @@ import (
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/png"
|
"image/png"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Img2rgba(img *image.RGBA) []color.RGBA {
|
func Img2rgba(img *image.RGBA) []color.RGBA {
|
||||||
header := *(*reflect.SliceHeader)(unsafe.Pointer(&img.Pix))
|
return unsafe.Slice((*color.RGBA)(unsafe.Pointer(unsafe.SliceData(img.Pix))), len(img.Pix)/4)
|
||||||
header.Len /= 4
|
|
||||||
header.Cap /= 4
|
|
||||||
return *(*[]color.RGBA)(unsafe.Pointer(&header))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPng(path string) (*image.RGBA, error) {
|
func loadPng(path string) (*image.RGBA, error) {
|
||||||
|
@ -25,7 +21,7 @@ func loadPng(path string) (*image.RGBA, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return (*image.RGBA)(img.(*image.NRGBA)), err
|
return (*image.RGBA)(img.(*image.NRGBA)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LERP is a linear interpolation function
|
// LERP is a linear interpolation function
|
||||||
|
|
|
@ -72,9 +72,6 @@ func (c *InteractiveCLI) Start(ctx context.Context, cancel context.CancelFunc) e
|
||||||
os.Args = append(os.Args, _cmd...)
|
os.Args = append(os.Args, _cmd...)
|
||||||
}
|
}
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
InitDNS()
|
|
||||||
|
|
||||||
subcommands.Execute(ctx)
|
subcommands.Execute(ctx)
|
||||||
|
|
||||||
if Options.IsInteractive {
|
if Options.IsInteractive {
|
||||||
|
|
186
utils/proxy.go
186
utils/proxy.go
|
@ -53,12 +53,12 @@ type (
|
||||||
|
|
||||||
type ProxyHandler struct {
|
type ProxyHandler struct {
|
||||||
Name string
|
Name string
|
||||||
ProxyRef func(*ProxyContext)
|
ProxyRef func(pc *ProxyContext)
|
||||||
//
|
//
|
||||||
AddressAndName func(address, hostname string) error
|
AddressAndName func(address, hostname string) error
|
||||||
|
|
||||||
// called to change game data
|
// called to change game data
|
||||||
GameDataModifier func(*minecraft.GameData)
|
GameDataModifier func(gd *minecraft.GameData)
|
||||||
|
|
||||||
// called for every packet
|
// called for every packet
|
||||||
PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr)
|
PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr)
|
||||||
|
@ -67,8 +67,8 @@ type ProxyHandler struct {
|
||||||
PacketCB func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error)
|
PacketCB func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error)
|
||||||
|
|
||||||
// called after client connected
|
// called after client connected
|
||||||
OnClientConnect func(conn *minecraft.Conn)
|
OnClientConnect func(conn minecraft.IConn)
|
||||||
SecondaryClientCB func(conn *minecraft.Conn)
|
SecondaryClientCB func(conn minecraft.IConn)
|
||||||
|
|
||||||
// called after game started
|
// called after game started
|
||||||
ConnectCB func(err error) bool
|
ConnectCB func(err error) bool
|
||||||
|
@ -78,8 +78,8 @@ type ProxyHandler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProxyContext struct {
|
type ProxyContext struct {
|
||||||
Server *minecraft.Conn
|
Server minecraft.IConn
|
||||||
Client *minecraft.Conn
|
Client minecraft.IConn
|
||||||
clientAddr net.Addr
|
clientAddr net.Addr
|
||||||
Listener *minecraft.Listener
|
Listener *minecraft.Listener
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ type ProxyContext struct {
|
||||||
func NewProxy() (*ProxyContext, error) {
|
func NewProxy() (*ProxyContext, error) {
|
||||||
p := &ProxyContext{
|
p := &ProxyContext{
|
||||||
commands: make(map[string]IngameCommand),
|
commands: make(map[string]IngameCommand),
|
||||||
|
AlwaysGetPacks: false,
|
||||||
WithClient: true,
|
WithClient: true,
|
||||||
IgnoreDisconnect: false,
|
IgnoreDisconnect: false,
|
||||||
}
|
}
|
||||||
|
@ -209,9 +210,9 @@ func (p *ProxyContext) AddHandler(handler *ProxyHandler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, toServer bool, _ time.Time) (packet.Packet, error) {
|
func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, toServer bool, _ time.Time) (packet.Packet, error) {
|
||||||
switch _pk := pk.(type) {
|
switch pk := pk.(type) {
|
||||||
case *packet.CommandRequest:
|
case *packet.CommandRequest:
|
||||||
cmd := strings.Split(_pk.CommandLine, " ")
|
cmd := strings.Split(pk.CommandLine, " ")
|
||||||
name := cmd[0][1:]
|
name := cmd[0][1:]
|
||||||
if h, ok := p.commands[name]; ok {
|
if h, ok := p.commands[name]; ok {
|
||||||
if h.Exec(cmd[1:]) {
|
if h.Exec(cmd[1:]) {
|
||||||
|
@ -219,20 +220,20 @@ func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, toServer bool, _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *packet.AvailableCommands:
|
case *packet.AvailableCommands:
|
||||||
cmds := make([]protocol.Command, len(p.commands))
|
cmds := make([]protocol.Command, 0, len(p.commands))
|
||||||
for _, ic := range p.commands {
|
for _, ic := range p.commands {
|
||||||
cmds = append(cmds, ic.Cmd)
|
cmds = append(cmds, ic.Cmd)
|
||||||
}
|
}
|
||||||
pk = &packet.AvailableCommands{
|
pk = &packet.AvailableCommands{
|
||||||
Constraints: _pk.Constraints,
|
Constraints: pk.Constraints,
|
||||||
Commands: append(_pk.Commands, cmds...),
|
Commands: append(pk.Commands, cmds...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pk, nil
|
return pk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool) error {
|
func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool) error {
|
||||||
var c1, c2 *minecraft.Conn
|
var c1, c2 minecraft.IConn
|
||||||
if toServer {
|
if toServer {
|
||||||
c1 = p.Client
|
c1 = p.Client
|
||||||
c2 = p.Server
|
c2 = p.Server
|
||||||
|
@ -282,10 +283,64 @@ func (p *ProxyContext) IsClient(addr net.Addr) bool {
|
||||||
|
|
||||||
var NewDebugLogger func(bool) *ProxyHandler
|
var NewDebugLogger func(bool) *ProxyHandler
|
||||||
|
|
||||||
|
func (p *ProxyContext) connectClient(ctx context.Context, serverAddress string, cdpp **login.ClientData) (err error) {
|
||||||
|
GetTokenSource() // ask for login before listening
|
||||||
|
|
||||||
|
var packs []*resource.Pack
|
||||||
|
if Options.Preload {
|
||||||
|
logrus.Info(locale.Loc("preloading_packs", nil))
|
||||||
|
serverConn, err := connectServer(ctx, serverAddress, nil, true, func(header packet.Header, payload []byte, src, dst net.Addr) {})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": serverAddress, "Err": err}))
|
||||||
|
}
|
||||||
|
serverConn.Close()
|
||||||
|
packs = serverConn.ResourcePacks()
|
||||||
|
logrus.Infof(locale.Locm("pack_count_loaded", locale.Strmap{"Count": len(packs)}, len(packs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
status := minecraft.NewStatusProvider(fmt.Sprintf("%s Proxy", serverAddress))
|
||||||
|
p.Listener, err = minecraft.ListenConfig{
|
||||||
|
StatusProvider: status,
|
||||||
|
ResourcePacks: packs,
|
||||||
|
AcceptedProtocols: []minecraft.Protocol{
|
||||||
|
//dummyProto{id: 567, ver: "1.19.60"},
|
||||||
|
},
|
||||||
|
}.Listen("raknet", ":19132")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof(locale.Loc("listening_on", locale.Strmap{"Address": p.Listener.Addr()}))
|
||||||
|
logrus.Infof(locale.Loc("help_connect", nil))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
p.Listener.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
c, err := p.Listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Client = c.(*minecraft.Conn)
|
||||||
|
cd := p.Client.ClientData()
|
||||||
|
*cdpp = &cd
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProxyContext) connectServer(ctx context.Context, serverAddress string, cdp *login.ClientData, packetFunc PacketFunc) (err error) {
|
||||||
|
p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, packetFunc)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err error) {
|
func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err error) {
|
||||||
if Options.Debug || Options.ExtraDebug {
|
if Options.Debug || Options.ExtraDebug {
|
||||||
p.AddHandler(NewDebugLogger(Options.ExtraDebug))
|
p.AddHandler(NewDebugLogger(Options.ExtraDebug))
|
||||||
}
|
}
|
||||||
|
p.AddHandler(&ProxyHandler{
|
||||||
|
Name: "Commands",
|
||||||
|
PacketCB: p.CommandHandlerPacketCB,
|
||||||
|
})
|
||||||
|
|
||||||
for _, handler := range p.handlers {
|
for _, handler := range p.handlers {
|
||||||
if handler.AddressAndName != nil {
|
if handler.AddressAndName != nil {
|
||||||
|
@ -296,54 +351,38 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
for _, handler := range p.handlers {
|
||||||
|
if handler.OnEnd != nil {
|
||||||
|
handler.OnEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
isReplay := false
|
||||||
if strings.HasPrefix(serverAddress, "PCAP!") {
|
if strings.HasPrefix(serverAddress, "PCAP!") {
|
||||||
return createReplayConnection(ctx, serverAddress[5:], p)
|
isReplay = true
|
||||||
}
|
}
|
||||||
|
|
||||||
GetTokenSource() // ask for login before listening
|
|
||||||
|
|
||||||
var cdp *login.ClientData = nil
|
var cdp *login.ClientData = nil
|
||||||
if p.WithClient {
|
if p.WithClient && !isReplay {
|
||||||
var packs []*resource.Pack
|
err = p.connectClient(ctx, serverAddress, &cdp)
|
||||||
if Options.Preload {
|
|
||||||
logrus.Info(locale.Loc("preloading_packs", nil))
|
|
||||||
serverConn, err := connectServer(ctx, serverAddress, nil, true, func(header packet.Header, payload []byte, src, dst net.Addr) {})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": serverAddress, "Err": err}))
|
|
||||||
}
|
|
||||||
serverConn.Close()
|
|
||||||
packs = serverConn.ResourcePacks()
|
|
||||||
logrus.Infof(locale.Locm("pack_count_loaded", locale.Strmap{"Count": len(packs)}, len(packs)))
|
|
||||||
}
|
|
||||||
|
|
||||||
_status := minecraft.NewStatusProvider("Server")
|
|
||||||
p.Listener, err = minecraft.ListenConfig{
|
|
||||||
StatusProvider: _status,
|
|
||||||
ResourcePacks: packs,
|
|
||||||
AcceptedProtocols: []minecraft.Protocol{
|
|
||||||
//dummyProto{id: 567, ver: "1.19.60"},
|
|
||||||
},
|
|
||||||
}.Listen("raknet", ":19132")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if p.Client != nil {
|
if p.Listener != nil {
|
||||||
p.Listener.Disconnect(p.Client, DisconnectReason)
|
if p.Client != nil {
|
||||||
|
p.Listener.Disconnect(p.Client.(*minecraft.Conn), DisconnectReason)
|
||||||
|
}
|
||||||
|
p.Listener.Close()
|
||||||
}
|
}
|
||||||
p.Listener.Close()
|
|
||||||
}()
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
logrus.Infof(locale.Loc("listening_on", locale.Strmap{"Address": p.Listener.Addr()}))
|
if p.CustomClientData != nil {
|
||||||
logrus.Infof(locale.Loc("help_connect", nil))
|
cdp = p.CustomClientData
|
||||||
|
|
||||||
c, err := p.Listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.Client = c.(*minecraft.Conn)
|
|
||||||
cd := p.Client.ClientData()
|
|
||||||
cdp = &cd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, handler := range p.handlers {
|
for _, handler := range p.handlers {
|
||||||
|
@ -353,11 +392,7 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
|
||||||
handler.OnClientConnect(p.Client)
|
handler.OnClientConnect(p.Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.CustomClientData != nil {
|
packetFunc := func(header packet.Header, payload []byte, src, dst net.Addr) {
|
||||||
cdp = p.CustomClientData
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, func(header packet.Header, payload []byte, src, dst net.Addr) {
|
|
||||||
if header.PacketID == packet.IDRequestNetworkSettings {
|
if header.PacketID == packet.IDRequestNetworkSettings {
|
||||||
p.clientAddr = src
|
p.clientAddr = src
|
||||||
}
|
}
|
||||||
|
@ -367,7 +402,16 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
|
||||||
}
|
}
|
||||||
handler.PacketFunc(header, payload, src, dst)
|
handler.PacketFunc(header, payload, src, dst)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if isReplay {
|
||||||
|
p.Server, err = createReplayConnector(serverAddress[5:], packetFunc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = p.connectServer(ctx, serverAddress, cdp, packetFunc)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, handler := range p.handlers {
|
for _, handler := range p.handlers {
|
||||||
if handler.ConnectCB == nil {
|
if handler.ConnectCB == nil {
|
||||||
|
@ -409,33 +453,23 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// append self to handlers for commands
|
|
||||||
p.handlers = append(p.handlers, &ProxyHandler{
|
|
||||||
Name: "Commands",
|
|
||||||
PacketCB: p.CommandHandlerPacketCB,
|
|
||||||
})
|
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
// server to client
|
doProxy := func(client bool) {
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := p.proxyLoop(ctx, false); err != nil {
|
if err := p.proxyLoop(ctx, client); err != nil {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
|
|
||||||
|
// server to client
|
||||||
|
wg.Add(1)
|
||||||
|
go doProxy(false)
|
||||||
|
|
||||||
// client to server
|
// client to server
|
||||||
if p.Client != nil {
|
if p.Client != nil {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go doProxy(true)
|
||||||
defer wg.Done()
|
|
||||||
if err := p.proxyLoop(ctx, true); err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wantSecondary := fp.Filter(func(handler *ProxyHandler) bool {
|
wantSecondary := fp.Filter(func(handler *ProxyHandler) bool {
|
||||||
|
@ -459,11 +493,5 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
for _, handler := range p.handlers {
|
|
||||||
if handler.OnEnd != nil {
|
|
||||||
handler.OnEnd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
528
utils/replay.go
528
utils/replay.go
|
@ -4,15 +4,18 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sandertv/gophertunnel/minecraft"
|
"github.com/sandertv/gophertunnel/minecraft"
|
||||||
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
"github.com/sandertv/gophertunnel/minecraft/protocol/login"
|
||||||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/resource"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,171 +37,430 @@ func WriteReplayHeader(f io.Writer) {
|
||||||
binary.Write(f, binary.LittleEndian, &header)
|
binary.Write(f, binary.LittleEndian, &header)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createReplayConnection(ctx context.Context, filename string, proxy *ProxyContext) error {
|
type replayConnector struct {
|
||||||
logrus.Infof("Reading replay %s", filename)
|
f *os.File
|
||||||
|
totalSize int64
|
||||||
|
ver int
|
||||||
|
|
||||||
f, err := os.Open(filename)
|
packets chan packet.Packet
|
||||||
if err != nil {
|
spawn chan struct{}
|
||||||
return err
|
close chan struct{}
|
||||||
}
|
once sync.Once
|
||||||
stat, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
totalSize := stat.Size()
|
|
||||||
|
|
||||||
// default version is version 1, since that didnt have a header
|
pool packet.Pool
|
||||||
ver := 1
|
proto minecraft.Protocol
|
||||||
|
clientData login.ClientData
|
||||||
|
|
||||||
|
gameData minecraft.GameData
|
||||||
|
|
||||||
|
packetFunc PacketFunc
|
||||||
|
|
||||||
|
downloadingPacks map[string]*downloadingPack
|
||||||
|
resourcePacks []*resource.Pack
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadingPack is a resource pack that is being downloaded by a client connection.
|
||||||
|
type downloadingPack struct {
|
||||||
|
buf *bytes.Buffer
|
||||||
|
chunkSize uint32
|
||||||
|
size uint64
|
||||||
|
expectedIndex uint32
|
||||||
|
newFrag chan []byte
|
||||||
|
contentKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) readHeader() error {
|
||||||
|
r.ver = 1
|
||||||
|
|
||||||
magic := make([]byte, 4)
|
magic := make([]byte, 4)
|
||||||
io.ReadAtLeast(f, magic, 4)
|
io.ReadAtLeast(r.f, magic, 4)
|
||||||
if bytes.Equal(magic, replayMagic) {
|
if bytes.Equal(magic, replayMagic) {
|
||||||
var header replayHeader
|
var header replayHeader
|
||||||
if err := binary.Read(f, binary.LittleEndian, &header); err != nil {
|
if err := binary.Read(r.f, binary.LittleEndian, &header); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ver = int(header.Version)
|
r.ver = int(header.Version)
|
||||||
} else {
|
} else {
|
||||||
logrus.Info("Version 1 capture assumed.")
|
logrus.Info("Version 1 capture assumed.")
|
||||||
f.Seek(-4, io.SeekCurrent)
|
r.f.Seek(-4, io.SeekCurrent)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) readPacket() (payload []byte, toServer bool, err error) {
|
||||||
|
var magic uint32 = 0
|
||||||
|
var packetLength uint32 = 0
|
||||||
|
timeReceived := time.Now()
|
||||||
|
|
||||||
|
offset, _ := r.f.Seek(0, io.SeekCurrent)
|
||||||
|
if offset == r.totalSize {
|
||||||
|
logrus.Info("Reached End")
|
||||||
|
return nil, toServer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
server, client := &net.UDPAddr{
|
binary.Read(r.f, binary.LittleEndian, &magic)
|
||||||
IP: net.IPv4(1, 1, 1, 1),
|
if magic != 0xAAAAAAAA {
|
||||||
}, &net.UDPAddr{
|
return nil, toServer, fmt.Errorf("wrong Magic")
|
||||||
IP: net.IPv4(2, 2, 2, 2),
|
}
|
||||||
|
binary.Read(r.f, binary.LittleEndian, &packetLength)
|
||||||
|
binary.Read(r.f, binary.LittleEndian, &toServer)
|
||||||
|
if r.ver >= 2 {
|
||||||
|
var timeMs int64
|
||||||
|
binary.Read(r.f, binary.LittleEndian, &timeMs)
|
||||||
|
timeReceived = time.UnixMilli(timeMs)
|
||||||
}
|
}
|
||||||
proxy.clientAddr = client
|
|
||||||
|
|
||||||
proxy.Server = minecraft.NewConn()
|
payload = make([]byte, packetLength)
|
||||||
|
n, err := r.f.Read(payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, toServer, err
|
||||||
|
}
|
||||||
|
if n != int(packetLength) {
|
||||||
|
return nil, toServer, fmt.Errorf("truncated")
|
||||||
|
}
|
||||||
|
|
||||||
|
var magic2 uint32
|
||||||
|
binary.Read(r.f, binary.LittleEndian, &magic2)
|
||||||
|
if magic2 != 0xBBBBBBBB {
|
||||||
|
return nil, toServer, fmt.Errorf("wrong Magic2")
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = timeReceived
|
||||||
|
return payload, toServer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) handleLoginSequence(pk packet.Packet) (bool, error) {
|
||||||
|
switch pk := pk.(type) {
|
||||||
|
case *packet.StartGame:
|
||||||
|
r.SetGameData(minecraft.GameData{
|
||||||
|
WorldName: pk.WorldName,
|
||||||
|
WorldSeed: pk.WorldSeed,
|
||||||
|
Difficulty: pk.Difficulty,
|
||||||
|
EntityUniqueID: pk.EntityUniqueID,
|
||||||
|
EntityRuntimeID: pk.EntityRuntimeID,
|
||||||
|
PlayerGameMode: pk.PlayerGameMode,
|
||||||
|
PersonaDisabled: pk.PersonaDisabled,
|
||||||
|
CustomSkinsDisabled: pk.CustomSkinsDisabled,
|
||||||
|
BaseGameVersion: pk.BaseGameVersion,
|
||||||
|
PlayerPosition: pk.PlayerPosition,
|
||||||
|
Pitch: pk.Pitch,
|
||||||
|
Yaw: pk.Yaw,
|
||||||
|
Dimension: pk.Dimension,
|
||||||
|
WorldSpawn: pk.WorldSpawn,
|
||||||
|
EditorWorld: pk.EditorWorld,
|
||||||
|
WorldGameMode: pk.WorldGameMode,
|
||||||
|
GameRules: pk.GameRules,
|
||||||
|
Time: pk.Time,
|
||||||
|
ServerBlockStateChecksum: pk.ServerBlockStateChecksum,
|
||||||
|
CustomBlocks: pk.Blocks,
|
||||||
|
Items: pk.Items,
|
||||||
|
PlayerMovementSettings: pk.PlayerMovementSettings,
|
||||||
|
ServerAuthoritativeInventory: pk.ServerAuthoritativeInventory,
|
||||||
|
Experiments: pk.Experiments,
|
||||||
|
ClientSideGeneration: pk.ClientSideGeneration,
|
||||||
|
ChatRestrictionLevel: pk.ChatRestrictionLevel,
|
||||||
|
DisablePlayerInteractions: pk.DisablePlayerInteractions,
|
||||||
|
})
|
||||||
|
|
||||||
|
case *packet.ResourcePacksInfo:
|
||||||
|
for _, pack := range pk.TexturePacks {
|
||||||
|
r.downloadingPacks[pack.UUID] = &downloadingPack{
|
||||||
|
size: pack.Size,
|
||||||
|
buf: bytes.NewBuffer(make([]byte, 0, pack.Size)),
|
||||||
|
newFrag: make(chan []byte),
|
||||||
|
contentKey: pack.ContentKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *packet.ResourcePackDataInfo:
|
||||||
|
pack, ok := r.downloadingPacks[pk.UUID]
|
||||||
|
if !ok {
|
||||||
|
// We either already downloaded the pack or we got sent an invalid UUID, that did not match any pack
|
||||||
|
// sent in the ResourcePacksInfo packet.
|
||||||
|
return false, fmt.Errorf("unknown pack to download with UUID %v", pk.UUID)
|
||||||
|
}
|
||||||
|
if pack.size != pk.Size {
|
||||||
|
// Size mismatch: The ResourcePacksInfo packet had a size for the pack that did not match with the
|
||||||
|
// size sent here.
|
||||||
|
logrus.Printf("pack %v had a different size in the ResourcePacksInfo packet than the ResourcePackDataInfo packet\n", pk.UUID)
|
||||||
|
pack.size = pk.Size
|
||||||
|
}
|
||||||
|
pack.chunkSize = pk.DataChunkSize
|
||||||
|
|
||||||
|
chunkCount := uint32(pk.Size / uint64(pk.DataChunkSize))
|
||||||
|
if pk.Size%uint64(pk.DataChunkSize) != 0 {
|
||||||
|
chunkCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for i := uint32(0); i < chunkCount; i++ {
|
||||||
|
select {
|
||||||
|
case <-r.close:
|
||||||
|
return
|
||||||
|
case frag := <-pack.newFrag:
|
||||||
|
// Write the fragment to the full buffer of the downloading resource pack.
|
||||||
|
_, _ = pack.buf.Write(frag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pack.buf.Len() != int(pack.size) {
|
||||||
|
logrus.Printf("incorrect resource pack size: expected %v, but got %v\n", pack.size, pack.buf.Len())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// First parse the resource pack from the total byte buffer we obtained.
|
||||||
|
newPack, err := resource.FromBytes(pack.buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
logrus.Printf("invalid full resource pack data for UUID %v: %v\n", pk.UUID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.resourcePacks = append(r.resourcePacks, newPack.WithContentKey(pack.contentKey))
|
||||||
|
}()
|
||||||
|
|
||||||
|
case *packet.ResourcePackChunkData:
|
||||||
|
pack, ok := r.downloadingPacks[pk.UUID]
|
||||||
|
if !ok {
|
||||||
|
// We haven't received a ResourcePackDataInfo packet from the server, so we can't use this data to
|
||||||
|
// download a resource pack.
|
||||||
|
return false, fmt.Errorf("resource pack chunk data for resource pack that was not being downloaded")
|
||||||
|
}
|
||||||
|
lastData := pack.buf.Len()+int(pack.chunkSize) >= int(pack.size)
|
||||||
|
if !lastData && uint32(len(pk.Data)) != pack.chunkSize {
|
||||||
|
// The chunk data didn't have the full size and wasn't the last data to be sent for the resource pack,
|
||||||
|
// meaning we got too little data.
|
||||||
|
return false, fmt.Errorf("resource pack chunk data had a length of %v, but expected %v", len(pk.Data), pack.chunkSize)
|
||||||
|
}
|
||||||
|
if pk.ChunkIndex != pack.expectedIndex {
|
||||||
|
return false, fmt.Errorf("resource pack chunk data had chunk index %v, but expected %v", pk.ChunkIndex, pack.expectedIndex)
|
||||||
|
}
|
||||||
|
pack.expectedIndex++
|
||||||
|
pack.newFrag <- pk.Data
|
||||||
|
|
||||||
|
case *packet.SetLocalPlayerAsInitialised:
|
||||||
|
if pk.EntityRuntimeID != r.gameData.EntityRuntimeID {
|
||||||
|
return false, fmt.Errorf("entity runtime ID mismatch: entity runtime ID in StartGame and SetLocalPlayerAsInitialised packets should be equal")
|
||||||
|
}
|
||||||
|
close(r.spawn)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) loop() {
|
||||||
gameStarted := false
|
gameStarted := false
|
||||||
i := 0
|
defer r.Close()
|
||||||
for {
|
for {
|
||||||
i += 1
|
payload, toServer, err := r.readPacket()
|
||||||
var magic uint32 = 0
|
|
||||||
var packetLength uint32 = 0
|
|
||||||
var toServer bool = false
|
|
||||||
timeReceived := time.Now()
|
|
||||||
|
|
||||||
offset, _ := f.Seek(0, io.SeekCurrent)
|
|
||||||
if offset == totalSize {
|
|
||||||
logrus.Info("Reached End")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
binary.Read(f, binary.LittleEndian, &magic)
|
|
||||||
if magic != 0xAAAAAAAA {
|
|
||||||
return fmt.Errorf("wrong Magic")
|
|
||||||
}
|
|
||||||
binary.Read(f, binary.LittleEndian, &packetLength)
|
|
||||||
binary.Read(f, binary.LittleEndian, &toServer)
|
|
||||||
if ver >= 2 {
|
|
||||||
var timeMs int64
|
|
||||||
binary.Read(f, binary.LittleEndian, &timeMs)
|
|
||||||
timeReceived = time.UnixMilli(timeMs)
|
|
||||||
}
|
|
||||||
|
|
||||||
payload := make([]byte, packetLength)
|
|
||||||
n, err := f.Read(payload)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if n != int(packetLength) {
|
if payload == nil {
|
||||||
return fmt.Errorf("truncated %d", i)
|
return
|
||||||
|
}
|
||||||
|
var src, dst = r.RemoteAddr(), r.LocalAddr()
|
||||||
|
if toServer {
|
||||||
|
src, dst = r.LocalAddr(), r.RemoteAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
var magic2 uint32
|
pkData, err := minecraft.ParseData(payload, r, src, dst)
|
||||||
binary.Read(f, binary.LittleEndian, &magic2)
|
|
||||||
if magic2 != 0xBBBBBBBB {
|
|
||||||
return fmt.Errorf("wrong Magic2")
|
|
||||||
}
|
|
||||||
|
|
||||||
pkData, err := minecraft.ParseData(payload, proxy.Server)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
logrus.Error(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
pks, err := pkData.Decode(proxy.Server)
|
pks, err := pkData.Decode(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pk := range pks {
|
for _, pk := range pks {
|
||||||
f := bytes.NewBuffer(nil)
|
if !gameStarted {
|
||||||
b := protocol.NewWriter(f, 0)
|
gameStarted, _ = r.handleLoginSequence(pk)
|
||||||
pk.Marshal(b)
|
|
||||||
|
|
||||||
hdr := packet.Header{PacketID: pk.ID()}
|
|
||||||
|
|
||||||
var src, dst net.Addr
|
|
||||||
if toServer {
|
|
||||||
src = client
|
|
||||||
dst = server
|
|
||||||
} else {
|
} else {
|
||||||
src = server
|
r.packets <- pk
|
||||||
dst = client
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, handler := range proxy.handlers {
|
|
||||||
if handler.PacketFunc != nil {
|
|
||||||
handler.PacketFunc(hdr, f.Bytes(), src, dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if gameStarted {
|
|
||||||
for _, handler := range proxy.handlers {
|
|
||||||
if handler.PacketCB != nil {
|
|
||||||
handler.PacketCB(pk, toServer, timeReceived)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch pk := pk.(type) {
|
|
||||||
case *packet.StartGame:
|
|
||||||
proxy.Server.SetGameData(minecraft.GameData{
|
|
||||||
WorldName: pk.WorldName,
|
|
||||||
WorldSeed: pk.WorldSeed,
|
|
||||||
Difficulty: pk.Difficulty,
|
|
||||||
EntityUniqueID: pk.EntityUniqueID,
|
|
||||||
EntityRuntimeID: pk.EntityRuntimeID,
|
|
||||||
PlayerGameMode: pk.PlayerGameMode,
|
|
||||||
PersonaDisabled: pk.PersonaDisabled,
|
|
||||||
CustomSkinsDisabled: pk.CustomSkinsDisabled,
|
|
||||||
BaseGameVersion: pk.BaseGameVersion,
|
|
||||||
PlayerPosition: pk.PlayerPosition,
|
|
||||||
Pitch: pk.Pitch,
|
|
||||||
Yaw: pk.Yaw,
|
|
||||||
Dimension: pk.Dimension,
|
|
||||||
WorldSpawn: pk.WorldSpawn,
|
|
||||||
EditorWorld: pk.EditorWorld,
|
|
||||||
WorldGameMode: pk.WorldGameMode,
|
|
||||||
GameRules: pk.GameRules,
|
|
||||||
Time: pk.Time,
|
|
||||||
ServerBlockStateChecksum: pk.ServerBlockStateChecksum,
|
|
||||||
CustomBlocks: pk.Blocks,
|
|
||||||
Items: pk.Items,
|
|
||||||
PlayerMovementSettings: pk.PlayerMovementSettings,
|
|
||||||
ServerAuthoritativeInventory: pk.ServerAuthoritativeInventory,
|
|
||||||
Experiments: pk.Experiments,
|
|
||||||
ClientSideGeneration: pk.ClientSideGeneration,
|
|
||||||
ChatRestrictionLevel: pk.ChatRestrictionLevel,
|
|
||||||
DisablePlayerInteractions: pk.DisablePlayerInteractions,
|
|
||||||
})
|
|
||||||
gameStarted = true
|
|
||||||
for _, handler := range proxy.handlers {
|
|
||||||
if handler.ConnectCB != nil {
|
|
||||||
handler.ConnectCB(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, handler := range proxy.handlers {
|
}
|
||||||
if handler.OnEnd != nil {
|
|
||||||
handler.OnEnd()
|
func createReplayConnector(filename string, packetFunc PacketFunc) (r *replayConnector, err error) {
|
||||||
}
|
r = &replayConnector{
|
||||||
|
pool: packet.NewPool(),
|
||||||
|
proto: minecraft.DefaultProtocol,
|
||||||
|
packetFunc: packetFunc,
|
||||||
|
spawn: make(chan struct{}),
|
||||||
|
close: make(chan struct{}),
|
||||||
|
packets: make(chan packet.Packet),
|
||||||
|
downloadingPacks: make(map[string]*downloadingPack),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.Infof("Reading replay %s", filename)
|
||||||
|
|
||||||
|
r.f, err = os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stat, err := r.f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.totalSize = stat.Size()
|
||||||
|
|
||||||
|
err = r.readHeader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
go r.loop()
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) Close() error {
|
||||||
|
r.once.Do(func() {
|
||||||
|
close(r.close)
|
||||||
|
close(r.packets)
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) Authenticated() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) ChunkRadius() int {
|
||||||
|
return 80
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) ClientCacheEnabled() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) ClientData() login.ClientData {
|
||||||
|
return r.clientData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) DoSpawn() error {
|
||||||
|
return r.DoSpawnContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) DoSpawnContext(ctx context.Context) error {
|
||||||
|
select {
|
||||||
|
case <-r.close:
|
||||||
|
return errors.New("do spawn")
|
||||||
|
case <-ctx.Done():
|
||||||
|
return errors.New("do spawn")
|
||||||
|
case <-r.spawn:
|
||||||
|
// Conn was spawned successfully.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) DoSpawnTimeout(timeout time.Duration) error {
|
||||||
|
c, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
return r.DoSpawnContext(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) Flush() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) GameData() minecraft.GameData {
|
||||||
|
return r.gameData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) IdentityData() login.IdentityData {
|
||||||
|
return login.IdentityData{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) Latency() time.Duration {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) LocalAddr() net.Addr {
|
||||||
|
return &net.UDPAddr{
|
||||||
|
IP: net.IPv4(1, 1, 1, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) Read(b []byte) (n int, err error) {
|
||||||
|
return 0, errors.New("not Implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) ReadPacket() (pk packet.Packet, err error) {
|
||||||
|
select {
|
||||||
|
case <-r.close:
|
||||||
|
return nil, net.ErrClosed
|
||||||
|
case p, ok := <-r.packets:
|
||||||
|
if !ok {
|
||||||
|
err = net.ErrClosed
|
||||||
|
}
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) Write(b []byte) (n int, err error) {
|
||||||
|
return 0, errors.New("not Implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) WritePacket(pk packet.Packet) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) RemoteAddr() net.Addr {
|
||||||
|
return &net.UDPAddr{
|
||||||
|
IP: net.IPv4(2, 2, 2, 2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) ResourcePacks() []*resource.Pack {
|
||||||
|
return r.resourcePacks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) SetGameData(data minecraft.GameData) {
|
||||||
|
r.gameData = data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) StartGame(data minecraft.GameData) error {
|
||||||
|
return r.StartGameContext(context.Background(), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) StartGameContext(ctx context.Context, data minecraft.GameData) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) StartGameTimeout(data minecraft.GameData, timeout time.Duration) error {
|
||||||
|
c, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
return r.StartGameContext(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) SetDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) SetReadDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) SetWriteDeadline(time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) Pool() packet.Pool {
|
||||||
|
return r.pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) ShieldID() int32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) Proto() minecraft.Protocol {
|
||||||
|
return r.proto
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *replayConnector) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) {
|
||||||
|
if r.packetFunc != nil {
|
||||||
|
r.packetFunc(header, payload, src, dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -60,8 +60,7 @@ var PackFromBase = func(pack *resource.Pack) Pack {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPacks(server *minecraft.Conn) (packs map[string]Pack, err error) {
|
func GetPacks(server minecraft.IConn) (packs []Pack, err error) {
|
||||||
packs = make(map[string]Pack)
|
|
||||||
for _, pack := range server.ResourcePacks() {
|
for _, pack := range server.ResourcePacks() {
|
||||||
pack := PackFromBase(pack)
|
pack := PackFromBase(pack)
|
||||||
if pack.Encrypted() && pack.CanDecrypt() {
|
if pack.Encrypted() && pack.CanDecrypt() {
|
||||||
|
@ -73,9 +72,9 @@ func GetPacks(server *minecraft.Conn) (packs map[string]Pack, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
packs[pack.Name()] = &Packb{pack2}
|
packs = append(packs, &Packb{pack2})
|
||||||
} else {
|
} else {
|
||||||
packs[pack.Name()] = pack
|
packs = append(packs, pack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
@ -76,7 +76,7 @@ func connectServer(ctx context.Context, address string, ClientData *login.Client
|
||||||
return serverConn, nil
|
return serverConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func spawnConn(ctx context.Context, clientConn *minecraft.Conn, serverConn *minecraft.Conn, gd minecraft.GameData) error {
|
func spawnConn(ctx context.Context, clientConn minecraft.IConn, serverConn minecraft.IConn, gd minecraft.GameData) error {
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
errs := make(chan error, 2)
|
errs := make(chan error, 2)
|
||||||
if clientConn != nil {
|
if clientConn != nil {
|
||||||
|
|
Loading…
Reference in New Issue