fix README, SYNC, DATENSCHUTZ
Some checks failed
Dynamic Branch Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Dynamic Branch Deploy / build-and-deploy (push) Has been cancelled
This commit is contained in:
56
websocket.go
56
websocket.go
@@ -12,8 +12,8 @@ import (
|
||||
const (
|
||||
ServerTickRate = 50 * time.Millisecond
|
||||
|
||||
BufferAhead = 60 // Puffergröße (Zukunft)
|
||||
SpawnXStart = 2000.0 // Spawn Abstand
|
||||
BufferAhead = 60
|
||||
SpawnXStart = 2000.0
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
@@ -24,7 +24,7 @@ var upgrader = websocket.Upgrader{
|
||||
type WSInputMsg struct {
|
||||
Type string `json:"type"`
|
||||
Input string `json:"input"`
|
||||
Tick int `json:"tick"` // Optional: Client Timestamp für Ping
|
||||
Tick int `json:"tick"`
|
||||
PosY float64 `json:"y"`
|
||||
VelY float64 `json:"vy"`
|
||||
}
|
||||
@@ -37,7 +37,7 @@ type WSServerMsg struct {
|
||||
Score int `json:"score"`
|
||||
PowerUps PowerUpState `json:"powerups"`
|
||||
SessionID string `json:"sessionId"`
|
||||
Ts int `json:"ts,omitempty"` // Für Pong
|
||||
Ts int `json:"ts,omitempty"`
|
||||
CurrentSpeed float64 `json:"currentSpeed"`
|
||||
}
|
||||
|
||||
@@ -107,15 +107,7 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
case <-closeChan:
|
||||
return // Client weg
|
||||
|
||||
// WICHTIG: Wir verarbeiten Inputs hier NICHT einzeln,
|
||||
// sondern sammeln sie im Default-Case (siehe unten),
|
||||
// oder nutzen eine nicht-blockierende Schleife.
|
||||
// Aber für einfache Logik reicht select.
|
||||
// Um "Input Lag" zu verhindern, lesen wir den Channel leer:
|
||||
|
||||
case <-ticker.C:
|
||||
// A. INPUTS VERARBEITEN (Alle die angekommen sind!)
|
||||
// Wir loopen solange durch den Channel, bis er leer ist.
|
||||
InputLoop:
|
||||
for {
|
||||
select {
|
||||
@@ -136,7 +128,6 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
if msg.Type == "ping" {
|
||||
// Sofort Pong zurück (Performance wichtig!)
|
||||
conn.WriteJSON(WSServerMsg{Type: "pong", Ts: msg.Tick})
|
||||
}
|
||||
|
||||
@@ -163,25 +154,21 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("🐞 Debug Snapshot an Client gesendet (Tick %d)", state.Ticks)
|
||||
}
|
||||
default:
|
||||
// Channel leer, weiter zur Physik
|
||||
break InputLoop
|
||||
}
|
||||
}
|
||||
|
||||
// B. LIVE SIMULATION (1 Tick)
|
||||
// Jetzt simulieren wir genau EINEN Frame (16ms)
|
||||
state.Ticks++
|
||||
state.Score++ // Score wächst mit der Zeit
|
||||
state.Score++
|
||||
|
||||
currentSpeed := calculateSpeed(state.Ticks)
|
||||
|
||||
updatePhysics(&state, pendingJump, isCrouching, currentSpeed)
|
||||
pendingJump = false // Jump Trigger reset
|
||||
pendingJump = false
|
||||
|
||||
checkCollisions(&state, isCrouching, currentSpeed)
|
||||
|
||||
if state.IsDead {
|
||||
// Score Persistieren
|
||||
rdb.HSet(ctx, "session:"+sessionID, map[string]interface{}{
|
||||
"score": state.Score,
|
||||
"is_dead": 1,
|
||||
@@ -194,13 +181,10 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
moveWorld(&state, currentSpeed)
|
||||
|
||||
// C. STREAMING (Zukunft)
|
||||
// Wir generieren nur, wenn der Puffer zur Neige geht
|
||||
targetTick := state.Ticks + BufferAhead
|
||||
var newObs []ActiveObstacle
|
||||
var newPlats []ActivePlatform
|
||||
|
||||
// Um CPU zu sparen, generieren wir max 10 Ticks pro Frame nach
|
||||
loops := 0
|
||||
for generatedHeadTick < targetTick && loops < 10 {
|
||||
generatedHeadTick++
|
||||
@@ -216,8 +200,6 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// D. SENDEN (Effizienz)
|
||||
// Nur senden wenn Daten da sind ODER alle 15 Frames (Heartbeat/Score Sync)
|
||||
if len(newObs) > 0 || len(newPlats) > 0 || state.Ticks%15 == 0 {
|
||||
msg := WSServerMsg{
|
||||
Type: "chunk",
|
||||
@@ -235,7 +217,6 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Generiert Objekte für EINEN Tick in der Zukunft
|
||||
func generateFutureObjects(s *SimState, tick int, speed float64) ([]ActiveObstacle, []ActivePlatform) {
|
||||
var createdObs []ActiveObstacle
|
||||
var createdPlats []ActivePlatform
|
||||
@@ -249,19 +230,12 @@ func generateFutureObjects(s *SimState, tick int, speed float64) ([]ActiveObstac
|
||||
if tick >= s.NextSpawnTick {
|
||||
spawnX := SpawnXStart
|
||||
|
||||
// --- ENTSCHEIDUNG: CHUNK vs RANDOM ---
|
||||
|
||||
// Wir nutzen die globalen Chunks (da Read-Only während des Spiels, ist Zugriff sicher)
|
||||
chunkCount := len(defaultConfig.Chunks)
|
||||
|
||||
if chunkCount > 0 && s.RNG.NextFloat() > 0.8 {
|
||||
// =================================================
|
||||
// OPTION A: CHUNK SPAWNING
|
||||
// =================================================
|
||||
idx := int(s.RNG.NextRange(0, float64(chunkCount)))
|
||||
chunk := defaultConfig.Chunks[idx]
|
||||
|
||||
// 1. Plattformen übernehmen
|
||||
for _, p := range chunk.Platforms {
|
||||
createdPlats = append(createdPlats, ActivePlatform{
|
||||
X: spawnX + p.X,
|
||||
@@ -271,21 +245,18 @@ func generateFutureObjects(s *SimState, tick int, speed float64) ([]ActiveObstac
|
||||
})
|
||||
}
|
||||
|
||||
// 2. Hindernisse übernehmen & Speech berechnen
|
||||
for _, o := range chunk.Obstacles {
|
||||
|
||||
// Speech-Logik: Wir müssen die Original-Def finden, um zu wissen, ob er sprechen kann
|
||||
speech := ""
|
||||
for _, def := range defaultConfig.Obstacles {
|
||||
if def.ID == o.ID {
|
||||
// Wenn gefunden, würfeln wir
|
||||
if def.CanTalk && len(def.SpeechLines) > 0 {
|
||||
if s.RNG.NextFloat() > 0.7 { // 30% Wahrscheinlichkeit
|
||||
sIdx := int(s.RNG.NextRange(0, float64(len(def.SpeechLines))))
|
||||
speech = def.SpeechLines[sIdx]
|
||||
}
|
||||
}
|
||||
break // Def gefunden, Loop abbrechen
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,11 +267,10 @@ func generateFutureObjects(s *SimState, tick int, speed float64) ([]ActiveObstac
|
||||
Y: o.Y,
|
||||
Width: o.Width,
|
||||
Height: o.Height,
|
||||
Speech: speech, // <--- HIER wird der Text gesetzt
|
||||
Speech: speech,
|
||||
})
|
||||
}
|
||||
|
||||
// Timer setzen (Länge des Chunks)
|
||||
width := float64(chunk.TotalWidth)
|
||||
if width == 0 {
|
||||
width = 2000.0
|
||||
@@ -308,15 +278,10 @@ func generateFutureObjects(s *SimState, tick int, speed float64) ([]ActiveObstac
|
||||
s.NextSpawnTick = tick + int(width/speed)
|
||||
|
||||
} else {
|
||||
// =================================================
|
||||
// OPTION B: RANDOM SPAWNING
|
||||
// =================================================
|
||||
|
||||
// Lücke berechnen
|
||||
gap := 400 + int(s.RNG.NextRange(0, 500))
|
||||
s.NextSpawnTick = tick + int(float64(gap)/speed)
|
||||
|
||||
// Pool bilden (Boss Phase?)
|
||||
defs := defaultConfig.Obstacles
|
||||
if len(defs) > 0 {
|
||||
isBoss := (tick % 1500) > 1200
|
||||
@@ -334,11 +299,9 @@ func generateFutureObjects(s *SimState, tick int, speed float64) ([]ActiveObstac
|
||||
}
|
||||
}
|
||||
|
||||
// Objekt auswählen
|
||||
def := s.RNG.PickDef(pool)
|
||||
|
||||
if def != nil {
|
||||
// Powerup Rarity (90% Chance, dass es NICHT spawnt)
|
||||
if def.Type == "powerup" && s.RNG.NextFloat() > 0.1 {
|
||||
def = nil
|
||||
}
|
||||
@@ -353,7 +316,6 @@ func generateFutureObjects(s *SimState, tick int, speed float64) ([]ActiveObstac
|
||||
}
|
||||
}
|
||||
|
||||
// Y-Position berechnen (Boden - Höhe - Offset)
|
||||
spawnY := GroundY - def.Height - def.YOffset
|
||||
|
||||
createdObs = append(createdObs, ActiveObstacle{
|
||||
@@ -363,7 +325,7 @@ func generateFutureObjects(s *SimState, tick int, speed float64) ([]ActiveObstac
|
||||
Y: spawnY,
|
||||
Width: def.Width,
|
||||
Height: def.Height,
|
||||
Speech: speech, // <--- HIER wird der Text gesetzt
|
||||
Speech: speech,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user