diff --git a/locale/de.yaml b/locale/de.yaml index 3cd9edc..1922116 100644 --- a/locale/de.yaml +++ b/locale/de.yaml @@ -32,6 +32,8 @@ debug_mode: other: "Debug-Modus" refreshed_token: other: Token wurde aktualisiert +done: + other: Fertig starting_dns: other: "Starten des DNS auf {{.Ip}}:53" diff --git a/locale/en.yaml b/locale/en.yaml index 0a7dec3..59dab53 100644 --- a/locale/en.yaml +++ b/locale/en.yaml @@ -31,7 +31,9 @@ preload_packs: debug_mode: other: "debug mode" refreshed_token: - other: refreshed token + other: "refreshed token" +done: + other: "done" starting_dns: other: "Starting dns at {{.Ip}}:53" diff --git a/locale/owo.yaml b/locale/owo.yaml index 04881e1..6420bbd 100644 --- a/locale/owo.yaml +++ b/locale/owo.yaml @@ -30,6 +30,10 @@ preload_packs: other: "pwewoad wewesouwcepacks for pwoxy" debug_mode: other: "debug mode" +refreshed_token: + other: "refweshed token" +done: + other: done starting_dns: other: "Stawting dns at {{.Ip}}:53" diff --git a/subcommands/skins/skins.go b/subcommands/skins/skins.go index aaf5ef0..5bf20c8 100644 --- a/subcommands/skins/skins.go +++ b/subcommands/skins/skins.go @@ -276,36 +276,29 @@ func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{} return 1 } - serverConn, err := utils.ConnectServer(ctx, address, nil, false, nil) - if err != nil { - logrus.Error(err) - return 1 - } - defer serverConn.Close() - out_path := fmt.Sprintf("skins/%s", hostname) - if err := serverConn.DoSpawnContext(ctx); err != nil { + p := utils.NewProxy() + p.WithClient = false + p.ConnectCB = func(proxy *utils.ProxyContext) { + logrus.Info(locale.Loc("connected", nil)) + logrus.Info(locale.Loc("ctrl_c_to_exit", nil)) + + os.MkdirAll(out_path, 0o755) + } + + p.PacketCB = func(pk packet.Packet, proxy *utils.ProxyContext, toServer bool) (packet.Packet, error) { + if !toServer { + process_packet_skins(nil, out_path, pk, c.filter, false) + } + return pk, nil + } + + err = p.Run(ctx, address) + if err != nil { logrus.Error(err) - return 1 } - logrus.Info(locale.Loc("connected", nil)) - logrus.Info(locale.Loc("ctrl_c_to_exit", nil)) - - os.MkdirAll(out_path, 0o755) - - for { - pk, err := serverConn.ReadPacket() - if err != nil { - return 1 - } - process_packet_skins(nil, out_path, pk, c.filter, false) - if ctx.Err() != nil { - serverConn.Close() - break - } - } return 0 } diff --git a/subcommands/world/world.go b/subcommands/world/world.go index f43529f..7df846e 100644 --- a/subcommands/world/world.go +++ b/subcommands/world/world.go @@ -158,6 +158,7 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{ w.saveImage = c.saveImage w.experimentInventory = c.experimentInventory w.ctx = ctx + w.bp = behaviourpack.New(w.ServerName) proxy := utils.NewProxy() proxy.AlwaysGetPacks = true @@ -442,7 +443,7 @@ func (w *WorldState) SaveAndReset() { ld.Generator = 2 } - if w.bp != nil { + if w.bp.HasContent() { if ld.Experiments == nil { ld.Experiments = map[string]any{} } @@ -489,7 +490,7 @@ func (w *WorldState) SaveAndReset() { add_packs_json("world_resource_packs.json", rdeps) } - if w.bp != nil { + if w.bp.HasContent() { name := strings.ReplaceAll(w.ServerName, "/", "-") + "_blocks" pack_folder := path.Join(folder, "behavior_packs", name) os.MkdirAll(pack_folder, 0o755) @@ -519,7 +520,7 @@ func (w *WorldState) SaveAndReset() { fmt.Println(err) } logrus.Info(locale.Loc("saved", locale.Strmap{"Name": filename})) - os.RemoveAll(folder) + // os.RemoveAll(folder) w.Reset() } @@ -529,6 +530,12 @@ func (w *WorldState) OnConnect(proxy *utils.ProxyContext) { world.InsertCustomItems(gd.Items) + for _, ie := range gd.Items { + if ie.ComponentBased { + w.bp.AddItem(ie) + } + } + map_item_id, _ := world.ItemRidByName("minecraft:filled_map") MAP_ITEM_PACKET.Content[0].Stack.ItemType.NetworkID = map_item_id if gd.ServerAuthoritativeInventory { @@ -537,12 +544,9 @@ func (w *WorldState) OnConnect(proxy *utils.ProxyContext) { if len(gd.CustomBlocks) > 0 { logrus.Info(locale.Loc("using_customblocks", nil)) - - w.bp = behaviourpack.New(w.ServerName + " Custom Blocks") for _, be := range gd.CustomBlocks { w.bp.AddBlock(be) } - // telling the chunk code what custom blocks there are so it can generate offsets world.InsertCustomBlocks(gd.CustomBlocks) } @@ -785,6 +789,8 @@ func (w *WorldState) ProcessPacketServer(pk packet.Packet) (packet.Packet, bool) delete(w.openItemContainers, byte(pk.WindowID)) } } + case *packet.ItemComponent: + w.bp.ApplyComponentEntries(pk.Items) } return pk, true } diff --git a/utils/behaviourpack/bp.go b/utils/behaviourpack/bp.go index 00f1eea..7ddc7cf 100644 --- a/utils/behaviourpack/bp.go +++ b/utils/behaviourpack/bp.go @@ -18,8 +18,10 @@ import ( ) type BehaviourPack struct { - Manifest *resource.Manifest - blocks []blockBehaviour + formatVersion string + Manifest *resource.Manifest + blocks []blockBehaviour + items map[string]itemBehaviour } type blockBehaviour struct { @@ -27,6 +29,22 @@ type blockBehaviour struct { MinecraftBlock world.MinecraftBlock `json:"minecraft:block"` } +type itemDescription struct { + Category string `json:"category"` + Identifier string `json:"identifier"` + IsExperimental bool `json:"is_experimental"` +} + +type minecraftItem struct { + Description itemDescription `json:"description"` + Components map[string]any `json:"components,omitempty"` +} + +type itemBehaviour struct { + FormatVersion string `json:"format_version"` + MinecraftItem minecraftItem `json:"minecraft:item"` +} + func check(err error) { if err != nil { logrus.Fatal(err) @@ -41,6 +59,7 @@ func rand_seeded_uuid(str string) string { func New(name string) *BehaviourPack { return &BehaviourPack{ + formatVersion: "1.16.0", Manifest: &resource.Manifest{ FormatVersion: 2, Header: resource.Header{ @@ -60,6 +79,8 @@ func New(name string) *BehaviourPack { Dependencies: []resource.Dependency{}, Capabilities: []resource.Capability{}, }, + blocks: []blockBehaviour{}, + items: make(map[string]itemBehaviour), } } @@ -72,17 +93,42 @@ func (bp *BehaviourPack) AddDependency(id string, ver [3]int) { func (bp *BehaviourPack) AddBlock(block protocol.BlockEntry) { entry := blockBehaviour{ - FormatVersion: "1.16.0", + FormatVersion: bp.formatVersion, MinecraftBlock: world.ParseBlock(block), } bp.blocks = append(bp.blocks, entry) } +func (bp *BehaviourPack) AddItem(item protocol.ItemEntry) { + entry := itemBehaviour{ + FormatVersion: bp.formatVersion, + MinecraftItem: minecraftItem{ + Description: itemDescription{ + Identifier: item.Name, + IsExperimental: true, + }, + Components: make(map[string]any), + }, + } + bp.items[item.Name] = entry +} + +func (bp *BehaviourPack) ApplyComponentEntries(entries []protocol.ItemComponentEntry) { + for _, ice := range entries { + item, ok := bp.items[ice.Name] + if !ok { + continue + } + item.MinecraftItem.Components = ice.Data + } +} + func (bp *BehaviourPack) CheckAddLink(pack utils.Pack) { z, err := zip.NewReader(pack, int64(pack.Len())) if err != nil { logrus.Error(err) + return } _, err = z.Open("blocks.json") if err != nil { @@ -92,25 +138,54 @@ func (bp *BehaviourPack) CheckAddLink(pack utils.Pack) { bp.AddDependency(h.UUID, h.Version) } +func (bp *BehaviourPack) HasContent() bool { + return len(bp.blocks) > 0 || len(bp.items) > 0 +} + func (bp *BehaviourPack) Save(fpath string) error { { // write manifest w, err := os.Create(path.Join(fpath, "manifest.json")) if err != nil { return err } - check(json.NewEncoder(w).Encode(bp.Manifest)) + e := json.NewEncoder(w) + e.SetIndent("", "\t") + check(e.Encode(bp.Manifest)) } - { // blocks - block_dir := path.Join(fpath, "blocks") - os.Mkdir(block_dir, 0o755) + if len(bp.blocks) > 0 { // blocks + blocks_dir := path.Join(fpath, "blocks") + os.Mkdir(blocks_dir, 0o755) for _, be := range bp.blocks { - ns := strings.Split(be.MinecraftBlock.Description.Identifier, ":") - name := ns[len(ns)-1] + ns_name := strings.Split(be.MinecraftBlock.Description.Identifier, ":") + ns := ns_name[0] + name := ns_name[len(ns_name)-1] + block_dir := path.Join(blocks_dir, ns) + os.Mkdir(block_dir, 0o755) w, err := os.Create(path.Join(block_dir, name+".json")) if err != nil { return err } - check(json.NewEncoder(w).Encode(be)) + e := json.NewEncoder(w) + e.SetIndent("", "\t") + check(e.Encode(be)) + } + } + if len(bp.items) > 0 { // items + items_dir := path.Join(fpath, "items") + os.Mkdir(items_dir, 0o755) + for _, ib := range bp.items { + ns_name := strings.Split(ib.MinecraftItem.Description.Identifier, ":") + ns := ns_name[0] + name := ns_name[len(ns_name)-1] + item_dir := path.Join(items_dir, ns) + os.Mkdir(item_dir, 0o755) + w, err := os.Create(path.Join(item_dir, name+".json")) + if err != nil { + return err + } + e := json.NewEncoder(w) + e.SetIndent("", "\t") + check(e.Encode(ib)) } } return nil diff --git a/utils/proxy.go b/utils/proxy.go index 29501b4..3a7a855 100644 --- a/utils/proxy.go +++ b/utils/proxy.go @@ -11,6 +11,7 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "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" @@ -40,6 +41,7 @@ type ProxyContext struct { Listener *minecraft.Listener commands map[string]IngameCommand AlwaysGetPacks bool + WithClient bool // called for every packet PacketFunc PacketFunc @@ -105,14 +107,14 @@ func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, proxy *ProxyCont return pk, nil } -func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCBs []PacketCallback) error { +func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool, packetCBs []PacketCallback) error { var c1, c2 *minecraft.Conn if toServer { - c1 = proxy.Client - c2 = proxy.Server + c1 = p.Client + c2 = p.Server } else { - c1 = proxy.Server - c2 = proxy.Client + c1 = p.Server + c2 = p.Client } for { @@ -126,13 +128,13 @@ func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCB } for _, packetCB := range packetCBs { - pk, err = packetCB(pk, proxy, toServer) + pk, err = packetCB(pk, p, toServer) if err != nil { return err } } - if pk != nil { + if pk != nil && c2 != nil { if err := c2.WritePacket(pk); err != nil { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { G_disconnect_reason = disconnect.Error() @@ -145,7 +147,8 @@ func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCB func NewProxy() *ProxyContext { return &ProxyContext{ - commands: make(map[string]IngameCommand), + commands: make(map[string]IngameCommand), + WithClient: true, } } @@ -160,50 +163,54 @@ func (p *ProxyContext) Run(ctx context.Context, server_address string) (err erro } GetTokenSource() // ask for login before listening - var packs []*resource.Pack - if G_preload_packs { - logrus.Info(locale.Loc("preloading_packs", nil)) - var serverConn *minecraft.Conn - serverConn, err = ConnectServer(ctx, server_address, nil, true, nil) + + var cdp *login.ClientData = nil + if p.WithClient { + var packs []*resource.Pack + if G_preload_packs { + logrus.Info(locale.Loc("preloading_packs", nil)) + var serverConn *minecraft.Conn + serverConn, err = connectServer(ctx, server_address, nil, true, nil) + if err != nil { + err = fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": server_address, "Err": err})) + return + } + 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: 544, ver: "1.19.20"}, + }, + }.Listen("raknet", ":19132") if err != nil { - err = fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": server_address, "Err": err})) return } - serverConn.Close() - packs = serverConn.ResourcePacks() - logrus.Infof(locale.Locm("pack_count_loaded", locale.Strmap{"Count": len(packs)}, len(packs))) + defer p.Listener.Close() + + 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() + }() + + var c net.Conn + c, err = p.Listener.Accept() + if err != nil { + logrus.Fatal(err) + } + p.Client = c.(*minecraft.Conn) + cd := p.Client.ClientData() + cdp = &cd } - - _status := minecraft.NewStatusProvider("Server") - p.Listener, err = minecraft.ListenConfig{ - StatusProvider: _status, - ResourcePacks: packs, - AcceptedProtocols: []minecraft.Protocol{ - dummyProto{id: 544, ver: "1.19.20"}, - }, - }.Listen("raknet", ":19132") - if err != nil { - return - } - defer p.Listener.Close() - - 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() - }() - - var c net.Conn - c, err = p.Listener.Accept() - if err != nil { - logrus.Fatal(err) - } - p.Client = c.(*minecraft.Conn) - - cd := p.Client.ClientData() - p.Server, err = ConnectServer(ctx, server_address, &cd, p.AlwaysGetPacks, p.PacketFunc) + p.Server, err = connectServer(ctx, server_address, cdp, p.AlwaysGetPacks, p.PacketFunc) if err != nil { err = fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": server_address, "Err": err})) return @@ -215,7 +222,9 @@ func (p *ProxyContext) Run(ctx context.Context, server_address string) (err erro } defer p.Server.Close() - defer p.Listener.Disconnect(p.Client, G_disconnect_reason) + if p.Listener != nil { + defer p.Listener.Disconnect(p.Client, G_disconnect_reason) + } if p.ConnectCB != nil { p.ConnectCB(p) @@ -233,21 +242,23 @@ func (p *ProxyContext) Run(ctx context.Context, server_address string) (err erro wg.Add(1) go func() { defer wg.Done() - if err := proxyLoop(ctx, p, false, cbs); err != nil { + if err := p.proxyLoop(ctx, false, cbs); err != nil { logrus.Error(err) return } }() // client to server - wg.Add(1) - go func() { - defer wg.Done() - if err := proxyLoop(ctx, p, true, cbs); err != nil { - logrus.Error(err) - return - } - }() + if p.Client != nil { + wg.Add(1) + go func() { + defer wg.Done() + if err := p.proxyLoop(ctx, true, cbs); err != nil { + logrus.Error(err) + return + } + }() + } wg.Wait() return err diff --git a/utils/replay.go b/utils/replay.go index dece900..8619064 100644 --- a/utils/replay.go +++ b/utils/replay.go @@ -5,7 +5,6 @@ import ( "context" "encoding/binary" "io" - "net" "os" "github.com/sandertv/gophertunnel/minecraft" @@ -85,7 +84,7 @@ func create_replay_connection(ctx context.Context, filename string, onConnect Co b := protocol.NewWriter(f, 0) pk.Marshal(b) - PacketLogger(packet.Header{PacketID: pk.ID()}, f.Bytes(), &net.UDPAddr{}, &net.UDPAddr{}) + // PacketLogger(packet.Header{PacketID: pk.ID()}, f.Bytes(), &net.UDPAddr{}, &net.UDPAddr{}) if game_started { if packetCB != nil { diff --git a/utils/resourcepack-ace.go b/utils/resourcepack-ace.go index 7030c8e..790e6ef 100644 Binary files a/utils/resourcepack-ace.go and b/utils/resourcepack-ace.go differ diff --git a/utils/utils.go b/utils/utils.go index 3a6e30d..c70b234 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -8,6 +8,7 @@ import ( "path" "regexp" "strings" + "sync" "github.com/bedrock-tool/bedrocktool/locale" "github.com/google/uuid" @@ -45,7 +46,7 @@ func CleanupName(name string) string { // connections -func ConnectServer(ctx context.Context, address string, ClientData *login.ClientData, want_packs bool, packetFunc PacketFunc) (serverConn *minecraft.Conn, err error) { +func connectServer(ctx context.Context, address string, ClientData *login.ClientData, want_packs bool, packetFunc PacketFunc) (serverConn *minecraft.Conn, err error) { packet_func := func(header packet.Header, payload []byte, src, dst net.Addr) { if G_debug { PacketLogger(header, payload, src, dst) @@ -79,15 +80,22 @@ func ConnectServer(ctx context.Context, address string, ClientData *login.Client } func spawn_conn(ctx context.Context, clientConn *minecraft.Conn, serverConn *minecraft.Conn) error { + wg := sync.WaitGroup{} errs := make(chan error, 2) + if clientConn != nil { + wg.Add(1) + go func() { + defer wg.Done() + errs <- clientConn.StartGame(serverConn.GameData()) + }() + } + wg.Add(1) go func() { - errs <- clientConn.StartGame(serverConn.GameData()) - }() - go func() { + defer wg.Done() errs <- serverConn.DoSpawn() }() - // wait for both to finish + wg.Wait() for i := 0; i < 2; i++ { select { case err := <-errs: @@ -96,6 +104,7 @@ func spawn_conn(ctx context.Context, clientConn *minecraft.Conn, serverConn *min } case <-ctx.Done(): return errors.New(locale.Loc("connection_cancelled", nil)) + default: } } return nil