Skip to main content

Season Grains

Grains that manage seasons (leagues) and character migration.

SeasonRegistryGrain

Singleton registry for season definitions.

Key: string ("default")

Methods

MethodDescription
GetAllSeasonsAsync()Get all seasons
GetSeasonAsync(id)Get specific season
GetCurrentSeasonAsync()Get active temporary season
CreateSeasonAsync()Create new season
UpdateSeasonStatusAsync()Update season status
EndSeasonAsync()End season, trigger migration

Season Model

{
seasonId: string; // e.g., "standard", "season-1"
name: string; // Display name
description?: string;
status: SeasonStatus;
isPermanent: boolean; // true for "standard", "void"
targetSeasonId?: string; // Where characters migrate to
startedAt?: string;
endsAt?: string; // null for permanent
}

Built-in Seasons

Season IDTypeMigration TargetDescription
standardPermanent-Default permanent league
voidPermanent-Items don't migrate (Void League)
season-NTemporarystandardTemporary challenge league

SeasonMigrationGrain

Manages character migration when seasons end.

Key: string (seasonId)

State

public class SeasonMigrationState
{
public MigrationStatus Status { get; set; }
public List<Guid> PendingCharacters { get; set; }
public List<Guid> CompletedCharacters { get; set; }
public List<MigrationError> Errors { get; set; }
}

Methods

MethodDescription
GetStatusAsync()Get migration status
StartMigrationAsync()Begin migration process
MigrateCharacterAsync()Migrate single character
CancelMigrationAsync()Cancel in-progress migration

Migration Flow

Hardcore Character Migration

When a hardcore character dies:

The character:

  1. Is marked as dead
  2. Is migrated to the permanent league
  3. Keeps the Hardcore flag (for display)
  4. Cannot be used for trading (dead)

Void League

For "Void League" seasons, characters migrate to the void season where:

  • Characters exist but can't interact with the economy
  • Items don't enter the permanent league
  • Used for experimental league mechanics
var voidSeason = new Season
{
SeasonId = "void",
Name = "Void",
IsPermanent = true,
// No targetSeasonId - nothing leaves the void
};

Season Lifecycle