From 400a7e752bada15d8165342e9badb281e48ffe12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Untersch=C3=BCtz?= Date: Sun, 4 Jan 2026 17:34:19 +0100 Subject: [PATCH] Add `PlayerCode` for enhanced score tracking and host validation logic in cooperative mode. Optimize UI for mobile devices with new responsive styles. --- cmd/client/connection_native.go | 32 ++++++++++++++++++++++---------- cmd/client/connection_wasm.go | 32 ++++++++++++++++++++++---------- cmd/client/web/style.css | 28 ++++++++++++++++++++++++++++ cmd/server/main.go | 20 ++++++++++++++++++-- pkg/game/data.go | 17 ++++++++++------- pkg/server/room.go | 2 ++ 6 files changed, 102 insertions(+), 29 deletions(-) diff --git a/cmd/client/connection_native.go b/cmd/client/connection_native.go index ea3abd7..9915781 100644 --- a/cmd/client/connection_native.go +++ b/cmd/client/connection_native.go @@ -161,27 +161,30 @@ func (g *Game) sendJoinRequest() { msg := WebSocketMessage{ Type: "join", Payload: game.JoinRequest{ - Name: g.playerName, - RoomID: g.roomID, - GameMode: g.gameMode, - IsHost: g.isHost, - TeamName: g.teamName, + Name: g.playerName, + RoomID: g.roomID, + GameMode: g.gameMode, + IsHost: g.isHost, + TeamName: g.teamName, + PlayerCode: g.playerCode, }, } g.sendWebSocketMessage(msg) - log.Printf("➡️ JOIN gesendet über WebSocket: Name=%s, RoomID=%s", g.playerName, g.roomID) + log.Printf("➡️ JOIN gesendet über WebSocket: Name=%s, RoomID=%s, PlayerCode=%s", g.playerName, g.roomID, g.playerCode) } // sendStartRequest sendet Start-Request über WebSocket func (g *Game) sendStartRequest() { + myID := g.getMyPlayerID() msg := WebSocketMessage{ Type: "start", Payload: game.StartRequest{ - RoomID: g.roomID, + RoomID: g.roomID, + PlayerID: myID, }, } g.sendWebSocketMessage(msg) - log.Printf("▶️ START gesendet über WebSocket: RoomID=%s", g.roomID) + log.Printf("▶️ START gesendet über WebSocket: RoomID=%s, PlayerID=%s", g.roomID, myID) } // publishInput sendet Input über WebSocket @@ -255,21 +258,30 @@ func (g *Game) submitScore() { // Verwende Team-Name für Coop-Mode, sonst Player-Name displayName := name teamName := "" + playerCodeToUse := g.playerCode + if g.gameMode == "coop" { g.stateMutex.Lock() teamName = g.gameState.TeamName + hostPlayerCode := g.gameState.HostPlayerCode g.stateMutex.Unlock() if teamName != "" { displayName = teamName } + + // In Coop: Verwende Host's PlayerCode für Score + if hostPlayerCode != "" { + playerCodeToUse = hostPlayerCode + log.Printf("🔑 Coop-Mode: Verwende Host PlayerCode für Score-Submission") + } } msg := WebSocketMessage{ Type: "score_submit", Payload: game.ScoreSubmission{ PlayerName: displayName, - PlayerCode: g.playerCode, + PlayerCode: playerCodeToUse, Name: displayName, // Für Kompatibilität Score: score, Mode: g.gameMode, @@ -279,5 +291,5 @@ func (g *Game) submitScore() { g.sendWebSocketMessage(msg) g.scoreSubmitted = true - log.Printf("📊 Score submitted: %s = %d (TeamName: %s)", displayName, score, teamName) + log.Printf("📊 Score submitted: %s = %d (PlayerCode: %s, TeamName: %s)", displayName, score, playerCodeToUse, teamName) } diff --git a/cmd/client/connection_wasm.go b/cmd/client/connection_wasm.go index 010ef64..c2aaa76 100644 --- a/cmd/client/connection_wasm.go +++ b/cmd/client/connection_wasm.go @@ -152,16 +152,17 @@ func (g *Game) connectToServer() { joinMsg := WebSocketMessage{ Type: "join", Payload: game.JoinRequest{ - Name: g.playerName, - RoomID: g.roomID, - GameMode: g.gameMode, - IsHost: g.isHost, - TeamName: g.teamName, + Name: g.playerName, + RoomID: g.roomID, + GameMode: g.gameMode, + IsHost: g.isHost, + TeamName: g.teamName, + PlayerCode: g.playerCode, }, } g.sendWebSocketMessage(joinMsg) - log.Printf("➡️ JOIN gesendet über WebSocket: Name=%s, RoomID=%s", g.playerName, g.roomID) + log.Printf("➡️ JOIN gesendet über WebSocket: Name=%s, RoomID=%s, PlayerCode=%s", g.playerName, g.roomID, g.playerCode) } // sendWebSocketMessage sendet eine Nachricht über WebSocket @@ -191,14 +192,16 @@ func (g *Game) sendInput(input game.ClientInput) { // startGame sendet den Start-Befehl über WebSocket func (g *Game) startGame() { + myID := g.getMyPlayerID() msg := WebSocketMessage{ Type: "start", Payload: game.StartRequest{ - RoomID: g.roomID, + RoomID: g.roomID, + PlayerID: myID, }, } g.sendWebSocketMessage(msg) - log.Printf("▶️ START gesendet über WebSocket: RoomID=%s", g.roomID) + log.Printf("▶️ START gesendet über WebSocket: RoomID=%s, PlayerID=%s", g.roomID, myID) } // connectForLeaderboard verbindet für Leaderboard-Abfrage @@ -281,21 +284,30 @@ func (g *Game) submitScore() { // Verwende Team-Name für Coop-Mode, sonst Player-Name displayName := name teamName := "" + playerCodeToUse := g.playerCode + if g.gameMode == "coop" { g.stateMutex.Lock() teamName = g.gameState.TeamName + hostPlayerCode := g.gameState.HostPlayerCode g.stateMutex.Unlock() if teamName != "" { displayName = teamName } + + // In Coop: Verwende Host's PlayerCode für Score + if hostPlayerCode != "" { + playerCodeToUse = hostPlayerCode + log.Printf("🔑 Coop-Mode: Verwende Host PlayerCode für Score-Submission") + } } msg := WebSocketMessage{ Type: "score_submit", Payload: game.ScoreSubmission{ PlayerName: displayName, - PlayerCode: g.playerCode, + PlayerCode: playerCodeToUse, Name: displayName, // Für Kompatibilität Score: score, Mode: g.gameMode, @@ -305,7 +317,7 @@ func (g *Game) submitScore() { g.sendWebSocketMessage(msg) g.scoreSubmitted = true - log.Printf("📊 Score submitted: %s = %d (TeamName: %s)", displayName, score, teamName) + log.Printf("📊 Score submitted: %s = %d (PlayerCode: %s, TeamName: %s)", displayName, score, playerCodeToUse, teamName) } // Dummy-Funktionen für Kompatibilität mit anderen Teilen des Codes diff --git a/cmd/client/web/style.css b/cmd/client/web/style.css index 1daefa6..66422d4 100644 --- a/cmd/client/web/style.css +++ b/cmd/client/web/style.css @@ -44,5 +44,33 @@ input[type=range]{width:100%;max-width:300px} #mute-btn:hover{background:rgba(255,255,255,.2);border-color:#fff} #rotate-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:#222;z-index:99999;color:#fff;flex-direction:column;align-items:center;justify-content:center;text-align:center} .icon{font-size:60px;margin-bottom:20px} +/* Mobile First - Base Styles bereits für Mobile optimiert */ +@media screen and (max-width: 768px) { + h1 { font-size: 20px; margin: 5px 0 15px; } + button { font-size: 12px; padding: 12px 20px; margin: 8px; } + .big-btn { font-size: 14px; padding: 14px 25px; } + input[type=text] { font-size: 12px; padding: 10px; max-width: 280px; } + .info-box { max-width: 280px; padding: 10px; font-size: 10px; } + .info-box p { font-size: 11px; } + .overlay-screen { padding: 10px; } + #startScreen { flex-direction: column; gap: 20px; } + .start-left, .start-right { max-width: 90%; flex: 1; } + .center-box { max-width: 90%; } + .leaderboard-box { max-width: 90%; padding: 10px; } + .hall-of-fame-box { max-height: 200px; max-width: 90%; } + .legal-bar { gap: 8px; } + .legal-btn { font-size: 8px; padding: 6px 10px; } + .back-btn { font-size: 10px; padding: 8px 16px; } +} + +@media screen and (max-width: 480px) { + h1 { font-size: 16px; line-height: 1.2; } + button { font-size: 10px; padding: 10px 16px; margin: 6px; } + .big-btn { font-size: 12px; padding: 12px 20px; } + input[type=text] { font-size: 11px; padding: 8px; max-width: 240px; } + .overlay-screen { padding: 8px; } + body, html { font-size: 11px; } +} + @media screen and (orientation:portrait){#rotate-overlay{display:flex}#game-container{display:none!important}} @media (min-width:1024px){h1{font-size:48px}button{font-size:22px;padding:20px 40px}input[type=text]{max-width:450px;font-size:20px;padding:15px}.info-box{max-width:500px}.hall-of-fame-box{max-height:400px}} diff --git a/cmd/server/main.go b/cmd/server/main.go index f276851..ec2ce21 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -148,6 +148,14 @@ func main() { // Spieler hinzufügen (ID, Name) room.AddPlayer(playerID, req.Name) + // Falls Host, speichere PlayerCode + if req.IsHost && req.PlayerCode != "" { + room.Mutex.Lock() + room.HostPlayerCode = req.PlayerCode + room.Mutex.Unlock() + log.Printf("🔑 Host PlayerCode gesetzt: %s", req.PlayerCode) + } + // Session speichern playerSessions[playerID] = room log.Printf("➡️ Spieler '%s' ist Raum '%s' beigetreten.", playerID, roomID) @@ -160,7 +168,7 @@ func main() { // 4. HANDLER: GAME START (broadcast - alle Pods empfangen, nur der mit dem Raum reagiert) _, _ = ec.Subscribe("game.start", func(req *game.StartRequest) { - log.Printf("▶️ START empfangen: RoomID=%s", req.RoomID) + log.Printf("▶️ START empfangen: RoomID=%s, PlayerID=%s", req.RoomID, req.PlayerID) mu.RLock() room, exists := rooms[req.RoomID] @@ -168,9 +176,17 @@ func main() { if exists { room.Mutex.Lock() + + // Prüfe ob der Spieler der Host ist + if room.HostID != "" && room.HostID != req.PlayerID { + log.Printf("⚠️ Spieler '%s' ist nicht Host von Raum '%s' (Host: %s)", req.PlayerID, req.RoomID, room.HostID) + room.Mutex.Unlock() + return + } + room.StartCountdown() room.Mutex.Unlock() - log.Printf("🎮 Raum '%s' Countdown gestartet", req.RoomID) + log.Printf("🎮 Raum '%s' Countdown gestartet von Host '%s'", req.RoomID, req.PlayerID) } else { log.Printf("❌ Raum '%s' nicht gefunden", req.RoomID) } diff --git a/pkg/game/data.go b/pkg/game/data.go index 9a110e1..4ae5457 100644 --- a/pkg/game/data.go +++ b/pkg/game/data.go @@ -74,11 +74,12 @@ type ClientInput struct { } type JoinRequest struct { - Name string `json:"name"` - RoomID string `json:"room_id"` - GameMode string `json:"game_mode"` // "solo" oder "coop" - IsHost bool `json:"is_host"` - TeamName string `json:"team_name"` + Name string `json:"name"` + RoomID string `json:"room_id"` + GameMode string `json:"game_mode"` // "solo" oder "coop" + IsHost bool `json:"is_host"` + TeamName string `json:"team_name"` + PlayerCode string `json:"player_code"` // PlayerCode für Score-Tracking } type PlayerState struct { @@ -106,7 +107,8 @@ type GameState struct { TimeLeft int `json:"time_left"` WorldChunks []ActiveChunk `json:"world_chunks"` HostID string `json:"host_id"` - TeamName string `json:"team_name"` // Team-Name (vom Host gesetzt) + HostPlayerCode string `json:"host_player_code"` // PlayerCode des Hosts (für Coop-Score) + TeamName string `json:"team_name"` // Team-Name (vom Host gesetzt) ScrollX float64 `json:"scroll_x"` CollectedCoins map[string]bool `json:"collected_coins"` // Welche Coins wurden eingesammelt (Key: ChunkID_ObjectIndex) CollectedPowerups map[string]bool `json:"collected_powerups"` // Welche Powerups wurden eingesammelt @@ -150,7 +152,8 @@ type ScoreSubmissionResponse struct { // Start-Request vom Client type StartRequest struct { - RoomID string `json:"room_id"` + RoomID string `json:"room_id"` + PlayerID string `json:"player_id"` // Wer startet das Spiel } // Leaderboard-Request vom Client diff --git a/pkg/server/room.go b/pkg/server/room.go index 8705e5d..0687b55 100644 --- a/pkg/server/room.go +++ b/pkg/server/room.go @@ -70,6 +70,7 @@ type Room struct { Countdown int NextStart time.Time HostID string + HostPlayerCode string // PlayerCode des Hosts (für Coop-Score Submission) TeamName string // Name des Teams (vom Host gesetzt) CollectedCoins map[string]bool // Key: "chunkID_objectIndex" CollectedPowerups map[string]bool // Key: "chunkID_objectIndex" @@ -790,6 +791,7 @@ func (r *Room) Broadcast() { TimeLeft: r.Countdown, WorldChunks: r.ActiveChunks, HostID: r.HostID, + HostPlayerCode: r.HostPlayerCode, TeamName: r.TeamName, ScrollX: r.GlobalScrollX, CollectedCoins: r.CollectedCoins,