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**
|
||||
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**
|
||||
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.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
- OS: [e.g. windows]
|
||||
- Version [e.g. 1.28.0-36]
|
||||
- Minecraft Version [e.g. 1.19.73]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
- OS: [e.g. iOS12]
|
||||
- Version [e.g. 1.28.0-36]
|
||||
|
||||
**Additional context**
|
||||
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 {
|
||||
flag.Parse()
|
||||
utils.InitDNS()
|
||||
subcommands.Execute(ctx)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
cancel()
|
||||
|
|
5
go.mod
5
go.mod
|
@ -3,10 +3,10 @@ module github.com/bedrock-tool/bedrocktool
|
|||
go 1.20
|
||||
|
||||
//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 => 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 => 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/df-mc/atomic v1.10.0 // 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-text/typesetting v0.0.0-20221214153724-0399769901d5 // 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/worldupgrader v1.0.3 h1:3nbthy6vfSNQZdqHBR+E5Fh3mCeWmCwLtqrYDiPUG5I=
|
||||
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/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
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-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-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/go.mod h1:+W1Kpf96YcfissZocFqIp6O42FDTuphkObbEybp+Ffc=
|
||||
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-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.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
|
@ -14,23 +13,22 @@ import (
|
|||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||
)
|
||||
|
||||
var dumpLock sync.Mutex
|
||||
|
||||
func dumpPacket(f io.WriteCloser, toServer bool, payload []byte) {
|
||||
dumpLock.Lock()
|
||||
defer dumpLock.Unlock()
|
||||
f.Write([]byte{0xAA, 0xAA, 0xAA, 0xAA})
|
||||
func (p *packetCapturer) dumpPacket(toServer bool, payload []byte) {
|
||||
p.dumpLock.Lock()
|
||||
defer p.dumpLock.Unlock()
|
||||
p.fio.Write([]byte{0xAA, 0xAA, 0xAA, 0xAA})
|
||||
packetSize := uint32(len(payload))
|
||||
binary.Write(f, binary.LittleEndian, packetSize)
|
||||
binary.Write(f, binary.LittleEndian, toServer)
|
||||
binary.Write(f, binary.LittleEndian, time.Now().UnixMilli())
|
||||
f.Write(payload)
|
||||
f.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB})
|
||||
binary.Write(p.fio, binary.LittleEndian, packetSize)
|
||||
binary.Write(p.fio, binary.LittleEndian, toServer)
|
||||
binary.Write(p.fio, binary.LittleEndian, time.Now().UnixMilli())
|
||||
p.fio.Write(payload)
|
||||
p.fio.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB})
|
||||
}
|
||||
|
||||
type packetCapturer struct {
|
||||
proxy *utils.ProxyContext
|
||||
fio *os.File
|
||||
proxy *utils.ProxyContext
|
||||
fio *os.File
|
||||
dumpLock sync.Mutex
|
||||
}
|
||||
|
||||
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) {
|
||||
IsfromClient := p.proxy.IsClient(src)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
header.Write(buf)
|
||||
buf.Write(payload)
|
||||
dumpPacket(p.fio, IsfromClient, buf.Bytes())
|
||||
p.dumpPacket(p.proxy.IsClient(src), buf.Bytes())
|
||||
}
|
||||
|
||||
func NewPacketCapturer() *utils.ProxyHandler {
|
||||
|
@ -63,8 +59,8 @@ func NewPacketCapturer() *utils.ProxyHandler {
|
|||
AddressAndName: p.AddressAndName,
|
||||
PacketFunc: p.PacketFunc,
|
||||
OnEnd: func() {
|
||||
dumpLock.Lock()
|
||||
defer dumpLock.Unlock()
|
||||
p.dumpLock.Lock()
|
||||
defer p.dumpLock.Unlock()
|
||||
p.fio.Close()
|
||||
},
|
||||
}
|
||||
|
|
|
@ -48,11 +48,6 @@ var MutedPackets = []string{
|
|||
"packet.PlaySound",
|
||||
}
|
||||
|
||||
var (
|
||||
FLog io.Writer
|
||||
dmpLock sync.Mutex
|
||||
)
|
||||
|
||||
func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s string) {
|
||||
tBase := strings.Repeat("\t", level)
|
||||
|
||||
|
@ -173,15 +168,6 @@ func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s stri
|
|||
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 dirC2S = color.CyanString("C") + "->" + color.GreenString("S")
|
||||
var pool = packet.NewPool()
|
||||
|
@ -189,6 +175,7 @@ var pool = packet.NewPool()
|
|||
func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
||||
var logPlain, logCrypt, logCryptEnc io.WriteCloser
|
||||
var packetsLogF *bufio.Writer
|
||||
var dmpLock sync.Mutex
|
||||
|
||||
if extraVerbose {
|
||||
// open plain text log
|
||||
|
@ -196,19 +183,14 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
|||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
||||
// open gpg log
|
||||
logCrypt, err := os.Create("packets.log.gpg")
|
||||
logCryptEnc, err = crypt.Encer("packets.log.gpg")
|
||||
if err != nil {
|
||||
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
|
||||
|
@ -233,7 +215,7 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
|||
}()
|
||||
pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0))
|
||||
|
||||
if extraVerbose {
|
||||
if packetsLogF != nil {
|
||||
dmpLock.Lock()
|
||||
packetsLogF.Write([]byte(dmpStruct(0, pk, true, false) + "\n\n\n"))
|
||||
dmpLock.Unlock()
|
||||
|
@ -249,6 +231,7 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
|||
}
|
||||
},
|
||||
OnEnd: func() {
|
||||
dmpLock.Lock()
|
||||
if packetsLogF != nil {
|
||||
packetsLogF.Flush()
|
||||
}
|
||||
|
@ -261,6 +244,7 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler {
|
|||
if logCrypt != nil {
|
||||
logCrypt.Close()
|
||||
}
|
||||
dmpLock.Unlock()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,9 +80,8 @@ func (s *secondaryUser) processLevelChunk(pk *packet.LevelChunk) {
|
|||
}
|
||||
|
||||
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 {
|
||||
offsets = append(offsets, sub.Offset)
|
||||
var (
|
||||
absX = pk.Position[0] + int32(sub.Offset[0])
|
||||
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}
|
||||
pos = world.ChunkPos{absX, absZ}
|
||||
)
|
||||
offsets[pos] = true
|
||||
ch, ok := s.chunks[pos]
|
||||
if !ok {
|
||||
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() {
|
||||
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
|
||||
},
|
||||
SecondaryClientCB: s.SecondaryClientCB,
|
||||
OnClientConnect: func(conn *minecraft.Conn) {
|
||||
OnClientConnect: func(conn minecraft.IConn) {
|
||||
id := conn.IdentityData()
|
||||
s.mainPlayer = player.New(id.DisplayName, skin.New(64, 64), mgl64.Vec3{0, 00})
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -56,13 +56,13 @@ func (s *SkinSaver) AddSkin(playerName string, playerID uuid.UUID, playerSkin *p
|
|||
}
|
||||
}
|
||||
if !strings.HasPrefix(playerName, s.PlayerNameFilter) {
|
||||
return "", nil, false
|
||||
return playerName, nil, false
|
||||
}
|
||||
s.playerNames[playerID] = playerName
|
||||
|
||||
skin := &utils.Skin{Skin: playerSkin}
|
||||
if s.OnlyIfHasGeometry && !skin.HaveGeometry() {
|
||||
return "", nil, false
|
||||
return playerName, nil, false
|
||||
}
|
||||
wasAdded := s.AddPlayerSkin(playerID, playerName, skin)
|
||||
|
||||
|
|
|
@ -11,18 +11,12 @@ import (
|
|||
)
|
||||
|
||||
func (w *worldsHandler) processChangeDimension(pk *packet.ChangeDimension) {
|
||||
if len(w.worldState.chunks) > 0 {
|
||||
w.SaveAndReset()
|
||||
} else {
|
||||
logrus.Info(locale.Loc("not_saving_empty", nil))
|
||||
w.Reset(w.CurrentName())
|
||||
}
|
||||
w.SaveAndReset()
|
||||
dimensionID := pk.Dimension
|
||||
if w.serverState.ispre118 && dimensionID == 0 {
|
||||
dimensionID += 10
|
||||
}
|
||||
d, _ := world.DimensionByID(int(dimensionID))
|
||||
w.worldState.dimension = d
|
||||
w.worldState.dimension, _ = world.DimensionByID(int(dimensionID))
|
||||
}
|
||||
|
||||
func (w *worldsHandler) processLevelChunk(pk *packet.LevelChunk) {
|
||||
|
@ -158,7 +152,7 @@ func (w *worldsHandler) ProcessChunkPackets(pk packet.Packet) packet.Packet {
|
|||
if ok {
|
||||
x, y, z := blockPosInChunk(pk.Position)
|
||||
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:
|
||||
|
@ -174,7 +168,7 @@ func (w *worldsHandler) ProcessChunkPackets(pk packet.Packet) packet.Packet {
|
|||
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) {
|
||||
data, _ := os.ReadFile("chunk.bin")
|
||||
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")
|
||||
png.Encode(f, i)
|
||||
f.Close()
|
||||
|
@ -47,7 +47,7 @@ func Benchmark_render_chunk(b *testing.B) {
|
|||
b.Error(err)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
utils.Chunk2Img(ch, true)
|
||||
utils.Chunk2Img(ch)
|
||||
}
|
||||
pprof.StopCPUProfile()
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ func (e serverEntity) Type() world.EntityType {
|
|||
}
|
||||
|
||||
func (w *worldsHandler) processAddActor(pk *packet.AddActor) {
|
||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
||||
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||
if !ok {
|
||||
e = &entityState{
|
||||
RuntimeID: pk.EntityRuntimeID,
|
||||
|
@ -197,7 +197,7 @@ func (s *entityState) ToServerEntity() serverEntity {
|
|||
Encoded: s.EntityType,
|
||||
NBT: map[string]any{
|
||||
"Pos": vec3float32(s.Position),
|
||||
"Rotation": []float32{s.Yaw, s.Pitch},
|
||||
"Rotation": []float32{s.HeadYaw, s.Pitch},
|
||||
"Motion": vec3float32(s.Velocity),
|
||||
"UniqueID": int64(s.UniqueID),
|
||||
},
|
||||
|
@ -216,6 +216,11 @@ func (s *entityState) ToServerEntity() serverEntity {
|
|||
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 {
|
||||
if !w.settings.SaveEntities {
|
||||
return pk
|
||||
|
@ -226,7 +231,7 @@ func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
|||
w.processAddActor(pk)
|
||||
case *packet.RemoveActor:
|
||||
case *packet.SetActorData:
|
||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
||||
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||
if ok {
|
||||
e.Metadata = pk.EntityMetadata
|
||||
w.bp.AddEntity(behaviourpack.EntityIn{
|
||||
|
@ -236,12 +241,12 @@ func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
|||
})
|
||||
}
|
||||
case *packet.SetActorMotion:
|
||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
||||
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||
if ok {
|
||||
e.Velocity = pk.Velocity
|
||||
}
|
||||
case *packet.MoveActorDelta:
|
||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
||||
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||
if ok {
|
||||
if pk.Flags&packet.MoveActorDeltaFlagHasX != 0 {
|
||||
e.Position[0] = pk.Position[0]
|
||||
|
@ -263,14 +268,14 @@ func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
|||
//}
|
||||
}
|
||||
case *packet.MoveActorAbsolute:
|
||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
||||
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||
if ok {
|
||||
e.Position = pk.Position
|
||||
e.Pitch = pk.Rotation.X()
|
||||
e.Yaw = pk.Rotation.Y()
|
||||
}
|
||||
case *packet.MobEquipment:
|
||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
||||
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||
if ok {
|
||||
w, ok := e.Inventory[pk.WindowID]
|
||||
if !ok {
|
||||
|
@ -280,7 +285,7 @@ func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
|
|||
w[pk.HotBarSlot] = pk.NewItem
|
||||
}
|
||||
case *packet.MobArmourEquipment:
|
||||
e, ok := w.worldState.entities[pk.EntityRuntimeID]
|
||||
e, ok := w.getEntity(pk.EntityRuntimeID)
|
||||
if ok {
|
||||
e.Helmet = &pk.Helmet
|
||||
e.Chestplate = &pk.Chestplate
|
||||
|
|
|
@ -112,10 +112,6 @@ func (w *worldsHandler) processItemPacketsServer(pk packet.Packet) packet.Packet
|
|||
|
||||
case *packet.ItemComponent:
|
||||
w.bp.ApplyComponentEntries(pk.Items)
|
||||
case *packet.MobArmourEquipment:
|
||||
if pk.EntityRuntimeID == w.proxy.Server.GameData().EntityRuntimeID {
|
||||
|
||||
}
|
||||
}
|
||||
return pk
|
||||
}
|
||||
|
@ -190,199 +186,3 @@ func stackToItem(it protocol.ItemStack) item.Stack {
|
|||
s := item.NewStack(t, int(it.Count))
|
||||
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
|
||||
|
||||
// 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,
|
||||
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) {
|
||||
// get the chunk coord bounds
|
||||
i := 0
|
||||
for _ch := range m.renderedChunks {
|
||||
if _ch.X() < min.X() || i == 0 {
|
||||
min[0] = _ch.X()
|
||||
}
|
||||
if _ch.Z() < min.Z() || i == 0 {
|
||||
min[1] = _ch.Z()
|
||||
}
|
||||
if _ch.X() > max.X() || i == 0 {
|
||||
max[0] = _ch.X()
|
||||
}
|
||||
if _ch.Z() > max.Z() || i == 0 {
|
||||
max[1] = _ch.Z()
|
||||
}
|
||||
i++
|
||||
if len(m.renderedChunks) == 0 {
|
||||
return
|
||||
}
|
||||
min = protocol.ChunkPos{math.MaxInt32, math.MaxInt32}
|
||||
for chunk := range m.renderedChunks {
|
||||
min[0] = imin(min[0], chunk[0])
|
||||
min[1] = imin(min[1], chunk[1])
|
||||
max[0] = imax(max[0], chunk[0])
|
||||
max[1] = imax(max[1], chunk[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type RenderElem struct {
|
||||
pos protocol.ChunkPos
|
||||
ch *chunk.Chunk
|
||||
complete bool
|
||||
pos protocol.ChunkPos
|
||||
ch *chunk.Chunk
|
||||
}
|
||||
|
||||
type MapUI struct {
|
||||
|
@ -106,9 +112,6 @@ func (m *MapUI) Start() {
|
|||
MapID: ViewMapID,
|
||||
Scale: 4,
|
||||
MapsIncludedIn: []int64{ViewMapID},
|
||||
Width: 0,
|
||||
Height: 0,
|
||||
Pixels: nil,
|
||||
UpdateFlags: packet.MapUpdateFlagInitialisation,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -190,9 +193,7 @@ func (m *MapUI) Redraw() {
|
|||
break
|
||||
}
|
||||
if r.ch != nil {
|
||||
m.renderedChunks[r.pos] = utils.Chunk2Img(r.ch, !r.complete)
|
||||
} else {
|
||||
m.renderedChunks[r.pos] = black16x16
|
||||
m.renderedChunks[r.pos] = utils.Chunk2Img(r.ch)
|
||||
}
|
||||
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
|
||||
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()
|
||||
for pos, tile := range m.renderedChunks {
|
||||
|
@ -252,17 +253,17 @@ func (m *MapUI) ToImage() *image.RGBA {
|
|||
int((pos.X()-min.X())*16),
|
||||
int((pos.Z()-min.Z())*16),
|
||||
)
|
||||
draw.Draw(img2, image.Rect(
|
||||
draw.Draw(img, image.Rect(
|
||||
px.X, px.Y,
|
||||
px.X+16, px.Y+16,
|
||||
), tile, image.Point{}, draw.Src)
|
||||
}
|
||||
m.l.RUnlock()
|
||||
return img2
|
||||
return img
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bedrock-tool/bedrocktool/locale"
|
||||
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
||||
"github.com/bedrock-tool/bedrocktool/utils"
|
||||
"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/world"
|
||||
|
@ -72,6 +74,7 @@ type serverState struct {
|
|||
|
||||
type worldsHandler struct {
|
||||
ctx context.Context
|
||||
wg sync.WaitGroup
|
||||
proxy *utils.ProxyContext
|
||||
mapUI *MapUI
|
||||
gui utils.UI
|
||||
|
@ -82,34 +85,22 @@ type worldsHandler struct {
|
|||
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 {
|
||||
w := &worldsHandler{
|
||||
ctx: ctx,
|
||||
mapUI: nil,
|
||||
gui: ui,
|
||||
bp: nil,
|
||||
ctx: ctx,
|
||||
gui: ui,
|
||||
|
||||
serverState: serverState{
|
||||
ispre118: false,
|
||||
worldCounter: 0,
|
||||
ChunkRadius: 0,
|
||||
|
||||
playerInventory: nil,
|
||||
PlayerPos: TPlayerPos{},
|
||||
PlayerPos: TPlayerPos{},
|
||||
},
|
||||
|
||||
settings: settings,
|
||||
}
|
||||
w.mapUI = NewMapUI(w)
|
||||
w.Reset(w.CurrentName())
|
||||
w.Reset()
|
||||
|
||||
return &utils.ProxyHandler{
|
||||
Name: "Worlds",
|
||||
|
@ -148,7 +139,7 @@ func NewWorldsHandler(ctx context.Context, ui utils.UI, settings WorldSettings)
|
|||
w.serverState.Name = hostname
|
||||
return nil
|
||||
},
|
||||
OnClientConnect: func(conn *minecraft.Conn) {
|
||||
OnClientConnect: func(conn minecraft.IConn) {
|
||||
w.gui.Message(messages.SetUIState(messages.UIStateConnecting))
|
||||
},
|
||||
GameDataModifier: func(gd *minecraft.GameData) {
|
||||
|
@ -181,6 +172,7 @@ func NewWorldsHandler(ctx context.Context, ui utils.UI, settings WorldSettings)
|
|||
},
|
||||
OnEnd: func() {
|
||||
w.SaveAndReset()
|
||||
w.wg.Wait()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -215,22 +207,22 @@ func (w *worldsHandler) setWorldName(val string, fromUI bool) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (w *worldsHandler) CurrentName() string {
|
||||
func (w *worldsHandler) currentName() string {
|
||||
worldName := "world"
|
||||
if w.serverState.worldCounter > 1 {
|
||||
if w.serverState.worldCounter > 0 {
|
||||
worldName = fmt.Sprintf("world-%d", w.serverState.worldCounter)
|
||||
}
|
||||
return worldName
|
||||
}
|
||||
|
||||
func (w *worldsHandler) Reset(newName string) {
|
||||
func (w *worldsHandler) Reset() {
|
||||
w.worldState = worldState{
|
||||
dimension: w.worldState.dimension,
|
||||
chunks: make(map[protocol.ChunkPos]*chunk.Chunk),
|
||||
blockNBT: make(map[protocol.SubChunkPos][]map[string]any),
|
||||
entities: make(map[uint64]*entityState),
|
||||
openItemContainers: make(map[byte]*itemContainer),
|
||||
Name: newName,
|
||||
Name: w.currentName(),
|
||||
}
|
||||
w.mapUI.Reset()
|
||||
}
|
||||
|
@ -249,8 +241,11 @@ func (w *worldState) cullChunks() {
|
|||
}
|
||||
}
|
||||
|
||||
func (w *worldState) Save(folder string) (*mcdb.Provider, error) {
|
||||
provider, err := mcdb.New(logrus.StandardLogger(), folder, opt.DefaultCompression)
|
||||
func (w *worldState) Save(folder string) (*mcdb.DB, error) {
|
||||
provider, err := mcdb.Config{
|
||||
Log: logrus.StandardLogger(),
|
||||
Compression: opt.DefaultCompression,
|
||||
}.New(folder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -289,138 +284,174 @@ func (w *worldState) Save(folder string) (*mcdb.Provider, error) {
|
|||
return provider, err
|
||||
}
|
||||
|
||||
// SaveAndReset writes the world to a folder, resets all the chunks
|
||||
func (w *worldsHandler) SaveAndReset() {
|
||||
w.worldState.cullChunks()
|
||||
if len(w.worldState.chunks) == 0 {
|
||||
w.Reset(w.CurrentName())
|
||||
w.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Infof(locale.Loc("saving_world", locale.Strmap{"Name": w.worldState.Name, "Count": len(w.worldState.chunks)}))
|
||||
w.gui.Message(messages.SavingWorld{
|
||||
Name: w.worldState.Name,
|
||||
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
|
||||
worldStateCopy := w.worldState
|
||||
playerData := w.playerData()
|
||||
playerPos := w.serverState.PlayerPos.Position
|
||||
spawnPos := cube.Pos{int(playerPos.X()), int(playerPos.Y()), int(playerPos.Z())}
|
||||
|
||||
// write metadata
|
||||
s := provider.Settings()
|
||||
s.Spawn = spawnPos
|
||||
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)
|
||||
var img image.Image
|
||||
if w.settings.SaveImage {
|
||||
img = w.mapUI.ToImage()
|
||||
}
|
||||
|
||||
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 {
|
||||
PackID string `json:"pack_id"`
|
||||
Version [3]int `json:"version"`
|
||||
|
@ -467,22 +498,31 @@ func (w *worldsHandler) SaveAndReset() {
|
|||
if err != nil {
|
||||
logrus.Error(err)
|
||||
} else {
|
||||
packNames := make(map[string]int)
|
||||
for _, pack := range packs {
|
||||
packNames[pack.Name()] += 1
|
||||
}
|
||||
|
||||
var rdeps []dep
|
||||
for k, p := range packs {
|
||||
if p.Encrypted() && !p.CanDecrypt() {
|
||||
logrus.Warnf("Cant add %s, it is encrypted", p.Name())
|
||||
for _, pack := range packs {
|
||||
if pack.Encrypted() && !pack.CanDecrypt() {
|
||||
logrus.Warnf("Cant add %s, it is encrypted", pack.Name())
|
||||
continue
|
||||
}
|
||||
logrus.Infof(locale.Loc("adding_pack", locale.Strmap{"Name": k}))
|
||||
name := p.Name()
|
||||
name = strings.ReplaceAll(name, ":", "_")
|
||||
packFolder := path.Join(folder, "resource_packs", name)
|
||||
logrus.Infof(locale.Loc("adding_pack", locale.Strmap{"Name": pack.Name()}))
|
||||
|
||||
packName := pack.Name()
|
||||
if packNames[packName] > 1 {
|
||||
packName += "_" + pack.UUID()
|
||||
}
|
||||
packName, _ = filenamify.FilenamifyV2(packName)
|
||||
packFolder := path.Join(folder, "resource_packs", packName)
|
||||
os.MkdirAll(packFolder, 0o755)
|
||||
utils.UnpackZip(p, int64(p.Len()), packFolder)
|
||||
utils.UnpackZip(pack, int64(pack.Len()), packFolder)
|
||||
|
||||
rdeps = append(rdeps, dep{
|
||||
PackID: p.Manifest().Header.UUID,
|
||||
Version: p.Manifest().Header.Version,
|
||||
PackID: pack.Manifest().Header.UUID,
|
||||
Version: pack.Manifest().Header.Version,
|
||||
})
|
||||
}
|
||||
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 {
|
||||
|
|
|
@ -33,6 +33,7 @@ func (c *CaptureCMD) Execute(ctx context.Context, ui utils.UI) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proxy.AlwaysGetPacks = true
|
||||
proxy.AddHandler(handlers.NewPacketCapturer())
|
||||
return proxy.Run(ctx, address, hostname)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/bedrock-tool/bedrocktool/locale"
|
||||
"github.com/bedrock-tool/bedrocktool/ui/messages"
|
||||
"github.com/bedrock-tool/bedrocktool/utils"
|
||||
|
||||
"github.com/sandertv/gophertunnel/minecraft"
|
||||
)
|
||||
|
||||
|
@ -43,7 +42,7 @@ func (c *SkinCMD) Execute(ctx context.Context, ui utils.UI) error {
|
|||
}))
|
||||
proxy.AddHandler(&utils.ProxyHandler{
|
||||
Name: "Skin CMD",
|
||||
OnClientConnect: func(conn *minecraft.Conn) {
|
||||
OnClientConnect: func(conn minecraft.IConn) {
|
||||
ui.Message(messages.SetUIState(messages.UIStateConnecting))
|
||||
},
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ type Page interface {
|
|||
NavItem() component.NavItem
|
||||
|
||||
// handle events from program
|
||||
Handler() HandlerFunc
|
||||
Handler(data any) messages.MessageResponse
|
||||
}
|
||||
|
||||
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 {
|
||||
page, ok := r.pages[r.current]
|
||||
if ok {
|
||||
return page.Handler()(data)
|
||||
return page.Handler(data)
|
||||
}
|
||||
return messages.MessageResponse{}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,6 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
|||
p.Router.Wg.Add(1)
|
||||
go func() {
|
||||
defer p.Router.Wg.Done()
|
||||
utils.InitDNS()
|
||||
|
||||
err := cmd.Execute(p.Router.Ctx, utils.CurrentUI)
|
||||
if err != nil {
|
||||
|
@ -174,11 +173,9 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *Page) Handler() pages.HandlerFunc {
|
||||
return func(data interface{}) messages.MessageResponse {
|
||||
return messages.MessageResponse{
|
||||
Ok: false,
|
||||
Data: nil,
|
||||
}
|
||||
func (p *Page) Handler(any) messages.MessageResponse {
|
||||
return messages.MessageResponse{
|
||||
Ok: false,
|
||||
Data: nil,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"gioui.org/widget"
|
||||
"gioui.org/widget/material"
|
||||
"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/messages"
|
||||
)
|
||||
|
@ -96,7 +95,7 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
|||
return layout.Flex{}.Layout(gtx)
|
||||
}
|
||||
|
||||
func (p *Page) handler(data interface{}) messages.MessageResponse {
|
||||
func (p *Page) Handler(data interface{}) messages.MessageResponse {
|
||||
r := messages.MessageResponse{
|
||||
Ok: false,
|
||||
Data: nil,
|
||||
|
@ -116,7 +115,3 @@ func (p *Page) handler(data interface{}) messages.MessageResponse {
|
|||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *Page) Handler() gui.HandlerFunc {
|
||||
return p.handler
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"gioui.org/widget"
|
||||
"gioui.org/widget/material"
|
||||
"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/messages"
|
||||
)
|
||||
|
@ -115,7 +114,7 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
|||
return layout.Dimensions{}
|
||||
}
|
||||
|
||||
func (u *Page) handler(data interface{}) messages.MessageResponse {
|
||||
func (u *Page) Handler(data any) messages.MessageResponse {
|
||||
r := messages.MessageResponse{
|
||||
Ok: false,
|
||||
Data: nil,
|
||||
|
@ -148,7 +147,3 @@ func (u *Page) handler(data interface{}) messages.MessageResponse {
|
|||
}
|
||||
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
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
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,
|
||||
|
||||
width, widthOk := entity.Meta[protocol.EntityDataKeyWidth].(float32)
|
||||
height, heightOk := entity.Meta[protocol.EntityDataKeyHeight].(float32)
|
||||
if widthOk || heightOk {
|
||||
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
|
||||
}
|
||||
|
||||
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))
|
||||
hm := c.HeightMapWithWater()
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
_ "embed"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
|
@ -42,9 +43,13 @@ func Enc(name string, data []byte) ([]byte, error) {
|
|||
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{
|
||||
IsBinary: true, FileName: name, ModTime: time.Now(),
|
||||
IsBinary: true, FileName: filename, ModTime: time.Now(),
|
||||
}, nil)
|
||||
return wc, err
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//go:build false
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
|
|
|
@ -5,15 +5,11 @@ import (
|
|||
"image/color"
|
||||
"image/png"
|
||||
"os"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Img2rgba(img *image.RGBA) []color.RGBA {
|
||||
header := *(*reflect.SliceHeader)(unsafe.Pointer(&img.Pix))
|
||||
header.Len /= 4
|
||||
header.Cap /= 4
|
||||
return *(*[]color.RGBA)(unsafe.Pointer(&header))
|
||||
return unsafe.Slice((*color.RGBA)(unsafe.Pointer(unsafe.SliceData(img.Pix))), len(img.Pix)/4)
|
||||
}
|
||||
|
||||
func loadPng(path string) (*image.RGBA, error) {
|
||||
|
@ -25,7 +21,7 @@ func loadPng(path string) (*image.RGBA, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*image.RGBA)(img.(*image.NRGBA)), err
|
||||
return (*image.RGBA)(img.(*image.NRGBA)), nil
|
||||
}
|
||||
|
||||
// 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...)
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
InitDNS()
|
||||
|
||||
subcommands.Execute(ctx)
|
||||
|
||||
if Options.IsInteractive {
|
||||
|
|
186
utils/proxy.go
186
utils/proxy.go
|
@ -53,12 +53,12 @@ type (
|
|||
|
||||
type ProxyHandler struct {
|
||||
Name string
|
||||
ProxyRef func(*ProxyContext)
|
||||
ProxyRef func(pc *ProxyContext)
|
||||
//
|
||||
AddressAndName func(address, hostname string) error
|
||||
|
||||
// called to change game data
|
||||
GameDataModifier func(*minecraft.GameData)
|
||||
GameDataModifier func(gd *minecraft.GameData)
|
||||
|
||||
// called for every packet
|
||||
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)
|
||||
|
||||
// called after client connected
|
||||
OnClientConnect func(conn *minecraft.Conn)
|
||||
SecondaryClientCB func(conn *minecraft.Conn)
|
||||
OnClientConnect func(conn minecraft.IConn)
|
||||
SecondaryClientCB func(conn minecraft.IConn)
|
||||
|
||||
// called after game started
|
||||
ConnectCB func(err error) bool
|
||||
|
@ -78,8 +78,8 @@ type ProxyHandler struct {
|
|||
}
|
||||
|
||||
type ProxyContext struct {
|
||||
Server *minecraft.Conn
|
||||
Client *minecraft.Conn
|
||||
Server minecraft.IConn
|
||||
Client minecraft.IConn
|
||||
clientAddr net.Addr
|
||||
Listener *minecraft.Listener
|
||||
|
||||
|
@ -95,6 +95,7 @@ type ProxyContext struct {
|
|||
func NewProxy() (*ProxyContext, error) {
|
||||
p := &ProxyContext{
|
||||
commands: make(map[string]IngameCommand),
|
||||
AlwaysGetPacks: false,
|
||||
WithClient: true,
|
||||
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) {
|
||||
switch _pk := pk.(type) {
|
||||
switch pk := pk.(type) {
|
||||
case *packet.CommandRequest:
|
||||
cmd := strings.Split(_pk.CommandLine, " ")
|
||||
cmd := strings.Split(pk.CommandLine, " ")
|
||||
name := cmd[0][1:]
|
||||
if h, ok := p.commands[name]; ok {
|
||||
if h.Exec(cmd[1:]) {
|
||||
|
@ -219,20 +220,20 @@ func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, toServer bool, _
|
|||
}
|
||||
}
|
||||
case *packet.AvailableCommands:
|
||||
cmds := make([]protocol.Command, len(p.commands))
|
||||
cmds := make([]protocol.Command, 0, len(p.commands))
|
||||
for _, ic := range p.commands {
|
||||
cmds = append(cmds, ic.Cmd)
|
||||
}
|
||||
pk = &packet.AvailableCommands{
|
||||
Constraints: _pk.Constraints,
|
||||
Commands: append(_pk.Commands, cmds...),
|
||||
Constraints: pk.Constraints,
|
||||
Commands: append(pk.Commands, cmds...),
|
||||
}
|
||||
}
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool) error {
|
||||
var c1, c2 *minecraft.Conn
|
||||
var c1, c2 minecraft.IConn
|
||||
if toServer {
|
||||
c1 = p.Client
|
||||
c2 = p.Server
|
||||
|
@ -282,10 +283,64 @@ func (p *ProxyContext) IsClient(addr net.Addr) bool {
|
|||
|
||||
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) {
|
||||
if Options.Debug || Options.ExtraDebug {
|
||||
p.AddHandler(NewDebugLogger(Options.ExtraDebug))
|
||||
}
|
||||
p.AddHandler(&ProxyHandler{
|
||||
Name: "Commands",
|
||||
PacketCB: p.CommandHandlerPacketCB,
|
||||
})
|
||||
|
||||
for _, handler := range p.handlers {
|
||||
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!") {
|
||||
return createReplayConnection(ctx, serverAddress[5:], p)
|
||||
isReplay = true
|
||||
}
|
||||
|
||||
GetTokenSource() // ask for login before listening
|
||||
|
||||
var cdp *login.ClientData = nil
|
||||
if p.WithClient {
|
||||
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("Server")
|
||||
p.Listener, err = minecraft.ListenConfig{
|
||||
StatusProvider: _status,
|
||||
ResourcePacks: packs,
|
||||
AcceptedProtocols: []minecraft.Protocol{
|
||||
//dummyProto{id: 567, ver: "1.19.60"},
|
||||
},
|
||||
}.Listen("raknet", ":19132")
|
||||
if p.WithClient && !isReplay {
|
||||
err = p.connectClient(ctx, serverAddress, &cdp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if p.Client != nil {
|
||||
p.Listener.Disconnect(p.Client, DisconnectReason)
|
||||
if p.Listener != nil {
|
||||
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()}))
|
||||
logrus.Infof(locale.Loc("help_connect", nil))
|
||||
|
||||
c, err := p.Listener.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Client = c.(*minecraft.Conn)
|
||||
cd := p.Client.ClientData()
|
||||
cdp = &cd
|
||||
if p.CustomClientData != nil {
|
||||
cdp = p.CustomClientData
|
||||
}
|
||||
|
||||
for _, handler := range p.handlers {
|
||||
|
@ -353,11 +392,7 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
|
|||
handler.OnClientConnect(p.Client)
|
||||
}
|
||||
|
||||
if p.CustomClientData != nil {
|
||||
cdp = p.CustomClientData
|
||||
}
|
||||
|
||||
p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, func(header packet.Header, payload []byte, src, dst net.Addr) {
|
||||
packetFunc := func(header packet.Header, payload []byte, src, dst net.Addr) {
|
||||
if header.PacketID == packet.IDRequestNetworkSettings {
|
||||
p.clientAddr = src
|
||||
}
|
||||
|
@ -367,7 +402,16 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
|
|||
}
|
||||
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 {
|
||||
for _, handler := range p.handlers {
|
||||
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{}
|
||||
// server to client
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
doProxy := func(client bool) {
|
||||
defer wg.Done()
|
||||
if err := p.proxyLoop(ctx, false); err != nil {
|
||||
if err := p.proxyLoop(ctx, client); err != nil {
|
||||
logrus.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// server to client
|
||||
wg.Add(1)
|
||||
go doProxy(false)
|
||||
|
||||
// client to server
|
||||
if p.Client != nil {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := p.proxyLoop(ctx, true); err != nil {
|
||||
logrus.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
go doProxy(true)
|
||||
}
|
||||
|
||||
wantSecondary := fp.Filter(func(handler *ProxyHandler) bool {
|
||||
|
@ -459,11 +493,5 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
|
|||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
for _, handler := range p.handlers {
|
||||
if handler.OnEnd != nil {
|
||||
handler.OnEnd()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
528
utils/replay.go
528
utils/replay.go
|
@ -4,15 +4,18 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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/resource"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -34,171 +37,430 @@ func WriteReplayHeader(f io.Writer) {
|
|||
binary.Write(f, binary.LittleEndian, &header)
|
||||
}
|
||||
|
||||
func createReplayConnection(ctx context.Context, filename string, proxy *ProxyContext) error {
|
||||
logrus.Infof("Reading replay %s", filename)
|
||||
type replayConnector struct {
|
||||
f *os.File
|
||||
totalSize int64
|
||||
ver int
|
||||
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
totalSize := stat.Size()
|
||||
packets chan packet.Packet
|
||||
spawn chan struct{}
|
||||
close chan struct{}
|
||||
once sync.Once
|
||||
|
||||
// default version is version 1, since that didnt have a header
|
||||
ver := 1
|
||||
pool packet.Pool
|
||||
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)
|
||||
io.ReadAtLeast(f, magic, 4)
|
||||
io.ReadAtLeast(r.f, magic, 4)
|
||||
if bytes.Equal(magic, replayMagic) {
|
||||
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
|
||||
}
|
||||
ver = int(header.Version)
|
||||
r.ver = int(header.Version)
|
||||
} else {
|
||||
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{
|
||||
IP: net.IPv4(1, 1, 1, 1),
|
||||
}, &net.UDPAddr{
|
||||
IP: net.IPv4(2, 2, 2, 2),
|
||||
binary.Read(r.f, binary.LittleEndian, &magic)
|
||||
if magic != 0xAAAAAAAA {
|
||||
return nil, toServer, fmt.Errorf("wrong Magic")
|
||||
}
|
||||
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
|
||||
i := 0
|
||||
defer r.Close()
|
||||
for {
|
||||
i += 1
|
||||
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)
|
||||
payload, toServer, err := r.readPacket()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
break
|
||||
}
|
||||
if n != int(packetLength) {
|
||||
return fmt.Errorf("truncated %d", i)
|
||||
if payload == nil {
|
||||
return
|
||||
}
|
||||
var src, dst = r.RemoteAddr(), r.LocalAddr()
|
||||
if toServer {
|
||||
src, dst = r.LocalAddr(), r.RemoteAddr()
|
||||
}
|
||||
|
||||
var magic2 uint32
|
||||
binary.Read(f, binary.LittleEndian, &magic2)
|
||||
if magic2 != 0xBBBBBBBB {
|
||||
return fmt.Errorf("wrong Magic2")
|
||||
}
|
||||
|
||||
pkData, err := minecraft.ParseData(payload, proxy.Server)
|
||||
pkData, err := minecraft.ParseData(payload, r, src, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
logrus.Error(err)
|
||||
return
|
||||
}
|
||||
pks, err := pkData.Decode(proxy.Server)
|
||||
pks, err := pkData.Decode(r)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pk := range pks {
|
||||
f := bytes.NewBuffer(nil)
|
||||
b := protocol.NewWriter(f, 0)
|
||||
pk.Marshal(b)
|
||||
|
||||
hdr := packet.Header{PacketID: pk.ID()}
|
||||
|
||||
var src, dst net.Addr
|
||||
if toServer {
|
||||
src = client
|
||||
dst = server
|
||||
if !gameStarted {
|
||||
gameStarted, _ = r.handleLoginSequence(pk)
|
||||
} else {
|
||||
src = server
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
r.packets <- pk
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func GetPacks(server *minecraft.Conn) (packs map[string]Pack, err error) {
|
||||
packs = make(map[string]Pack)
|
||||
func GetPacks(server minecraft.IConn) (packs []Pack, err error) {
|
||||
for _, pack := range server.ResourcePacks() {
|
||||
pack := PackFromBase(pack)
|
||||
if pack.Encrypted() && pack.CanDecrypt() {
|
||||
|
@ -73,9 +72,9 @@ func GetPacks(server *minecraft.Conn) (packs map[string]Pack, err error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packs[pack.Name()] = &Packb{pack2}
|
||||
packs = append(packs, &Packb{pack2})
|
||||
} else {
|
||||
packs[pack.Name()] = pack
|
||||
packs = append(packs, pack)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
|
|
@ -76,7 +76,7 @@ func connectServer(ctx context.Context, address string, ClientData *login.Client
|
|||
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{}
|
||||
errs := make(chan error, 2)
|
||||
if clientConn != nil {
|
||||
|
|
Loading…
Reference in New Issue