Files
hytale-server/docs/08-asset-system.md

9.9 KiB

Asset System

The Hytale asset system manages game assets including blocks, items, models, sounds, particles, and more. This guide covers how to work with assets and create custom content.

Overview

Assets are defined in JSON files and loaded by the AssetStore. The server provides a type-safe asset system with:

  • JSON-based asset definitions
  • Codec-based serialization
  • Asset type registration
  • Asset pack support for mods

Asset Store

Accessing Assets

// Get the asset store
AssetStore store = HytaleAssetStore.get();

// Get a specific asset by ID
BlockType block = store.get(BlockType.class, "stone");
ItemType item = store.get(ItemType.class, "sword");

// Get all assets of a type
Collection<BlockType> allBlocks = store.getAll(BlockType.class);

Asset Loading

Assets are loaded from:

  1. Core game assets
  2. Plugin asset packs (when IncludesAssetPack: true in manifest)

Asset Types

The server supports many built-in asset types located in server/core/asset/type/:

Asset Type Description
BlockType Block definitions (stone, dirt, etc.)
ItemType Item definitions
ModelAsset 3D model definitions
SoundEvent Sound effect definitions
ParticleAsset Particle system definitions
WeatherAsset Weather type definitions
EnvironmentAsset Environment settings
FluidAsset Fluid definitions
EntityEffect Entity effect definitions
BiomeAsset Biome definitions
StructureAsset Structure/prefab definitions

Blocks

Block Type Definition

Block types are defined in JSON:

{
    "id": "mymod:custom_block",
    "name": "Custom Block",
    "model": "mymod:models/custom_block",
    "hardness": 2.0,
    "resistance": 6.0,
    "drops": ["mymod:custom_block"],
    "sounds": {
        "break": "block.stone.break",
        "place": "block.stone.place",
        "step": "block.stone.step"
    },
    "properties": {
        "transparent": false,
        "solid": true,
        "flammable": false
    }
}

Block States

Blocks can have multiple states (orientations, variations):

// Get block state
BlockState state = BlockStates.get("stone");

// Create state with properties
BlockState orientedBlock = BlockStates.get("log", Map.of(
    "axis", "y"
));

// Check block properties
boolean isSolid = state.isSolid();
boolean isTransparent = state.isTransparent();

Registering Block States

@Override
protected void setup() {
    getBlockStateRegistry().register("custom_block", CustomBlockState.class);
}

Items

Item Type Definition

{
    "id": "mymod:custom_sword",
    "name": "Custom Sword",
    "model": "mymod:models/custom_sword",
    "maxStackSize": 1,
    "durability": 500,
    "itemType": "weapon",
    "damage": 7.0,
    "attackSpeed": 1.6,
    "enchantable": true
}

Working with Items

// Get item type
ItemType swordType = store.get(ItemType.class, "mymod:custom_sword");

// Create item stack
ItemStack stack = new ItemStack(swordType, 1);

// Set item data
stack.setDurability(400);
stack.setCustomName("Legendary Sword");

// Add to inventory
player.getInventory().addItem(stack);

Models

Model Asset Definition

{
    "id": "mymod:models/custom_block",
    "type": "block",
    "textures": {
        "all": "mymod:textures/custom_block"
    },
    "elements": [
        {
            "from": [0, 0, 0],
            "to": [16, 16, 16],
            "faces": {
                "north": {"texture": "#all"},
                "south": {"texture": "#all"},
                "east": {"texture": "#all"},
                "west": {"texture": "#all"},
                "up": {"texture": "#all"},
                "down": {"texture": "#all"}
            }
        }
    ]
}

Sounds

Sound Event Definition

{
    "id": "mymod:custom_sound",
    "sounds": [
        {
            "name": "mymod:sounds/custom1",
            "volume": 1.0,
            "pitch": 1.0,
            "weight": 1
        },
        {
            "name": "mymod:sounds/custom2",
            "volume": 0.8,
            "pitch": 1.2,
            "weight": 1
        }
    ],
    "category": "block"
}

Playing Sounds

// Get sound event
SoundEvent sound = store.get(SoundEvent.class, "mymod:custom_sound");

// Play at position
world.playSound(sound, position, volume, pitch);

// Play to specific player
player.playSound(sound, volume, pitch);

// Play to all nearby players
world.playSoundNearby(sound, position, radius, volume, pitch);

Particles

Particle Asset Definition

{
    "id": "mymod:custom_particle",
    "texture": "mymod:textures/particles/custom",
    "lifetime": 20,
    "scale": 1.0,
    "gravity": 0.04,
    "color": [1.0, 1.0, 1.0, 1.0],
    "fadeOut": true
}

Spawning Particles

// Get particle asset
ParticleAsset particle = store.get(ParticleAsset.class, "mymod:custom_particle");

// Spawn particles
world.spawnParticle(particle, position, count, spreadX, spreadY, spreadZ, speed);

// Spawn for specific player
player.spawnParticle(particle, position, count);

Custom Asset Types

Defining a Custom Asset Type

public class MyCustomAsset {
    public static final Codec<MyCustomAsset> CODEC = BuilderCodec.of(MyCustomAsset::new)
        .with("id", Codec.STRING, a -> a.id)
        .with("value", Codec.INTEGER, a -> a.value, 0)
        .with("enabled", Codec.BOOLEAN, a -> a.enabled, true)
        .build();
    
    private final String id;
    private final int value;
    private final boolean enabled;
    
    public MyCustomAsset(String id, int value, boolean enabled) {
        this.id = id;
        this.value = value;
        this.enabled = enabled;
    }
    
    public String getId() { return id; }
    public int getValue() { return value; }
    public boolean isEnabled() { return enabled; }
}

Registering Custom Asset Types

@Override
protected void setup() {
    getAssetRegistry().register(MyCustomAsset.class, MyCustomAsset.CODEC);
}

Using Custom Assets

@Override
protected void start() {
    AssetStore store = HytaleAssetStore.get();
    
    // Get all custom assets
    Collection<MyCustomAsset> assets = store.getAll(MyCustomAsset.class);
    
    for (MyCustomAsset asset : assets) {
        if (asset.isEnabled()) {
            processAsset(asset);
        }
    }
}

Asset Packs

Creating an Asset Pack

  1. Set IncludesAssetPack: true in your manifest.json
  2. Create an assets/ directory in your JAR
  3. Organize assets following Hytale's structure:
assets/
├── mymod/
│   ├── blocks/
│   │   └── custom_block.json
│   ├── items/
│   │   └── custom_item.json
│   ├── models/
│   │   ├── block/
│   │   │   └── custom_block.json
│   │   └── item/
│   │       └── custom_item.json
│   ├── textures/
│   │   ├── blocks/
│   │   │   └── custom_block.png
│   │   └── items/
│   │       └── custom_item.png
│   └── sounds/
│       └── custom_sound.ogg

Asset Namespacing

Assets are namespaced by plugin ID:

{group}:{name} -> mymod:custom_block

Reference assets using their full namespaced ID to avoid conflicts.

Entity Assets

Entity Type Definition

Entities can have associated assets:

{
    "id": "mymod:custom_mob",
    "name": "Custom Mob",
    "model": "mymod:models/entity/custom_mob",
    "textures": {
        "default": "mymod:textures/entity/custom_mob"
    },
    "animations": {
        "idle": "mymod:animations/custom_mob_idle",
        "walk": "mymod:animations/custom_mob_walk",
        "attack": "mymod:animations/custom_mob_attack"
    },
    "sounds": {
        "hurt": "mymod:entity.custom_mob.hurt",
        "death": "mymod:entity.custom_mob.death",
        "ambient": "mymod:entity.custom_mob.ambient"
    },
    "attributes": {
        "maxHealth": 20.0,
        "movementSpeed": 0.3,
        "attackDamage": 4.0
    }
}

Weather Assets

Weather Definition

{
    "id": "mymod:custom_weather",
    "name": "Custom Weather",
    "particles": "mymod:weather_particle",
    "skyColor": [0.5, 0.5, 0.7],
    "fogColor": [0.6, 0.6, 0.8],
    "lightLevel": 0.7,
    "sounds": {
        "ambient": "mymod:weather.custom.ambient"
    }
}

Environment Assets

Environment Definition

{
    "id": "mymod:custom_environment",
    "skybox": "mymod:textures/skybox/custom",
    "ambientLight": 0.4,
    "sunLight": 1.0,
    "fogDensity": 0.02,
    "music": "mymod:music/custom_ambient"
}

Best Practices

  1. Use namespaces - Always prefix asset IDs with your mod namespace
  2. Follow conventions - Use lowercase, underscores for asset IDs
  3. Optimize textures - Use appropriate resolutions and compression
  4. Provide fallbacks - Handle missing assets gracefully
  5. Document assets - Comment complex asset definitions
  6. Test loading - Verify all assets load correctly
  7. Version assets - Track asset changes with mod versions
  8. Reuse when possible - Reference existing assets instead of duplicating

Troubleshooting

Asset Not Found

// Check if asset exists
if (store.has(MyAsset.class, "mymod:asset_id")) {
    MyAsset asset = store.get(MyAsset.class, "mymod:asset_id");
}

Asset Loading Errors

  • Check JSON syntax
  • Verify codec definitions match JSON structure
  • Ensure all referenced assets (textures, models) exist
  • Check namespace spelling

Asset Pack Not Loading

  • Verify IncludesAssetPack: true in manifest
  • Check asset directory structure
  • Ensure JAR file includes assets