Add partners, Immersive Weathering block growth (#231)

* Add partners: Immersive Weathering block growth

* Fix block state and modded block IDs

* Add categories

* Add custom rule tests

* Add missing translation
This commit is contained in:
Misode
2022-05-14 02:41:01 +02:00
committed by GitHub
parent b2d7ee49c6
commit 114164c740
19 changed files with 357 additions and 18 deletions
+2 -1
View File
@@ -4,7 +4,7 @@ import '../styles/global.css'
import '../styles/nodes.css'
import { Analytics } from './Analytics'
import { Header } from './components'
import { Category, Changelog, Generator, Guide, Guides, Home, Project, Sounds, Versions } from './pages'
import { Category, Changelog, Generator, Guide, Guides, Home, Partners, Project, Sounds, Versions } from './pages'
import { cleanUrl } from './Utils'
export function App() {
@@ -20,6 +20,7 @@ export function App() {
<Home path="/" />
<Category path="/worldgen" category="worldgen" />
<Category path="/assets" category="assets" />
<Partners path="/partners" />
<Sounds path="/sounds" />
<Changelog path="/changelog" />
<Versions path="/versions" />
+1 -1
View File
@@ -26,7 +26,7 @@ export function Header() {
{config.generators
.filter(g => g.category === gen?.category && checkVersion(version, g.minVersion))
.map(g =>
<Btn label={locale(g.id)} active={g.id === gen.id} onClick={() => route(cleanUrl(g.url))} />
<Btn label={locale(g.partner ? `partner.${g.partner}.${g.id}` : g.id)} active={g.id === gen.id} onClick={() => route(cleanUrl(g.url))} />
)}
</BtnMenu>}
</div>
+5 -1
View File
@@ -45,7 +45,11 @@ async function loadLocale(language: string) {
const data = await import(`../../locales/${language}.json`)
const schema = langConfig.schemas !== false
&& await import(`../../../node_modules/@mcschema/locales/src/${language}.json`)
Locales[language] = { ...data.default, ...schema.default }
let partners = { default: {} }
if (language === 'en') {
partners = await import('../partners/locales/en.json')
}
Locales[language] = { ...data.default, ...schema.default, ...partners.default }
}
export function useLocale() {
+3
View File
@@ -124,6 +124,9 @@ export function ProjectProvider({ children }: { children: ComponentChildren }) {
export function getFilePath(file: ProjectFile) {
const [namespace, id] = file.id.includes(':') ? file.id.split(':') : ['minecraft', file.id]
if (file.type === 'pack_mcmeta') {
return 'pack.mcmeta'
}
const gen = config.generators.find(g => g.id === file.type)
if (!gen) {
throw new Error(`Cannot find generator of type ${file.type}`)
+3 -3
View File
@@ -40,7 +40,7 @@ export function Generator({}: Props) {
.reverse()
}, [gen.minVersion, gen.maxVersion])
useTitle(locale('title.generator', locale(gen.id)), allowedVersions)
useTitle(locale('title.generator', locale(gen.partner ? `partner.${gen.partner}.${gen.id}` : gen.id)), allowedVersions)
if (!checkVersion(version, gen.minVersion)) {
setError(`The minimum version for this generator is ${gen.minVersion}`)
@@ -210,7 +210,7 @@ export function Generator({}: Props) {
const [presets, setPresets] = useState<string[]>([])
useEffect(() => {
getCollections(version).then(collections => {
setPresets(collections.get(gen.id).map(p => p.slice(10)))
setPresets(collections.get(gen.id).map(p => p.startsWith('minecraft:') ? p.slice(10) : p))
})
.catch(e => { console.error(e); setError(e) })
}, [version, gen.id])
@@ -341,7 +341,7 @@ export function Generator({}: Props) {
return <>
<main class={previewShown ? 'has-preview' : ''}>
<Ad id="data-pack-generator" type="text" />
{!gen.partner && <Ad id="data-pack-generator" type="text" />}
<div class="controls">
<div class={`project-controls ${file && 'has-file'}`}>
<div class="btn-row">
+1
View File
@@ -22,6 +22,7 @@ export function Home({}: Props) {
<ToolCard title={locale(g.id)} link={cleanUrl(g.url)} />
)}
</ToolCard>
<ToolCard title="Partners" link="/partners/" />
<ToolCard title="Report Inspector" icon="report"
link="https://misode.github.io/report/"
desc="Analyse your performance reports" />
+27
View File
@@ -0,0 +1,27 @@
import config from '../../config.json'
import { ToolCard } from '../components'
import { useLocale, useTitle } from '../contexts'
import { cleanUrl } from '../Utils'
const partners = [...new Set(config.generators
.filter(g => g.partner !== undefined)
.map(g => g.partner as string)
)]
interface Props {
path?: string,
}
export function Partners({}: Props) {
const { locale } = useLocale()
useTitle(locale('title.partners'))
return <main>
<div class="category">
{partners.map(p => <ToolCard title={locale(`partner.${p}`)}>
{config.generators.filter(g => g.partner === p).map(g =>
<ToolCard title={locale(`partner.${p}.${g.id}`)} link={cleanUrl(g.url)} />
)}
</ToolCard>)}
</div>
</main>
}
+1
View File
@@ -4,6 +4,7 @@ export * from './Generator'
export * from './Guide'
export * from './Guides'
export * from './Home'
export * from './Partners'
export * from './Project'
export * from './Sounds'
export * from './Versions'
+210
View File
@@ -0,0 +1,210 @@
import type { CollectionRegistry, ResourceType, SchemaRegistry } from '@mcschema/core'
import { BooleanNode, Case, ChoiceNode, ListNode, MapNode, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core'
const ID = 'immersive_weathering'
export function initImmersiveWeathering(schemas: SchemaRegistry, collections: CollectionRegistry) {
const Reference = RawReference.bind(undefined, schemas)
const StringNode = RawStringNode.bind(undefined, collections)
const Tag = (id: Exclude<ResourceType, `$tag/${string}`>) => ChoiceNode([
{
type: 'string',
node: StringNode({ validator: 'resource', params: { pool: id, allowTag: true } }),
change: (v: unknown) => {
if (Array.isArray(v) && typeof v[0] === 'string' && !v[0].startsWith('#')) {
return v[0]
}
return undefined
},
},
{
type: 'list',
node: ListNode(
StringNode({ validator: 'resource', params: { pool: id } })
),
change: (v: unknown) => {
if (typeof v === 'string' && !v.startsWith('#')) {
return [v]
}
return []
},
},
], { choiceContext: 'tag' })
schemas.register(`${ID}:block_growth`, ObjectNode({
area_condition: Reference(`${ID}:area_condition`),
position_predicates: Opt(ListNode(
Reference(`${ID}:position_test`)
)),
growth_chance: NumberNode({ min: 0, max: 1 }),
growth_for_face: ListNode(
ObjectNode({
direction: Opt(StringNode({ enum: 'direction' })),
weight: Opt(NumberNode({ integer: true })),
growth: ListNode(
ObjectNode({
data: Reference(`${ID}:block_pair`),
weight: NumberNode({ integer: true }),
})
),
}, { category: 'pool' })
),
owners: ListNode(
StringNode({ validator: 'resource', params: { pool: 'block' } })
),
replacing_target: Reference(`${ID}:rule_test`),
target_self: Opt(BooleanNode()),
destroy_target: Opt(BooleanNode()),
}, { context: `${ID}.block_growth` }))
schemas.register(`${ID}:area_condition`, ObjectNode({
type: StringNode({ enum: ['generate_if_not_too_many', 'neighbor_based_generation'] }),
[Switch]: [{ push: 'type' }],
[Case]: {
generate_if_not_too_many: {
radiusX: NumberNode({ integer: true }),
radiusY: NumberNode({ integer: true }),
radiusZ: NumberNode({ integer: true }),
requiredAmount: NumberNode({ integer: true }),
yOffset: Opt(NumberNode({ integer: true })),
must_have: Opt(Reference(`${ID}:rule_test`)),
must_not_have: Opt(Reference(`${ID}:rule_test`)),
includes: Opt(Tag('block')),
},
neighbor_based_generation: {
must_have: Reference(`${ID}:rule_test`),
must_not_have: Opt(Reference(`${ID}:rule_test`)),
required_amount: Opt(NumberNode({ integer: true })),
directions: ListNode(
StringNode({ enum: 'direction' })
),
},
},
}, { context: `${ID}.area_condition` }))
schemas.register(`${ID}:block_pair`, ObjectNode({
block: Reference(`${ID}:block_state`),
above_block: Opt(Reference(`${ID}:block_state`)),
}, { context: `${ID}.block_pair` }))
schemas.register(`${ID}:block_state`, ObjectNode({
Name: StringNode({ validator: 'resource', params: { pool: 'block' } }),
Properties: Opt(MapNode(
StringNode(),
StringNode(),
)),
}, { context: 'block_state' }))
schemas.register(`${ID}:position_test`, ObjectNode({
predicate_type: StringNode({ enum: ['biome_match', 'day_test', 'nand', 'precipitation_test', 'temperature_range'] }),
[Switch]: [{ push: 'predicate_type' }],
[Case]: {
biome_match: {
biomes: Tag('$worldgen/biome'),
},
day_test: {
day: BooleanNode(),
},
nand: {
predicates: ListNode(
Reference(`${ID}:position_test`)
),
},
precipitation_test: {
precipitation: StringNode({ enum: ['none', 'rain', 'snow']}),
},
temperature_range: {
min: NumberNode(),
max: NumberNode(),
use_local_pos: Opt(BooleanNode()),
},
},
}, { context: `${ID}.position_test`, category: 'predicate' }))
collections.register(`${ID}:rule_test`, [
...collections.get('rule_test'),
'immersive_weathering:block_set_match',
'immersive_weathering:fluid_match',
'immersive_weathering:tree_log',
])
schemas.register(`${ID}:rule_test`, ObjectNode({
predicate_type: StringNode({ validator: 'resource', params: { pool: `${ID}:rule_test` as any } }),
[Switch]: [{ push: 'predicate_type' }],
[Case]: {
'minecraft:block_match': {
block: StringNode({ validator: 'resource', params: { pool: 'block' } }),
},
'minecraft:blockstate_match': {
block_state: Reference('block_state'),
},
'minecraft:random_block_match': {
block: StringNode({ validator: 'resource', params: { pool: 'block' } }),
probability: NumberNode({ min: 0, max: 1 }),
},
'minecraft:random_blockstate_match': {
block_state: Reference('block_state'),
probability: NumberNode({ min: 0, max: 1 }),
},
'minecraft:tag_match': {
tag: StringNode({ validator: 'resource', params: { pool: '$tag/block' }}),
},
'immersive_weathering:block_set_match': {
blocks: Tag('block'),
probability: Opt(NumberNode({ min: 0, max: 1 })),
},
'immersive_weathering:fluid_match': {
fluid: StringNode({ validator: 'resource', params: { pool: 'fluid' } }),
},
},
}, { context: 'rule_test', disableSwitchContext: true }))
collections.register('block_growth', [
'immersive_weathering:brain_coral',
'immersive_weathering:bubble_coral',
'immersive_weathering:cracked_mud_rivers',
'immersive_weathering:crimson_nylium',
'immersive_weathering:cryosol',
'immersive_weathering:farmland_rare_weeds',
'immersive_weathering:farmland_weeds',
'immersive_weathering:fire_coral',
'immersive_weathering:fire_soot',
'immersive_weathering:fluvisol',
'immersive_weathering:grass_base',
'immersive_weathering:grass_block_badlands',
'immersive_weathering:grass_block_bamboo_jungle',
'immersive_weathering:grass_block_birch_forest',
'immersive_weathering:grass_block_dark_forest',
'immersive_weathering:grass_block_flower_forest',
'immersive_weathering:grass_block_forest',
'immersive_weathering:grass_block_jungle',
'immersive_weathering:grass_block_lush_caves',
'immersive_weathering:grass_block_old_growth_spruce',
'immersive_weathering:grass_block_plains',
'immersive_weathering:grass_block_sunflower_plains',
'immersive_weathering:grass_block_swamp',
'immersive_weathering:grass_block_taiga',
'immersive_weathering:grass_block_wooded_badlands',
'immersive_weathering:hanging_roots',
'immersive_weathering:horn_coral',
'immersive_weathering:humus',
'immersive_weathering:icicle_growth',
'immersive_weathering:large_fern',
'immersive_weathering:magma',
'immersive_weathering:mycelium',
'immersive_weathering:podzol',
'immersive_weathering:red_sand_weathering',
'immersive_weathering:rooted_dirt',
'immersive_weathering:rooted_grass',
'immersive_weathering:sand_weathering',
'immersive_weathering:sapling',
'immersive_weathering:sapling_nether',
'immersive_weathering:silt',
'immersive_weathering:tall_grass',
'immersive_weathering:tall_seagrass',
'immersive_weathering:tube_coral',
'immersive_weathering:vertisol',
'immersive_weathering:warped_nylium',
])
}
+8
View File
@@ -0,0 +1,8 @@
import type { CollectionRegistry, SchemaRegistry } from '@mcschema/core'
import { initImmersiveWeathering } from './ImmersiveWeathering'
export * from './ImmersiveWeathering'
export function initPartners(schemas: SchemaRegistry, collections: CollectionRegistry) {
initImmersiveWeathering(schemas, collections)
}
+60
View File
@@ -0,0 +1,60 @@
{
"immersive_weathering.area_condition.type": "Type",
"immersive_weathering.area_condition.type.generate_if_not_too_many": "Generate if not too many",
"immersive_weathering.area_condition.type.neighbor_based_generation": "Neighbor based generation",
"immersive_weathering.area_condition.generate_if_not_too_many.radiusX": "Radius X",
"immersive_weathering.area_condition.generate_if_not_too_many.radiusY": "Radius Y",
"immersive_weathering.area_condition.generate_if_not_too_many.radiusZ": "Radius Z",
"immersive_weathering.area_condition.generate_if_not_too_many.requiredAmount": "Required amount",
"immersive_weathering.area_condition.generate_if_not_too_many.yOffset": "Y offset",
"immersive_weathering.area_condition.generate_if_not_too_many.must_have": "Must have",
"immersive_weathering.area_condition.generate_if_not_too_many.must_not_have": "Must not have",
"immersive_weathering.area_condition.generate_if_not_too_many.includes": "Includes",
"immersive_weathering.area_condition.neighbor_based_generation.must_have": "Must have",
"immersive_weathering.area_condition.neighbor_based_generation.must_not_have": "Must not have",
"immersive_weathering.area_condition.neighbor_based_generation.required_amount": "Required amount",
"immersive_weathering.area_condition.neighbor_based_generation.directions": "Directions",
"immersive_weathering.area_condition.neighbor_based_generation.directions.entry": "Direction",
"immersive_weathering.block_growth.area_condition": "Area conditions",
"immersive_weathering.block_growth.position_predicates": "Position predicates",
"immersive_weathering.block_growth.position_predicates.entry": "Position test",
"immersive_weathering.block_growth.growth_chance": "Growth chance",
"immersive_weathering.block_growth.growth_for_face": "Growth for face",
"immersive_weathering.block_growth.growth_for_face.entry": "Face",
"immersive_weathering.block_growth.growth_for_face.entry.direction": "Direction",
"immersive_weathering.block_growth.growth_for_face.entry.weight": "Weight",
"immersive_weathering.block_growth.growth_for_face.entry.growth": "Growth",
"immersive_weathering.block_growth.growth_for_face.entry.growth.entry.data": "Block pair",
"immersive_weathering.block_growth.growth_for_face.entry.growth.entry.weight": "Weight",
"immersive_weathering.block_growth.owners": "Owners",
"immersive_weathering.block_growth.owners.entry": "Block",
"immersive_weathering.block_growth.replacing_target": "Replacing target",
"immersive_weathering.block_growth.target_self": "Target self",
"immersive_weathering.block_growth.destroy_target": "Destroy target",
"immersive_weathering.block_pair.block": "Block",
"immersive_weathering.block_pair.above_block": "Above block",
"immersive_weathering.position_test.predicate_type": "Predicate type",
"immersive_weathering.position_test.predicate_type.biome_match": "Biome match",
"immersive_weathering.position_test.predicate_type.day_test": "Day test",
"immersive_weathering.position_test.predicate_type.nand": "NAND",
"immersive_weathering.position_test.predicate_type.precipitation_test": "Precipitation test",
"immersive_weathering.position_test.predicate_type.temperature_range": "Temperature range",
"immersive_weathering.position_test.biome_match.biomes": "Biomes",
"immersive_weathering.position_test.day_test.day": "Day",
"immersive_weathering.position_test.nand.predicates": "Predicates",
"immersive_weathering.position_test.precipitation_test.precipitation": "Precipitation",
"immersive_weathering.position_test.temperature_range.min": "Min",
"immersive_weathering.position_test.temperature_range.max": "Max",
"immersive_weathering.position_test.temperature_range.use_local_pos": "Use local pos",
"immersive_weathering:rule_test.always_true": "Always true",
"immersive_weathering:rule_test.block_match": "Block match",
"immersive_weathering:rule_test.blockstate_match": "Block state match",
"immersive_weathering:rule_test.random_block_match": "Random block match",
"immersive_weathering:rule_test.random_blockstate_match": "Random block state match",
"immersive_weathering:rule_test.tag_match": "Tag match",
"immersive_weathering:rule_test.immersive_weathering:block_set_match": "Block set match",
"immersive_weathering:rule_test.immersive_weathering:fluid_match": "Fluid match",
"immersive_weathering:rule_test.immersive_weathering:tree_log": "Tree log",
"rule_test.blocks": "Blocks",
"rule_test.fluid": "Fluid"
}
+1 -1
View File
@@ -15,7 +15,7 @@ import { ModelWrapper } from './ModelWrapper'
const selectRegistries = ['loot_table.type', 'loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'recipe.type', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'dimension.generator.biome_source.preset', 'carver.type', 'feature.type', 'decorator.type', 'feature.tree.minimum_size.type', 'block_state_provider.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'int_provider.type', 'float_provider.type', 'height_provider.type', 'structure_feature.type', 'surface_builder.type', 'processor.processor_type', 'rule_test.predicate_type', 'pos_rule_test.predicate_type', 'template_element.element_type', 'block_placer.type', 'block_predicate.type', 'material_rule.type', 'material_condition.type', 'structure_placement.type', 'density_function.type', 'root_placer.type', 'entity.type_specific.cat.variant', 'entity.type_specific.frog.variant']
const hiddenFields = ['number_provider.type', 'score_provider.type', 'nbt_provider.type', 'int_provider.type', 'float_provider.type', 'height_provider.type']
const flattenedFields = ['feature.config', 'decorator.config', 'int_provider.value', 'float_provider.value', 'block_state_provider.simple_state_provider.state', 'block_state_provider.rotated_block_provider.state', 'block_state_provider.weighted_state_provider.entries.entry.data', 'rule_test.block_state', 'structure_feature.config', 'surface_builder.config', 'template_pool.elements.entry.element', 'decorator.block_survives_filter.state', 'material_rule.block.result_state']
const inlineFields = ['loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'feature.type', 'decorator.type', 'block_state_provider.type', 'feature.tree.minimum_size.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'block_placer.type', 'rule_test.predicate_type', 'processor.processor_type', 'template_element.element_type', 'nbt_operation.op', 'number_provider.value', 'score_provider.name', 'score_provider.target', 'nbt_provider.source', 'nbt_provider.target', 'generator_biome.biome', 'block_predicate.type', 'material_rule.type', 'material_condition.type', 'density_function.type', 'root_placer.type', 'entity.type_specific.type']
const inlineFields = ['loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'feature.type', 'decorator.type', 'block_state_provider.type', 'feature.tree.minimum_size.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'block_placer.type', 'rule_test.predicate_type', 'processor.processor_type', 'template_element.element_type', 'nbt_operation.op', 'number_provider.value', 'score_provider.name', 'score_provider.target', 'nbt_provider.source', 'nbt_provider.target', 'generator_biome.biome', 'block_predicate.type', 'material_rule.type', 'material_condition.type', 'density_function.type', 'root_placer.type', 'entity.type_specific.type', 'immersive_weathering.area_condition.type', 'immersive_weathering.block_growth.growth_for_face.entry.direction', 'immersive_weathering.position_test.predicate_type']
const nbtFields = ['function.set_nbt.tag', 'advancement.display.icon.nbt', 'text_component_object.nbt', 'entity.nbt', 'block.nbt', 'item.nbt']
const fixedLists = ['generator_biome.parameters.temperature', 'generator_biome.parameters.humidity', 'generator_biome.parameters.continentalness', 'generator_biome.parameters.erosion', 'generator_biome.parameters.depth', 'generator_biome.parameters.weirdness', 'feature.end_spike.crystal_beam_target', 'feature.end_gateway.exit', 'decorator.block_filter.offset', 'block_predicate.matching_blocks.offset', 'block_predicate.matching_fluids.offset', 'model_element.from', 'model_element.to', 'model_element.rotation.origin', 'model_element.faces.uv', 'item_transform.rotation', 'item_transform.translation', 'item_transform.scale', 'generator_structure.random_spread.locate_offset']
const collapsedFields = ['noise_settings.surface_rule', 'noise_settings.noise.terrain_shaper']
+7 -2
View File
@@ -83,8 +83,13 @@ export async function fetchPreset(versionId: VersionId, registry: string, id: st
console.debug(`[fetchPreset] ${versionId} ${registry} ${id}`)
const version = config.versions.find(v => v.id === versionId)!
try {
const type = ['blockstates', 'models'].includes(registry) ? 'assets' : 'data'
const url = `${mcmeta(version, type)}/${type}/minecraft/${registry}/${id}.json`
let url
if (id.startsWith('immersive_weathering:')) {
url = `https://raw.githubusercontent.com/AstralOrdana/Immersive-Weathering/main/src/main/resources/data/immersive_weathering/block_growths/${id.slice(21)}.json`
} else {
const type = ['blockstates', 'models'].includes(registry) ? 'assets' : 'data'
url = `${mcmeta(version, type)}/${type}/minecraft/${registry}/${id}.json`
}
const res = await fetch(url)
return await res.json()
} catch (e) {
+2
View File
@@ -1,6 +1,7 @@
import type { CollectionRegistry, INode, SchemaRegistry } from '@mcschema/core'
import { ChoiceNode, DataModel, Reference, StringNode } from '@mcschema/core'
import config from '../../config.json'
import { initPartners } from '../partners'
import { message } from '../Utils'
import { fetchData } from './DataFetcher'
@@ -58,6 +59,7 @@ async function getVersion(id: VersionId): Promise<VersionData> {
const blockStates: BlockStateRegistry = {}
await fetchData(id, collections, blockStates)
const schemas = mcschema.getSchemas(collections)
initPartners(schemas, collections)
Versions[id] = { collections, schemas, blockStates }
return Versions[id]
} catch (e) {