Skip to content

Scheduler Jobs

Run background tasks on a schedule.

Overview

Scheduler jobs run periodically outside of game sessions:

import { SchedulerJob, SchedulerJobContext } from '@lands.io/mod-sdk';

class LeaderboardUpdateJob extends SchedulerJob {
  readonly cronExpression = '0 0 * * * *'; // Every hour

  async execute(context: SchedulerJobContext): Promise<void> {
    const topPlayers = await context.storage.getOrderedDesc(
      'players',
      'elo',
      100
    );
    console.log('Updated leaderboard:', topPlayers.length, 'players');
  }
}

SchedulerJob Class

Required Properties

// Cron expression (minimum interval: 1 hour)
abstract readonly cronExpression: string;

Required Methods

// Job logic
abstract execute(context: SchedulerJobContext): Promise<void>;

Cron Expression Format

┌──────────── second (0-59)
│ ┌────────── minute (0-59)
│ │ ┌──────── hour (0-23)
│ │ │ ┌────── day of month (1-31)
│ │ │ │ ┌──── month (1-12)
│ │ │ │ │ ┌── day of week (0-6, Sunday=0)
│ │ │ │ │ │
* * * * * *

Common Patterns

Expression Description
0 0 * * * * Every hour
0 0 */2 * * * Every 2 hours
0 0 0 * * * Daily at midnight
0 0 12 * * * Daily at noon
0 0 0 * * 0 Weekly on Sunday
0 0 0 1 * * Monthly on the 1st

Minimum Interval

Jobs must run at least 1 hour apart to prevent excessive resource usage.

SchedulerJobContext

interface SchedulerJobContext {
  storage: IModStorage; // Persistent storage
  modId: string; // Your mod's ID
}

Example: Weekly Leaderboard Reset

import { SchedulerJob, SchedulerJobContext } from '@lands.io/mod-sdk';

export class WeeklyResetJob extends SchedulerJob {
  // Run every Sunday at midnight
  readonly cronExpression = '0 0 0 * * 0';

  async execute(context: SchedulerJobContext): Promise<void> {
    // Get top 10 players
    const topPlayers = await context.storage.getOrderedDesc(
      'players',
      'weeklyScore',
      10
    );

    // Award bonuses to top players
    for (let i = 0; i < topPlayers.length; i++) {
      const bonus = this.calculateBonus(i + 1);
      await context.storage.increment(
        'players',
        topPlayers[i].oderId,
        'gems',
        bonus
      );
    }

    // Reset weekly scores
    // (Implementation depends on your storage API)
    console.log(`Weekly reset complete. Awarded ${topPlayers.length} players.`);
  }

  private calculateBonus(rank: number): number {
    const bonuses = [100, 75, 50, 40, 30, 25, 20, 15, 10, 5];
    return bonuses[rank - 1] || 0;
  }
}

Example: ELO Decay Job

export class EloDecayJob extends SchedulerJob {
  // Run daily at 3 AM
  readonly cronExpression = '0 0 3 * * *';

  async execute(context: SchedulerJobContext): Promise<void> {
    // Get inactive players (no games in 7 days)
    const inactivePlayers = await this.getInactivePlayers(context);

    for (const player of inactivePlayers) {
      // Apply 1% ELO decay
      const decay = Math.floor(player.elo * 0.01);
      if (decay > 0) {
        await context.storage.increment(
          'players',
          player.oderId,
          'elo',
          -decay
        );
      }
    }

    console.log(
      `Applied ELO decay to ${inactivePlayers.length} inactive players`
    );
  }

  private async getInactivePlayers(context: SchedulerJobContext) {
    // Implementation depends on your data model
    return [];
  }
}

File Organization

Place scheduler jobs in a dedicated folder:

my-mod/
├── schedulerJobs/
│   ├── leaderboard-update.ts
│   ├── weekly-reset.ts
│   └── elo-decay.ts
└── ...