diff --git a/capture.go b/capture.go index 56ad8a6..28fdeea 100644 --- a/capture.go +++ b/capture.go @@ -14,7 +14,6 @@ import ( "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcapgo" - "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" ) @@ -83,46 +82,12 @@ func packets_main(ctx context.Context, args []string) error { return nil } - var hostname string - hostname, server = server_input(server) + address, hostname, err := server_input(server) - _status := minecraft.NewStatusProvider("Server") - listener, err := minecraft.ListenConfig{ - StatusProvider: _status, - }.Listen("raknet", ":19132") + listener, serverConn, clientConn, err := create_proxy(ctx, address) if err != nil { return err } - defer listener.Close() - - fmt.Printf("Listening on %s\n", listener.Addr()) - - c, err := listener.Accept() - if err != nil { - log.Fatal(err) - } - conn := c.(*minecraft.Conn) - - var packet_func func(header packet.Header, payload []byte, src, dst net.Addr) = nil - if G_debug { - packet_func = PacketLogger - } - - fmt.Printf("Connecting to %s\n", server) - serverConn, err := minecraft.Dialer{ - TokenSource: G_src, - ClientData: conn.ClientData(), - PacketFunc: packet_func, - }.DialContext(ctx, "raknet", server) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to connect to %s: %s\n", server, err) - return nil - } - - if err := spawn_conn(ctx, conn, serverConn); err != nil { - fmt.Fprintf(os.Stderr, "Failed to spawn: %s\n", err) - return nil - } f, err := os.Create(hostname + "-" + time.Now().Format("2006-01-02_15-04-05") + ".pcap") if err != nil { @@ -133,25 +98,13 @@ func packets_main(ctx context.Context, args []string) error { w.WriteFileHeader(65536, layers.LinkTypeEthernet) _wl := sync.Mutex{} - /* TEST - { - for i := 0; i < 1000; i++ { - dump_packet(false, w, &packet.SetTitle{ - Text: fmt.Sprintf("Test %d", i), - }) - dump_packet(true, w, &packet.MovePlayer{ - Tick: uint64(i), - }) - } - } - return nil - */ + go func() { - defer listener.Disconnect(conn, "connection lost") + defer listener.Disconnect(clientConn, "connection lost") defer serverConn.Close() for { - pk, err := conn.ReadPacket() + pk, err := clientConn.ReadPacket() if err != nil { fmt.Fprintf(os.Stderr, "Failed to read packet: %s\n", err) return @@ -180,7 +133,7 @@ func packets_main(ctx context.Context, args []string) error { dump_packet(false, w, pk) _wl.Unlock() - err = conn.WritePacket(pk) + err = clientConn.WritePacket(pk) if err != nil { fmt.Fprintf(os.Stderr, "Failed to write packet: %s\n", err) return nil diff --git a/resourcepack-ace.go b/resourcepack-ace.go index 2f74f8e..66f3378 100644 Binary files a/resourcepack-ace.go and b/resourcepack-ace.go differ diff --git a/skins-proxy.go b/skins-proxy.go index ba24da7..d0ac9ac 100644 --- a/skins-proxy.go +++ b/skins-proxy.go @@ -28,33 +28,19 @@ func skin_proxy_main(ctx context.Context, args []string) error { return nil } - _status := minecraft.NewStatusProvider("Server") - listener, err := minecraft.ListenConfig{ - StatusProvider: _status, - }.Listen("raknet", ":19132") + address, hostname, err := server_input(server) + if err != nil { + return err + } + + listener, clientConn, serverConn, err := create_proxy(ctx, address) if err != nil { return err } defer listener.Close() - fmt.Printf("Listening on %s\n", listener.Addr()) - - c, err := listener.Accept() - if err != nil { - return err - } - conn := c.(*minecraft.Conn) - - hostname, serverConn, err := connect_server(ctx, server) - if err != nil { - return err - } out_path := fmt.Sprintf("skins/%s", hostname) - if err := spawn_conn(ctx, conn, serverConn); err != nil { - return err - } - println("Connected") println("Press ctrl+c to exit") @@ -63,18 +49,18 @@ func skin_proxy_main(ctx context.Context, args []string) error { errs := make(chan error, 2) go func() { // server -> client defer serverConn.Close() - defer listener.Disconnect(conn, "connection lost") + defer listener.Disconnect(clientConn, "connection lost") for { pk, err := serverConn.ReadPacket() if err != nil { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { - _ = listener.Disconnect(conn, disconnect.Error()) + _ = listener.Disconnect(clientConn, disconnect.Error()) } return } - process_packet_skins(conn, out_path, pk) + process_packet_skins(clientConn, out_path, pk) - if err = conn.WritePacket(pk); err != nil { + if err = clientConn.WritePacket(pk); err != nil { return } } @@ -82,14 +68,14 @@ func skin_proxy_main(ctx context.Context, args []string) error { go func() { // client -> server for { - pk, err := conn.ReadPacket() + pk, err := clientConn.ReadPacket() if err != nil { return } if err := serverConn.WritePacket(pk); err != nil { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { - _ = listener.Disconnect(conn, disconnect.Error()) + _ = listener.Disconnect(clientConn, disconnect.Error()) } return } diff --git a/skins.go b/skins.go index 4c47a34..2fb689c 100644 --- a/skins.go +++ b/skins.go @@ -184,7 +184,12 @@ func skin_main(ctx context.Context, args []string) error { return nil } - hostname, serverConn, err := connect_server(ctx, server) + address, hostname, err := server_input(server) + if err != nil { + return err + } + + serverConn, err := connect_server(ctx, address, nil) if err != nil { return err } diff --git a/utils.go b/utils.go index fcc3711..b74118a 100644 --- a/utils.go +++ b/utils.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "log" "net" "os" "regexp" @@ -12,6 +13,7 @@ import ( "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/auth" + "github.com/sandertv/gophertunnel/minecraft/protocol/login" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "golang.org/x/oauth2" ) @@ -49,55 +51,69 @@ func get_token() oauth2.Token { return token } -func server_input(server string) (string, string) { - if server == "" { +func server_input(server string) (address, name string, err error) { + if server == "" { // no arg provided, interactive input fmt.Printf("Enter Server: ") reader := bufio.NewReader(os.Stdin) server, _ = reader.ReadString('\n') r, _ := regexp.Compile(`[\n\r]`) server = string(r.ReplaceAll([]byte(server), []byte(""))) } - if len(strings.Split(server, ":")) == 1 { - server += ":19132" + + if strings.HasPrefix(server, "realm:") { // for realms use api to get ip address + name, address, err = get_realm(strings.Split(server, ":")[1]) + if err != nil { + return "", "", err + } + } else { + // if an actual server address if given + // add port if necessary + address = server + if len(strings.Split(address, ":")) == 1 { + address += ":19132" + } + name = server_url_to_name(address) } + + return address, name, nil +} + +func server_url_to_name(server string) string { host, _, err := net.SplitHostPort(server) if err != nil { fmt.Fprintf(os.Stderr, "Invalid server: %s\n", err) os.Exit(1) } - return host, server + return host } -func connect_server(ctx context.Context, server string) (hostname string, conn *minecraft.Conn, err error) { - if strings.HasPrefix(server, "realm:") { - hostname, server, err = get_realm(strings.Split(server, ":")[1]) - if err != nil { - return "", nil, err - } - } else { - hostname, server = server_input(server) - } - +func connect_server(ctx context.Context, address string, ClientData *login.ClientData) (serverConn *minecraft.Conn, err error) { var packet_func func(header packet.Header, payload []byte, src, dst net.Addr) = nil if G_debug { packet_func = PacketLogger } - fmt.Printf("Connecting to %s\n", server) - conn, err = minecraft.Dialer{ - TokenSource: G_src, - PacketFunc: packet_func, - }.DialContext(ctx, "raknet", server) - if err != nil { - return "", nil, err + cd := login.ClientData{} + if ClientData != nil { + cd = *ClientData } - return hostname, conn, nil + + fmt.Printf("Connecting to %s\n", address) + serverConn, err = minecraft.Dialer{ + TokenSource: G_src, + ClientData: cd, + PacketFunc: packet_func, + }.DialContext(ctx, "raknet", address) + if err != nil { + return nil, err + } + return serverConn, nil } -func spawn_conn(ctx context.Context, conn *minecraft.Conn, serverConn *minecraft.Conn) error { +func spawn_conn(ctx context.Context, clientConn *minecraft.Conn, serverConn *minecraft.Conn) error { errs := make(chan error, 2) go func() { - errs <- conn.StartGame(serverConn.GameData()) + errs <- clientConn.StartGame(serverConn.GameData()) }() go func() { errs <- serverConn.DoSpawn() @@ -116,3 +132,41 @@ func spawn_conn(ctx context.Context, conn *minecraft.Conn, serverConn *minecraft } return nil } + +func create_proxy(ctx context.Context, server_address string) (l *minecraft.Listener, clientConn, serverConn *minecraft.Conn, err error) { + _status := minecraft.NewStatusProvider("Server") + listener, err := minecraft.ListenConfig{ + StatusProvider: _status, + }.Listen("raknet", ":19132") + if err != nil { + return nil, nil, nil, err + } + l = listener + + fmt.Printf("Listening on %s\n", listener.Addr()) + + c, err := listener.Accept() + if err != nil { + log.Fatal(err) + } + clientConn = c.(*minecraft.Conn) + + cd := clientConn.ClientData() + serverConn, err = connect_server(ctx, server_address, &cd) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to connect to %s: %s", server_address, err) + } + + if err := spawn_conn(ctx, clientConn, serverConn); err != nil { + return nil, nil, nil, fmt.Errorf("failed to spawn: %s", err) + } + + G_exit = append(G_exit, func() { + serverConn.Close() + listener.Disconnect(clientConn, "Closing") + clientConn.Close() + listener.Close() + }) + + return l, clientConn, serverConn, nil +} diff --git a/world.go b/world.go index 48d3759..4a5c986 100644 --- a/world.go +++ b/world.go @@ -8,8 +8,6 @@ import ( "image" "image/draw" "log" - "net" - "os" "path" "time" @@ -36,9 +34,11 @@ type TPlayerPos struct { // the state used for drawing and saving type WorldState struct { - chunks map[protocol.ChunkPos]*chunk.Chunk - Dim world.Dimension - WorldName string + chunks map[protocol.ChunkPos]*chunk.Chunk + Dim world.Dimension + WorldName string + ServerName string + worldCounter int PlayerPos TPlayerPos ClientConn *minecraft.Conn @@ -66,13 +66,12 @@ func init() { } func world_main(ctx context.Context, args []string) error { - var server string + var server_address string if len(args) >= 1 { - server = args[0] + server_address = args[0] args = args[1:] } - _, server = server_input(server) flag.CommandLine.Parse(args) if G_help { @@ -80,26 +79,16 @@ func world_main(ctx context.Context, args []string) error { return nil } - _status := minecraft.NewStatusProvider("Server") - listener, err := minecraft.ListenConfig{ - StatusProvider: _status, - }.Listen("raknet", ":19132") + server_address, hostname, err := server_input(server_address) if err != nil { return err } - defer listener.Close() - fmt.Printf("Listening on %s\n", listener.Addr()) - - c, err := listener.Accept() + listener, clientConn, serverConn, err := create_proxy(ctx, server_address) if err != nil { - log.Fatal(err) + return err } - - // not a goroutine, only 1 client at a time - - world_state := NewWorldState() - world_state.handleConn(ctx, c.(*minecraft.Conn), listener, server) + handleConn(ctx, listener, clientConn, serverConn, hostname) return nil } @@ -188,9 +177,8 @@ func (w *WorldState) ProcessAnimate(pk *packet.Animate) { func (w *WorldState) ProcessChangeDimension(pk *packet.ChangeDimension) { fmt.Printf("ChangeDimension %d\n", pk.Dimension) + w.SaveAndReset() w.Dim = dimension_ids[pk.Dimension] - w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk) - w.ui.Reset() } func (w *WorldState) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) { @@ -220,57 +208,36 @@ func (w *WorldState) SaveAndReset() { int(w.PlayerPos.Position[1]), int(w.PlayerPos.Position[2]), } + + for _, gr := range w.ServerConn.GameData().GameRules { + switch gr.Name { + // todo + } + } + provider.SaveSettings(s) provider.Close() w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk) w.ui.Reset() } -func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, listener *minecraft.Listener, target string) { - w.ClientConn = conn - - var packet_func func(header packet.Header, payload []byte, src, dst net.Addr) = nil - if G_debug { - packet_func = PacketLogger - } - - fmt.Printf("Connecting to %s\n", target) - serverConn, err := minecraft.Dialer{ - TokenSource: G_src, - ClientData: conn.ClientData(), - PacketFunc: packet_func, - }.DialContext(ctx, "raknet", target) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to connect to %s: %s\n", target, err) - return - } - w.ServerConn = serverConn - - if err := spawn_conn(ctx, conn, serverConn); err != nil { - fmt.Fprintf(os.Stderr, "Failed to spawn: %s\n", err) - return - } +func handleConn(ctx context.Context, l *minecraft.Listener, cc, sc *minecraft.Conn, server_name string) { + w := NewWorldState() + w.ServerName = server_name + w.ClientConn = cc + w.ServerConn = sc G_exit = append(G_exit, func() { - serverConn.Close() - conn.WritePacket(&packet.Disconnect{ - Message: "Closing", - HideDisconnectionScreen: false, - }) - conn.Close() - listener.Close() w.SaveAndReset() }) done := make(chan struct{}) go func() { // client loop - defer listener.Disconnect(conn, "connection lost") - defer serverConn.Close() defer func() { done <- struct{}{} }() for { skip := false - pk, err := conn.ReadPacket() + pk, err := w.ClientConn.ReadPacket() if err != nil { return } @@ -287,16 +254,16 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste } case *packet.ClientCacheStatus: pk.Enabled = false - serverConn.WritePacket(pk) + w.ServerConn.WritePacket(pk) skip = true case *packet.Animate: w.ProcessAnimate(pk) } if !skip { - if err := serverConn.WritePacket(pk); err != nil { + if err := w.ServerConn.WritePacket(pk); err != nil { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { - _ = listener.Disconnect(conn, disconnect.Error()) + _ = l.Disconnect(w.ClientConn, disconnect.Error()) } return } @@ -311,7 +278,7 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste default: for { time.Sleep(1 * time.Second) - err := conn.WritePacket(&MAP_ITEM_PACKET) + err := w.ClientConn.WritePacket(&MAP_ITEM_PACKET) if err != nil { return } @@ -320,11 +287,11 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste }() go func() { // server loop - defer serverConn.Close() - defer listener.Disconnect(conn, "connection lost") + defer w.ServerConn.Close() + defer l.Disconnect(w.ClientConn, "connection lost") defer func() { done <- struct{}{} }() - gd := serverConn.GameData() + gd := w.ServerConn.GameData() w.ProcessChangeDimension(&packet.ChangeDimension{ Dimension: gd.Dimension, Position: gd.PlayerPosition, @@ -332,10 +299,10 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste }) for { - pk, err := serverConn.ReadPacket() + pk, err := w.ServerConn.ReadPacket() if err != nil { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { - _ = listener.Disconnect(conn, disconnect.Error()) + _ = l.Disconnect(w.ClientConn, disconnect.Error()) } return } @@ -346,12 +313,12 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste case *packet.LevelChunk: w.ProcessLevelChunk(pk) w.ui.Send(w) - send_popup(conn, fmt.Sprintf("%d chunks loaded\n", len(w.chunks))) + send_popup(w.ClientConn, fmt.Sprintf("%d chunks loaded\n", len(w.chunks))) case *packet.SubChunk: w.ProcessSubChunk(pk) } - if err := conn.WritePacket(pk); err != nil { + if err := w.ClientConn.WritePacket(pk); err != nil { return } }