152 lines
3.4 KiB
Go
152 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/akmalfairuz/legacy-version/legacyver"
|
|
"github.com/pelletier/go-toml"
|
|
"github.com/sandertv/gophertunnel/minecraft"
|
|
"github.com/sandertv/gophertunnel/minecraft/auth"
|
|
"golang.org/x/oauth2"
|
|
"log"
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
// The following program implements a proxy that forwards players from one local address to a remote address.
|
|
func main() {
|
|
config := readConfig()
|
|
token, err := auth.RequestLiveToken()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
src := auth.RefreshTokenSource(token)
|
|
|
|
p, err := minecraft.NewForeignStatusProvider(config.Connection.RemoteAddress)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
listener, err := minecraft.ListenConfig{
|
|
StatusProvider: p,
|
|
AcceptedProtocols: []minecraft.Protocol{
|
|
legacyver.New748(),
|
|
legacyver.New729(),
|
|
legacyver.New712(),
|
|
legacyver.New686(),
|
|
legacyver.New685(),
|
|
legacyver.New671(),
|
|
},
|
|
}.Listen("raknet", config.Connection.LocalAddress)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer listener.Close()
|
|
for {
|
|
c, err := listener.Accept()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
go handleConn(c.(*minecraft.Conn), listener, config, src)
|
|
}
|
|
}
|
|
|
|
// handleConn handles a new incoming minecraft.Conn from the minecraft.Listener passed.
|
|
func handleConn(conn *minecraft.Conn, listener *minecraft.Listener, config config, src oauth2.TokenSource) {
|
|
serverConn, err := minecraft.Dialer{
|
|
TokenSource: src,
|
|
ClientData: conn.ClientData(),
|
|
}.Dial("raknet", config.Connection.RemoteAddress)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
var g sync.WaitGroup
|
|
g.Add(2)
|
|
go func() {
|
|
if err := conn.StartGame(serverConn.GameData()); err != nil {
|
|
panic(err)
|
|
}
|
|
g.Done()
|
|
}()
|
|
go func() {
|
|
if err := serverConn.DoSpawn(); err != nil {
|
|
panic(err)
|
|
}
|
|
g.Done()
|
|
}()
|
|
g.Wait()
|
|
|
|
go func() {
|
|
defer listener.Disconnect(conn, "connection lost")
|
|
defer serverConn.Close()
|
|
for {
|
|
pk, err := conn.ReadPacket()
|
|
if err != nil {
|
|
return
|
|
}
|
|
if err := serverConn.WritePacket(pk); err != nil {
|
|
var disc minecraft.DisconnectError
|
|
if ok := errors.As(err, &disc); ok {
|
|
_ = listener.Disconnect(conn, disc.Error())
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
go func() {
|
|
defer serverConn.Close()
|
|
defer listener.Disconnect(conn, "connection lost")
|
|
for {
|
|
pk, err := serverConn.ReadPacket()
|
|
if err != nil {
|
|
var disc minecraft.DisconnectError
|
|
if ok := errors.As(err, &disc); ok {
|
|
_ = listener.Disconnect(conn, disc.Error())
|
|
}
|
|
return
|
|
}
|
|
if err := conn.WritePacket(pk); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
type config struct {
|
|
Connection struct {
|
|
LocalAddress string
|
|
RemoteAddress string
|
|
}
|
|
}
|
|
|
|
func readConfig() config {
|
|
c := config{}
|
|
if _, err := os.Stat("config.toml"); os.IsNotExist(err) {
|
|
f, err := os.Create("config.toml")
|
|
if err != nil {
|
|
log.Fatalf("create config: %v", err)
|
|
}
|
|
data, err := toml.Marshal(c)
|
|
if err != nil {
|
|
log.Fatalf("encode default config: %v", err)
|
|
}
|
|
if _, err := f.Write(data); err != nil {
|
|
log.Fatalf("write default config: %v", err)
|
|
}
|
|
_ = f.Close()
|
|
}
|
|
data, err := os.ReadFile("config.toml")
|
|
if err != nil {
|
|
log.Fatalf("read config: %v", err)
|
|
}
|
|
if err := toml.Unmarshal(data, &c); err != nil {
|
|
log.Fatalf("decode config: %v", err)
|
|
}
|
|
if c.Connection.LocalAddress == "" {
|
|
c.Connection.LocalAddress = "0.0.0.0:19132"
|
|
}
|
|
data, _ = toml.Marshal(c)
|
|
if err := os.WriteFile("config.toml", data, 0644); err != nil {
|
|
log.Fatalf("write config: %v", err)
|
|
}
|
|
return c
|
|
}
|