diff --git a/.gitignore b/.gitignore index 723ef36..9dd500b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -.idea \ No newline at end of file +.idea +token.tok +example-proxy \ No newline at end of file diff --git a/example-proxy.go b/example-proxy.go index 4f6678b..6d9b32c 100644 --- a/example-proxy.go +++ b/example-proxy.go @@ -7,21 +7,21 @@ import ( "sync" "time" + "encoding/json" + "fmt" "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" + "os/signal" + "syscall" ) // 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) + src := tokenSource() p, err := minecraft.NewForeignStatusProvider(config.Connection.RemoteAddress) if err != nil { @@ -98,6 +98,12 @@ func handleConn(conn *minecraft.Conn, listener *minecraft.Listener, config confi } return } + fmt.Println(pk.ID()) + time.Sleep(time.Millisecond * 1000) + if pk.ID() == 498 { + fmt.Println("skipping packet 498") + continue + } if err := conn.WritePacket(pk); err != nil { return } @@ -144,3 +150,41 @@ func readConfig() config { } return c } + +// tokenSource returns a token source for using with a gophertunnel client. It either reads it from the +// token.tok file if cached or requests logging in with a device code. +func tokenSource() oauth2.TokenSource { + check := func(err error) { + if err != nil { + panic(err) + } + } + token := new(oauth2.Token) + tokenData, err := os.ReadFile("token.tok") + if err == nil { + _ = json.Unmarshal(tokenData, token) + } else { + token, err = auth.RequestLiveToken() + check(err) + } + src := auth.RefreshTokenSource(token) + _, err = src.Token() + if err != nil { + // The cached refresh token expired and can no longer be used to obtain a new token. We require the + // user to log in again and use that token instead. + token, err = auth.RequestLiveToken() + check(err) + src = auth.RefreshTokenSource(token) + } + go func() { + c := make(chan os.Signal, 3) + signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) + <-c + + tok, _ := src.Token() + b, _ := json.Marshal(tok) + _ = os.WriteFile("token.tok", b, 0644) + os.Exit(0) + }() + return src +} diff --git a/go.mod b/go.mod index 40e5ac7..d4529d9 100644 --- a/go.mod +++ b/go.mod @@ -33,4 +33,4 @@ require ( golang.org/x/text v0.30.0 // indirect ) -replace github.com/sandertv/gophertunnel => github.com/cooldogedev/gophertunnel v0.0.0-20251210050513-6e8700778c34 +replace github.com/sandertv/gophertunnel => ../../venitydf/gophertunnel diff --git a/legacyver/all.go b/legacyver/all.go index 3b012ca..e3350c0 100644 --- a/legacyver/all.go +++ b/legacyver/all.go @@ -21,6 +21,7 @@ var ( // must be set to true if you're using Dragonfly. func All(dragonflyMapping bool) []minecraft.Protocol { return []minecraft.Protocol{ + New859(dragonflyMapping), New844(dragonflyMapping), New827(dragonflyMapping), New819(dragonflyMapping), diff --git a/legacyver/legacypacket/command_output.go b/legacyver/legacypacket/command_output.go index ff115ee..8add30b 100644 --- a/legacyver/legacypacket/command_output.go +++ b/legacyver/legacypacket/command_output.go @@ -52,7 +52,11 @@ func (pk *CommandOutput) Marshal(io protocol.IO) { } else { v, _ := pk.DataSet.Value() io.String(&v) - pk.DataSet = protocol.Option(v) + if v != "" { + pk.DataSet = protocol.Option(v) + } else { + pk.DataSet = protocol.Optional[string]{} + } } } diff --git a/legacyver/legacypacket/interact.go b/legacyver/legacypacket/interact.go index 07e01fe..2e5a384 100644 --- a/legacyver/legacypacket/interact.go +++ b/legacyver/legacypacket/interact.go @@ -32,9 +32,13 @@ func (pk *Interact) Marshal(io protocol.IO) { io.Varuint64(&pk.TargetEntityRuntimeID) if proto.IsProtoGTE(io, proto.ID898) { protocol.OptionalFunc(io, &pk.Position, io.Vec3) - } else { + } else if pk.ActionType == packet.InteractActionMouseOverEntity || pk.ActionType == packet.InteractActionLeaveVehicle { pos, _ := pk.Position.Value() io.Vec3(&pos) - pk.Position = protocol.Option(pos) + if pos != (mgl32.Vec3{}) { + pk.Position = protocol.Option(pos) + } else { + pk.Position = protocol.Optional[mgl32.Vec3]{} + } } } diff --git a/legacyver/legacypacket/text.go b/legacyver/legacypacket/text.go index f1d63e1..46db909 100644 --- a/legacyver/legacypacket/text.go +++ b/legacyver/legacypacket/text.go @@ -73,6 +73,7 @@ func (pk *Text) Marshal(io protocol.IO) { categoryType = protocol.TextCategoryMessageWithParameters } io.TextCategory(&categoryType) + io.Uint8(&pk.TextType) } switch pk.TextType { case TextTypeChat, TextTypeWhisper, TextTypeAnnouncement: @@ -84,6 +85,11 @@ func (pk *Text) Marshal(io protocol.IO) { io.String(&pk.Message) protocol.FuncSlice(io, &pk.Parameters, io.String) } + if proto.IsProtoGTE(io, proto.ID898) { + if len(pk.Message) == 0 { + io.InvalidValue(pk.Message, "message", "string cannot be empty") + } + } io.String(&pk.XUID) io.String(&pk.PlatformChatID) if proto.IsProtoGTE(io, proto.ID685) { @@ -92,7 +98,11 @@ func (pk *Text) Marshal(io protocol.IO) { } else { v, _ := pk.FilteredMessage.Value() io.String(&v) - pk.FilteredMessage = protocol.Option(v) + if v != "" { + pk.FilteredMessage = protocol.Option(v) + } else { + pk.FilteredMessage = protocol.Optional[string]{} + } } } } diff --git a/legacyver/proto/container.go b/legacyver/proto/container.go index e45475c..f93fe50 100644 --- a/legacyver/proto/container.go +++ b/legacyver/proto/container.go @@ -28,11 +28,13 @@ func (x *FullContainerName) Marshal(r protocol.IO) { r.Uint8(&x.ContainerID) if IsProtoGTE(r, ID729) { protocol.OptionalFunc(r, &x.DynamicContainerID, r.Uint32) - } else if IsProtoGTE(r, 712) { + } else if IsProtoGTE(r, ID712) { dynamicContainerID, _ := x.DynamicContainerID.Value() r.Uint32(&dynamicContainerID) if dynamicContainerID != 0 { x.DynamicContainerID = protocol.Option(dynamicContainerID) + } else { + x.DynamicContainerID = protocol.Optional[uint32]{} } } }