Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions pkg/edition/java/proxy/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,69 @@ func (e *PostLoginEvent) Player() Player {
return e.player
}

//
//
//
//
//
//
//
//

// PlayerConfigurationStartEvent is fired when the backend starts the configuration stage
//
// This is the earliest proxy-side hook to send config-state packets (e.g. CodeOfConductPacket)
// before configuration finishes.
type PlayerConfigurationStartEvent struct {
player Player
server RegisteredServer
codeOfConduct []byte
}

// Player returns the player entering configuration stage.
func (e *PlayerConfigurationStartEvent) Player() Player {
return e.player
}

// Server returns the backend server the player is currently configuring against.
func (e *PlayerConfigurationStartEvent) Server() RegisteredServer {
return e.server
}

// SendCodeOfConduct asks the proxy to send CodeOfConductPacket to the client
// and hold configuration completion until the player accepts.
func (e *PlayerConfigurationStartEvent) SendCodeOfConduct(payload []byte) {
e.codeOfConduct = payload
}

// ShouldSendCodeOfConduct reports whether sending code-of-conduct packet was requested.
func (e *PlayerConfigurationStartEvent) ShouldSendCodeOfConduct() bool {
return e.codeOfConduct != nil
}

// CodeOfConductPayload returns the payload previously provided via SendCodeOfConduct.
func (e *PlayerConfigurationStartEvent) CodeOfConductPayload() []byte {
return e.codeOfConduct
}

//
//
//
//
//
//

// CodeOfConductAcceptEvent is fired when a player accepts the code of conduct
// in config state (Minecraft 1.21.9+).
type CodeOfConductAcceptEvent struct {
player Player
}

// Player returns the player that accepted the code of conduct.
func (e *CodeOfConductAcceptEvent) Player() Player {
return e.player
}

//
//
//
Expand Down
76 changes: 65 additions & 11 deletions pkg/edition/java/proxy/session_backend_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type backendConfigSessionHandler struct {
serverConn *serverConnection
requestCtx *connRequestCxt
state backendConfigSessionState
codeOfConductHold bool
resourcePackToApply *ResourcePackInfo
log logr.Logger

Expand Down Expand Up @@ -66,11 +67,22 @@ func (b *backendConfigSessionHandler) HandlePacket(pc *proto.PacketContext) {
if !b.shouldHandle() {
return
}
if b.codeOfConductHold {
switch p := pc.Packet.(type) {
case *packet.KeepAlive:
b.handleKeepAlive(p)
case *packet.Disconnect:
b.handleDisconnect(p)
}
return
}
switch p := pc.Packet.(type) {
case *packet.KeepAlive:
b.handleKeepAlive(p)
case *config.StartUpdate:
b.forwardToServer(pc, nil)
case *config.CodeOfConductPacket:
b.handleCodeOfConductPacket(pc)
case *packet.ResourcePackRequest:
b.handleResourcePackRequest(p)
case *packet.RemoveResourcePack:
Expand All @@ -84,17 +96,7 @@ func (b *backendConfigSessionHandler) HandlePacket(pc *proto.PacketContext) {
case *plugin.Message:
b.handlePluginMessage(pc, p)
case *packet.Disconnect:
b.serverConn.disconnect()
// If the player receives a DisconnectPacket without a connection to a server in progress,
// it means that the backend server has kicked the player during reconfiguration
if b.serverConn.player.connectionInFlight() != nil {
result := disconnectResultForPacket(b.log.V(1), p,
b.serverConn.player.Protocol(), b.serverConn.server, true,
)
b.requestCtx.result(result, nil)
} else {
b.serverConn.player.handleDisconnect(b.serverConn.server, p, true)
}
b.handleDisconnect(p)
case *packet.Transfer:
b.handleTransfer(p)
case *cookie.CookieStore:
Expand Down Expand Up @@ -123,6 +125,16 @@ func (b *backendConfigSessionHandler) Activated() {
b.resourcePackToApply = player.resourcePackHandler.FirstAppliedPack()
player.resourcePackHandler.ClearAppliedResourcePacks()
}

// Emit Configuration event and initiate CoC
e := &PlayerConfigurationStartEvent{
player: b.serverConn.player,
server: b.serverConn.server,
}
b.proxy().event.Fire(e)
if player.Protocol().Greater(version.Minecraft_1_21_9) && e.ShouldSendCodeOfConduct() {
b.initiateCodeOfConduct(e.CodeOfConductPayload())
}
}

// Disconnected is called when the session handler is disconnected.
Expand Down Expand Up @@ -211,6 +223,48 @@ func (b *backendConfigSessionHandler) handleFinishedUpdate(p *config.FinishedUpd
})
}

func (b *backendConfigSessionHandler) handleDisconnect(p *packet.Disconnect) {
b.serverConn.disconnect()
// If the player receives a DisconnectPacket without a connection to a server in progress,
// it means that the backend server has kicked the player during reconfiguration
if b.serverConn.player.connectionInFlight() != nil {
result := disconnectResultForPacket(b.log.V(1), p,
b.serverConn.player.Protocol(), b.serverConn.server, true,
)
b.requestCtx.result(result, nil)
} else {
b.serverConn.player.handleDisconnect(b.serverConn.server, p, true)
}
}

func (b *backendConfigSessionHandler) handleCodeOfConductPacket(pc *proto.PacketContext) {
b.requireCodeOfConduct()
b.forwardToPlayer(pc, nil)
}

func (b *backendConfigSessionHandler) initiateCodeOfConduct(payload []byte) {
b.requireCodeOfConduct()
if err := b.serverConn.player.WritePacket(&config.CodeOfConductPacket{Data: payload}); err != nil {
b.log.V(1).Error(err, "error writing CodeOfConduct packet")
}
}

func (b *backendConfigSessionHandler) requireCodeOfConduct() {
if b.codeOfConductHold {
return
}
b.codeOfConductHold = true
b.serverConn.connection.SetAutoReading(false)
}

func (b *backendConfigSessionHandler) releaseCodeOfConductHold() {
if !b.codeOfConductHold {
return
}
b.codeOfConductHold = false
b.serverConn.connection.SetAutoReading(true)
}

func (b *backendConfigSessionHandler) handleTransfer(p *packet.Transfer) {
handleTransfer(p, b.serverConn.player, b.log, b.proxy().Event())
}
Expand Down
13 changes: 13 additions & 0 deletions pkg/edition/java/proxy/session_client_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ func (h *clientConfigSessionHandler) HandlePacket(pc *proto.PacketContext) {
forwardKeepAlive(p, h.player)
case *packet.ClientSettings:
h.player.setClientSettings(p)
case *config.CodeOfConductAcceptPacket:
h.markCodeOfConductAccepted()
forwardToServer(pc, h.player)
case *packet.ResourcePackResponse:
if !handleResourcePackResponse(p, h.player.resourcePackHandler, h.log) {
forwardToServer(pc, h.player)
Expand Down Expand Up @@ -104,6 +107,16 @@ func (h *clientConfigSessionHandler) handleBackendFinishUpdate(serverConn *serve
return &h.configSwitchDone
}

func (h *clientConfigSessionHandler) markCodeOfConductAccepted() {
if serverConn := h.player.connectionInFlightOrConnectedServer(); serverConn != nil {
if smc, ok := serverConn.ensureConnected(); ok {
if backendConfig, ok := smc.ActiveSessionHandler().(*backendConfigSessionHandler); ok {
backendConfig.releaseCodeOfConductHold()
}
}
}
}

func handleResourcePackResponse(p *packet.ResourcePackResponse, handler resourcepack.Handler, log logr.Logger) bool {
handled, err := handler.OnResourcePackResponse(
resourcepack.BundleForResponse(p))
Expand Down
Loading