Season Grains
Grains that manage seasons (leagues) and character migration.
SeasonRegistryGrain
Singleton registry for season definitions.
Key: string ("default")
Methods
| Method | Description |
|---|---|
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 ID | Type | Migration Target | Description |
|---|---|---|---|
standard | Permanent | - | Default permanent league |
void | Permanent | - | Items don't migrate (Void League) |
season-N | Temporary | standard | Temporary 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
| Method | Description |
|---|---|
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:
- Is marked as dead
- Is migrated to the permanent league
- Keeps the Hardcore flag (for display)
- 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
};