clean up connection code

This commit is contained in:
olebeck 2022-08-13 16:30:46 +02:00
parent 02b92b9788
commit 4068840bdf
6 changed files with 139 additions and 174 deletions

View File

@ -14,7 +14,6 @@ import (
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/google/gopacket/pcapgo" "github.com/google/gopacket/pcapgo"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
) )
@ -83,46 +82,12 @@ func packets_main(ctx context.Context, args []string) error {
return nil return nil
} }
var hostname string address, hostname, err := server_input(server)
hostname, server = server_input(server)
_status := minecraft.NewStatusProvider("Server") listener, serverConn, clientConn, err := create_proxy(ctx, address)
listener, err := minecraft.ListenConfig{
StatusProvider: _status,
}.Listen("raknet", ":19132")
if err != nil { if err != nil {
return err 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") f, err := os.Create(hostname + "-" + time.Now().Format("2006-01-02_15-04-05") + ".pcap")
if err != nil { if err != nil {
@ -133,25 +98,13 @@ func packets_main(ctx context.Context, args []string) error {
w.WriteFileHeader(65536, layers.LinkTypeEthernet) w.WriteFileHeader(65536, layers.LinkTypeEthernet)
_wl := sync.Mutex{} _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() { go func() {
defer listener.Disconnect(conn, "connection lost") defer listener.Disconnect(clientConn, "connection lost")
defer serverConn.Close() defer serverConn.Close()
for { for {
pk, err := conn.ReadPacket() pk, err := clientConn.ReadPacket()
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read packet: %s\n", err) fmt.Fprintf(os.Stderr, "Failed to read packet: %s\n", err)
return return
@ -180,7 +133,7 @@ func packets_main(ctx context.Context, args []string) error {
dump_packet(false, w, pk) dump_packet(false, w, pk)
_wl.Unlock() _wl.Unlock()
err = conn.WritePacket(pk) err = clientConn.WritePacket(pk)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Failed to write packet: %s\n", err) fmt.Fprintf(os.Stderr, "Failed to write packet: %s\n", err)
return nil return nil

Binary file not shown.

View File

@ -28,33 +28,19 @@ func skin_proxy_main(ctx context.Context, args []string) error {
return nil return nil
} }
_status := minecraft.NewStatusProvider("Server") address, hostname, err := server_input(server)
listener, err := minecraft.ListenConfig{ if err != nil {
StatusProvider: _status, return err
}.Listen("raknet", ":19132") }
listener, clientConn, serverConn, err := create_proxy(ctx, address)
if err != nil { if err != nil {
return err return err
} }
defer listener.Close() 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) out_path := fmt.Sprintf("skins/%s", hostname)
if err := spawn_conn(ctx, conn, serverConn); err != nil {
return err
}
println("Connected") println("Connected")
println("Press ctrl+c to exit") 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) errs := make(chan error, 2)
go func() { // server -> client go func() { // server -> client
defer serverConn.Close() defer serverConn.Close()
defer listener.Disconnect(conn, "connection lost") defer listener.Disconnect(clientConn, "connection lost")
for { for {
pk, err := serverConn.ReadPacket() pk, err := serverConn.ReadPacket()
if err != nil { if err != nil {
if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
_ = listener.Disconnect(conn, disconnect.Error()) _ = listener.Disconnect(clientConn, disconnect.Error())
} }
return 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 return
} }
} }
@ -82,14 +68,14 @@ func skin_proxy_main(ctx context.Context, args []string) error {
go func() { // client -> server go func() { // client -> server
for { for {
pk, err := conn.ReadPacket() pk, err := clientConn.ReadPacket()
if err != nil { if err != nil {
return return
} }
if err := serverConn.WritePacket(pk); err != nil { if err := serverConn.WritePacket(pk); err != nil {
if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
_ = listener.Disconnect(conn, disconnect.Error()) _ = listener.Disconnect(clientConn, disconnect.Error())
} }
return return
} }

View File

@ -184,7 +184,12 @@ func skin_main(ctx context.Context, args []string) error {
return nil 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 { if err != nil {
return err return err
} }

104
utils.go
View File

@ -5,6 +5,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"net" "net"
"os" "os"
"regexp" "regexp"
@ -12,6 +13,7 @@ import (
"github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/auth" "github.com/sandertv/gophertunnel/minecraft/auth"
"github.com/sandertv/gophertunnel/minecraft/protocol/login"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -49,55 +51,69 @@ func get_token() oauth2.Token {
return token return token
} }
func server_input(server string) (string, string) { func server_input(server string) (address, name string, err error) {
if server == "" { if server == "" { // no arg provided, interactive input
fmt.Printf("Enter Server: ") fmt.Printf("Enter Server: ")
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
server, _ = reader.ReadString('\n') server, _ = reader.ReadString('\n')
r, _ := regexp.Compile(`[\n\r]`) r, _ := regexp.Compile(`[\n\r]`)
server = string(r.ReplaceAll([]byte(server), []byte(""))) 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) host, _, err := net.SplitHostPort(server)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Invalid server: %s\n", err) fmt.Fprintf(os.Stderr, "Invalid server: %s\n", err)
os.Exit(1) os.Exit(1)
} }
return host, server return host
} }
func connect_server(ctx context.Context, server string) (hostname string, conn *minecraft.Conn, err error) { func connect_server(ctx context.Context, address string, ClientData *login.ClientData) (serverConn *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)
}
var packet_func func(header packet.Header, payload []byte, src, dst net.Addr) = nil var packet_func func(header packet.Header, payload []byte, src, dst net.Addr) = nil
if G_debug { if G_debug {
packet_func = PacketLogger packet_func = PacketLogger
} }
fmt.Printf("Connecting to %s\n", server) cd := login.ClientData{}
conn, err = minecraft.Dialer{ if ClientData != nil {
TokenSource: G_src, cd = *ClientData
PacketFunc: packet_func,
}.DialContext(ctx, "raknet", server)
if err != nil {
return "", nil, err
} }
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) errs := make(chan error, 2)
go func() { go func() {
errs <- conn.StartGame(serverConn.GameData()) errs <- clientConn.StartGame(serverConn.GameData())
}() }()
go func() { go func() {
errs <- serverConn.DoSpawn() errs <- serverConn.DoSpawn()
@ -116,3 +132,41 @@ func spawn_conn(ctx context.Context, conn *minecraft.Conn, serverConn *minecraft
} }
return nil 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
}

105
world.go
View File

@ -8,8 +8,6 @@ import (
"image" "image"
"image/draw" "image/draw"
"log" "log"
"net"
"os"
"path" "path"
"time" "time"
@ -36,9 +34,11 @@ type TPlayerPos struct {
// the state used for drawing and saving // the state used for drawing and saving
type WorldState struct { type WorldState struct {
chunks map[protocol.ChunkPos]*chunk.Chunk chunks map[protocol.ChunkPos]*chunk.Chunk
Dim world.Dimension Dim world.Dimension
WorldName string WorldName string
ServerName string
worldCounter int
PlayerPos TPlayerPos PlayerPos TPlayerPos
ClientConn *minecraft.Conn ClientConn *minecraft.Conn
@ -66,13 +66,12 @@ func init() {
} }
func world_main(ctx context.Context, args []string) error { func world_main(ctx context.Context, args []string) error {
var server string var server_address string
if len(args) >= 1 { if len(args) >= 1 {
server = args[0] server_address = args[0]
args = args[1:] args = args[1:]
} }
_, server = server_input(server)
flag.CommandLine.Parse(args) flag.CommandLine.Parse(args)
if G_help { if G_help {
@ -80,26 +79,16 @@ func world_main(ctx context.Context, args []string) error {
return nil return nil
} }
_status := minecraft.NewStatusProvider("Server") server_address, hostname, err := server_input(server_address)
listener, err := minecraft.ListenConfig{
StatusProvider: _status,
}.Listen("raknet", ":19132")
if err != nil { if err != nil {
return err return err
} }
defer listener.Close()
fmt.Printf("Listening on %s\n", listener.Addr()) listener, clientConn, serverConn, err := create_proxy(ctx, server_address)
c, err := listener.Accept()
if err != nil { if err != nil {
log.Fatal(err) return err
} }
handleConn(ctx, listener, clientConn, serverConn, hostname)
// not a goroutine, only 1 client at a time
world_state := NewWorldState()
world_state.handleConn(ctx, c.(*minecraft.Conn), listener, server)
return nil return nil
} }
@ -188,9 +177,8 @@ func (w *WorldState) ProcessAnimate(pk *packet.Animate) {
func (w *WorldState) ProcessChangeDimension(pk *packet.ChangeDimension) { func (w *WorldState) ProcessChangeDimension(pk *packet.ChangeDimension) {
fmt.Printf("ChangeDimension %d\n", pk.Dimension) fmt.Printf("ChangeDimension %d\n", pk.Dimension)
w.SaveAndReset()
w.Dim = dimension_ids[pk.Dimension] 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) { 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[1]),
int(w.PlayerPos.Position[2]), int(w.PlayerPos.Position[2]),
} }
for _, gr := range w.ServerConn.GameData().GameRules {
switch gr.Name {
// todo
}
}
provider.SaveSettings(s) provider.SaveSettings(s)
provider.Close() provider.Close()
w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk) w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk)
w.ui.Reset() w.ui.Reset()
} }
func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, listener *minecraft.Listener, target string) { func handleConn(ctx context.Context, l *minecraft.Listener, cc, sc *minecraft.Conn, server_name string) {
w.ClientConn = conn w := NewWorldState()
w.ServerName = server_name
var packet_func func(header packet.Header, payload []byte, src, dst net.Addr) = nil w.ClientConn = cc
if G_debug { w.ServerConn = sc
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
}
G_exit = append(G_exit, func() { G_exit = append(G_exit, func() {
serverConn.Close()
conn.WritePacket(&packet.Disconnect{
Message: "Closing",
HideDisconnectionScreen: false,
})
conn.Close()
listener.Close()
w.SaveAndReset() w.SaveAndReset()
}) })
done := make(chan struct{}) done := make(chan struct{})
go func() { // client loop go func() { // client loop
defer listener.Disconnect(conn, "connection lost")
defer serverConn.Close()
defer func() { done <- struct{}{} }() defer func() { done <- struct{}{} }()
for { for {
skip := false skip := false
pk, err := conn.ReadPacket() pk, err := w.ClientConn.ReadPacket()
if err != nil { if err != nil {
return return
} }
@ -287,16 +254,16 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste
} }
case *packet.ClientCacheStatus: case *packet.ClientCacheStatus:
pk.Enabled = false pk.Enabled = false
serverConn.WritePacket(pk) w.ServerConn.WritePacket(pk)
skip = true skip = true
case *packet.Animate: case *packet.Animate:
w.ProcessAnimate(pk) w.ProcessAnimate(pk)
} }
if !skip { 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 { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
_ = listener.Disconnect(conn, disconnect.Error()) _ = l.Disconnect(w.ClientConn, disconnect.Error())
} }
return return
} }
@ -311,7 +278,7 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste
default: default:
for { for {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
err := conn.WritePacket(&MAP_ITEM_PACKET) err := w.ClientConn.WritePacket(&MAP_ITEM_PACKET)
if err != nil { if err != nil {
return return
} }
@ -320,11 +287,11 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste
}() }()
go func() { // server loop go func() { // server loop
defer serverConn.Close() defer w.ServerConn.Close()
defer listener.Disconnect(conn, "connection lost") defer l.Disconnect(w.ClientConn, "connection lost")
defer func() { done <- struct{}{} }() defer func() { done <- struct{}{} }()
gd := serverConn.GameData() gd := w.ServerConn.GameData()
w.ProcessChangeDimension(&packet.ChangeDimension{ w.ProcessChangeDimension(&packet.ChangeDimension{
Dimension: gd.Dimension, Dimension: gd.Dimension,
Position: gd.PlayerPosition, Position: gd.PlayerPosition,
@ -332,10 +299,10 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste
}) })
for { for {
pk, err := serverConn.ReadPacket() pk, err := w.ServerConn.ReadPacket()
if err != nil { if err != nil {
if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
_ = listener.Disconnect(conn, disconnect.Error()) _ = l.Disconnect(w.ClientConn, disconnect.Error())
} }
return return
} }
@ -346,12 +313,12 @@ func (w *WorldState) handleConn(ctx context.Context, conn *minecraft.Conn, liste
case *packet.LevelChunk: case *packet.LevelChunk:
w.ProcessLevelChunk(pk) w.ProcessLevelChunk(pk)
w.ui.Send(w) 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: case *packet.SubChunk:
w.ProcessSubChunk(pk) w.ProcessSubChunk(pk)
} }
if err := conn.WritePacket(pk); err != nil { if err := w.ClientConn.WritePacket(pk); err != nil {
return return
} }
} }