9.9 KiB
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:
- Core game assets
- Plugin asset packs (when
IncludesAssetPack: truein 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
- Set
IncludesAssetPack: truein yourmanifest.json - Create an
assets/directory in your JAR - 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
- Use namespaces - Always prefix asset IDs with your mod namespace
- Follow conventions - Use lowercase, underscores for asset IDs
- Optimize textures - Use appropriate resolutions and compression
- Provide fallbacks - Handle missing assets gracefully
- Document assets - Comment complex asset definitions
- Test loading - Verify all assets load correctly
- Version assets - Track asset changes with mod versions
- 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: truein manifest - Check asset directory structure
- Ensure JAR file includes assets