Compare audio environment attributes and Musics with the vanilla data

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Ludovic MarieandClaude Opus 5.5 committed 2026-10-10 21:52:28 +02:00
1 parent 13e434d49d
commit 6eee2e3262
4 files changed
+249

No files matched your search

@@ -0,0 +1,85 @@
//! Audio environment attributes from Horizon's `MusicTables` and
//! `AmbientTables` for `tools/test_audio_attributes_parity.py`:
//! `audio_attributes_fixture ROOT INPUT OUTPUT`.
use horizon_client::ambience::AmbientTables;
use horizon_client::music::{Music, MusicTables};
use horizon_client::sound_events::normalize;
use std::io::Write;
fn music(m: Option<&Music>) -> String {
m.map_or("-".to_owned(), |m| {
format!(
"{},{},{},{}",
normalize(&m.sound),
m.min_delay,
m.max_delay,
m.replace_current_music
)
})
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let root = std::path::Path::new(&args[1]);
let tables = MusicTables::load(root);
let ambient = AmbientTables::load(root);
let input = std::fs::read_to_string(&args[2]).expect("input");
let mut out = std::io::BufWriter::new(std::fs::File::create(&args[3]).expect("output"));
for (name, m) in [
("MENU", Music::menu()),
("CREDITS", Music::credits()),
("END_BOSS", Music::end_boss()),
("CREATIVE", Music::creative()),
("END", Music::end()),
("UNDER_WATER", Music::under_water()),
("GAME", Music::game()),
] {
writeln!(out, "{name} {}", music(Some(&m))).expect("write");
}
for line in input.lines() {
let f: Vec<&str> = line.split(' ').collect();
let (dimension, biome) = (f[0], f[1]);
let bg = tables.background(dimension, biome);
let mut s = format!(
"{line} default={} creative={} underwater={}",
music(bg.default.as_ref()),
music(bg.creative.as_ref()),
music(bg.underwater.as_ref())
);
for creative in [false, true] {
for underwater in [false, true] {
s.push_str(&format!(
" select={}",
music(bg.select(creative, underwater))
));
}
}
s.push_str(&format!(
" volume={:08x}",
tables.volume(dimension, biome).to_bits()
));
let a = ambient.get(dimension, biome);
s.push_str(&format!(
" loop={}",
a.r#loop.as_deref().map_or("-".to_owned(), normalize)
));
s.push_str(&format!(
" mood={}",
a.mood.as_ref().map_or("-".to_owned(), |m| format!(
"{},{},{},{:016x}",
normalize(&m.sound),
m.tick_delay,
m.block_search_extent,
m.offset.to_bits()
))
));
for (sound, chance) in &a.additions {
s.push_str(&format!(
" addition={},{:016x}",
normalize(sound),
chance.to_bits()
));
}
writeln!(out, "{s}").expect("write");
}
}
+16
View File
@@ -36,6 +36,22 @@ impl Music {
pub fn end_boss() -> Self {
Self::new("minecraft:music.dragon", 0, 0, true)
}
/// `Musics.CREATIVE`.
pub fn creative() -> Self {
Self::new("minecraft:music.creative", 12000, 24000, false)
}
/// `Musics.END`.
pub fn end() -> Self {
Self::new("minecraft:music.end", 6000, 24000, true)
}
/// `Musics.UNDER_WATER`.
pub fn under_water() -> Self {
Self::new("minecraft:music.under_water", 12000, 24000, false)
}
/// `Musics.GAME`.
pub fn game() -> Self {
Self::new("minecraft:music.game", 12000, 24000, false)
}
/// `Music.CODEC`.
fn parse(v: &Value) -> Option<Self> {
let sound = v["sound"].as_str()?;
@@ -0,0 +1,112 @@
package dev.horizonlab.parity;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import net.minecraft.SharedConstants;
import net.minecraft.core.*;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.RegistryLayer;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.repository.ServerPacksSource;
import net.minecraft.server.packs.resources.MultiPackResourceManager;
import net.minecraft.sounds.Music;
import net.minecraft.sounds.Musics;
import net.minecraft.tags.TagLoader;
import net.minecraft.world.attribute.*;
import net.minecraft.world.clock.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.phys.Vec3;
/**
* The audio environment attributes as the vanilla EnvironmentAttributeSystem
* evaluates them from the vanilla data pack (dimension layer, the biome's
* positional layer, the dimension timelines and weather layers):
* BACKGROUND_MUSIC with BackgroundMusic.select for every creative/underwater
* combination, MUSIC_VOLUME and AMBIENT_SOUNDS, for lines "dimension biome
* ticks"; plus the Musics constants.
*/
public final class VanillaAudioAttributesOracle {
static String music(Music m) {
return m == null ? "-" : m.sound().value().location() + "," + m.minDelay() + "," + m.maxDelay() + "," + m.replaceCurrentMusic();
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static void addBiome(EnvironmentAttributeSystem.Builder b, EnvironmentAttribute attr, Biome biome) {
b.addPositionalLayer(attr, (base, pos, w) -> biome.getAttributes().applyModifier(attr, base));
}
public static void main(String[] args) throws Exception {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
var repo = ServerPacksSource.createVanillaTrustedRepository();
repo.reload();
repo.setSelected(List.of("vanilla"));
var resources = new MultiPackResourceManager(PackType.SERVER_DATA, repo.openAllSelected());
var layers = RegistryLayer.createRegistryAccess();
var staticTags = TagLoader.loadTagsForExistingRegistries(resources, layers.getLayer(RegistryLayer.STATIC));
var context = TagLoader.buildUpdatedLookups(layers.getAccessForLoading(RegistryLayer.WORLD), staticTags);
RegistryAccess.Frozen world = RegistryDataLoader.load(resources, context, RegistryDataLoader.WORLD_REGISTRIES, Runnable::run).join();
staticTags.forEach(Registry.PendingTags::apply);
var dimensions = world.lookupOrThrow(Registries.DIMENSION_TYPE);
var biomes = world.lookupOrThrow(Registries.BIOME);
try (var w = new PrintWriter(Files.newBufferedWriter(Path.of(args[1]), StandardCharsets.UTF_8))) {
w.println("MENU " + music(Musics.MENU));
w.println("CREDITS " + music(Musics.CREDITS));
w.println("END_BOSS " + music(Musics.END_BOSS));
w.println("CREATIVE " + music(Musics.CREATIVE));
w.println("END " + music(Musics.END));
w.println("UNDER_WATER " + music(Musics.UNDER_WATER));
w.println("GAME " + music(Musics.GAME));
for (String line : Files.readAllLines(Path.of(args[0]), StandardCharsets.UTF_8)) {
String[] f = line.split(" ");
DimensionType dim = dimensions.getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, Identifier.parse(f[0]))).value();
Biome biome = biomes.getOrThrow(ResourceKey.create(Registries.BIOME, Identifier.parse(f[1]))).value();
long ticks = Long.parseLong(f[2]);
ClockManager clocks = h -> new ClockInstance() {
public long totalTicks() { return ticks; }
public float partialTick() { return 0.0F; }
public float rate() { return 1.0F; }
public boolean isPaused() { return false; }
};
var b = EnvironmentAttributeSystem.builder();
b.addConstantLayer(dim.attributes());
for (EnvironmentAttribute<?> attr : biome.getAttributes().keySet()) addBiome(b, attr, biome);
dim.timelines().forEach(t -> b.addTimelineLayer(t, clocks));
if (dim.hasSkyLight() && !dim.hasCeiling()) {
WeatherAttributes.addBuiltinLayers(b, new WeatherAttributes.WeatherAccess() {
public float rainLevel() { return 0.0F; }
public float thunderLevel() { return 0.0F; }
});
}
var system = b.build();
BackgroundMusic bg = system.getValue(EnvironmentAttributes.BACKGROUND_MUSIC, Vec3.ZERO);
StringBuilder sb = new StringBuilder(line);
sb.append(" default=").append(music(bg.defaultMusic().orElse(null)))
.append(" creative=").append(music(bg.creativeMusic().orElse(null)))
.append(" underwater=").append(music(bg.underwaterMusic().orElse(null)));
for (boolean creative : new boolean[]{false, true}) {
for (boolean underwater : new boolean[]{false, true}) {
sb.append(" select=").append(music(bg.select(creative, underwater).orElse(null)));
}
}
sb.append(String.format(" volume=%08x", Float.floatToRawIntBits(system.getValue(EnvironmentAttributes.MUSIC_VOLUME, Vec3.ZERO))));
AmbientSounds ambient = system.getValue(EnvironmentAttributes.AMBIENT_SOUNDS, Vec3.ZERO);
sb.append(" loop=").append(ambient.loop().map(s -> s.value().location().toString()).orElse("-"));
sb.append(" mood=").append(ambient.mood().map(m -> m.soundEvent().value().location() + "," + m.tickDelay() + ","
+ m.blockSearchExtent() + "," + String.format("%016x", Double.doubleToRawLongBits(m.soundPositionOffset()))).orElse("-"));
for (AmbientAdditionsSettings a : ambient.additions()) {
sb.append(" addition=").append(a.soundEvent().value().location()).append(String.format(",%016x", Double.doubleToRawLongBits(a.tickChance())));
}
w.println(sb);
}
}
System.exit(0);
}
}
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Compare the audio environment attributes with the vanilla 26.3 data pack.
The oracle evaluates vanilla's `EnvironmentAttributeSystem` (dimension layer,
biome positional layer, dimension timelines, weather layers) for every
dimension type and biome and prints `BACKGROUND_MUSIC` (all three tracks and
`BackgroundMusic.select` for each creative/underwater combination),
`MUSIC_VOLUME` and `AMBIENT_SOUNDS` (loop, mood, additions), plus the
`Musics` constants. Horizon's `MusicTables` and `AmbientTables` answer the same.
"""
try:
from . import audio_harness as h
except ImportError:
import audio_harness as h
def fixtures():
data = h.RESOURCES / 'data/minecraft'
biomes = sorted('minecraft:' + p.stem for p in (data / 'worldgen/biome').glob('*.json'))
dimensions = sorted('minecraft:' + p.stem for p in (data / 'dimension_type').glob('*.json'))
return [f'{d} {b} {t}' for d in dimensions for b in biomes for t in (0, 13000)]
def main():
lines = fixtures()
matched, report = h.run_harness(
'audio-attributes', 'VanillaAudioAttributesOracle', 'audio_attributes_fixture', ''.join(l + '\n' for l in lines),
extra_sources=[h.lab.ROOT / 'crates/horizon-client/src/music.rs', h.lab.ROOT / 'crates/horizon-client/src/ambience.rs'],
rust_args=[h.RESOURCES],
scope='Vanilla EnvironmentAttributeSystem values of audio/background_music (with BackgroundMusic.select), audio/music_volume and '
'audio/ambient_sounds for every dimension type x biome at two times of day, and the Musics constants.')
print(f'{matched:,} audio attribute lines match the pinned data ({len(lines):,} cases): {report}', flush=True)
if __name__ == '__main__':
main()