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/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

Binary file not shown.

View File

@ -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
}

View File

@ -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
}

104
utils.go
View File

@ -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
}

105
world.go
View File

@ -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
}
}