Skip to content

fizx/devvit-phaser

Repository files navigation

Devvit-Phaser 🎮

npm version License: MIT Tests

A library to integrate Phaser.js with Reddit's Devvit platform for building interactive multiplayer games. This library provides a simple, yet powerful framework for creating multiplayer games with synchronized state between players.

Installation

npm install devvit-phaser

Architecture

Devvit-Phaser is built around a few core concepts:

  1. BasicGameServer - The foundation for all multiplayer Devvit games, handling communication between players, timers, and more.

  2. PhaserGameServer - Extends BasicGameServer with Phaser-specific functionality for game state synchronization.

  3. SyncedDataManager - Client-side component that extends Phaser's DataManager, providing synchronized state between all players.

  4. DataManagerServer - Server-side component that manages the persistent state in Redis and broadcasts changes to all connected players.

Usage

Creating a Game Server

import { PhaserGameServer } from 'devvit-phaser';
import { Devvit } from '@devvit/public-api';

// Create a game server with your game name
const gameServer = new PhaserGameServer('Asteroid Blaster');

// Set up custom handlers if needed
gameServer.onDataManagerSubscribe = async (subscriptions) => {
  console.log('Player subscribed to data managers', subscriptions);
  // Perform authorization checks, etc.
};

gameServer.onDataManagerMutate = async (mutation) => {
  console.log('Player mutated data manager', mutation);
  // Validate mutations if needed
};

// Override methods to handle game-specific logic
gameServer.onPlayerJoined = async () => {
  await super.onPlayerJoined(); // Call the parent method to handle basic setup
  
  // Create a player-specific data manager
  const playerData = new DataManagerServer(this, { id: `player_${this.userId}` });
  
  // Initialize with default values
  await playerData.setAll({
    score: 0,
    health: 100,
    position: { x: 0, y: 0 }
  });
  
  // Subscribe the player to their data
  await this.subscribePlayerToDataManager(playerData);
};

// Build the game server (sets up Devvit integration)
gameServer.build();

Using Synchronized Data in Phaser

import { SyncedDataManager } from 'devvit-phaser';
import Phaser from 'phaser';

export class MainScene extends Phaser.Scene {
  gameState: SyncedDataManager;
  playerState: SyncedDataManager;
  ship: Phaser.GameObjects.Sprite;
  
  create() {
    // Create a global game state all players can see
    this.gameState = new SyncedDataManager('gameState', this);
    
    // Create player-specific state (using the player's ID)
    const screenId = window.me.get('screenId') as string;
    this.playerState = new SyncedDataManager(`player_${screenId}`, this);
    
    // Wait for initial data
    this.playerState.events.once('ready', () => {
      // Create the player's ship
      this.ship = this.add.sprite(
        this.playerState.get('position').x, 
        this.playerState.get('position').y, 
        'ship'
      );
      
      // Update UI
      this.updateScoreDisplay(this.playerState.get('score'));
    });
    
    // Listen for changes in player data
    this.playerState.events.on('changedata-position', (_, position) => {
      // Update ship position when it changes on the server
      this.ship.setPosition(position.x, position.y);
    });
    
    // Listen for changes in game state
    this.gameState.events.on('changedata-asteroids', (_, asteroids) => {
      // Update asteroid positions when they change
      this.updateAsteroids(asteroids);
    });
    
    // Handle ship movement
    this.input.keyboard.on('keydown-W', () => {
      const position = this.playerState.get('position');
      position.y -= 10;
      // This will sync to other players automatically
      this.playerState.set('position', position);
    });
  }
  
  increaseScore(points: number) {
    // Increment the score (synchronized to server and other players)
    this.playerState.inc('score', points);
  }
  
  updateScoreDisplay(score: number) {
    // Update UI based on score changes
  }
  
  updateAsteroids(asteroids: any[]) {
    // Update asteroid display based on server data
  }
}

Advanced Features

Timers and Scheduled Events

The PhaserGameServer includes reliable timer functions that persist even through server restarts:

// One-time event after 5 seconds
gameServer.setTimeout('respawn_player', 5000, { playerId: '12345' });

// Recurring event every 1 second
gameServer.setInterval('spawn_asteroid', 1000);

// Handle timer events
gameServer.onTimerEvent = async (event) => {
  if (event.name === 'spawn_asteroid') {
    const asteroidData = new DataManagerServer(this, { id: 'asteroids' });
    const asteroids = await asteroidData.get('list') || [];
    
    asteroids.push({
      id: v4(),
      x: Math.random() * 800,
      y: 0,
      size: Math.floor(Math.random() * 3) + 1
    });
    
    await asteroidData.set('list', asteroids);
  }
};

Player Management

Track players joining and leaving:

gameServer.onPlayerJoined = async () => {
  await super.onPlayerJoined();
  
  // Get the list of active players
  const playersData = new DataManagerServer(this, { id: 'players' });
  const players = await playersData.get('active') || [];
  
  // Add the new player
  players.push({
    id: this.userId,
    name: this.userInfo.username,
    joinedAt: Date.now()
  });
  
  await playersData.set('active', players);
  await this.broadcast(this.postId, { 
    type: 'PLAYER_JOINED', 
    player: this.userInfo 
  });
};

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published