Add PlayerCode for enhanced score tracking and host validation logic in cooperative mode. Optimize UI for mobile devices with new responsive styles.
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 2m27s
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 2m27s
This commit is contained in:
@@ -166,22 +166,25 @@ func (g *Game) sendJoinRequest() {
|
|||||||
GameMode: g.gameMode,
|
GameMode: g.gameMode,
|
||||||
IsHost: g.isHost,
|
IsHost: g.isHost,
|
||||||
TeamName: g.teamName,
|
TeamName: g.teamName,
|
||||||
|
PlayerCode: g.playerCode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
g.sendWebSocketMessage(msg)
|
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
|
// sendStartRequest sendet Start-Request über WebSocket
|
||||||
func (g *Game) sendStartRequest() {
|
func (g *Game) sendStartRequest() {
|
||||||
|
myID := g.getMyPlayerID()
|
||||||
msg := WebSocketMessage{
|
msg := WebSocketMessage{
|
||||||
Type: "start",
|
Type: "start",
|
||||||
Payload: game.StartRequest{
|
Payload: game.StartRequest{
|
||||||
RoomID: g.roomID,
|
RoomID: g.roomID,
|
||||||
|
PlayerID: myID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
g.sendWebSocketMessage(msg)
|
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
|
// publishInput sendet Input über WebSocket
|
||||||
@@ -255,21 +258,30 @@ func (g *Game) submitScore() {
|
|||||||
// Verwende Team-Name für Coop-Mode, sonst Player-Name
|
// Verwende Team-Name für Coop-Mode, sonst Player-Name
|
||||||
displayName := name
|
displayName := name
|
||||||
teamName := ""
|
teamName := ""
|
||||||
|
playerCodeToUse := g.playerCode
|
||||||
|
|
||||||
if g.gameMode == "coop" {
|
if g.gameMode == "coop" {
|
||||||
g.stateMutex.Lock()
|
g.stateMutex.Lock()
|
||||||
teamName = g.gameState.TeamName
|
teamName = g.gameState.TeamName
|
||||||
|
hostPlayerCode := g.gameState.HostPlayerCode
|
||||||
g.stateMutex.Unlock()
|
g.stateMutex.Unlock()
|
||||||
|
|
||||||
if teamName != "" {
|
if teamName != "" {
|
||||||
displayName = 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{
|
msg := WebSocketMessage{
|
||||||
Type: "score_submit",
|
Type: "score_submit",
|
||||||
Payload: game.ScoreSubmission{
|
Payload: game.ScoreSubmission{
|
||||||
PlayerName: displayName,
|
PlayerName: displayName,
|
||||||
PlayerCode: g.playerCode,
|
PlayerCode: playerCodeToUse,
|
||||||
Name: displayName, // Für Kompatibilität
|
Name: displayName, // Für Kompatibilität
|
||||||
Score: score,
|
Score: score,
|
||||||
Mode: g.gameMode,
|
Mode: g.gameMode,
|
||||||
@@ -279,5 +291,5 @@ func (g *Game) submitScore() {
|
|||||||
g.sendWebSocketMessage(msg)
|
g.sendWebSocketMessage(msg)
|
||||||
|
|
||||||
g.scoreSubmitted = true
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,11 +157,12 @@ func (g *Game) connectToServer() {
|
|||||||
GameMode: g.gameMode,
|
GameMode: g.gameMode,
|
||||||
IsHost: g.isHost,
|
IsHost: g.isHost,
|
||||||
TeamName: g.teamName,
|
TeamName: g.teamName,
|
||||||
|
PlayerCode: g.playerCode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
g.sendWebSocketMessage(joinMsg)
|
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
|
// sendWebSocketMessage sendet eine Nachricht über WebSocket
|
||||||
@@ -191,14 +192,16 @@ func (g *Game) sendInput(input game.ClientInput) {
|
|||||||
|
|
||||||
// startGame sendet den Start-Befehl über WebSocket
|
// startGame sendet den Start-Befehl über WebSocket
|
||||||
func (g *Game) startGame() {
|
func (g *Game) startGame() {
|
||||||
|
myID := g.getMyPlayerID()
|
||||||
msg := WebSocketMessage{
|
msg := WebSocketMessage{
|
||||||
Type: "start",
|
Type: "start",
|
||||||
Payload: game.StartRequest{
|
Payload: game.StartRequest{
|
||||||
RoomID: g.roomID,
|
RoomID: g.roomID,
|
||||||
|
PlayerID: myID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
g.sendWebSocketMessage(msg)
|
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
|
// connectForLeaderboard verbindet für Leaderboard-Abfrage
|
||||||
@@ -281,21 +284,30 @@ func (g *Game) submitScore() {
|
|||||||
// Verwende Team-Name für Coop-Mode, sonst Player-Name
|
// Verwende Team-Name für Coop-Mode, sonst Player-Name
|
||||||
displayName := name
|
displayName := name
|
||||||
teamName := ""
|
teamName := ""
|
||||||
|
playerCodeToUse := g.playerCode
|
||||||
|
|
||||||
if g.gameMode == "coop" {
|
if g.gameMode == "coop" {
|
||||||
g.stateMutex.Lock()
|
g.stateMutex.Lock()
|
||||||
teamName = g.gameState.TeamName
|
teamName = g.gameState.TeamName
|
||||||
|
hostPlayerCode := g.gameState.HostPlayerCode
|
||||||
g.stateMutex.Unlock()
|
g.stateMutex.Unlock()
|
||||||
|
|
||||||
if teamName != "" {
|
if teamName != "" {
|
||||||
displayName = 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{
|
msg := WebSocketMessage{
|
||||||
Type: "score_submit",
|
Type: "score_submit",
|
||||||
Payload: game.ScoreSubmission{
|
Payload: game.ScoreSubmission{
|
||||||
PlayerName: displayName,
|
PlayerName: displayName,
|
||||||
PlayerCode: g.playerCode,
|
PlayerCode: playerCodeToUse,
|
||||||
Name: displayName, // Für Kompatibilität
|
Name: displayName, // Für Kompatibilität
|
||||||
Score: score,
|
Score: score,
|
||||||
Mode: g.gameMode,
|
Mode: g.gameMode,
|
||||||
@@ -305,7 +317,7 @@ func (g *Game) submitScore() {
|
|||||||
g.sendWebSocketMessage(msg)
|
g.sendWebSocketMessage(msg)
|
||||||
|
|
||||||
g.scoreSubmitted = true
|
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
|
// Dummy-Funktionen für Kompatibilität mit anderen Teilen des Codes
|
||||||
|
|||||||
@@ -44,5 +44,33 @@ input[type=range]{width:100%;max-width:300px}
|
|||||||
#mute-btn:hover{background:rgba(255,255,255,.2);border-color:#fff}
|
#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}
|
#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}
|
.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 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}}
|
@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}}
|
||||||
|
|||||||
@@ -148,6 +148,14 @@ func main() {
|
|||||||
// Spieler hinzufügen (ID, Name)
|
// Spieler hinzufügen (ID, Name)
|
||||||
room.AddPlayer(playerID, req.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
|
// Session speichern
|
||||||
playerSessions[playerID] = room
|
playerSessions[playerID] = room
|
||||||
log.Printf("➡️ Spieler '%s' ist Raum '%s' beigetreten.", playerID, roomID)
|
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)
|
// 4. HANDLER: GAME START (broadcast - alle Pods empfangen, nur der mit dem Raum reagiert)
|
||||||
_, _ = ec.Subscribe("game.start", func(req *game.StartRequest) {
|
_, _ = 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()
|
mu.RLock()
|
||||||
room, exists := rooms[req.RoomID]
|
room, exists := rooms[req.RoomID]
|
||||||
@@ -168,9 +176,17 @@ func main() {
|
|||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
room.Mutex.Lock()
|
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.StartCountdown()
|
||||||
room.Mutex.Unlock()
|
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 {
|
} else {
|
||||||
log.Printf("❌ Raum '%s' nicht gefunden", req.RoomID)
|
log.Printf("❌ Raum '%s' nicht gefunden", req.RoomID)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ type JoinRequest struct {
|
|||||||
GameMode string `json:"game_mode"` // "solo" oder "coop"
|
GameMode string `json:"game_mode"` // "solo" oder "coop"
|
||||||
IsHost bool `json:"is_host"`
|
IsHost bool `json:"is_host"`
|
||||||
TeamName string `json:"team_name"`
|
TeamName string `json:"team_name"`
|
||||||
|
PlayerCode string `json:"player_code"` // PlayerCode für Score-Tracking
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlayerState struct {
|
type PlayerState struct {
|
||||||
@@ -106,6 +107,7 @@ type GameState struct {
|
|||||||
TimeLeft int `json:"time_left"`
|
TimeLeft int `json:"time_left"`
|
||||||
WorldChunks []ActiveChunk `json:"world_chunks"`
|
WorldChunks []ActiveChunk `json:"world_chunks"`
|
||||||
HostID string `json:"host_id"`
|
HostID string `json:"host_id"`
|
||||||
|
HostPlayerCode string `json:"host_player_code"` // PlayerCode des Hosts (für Coop-Score)
|
||||||
TeamName string `json:"team_name"` // Team-Name (vom Host gesetzt)
|
TeamName string `json:"team_name"` // Team-Name (vom Host gesetzt)
|
||||||
ScrollX float64 `json:"scroll_x"`
|
ScrollX float64 `json:"scroll_x"`
|
||||||
CollectedCoins map[string]bool `json:"collected_coins"` // Welche Coins wurden eingesammelt (Key: ChunkID_ObjectIndex)
|
CollectedCoins map[string]bool `json:"collected_coins"` // Welche Coins wurden eingesammelt (Key: ChunkID_ObjectIndex)
|
||||||
@@ -151,6 +153,7 @@ type ScoreSubmissionResponse struct {
|
|||||||
// Start-Request vom Client
|
// Start-Request vom Client
|
||||||
type StartRequest struct {
|
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
|
// Leaderboard-Request vom Client
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ type Room struct {
|
|||||||
Countdown int
|
Countdown int
|
||||||
NextStart time.Time
|
NextStart time.Time
|
||||||
HostID string
|
HostID string
|
||||||
|
HostPlayerCode string // PlayerCode des Hosts (für Coop-Score Submission)
|
||||||
TeamName string // Name des Teams (vom Host gesetzt)
|
TeamName string // Name des Teams (vom Host gesetzt)
|
||||||
CollectedCoins map[string]bool // Key: "chunkID_objectIndex"
|
CollectedCoins map[string]bool // Key: "chunkID_objectIndex"
|
||||||
CollectedPowerups map[string]bool // Key: "chunkID_objectIndex"
|
CollectedPowerups map[string]bool // Key: "chunkID_objectIndex"
|
||||||
@@ -790,6 +791,7 @@ func (r *Room) Broadcast() {
|
|||||||
TimeLeft: r.Countdown,
|
TimeLeft: r.Countdown,
|
||||||
WorldChunks: r.ActiveChunks,
|
WorldChunks: r.ActiveChunks,
|
||||||
HostID: r.HostID,
|
HostID: r.HostID,
|
||||||
|
HostPlayerCode: r.HostPlayerCode,
|
||||||
TeamName: r.TeamName,
|
TeamName: r.TeamName,
|
||||||
ScrollX: r.GlobalScrollX,
|
ScrollX: r.GlobalScrollX,
|
||||||
CollectedCoins: r.CollectedCoins,
|
CollectedCoins: r.CollectedCoins,
|
||||||
|
|||||||
Reference in New Issue
Block a user