Skip to content

Latest commit

 

History

History
190 lines (134 loc) · 5.86 KB

FILE_SYSTEM.md

File metadata and controls

190 lines (134 loc) · 5.86 KB

File System

Source: Decompiled from HytaleServer.jar (pre-release 2026.02.05-9ce2783f7)

Hytale's world generation uses a layered file system for asset resolution. This enables mod asset overrides — mods can replace or extend base generator files without modifying the originals. The system is built on three layers: FileIO (facade), FileIOSystem (resolution strategy), and AssetFileSystem (multi-pack overlay).


Architecture

graph TD
    A[FileIO - Static Facade] --> B[FileIOSystem - ThreadLocal]
    B --> C[AssetFileSystem]
    C --> D[Asset Pack 1 - Mod Override]
    C --> E[Asset Pack 2 - Version Pack]
    C --> F[Base Asset Pack]
    D --> G[resolve path → first match wins]
    E --> G
    F --> G
Loading

FileIO (Facade)

Class: FileIO

Static facade that dispatches all file operations through the currently active FileIOSystem. Uses a thread-local holder so each generation thread can have its own file system view.

Key Methods

Method Description
setDefaultRoot(Path) Set the global default file system root
openFileIOSystem(FileIOSystem) Activate a custom FileIOSystem on the current thread
closeFileIOSystem() Close the active system, revert to default
exists(AssetPath) Check if a file exists
resolve(Path) Resolve a path through the active FileIOSystem
load(AssetPath, AssetLoader) Load and deserialize a file
list(Path, Predicate, UnaryOperator) List files with filtering

FileIOSystem (Interface)

Class: FileIOSystem

Defines the resolution strategy for finding files across multiple roots.

Key Methods

Method Description
baseRoot() Get the base root path
roots() Get PathArray of all resolution roots (ordered by priority)
resolve(Path) Resolve a relative path through the root hierarchy
load(AssetPath, AssetLoader) Load file content

Default Resolution Algorithm

  1. Iterate through roots() in order (highest priority first)
  2. For each root, check if root + relativePath exists
  3. Return the first match as an AssetPath
  4. If no match, fall back to baseRoot()

AssetFileSystem (Multi-Pack Overlay)

Class: AssetFileSystem implements FileIOSystem

The production file system used during world generation. Supports layered asset packs with priority-based resolution.

Resolution Priority (highest to lowest)

  1. Non-version asset packs (mod overrides, in reverse registration order)
  2. Version-specific packs (if VERSION_OVERRIDES feature flag is enabled, packs where version <= config version)
  3. Base asset pack (always lowest priority)

Caching

Cache Type Description
files Map<Path, AssetPath> Resolved path cache
resources Map<AssetPath, Resource<?>> Loaded content cache (type-safe)

The resource cache enforces type safety — if a file was previously loaded as type A, attempting to load it as type B throws an IllegalStateException.

Asset Pack Layering Logic

For each registered asset pack (reverse order):
  1. If pack root is NOT under $Versions/ directory:
     → Add to resolution roots (mod override)

  2. If VERSION_OVERRIDES enabled AND pack root IS under $Versions/:
     → If pack version <= world config version:
       → Add to resolution roots (version pack)

  3. Base pack always added last (lowest priority)

AssetPath (Dual-Path)

Class: AssetPath

Represents a file with both relative and absolute paths.

Field Type Description
path Path Relative path (without root prefix, used for comparison/hashing)
filepath Path Absolute file path (root + relative, used for actual I/O)

Factory Methods

Method Description
fromAbsolute(root, filepath) Create from absolute path — relativizes against root
fromRelative(root, assetPath) Create from relative path — appends to root
rename(filename) Create new AssetPath with renamed file

Equality and hashing are based on the relative path only, enabling consistent lookup regardless of which root resolved the file.


Asset Pack Manifest

Each asset pack has a manifest with metadata:

Field Type Description
name string Pack name
group string Pack group/category
website string Pack website URL
description string Pack description
version string Version string (used in version-specific resolution)
authors AuthorInfo[] Pack authors
serverVersion string Compatible server version

Resolution Flow

graph TD
    A["FileIO.load(path)"] --> B["FileIOSystem.resolve(path)"]
    B --> C["Iterate roots() in priority order"]
    C --> D{"File exists at root + path?"}
    D -- Yes --> E["Return AssetPath"]
    D -- No --> F["Try next root"]
    F --> D
    E --> G["Files.newInputStream(filepath)"]
    G --> H["AssetLoader.load(stream)"]
    H --> I["Cache result in resources map"]
Loading

Mod Override Example

Given this pack structure:

base-pack/
├── HytaleGenerator/
│   └── Biomes/
│       ├── Forest.json
│       └── Desert.json

my-mod-pack/
├── HytaleGenerator/
│   └── Biomes/
│       └── Forest.json    ← overrides base Forest.json

When the generator loads HytaleGenerator/Biomes/Forest.json:

  1. Checks my-mod-pack first (higher priority)
  2. Finds Forest.json → uses mod version
  3. Never checks base pack for this file

When loading Desert.json:

  1. Checks my-mod-pack → not found
  2. Checks base pack → found → uses base version

Source files: FileIO.java, FileIOSystem.java, AssetFileSystem.java, AssetPath.java, FileLoadingContext.java