Turning Seasons

A standalone Hytale mod that automatically cycles through Spring, Summer, Autumn, and Winter with dynamic weather, atmosphere, lighting, and particle effects across 12 unique weather presets.

File Details

TurningSeasons-v1.0.6.jar

  • R
  • Apr 30, 2026
  • 42.88 KB
  • 154
  • Early Access

File Name

TurningSeasons-v1.0.6.jar

Supported Versions

  • Early Access

# Turning Seasons — Changelog

## v1.0.6 — Defensive Logging + Hygiene

### Bugfix: who-ran-the-command log line

Five places in `SeasonCommand` were logging the player who triggered
a command via `context.sender().getDisplayName()`. After Hytale
Updates 1-2 (early 2026) renamed `CommandSender.getDisplayName()` to
`getUsername()` and removed `Player`'s `CommandSender` interface,
those calls were silently throwing `NoSuchMethodError` at runtime —
swallowed by the parent `try/catch` so the user-facing chat reply
worked but the server log lost its "who triggered the season change"
audit trail.

v1.0.6 replaces the calls with a defensive `safeSenderName(context)`
helper that:

1. Tries `getUsername()` first (post-Update-1 API)
2. Falls back to legacy `getDisplayName()` (older / aliased builds)
3. Falls back to the sender's UUID as a last-ditch identifier
4. Returns `"<unknown>"` if nothing resolves — never throws

The helper uses pure reflection so the plugin tolerates whatever
shape `context.sender()` returns on a given Hytale version
(CommandSender interface, PlayerRef wrapper, raw Player, etc.).

### Hygiene pass

Same documentation/metadata polish I've applied to Project Sakura
v1.4.1, Structure Atlas v1.1.2, and HAARP Weather Control v3.0.3:

- `ServerVersion` changed from a pinned Update-4 build hash
  (`2026.03.26-89796e57b`) to wildcard `*`. Future Hytale updates
  no longer require a re-publish.
- `Description` rewritten to capture what's actually in the mod
  (12 hand-tuned weather presets, persistence across logins, admin
  commands).
- Author block now includes `Email` and `Url` for credit and contact.
- New `Website` field pointing to the CurseForge mod page.
- New `CHANGELOG.md` (this file) covering the full v1.0.x history.
- New `CURSEFORGE_DESCRIPTION.md` and `CURSEFORGE_RELEASE_NOTES.md`
  for paste-ready CurseForge updates on every future release.

### Build pipeline

v1.0.6 ships a new `build_turning_seasons_jar.py` modelled on the
HAARP/Sakura pattern:

- Reads version from `manifest.json` automatically
- Paths resolve relative to the script — workspace-relocatable
- Bundles `Server/` assets (weather files, environment, lang) into
  the JAR alongside compiled classes
- **Selectively packages only `com/turningseasons/**` + `Server/**`
  + `manifest.json`** — never accidentally includes leaked stub
  classes (a real bug pattern that almost shipped in HAARP today)
- Outputs to `releases/turning-seasons/` automatically

---

## v1.0.5 — Season Persistence

Fixed the "seasons reset on login/logout" bug reported by users.

### What changed

Turning Seasons now reads Hytale's built-in `World.tick` field — the
same persistent counter Hytale already saves with the world — instead
of a private in-memory tick count that reset every plugin load. Plus
a tiny per-save state file (`mods/turning_seasons/state.json`)
records:

- `startTick` — the world tick when the mod first activated in this
  save. Captures "where we anchored zero." Set once, never changes.
- `dayOffset` — accumulated adjustment from `/season set` and
  `/season skip`, bounded to `[0, daysPerSeason*4)`.
- `daysPerSeason` — user-configured season length (3–120, default 15).

### Effect

Seasons advance during gameplay, pause when you're logged out, and
resume exactly where they left off when you next play. Mid-game
installs anchor at the install moment; Spring Early starts there
rather than wherever the world's current tick happened to fall.

### Graceful degradation

If `World.tick` reflection fails on a future Hytale build, the mod
falls back to the v1.0.4 ephemeral counter and logs a warning. No
crash; persistence just disabled.

---

## v1.0.4 — HAARP Weather Pattern

The version where the visible sky actually started changing on
season transitions.

### Root cause of the v1.0.0–v1.0.3 visual silence

The original design tried to mutate `WeatherForecast` weights in the
active environment object via reflection. That approach failed on
the real Hytale runtime because the field name and structure didn't
match what we expected, and even if we had landed on the right
field, Hytale caches weather decisions — so re-weighting the
forecast wouldn't actually change the visible weather until the
next forecast resample.

### The fix

Replaced the forecast-mutation code with HAARP's proven
`WeatherResource.setForcedWeather(weatherId)` chain:

```
World → getEntityStore() → getStore()
      → getResource(WeatherResource.type)
      → setForcedWeather("Spring_Early")
```

This directly forces a specific weather ID and bypasses Hytale's
forecast cache entirely. The same pattern HAARP Weather Control
uses for its 14 weather programs.

### Other v1.0.4 fixes

- Tick scheduler properly rate-limited to 20 TPS via
  `ScheduledExecutorService` (was previously firing at thread-pool
  speed, racing through seasons)
- `Set<Player>` broadcast pattern via `PlayerReadyEvent` (was
  previously trying to enumerate world players via reflection,
  silently failing)

---

## v1.0.3 — Command Argument Parsing

`/season set winter`, `/season skip`, and `/season length 30` were
silently doing nothing because Hytale's `withOptionalArg()` API
doesn't reliably bind positional arguments. The arguments were
being lost before reaching the dispatcher.

Replaced with HAARP's proven manual parsing pattern:
{@code CommandContext.getInputString()} then split on whitespace.
All subcommands work now.

---

## v1.0.2 — Chat Formatting + Plugin Loading

### Chat formatting

The original code used Minecraft-style section-sign colour codes
(`§a`, `§b`, etc.). Hytale doesn't interpret those — players saw
the literal `§` characters in chat. Replaced with Hytale's hex
colour API: `Message.raw(text).color("#FFD54A").bold(true)`.

### Plugin loading

The v1.0.0 / v1.0.1 ZIP packaging bundled a JAR inside a ZIP. Hytale's
PluginManager doesn't load JARs from inside ZIPs — it scans the Mods
folder directly. So the asset pack loaded but the plugin code never
ran.

Switched to the hybrid-JAR pattern: a single `.jar` file containing
compiled classes, manifest at root, AND the `Server/` asset tree.
The PluginManager picks it up as a plugin; the AssetModule (via
`IncludesAssetPack: true`) processes the bundled assets.

### Other

- `Main` field added to manifest (was missing — required to identify
  the plugin entry class)

---

## v1.0.1 — Spring Hex Color + Lang File

The v1.0.0 release failed to load entire worlds when enabled. Two
fatal bugs:

1. **Invalid hex color** in the Spring weather files. The generator
   script had `"#3040660"` (7 hex digits, illegal — should have been
   `"#304066"`) for the Spring moon glow. Hytale's color parser
   threw `IllegalArgumentException: Invalid hex color size: 7` on
   load, which cascaded to environment validation failure, which
   cascaded to a `Mod failed to load` shutdown.

2. **Wrong lang file comment syntax**. The `en-US/TurningSeasons.lang`
   file used `//` comments at the top. Hytale's lang parser only
   accepts `#` comments. The parser threw on the first line.

Both fixed in v1.0.1. The seasonal weather generator now produces
valid 6-digit hex; the lang file uses `#` comments.

---

## v1.0.0 — Initial Release (Did Not Load)

The first attempted public release. Hit the two parser bugs above
on world load and was withdrawn. Replaced by v1.0.1.