Skip to content

Commit 48a7f91

Browse files
committed
sync java protocol 26.2 updates
1 parent a4fa684 commit 48a7f91

10 files changed

Lines changed: 196 additions & 7 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package brigadier
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
"go.minekube.com/gate/pkg/edition/java/proto/util"
9+
"go.minekube.com/gate/pkg/edition/java/proto/version"
10+
)
11+
12+
func TestMinecraft262RenamesColorArgumentToTeamColor(t *testing.T) {
13+
var buf bytes.Buffer
14+
require.NoError(t, util.WriteVarInt(&buf, 16))
15+
16+
arg, err := Decode(&buf, version.Minecraft_26_2.Protocol)
17+
require.NoError(t, err)
18+
require.Equal(t, "minecraft:team_color", arg.String())
19+
}
20+
21+
func TestColorArgumentStillDecodesBeforeMinecraft262(t *testing.T) {
22+
var buf bytes.Buffer
23+
require.NoError(t, util.WriteVarInt(&buf, 16))
24+
25+
arg, err := Decode(&buf, version.Minecraft_1_21_11.Protocol)
26+
require.NoError(t, err)
27+
require.Equal(t, "minecraft:color", arg.String())
28+
}

pkg/edition/java/proto/packet/brigadier/registry.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ func init() {
191191
empty(id("minecraft:block_predicate", mapSet(Minecraft_1_19, 13)))
192192
empty(id("minecraft:item_stack", mapSet(Minecraft_1_19, 14)))
193193
empty(id("minecraft:item_predicate", mapSet(Minecraft_1_19, 15)))
194-
empty(id("minecraft:color", mapSet(Minecraft_1_19, 16)))
195-
empty(id("minecraft:hex_color", mapSet(Minecraft_1_21_6, 17))) // added in 1.21.6
194+
empty(id("minecraft:color", mapSet(Minecraft_26_2, -1), mapSet(Minecraft_1_19, 16))) // renamed to team_color in 26.2
195+
empty(id("minecraft:hex_color", mapSet(Minecraft_1_21_6, 17))) // added in 1.21.6
196196
empty(id("minecraft:component", mapSet(Minecraft_1_21_6, 18), mapSet(Minecraft_1_19, 17)))
197197
empty(id("minecraft:style", mapSet(Minecraft_1_21_6, 19), mapSet(Minecraft_1_20_3, 18)))
198198
empty(id("minecraft:message", mapSet(Minecraft_1_21_6, 20), mapSet(Minecraft_1_20_3, 19), mapSet(Minecraft_1_19, 18)))
@@ -236,7 +236,8 @@ func init() {
236236
empty(id("minecraft:loot_predicate", mapSet(Minecraft_1_21_6, 53), mapSet(Minecraft_1_21_5, 52), mapSet(Minecraft_1_20_5, 51)))
237237
empty(id("minecraft:loot_modifier", mapSet(Minecraft_1_21_6, 54), mapSet(Minecraft_1_21_5, 53), mapSet(Minecraft_1_20_5, 52)))
238238

239-
empty(id("minecraft:dialog", mapSet(Minecraft_1_21_6, 55))) // added in 1.21.6
239+
empty(id("minecraft:dialog", mapSet(Minecraft_1_21_6, 55))) // added in 1.21.6
240+
empty(id("minecraft:team_color", mapSet(Minecraft_26_2, 16))) // renamed from color in 26.2
240241

241242
// Crossstitch support
242243
register(id("crossstitch:mod_argument", mapSet(Minecraft_1_19, -256)), &ModArgumentProperty{}, ModArgumentPropertyCodec)

pkg/edition/java/proto/packet/joingame.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type JoinGame struct {
3232
LastDeathPosition *DeathPosition // 1.19+
3333
PortalCooldown int // 1.20+
3434
SeaLevel int // 1.21.2+
35+
OnlineMode bool // 26.2+
3536
EnforcesSecureChat bool // 1.20.5+
3637
}
3738

@@ -212,10 +213,13 @@ func (j *JoinGame) encode1202Up(c *proto.PacketContext, wr io.Writer) error {
212213
} else {
213214
w.Bool(false)
214215
}
216+
w.VarInt(j.PortalCooldown)
215217
if c.Protocol.GreaterEqual(version.Minecraft_1_21_2) {
216218
w.VarInt(j.SeaLevel)
217219
}
218-
w.VarInt(j.PortalCooldown)
220+
if c.Protocol.GreaterEqual(version.Minecraft_26_2) {
221+
w.Bool(j.OnlineMode)
222+
}
219223
if c.Protocol.GreaterEqual(version.Minecraft_1_20_5) {
220224
w.Bool(j.EnforcesSecureChat)
221225
}
@@ -396,11 +400,14 @@ func (j *JoinGame) decode1202Up(c *proto.PacketContext, rd io.Reader) error {
396400
}
397401
}
398402

403+
r.VarInt(&j.PortalCooldown)
399404
if c.Protocol.GreaterEqual(version.Minecraft_1_21_2) {
400405
r.VarInt(&j.SeaLevel)
401406
}
402407

403-
r.VarInt(&j.PortalCooldown)
408+
if c.Protocol.GreaterEqual(version.Minecraft_26_2) {
409+
r.Bool(&j.OnlineMode)
410+
}
404411
if c.Protocol.GreaterEqual(version.Minecraft_1_20_5) {
405412
r.Bool(&j.EnforcesSecureChat)
406413
}

pkg/edition/java/proto/packet/login.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ type ServerLoginSuccess struct {
308308
UUID uuid.UUID
309309
Username string
310310
Properties []profile.Property // 1.19+
311+
SessionID uuid.UUID // 26.2+
311312
}
312313

313314
const serverLoginSuccessStrictErrorHandling = true
@@ -344,6 +345,12 @@ func (s *ServerLoginSuccess) Encode(c *proto.PacketContext, wr io.Writer) (err e
344345
return err
345346
}
346347
}
348+
if c.Protocol.GreaterEqual(version.Minecraft_26_2) {
349+
err = util.WriteUUID(wr, s.SessionID)
350+
if err != nil {
351+
return err
352+
}
353+
}
347354
return nil
348355
}
349356

@@ -386,6 +393,12 @@ func (s *ServerLoginSuccess) Decode(c *proto.PacketContext, rd io.Reader) (err e
386393
return
387394
}
388395
}
396+
if c.Protocol.GreaterEqual(version.Minecraft_26_2) {
397+
s.SessionID, err = util.ReadUUID(rd)
398+
if err != nil {
399+
return
400+
}
401+
}
389402
return
390403
}
391404

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package packet
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
"go.minekube.com/gate/pkg/edition/java/proto/version"
9+
"go.minekube.com/gate/pkg/gate/proto"
10+
"go.minekube.com/gate/pkg/util/uuid"
11+
)
12+
13+
func TestJoinGameMinecraft262OnlineModeRoundTrip(t *testing.T) {
14+
levelName := "minecraft:overworld"
15+
original := &JoinGame{
16+
EntityID: 1,
17+
Hardcore: true,
18+
LevelNames: []string{"minecraft:overworld"},
19+
MaxPlayers: 100,
20+
ViewDistance: 12,
21+
SimulationDistance: 10,
22+
ReducedDebugInfo: true,
23+
ShowRespawnScreen: true,
24+
DoLimitedCrafting: true,
25+
Dimension: 0,
26+
DimensionInfo: &DimensionInfo{
27+
LevelName: &levelName,
28+
},
29+
PartialHashedSeed: 1234,
30+
Gamemode: 1,
31+
PreviousGamemode: -1,
32+
LastDeathPosition: &DeathPosition{Key: "minecraft:overworld", Value: 42},
33+
SeaLevel: 63,
34+
PortalCooldown: 5,
35+
OnlineMode: true,
36+
EnforcesSecureChat: true,
37+
}
38+
ctx := &proto.PacketContext{Direction: proto.ClientBound, Protocol: version.Minecraft_26_2.Protocol}
39+
40+
var buf bytes.Buffer
41+
require.NoError(t, original.Encode(ctx, &buf))
42+
require.True(t, bytes.HasSuffix(buf.Bytes(), []byte{
43+
0x05, // portal cooldown
44+
0x3f, // sea level
45+
0x01, // online mode
46+
0x01, // enforces secure chat
47+
}))
48+
49+
var decoded JoinGame
50+
require.NoError(t, decoded.Decode(ctx, &buf))
51+
require.True(t, decoded.OnlineMode)
52+
require.True(t, decoded.EnforcesSecureChat)
53+
require.Equal(t, 0, buf.Len())
54+
}
55+
56+
func TestServerLoginSuccessMinecraft262SessionIDRoundTrip(t *testing.T) {
57+
sessionID := uuid.New()
58+
original := &ServerLoginSuccess{
59+
UUID: testUUID,
60+
Username: "Robin",
61+
SessionID: sessionID,
62+
}
63+
ctx := &proto.PacketContext{Direction: proto.ClientBound, Protocol: version.Minecraft_26_2.Protocol}
64+
65+
var buf bytes.Buffer
66+
require.NoError(t, original.Encode(ctx, &buf))
67+
68+
var decoded ServerLoginSuccess
69+
require.NoError(t, decoded.Decode(ctx, &buf))
70+
require.Equal(t, sessionID, decoded.SessionID)
71+
require.Equal(t, 0, buf.Len())
72+
}

pkg/edition/java/proto/version/version.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ var (
5353
Minecraft_1_21_9 = v(773, "1.21.9", "1.21.10")
5454
Minecraft_1_21_11 = v(774, "1.21.11")
5555
Minecraft_26_1 = v(775, "26.1", "26.1.1", "26.1.2")
56+
Minecraft_26_2 = v(776, "26.2")
5657

5758
// Versions ordered from lowest to highest
5859
Versions = []*proto.Version{
@@ -72,7 +73,8 @@ var (
7273
Minecraft_1_18, Minecraft_1_18_2,
7374
Minecraft_1_19, Minecraft_1_19_1, Minecraft_1_19_3, Minecraft_1_19_4,
7475
Minecraft_1_20, Minecraft_1_20_2, Minecraft_1_20_3, Minecraft_1_20_5,
75-
Minecraft_1_21, Minecraft_1_21_2, Minecraft_1_21_4, Minecraft_1_21_5, Minecraft_1_21_6, Minecraft_1_21_7, Minecraft_1_21_9, Minecraft_1_21_11, Minecraft_26_1,
76+
Minecraft_1_21, Minecraft_1_21_2, Minecraft_1_21_4, Minecraft_1_21_5, Minecraft_1_21_6, Minecraft_1_21_7, Minecraft_1_21_9, Minecraft_1_21_11,
77+
Minecraft_26_1, Minecraft_26_2,
7678
}
7779
)
7880

pkg/edition/java/proxy/proxy.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ type Proxy struct {
6464
playerNames map[string]*connectedPlayer // lower case usernames map
6565
playerIDs map[uuid.UUID]*connectedPlayer // uuids map
6666

67+
sessionIDMu sync.Mutex
68+
currentSessionID uuid.UUID
69+
6770
connectionsQuota *addrquota.Quota
6871
loginsQuota *addrquota.Quota
6972

@@ -752,13 +755,45 @@ retry:
752755
// unregisters a connected player
753756
func (p *Proxy) unregisterConnection(player *connectedPlayer) (found bool) {
754757
p.muP.Lock()
755-
defer p.muP.Unlock()
756758
_, found = p.playerIDs[player.ID()]
757759
delete(p.playerNames, strings.ToLower(player.Username()))
758760
delete(p.playerIDs, player.ID())
761+
empty := len(p.playerIDs) == 0
762+
p.muP.Unlock()
763+
if empty {
764+
p.resetSessionIDIfEmpty()
765+
}
759766
return found
760767
}
761768

769+
func (p *Proxy) sessionID() uuid.UUID {
770+
p.sessionIDMu.Lock()
771+
defer p.sessionIDMu.Unlock()
772+
if p.currentSessionID == uuid.Nil {
773+
p.currentSessionID = uuid.New()
774+
}
775+
return p.currentSessionID
776+
}
777+
778+
func (p *Proxy) resetSessionIDIfEmpty() {
779+
p.muP.RLock()
780+
empty := len(p.playerIDs) == 0
781+
p.muP.RUnlock()
782+
if !empty {
783+
return
784+
}
785+
786+
p.sessionIDMu.Lock()
787+
defer p.sessionIDMu.Unlock()
788+
789+
p.muP.RLock()
790+
empty = len(p.playerIDs) == 0
791+
p.muP.RUnlock()
792+
if empty {
793+
p.currentSessionID = uuid.Nil
794+
}
795+
}
796+
762797
//
763798
//
764799
//

pkg/edition/java/proxy/session_backend_transition.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ func (b *backendTransitionSessionHandler) handleJoinGame(pc *proto.PacketContext
196196
b.serverConn.player.mu.Unlock()
197197
}
198198

199+
p.OnlineMode = b.serverConn.player.OnlineMode()
200+
199201
// The goods are in hand! We got JoinGame.
200202
// Let's transition completely to the new state.
201203
smc.SetAutoReading(false)

pkg/edition/java/proxy/session_client_auth.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ func (a *authSessionHandler) completeLoginProtocolPhaseAndInitialize(player *con
193193
Username: player.Username(),
194194
Properties: player.GameProfile().Properties,
195195
}
196+
if a.inbound.Protocol().GreaterEqual(version.Minecraft_26_2) {
197+
loginSuccess.SessionID = a.proxy.sessionID()
198+
}
196199

197200
// For Modern Forge clients on pre-1.20.2, delay sending LoginSuccess so the
198201
// client remains in the LOGIN state during the backend FML handshake relay.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package proxy
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
"go.minekube.com/gate/pkg/util/uuid"
8+
)
9+
10+
func TestProxySessionIDSharedUntilProxyEmpties(t *testing.T) {
11+
p := &Proxy{
12+
playerIDs: map[uuid.UUID]*connectedPlayer{},
13+
}
14+
15+
first := p.sessionID()
16+
require.Equal(t, first, p.sessionID())
17+
18+
playerID := uuid.New()
19+
p.playerIDs[playerID] = nil
20+
p.resetSessionIDIfEmpty()
21+
require.Equal(t, first, p.sessionID())
22+
23+
delete(p.playerIDs, playerID)
24+
p.resetSessionIDIfEmpty()
25+
require.NotEqual(t, first, p.sessionID())
26+
}

0 commit comments

Comments
 (0)