Skip to main content

Architecture Overview

This guide provides a comprehensive overview of Grimoire Infini’s architecture, explaining how different components work together to create a seamless real-time magical card gaming experience.

System Architecture

Core Components

Frontend Layer

SvelteKit

Responsibilities:
  • User interface rendering
  • Client-side routing
  • State management
  • Component lifecycle
Key Features:
  • Server-side rendering
  • Hot module replacement
  • TypeScript support
  • Responsive design

Three.js

Responsibilities:
  • 3D scene rendering
  • Card mesh creation
  • Animation handling
  • User interactions
Key Features:
  • WebGL rendering
  • Physics simulation
  • Custom materials
  • Event handling

Backend Layer

Firebase Firestore

Responsibilities:
  • Real-time data synchronization
  • Game state persistence
  • Player action logging
  • Offline support
Key Features:
  • NoSQL document database
  • Real-time listeners
  • Automatic scaling
  • Security rules

Firebase Auth

Responsibilities:
  • User authentication
  • Session management
  • Access control
  • Identity verification
Key Features:
  • Multiple auth providers
  • JWT token handling
  • Anonymous authentication
  • User management

Data Flow

The data flow in Grimoire Infini follows a clear pattern:
1

Player Action

A player interacts with a card in the 3D scene (move, flip, etc.)
2

Local Update

The local scene updates immediately for responsive feel
3

Action Broadcasting

MultiplayerController sends the action to Firebase Firestore
4

Remote Synchronization

Other players receive the action and update their local state
5

State Persistence

The game state is updated in Firestore for consistency

Component Interaction

MultiplayerController ↔ SceneManager

// MultiplayerController updates SceneManager
private handleGameStateUpdate(gameState: GameState): void {
  gameState.cards.forEach((cardState) => {
    const card = this.sceneManager.cards.find(c => c.getId() === cardState.id);
    if (card) {
      this.updateCardFromState(card, cardState);
    }
  });
}

// SceneManager notifies MultiplayerController
public async onCardMoved(cardId: string, position: Position3D): Promise<void> {
  await this.multiplayerController.moveCard(cardId, position);
}

Firebase Integration

// Real-time listener setup
private setupGameStateListener(): void {
  this.unsubscribeGameState = onSnapshot(
    this.gameStateRef,
    (doc: DocumentSnapshot) => {
      if (doc.exists()) {
        const gameState: GameState = doc.data() as GameState;
        this.handleGameStateUpdate(gameState);
      }
    }
  );
}

Design Patterns

Observer Pattern

The system uses the Observer pattern extensively:
  • Firebase Listeners: Observe document changes
  • Scene Events: Observe user interactions
  • State Changes: Observe game state modifications

Command Pattern

Player actions are implemented using the Command pattern:
interface GameAction {
  type: "MOVE_CARD" | "FLIP_CARD" | "DRAW_CARD";
  execute(): void;
  undo?(): void;
}

Singleton Pattern

Critical managers use Singleton pattern:
  • SceneManager: Single 3D scene instance
  • FirebaseService: Single Firebase connection
  • GameStateManager: Centralized state management

Performance Optimizations

Rendering Optimization

  • Uses requestAnimationFrame for smooth 60fps rendering
  • Implements frustum culling to hide off-screen objects
  • LOD (Level of Detail) system for distant cards
  • Object pooling for frequently created/destroyed objects
  • Texture atlasing to reduce GPU memory usage
  • Garbage collection optimization

Network Optimization

  • Position threshold system prevents micro-movement spam
  • Action batching reduces Firebase operations
  • Delta compression for state updates
  • Automatic reconnection on network failures
  • Offline-first approach with local state caching
  • Conflict resolution for simultaneous actions

Security Architecture

Firebase Security Rules

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Game rooms - authenticated users only
    match /gameStates/{roomId} {
      allow read, write: if request.auth != null;
      
      // Actions subcollection
      match /actions/{actionId} {
        allow read, write: if request.auth != null 
          && request.auth.uid == resource.data.playerId;
      }
    }
  }
}

Client-Side Security

  • Input validation on all user actions
  • Rate limiting for action submissions
  • Sanitization of user-generated content
  • Secure token handling

Scalability Considerations

Horizontal Scaling

Room-Based Architecture

Games are isolated in separate rooms, allowing independent scaling

Stateless Design

Client logic is stateless, enabling easy load balancing

Vertical Scaling

Firebase Auto-Scaling

Firestore automatically scales based on usage

CDN Integration

Static assets served via CDN for global performance

Error Handling Strategy

Graceful Degradation

// Network error handling
try {
  await this.multiplayerController.moveCard(cardId, position);
} catch (error) {
  // Fall back to local-only mode
  this.sceneManager.updateCardPosition(cardId, position);
  this.showNetworkError();
}

Recovery Mechanisms

  • Automatic state reconciliation on reconnection
  • Conflict resolution for simultaneous edits
  • Rollback mechanisms for failed operations

Monitoring and Debugging

Development Tools

  • Firebase Emulator: Local development environment
  • Three.js Inspector: 3D scene debugging
  • SvelteKit DevTools: Component state inspection

Production Monitoring

  • Firebase Analytics: User behavior tracking
  • Performance Monitoring: Render performance metrics
  • Error Reporting: Automated crash reporting

Next Steps