- Refactor dirt and stone generation to optimize visible depth and adjust randomization.
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 8m32s
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 8m32s
- Remove unused `StartWebSocketGateway` function from `websocket_gateway.go`. - Add security checks to track player-room mapping, enforce valid input, and prevent ID spoofing in `gateway.go`. - Refactor touch control logic to dynamically position joystick and buttons above gameplay floor. - Introduce dynamic floor Y-coordinate calculation (`GetFloorYFromHeight`) for better scaling across different screen sizes. - Adjust rendering logic to align assets, particles, and debug visuals with dynamic screen height transformations. - Update canvas CSS to support fullscreen scaling without center alignment.
This commit is contained in:
@@ -22,13 +22,18 @@ type Gateway struct {
|
||||
// In einer echten Microservice Welt wäre das separat,
|
||||
// aber hier hostet der Gateway auch Räume.
|
||||
LocalRooms map[string]*Room
|
||||
|
||||
// Security: Tracking welcher Spieler in welchem Raum ist
|
||||
// PlayerID -> RoomID Mapping
|
||||
PlayerRooms map[string]string
|
||||
}
|
||||
|
||||
func NewGateway(nc *nats.Conn, w *game.World) *Gateway {
|
||||
return &Gateway{
|
||||
NC: nc,
|
||||
World: w,
|
||||
LocalRooms: make(map[string]*Room),
|
||||
NC: nc,
|
||||
World: w,
|
||||
LocalRooms: make(map[string]*Room),
|
||||
PlayerRooms: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +61,13 @@ func (gw *Gateway) HandleWS(w http.ResponseWriter, r *http.Request) {
|
||||
playerID := fmt.Sprintf("p_%d", time.Now().UnixNano())
|
||||
roomID := login.RoomID
|
||||
|
||||
// 🔒 SECURITY: Prüfe ob dieser Spieler bereits in einem Raum ist
|
||||
if existingRoom, exists := gw.PlayerRooms[playerID]; exists {
|
||||
log.Printf("🚫 SECURITY: Player %s already in room %s, rejecting new connection", playerID, existingRoom)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(`{"error":"Already connected to a room"}`))
|
||||
return
|
||||
}
|
||||
|
||||
// 2. RAUM LOGIK
|
||||
if login.Action == "CREATE" {
|
||||
// Raum ID generieren (4 Zeichen Random)
|
||||
@@ -69,6 +81,9 @@ func (gw *Gateway) HandleWS(w http.ResponseWriter, r *http.Request) {
|
||||
// Spieler lokal hinzufügen (Hack für Demo, sauberer wäre via NATS Event)
|
||||
newRoom.AddPlayer(playerID, login.Name)
|
||||
|
||||
// 🔒 SECURITY: Spieler in Raum registrieren
|
||||
gw.PlayerRooms[playerID] = roomID
|
||||
|
||||
} else if login.Action == "JOIN" {
|
||||
// Wir müssen dem Raum (egal wo er läuft) sagen: Hier ist ein Neuer!
|
||||
// Da wir hier keine verteilte DB haben, tricksen wir:
|
||||
@@ -78,12 +93,15 @@ func (gw *Gateway) HandleWS(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if room, ok := gw.LocalRooms[roomID]; ok {
|
||||
room.AddPlayer(playerID, login.Name)
|
||||
|
||||
// 🔒 SECURITY: Spieler in Raum registrieren
|
||||
gw.PlayerRooms[playerID] = roomID
|
||||
} else {
|
||||
// Falls Raum nicht lokal: Senden wir ein "JOIN REQUEST" über NATS?
|
||||
// Für jetzt: Wir lassen es simpel. Wenn Raum nicht auf diesem Server -> Error.
|
||||
// (Für echtes Scaling bräuchten wir Redis oder NATS Request/Reply zur Raumsuche)
|
||||
log.Println("Raum nicht gefunden (oder auf anderem Node):", roomID)
|
||||
// Optional: Error an Client senden
|
||||
log.Println("❌ Raum nicht gefunden (oder auf anderem Node):", roomID)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(`{"error":"Room not found"}`))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -107,14 +125,37 @@ func (gw *Gateway) HandleWS(w http.ResponseWriter, r *http.Request) {
|
||||
// Wir parsen kurz, um den Typ zu prüfen, oder leiten blind weiter?
|
||||
// Besser: Wir wrappen es in ClientInput struct
|
||||
var raw map[string]interface{}
|
||||
json.Unmarshal(data, &raw)
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
log.Printf("⚠️ Invalid JSON from player %s: %v", playerID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
inputType, _ := raw["type"].(string)
|
||||
|
||||
// 🔒 SECURITY: Prüfe ob der Spieler versucht für jemand anderen zu sprechen
|
||||
claimedPlayerID, hasPlayerID := raw["player_id"].(string)
|
||||
claimedRoomID, hasRoomID := raw["room_id"].(string)
|
||||
|
||||
if hasPlayerID && claimedPlayerID != playerID {
|
||||
log.Printf("🚫 SECURITY BREACH: Player %s tried to send input as %s", playerID, claimedPlayerID)
|
||||
continue // Ignoriere böswilligen Input
|
||||
}
|
||||
|
||||
if hasRoomID && claimedRoomID != roomID {
|
||||
log.Printf("🚫 SECURITY BREACH: Player %s tried to send input to room %s (is in %s)", playerID, claimedRoomID, roomID)
|
||||
continue // Ignoriere böswilligen Input
|
||||
}
|
||||
|
||||
// 🔒 SECURITY: Setze IMMER die korrekten IDs (überschreibe Client-Werte)
|
||||
input := game.ClientInput{
|
||||
Type: inputType,
|
||||
RoomID: roomID,
|
||||
PlayerID: playerID,
|
||||
RoomID: roomID, // Server setzt den Raum (nicht Client!)
|
||||
PlayerID: playerID, // Server setzt die Player-ID (nicht Client!)
|
||||
}
|
||||
|
||||
// Sequence-Nummer vom Client übernehmen (für Client Prediction)
|
||||
if seq, ok := raw["sequence"].(float64); ok {
|
||||
input.Sequence = uint32(seq)
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(input)
|
||||
@@ -122,6 +163,11 @@ func (gw *Gateway) HandleWS(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Cleanup beim Disconnect
|
||||
log.Printf("✋ Player %s (%s) disconnected from Room %s", playerID, login.Name, roomID)
|
||||
|
||||
// 🔒 SECURITY: Entferne Spieler aus PlayerRooms Tracking
|
||||
delete(gw.PlayerRooms, playerID)
|
||||
|
||||
if room, ok := gw.LocalRooms[roomID]; ok {
|
||||
room.RemovePlayer(playerID)
|
||||
// Wenn leer -> Raum löschen?
|
||||
|
||||
Reference in New Issue
Block a user