mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Read the axe, hoe and shovel block transformers from the data pack
A new block transformer module evaluates the data pack's rule based state providers the way BlockTransformer.transformBlock does (matching blocks and tags, all-of, copy properties) and derives the waxed twin of a copper block from the wax-off transforms. A harness compares every transformer, block state, face and block above with vanilla's own providers and with HoneycombItem.getWaxed. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
89630c80fe
commit
d900b2a8e6
5 files changed
+532
No files matched your search
@@ -0,0 +1,94 @@
|
||||
//! Block transformer lookups for tools/test_block_transformer_parity.py: reads
|
||||
//! "transformer TAB face TAB clicked TAB above" lines (states as
|
||||
//! "name|prop=value,...") and prints the oracle's result lines.
|
||||
//!
|
||||
//! usage: block_transform_fixture RESOURCES INPUT OUTPUT
|
||||
use horizon_client::{
|
||||
assets::Assets,
|
||||
block_transform::{Around, Transformers},
|
||||
items::Items,
|
||||
};
|
||||
use horizon_world::chunk::BlockState;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
fn parse(text: &str) -> BlockState {
|
||||
let (name, props) = text.split_once('|').unwrap_or((text, ""));
|
||||
BlockState {
|
||||
name: name.to_owned(),
|
||||
properties: props
|
||||
.split(',')
|
||||
.filter(|p| !p.is_empty())
|
||||
.filter_map(|p| p.split_once('='))
|
||||
.map(|(k, v)| (k.to_owned(), v.to_owned()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn describe(state: &BlockState) -> String {
|
||||
let props: Vec<String> = state
|
||||
.properties
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{k}={v}"))
|
||||
.collect();
|
||||
format!("{}|{}", state.name, props.join(","))
|
||||
}
|
||||
|
||||
struct Column {
|
||||
clicked: BlockState,
|
||||
above: BlockState,
|
||||
}
|
||||
|
||||
impl Around for Column {
|
||||
fn state(&self, offset: [i32; 3]) -> BlockState {
|
||||
match offset {
|
||||
[0, 0, 0] => self.clicked.clone(),
|
||||
[0, 1, 0] => self.above.clone(),
|
||||
_ => BlockState {
|
||||
name: "minecraft:stone".into(),
|
||||
properties: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> horizon_client::Result<()> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let root = Path::new(args.get(1).ok_or("usage: block_transform_fixture RESOURCES INPUT OUTPUT")?);
|
||||
let input = fs::read_to_string(args.get(2).ok_or("missing INPUT")?)?;
|
||||
let items = Items::catalog(root)?;
|
||||
let assets = Assets::load(root)?;
|
||||
let transformers = Transformers::load(root, assets.registry)?;
|
||||
let mut out = String::new();
|
||||
for line in input.lines() {
|
||||
let f: Vec<&str> = line.split('\t').collect();
|
||||
let around = Column {
|
||||
clicked: parse(f[2]),
|
||||
above: parse(f[3]),
|
||||
};
|
||||
if f[0] == "honeycomb" {
|
||||
let waxed = transformers.waxed(assets.registry, &around.clicked);
|
||||
out += &format!("{}\n", waxed.map_or("none".to_owned(), |s| describe(&s)));
|
||||
continue;
|
||||
}
|
||||
let id = format!("minecraft:{}", f[0]);
|
||||
match transformers.apply(&items, assets.registry, &id, f[1], &around) {
|
||||
None => out += "none\n",
|
||||
Some((index, t, state)) => {
|
||||
out += &format!(
|
||||
"{index}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n",
|
||||
describe(&state),
|
||||
t.sound,
|
||||
t.particle,
|
||||
t.loot.as_deref().unwrap_or("-"),
|
||||
t.drop_strategy,
|
||||
t.update_from_neighbors,
|
||||
t.transform_type,
|
||||
t.consume_on_use,
|
||||
t.damage
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
fs::write(args.get(3).ok_or("missing OUTPUT")?, out)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
//! The `minecraft:block_transformer` registry (axe, hoe, shovel) that
|
||||
//! `BlockTransformer.transformBlock` reads: each transform's rule based state
|
||||
//! provider is evaluated against the clicked block and the block above.
|
||||
//! `tools/test_block_transformer_parity.py` compares every block state with
|
||||
//! vanilla.
|
||||
use crate::{Result, items::Items};
|
||||
use horizon_world::{chunk::BlockState, registry::BlockRegistry};
|
||||
use serde_json::Value;
|
||||
use std::{collections::HashMap, fs, path::Path};
|
||||
|
||||
enum Predicate {
|
||||
Blocks(Vec<String>),
|
||||
Tag { tag: String, offset: [i32; 3] },
|
||||
AllOf(Vec<Predicate>),
|
||||
AnyOf(Vec<Predicate>),
|
||||
}
|
||||
|
||||
enum Provider {
|
||||
Rules(Vec<(Predicate, Provider)>),
|
||||
State(BlockState),
|
||||
/// `CopyPropertiesProvider`: the source with the clicked block's properties.
|
||||
Copy(Box<Provider>),
|
||||
}
|
||||
|
||||
/// One `BlockTransformData`.
|
||||
pub struct Transform {
|
||||
provider: Provider,
|
||||
pub disallowed_faces: Vec<String>,
|
||||
pub sound: String,
|
||||
pub particle: String,
|
||||
pub loot: Option<String>,
|
||||
pub drop_strategy: String,
|
||||
pub update_from_neighbors: bool,
|
||||
pub transform_type: String,
|
||||
pub consume_on_use: bool,
|
||||
pub damage: u32,
|
||||
}
|
||||
|
||||
pub struct Transformers {
|
||||
transformers: HashMap<String, Vec<Transform>>,
|
||||
}
|
||||
|
||||
/// What the transformer reads of the world: the block state at an offset from
|
||||
/// the clicked position.
|
||||
pub trait Around {
|
||||
fn state(&self, offset: [i32; 3]) -> BlockState;
|
||||
}
|
||||
|
||||
fn state_of(value: &Value, registry: &BlockRegistry) -> Option<BlockState> {
|
||||
let name = value["id"].as_str().or_else(|| value["Name"].as_str())?;
|
||||
let default = registry.default_state(name)?;
|
||||
let mut state = registry.state(default)?.clone();
|
||||
for (k, v) in value["properties"].as_object().into_iter().flatten() {
|
||||
state.properties.insert(k.clone(), v.as_str()?.to_owned());
|
||||
}
|
||||
Some(state)
|
||||
}
|
||||
|
||||
fn predicate(value: &Value) -> Option<Predicate> {
|
||||
Some(match value["type"].as_str()? {
|
||||
"minecraft:matching_blocks" => Predicate::Blocks(match &value["blocks"] {
|
||||
Value::String(s) => vec![s.clone()],
|
||||
Value::Array(a) => a.iter().filter_map(|b| b.as_str().map(str::to_owned)).collect(),
|
||||
_ => return None,
|
||||
}),
|
||||
"minecraft:matching_block_tag" => {
|
||||
let o = value["offset"].as_array();
|
||||
let at = |i: usize| o.and_then(|o| o.get(i)).and_then(Value::as_i64).unwrap_or(0) as i32;
|
||||
Predicate::Tag {
|
||||
tag: value["tag"].as_str()?.to_owned(),
|
||||
offset: [at(0), at(1), at(2)],
|
||||
}
|
||||
}
|
||||
"minecraft:all_of" => Predicate::AllOf(
|
||||
value["predicates"]
|
||||
.as_array()?
|
||||
.iter()
|
||||
.map(predicate)
|
||||
.collect::<Option<_>>()?,
|
||||
),
|
||||
"minecraft:any_of" => Predicate::AnyOf(
|
||||
value["predicates"]
|
||||
.as_array()?
|
||||
.iter()
|
||||
.map(predicate)
|
||||
.collect::<Option<_>>()?,
|
||||
),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn provider(value: &Value, registry: &BlockRegistry) -> Option<Provider> {
|
||||
match value["type"].as_str() {
|
||||
Some("minecraft:rule_based") => {
|
||||
let mut rules = Vec::new();
|
||||
for rule in value["rules"].as_array()? {
|
||||
rules.push((predicate(&rule["if_true"])?, provider(&rule["then"], registry)?));
|
||||
}
|
||||
Some(Provider::Rules(rules))
|
||||
}
|
||||
Some("minecraft:copy_properties") => Some(Provider::Copy(Box::new(provider(
|
||||
&value["source"],
|
||||
registry,
|
||||
)?))),
|
||||
_ => state_of(value, registry).map(Provider::State),
|
||||
}
|
||||
}
|
||||
|
||||
/// `BlockState.withPropertiesOf`: the properties both blocks have.
|
||||
fn with_properties_of(
|
||||
registry: &BlockRegistry,
|
||||
mut state: BlockState,
|
||||
old: &BlockState,
|
||||
) -> Option<BlockState> {
|
||||
let defaults = registry
|
||||
.default_state(&state.name)
|
||||
.and_then(|id| registry.state(id))?;
|
||||
for (k, v) in &old.properties {
|
||||
if defaults.properties.contains_key(k) {
|
||||
let mut candidate = state.clone();
|
||||
candidate.properties.insert(k.clone(), v.clone());
|
||||
if registry.resolve(&candidate).is_ok() {
|
||||
state = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(state)
|
||||
}
|
||||
|
||||
impl Predicate {
|
||||
fn test(&self, items: &Items, around: &dyn Around) -> bool {
|
||||
match self {
|
||||
Self::Blocks(names) => {
|
||||
let state = around.state([0, 0, 0]);
|
||||
names.iter().any(|n| *n == state.name)
|
||||
}
|
||||
Self::Tag { tag, offset } => {
|
||||
let state = around.state(*offset);
|
||||
items
|
||||
.block_tags
|
||||
.get(tag)
|
||||
.is_some_and(|members| members.contains(&state.name))
|
||||
}
|
||||
Self::AllOf(all) => all.iter().all(|p| p.test(items, around)),
|
||||
Self::AnyOf(any) => any.iter().any(|p| p.test(items, around)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Provider {
|
||||
fn state(
|
||||
&self,
|
||||
items: &Items,
|
||||
registry: &BlockRegistry,
|
||||
around: &dyn Around,
|
||||
) -> Option<BlockState> {
|
||||
match self {
|
||||
Self::Rules(rules) => rules
|
||||
.iter()
|
||||
.find(|(p, _)| p.test(items, around))
|
||||
.and_then(|(_, then)| then.state(items, registry, around)),
|
||||
Self::State(state) => Some(state.clone()),
|
||||
Self::Copy(source) => {
|
||||
let state = source.state(items, registry, around)?;
|
||||
with_properties_of(registry, state, &around.state([0, 0, 0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Transformers {
|
||||
/// Reads `data/minecraft/block_transformer/*.json`.
|
||||
pub fn load(root: &Path, registry: &BlockRegistry) -> Result<Self> {
|
||||
let mut transformers = HashMap::new();
|
||||
let dir = root.join("data/minecraft/block_transformer");
|
||||
if dir.exists() {
|
||||
for entry in fs::read_dir(&dir)?.flatten() {
|
||||
let path = entry.path();
|
||||
let Some(stem) = path.file_stem().and_then(|s| s.to_str()) else {
|
||||
continue;
|
||||
};
|
||||
let list: Value = serde_json::from_slice(&fs::read(&path)?)?;
|
||||
let mut transforms = Vec::new();
|
||||
for t in list.as_array().into_iter().flatten() {
|
||||
let text = |k: &str, d: &str| t[k].as_str().unwrap_or(d).to_owned();
|
||||
let Some(provider) = provider(&t["block_state_provider"], registry) else {
|
||||
continue;
|
||||
};
|
||||
transforms.push(Transform {
|
||||
provider,
|
||||
disallowed_faces: t["disallowed_faces"]
|
||||
.as_array()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|f| f.as_str().map(str::to_owned))
|
||||
.collect(),
|
||||
sound: text("sound", "minecraft:empty"),
|
||||
particle: text("particle", "none"),
|
||||
loot: t["loot"].as_str().map(str::to_owned),
|
||||
drop_strategy: text("drop_strategy", "from_middle"),
|
||||
update_from_neighbors: t["update_from_neighbors"].as_bool().unwrap_or(true),
|
||||
transform_type: text("transform_type", "single_block"),
|
||||
consume_on_use: t["consume_on_use"].as_bool().unwrap_or(true),
|
||||
damage: t["item_damage_per_use"].as_u64().unwrap_or(0) as u32,
|
||||
});
|
||||
}
|
||||
transformers.insert(format!("minecraft:{stem}"), transforms);
|
||||
}
|
||||
}
|
||||
Ok(Self { transformers })
|
||||
}
|
||||
|
||||
/// `BlockTransformer.transformBlock` without the world writes: the first
|
||||
/// transform that allows the clicked face and provides a state, with its
|
||||
/// index.
|
||||
pub fn apply<'a>(
|
||||
&'a self,
|
||||
items: &Items,
|
||||
registry: &BlockRegistry,
|
||||
id: &str,
|
||||
face: &str,
|
||||
around: &dyn Around,
|
||||
) -> Option<(usize, &'a Transform, BlockState)> {
|
||||
self.transformers
|
||||
.get(id)?
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, t)| !t.disallowed_faces.iter().any(|f| f == face))
|
||||
.find_map(|(i, t)| Some((i, t, t.provider.state(items, registry, around)?)))
|
||||
}
|
||||
|
||||
/// `HoneycombItem.getWaxed`: the waxed twin of a copper block, found from
|
||||
/// the axe's wax-off transforms (`WAX_OFF_BY_BLOCK` is the inverse of
|
||||
/// `WAXABLES`).
|
||||
pub fn waxed(&self, registry: &BlockRegistry, old: &BlockState) -> Option<BlockState> {
|
||||
for transform in self.transformers.get("minecraft:axe")? {
|
||||
if transform.particle != "wax_off" {
|
||||
continue;
|
||||
}
|
||||
let Provider::Rules(rules) = &transform.provider else {
|
||||
continue;
|
||||
};
|
||||
for (predicate, then) in rules {
|
||||
if let (Predicate::Blocks(names), Provider::Copy(source)) = (predicate, then)
|
||||
&& let Provider::State(unwaxed) = &**source
|
||||
&& unwaxed.name == old.name
|
||||
&& let Some(waxed) = names.first()
|
||||
{
|
||||
let target = registry
|
||||
.default_state(waxed)
|
||||
.and_then(|id| registry.state(id))?
|
||||
.clone();
|
||||
return with_properties_of(registry, target, old);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ pub mod audio;
|
||||
pub mod banner;
|
||||
pub mod biome;
|
||||
pub mod block_entity_render;
|
||||
pub mod block_transform;
|
||||
pub mod blocks;
|
||||
pub mod bossbar;
|
||||
pub mod brewing;
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package dev.horizonlab.parity;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.core.component.BlockTransformer;
|
||||
|
||||
/**
|
||||
* Original oracle: the data-driven axe/hoe/shovel BlockTransformer entries.
|
||||
* "dump" lists every block state; a case line "transformer TAB face TAB
|
||||
* clicked TAB above" (states as "name|prop=value,...") prints the first
|
||||
* transform that yields a state and its settings, as BlockTransformer.transformBlock
|
||||
* would pick it.
|
||||
*/
|
||||
public final class VanillaBlockTransformerOracle {
|
||||
static String describe(BlockState state) {
|
||||
StringBuilder out = new StringBuilder(BuiltInRegistries.BLOCK.getKey(state.getBlock()).toString()).append('|');
|
||||
List<Property<?>> props = new ArrayList<>(state.getProperties());
|
||||
props.sort(Comparator.comparing(Property::getName));
|
||||
boolean first = true;
|
||||
for (Property<?> p : props) {
|
||||
out.append(first ? "" : ",").append(p.getName()).append('=').append(name(state, p));
|
||||
first = false;
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
static String name(BlockState state, Property p) {
|
||||
return p.getName(state.getValue(p));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
static BlockState parse(String text) {
|
||||
String[] parts = text.split("\\|", -1);
|
||||
Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(parts[0]));
|
||||
BlockState state = block.defaultBlockState();
|
||||
if (!parts[1].isEmpty()) {
|
||||
for (String kv : parts[1].split(",")) {
|
||||
String[] pair = kv.split("=");
|
||||
Property p = block.getStateDefinition().getProperty(pair[0]);
|
||||
state = state.setValue(p, (Comparable) p.getValue(pair[1]).orElseThrow());
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
static LevelAccessor level(BlockState clicked, BlockState above) {
|
||||
return (LevelAccessor) Proxy.newProxyInstance(LevelAccessor.class.getClassLoader(), new Class<?>[]{LevelAccessor.class}, (proxy, method, args) -> {
|
||||
if (method.getName().equals("getBlockState")) {
|
||||
BlockPos pos = (BlockPos) args[0];
|
||||
return pos.getY() == 0 ? clicked : pos.getY() == 1 && pos.getX() == 0 && pos.getZ() == 0 ? above : net.minecraft.world.level.block.Blocks.STONE.defaultBlockState();
|
||||
}
|
||||
throw new UnsupportedOperationException(method.getName());
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ParityData.init();
|
||||
if (args[0].equals("dump")) {
|
||||
try (var out = new PrintWriter(Files.newBufferedWriter(Path.of(args[1]), StandardCharsets.UTF_8))) {
|
||||
for (Block block : BuiltInRegistries.BLOCK) {
|
||||
for (BlockState state : block.getStateDefinition().getPossibleStates()) out.println(describe(state));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
var registry = ParityData.access.lookupOrThrow(Registries.BLOCK_TRANSFORMER);
|
||||
try (var out = new PrintWriter(Files.newBufferedWriter(Path.of(args[1]), StandardCharsets.UTF_8));
|
||||
var lines = Files.lines(Path.of(args[0]))) {
|
||||
for (String line : (Iterable<String>) lines::iterator) {
|
||||
String[] f = line.split("\t", -1);
|
||||
try {
|
||||
if (f[0].equals("honeycomb")) {
|
||||
out.println(net.minecraft.world.item.HoneycombItem.getWaxed(parse(f[2])).map(VanillaBlockTransformerOracle::describe).orElse("none"));
|
||||
continue;
|
||||
}
|
||||
var transformer = registry.getOrThrow(ResourceKey.create(Registries.BLOCK_TRANSFORMER, Identifier.parse(f[0]))).value();
|
||||
Direction face = Direction.byName(f[1]);
|
||||
BlockState clicked = parse(f[2]);
|
||||
LevelAccessor level = level(clicked, parse(f[3]));
|
||||
String result = "none";
|
||||
int index = 0;
|
||||
for (var data : transformer.transforms()) {
|
||||
if (!data.disallowedFaces().contains(face)) {
|
||||
BlockState state = data.blockStateProvider().value().getOptionalState(level, RandomSource.create(0L), BlockPos.ZERO);
|
||||
if (state != null) {
|
||||
result = index + "\t" + describe(state) + "\t" + BuiltInRegistries.SOUND_EVENT.getKey(data.sound().value())
|
||||
+ "\t" + data.particle().name().toLowerCase() + "\t"
|
||||
+ data.loot().map(k -> k.identifier().toString()).orElse("-") + "\t" + data.dropStrategy().name().toLowerCase()
|
||||
+ "\t" + data.updateFromNeighbors() + "\t" + data.transformType().name().toLowerCase() + "\t" + data.consumeOnUse()
|
||||
+ "\t" + data.itemDamagePerUse();
|
||||
break;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
out.println(result);
|
||||
} catch (Throwable e) {
|
||||
out.println("error " + e.getClass().getSimpleName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare the data-driven axe/hoe/shovel block transformers with vanilla.
|
||||
|
||||
The Java oracle (VanillaBlockTransformerOracle) evaluates the registry's
|
||||
BlockStateProviders on a stand-in level; the Rust example
|
||||
(block_transform_fixture) evaluates horizon_client::block_transform. Cases are
|
||||
every block state any transformer mentions (plus a random sample of all states)
|
||||
under each transformer, with a few blocks above and three clicked faces.
|
||||
"""
|
||||
import argparse
|
||||
import random
|
||||
try:
|
||||
from . import interaction_lab as lib, lab
|
||||
except ImportError:
|
||||
import interaction_lab as lib
|
||||
import lab
|
||||
|
||||
ABOVE = ['minecraft:air|', 'minecraft:cave_air|', 'minecraft:stone|', 'minecraft:water|level=0', 'minecraft:oak_slab|type=bottom,waterlogged=false']
|
||||
FACES = ['up', 'down', 'north']
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--seed', type=int, default=7)
|
||||
parser.add_argument('--sample', type=int, default=3000)
|
||||
parser.add_argument('--limit', type=int, default=4)
|
||||
args = parser.parse_args()
|
||||
work = lib.work_dir('block-transformer-parity')
|
||||
vanilla = lib.Vanilla(work)
|
||||
vanilla.compile('ParityData', 'VanillaBlockTransformerOracle')
|
||||
vanilla.run('VanillaBlockTransformerOracle', 'dump', work / 'states.txt')
|
||||
states = (work / 'states.txt').read_text().splitlines()
|
||||
mentioned = set()
|
||||
for path in (lib.RESOURCES / 'data/minecraft/block_transformer').glob('*.json'):
|
||||
text = path.read_text()
|
||||
for name in set(__import__('re').findall(r'"minecraft:([a-z_0-9]+)"', text)):
|
||||
mentioned.add('minecraft:' + name)
|
||||
tags = set()
|
||||
for tag in ['turns_into_farmland', 'turns_into_dirt_path', 'air']:
|
||||
for name in __import__('json').loads((lib.RESOURCES / 'data/minecraft/tags/block' / (tag + '.json')).read_text())['values']:
|
||||
if not name.startswith('#'):
|
||||
tags.add(name)
|
||||
rng = random.Random(args.seed)
|
||||
chosen = [s for s in states if s.split('|')[0] in mentioned or s.split('|')[0] in tags or 'copper' in s or 'lightning_rod' in s]
|
||||
chosen += rng.sample(states, args.sample)
|
||||
cases = []
|
||||
for transformer in ['axe', 'hoe', 'shovel', 'honeycomb']:
|
||||
for state in chosen:
|
||||
for face in FACES:
|
||||
cases.append('\t'.join([transformer, face, state, rng.choice(ABOVE)]))
|
||||
(work / 'cases.txt').write_text(''.join(c + '\n' for c in cases))
|
||||
vanilla.run('VanillaBlockTransformerOracle', work / 'cases.txt', work / 'native.txt')
|
||||
lib.cargo_example('horizon-client', 'block_transform_fixture', lib.RESOURCES, work / 'cases.txt', work / 'rust.txt')
|
||||
total = lib.compare(cases, work / 'native.txt', work / 'rust.txt',
|
||||
lambda case, line: case.split('\t')[0] + ' ' + line.split('\t')[0][:12], None, args.limit)
|
||||
raise SystemExit(1 if total else 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in new issue
Block a user