managarten/games/voxel-lava/DATABASE.md
Wuesteon d36b321d9d style: auto-format codebase with Prettier
Applied formatting to 1487+ files using pnpm format:write
  - TypeScript/JavaScript files
  - Svelte components
  - Astro pages
  - JSON configs
  - Markdown docs

  13 files still need manual review (Astro JSX comments)
2025-11-27 18:33:16 +01:00

9.8 KiB

Voxel-Lava Datenbank-Dokumentation

Diese Dokumentation beschreibt den Aufbau und die Funktionsweise der Supabase-Datenbank für das Voxel-Lava-Spiel. Die Datenbank ermöglicht das Speichern, Teilen und Bewerten von Levels.

Datenbankschema

Tabellen

1. levels

Speichert alle Level-Daten inklusive der Voxel-Informationen.

Spalte Typ Beschreibung
id UUID Primärschlüssel, automatisch generiert
name TEXT Name des Levels
description TEXT Optionale Beschreibung des Levels
user_id UUID Fremdschlüssel zur auth.users-Tabelle, Ersteller des Levels
voxel_data JSONB Speichert alle Blöcke mit Position und Typ als JSON
spawn_point JSONB Position des Spawn-Punkts als JSON {x, y, z}
world_size JSONB Größe der Welt als JSON {width, height, depth}
is_public BOOLEAN Gibt an, ob das Level öffentlich ist (Standard: false)
created_at TIMESTAMP Zeitpunkt der Erstellung
updated_at TIMESTAMP Zeitpunkt der letzten Aktualisierung
play_count INTEGER Anzahl der Aufrufe des Levels
likes_count INTEGER Anzahl der Likes des Levels
difficulty TEXT Optionaler Schwierigkeitsgrad des Levels
tags TEXT[] Array von Tags zur Kategorisierung
thumbnail_url TEXT Optionale URL zum Vorschaubild

2. level_likes

Speichert die Likes für Levels.

Spalte Typ Beschreibung
id UUID Primärschlüssel, automatisch generiert
level_id UUID Fremdschlüssel zur levels-Tabelle
user_id UUID Fremdschlüssel zur auth.users-Tabelle
created_at TIMESTAMP Zeitpunkt des Likes

Constraints:

  • Unique-Constraint auf (level_id, user_id), um doppelte Likes zu verhindern

3. level_plays

Speichert Spielstatistiken für Levels.

Spalte Typ Beschreibung
id UUID Primärschlüssel, automatisch generiert
level_id UUID Fremdschlüssel zur levels-Tabelle
user_id UUID Fremdschlüssel zur auth.users-Tabelle
completion_time FLOAT Zeit in Sekunden (null, wenn nicht abgeschlossen)
attempts INTEGER Anzahl der Versuche (Standard: 1)
completed BOOLEAN Gibt an, ob das Level abgeschlossen wurde
created_at TIMESTAMP Zeitpunkt des Spielversuchs

Indizes

Zur Optimierung der Abfrageleistung wurden folgende Indizes erstellt:

CREATE INDEX idx_levels_user_id ON levels(user_id);
CREATE INDEX idx_levels_is_public ON levels(is_public);
CREATE INDEX idx_level_likes_level_id ON level_likes(level_id);
CREATE INDEX idx_level_likes_user_id ON level_likes(user_id);
CREATE INDEX idx_level_plays_level_id ON level_plays(level_id);
CREATE INDEX idx_level_plays_user_id ON level_plays(user_id);

Funktionen und Trigger

1. update_updated_at_column()

Aktualisiert automatisch den updated_at-Zeitstempel bei Änderungen an einem Level.

CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = now();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_levels_updated_at
BEFORE UPDATE ON levels
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

2. update_likes_count()

Aktualisiert automatisch den likes_count in der levels-Tabelle, wenn ein Like hinzugefügt oder entfernt wird.

CREATE OR REPLACE FUNCTION update_likes_count()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'INSERT' THEN
    UPDATE levels SET likes_count = likes_count + 1 WHERE id = NEW.level_id;
  ELSIF TG_OP = 'DELETE' THEN
    UPDATE levels SET likes_count = likes_count - 1 WHERE id = OLD.level_id;
  END IF;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_level_likes_count
AFTER INSERT OR DELETE ON level_likes
FOR EACH ROW
EXECUTE FUNCTION update_likes_count();

3. update_play_count()

Aktualisiert automatisch den play_count in der levels-Tabelle, wenn ein Spielversuch hinzugefügt wird.

CREATE OR REPLACE FUNCTION update_play_count()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'INSERT' THEN
    UPDATE levels SET play_count = play_count + 1 WHERE id = NEW.level_id;
  END IF;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_level_play_count
AFTER INSERT ON level_plays
FOR EACH ROW
EXECUTE FUNCTION update_play_count();

4. increment_play_count(level_id UUID)

Erhöht den play_count für ein Level, auch wenn kein Benutzer angemeldet ist.

CREATE OR REPLACE FUNCTION increment_play_count(level_id UUID)
RETURNS VOID AS $$
BEGIN
  UPDATE levels SET play_count = play_count + 1 WHERE id = level_id;
END;
$$ LANGUAGE plpgsql;

Sicherheitsrichtlinien (RLS)

Die Datenbank verwendet Row Level Security (RLS), um den Zugriff auf Daten zu kontrollieren:

1. Levels

-- Levels sind für Ersteller und öffentlich sichtbar
CREATE POLICY "Levels sind für Ersteller und öffentlich sichtbar"
  ON levels FOR SELECT
  USING (auth.uid() = user_id OR is_public = true);

-- Levels können nur vom Ersteller bearbeitet werden
CREATE POLICY "Levels können nur vom Ersteller bearbeitet werden"
  ON levels FOR UPDATE
  USING (auth.uid() = user_id);

-- Levels können nur vom Ersteller gelöscht werden
CREATE POLICY "Levels können nur vom Ersteller gelöscht werden"
  ON levels FOR DELETE
  USING (auth.uid() = user_id);

-- Neue Levels können nur vom authentifizierten Benutzer erstellt werden
CREATE POLICY "Neue Levels können nur vom authentifizierten Benutzer erstellt werden"
  ON levels FOR INSERT
  WITH CHECK (auth.uid() = user_id);

2. Level Likes

-- Likes können von jedem authentifizierten Benutzer gesehen werden
CREATE POLICY "Likes können von jedem authentifizierten Benutzer gesehen werden"
  ON level_likes FOR SELECT
  USING (true);

-- Likes können nur vom Benutzer selbst erstellt werden
CREATE POLICY "Likes können nur vom Benutzer selbst erstellt werden"
  ON level_likes FOR INSERT
  WITH CHECK (auth.uid() = user_id);

-- Likes können nur vom Benutzer selbst entfernt werden
CREATE POLICY "Likes können nur vom Benutzer selbst entfernt werden"
  ON level_likes FOR DELETE
  USING (auth.uid() = user_id);

3. Level Plays

-- Spielstatistiken sind für Level-Ersteller sichtbar
CREATE POLICY "Spielstatistiken sind für Level-Ersteller sichtbar"
  ON level_plays FOR SELECT
  USING (EXISTS (
    SELECT 1 FROM levels
    WHERE levels.id = level_plays.level_id
    AND levels.user_id = auth.uid()
  ) OR auth.uid() = user_id);

-- Spielstatistiken können von jedem authentifizierten Benutzer erstellt werden
CREATE POLICY "Spielstatistiken können von jedem authentifizierten Benutzer erstellt werden"
  ON level_plays FOR INSERT
  WITH CHECK (auth.uid() = user_id);

-- Spielstatistiken können nur vom Benutzer selbst aktualisiert werden
CREATE POLICY "Spielstatistiken können nur vom Benutzer selbst aktualisiert werden"
  ON level_plays FOR UPDATE
  USING (auth.uid() = user_id);

Datenformat

Voxel-Daten (JSONB)

Die Voxel-Daten werden als JSONB in der voxel_data-Spalte gespeichert. Das Format ist:

{
	"x,y,z": {
		"type": "blockType",
		"isSpawnPoint": false,
		"isGoal": false
	},
	"1,1,1": {
		"type": "grass",
		"isSpawnPoint": true,
		"isGoal": false
	},
	"5,1,5": {
		"type": "goal",
		"isSpawnPoint": false,
		"isGoal": true
	}
}

Wobei:

  • Der Schlüssel ist eine Zeichenkette im Format "x,y,z", die die Position des Blocks angibt
  • type ist der Typ des Blocks (z.B. "grass", "stone", "lava")
  • isSpawnPoint gibt an, ob dieser Block ein Spawn-Punkt ist
  • isGoal gibt an, ob dieser Block ein Ziel ist

Verwendung in der Anwendung

Die Datenbank wird über den Supabase-Client in der Anwendung angesprochen. Dafür wurden folgende Services implementiert:

  1. LevelService: Zum Speichern, Laden und Verwalten von Levels
  2. AuthService: Für die Benutzerauthentifizierung

Beispiel für die Verwendung des LevelService:

import { LevelService } from '../services/LevelService';

// Level speichern
const level = {
	name: 'Mein Level',
	blocks: [
		/* ... */
	],
	spawnPoint: { x: 1, y: 1, z: 1 },
	worldSize: { width: 20, height: 10, depth: 20 },
	isPublic: true,
};
const levelId = await LevelService.saveLevel(level);

// Level laden
const loadedLevel = await LevelService.loadLevel(levelId);

// Öffentliche Levels abrufen
const publicLevels = await LevelService.getPublicLevels();

// Level liken
await LevelService.likeLevel(levelId);

Erweiterungsmöglichkeiten

Die Datenbankstruktur kann in Zukunft um folgende Funktionen erweitert werden:

  1. Kommentare: Eine neue Tabelle für Kommentare zu Levels
  2. Leaderboards: Detaillierte Bestenlisten für jedes Level
  3. Benutzerprofile: Erweiterte Benutzerinformationen und Statistiken
  4. Freundschaften: Beziehungen zwischen Benutzern
  5. Sammlungen: Gruppierung von Levels in Sammlungen oder Playlists