Private
Public Access
1
0

Introduce core components for "Escape From Teacher" game: server, client, physics, asset system, and protocol definitions. Add Docker-Compose setup for Redis and NATS infrastructure.

This commit is contained in:
Sebastian Unterschütz
2026-01-01 15:21:18 +01:00
commit 3099ac42c0
9 changed files with 1384 additions and 0 deletions

6
pkg/game/assets.go Normal file
View File

@@ -0,0 +1,6 @@
package game
// Manifest ist die ganze Konfigurationsdatei
type Manifest struct {
Assets map[string]AssetDefinition `json:"assets"`
}

86
pkg/game/data.go Normal file
View File

@@ -0,0 +1,86 @@
package game
import "image/color"
type Vec2 struct {
X, Y float64 `json:"x,y"`
}
type Rect struct {
OffsetX, OffsetY, W, H float64
Type string
} // Type hinzugefügt aus letztem Schritt
type HexColor struct {
R, G, B, A uint8 `json:"r,g,b,a"`
}
func (h HexColor) ToRGBA() color.RGBA {
if h.A == 0 {
return color.RGBA{h.R, h.G, h.B, 255}
}
return color.RGBA{h.R, h.G, h.B, h.A}
}
// --- ASSETS & CHUNKS (Bleiben gleich) ---
type AssetDefinition struct {
ID, Type, Filename string
Scale, ProcWidth, ProcHeight, DrawOffX, DrawOffY float64
Color HexColor
Hitbox Rect
}
type AssetManifest struct {
Assets map[string]AssetDefinition `json:"assets"`
}
type LevelObject struct {
AssetID string
X, Y float64
}
type Chunk struct {
ID string
Width int
Objects []LevelObject
}
type ActiveChunk struct {
ChunkID string
X float64
}
// --- NETZWERK & GAMEPLAY ---
// Login Request (Client -> Server beim Verbinden)
type LoginPayload struct {
Action string `json:"action"` // "CREATE" oder "JOIN"
RoomID string `json:"room_id"` // Leer bei CREATE, Code bei JOIN
Name string `json:"name"` // Spielername
}
// Input vom Spieler während des Spiels
type ClientInput struct {
Type string `json:"type"` // "JUMP", "START"
RoomID string `json:"room_id"`
PlayerID string `json:"player_id"`
}
type JoinRequest struct {
Name string `json:"name"`
RoomID string `json:"room_id"`
}
type PlayerState struct {
ID string `json:"id"`
Name string `json:"name"`
X float64 `json:"x"`
Y float64 `json:"y"`
State string `json:"state"`
OnGround bool `json:"on_ground"`
}
type GameState struct {
RoomID string `json:"room_id"`
Players map[string]PlayerState `json:"players"`
Status string `json:"status"`
TimeLeft int `json:"time_left"`
WorldChunks []ActiveChunk `json:"world_chunks"`
HostID string `json:"host_id"`
ScrollX float64 `json:"scroll_x"`
}

25
pkg/physics/engine.go Normal file
View File

@@ -0,0 +1,25 @@
package physics
// Entity repräsentiert alles, was sich bewegt (Spieler, Hindernis)
type Entity struct {
X, Y float64
Width, Height float64
VelocityY float64
}
// Config für Konstanten (Schwerkraft etc.)
type Config struct {
Gravity float64
Speed float64
}
// Update simuliert einen Tick (z.B. 1/60 sekunde)
func (e *Entity) Update(cfg Config) {
e.VelocityY += cfg.Gravity
e.Y += e.VelocityY
// Einfache Boden-Kollision (Hardcoded für den Anfang)
if e.Y > 300 {
e.Y = 300
e.VelocityY = 0
}
}

20
pkg/protocol/messages.go Normal file
View File

@@ -0,0 +1,20 @@
package protocol
// Input: Was der Spieler drückt
type InputMessage struct {
PlayerID string `json:"id"`
Jump bool `json:"jump"`
}
// State: Wo alles ist (Server -> Client)
type GameStateMessage struct {
Players map[string]*PlayerState `json:"players"` // Alle Spieler (1 bis 16)
Score float64 `json:"score"`
Multiplier int `json:"multiplier"`
}
type PlayerState struct {
// WICHTIG: Jedes Feld braucht ein eigenes JSON-Tag!
X float64 `json:"x"`
Y float64 `json:"y"`
}

32
pkg/stream/adapter.go Normal file
View File

@@ -0,0 +1,32 @@
package stream
import (
"log"
"github.com/nats-io/nats.go"
)
// Connect verbindet uns mit dem NATS Cluster (oder lokalem Docker Container)
func Connect(url string) (*nats.Conn, error) {
// Default auf localhost, wenn leer
if url == "" {
url = nats.DefaultURL
}
nc, err := nats.Connect(url)
if err != nil {
return nil, err
}
log.Printf("🔌 Verbunden mit NATS unter %s", url)
return nc, nil
}
// Helper Konstanten für unsere Topic-Struktur
const (
// Topic: game.<RoomID>.input -> Client sendet Tasten
SubjectGameInput = "game.%s.input"
// Topic: game.<RoomID>.state -> Server sendet Positionen
SubjectGameState = "game.%s.state"
)