diff --git a/subcommands/capture.go b/subcommands/capture.go index c21a774..09b2220 100644 --- a/subcommands/capture.go +++ b/subcommands/capture.go @@ -1,41 +1,18 @@ package subcommands import ( - "bytes" "context" - "encoding/binary" "flag" - "fmt" - "io" - "net" - "os" - "sync" - "time" "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - - "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/bedrock-tool/bedrocktool/utils/handlers" ) func init() { utils.RegisterCommand(&CaptureCMD{}) } -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}) - 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}) -} - type CaptureCMD struct { ServerAddress string } @@ -52,26 +29,10 @@ func (c *CaptureCMD) Execute(ctx context.Context, ui utils.UI) error { return err } - os.Mkdir("captures", 0o775) - fio, err := os.Create(fmt.Sprintf("captures/%s-%s.pcap2", hostname, time.Now().Format("2006-01-02_15-04-05"))) - if err != nil { - return err - } - defer fio.Close() - utils.WriteReplayHeader(fio) - proxy, err := utils.NewProxy() if err != nil { return err } - proxy.PacketFunc = func(header packet.Header, payload []byte, src, dst net.Addr) { - IsfromClient := src.String() == proxy.Client.LocalAddr().String() - - buf := bytes.NewBuffer(nil) - header.Write(buf) - buf.Write(payload) - dumpPacket(fio, IsfromClient, buf.Bytes()) - } - - return proxy.Run(ctx, address) + proxy.AddHandler(handlers.NewPacketCapturer()) + return proxy.Run(ctx, address, hostname) } diff --git a/subcommands/chat_log.go b/subcommands/chat_log.go index 4e69c09..c48ea93 100644 --- a/subcommands/chat_log.go +++ b/subcommands/chat_log.go @@ -3,15 +3,10 @@ package subcommands import ( "context" "flag" - "fmt" - "os" - "time" "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - - "github.com/sandertv/gophertunnel/minecraft/protocol/packet" - "github.com/sirupsen/logrus" + "github.com/bedrock-tool/bedrocktool/utils/handlers" ) type ChatLogCMD struct { @@ -32,34 +27,12 @@ func (c *ChatLogCMD) Execute(ctx context.Context, ui utils.UI) error { return err } - filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07")) - f, err := os.Create(filename) - if err != nil { - return err - } - defer f.Close() - proxy, err := utils.NewProxy() if err != nil { return err } - proxy.PacketCB = func(pk packet.Packet, toServer bool, t time.Time) (packet.Packet, error) { - if text, ok := pk.(*packet.Text); ok { - logLine := text.Message - if c.Verbose { - logLine += fmt.Sprintf(" (TextType: %d | XUID: %s | PlatformChatID: %s)", text.TextType, text.XUID, text.PlatformChatID) - } - f.WriteString(fmt.Sprintf("[%s] ", t.Format(time.RFC3339))) - logrus.Info(logLine) - if toServer { - f.WriteString("SENT: ") - } - f.WriteString(logLine + "\n") - } - return pk, nil - } - - return proxy.Run(ctx, address) + proxy.AddHandler(handlers.NewChatLogger()) + return proxy.Run(ctx, address, hostname) } func init() { diff --git a/subcommands/debug.go b/subcommands/debug.go index c935bdb..a2b4e0c 100644 --- a/subcommands/debug.go +++ b/subcommands/debug.go @@ -19,7 +19,7 @@ func (c *DebugProxyCMD) SetFlags(f *flag.FlagSet) { } func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error { - address, _, err := utils.ServerInput(ctx, c.ServerAddress) + address, hostname, err := utils.ServerInput(ctx, c.ServerAddress) if err != nil { return err } @@ -30,7 +30,7 @@ func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error { if err != nil { return err } - return proxy.Run(ctx, address) + return proxy.Run(ctx, address, hostname) } func init() { diff --git a/subcommands/resourcepack-d/resourcepack-d.go b/subcommands/resourcepack-d/resourcepack-d.go index 9bd0cc4..81ddb94 100644 Binary files a/subcommands/resourcepack-d/resourcepack-d.go and b/subcommands/resourcepack-d/resourcepack-d.go differ diff --git a/subcommands/skins/skins.go b/subcommands/skins/skins.go index 3a439dd..af56545 100644 --- a/subcommands/skins/skins.go +++ b/subcommands/skins/skins.go @@ -3,126 +3,16 @@ package skins import ( "context" "flag" - "fmt" - "os" - "path" - "strings" - "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/handlers" - "github.com/google/uuid" - "github.com/sandertv/gophertunnel/minecraft/protocol" - "github.com/sandertv/gophertunnel/minecraft/protocol/packet" - "github.com/sirupsen/logrus" + "github.com/sandertv/gophertunnel/minecraft" ) -type SkinMeta struct { - SkinID string - PlayFabID string - PremiumSkin bool - PersonaSkin bool - CapeID string - SkinColour string - ArmSize string - Trusted bool - PersonaPieces []protocol.PersonaPiece -} - type skinsSession struct { - PlayerNameFilter string - OnlyIfHasGeometry bool - ServerName string - Proxy *utils.ProxyContext - fpath string - - playerSkinPacks map[uuid.UUID]*SkinPack - playerNames map[uuid.UUID]string -} - -func NewSkinsSession(proxy *utils.ProxyContext, serverName, fpath string) *skinsSession { - return &skinsSession{ - ServerName: serverName, - Proxy: proxy, - fpath: fpath, - - playerSkinPacks: make(map[uuid.UUID]*SkinPack), - playerNames: make(map[uuid.UUID]string), - } -} - -func (s *skinsSession) AddPlayerSkin(playerID uuid.UUID, playerName string, skin *Skin) (added bool) { - p, ok := s.playerSkinPacks[playerID] - if !ok { - creating := fmt.Sprintf("Creating Skinpack for %s", playerName) - s.Proxy.SendPopup(creating) - logrus.Info(creating) - p = NewSkinPack(playerName, s.fpath) - s.playerSkinPacks[playerID] = p - } - if p.AddSkin(skin) { - if ok { - addedStr := fmt.Sprintf("Added a skin to %s", playerName) - s.Proxy.SendPopup(addedStr) - logrus.Info(addedStr) - } - added = true - } - if err := p.Save(path.Join(s.fpath, playerName), s.ServerName); err != nil { - logrus.Error(err) - } - return added -} - -func (s *skinsSession) AddSkin(playerName string, playerID uuid.UUID, playerSkin *protocol.Skin) (string, *Skin, bool) { - if playerName == "" { - playerName = s.playerNames[playerID] - if playerName == "" { - playerName = playerID.String() - } - } - if !strings.HasPrefix(playerName, s.PlayerNameFilter) { - return "", nil, false - } - s.playerNames[playerID] = playerName - - skin := &Skin{playerSkin} - if s.OnlyIfHasGeometry && !skin.HaveGeometry() { - return "", nil, false - } - wasAdded := s.AddPlayerSkin(playerID, playerName, skin) - - return playerName, skin, wasAdded -} - -type skinAdd struct { - PlayerName string - Skin *protocol.Skin -} - -func (s *skinsSession) ProcessPacket(pk packet.Packet) (out []skinAdd) { - switch pk := pk.(type) { - case *packet.PlayerList: - if pk.ActionType == 1 { // remove - return nil - } - for _, player := range pk.Entries { - playerName, skin, wasAdded := s.AddSkin(utils.CleanupName(player.Username), player.UUID, &player.Skin) - if wasAdded { - out = append(out, skinAdd{ - PlayerName: playerName, - Skin: skin.Skin, - }) - } - } - case *packet.AddPlayer: - if _, ok := s.playerNames[pk.UUID]; !ok { - s.playerNames[pk.UUID] = utils.CleanupName(pk.Username) - } - } - return out } type SkinCMD struct { @@ -148,41 +38,25 @@ func (c *SkinCMD) Execute(ctx context.Context, ui utils.UI) error { proxy, _ := utils.NewProxy() proxy.WithClient = !c.NoProxy - proxy.OnClientConnect = func(hasClient bool) { - ui.Message(messages.SetUIState(messages.UIStateConnecting)) - } - proxy.ConnectCB = func(err error) bool { - if err != nil { - return false - } - ui.Message(messages.SetUIState(messages.UIStateMain)) - logrus.Info(locale.Loc("ctrl_c_to_exit", nil)) - return true - } - - outPathBase := fmt.Sprintf("skins/%s", hostname) - os.MkdirAll(outPathBase, 0o755) - - s := NewSkinsSession(proxy, hostname, outPathBase) - - proxy.PacketCB = func(pk packet.Packet, toServer bool, _ time.Time) (packet.Packet, error) { - if !toServer { - for _, s := range s.ProcessPacket(pk) { - ui.Message(messages.NewSkin{ - PlayerName: s.PlayerName, - Skin: s.Skin, - }) - } - } - return pk, nil - } + proxy.AddHandler(handlers.NewSkinSaver(func(sa handlers.SkinAdd) { + ui.Message(messages.NewSkin{ + PlayerName: sa.PlayerName, + Skin: sa.Skin, + }) + })) + proxy.AddHandler(&utils.ProxyHandler{ + Name: "Skin CMD", + OnClientConnect: func(conn *minecraft.Conn) { + ui.Message(messages.SetUIState(messages.UIStateConnecting)) + }, + }) if proxy.WithClient { ui.Message(messages.SetUIState(messages.UIStateConnect)) } else { ui.Message(messages.SetUIState(messages.UIStateConnecting)) } - err = proxy.Run(ctx, address) + err = proxy.Run(ctx, address, hostname) return err } diff --git a/subcommands/world/world.go b/subcommands/world/world.go index 7a33e0f..74d9d57 100644 --- a/subcommands/world/world.go +++ b/subcommands/world/world.go @@ -172,37 +172,41 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error { proxy.GameDataModifier = func(gd *minecraft.GameData) { gd.ClientSideGeneration = false } - proxy.ConnectCB = w.OnConnect - proxy.OnClientConnect = func(hasClient bool) { - w.gui.Message(messages.SetUIState(messages.UIStateConnecting)) - } - proxy.PacketCB = func(pk packet.Packet, toServer bool, _ time.Time) (packet.Packet, error) { - forward := true - if toServer { - // from client - pk = w.processItemPacketsClient(pk, &forward) - pk = w.processMapPacketsClient(pk, &forward) - } else { - // from server - switch pk := pk.(type) { - case *packet.ChunkRadiusUpdated: - w.serverState.ChunkRadius = int(pk.ChunkRadius) - pk.ChunkRadius = 80 + proxy.AddHandler(&utils.ProxyHandler{ + Name: "Worlds", + ConnectCB: w.OnConnect, + OnClientConnect: func(conn *minecraft.Conn) { + w.gui.Message(messages.SetUIState(messages.UIStateConnecting)) + }, + PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) { + forward := true + + if toServer { + // from client + pk = w.processItemPacketsClient(pk, &forward) + pk = w.processMapPacketsClient(pk, &forward) + } else { + // from server + switch pk := pk.(type) { + case *packet.ChunkRadiusUpdated: + w.serverState.ChunkRadius = int(pk.ChunkRadius) + pk.ChunkRadius = 80 + } + pk = w.processItemPacketsServer(pk) + pk = w.ProcessChunkPackets(pk) + pk = w.ProcessEntityPackets(pk) } - pk = w.processItemPacketsServer(pk) - pk = w.ProcessChunkPackets(pk) - pk = w.ProcessEntityPackets(pk) - } - if !forward { - return nil, nil - } - return pk, nil - } + if !forward { + return nil, nil + } + return pk, nil + }, + }) w.gui.Message(messages.SetUIState(messages.UIStateConnect)) - err = w.proxy.Run(ctx, serverAddress) + err = w.proxy.Run(ctx, serverAddress, hostname) if err != nil { return err } diff --git a/utils/handlers/capture.go b/utils/handlers/capture.go new file mode 100644 index 0000000..9d86b36 --- /dev/null +++ b/utils/handlers/capture.go @@ -0,0 +1,69 @@ +package handlers + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "net" + "os" + "sync" + "time" + + "github.com/bedrock-tool/bedrocktool/utils" + "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}) + 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}) +} + +type PacketCapturer struct { + proxy *utils.ProxyContext + fio *os.File +} + +func (p *PacketCapturer) AddressAndName(address, hostname string) error { + os.Mkdir("captures", 0o775) + fio, err := os.Create(fmt.Sprintf("captures/%s-%s.pcap2", hostname, time.Now().Format("2006-01-02_15-04-05"))) + if err != nil { + return err + } + utils.WriteReplayHeader(fio) + p.fio = fio + return nil +} + +func (p *PacketCapturer) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) { + IsfromClient := utils.ClientAddr.String() == src.String() + + buf := bytes.NewBuffer(nil) + header.Write(buf) + buf.Write(payload) + dumpPacket(p.fio, IsfromClient, buf.Bytes()) +} + +func NewPacketCapturer() *utils.ProxyHandler { + p := &PacketCapturer{} + return &utils.ProxyHandler{ + Name: "Packet Capturer", + ProxyRef: func(pc *utils.ProxyContext) { + p.proxy = pc + }, + PacketFunc: p.PacketFunc, + AddressAndName: p.AddressAndName, + OnEnd: func() { + p.fio.Close() + }, + } +} diff --git a/utils/handlers/chat_log.go b/utils/handlers/chat_log.go new file mode 100644 index 0000000..3f485cc --- /dev/null +++ b/utils/handlers/chat_log.go @@ -0,0 +1,54 @@ +package handlers + +import ( + "fmt" + "os" + "time" + + "github.com/bedrock-tool/bedrocktool/utils" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" +) + +type ChatLogger struct { + Verbose bool + fio *os.File +} + +func (c *ChatLogger) AddressAndName(address, hostname string) error { + filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07")) + f, err := os.Create(filename) + if err != nil { + return err + } + c.fio = f + return nil +} + +func (c *ChatLogger) PacketCB(pk packet.Packet, toServer bool, t time.Time) (packet.Packet, error) { + if text, ok := pk.(*packet.Text); ok { + logLine := text.Message + if c.Verbose { + logLine += fmt.Sprintf(" (TextType: %d | XUID: %s | PlatformChatID: %s)", text.TextType, text.XUID, text.PlatformChatID) + } + c.fio.WriteString(fmt.Sprintf("[%s] ", t.Format(time.RFC3339))) + logrus.Info(logLine) + if toServer { + c.fio.WriteString("SENT: ") + } + c.fio.WriteString(logLine + "\n") + } + return pk, nil +} + +func NewChatLogger() *utils.ProxyHandler { + p := &ChatLogger{} + return &utils.ProxyHandler{ + Name: "Packet Capturer", + PacketCB: p.PacketCB, + AddressAndName: p.AddressAndName, + OnEnd: func() { + p.fio.Close() + }, + } +} diff --git a/utils/handlers/skins.go b/utils/handlers/skins.go new file mode 100644 index 0000000..6c6b591 --- /dev/null +++ b/utils/handlers/skins.go @@ -0,0 +1,127 @@ +package handlers + +import ( + "fmt" + "os" + "path" + "strings" + "time" + + "github.com/bedrock-tool/bedrocktool/utils" + "github.com/google/uuid" + "github.com/sandertv/gophertunnel/minecraft/protocol" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" +) + +type SkinSaver struct { + PlayerNameFilter string + OnlyIfHasGeometry bool + ServerName string + Proxy *utils.ProxyContext + fpath string + + playerSkinPacks map[uuid.UUID]*utils.SkinPack + playerNames map[uuid.UUID]string +} + +func (s *SkinSaver) AddPlayerSkin(playerID uuid.UUID, playerName string, skin *utils.Skin) (added bool) { + p, ok := s.playerSkinPacks[playerID] + if !ok { + creating := fmt.Sprintf("Creating Skinpack for %s", playerName) + s.Proxy.SendPopup(creating) + logrus.Info(creating) + p = utils.NewSkinPack(playerName, s.fpath) + s.playerSkinPacks[playerID] = p + } + if p.AddSkin(skin) { + if ok { + addedStr := fmt.Sprintf("Added a skin to %s", playerName) + s.Proxy.SendPopup(addedStr) + logrus.Info(addedStr) + } + added = true + } + if err := p.Save(path.Join(s.fpath, playerName), s.ServerName); err != nil { + logrus.Error(err) + } + return added +} + +func (s *SkinSaver) AddSkin(playerName string, playerID uuid.UUID, playerSkin *protocol.Skin) (string, *utils.Skin, bool) { + if playerName == "" { + playerName = s.playerNames[playerID] + if playerName == "" { + playerName = playerID.String() + } + } + if !strings.HasPrefix(playerName, s.PlayerNameFilter) { + return "", nil, false + } + s.playerNames[playerID] = playerName + + skin := &utils.Skin{playerSkin} + if s.OnlyIfHasGeometry && !skin.HaveGeometry() { + return "", nil, false + } + wasAdded := s.AddPlayerSkin(playerID, playerName, skin) + + return playerName, skin, wasAdded +} + +type SkinAdd struct { + PlayerName string + Skin *protocol.Skin +} + +func (s *SkinSaver) ProcessPacket(pk packet.Packet) (out []SkinAdd) { + switch pk := pk.(type) { + case *packet.PlayerList: + if pk.ActionType == 1 { // remove + return nil + } + for _, player := range pk.Entries { + playerName, skin, wasAdded := s.AddSkin(utils.CleanupName(player.Username), player.UUID, &player.Skin) + if wasAdded { + out = append(out, SkinAdd{ + PlayerName: playerName, + Skin: skin.Skin, + }) + } + } + case *packet.AddPlayer: + if _, ok := s.playerNames[pk.UUID]; !ok { + s.playerNames[pk.UUID] = utils.CleanupName(pk.Username) + } + } + return out +} + +func NewSkinSaver(skinCB func(SkinAdd)) *utils.ProxyHandler { + s := &SkinSaver{ + playerSkinPacks: make(map[uuid.UUID]*utils.SkinPack), + playerNames: make(map[uuid.UUID]string), + } + return &utils.ProxyHandler{ + Name: "Skin Saver", + ProxyRef: func(pc *utils.ProxyContext) { + s.Proxy = pc + }, + AddressAndName: func(address, hostname string) error { + outPathBase := fmt.Sprintf("skins/%s", hostname) + os.MkdirAll(outPathBase, 0o755) + s.fpath = outPathBase + return nil + }, + PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) { + if !toServer { + for _, s := range s.ProcessPacket(pk) { + if skinCB != nil { + skinCB(s) + } + } + } + return pk, nil + }, + } +} diff --git a/utils/packet_logger.go b/utils/packet_logger.go index a49a0da..c3461c5 100644 --- a/utils/packet_logger.go +++ b/utils/packet_logger.go @@ -183,10 +183,6 @@ var ClientAddr net.Addr var pool = packet.NewPool() func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { - if header.PacketID == packet.IDRequestNetworkSettings { - ClientAddr = src - } - var pk packet.Packet if pkFunc, ok := pool[header.PacketID]; ok { pk = pkFunc() diff --git a/utils/proxy.go b/utils/proxy.go index bda6824..27f12d5 100644 --- a/utils/proxy.go +++ b/utils/proxy.go @@ -8,11 +8,13 @@ import ( "fmt" "net" "os" + "reflect" "strings" "sync" "time" "github.com/bedrock-tool/bedrocktool/locale" + "github.com/repeale/fp-go" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/login" @@ -42,16 +44,36 @@ func (p dummyProto) ConvertFromLatest(pk packet.Packet, _ *minecraft.Conn) []pac */ type ( - PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr) - PacketCallback func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) - ClientConnectCallback func(hasClient bool) - ConnectCallback func(err error) bool - IngameCommand struct { + PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr) + IngameCommand struct { Exec func(cmdline []string) bool Cmd protocol.Command } ) +type ProxyHandler struct { + Name string + ProxyRef func(*ProxyContext) + // + AddressAndName func(address, name string) error + + // called for every packet + PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr) + + // called on every packet after login + 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) + + // called after game started + ConnectCB func(err error) bool + + // called when the proxy stops + OnEnd func() +} + type ProxyContext struct { Server *minecraft.Conn Client *minecraft.Conn @@ -62,14 +84,8 @@ type ProxyContext struct { IgnoreDisconnect bool CustomClientData *login.ClientData - // called for every packet - PacketFunc PacketFunc - // called after client connected - OnClientConnect ClientConnectCallback - // called after game started - ConnectCB ConnectCallback - // called on every packet after login - PacketCB PacketCallback + handlers []*ProxyHandler + // called to change game data GameDataModifier func(*minecraft.GameData) } @@ -186,6 +202,10 @@ func (p *ProxyContext) SendPopup(text string) { }) } +func (p *ProxyContext) AddHandler(handler *ProxyHandler) { + p.handlers = append(p.handlers, handler) +} + func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, toServer bool, _ time.Time) (packet.Packet, error) { switch _pk := pk.(type) { case *packet.CommandRequest: @@ -209,7 +229,7 @@ func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, toServer bool, _ return pk, nil } -func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool, packetCBs []PacketCallback) error { +func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool) error { var c1, c2 *minecraft.Conn if toServer { c1 = p.Client @@ -229,10 +249,17 @@ func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool, packetCBs [ return err } - for _, packetCB := range packetCBs { - pk, err = packetCB(pk, toServer, time.Now()) - if err != nil { - return err + pkName := reflect.TypeOf(pk).String() + for _, handler := range p.handlers { + if handler.PacketCB != nil { + pk, err = handler.PacketCB(pk, toServer, time.Now()) + if err != nil { + return err + } + if pk == nil { + logrus.Tracef("Dropped Packet: %s", pkName) + break + } } } @@ -247,7 +274,16 @@ func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool, packetCBs [ } } -func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error) { +func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err error) { + for _, handler := range p.handlers { + if handler.AddressAndName != nil { + handler.AddressAndName(serverAddress, name) + } + if handler.ProxyRef != nil { + handler.ProxyRef(p) + } + } + if strings.HasPrefix(serverAddress, "PCAP!") { return createReplayConnection(ctx, serverAddress[5:], p) } @@ -279,9 +315,7 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error if err != nil { return err } - - go func() { - <-ctx.Done() + defer func() { if p.Client != nil { p.Listener.Disconnect(p.Client, DisconnectReason) } @@ -300,22 +334,40 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error cdp = &cd } - if p.OnClientConnect != nil { - p.OnClientConnect(p.WithClient) + for _, handler := range p.handlers { + if handler.OnClientConnect == nil { + continue + } + handler.OnClientConnect(p.Client) } if p.CustomClientData != nil { cdp = p.CustomClientData } - p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, p.PacketFunc) + p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, func(header packet.Header, payload []byte, src, dst net.Addr) { + for _, handler := range p.handlers { + if handler.PacketFunc == nil { + continue + } + handler.PacketFunc(header, payload, src, dst) + } + }) if err != nil { - if p.ConnectCB != nil { - if p.ConnectCB(err) { + for _, handler := range p.handlers { + if handler.ConnectCB == nil { + continue + } + ignore := handler.ConnectCB(err) + if ignore { err = nil + break } } - err = fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": serverAddress, "Err": err})) + + if err != nil { + err = fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": serverAddress, "Err": err})) + } return err } defer p.Server.Close() @@ -331,25 +383,27 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error return err } - if p.ConnectCB != nil { - if !p.ConnectCB(nil) { + for _, handler := range p.handlers { + if handler.ConnectCB == nil { + continue + } + if !handler.ConnectCB(nil) { return errors.New("Cancelled") } } + // append self to handlers for commands + p.handlers = append(p.handlers, &ProxyHandler{ + Name: "Commands", + PacketCB: p.CommandHandlerPacketCB, + }) + wg := sync.WaitGroup{} - - var cbs []PacketCallback - cbs = append(cbs, p.CommandHandlerPacketCB) - if p.PacketCB != nil { - cbs = append(cbs, p.PacketCB) - } - // server to client wg.Add(1) go func() { defer wg.Done() - if err := p.proxyLoop(ctx, false, cbs); err != nil { + if err := p.proxyLoop(ctx, false); err != nil { logrus.Error(err) return } @@ -360,13 +414,37 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error wg.Add(1) go func() { defer wg.Done() - if err := p.proxyLoop(ctx, true, cbs); err != nil { + if err := p.proxyLoop(ctx, true); err != nil { logrus.Error(err) return } }() } + wantSecondary := fp.Filter(func(handler *ProxyHandler) bool { + return handler.SecondaryClientCB != nil + })(p.handlers) + + if len(wantSecondary) > 0 { + go func() { + c, err := p.Listener.Accept() + if err != nil { + logrus.Error(err) + return + } + + for _, handler := range wantSecondary { + go handler.SecondaryClientCB(c.(*minecraft.Conn)) + } + }() + } + wg.Wait() + + for _, handler := range p.handlers { + if handler.OnEnd != nil { + handler.OnEnd() + } + } return err } diff --git a/utils/replay.go b/utils/replay.go index 1d270b7..d73f57a 100644 --- a/utils/replay.go +++ b/utils/replay.go @@ -63,6 +63,13 @@ func createReplayConnection(ctx context.Context, filename string, proxy *ProxyCo f.Seek(-4, io.SeekCurrent) } + server, client := &net.UDPAddr{ + IP: net.IPv4(1, 1, 1, 1), + }, &net.UDPAddr{ + IP: net.IPv4(2, 2, 2, 2), + } + ClientAddr = client + proxy.Server = minecraft.NewConn() gameStarted := false @@ -123,13 +130,32 @@ func createReplayConnection(ctx context.Context, filename string, proxy *ProxyCo 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 + } else { + src = server + dst = client + } + if Options.Debug { - PacketLogger(packet.Header{PacketID: pk.ID()}, f.Bytes(), &net.UDPAddr{}, &net.UDPAddr{}) + PacketLogger(hdr, f.Bytes(), src, dst) + } + + for _, handler := range proxy.handlers { + if handler.PacketFunc != nil { + handler.PacketFunc(hdr, f.Bytes(), src, dst) + } } if gameStarted { - if proxy.PacketCB != nil { - proxy.PacketCB(pk, toServer, timeReceived) + for _, handler := range proxy.handlers { + if handler.PacketCB != nil { + handler.PacketCB(pk, toServer, timeReceived) + } } } else { switch pk := pk.(type) { @@ -164,11 +190,19 @@ func createReplayConnection(ctx context.Context, filename string, proxy *ProxyCo DisablePlayerInteractions: pk.DisablePlayerInteractions, }) gameStarted = true - if proxy.ConnectCB != nil { - proxy.ConnectCB(nil) + for _, handler := range proxy.handlers { + if handler.ConnectCB != nil { + handler.ConnectCB(nil) + } } } } } + + for _, handler := range proxy.handlers { + if handler.OnEnd != nil { + handler.OnEnd() + } + } } } diff --git a/subcommands/skins/skin.go b/utils/skin.go similarity index 99% rename from subcommands/skins/skin.go rename to utils/skin.go index 31688c4..327a580 100644 --- a/subcommands/skins/skin.go +++ b/utils/skin.go @@ -1,4 +1,4 @@ -package skins +package utils import ( "encoding/json" diff --git a/subcommands/skins/skinpack.go b/utils/skinpack.go similarity index 89% rename from subcommands/skins/skinpack.go rename to utils/skinpack.go index fb13cc2..ee60e4c 100644 --- a/subcommands/skins/skinpack.go +++ b/utils/skinpack.go @@ -1,4 +1,4 @@ -package skins +package utils import ( "encoding/json" @@ -6,12 +6,24 @@ import ( "os" "path" - "github.com/bedrock-tool/bedrocktool/utils" "github.com/google/uuid" + "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/resource" "github.com/sirupsen/logrus" ) +type SkinMeta struct { + SkinID string + PlayFabID string + PremiumSkin bool + PersonaSkin bool + CapeID string + SkinColour string + ArmSize string + Trusted bool + PersonaPieces []protocol.PersonaPiece +} + type _skinWithIndex struct { i int skin *Skin @@ -144,7 +156,7 @@ func (s *SkinPack) Save(fpath, serverName string) error { }, } - if err := utils.WriteManifest(&manifest, fpath); err != nil { + if err := WriteManifest(&manifest, fpath); err != nil { return err } } diff --git a/utils/utils.go b/utils/utils.go index fe6ec65..c6706c0 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -69,6 +69,10 @@ func connectServer(ctx context.Context, address string, ClientData *login.Client TokenSource: GetTokenSource(), ClientData: cd, PacketFunc: func(header packet.Header, payload []byte, src, dst net.Addr) { + if header.PacketID == packet.IDRequestNetworkSettings { + ClientAddr = src + } + if Options.Debug { PacketLogger(header, payload, src, dst) } @@ -85,7 +89,6 @@ func connectServer(ctx context.Context, address string, ClientData *login.Client } logrus.Debug(locale.Loc("connected", nil)) - ClientAddr = serverConn.LocalAddr() return serverConn, nil }