From 98d2f9b6b78c3a12c61d701c50c2513a365b6f57 Mon Sep 17 00:00:00 2001 From: Misode Date: Wed, 30 Oct 2024 16:42:43 +0100 Subject: [PATCH] Create copy of 1.21.2 for 1.21.4 --- java/1.21.4/package.json | 29 + java/1.21.4/src/Collections.ts | 664 ++++++++ java/1.21.4/src/LootContext.ts | 112 ++ java/1.21.4/src/index.ts | 31 + java/1.21.4/src/schemas/Advancement.ts | 299 ++++ java/1.21.4/src/schemas/BannerPattern.ts | 21 + java/1.21.4/src/schemas/ChatType.ts | 45 + java/1.21.4/src/schemas/Common.ts | 1331 +++++++++++++++++ java/1.21.4/src/schemas/Components.ts | 556 +++++++ java/1.21.4/src/schemas/Condition.ts | 32 + java/1.21.4/src/schemas/DamageType.ts | 27 + java/1.21.4/src/schemas/Dimension.ts | 129 ++ java/1.21.4/src/schemas/DimensionType.ts | 78 + java/1.21.4/src/schemas/Enchantment.ts | 357 +++++ java/1.21.4/src/schemas/Instrument.ts | 28 + java/1.21.4/src/schemas/ItemModifier.ts | 41 + java/1.21.4/src/schemas/JukeboxSong.ts | 28 + java/1.21.4/src/schemas/LootTable.ts | 214 +++ java/1.21.4/src/schemas/PackMcmeta.ts | 83 + java/1.21.4/src/schemas/PaintingVariant.ts | 28 + java/1.21.4/src/schemas/Predicates.ts | 400 +++++ java/1.21.4/src/schemas/Recipe.ts | 103 ++ java/1.21.4/src/schemas/Tags.ts | 84 ++ java/1.21.4/src/schemas/TextComponent.ts | 211 +++ java/1.21.4/src/schemas/TrialSpawner.ts | 105 ++ java/1.21.4/src/schemas/Trims.ts | 53 + java/1.21.4/src/schemas/WolfVariant.ts | 27 + java/1.21.4/src/schemas/WorldSettings.ts | 73 + java/1.21.4/src/schemas/assets/Atlas.ts | 74 + .../src/schemas/assets/BlockDefinition.ts | 106 ++ java/1.21.4/src/schemas/assets/Font.ts | 83 + java/1.21.4/src/schemas/assets/Model.ts | 101 ++ java/1.21.4/src/schemas/assets/index.ts | 12 + java/1.21.4/src/schemas/index.ts | 57 + java/1.21.4/src/schemas/worldgen/Biome.ts | 109 ++ java/1.21.4/src/schemas/worldgen/Carver.ts | 69 + java/1.21.4/src/schemas/worldgen/Decorator.ts | 77 + .../src/schemas/worldgen/DensityFunction.ts | 194 +++ java/1.21.4/src/schemas/worldgen/Feature.ts | 679 +++++++++ .../src/schemas/worldgen/NoiseSettings.ts | 95 ++ .../src/schemas/worldgen/ProcessorList.ts | 193 +++ java/1.21.4/src/schemas/worldgen/Structure.ts | 140 ++ .../src/schemas/worldgen/StructureSet.ts | 88 ++ .../src/schemas/worldgen/SurfaceRule.ts | 90 ++ .../src/schemas/worldgen/TemplatePool.ts | 90 ++ .../src/schemas/worldgen/WorldPreset.ts | 83 + java/1.21.4/src/schemas/worldgen/index.ts | 29 + java/1.21.4/tsconfig.json | 13 + 48 files changed, 7571 insertions(+) create mode 100644 java/1.21.4/package.json create mode 100644 java/1.21.4/src/Collections.ts create mode 100644 java/1.21.4/src/LootContext.ts create mode 100644 java/1.21.4/src/index.ts create mode 100644 java/1.21.4/src/schemas/Advancement.ts create mode 100644 java/1.21.4/src/schemas/BannerPattern.ts create mode 100644 java/1.21.4/src/schemas/ChatType.ts create mode 100644 java/1.21.4/src/schemas/Common.ts create mode 100644 java/1.21.4/src/schemas/Components.ts create mode 100644 java/1.21.4/src/schemas/Condition.ts create mode 100644 java/1.21.4/src/schemas/DamageType.ts create mode 100644 java/1.21.4/src/schemas/Dimension.ts create mode 100644 java/1.21.4/src/schemas/DimensionType.ts create mode 100644 java/1.21.4/src/schemas/Enchantment.ts create mode 100644 java/1.21.4/src/schemas/Instrument.ts create mode 100644 java/1.21.4/src/schemas/ItemModifier.ts create mode 100644 java/1.21.4/src/schemas/JukeboxSong.ts create mode 100644 java/1.21.4/src/schemas/LootTable.ts create mode 100644 java/1.21.4/src/schemas/PackMcmeta.ts create mode 100644 java/1.21.4/src/schemas/PaintingVariant.ts create mode 100644 java/1.21.4/src/schemas/Predicates.ts create mode 100644 java/1.21.4/src/schemas/Recipe.ts create mode 100644 java/1.21.4/src/schemas/Tags.ts create mode 100644 java/1.21.4/src/schemas/TextComponent.ts create mode 100644 java/1.21.4/src/schemas/TrialSpawner.ts create mode 100644 java/1.21.4/src/schemas/Trims.ts create mode 100644 java/1.21.4/src/schemas/WolfVariant.ts create mode 100644 java/1.21.4/src/schemas/WorldSettings.ts create mode 100644 java/1.21.4/src/schemas/assets/Atlas.ts create mode 100644 java/1.21.4/src/schemas/assets/BlockDefinition.ts create mode 100644 java/1.21.4/src/schemas/assets/Font.ts create mode 100644 java/1.21.4/src/schemas/assets/Model.ts create mode 100644 java/1.21.4/src/schemas/assets/index.ts create mode 100644 java/1.21.4/src/schemas/index.ts create mode 100644 java/1.21.4/src/schemas/worldgen/Biome.ts create mode 100644 java/1.21.4/src/schemas/worldgen/Carver.ts create mode 100644 java/1.21.4/src/schemas/worldgen/Decorator.ts create mode 100644 java/1.21.4/src/schemas/worldgen/DensityFunction.ts create mode 100644 java/1.21.4/src/schemas/worldgen/Feature.ts create mode 100644 java/1.21.4/src/schemas/worldgen/NoiseSettings.ts create mode 100644 java/1.21.4/src/schemas/worldgen/ProcessorList.ts create mode 100644 java/1.21.4/src/schemas/worldgen/Structure.ts create mode 100644 java/1.21.4/src/schemas/worldgen/StructureSet.ts create mode 100644 java/1.21.4/src/schemas/worldgen/SurfaceRule.ts create mode 100644 java/1.21.4/src/schemas/worldgen/TemplatePool.ts create mode 100644 java/1.21.4/src/schemas/worldgen/WorldPreset.ts create mode 100644 java/1.21.4/src/schemas/worldgen/index.ts create mode 100644 java/1.21.4/tsconfig.json diff --git a/java/1.21.4/package.json b/java/1.21.4/package.json new file mode 100644 index 00000000..cab89a8e --- /dev/null +++ b/java/1.21.4/package.json @@ -0,0 +1,29 @@ +{ + "name": "@mcschema/java-1.21.4", + "version": "0.0.1", + "description": "Schemas for Java Edition 1.21.4", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "keywords": [], + "author": "Misode", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/misode/minecraft-schemas.git" + }, + "dependencies": { + "@mcschema/core": "^0.13.0" + }, + "publishConfig": { + "access": "public" + }, + "files": [ + "lib/**/*", + "!lib/**/*.tsbuildinfo" + ] +} diff --git a/java/1.21.4/src/Collections.ts b/java/1.21.4/src/Collections.ts new file mode 100644 index 00000000..c75f9bb3 --- /dev/null +++ b/java/1.21.4/src/Collections.ts @@ -0,0 +1,664 @@ +import { CollectionRegistry } from '@mcschema/core' + +export function initCollections(collections: CollectionRegistry) { + collections.register('keybind', [ + 'key.advancements', + 'key.attack', + 'key.back', + 'key.chat', + 'key.command', + 'key.drop', + 'key.forward', + 'key.fullscreen', + 'key.hotbar.1', + 'key.hotbar.2', + 'key.hotbar.3', + 'key.hotbar.4', + 'key.hotbar.5', + 'key.hotbar.6', + 'key.hotbar.7', + 'key.hotbar.8', + 'key.hotbar.9', + 'key.inventory', + 'key.jump', + 'key.left', + 'key.loadToolbarActivator', + 'key.pickItem', + 'key.playerlist', + 'key.right', + 'key.saveToolbarActivator', + 'key.screenshot', + 'key.smoothCamera', + 'key.sneak', + 'key.spectatorOutlines', + 'key.sprint', + 'key.swapOffhand', + 'key.togglePerspective', + 'key.use' + ]) + + collections.register('equipment_slot', [ + 'mainhand', + 'offhand', + 'head', + 'chest', + 'legs', + 'feet', + 'body', + ]) + + collections.register('equipment_slot_group', [ + 'any', + 'mainhand', + 'offhand', + 'hand', + 'head', + 'chest', + 'legs', + 'feet', + 'armor', + 'body', + ]) + + const slotRange = (prefix: string, size: number) => [ + `${prefix}.*`, + ...[...Array(size)].map((_, i) => `${prefix}.${i}`), + ] + + collections.register('slot_range', [ + 'contents', + ...slotRange('container', 54), + ...slotRange('hotbar', 9), + ...slotRange('inventory', 27), + ...slotRange('enderchest', 27), + ...slotRange('villager', 8), + ...slotRange('horse', 15), + 'weapon', + 'weapon.mainhand', + 'weapon.offhand', + 'weapon.*', + 'armor.head', + 'armor.chest', + 'armor.legs', + 'armor.feet', + 'armor.body', + 'armor.*', + 'horse.saddle', + 'horse.chest', + 'player.cursor', + ...slotRange('player.crafting', 4), + ]) + + collections.register('gamemode', [ + 'survival', + 'creative', + 'adventure', + 'spectator' + ]) + + // LootContext.EntityTarget + collections.register('entity_source', [ + 'this', + 'attacker', + 'direct_attacker', + 'attacking_player' + ]) + + // CopyNameFunction.NameSource + collections.register('copy_source', [ + 'block_entity', + 'this', + 'attacking_entity', + 'last_damage_player' + ]) + + collections.register('loot_table_apply_bonus_formula', [ + 'minecraft:uniform_bonus_count', + 'minecraft:binomial_with_bonus_count', + 'minecraft:ore_drops' + ]) + + collections.register('map_decoration', [ + 'mansion', + 'monument', + 'village_desert', + 'village_plains', + 'village_savanna', + 'village_snowy', + 'village_taiga', + 'jungle_temple', + 'swamp_hut', + 'player', + 'frame', + 'red_marker', + 'blue_marker', + 'target_x', + 'target_point', + 'player_off_map', + 'player_off_limits', + 'red_x', + 'banner_white', + 'banner_orange', + 'banner_magenta', + 'banner_light_blue', + 'banner_yellow', + 'banner_lime', + 'banner_pink', + 'banner_gray', + 'banner_light_gray', + 'banner_cyan', + 'banner_purple', + 'banner_blue', + 'banner_brown', + 'banner_green', + 'banner_red', + 'banner_black' + ]) + + collections.register('map_feature', [ + 'bastion_remnant', + 'buried_treasure', + 'desert_pyramid', + 'endcity', + 'fortress', + 'igloo', + 'jungle_pyramid', + 'mansion', + 'mineshaft', + 'monument', + 'nether_fossil', + 'ocean_ruin', + 'pillager_outpost', + 'ruined_portal', + 'shipwreck', + 'stronghold', + 'swamp_hut', + 'village' + ]) + + collections.register('recipe_group', [ + 'bed', + 'light_gray_dye', + 'iron_ingot', + 'wooden_stairs', + 'black_dye', + 'stained_terracotta', + 'dyed_bed', + 'magenta_dye', + 'bark', + 'rabbit_stew', + 'light_blue_dye', + 'wooden_fence_gate', + 'planks', + 'stained_glass', + 'wooden_door', + 'stained_glass_pane', + 'wooden_fence', + 'sticks', + 'concrete_powder', + 'wooden_pressure_plate', + 'boat', + 'brown_dye', + 'yellow_dye', + 'bonemeal', + 'red_dye', + 'wooden_trapdoor', + 'wooden_button', + 'gold_ingot', + 'pink_dye', + 'wooden_slab', + 'orange_dye', + 'carpet', + 'sign', + 'wool', + 'sugar', + 'blue_dye', + 'white_dye', + 'banner', + 'netherite_ingot' + ]) + + collections.register('biome_category', [ + 'beach', + 'desert', + 'extreme_hills', + 'forest', + 'icy', + 'jungle', + 'mesa', + 'mountain', + 'mushroom', + 'nether', + 'none', + 'ocean', + 'plains', + 'river', + 'savanna', + 'swamp', + 'taiga', + 'the_end', + 'underground' + ]) + + collections.register('heightmap_type', [ + 'MOTION_BLOCKING', + 'MOTION_BLOCKING_NO_LEAVES', + 'OCEAN_FLOOR', + 'OCEAN_FLOOR_WG', + 'WORLD_SURFACE', + 'WORLD_SURFACE_WG' + ]) + + collections.register('generation_step', [ + 'air', + 'liquid' + ]) + + collections.register('decoration_step', [ + 'raw_generation', + 'lakes', + 'local_modifications', + 'underground_structures', + 'surface_structures', + 'strongholds', + 'underground_ores', + 'underground_decoration', + 'fluid_springs', + 'vegetal_decoration', + 'top_layer_modification' + ]) + + collections.register('loot_context_type', [ + 'minecraft:empty', + 'minecraft:chest', + 'minecraft:command', + 'minecraft:selector', + 'minecraft:fishing', + 'minecraft:entity', + 'minecraft:equipment', + 'minecraft:archaeology', + 'minecraft:gift', + 'minecraft:barter', + 'minecraft:vault', + 'minecraft:advancement_reward', + 'minecraft:advancement_entity', + 'minecraft:advancement_location', + 'minecraft:block_use', + 'minecraft:generic', + 'minecraft:block', + 'minecraft:shearing', + ]) + + collections.register('banner_pattern', [ + 'base', + 'square_bottom_left', + 'square_bottom_right', + 'square_top_left', + 'square_top_right', + 'stripe_bottom', + 'stripe_top', + 'stripe_left', + 'stripe_right', + 'stripe_center', + 'stripe_middle', + 'stripe_downright', + 'stripe_downleft', + 'small_stripes', + 'cross', + 'straight_cross', + 'triangle_bottom', + 'triangle_top', + 'triangles_bottom', + 'triangles_top', + 'diagonal_left', + 'diagonal_up_right', + 'diagonal_up_left', + 'diagonal_right', + 'circle', + 'rhombus', + 'half_vertical', + 'half_horizontal', + 'half_vertical_right', + 'half_horizontal_bottom', + 'border', + 'curly_border', + 'gradient', + 'gradient_up', + 'bricks', + 'globe', + 'creeper', + 'skull', + 'flower', + 'mojang', + 'piglin' + ]) + + collections.register('dye_color', [ + 'white', + 'orange', + 'magenta', + 'light_blue', + 'yellow', + 'lime', + 'pink', + 'gray', + 'light_gray', + 'cyan', + 'purple', + 'blue', + 'brown', + 'green', + 'red', + 'black' + ]) + + collections.register('cave_surface', [ + 'floor', + 'ceiling' + ]) + + collections.register('direction', [ + 'down', + 'up', + 'north', + 'east', + 'south', + 'west' + ]) + + collections.register('axis', [ + 'x', + 'y', + 'z' + ]) + + collections.register('display_position', [ + 'firstperson_righthand', + 'firstperson_lefthand', + 'thirdperson_righthand', + 'thirdperson_lefthand', + 'gui', + 'head', + 'ground', + 'fixed' + ]) + + collections.register('gui_light', [ + 'front', + 'side' + ]) + + collections.register('item_model_predicates', [ + 'angle', + 'blocking', + 'broken', + 'cast', + 'charged', + 'cooldown', + 'custom_model_data', + 'damage', + 'damaged', + 'firework', + 'lefthanded', + 'pull', + 'pulling', + 'throwing', + 'time' + ]) + + collections.register('glyph_provider_type', [ + 'bitmap', + 'reference', + 'ttf', + 'space', + 'unihex', + ]) + + collections.register('mob_category', [ + 'monster', + 'creature', + 'ambient', + 'axolotls', + 'underground_water_creature', + 'water_creature', + 'water_ambient', + 'misc', + ]) + + collections.register('feature_flags', [ + 'vanilla', + 'bundle', + 'trade_rebalance', + 'update_1_21', + ]) + + collections.register('sprite_source_type', [ + 'single', + 'directory', + 'filter', + 'unstitch', + 'paletted_permutations', + ]) + + collections.register('type_specific_type', [ + 'axolotl', + 'boat', + 'cat', + 'fishing_hook', + 'fox', + 'frog', + 'horse', + 'lightning', + 'llama', + 'mooshroom', + 'painting', + 'parrot', + 'player', + 'rabbit', + 'slime', + 'tropical_fish', + 'villager', + ]) + + collections.register('axolotl_variant', [ + 'lucy', + 'wild', + 'gold', + 'cyan', + 'blue', + ]) + + collections.register('boat_variant', [ + 'oak', + 'spruce', + 'birch', + 'jungle', + 'acacia', + 'dark_oak', + 'mangrove', + 'bamboo', + ]) + + collections.register('fox_variant', [ + 'red', + 'snow', + ]) + + collections.register('horse_variant', [ + 'white', + 'creamy', + 'chestnut', + 'brown', + 'black', + 'gray', + 'dark_brown', + ]) + + collections.register('llama_variant', [ + 'creamy', + 'white', + 'brown', + 'gray', + ]) + + collections.register('mooshroom_variant', [ + 'red', + 'brown', + ]) + + collections.register('parrot_variant', [ + 'red_blue', + 'blue', + 'green', + 'yellow_blue', + 'gray', + ]) + + collections.register('rabbit_variant', [ + 'brown', + 'white', + 'black', + 'white_splotched', + 'gold', + 'salt', + 'evil', + ]) + + collections.register('tropical_fish_variant', [ + 'kob', + 'sunstreak', + 'snooper', + 'dasher', + 'brinely', + 'spotty', + 'flopper', + 'stripey', + 'glitter', + 'blockfish', + 'betty', + 'clayfish', + ]) + + collections.register('armor_material', [ + 'leather', + 'chainmail', + 'iron', + 'gold', + 'diamond', + 'turtle', + 'netherite', + ]) + + collections.register('damage_scaling', [ + 'never', + 'always', + 'when_caused_by_living_non_player', + ]) + + collections.register('damage_effects', [ + 'hurt', + 'thorns', + 'drowning', + 'burning', + 'poking', + 'freezing', + ]) + + collections.register('death_message_type', [ + 'default', + 'fall_variants', + 'intentional_game_design', + ]) + + collections.register('recipe_category', [ + 'blocks', + 'building', + 'equipment', + 'food', + 'misc', + 'redstone', + ]) + + collections.register('font_option', [ + 'uniform', + 'jp', + ]) + + collections.register('attribute_modifier_operation', [ + 'add_value', + 'add_multiplied_base', + 'add_multiplied_total', + ]) + + collections.register('firework_explosion_shape', [ + 'small_ball', + 'large_ball', + 'star', + 'creeper', + 'burst', + ]) + + collections.register('list_operation', [ + 'replace_all', + 'replace_section', + 'insert', + 'append', + ]) + + collections.register('toggleable_data_component_type', [ + 'minecraft:trim', + 'minecraft:dyed_color', + 'minecraft:enchantments', + 'minecraft:stored_enchantments', + 'minecraft:unbreakable', + 'minecraft:can_break', + 'minecraft:can_place_on', + 'minecraft:attribute_modifiers', + ]) + + collections.register('container_component_manipulators', [ + 'minecraft:container', + 'minecraft:bundle_contents', + 'minecraft:charged_projectiles', + ]) + + collections.register('rarity', [ + 'common', + 'uncommon', + 'rare', + 'epic', + ]) + + collections.register('enchantment_target', [ + 'attacker', + 'damaging_entity', + 'victim', + ]) + + collections.register('explosion_interaction', [ + 'none', + 'block', + 'mob', + 'tnt', + 'trigger', + ]) + + collections.register('use_animation', [ + 'none', + 'eat', + 'drink', + 'block', + 'bow', + 'spear', + 'crossbow', + 'spyglass', + 'toot_horn', + 'brush', + ]) + + collections.register('salmon_variant', [ + 'small', + 'medium', + 'large', + ]) +} diff --git a/java/1.21.4/src/LootContext.ts b/java/1.21.4/src/LootContext.ts new file mode 100644 index 00000000..67bcdf2e --- /dev/null +++ b/java/1.21.4/src/LootContext.ts @@ -0,0 +1,112 @@ +export const enum LootContext { + AttackingEntity, + BlockEntity, + BlockState, + DamageSource, + DirectAttackingEntity, + EnchantmentLevel, + EnchantmentActive, + ExplosionRadius, + LastDamagePlayer, + Origin, + ThisEntity, + Tool +} + +export type LootContextRegistration = { requires: LootContext[], allows: LootContext[] } + +export const LootTableTypes = new Map([ + ['minecraft:advancement_entity', { requires: [LootContext.Origin, LootContext.ThisEntity], allows: [] }], + ['minecraft:advancement_location', { requires: [LootContext.Origin, LootContext.ThisEntity, LootContext.Tool, LootContext.BlockState], allows: [] }], + ['minecraft:advancement_reward', { requires: [LootContext.Origin, LootContext.ThisEntity], allows: [] }], + ['minecraft:archaeology', { requires: [LootContext.Origin, LootContext.ThisEntity, LootContext.Tool], allows: [] }], + ['minecraft:barter', { requires: [LootContext.ThisEntity], allows: [] }], + ['minecraft:block', { requires: [LootContext.BlockState, LootContext.Origin, LootContext.Tool], allows: [LootContext.BlockEntity, LootContext.ExplosionRadius, LootContext.ThisEntity] }], + ['minecraft:block_use', { requires: [LootContext.ThisEntity, LootContext.Origin, LootContext.BlockState], allows: [] }], + ['minecraft:chest', { requires: [LootContext.Origin], allows: [LootContext.ThisEntity] }], + ['minecraft:command', { requires: [LootContext.Origin], allows: [LootContext.ThisEntity] }], + ['minecraft:empty', { requires: [], allows: [] }], + ['minecraft:enchanted_damage', { requires: [LootContext.ThisEntity, LootContext.EnchantmentLevel, LootContext.Origin, LootContext.DamageSource], allows: [LootContext.DirectAttackingEntity, LootContext.AttackingEntity] }], + ['minecraft:enchanted_item', { requires: [LootContext.Tool, LootContext.EnchantmentLevel], allows: [] }], + ['minecraft:enchanted_location', { requires: [LootContext.ThisEntity, LootContext.EnchantmentLevel, LootContext.Origin, LootContext.EnchantmentActive], allows: [] }], + ['minecraft:enchanted_entity', { requires: [LootContext.ThisEntity, LootContext.EnchantmentLevel, LootContext.Origin], allows: [] }], + ['minecraft:entity', { requires: [LootContext.DamageSource, LootContext.Origin, LootContext.ThisEntity], allows: [LootContext.DirectAttackingEntity, LootContext.AttackingEntity, LootContext.LastDamagePlayer] }], + ['minecraft:equipment', { requires: [LootContext.Origin, LootContext.ThisEntity], allows: [] } ], + ['minecraft:fishing', { requires: [LootContext.Origin, LootContext.Tool], allows: [LootContext.ThisEntity] }], + ['minecraft:generic', { requires: [LootContext.DamageSource, LootContext.BlockEntity, LootContext.BlockState, LootContext.DirectAttackingEntity, LootContext.ExplosionRadius, LootContext.AttackingEntity, LootContext.LastDamagePlayer, LootContext.Origin, LootContext.ThisEntity, LootContext.Tool], allows: [] }], + ['minecraft:gift', { requires: [LootContext.Origin, LootContext.ThisEntity], allows: [] }], + ['minecraft:selector', { requires: [LootContext.Origin], allows: [LootContext.ThisEntity] }], + ['minecraft:shearing', { requires: [LootContext.Origin, LootContext.ThisEntity, LootContext.Tool], allows: [] }], + ['minecraft:vault', { requires: [LootContext.Origin], allows: [LootContext.ThisEntity, LootContext.Tool] }] +]) + +/** + * A map from loot entity sources to an array of their required context. + */ +export const LootEntitySources = new Map([ + ['direct_killer', [LootContext.DirectAttackingEntity]], + ['killer', [LootContext.AttackingEntity]], + ['killer_player', [LootContext.AttackingEntity]], + ['this', [LootContext.ThisEntity]] +]) + +/** + * A map from loot copy sources to an array of their required context. + */ +export const LootCopySources = new Map([ + ['block_entity', [LootContext.BlockEntity]], + ['killer', [LootContext.AttackingEntity]], + ['killer_player', [LootContext.AttackingEntity]], + ['this', [LootContext.ThisEntity]] +]) + +/** + * A map from loot condition IDs to an array of their required context. + */ +export const LootConditions = new Map([ + ['minecraft:alternative', []], + ['minecraft:block_state_property', [LootContext.BlockState]], + ['minecraft:damage_source_properties', [LootContext.DamageSource]], + ['minecraft:enchantment_active_check', [LootContext.EnchantmentActive]], + ['minecraft:entity_properties', [LootContext.Origin]], + ['minecraft:entity_scores', []], + ['minecraft:inverted', []], + ['minecraft:killed_by_player', [LootContext.LastDamagePlayer]], + ['minecraft:location_check', []], + ['minecraft:match_tool', [LootContext.Tool]], + ['minecraft:random_chance', []], + ['minecraft:random_chance_with_looting', [LootContext.AttackingEntity]], + ['minecraft:reference', []], + ['minecraft:survives_explosion', [LootContext.ExplosionRadius]], + ['minecraft:table_bonus', [LootContext.Tool]], + ['minecraft:time_check', []], + ['minecraft:weather_check', []] +]) + +/** + * A map from loot function IDs to an array of their required context. + */ +export const LootFunctions = new Map([ + ['minecraft:apply_bonus', [LootContext.Tool]], + ['minecraft:copy_name', []], + ['minecraft:copy_nbt', []], + ['minecraft:copy_state', [LootContext.BlockState]], + ['minecraft:enchant_randomly', []], + ['minecraft:enchant_with_levels', []], + ['minecraft:exploration_map', [LootContext.Origin]], + ['minecraft:explosion_decay', []], + ['minecraft:fill_player_head', []], + ['minecraft:furnace_smelt', []], + ['minecraft:limit_count', []], + ['minecraft:looting_enchant', [LootContext.AttackingEntity]], + ['minecraft:set_attributes', []], + ['minecraft:set_banner_pattern', []], + ['minecraft:set_contents', []], + ['minecraft:set_count', []], + ['minecraft:set_damage', []], + ['minecraft:set_loot_table', []], + ['minecraft:set_lore', []], + ['minecraft:set_name', []], + ['minecraft:set_nbt', []], + ['minecraft:set_stew_effect', []] +]) diff --git a/java/1.21.4/src/index.ts b/java/1.21.4/src/index.ts new file mode 100644 index 00000000..748716e5 --- /dev/null +++ b/java/1.21.4/src/index.ts @@ -0,0 +1,31 @@ +import { CollectionRegistry, SchemaRegistry } from '@mcschema/core' +import { initCollections } from './Collections' +import { initSchemas } from './schemas' + +export * as lootContext from './LootContext' + +export function getCollections() { + const collections = new CollectionRegistry() + initCollections(collections) + return collections +} + +/** + * @param collections The CollectionRegistry for this version. Note that apart from + * the built-in collections, the client of this module is responsible for futhermore + * adding the vanilla registries to it: the collection IDs shouldn't contain the + * namespace (`minecraft:`) part, while the values within the collections should. + * + * @example + * for (const key in VANILLA_REGISTRIES>) { + * collections.register( + * key.replace(/^minecraft:/, ''), + * Object.keys(VANILLA_REGISTRIES[key].entries) + * ) + * } + */ +export function getSchemas(collections: CollectionRegistry) { + const schemas = new SchemaRegistry() + initSchemas(schemas, collections) + return schemas +} diff --git a/java/1.21.4/src/schemas/Advancement.ts b/java/1.21.4/src/schemas/Advancement.ts new file mode 100644 index 00000000..7cb6ff05 --- /dev/null +++ b/java/1.21.4/src/schemas/Advancement.ts @@ -0,0 +1,299 @@ +import { + BooleanNode, + Case, + ChoiceNode, + StringNode as RawStringNode, + ListNode, + MapNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, + Opt, +} from '@mcschema/core' + +export function initAdvancementSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + const EntityPredicate = ChoiceNode([ + { + type: 'object', + node: Opt(Reference('entity_predicate')), + change: v => v[0]?.predicate ?? ({}) + }, + { + type: 'list', + node: ListNode(Reference('condition')), + change: v => [{ + condition: 'minecraft:entity_properties', + predicate: v + }] + } + ], { context: 'conditions' }) + + schemas.register('advancement', Mod(ObjectNode({ + display: Opt(Mod(ObjectNode({ + icon: Reference('item_stack'), + title: Reference('text_component'), + description: Reference('text_component'), + background: Opt(StringNode()), + frame: Opt(StringNode({ enum: ['task', 'challenge', 'goal'] })), + show_toast: Opt(BooleanNode()), + announce_to_chat: Opt(BooleanNode()), + hidden: Opt(BooleanNode()) + }), { + default: () => ({ + icon: { + id: 'minecraft:stone' + }, + title: '', + description: '' + }) + })), + parent: Opt(StringNode({ validator: 'resource', params: { pool: '$advancement' } })), + criteria: MapNode( + StringNode(), + Reference('advancement_criteria') + ), + requirements: Opt(ListNode( + ListNode( + StringNode() // TODO: add validation + ) + )), + rewards: Opt(ObjectNode({ + function: Opt(StringNode({ validator: 'resource', params: { pool: '$function' } })), + loot: Opt(ListNode( + StringNode({ validator: 'resource', params: { pool: '$loot_table' } }) + )), + recipes: Opt(ListNode( + StringNode({ validator: 'resource', params: { pool: '$recipe' } }) + )), + experience: Opt(NumberNode({ integer: true })) + })), + sends_telemetry_event: Opt(BooleanNode()), + }, { context: 'advancement' }), { + default: () => ({ + criteria: { + requirement: { + trigger: 'minecraft:location' + } + } + }) + })) + + schemas.register('advancement_criteria', ObjectNode({ + trigger: StringNode({ validator: 'resource', params: { pool: 'trigger_type' } }), + conditions: Opt(ObjectNode({ + player: Mod(EntityPredicate, { + enabled: path => path.pop().push('trigger').get() !== 'minecraft:impossible' + }), + [Switch]: ['pop', { push: 'trigger' }], + [Case]: { + 'minecraft:allay_drop_item_on_block': { + location: EntityPredicate + }, + 'minecraft:any_block_use': { + location: EntityPredicate + }, + 'minecraft:bee_nest_destroyed': { + block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })), + num_bees_inside: Opt(Reference('int_bounds')), + item: Opt(Reference('item_predicate')) + }, + 'minecraft:bred_animals': { + parent: EntityPredicate, + partner: EntityPredicate, + child: EntityPredicate + }, + 'minecraft:brewed_potion': { + potion: Opt(StringNode({ validator: 'resource', params: { pool: 'potion' } })) + }, + 'minecraft:changed_dimension': { + from: Opt(StringNode({ validator: 'resource', params: { pool: '$dimension' } })), + to: Opt(StringNode({ validator: 'resource', params: { pool: '$dimension' } })) + }, + 'minecraft:channeled_lightning': { + victims: Opt(ListNode( + EntityPredicate + )) + }, + 'minecraft:construct_beacon': { + level: Opt(Reference('int_bounds')) + }, + 'minecraft:consume_item': { + item: Opt(Reference('item_predicate')) + }, + 'minecraft:crafter_recipe_crafted': { + recipe_id: StringNode({ validator: 'resource', params: { pool: '$recipe' } }), + ingredients: Opt(ListNode( + Reference('item_predicate') + )) + }, + 'minecraft:cured_zombie_villager': { + villager: EntityPredicate, + zombie: EntityPredicate + }, + 'minecraft:default_block_use': { + location: EntityPredicate + }, + 'minecraft:effects_changed': { + effects: Opt(MapNode( + StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }), + Reference('status_effect_predicate') + )), + source: Opt(EntityPredicate) + }, + 'minecraft:enter_block': { + block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })), + state: Opt(MapNode( + StringNode(), + StringNode(), + { validation: { validator: 'block_state_map', params: { id: ['pop', { push: 'block' }] } } } + )) + }, + 'minecraft:enchanted_item': { + levels: Opt(Reference('int_bounds')), + item: Opt(Reference('item_predicate')) + }, + 'minecraft:entity_hurt_player': { + damage: Opt(Reference('damage_predicate')) + }, + 'minecraft:entity_killed_player': { + entity: EntityPredicate, + killing_blow: Opt(Reference('damage_source_predicate')) + }, + 'minecraft:fall_after_explosion': { + start_position: Opt(Reference('location_predicate')), + distance: Opt(Reference('distance_predicate')), + cause: EntityPredicate, + }, + 'minecraft:fall_from_height': { + start_position: Opt(Reference('location_predicate')), + distance: Opt(Reference('distance_predicate')) + }, + 'minecraft:filled_bucket': { + item: Opt(Reference('item_predicate')) + }, + 'minecraft:fishing_rod_hooked': { + rod: Opt(Reference('item_predicate')), + entity: EntityPredicate, + item: Opt(Reference('item_predicate')) + }, + 'minecraft:inventory_changed': { + slots: Opt(ObjectNode({ + empty: Opt(Reference('int_bounds')), + occupied: Opt(Reference('int_bounds')), + full: Opt(Reference('int_bounds')) + })), + items: Opt(ListNode( + Reference('item_predicate') + )) + }, + 'minecraft:item_durability_changed': { + delta: Opt(Reference('int_bounds')), + durability: Opt(Reference('int_bounds')), + item: Opt(Reference('item_predicate')) + }, + 'minecraft:item_used_on_block': { + location: EntityPredicate + }, + 'minecraft:kill_mob_near_sculk_catalyst': { + entity: EntityPredicate, + killing_blow: Opt(Reference('damage_source_predicate')) + }, + 'minecraft:killed_by_arrow': { + unique_entity_types: Opt(Reference('int_bounds')), + fired_from_weapon: Opt(Reference('item_predicate')), + victims: Opt(ListNode( + EntityPredicate + )) + }, + 'minecraft:levitation': { + distance: Opt(Reference('distance_predicate')), + duration: Opt(Reference('int_bounds')) + }, + 'minecraft:lightning_strike': { + lightning: EntityPredicate, + bystander: EntityPredicate, + }, + 'minecraft:nether_travel': { + start_position: Opt(Reference('location_predicate')), + distance: Opt(Reference('distance_predicate')), + }, + 'minecraft:placed_block': { + location: EntityPredicate + }, + 'minecraft:player_generates_container_loot': { + loot_table: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }) + }, + 'minecraft:player_hurt_entity': { + damage: Opt(Reference('damage_predicate')), + entity: EntityPredicate + }, + 'minecraft:player_interacted_with_entity': { + item: Opt(Reference('item_predicate')), + entity: EntityPredicate + }, + 'minecraft:player_killed_entity': { + entity: EntityPredicate, + killing_blow: Opt(Reference('damage_source_predicate')) + }, + 'minecraft:recipe_crafted': { + recipe_id: StringNode({ validator: 'resource', params: { pool: '$recipe' } }), + ingredients: Opt(ListNode( + Reference('item_predicate') + )) + }, + 'minecraft:recipe_unlocked': { + recipe: StringNode({ validator: 'resource', params: { pool: '$recipe' } }) + }, + 'minecraft:ride_entity_in_lava': { + start_position: Opt(Reference('location_predicate')), + distance: Opt(Reference('distance_predicate')) + }, + 'minecraft:slide_down_block': { + block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })) + }, + 'minecraft:shot_crossbow': { + item: Opt(Reference('item_predicate')) + }, + 'minecraft:summoned_entity': { + entity: EntityPredicate + }, + 'minecraft:tame_animal': { + entity: EntityPredicate + }, + 'minecraft:target_hit': { + projectile: EntityPredicate, + shooter: EntityPredicate, + signal_strength: Opt(Reference('int_bounds')) + }, + 'minecraft:thrown_item_picked_up_by_entity': { + entity: EntityPredicate, + item: Opt(Reference('item_predicate')) + }, + 'minecraft:thrown_item_picked_up_by_player': { + entity: EntityPredicate, + item: Opt(Reference('item_predicate')) + }, + 'minecraft:used_ender_eye': { + distance: Opt(Reference('float_bounds')) + }, + 'minecraft:used_totem': { + item: Opt(Reference('item_predicate')) + }, + 'minecraft:using_item': { + item: Opt(Reference('item_predicate')) + }, + 'minecraft:villager_trade': { + villager: EntityPredicate, + item: Opt(Reference('item_predicate')) + }, + } + }, { context: 'criterion' })) + }, { category: 'predicate', context: 'criterion' })) +} diff --git a/java/1.21.4/src/schemas/BannerPattern.ts b/java/1.21.4/src/schemas/BannerPattern.ts new file mode 100644 index 00000000..d2d63246 --- /dev/null +++ b/java/1.21.4/src/schemas/BannerPattern.ts @@ -0,0 +1,21 @@ +import { + StringNode as RawStringNode, + Mod, + ObjectNode, + SchemaRegistry, + CollectionRegistry +} from '@mcschema/core' + +export function initBannerPatternSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('banner_pattern', Mod(ObjectNode({ + asset_id: StringNode(), + translation_key: StringNode(), + }, { context: 'banner_pattern' }), { + default: () => ({ + asset_id: 'minecraft:globe', + translation_key: 'block.minecraft.banner.globe' + }) + })) +} diff --git a/java/1.21.4/src/schemas/ChatType.ts b/java/1.21.4/src/schemas/ChatType.ts new file mode 100644 index 00000000..73fecb91 --- /dev/null +++ b/java/1.21.4/src/schemas/ChatType.ts @@ -0,0 +1,45 @@ +import { + StringNode as RawStringNode, + ListNode, + Mod, + ObjectNode, + Reference as RawReference, + SchemaRegistry, + CollectionRegistry, + Opt, +} from '@mcschema/core' + +export function initChatTypeSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('chat_type', Mod(ObjectNode({ + chat: Reference('text_decoration'), + narration: Reference('text_decoration'), + }, { context: 'chat_type' }), { + default: () => ({ + chat: { + translation_key: 'chat.type.text', + parameters: ['sender', 'content'], + }, + narration: { + translation_key: 'chat.type.text.narrate', + parameters: ['sender', 'content'], + } + }) + })) + + schemas.register('text_decoration', Mod(ObjectNode({ + translation_key: StringNode(), + parameters: ListNode( + StringNode({ enum: ['sender', 'target', 'content'] }) + ), + style: Opt(Reference('text_style')) + }, { context: 'text_decoration' }), { + default: () => ({ + translation_key: 'chat.type.text', + parameters: ['sender', 'content'], + style: {}, + }) + })) +} diff --git a/java/1.21.4/src/schemas/Common.ts b/java/1.21.4/src/schemas/Common.ts new file mode 100644 index 00000000..9180aa6c --- /dev/null +++ b/java/1.21.4/src/schemas/Common.ts @@ -0,0 +1,1331 @@ +import { + StringNode as RawStringNode, + ObjectNode, + MapNode, + ListNode, + NumberNode, + ChoiceNode, + Reference as RawReference, + INode, + SchemaRegistry, + CollectionRegistry, + NestedNodeChildren, + BooleanNode, + ObjectOrPreset, + Opt, + Mod, + Switch, + Case, + ResourceType, + NodeChildren, +} from '@mcschema/core' + +export let ConditionCases: (entitySourceNode?: INode) => NestedNodeChildren +export let FunctionCases: (conditions: NodeChildren, copySourceNode?: INode, entitySourceNode?: INode) => NestedNodeChildren + +export const DefaultNoiseSettings = { + sea_level: 63, + ore_veins_enabled: true, + disable_mob_generation: false, + aquifers_enabled: true, + legacy_random_source: false, + default_block: { + Name: 'minecraft:stone' + }, + default_fluid: { + Properties: { + level: '0' + }, + Name: 'minecraft:water' + }, + noise: { + min_y: -64, + height: 384, + size_horizontal: 1, + size_vertical: 2, + top_slide: { + target: -0.078125, + size: 2, + offset: 8 + }, + bottom_slide: { + target: 0.1171875, + size: 3, + offset: 0 + }, + sampling: { + xz_scale: 0.9999999814507745, + y_scale: 0.9999999814507745, + xz_factor: 80, + y_factor: 160 + }, + terrain_shaper: { + offset: 0, + factor: 0, + jaggedness: 0 + } + }, + noise_router: { + barrier: 0, + fluid_level_floodedness: 0, + fluid_level_spread: 0, + lava: 0, + temperature: 0, + vegetation: 0, + continents: 0, + erosion: 0, + depth: 0, + ridges: 0, + initial_density_without_jaggedness: 0, + final_density: { + type: 'minecraft:interpolated', + argument: 'minecraft:overworld/base_3d_noise' + }, + vein_toggle: 0, + vein_ridged: 0, + vein_gap: 0, + }, + surface_rule: { + type: 'minecraft:sequence', + sequence: [] + } +} +export let NoiseSettingsPresets: (node: INode) => INode + +type MinMaxConfig = { + min?: number + max?: number +} +export let FloatProvider: (config?: MinMaxConfig) => INode +export let IntProvider: (config?: MinMaxConfig) => INode + +type InclusiveRangeConfig = { + integer?: boolean + min?: number + max?: number +} +export let InclusiveRange: (config?: InclusiveRangeConfig) => INode + +type NonTagResources = Exclude + +type TagConfig = { + resource: NonTagResources, + inlineSchema?: string, +} +export let Tag: (config: TagConfig) => INode + +export let Filterable: (node: INode) => INode + +type SizeLimitedStringConfig = { + minLength?: number, + maxLength?: number, +} +export let SizeLimitedString: (config: SizeLimitedStringConfig) => INode + +export function initCommonSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + const Reference = RawReference.bind(undefined, schemas) + + schemas.register('block_state', Mod(ObjectNode({ + Name: StringNode({ validator: 'resource', params: { pool: 'block' } }), + Properties: Opt(MapNode( + StringNode(), + StringNode(), + { validation: { validator: 'block_state_map', params: { id: ['pop', { push: 'Name' }] } } } + )) + }, { context: 'block_state' }), { + default: () => ({ + Name: 'minecraft:stone' + }) + })) + + schemas.register('fluid_state', Mod(ObjectNode({ + Name: StringNode({ validator: 'resource', params: { pool: 'fluid' } }), + Properties: Opt(MapNode( + StringNode(), + StringNode() + )) + }, { context: 'fluid_state' }), { + default: () => ({ + Name: 'minecraft:water', + Properties: { + 'level': '0' + } + }) + })) + + schemas.register('item_non_air', Mod(StringNode({ validator: 'resource', params: { pool: 'item' } }), node => ({ + validate: (path, value, errors, options) => { + if (typeof value === 'string' && value?.replace(/^minecraft:/, '') === 'air') { + errors.add(path, 'error.item_stack_not_air') + } + return node.validate(path, value, errors, options) + } + }))) + + schemas.register('item_stack', ObjectNode({ + id: Reference('item_non_air'), + count: Opt(NumberNode({ integer: true, min: 1 })), + components: Opt(Reference('data_component_patch')), + }, { context: 'item_stack' })) + + schemas.register('single_item_stack', Mod(ObjectNode({ + id: Reference('item_non_air'), + components: Opt(Reference('data_component_patch')) + }), { + default: () => ({ + id: 'minecraft:stone' + }) + })) + + schemas.register('vec3', Mod(ListNode( + NumberNode(), + { minLength: 3, maxLength: 3 } + ), { + default: () => [0, 0, 0] + })) + + schemas.register('block_pos', Mod(ListNode( + NumberNode({ integer: true }), + { minLength: 3, maxLength: 3 } + ), { + default: () => [0, 0, 0] + })) + + const blockParticleFields = { + block_state: ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: 'block' } }), + change: v => typeof v === 'object' && v !== null ? v.Name ?? '' : '' + }, + { + type: 'object', + node: Reference('block_state'), + change: v => ({ Name: v }), + } + ]) + } + + schemas.register('particle', ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'particle_type' }}), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:block': blockParticleFields, + 'minecraft:block_marker': blockParticleFields, + 'minecraft:dust': { + color: Reference('vec3'), + scale: NumberNode({ min: 0.01, max: 4 }), + }, + 'minecraft:dust_color_transition': { + from_color: Reference('vec3'), + to_color: Reference('vec3'), + scale: NumberNode({ min: 0.01, max: 4 }), + }, + 'minecraft:dust_pillar': blockParticleFields, + 'minecraft:entity_effect': { + color: ChoiceNode([ + { + type: 'number', + node: NumberNode({ integer: true }) + }, + { + type: 'list', + node: ListNode( + NumberNode({ min: 0, max: 1 }), + { minLength: 4, maxLength: 4 } + ) + }, + ]) + }, + 'minecraft:item': { + item: ChoiceNode([ + { + type: 'string', + node: Reference('item_non_air'), + }, + { + type: 'object', + node: Reference('item_stack'), + }, + ]), + }, + 'minecraft:falling_dust': blockParticleFields, + 'minecraft:sculk_charge': { + roll: NumberNode(), + }, + 'minecraft:vibration': { + destination: ObjectNode({ + type: StringNode({ enum: ['block'] }), + pos: Reference('block_pos'), + }), + arrival_in_ticks: NumberNode({ integer: true }), + }, + 'minecraft:shriek': { + delay: NumberNode({ integer: true }), + }, + } + }, { context: 'particle' })) + + schemas.register('sound_event', ChoiceNode([ + { + type: 'string', + node: StringNode() + }, + { + type: 'object', + node: ObjectNode({ + sound_id: StringNode(), + range: Opt(NumberNode()), + }) + }, + ], { context: 'sound_event' })) + + const Bounds = (integer?: boolean) => ChoiceNode([ + { + type: 'number', + node: NumberNode({ integer }), + change: (v: any) => v === undefined ? 0 : v.min ?? v.max ?? 0 + }, + { + type: 'object', + node: ObjectNode({ + min: Opt(NumberNode({ integer })), + max: Opt(NumberNode({ integer })) + }, { context: 'range' }), + change: (v: any) => ({ + min: v ?? 0, + max: v ?? 0 + }) + } + ], { context: 'range' }) + + schemas.register('int_bounds', Bounds(true)) + + schemas.register('float_bounds', Bounds()) + + schemas.register('int_range', ChoiceNode([ + { + type: 'object', + node: ObjectNode({ + min: Opt(Reference('number_provider')), + max: Opt(Reference('number_provider')) + }) + }, + { + type: 'number', + node: NumberNode({ integer: true }) + } + ], { context: 'range' })) + + schemas.register('resource_location_pattern', ObjectNode({ + namespace: Opt(StringNode({ validator: 'regex_pattern' })), + path: Opt(StringNode({ validator: 'regex_pattern' })), + }, { context: 'resource_location_pattern' })) + + const ObjectWithType = (pool: ResourceType, directType: string, directPath: string, directDefault: string, objectDefault: string | null, context: string, cases: NestedNodeChildren) => { + let defaultCase: NodeChildren = {} + if (objectDefault) { + Object.keys(cases[objectDefault]).forEach(k => { + defaultCase[k] = Mod(cases[objectDefault][k], { + enabled: path => path.push('type').get() === undefined + }) + }) + } + const provider = ObjectNode({ + type: Mod(Opt(StringNode({ validator: 'resource', params: { pool } })), { + hidden: () => true + }), + [Switch]: [{ push: 'type' }], + [Case]: cases, + ...defaultCase + }, { context, disableSwitchContext: true }) + + const choices: any[] = [{ + type: directType, + node: cases[directDefault][directPath] + }] + if (objectDefault) { + choices.push({ + type: 'object', + priority: -1, + node: provider + }) + } + Object.keys(cases).forEach(k => { + choices.push({ + type: k, + match: (v: any) => { + const type = 'minecraft:' + v?.type?.replace(/^minecraft:/, '') + if (type === k) return true + const keys = v ? Object.keys(v) : [] + return typeof v === 'object' && (keys?.length === 0 || (keys?.length === 1 && keys?.[0] === 'type')) + }, + node: provider, + change: (v: any) => ({type: k}) + }) + }) + return ChoiceNode(choices, { context, choiceContext: `${context}.type` }) + } + + schemas.register('number_provider', ObjectWithType( + 'loot_number_provider_type', + 'number', 'value', 'minecraft:constant', + 'minecraft:uniform', + 'number_provider', + { + 'minecraft:constant': { + value: NumberNode() + }, + 'minecraft:uniform': { + min: Reference('number_provider'), + max: Reference('number_provider') + }, + 'minecraft:binomial': { + n: Reference('number_provider'), + p: Reference('number_provider') + }, + 'minecraft:score': { + target: Reference('scoreboard_name_provider'), + score: StringNode({ validator: 'objective' }), + scale: Opt(NumberNode()) + }, + 'minecraft:storage': { + storage: StringNode({ validator: 'resource', params: { pool: '$storage' } }), + path: StringNode({ validator: 'nbt_path' }), + }, + 'minecraft:enchantment_level': { + amount: Reference('level_based_value'), + } + })) + + schemas.register('scoreboard_name_provider', ObjectWithType( + 'loot_score_provider_type', + 'string', 'target', 'minecraft:context', + null, + 'score_provider', + { + 'minecraft:fixed': { + name: StringNode({ validator: 'entity', params: { amount: 'multiple', type: 'entities', isScoreHolder: true } }) // FIXME: doesn't support selectors + }, + 'minecraft:context': { + target: Mod(StringNode({ enum: 'entity_source' }), { default: () => 'this' }) + } + })) + + schemas.register('nbt_provider', ObjectWithType( + 'loot_nbt_provider_type', + 'string', 'target', 'minecraft:context', + null, + 'nbt_provider', + { + 'minecraft:storage': { + source: StringNode({ validator: 'resource', params: { pool: '$storage' } }) + }, + 'minecraft:context': { + target: Mod(StringNode({ enum: 'copy_source' }), { default: () => 'this' }) + } + } + )) + + schemas.register('level_based_value', ObjectWithType( + 'enchantment_level_based_value_type', + 'number', 'value', 'minecraft:constant', + null, + 'level_based_value', + { + 'minecraft:constant': { // TODO: doesn't actually exist in this form? + value: NumberNode(), + }, + 'minecraft:clamped': { + value: Reference('level_based_value'), + min: NumberNode(), + max: NumberNode(), + }, + 'minecraft:fraction': { + numerator: Reference('level_based_value'), + denominator: Reference('level_based_value'), + }, + 'minecraft:levels_squared': { + added: NumberNode(), + }, + 'minecraft:linear': { + base: NumberNode(), + per_level_above_first: NumberNode(), + }, + 'minecraft:lookup': { + values: ListNode(NumberNode()), + fallback: Reference('level_based_value') + } + })) + + FloatProvider = (config?: MinMaxConfig) => ObjectWithType( + 'float_provider_type', + 'number', 'value', 'minecraft:constant', + null, + 'float_provider', + { + 'minecraft:constant': { + value: NumberNode(config) + }, + 'minecraft:uniform': { + min_inclusive: NumberNode(config), + max_exclusive: NumberNode(config) + }, + 'minecraft:clamped_normal': { + min: NumberNode(), + max: NumberNode(), + mean: NumberNode(), + deviation: NumberNode() + }, + 'minecraft:trapezoid': { + min: NumberNode(), + max: NumberNode(), + plateau: NumberNode() + } + } + ) + + schemas.register('float_provider', FloatProvider()) + + IntProvider = (config?: MinMaxConfig) => ObjectWithType( + 'int_provider_type', + 'number', 'value', 'minecraft:constant', + null, + 'int_provider', + { + 'minecraft:constant': { + value: NumberNode({ integer: true, ...config }) + }, + 'minecraft:uniform': { + min_inclusive: NumberNode({ integer: true, ...config }), + max_inclusive: NumberNode({ integer: true, ...config }) + }, + 'minecraft:biased_to_bottom': { + min_inclusive: NumberNode({ integer: true, ...config }), + max_inclusive: NumberNode({ integer: true, ...config }) + }, + 'minecraft:clamped': { + min_inclusive: NumberNode({ integer: true, ...config }), + max_inclusive: NumberNode({ integer: true, ...config }), + source: Reference('int_provider') + }, + 'minecraft:clamped_normal': { + min_inclusive: NumberNode({ integer: true, ...config }), + max_inclusive: NumberNode({ integer: true, ...config }), + mean: NumberNode(), + deviation: NumberNode() + }, + 'minecraft:weighted_list': { + distribution: ListNode( + ObjectNode({ + weight: NumberNode({ integer: true }), + data: Reference('int_provider'), + }) + ) + } + } + ) + + schemas.register('int_provider', IntProvider()) + + schemas.register('vertical_anchor', ChoiceNode( + ['absolute', 'above_bottom', 'below_top'].map(t => ({ + type: t, + match: v => v?.[t] !== undefined, + change: v => ({ [t]: v.absolute ?? v.above_bottom ?? v.below_top ?? 0 }), + node: ObjectNode({ + [t]: NumberNode({ integer: true, min: -2048, max: 2047 }) + }) + })), + { context: 'vertical_anchor' } + )) + + schemas.register('height_provider', ObjectWithType( + 'height_provider_type', + 'number', 'value', 'minecraft:constant', + null, + 'height_provider', + { + 'minecraft:constant': { + value: Reference('vertical_anchor') + }, + 'minecraft:uniform': { + min_inclusive: Reference('vertical_anchor'), + max_inclusive: Reference('vertical_anchor') + }, + 'minecraft:biased_to_bottom': { + min_inclusive: Reference('vertical_anchor'), + max_inclusive: Reference('vertical_anchor'), + inner: Opt(NumberNode({ integer: true, min: 1 })) + }, + 'minecraft:very_biased_to_bottom': { + min_inclusive: Reference('vertical_anchor'), + max_inclusive: Reference('vertical_anchor'), + inner: Opt(NumberNode({ integer: true, min: 1 })) + }, + 'minecraft:trapezoid': { + min_inclusive: Reference('vertical_anchor'), + max_inclusive: Reference('vertical_anchor'), + plateau: Opt(NumberNode({ integer: true })) + }, + 'minecraft:weighted_list': { + distribution: ListNode( + ObjectNode({ + weight: NumberNode({ integer: true }), + data: Reference('height_provider'), + }) + ) + } + } + )) + + InclusiveRange = (config?: MinMaxConfig) => ObjectNode({ + min_inclusive: NumberNode(config), + max_inclusive: NumberNode(config) + }, { context: 'range' }) + + schemas.register('noise_parameters',Mod(ObjectNode({ + firstOctave: NumberNode({ integer: true }), + amplitudes: ListNode( + NumberNode() + ) + }, { context: 'noise_parameters' }), { + default: () => ({ + firstOctave: -7, + amplitudes: [1, 1] + }) + })) + + Tag = (config: TagConfig) => ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: config.resource, 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( + config.inlineSchema + ? ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: config.resource } }) + }, + { + type: 'object', + node: Reference(config.inlineSchema) + } + ], { choiceContext: 'tag.list' }) + : StringNode({ validator: 'resource', params: { pool: config.resource } }) + ), + change: (v: unknown) => { + if (typeof v === 'string' && !v.startsWith('#')) { + return [v] + } + return [""] + } + }, + ], { choiceContext: 'tag' }) + + Filterable = (node: INode) => ChoiceNode([ + { + type: 'simple', + match: () => true, + change: v => typeof v === 'object' && v?.raw ? v.raw : undefined, + node: node, + }, + { + type: 'filtered', + match: v => typeof v === 'object' && v !== null && v.raw !== undefined, + change: v => ({ raw: v }), + priority: 1, + node: ObjectNode({ + raw: node, + filtered: Opt(node), + }), + }, + ], { context: 'filterable' }) + + SizeLimitedString = ({ minLength, maxLength }: SizeLimitedStringConfig) => Mod(StringNode(), node => ({ + validate: (path, value, errors, options) => { + value = node.validate(path, value, errors, options) + if (minLength !== undefined && typeof value === 'string' && value.length < minLength) { + errors.add(path, 'error.invalid_string_range.smaller', value.length, minLength) + } + if (maxLength !== undefined && typeof value === 'string' && value.length > maxLength) { + errors.add(path, 'error.invalid_string_range.larger', value.length, maxLength) + } + return value + } + })) + + const ListOperationFields = ({ maxLength }: { maxLength: number }) => ({ + mode: StringNode({ enum: 'list_operation' }), + offset: Opt(Mod(NumberNode({ integer: true, min: 0 }), { + enabled: (path) => ['insert', 'replace_section'].includes(path.push("mode").get()) + })), + size: Opt(Mod(NumberNode({ integer: true, min: 0, max: maxLength }), { + enabled: (path) => ['replace_section'].includes(path.push("mode").get()) + })), + }) + + const ListOperation = ({ node, maxLength }: { node: INode, maxLength: number }) => ObjectNode({ + values: ListNode(node), + ...ListOperationFields({ maxLength }) + }, { context: 'list_operation'}) + + ConditionCases = (entitySourceNode: INode = StringNode({ enum: 'entity_source' })) => ({ + 'minecraft:all_of': { + terms: ListNode( + Reference('condition') + ) + }, + 'minecraft:any_of': { + terms: ListNode( + Reference('condition') + ) + }, + 'minecraft:block_state_property': { + block: StringNode({ validator: 'resource', params: { pool: 'block' } }), + properties: MapNode( + StringNode(), + StringNode(), + { validation: { validator: 'block_state_map', params: { id: ['pop', { push: 'block' }] } } } + ) + }, + 'minecraft:damage_source_properties': { + predicate: Reference('damage_source_predicate') + }, + 'minecraft:enchantment_active_check': { + active: BooleanNode(), + }, + 'minecraft:entity_properties': { + entity: entitySourceNode, + predicate: Reference('entity_predicate') + }, + 'minecraft:entity_scores': { + entity: entitySourceNode, + scores: MapNode( + StringNode({ validator: 'objective' }), + Reference('int_range') + ) + }, + 'minecraft:inverted': { + term: Reference('condition') + }, + 'minecraft:killed_by_player': { + inverse: Opt(BooleanNode()) + }, + 'minecraft:location_check': { + offsetX: Opt(NumberNode({ integer: true })), + offsetY: Opt(NumberNode({ integer: true })), + offsetZ: Opt(NumberNode({ integer: true })), + predicate: Reference('location_predicate') + }, + 'minecraft:match_tool': { + predicate: Reference('item_predicate') + }, + 'minecraft:random_chance': { + chance: Reference('number_provider') + }, + 'minecraft:random_chance_with_enchanted_bonus': { + enchantment: StringNode({ validator: 'resource', params: { pool: 'enchantment' } }), + unenchanted_chance: NumberNode({ min: 0, max: 1}), + enchanted_chance: Reference('level_based_value'), + }, + 'minecraft:reference': { + name: StringNode({ validator: 'resource', params: { pool: '$predicate' } }) + }, + 'minecraft:table_bonus': { + enchantment: StringNode({ validator: 'resource', params: { pool: 'enchantment' } }), + chances: ListNode( + NumberNode({ min: 0, max: 1 }), + { minLength: 1 }, + ) + }, + 'minecraft:time_check': { + value: Reference('int_range'), + period: Opt(NumberNode({ integer: true })) + }, + 'minecraft:value_check': { + value: Reference('number_provider'), + range: Reference('int_range') + }, + 'minecraft:weather_check': { + raining: Opt(BooleanNode()), + thundering: Opt(BooleanNode()) + } + }) + + FunctionCases = (conditions: NodeChildren, copySourceNode: INode = StringNode({ enum: 'copy_source' }), entitySourceNode: INode = StringNode({ enum: 'entity_source' })) => { + const cases: NestedNodeChildren = { + 'minecraft:apply_bonus': { + enchantment: StringNode({ validator: 'resource', params: { pool: 'enchantment' } }), + formula: StringNode({ validator: 'resource', params: { pool: collections.get('loot_table_apply_bonus_formula') } }), + parameters: Mod(ObjectNode({ + bonusMultiplier: Mod(NumberNode(), { + enabled: path => path.pop().push('formula').get() === 'minecraft:uniform_bonus_count' + }), + extra: Mod(NumberNode(), { + enabled: path => path.pop().push('formula').get() === 'minecraft:binomial_with_bonus_count' + }), + probability: Mod(NumberNode(), { + enabled: path => path.pop().push('formula').get() === 'minecraft:binomial_with_bonus_count' + }) + }), { + enabled: path => path.push('formula').get() !== 'minecraft:ore_drops' + }) + }, + 'minecraft:copy_components': { + source: StringNode({ enum: ['block_entity'] }), + include: Opt(ListNode( + StringNode({ validator: 'resource', params: { pool: 'data_component_type' } }), + )), + exclude: Opt(ListNode( + StringNode({ validator: 'resource', params: { pool: 'data_component_type' } }), + )), + }, + 'minecraft:copy_custom_data': { + source: Reference('nbt_provider'), + ops: ListNode( + ObjectNode({ + source: StringNode({ validator: 'nbt_path', params: { category: { getter: 'copy_source', path: ['pop', 'pop', 'pop', { push: 'source' }] } } }), + target: StringNode({ validator: 'nbt_path', params: { category: 'minecraft:item' } }), + op: StringNode({ enum: ['replace', 'append', 'merge'] }) + }, { context: 'nbt_operation' }) + ) + }, + 'minecraft:copy_name': { + source: copySourceNode + }, + 'minecraft:copy_state': { + block: StringNode({ validator: 'resource', params: { pool: 'block' } }), + properties: ListNode( + StringNode({ validator: 'block_state_key', params: { id: ['pop', 'pop', { push: 'block' }] } }) + ) + }, + 'minecraft:enchant_randomly': { + options: Opt(Tag({ resource: 'enchantment' })), + only_compatible: Opt(BooleanNode()), + }, + 'minecraft:enchant_with_levels': { + levels: Reference('number_provider'), + options: Opt(Tag({ resource: 'enchantment' })), + }, + 'minecraft:enchanted_count_increase': { + enchantment: StringNode({ validator: 'resource', params: { pool: 'enchantment' }}), + count: Reference('number_provider'), + limit: Opt(NumberNode({ integer: true })) + }, + 'minecraft:exploration_map': { + destination: Opt(StringNode({ validator: 'resource', params: { pool: '$tag/worldgen/structure' } })), + decoration: Opt(StringNode({ enum: 'map_decoration' })), + zoom: Opt(NumberNode({ integer: true })), + search_radius: Opt(NumberNode({ integer: true })), + skip_existing_chunks: Opt(BooleanNode()) + }, + 'minecraft:fill_player_head': { + entity: entitySourceNode + }, + 'minecraft:filtered': { + item_filter: Reference('item_predicate'), + modifier: Reference('item_modifier'), + }, + 'minecraft:limit_count': { + limit: Reference('int_range') + }, + 'minecraft:modify_contents': { + component: StringNode({ validator: 'resource', params: { pool: collections.get('container_component_manipulators') } }), + modifier: Reference('item_modifier'), + }, + 'minecraft:reference': { + name: StringNode({ validator: 'resource', params: { pool: '$item_modifier' } }) + }, + 'minecraft:sequence': { + functions: ListNode( + Reference('function') + ), + }, + 'minecraft:set_attributes': { + modifiers: ListNode( + Reference('attribute_modifier') + ), + replace: Opt(BooleanNode()), + }, + 'minecraft:set_banner_pattern': { + patterns: ListNode( + ObjectNode({ + pattern: StringNode({ enum: 'banner_pattern' }), + color: StringNode({ enum: 'dye_color' }) + }) + ), + append: BooleanNode() + }, + 'minecraft:set_book_cover': { + title: Opt(Filterable(SizeLimitedString({ maxLength: 32 }))), + author: Opt(StringNode()), + generation: Opt(NumberNode({ integer: true, min: 0, max: 3 })), + }, + 'minecraft:set_components': { + components: Reference('data_component_patch'), + }, + 'minecraft:set_contents': { + component: StringNode({ validator: 'resource', params: { pool: collections.get('container_component_manipulators') } }), + entries: ListNode( + Reference('loot_entry') + ) + }, + 'minecraft:set_count': { + count: Reference('number_provider'), + add: Opt(BooleanNode()) + }, + 'minecraft:set_custom_data': { + tag: Reference('custom_data_component'), + }, + 'minecraft:set_custom_model_data': { + value: Reference('number_provider'), + }, + 'minecraft:set_damage': { + damage: Reference('number_provider'), + add: Opt(BooleanNode()) + }, + 'minecraft:set_enchantments': { + enchantments: MapNode( + StringNode({ validator: 'resource', params: { pool: 'enchantment' } }), + Reference('number_provider') + ), + add: Opt(BooleanNode()) + }, + 'minecraft:set_fireworks': { + explosions: Opt(ListOperation({ + node: Reference('firework_explosion'), + maxLength: 256, + })), + flight_duration: Opt(NumberNode({ integer: true, min: 0, max: 255 })), + }, + 'minecraft:set_firework_explosion': { + shape: Opt(StringNode({ enum: 'firework_explosion_shape' })), + colors: Opt(ListNode( + NumberNode({ color: true }), + )), + fade_colors: Opt(ListNode( + NumberNode({ color: true }), + )), + trail: Opt(BooleanNode()), + twinkle: Opt(BooleanNode()), + }, + 'minecraft:set_instrument': { + options: StringNode({ validator: 'resource', params: { pool: 'instrument', requireTag: true } }) + }, + 'minecraft:set_item': { + item: StringNode({ validator: 'resource', params: { pool: 'item' } }), + }, + 'minecraft:set_loot_table': { + type: StringNode({ validator: 'resource', params: { pool: 'block_entity_type' } }), + name: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }), + seed: Opt(NumberNode({ integer: true })) + }, + 'minecraft:set_lore': { + entity: Opt(entitySourceNode), + lore: ListNode( + Reference('text_component'), + { maxLength: 256 }, + ), + ...ListOperationFields({ maxLength: 256 }), + }, + 'minecraft:set_name': { + entity: Opt(entitySourceNode), + target: Opt(StringNode({ enum: ['custom_name', 'item_name'] })), + name: Opt(Reference('text_component')) + }, + 'minecraft:set_ominous_bottle_amplifier': { + amplifier: Reference('number_provider'), + }, + 'minecraft:set_potion': { + id: StringNode({ validator: 'resource', params: { pool: 'potion' } }) + }, + 'minecraft:ominous_bottle_amplifier': { + amplifier: Reference('number_provider') + }, + 'minecraft:set_stew_effect': { + effects: Opt(ListNode( + ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }), + duration: Reference('number_provider') + }) + )) + }, + 'minecraft:set_writable_book_pages': { + pages: ListNode( + Filterable(SizeLimitedString({ maxLength: 1024 })), + { maxLength: 100 }, + ), + ...ListOperationFields({ maxLength: 100 }), + }, + 'minecraft:set_written_book_pages': { + pages: ListNode( + Filterable(Reference('text_component')), + { maxLength: 100 }, + ), + ...ListOperationFields({ maxLength: 100 }), + }, + 'minecraft:toggle_tooltips': { + toggles: MapNode( + StringNode({ validator: 'resource', params: { pool: collections.get('toggleable_data_component_type') }}), + BooleanNode(), + ), + }, + } + const res: NestedNodeChildren = {} + collections.get('loot_function_type').forEach(f => { + res[f] = {...cases[f], ...(f === 'minecraft:sequence' ? {} : conditions) } + }) + return res + } + + NoiseSettingsPresets = (node: INode) => ObjectOrPreset( + StringNode({ validator: 'resource', params: { pool: '$worldgen/noise_settings' } }), + node, + { + 'minecraft:overworld': DefaultNoiseSettings, + 'minecraft:nether': { + sea_level: 32, + ore_veins_enabled: false, + disable_mob_generation: false, + aquifers_enabled: false, + legacy_random_source: true, + default_block: { + Name: 'minecraft:netherrack' + }, + default_fluid: { + Properties: { + level: '0' + }, + Name: 'minecraft:lava' + }, + noise: { + min_y: 0, + height: 128, + size_horizontal: 1, + size_vertical: 2, + top_slide: { + target: 0.9375, + size: 3, + offset: 0 + }, + bottom_slide: { + target: 2.5, + size: 4, + offset: -1 + }, + sampling: { + xz_scale: 1.0, + y_scale: 3.0, + xz_factor: 80.0, + y_factor: 60.0 + }, + terrain_shaper: { + offset: 0, + factor: 0, + jaggedness: 0 + } + }, + noise_router: { + barrier: 0, + fluid_level_floodedness: 0, + fluid_level_spread: 0, + lava: 0, + temperature: 0, + vegetation: 0, + continents: 0, + erosion: 0, + depth: 0, + ridges: 0, + initial_density_without_jaggedness: 0, + final_density: { + type: 'minecraft:interpolated', + argument: 'minecraft:overworld/base_3d_noise' + }, + vein_toggle: 0, + vein_ridged: 0, + vein_gap: 0, + }, + surface_rule: { + type: 'minecraft:sequence', + sequence: [] + } + }, + 'minecraft:end': { + sea_level: 0, + ore_veins_enabled: false, + disable_mob_generation: false, + aquifers_enabled: false, + legacy_random_source: true, + default_block: { + Name: 'minecraft:end_stone' + }, + default_fluid: { + Name: 'minecraft:air' + }, + noise: { + min_y: 0, + height: 128, + size_horizontal: 2, + size_vertical: 1, + top_slide: { + target: -23.4375, + size: 64, + offset: -46 + }, + bottom_slide: { + target: -0.234375, + size: 7, + offset: 1 + }, + sampling: { + xz_scale: 2.0, + y_scale: 1.0, + xz_factor: 80.0, + y_factor: 160.0 + }, + terrain_shaper: { + offset: 0, + factor: 1, + jaggedness: 0 + } + }, + noise_router: { + barrier: 0, + fluid_level_floodedness: 0, + fluid_level_spread: 0, + lava: 0, + temperature: 0, + vegetation: 0, + continents: 0, + erosion: 0, + depth: 0, + ridges: 0, + initial_density_without_jaggedness: 0, + final_density: { + type: 'minecraft:interpolated', + argument: 'minecraft:overworld/base_3d_noise' + }, + vein_toggle: 0, + vein_ridged: 0, + vein_gap: 0, + }, + surface_rule: { + type: 'minecraft:sequence', + sequence: [] + } + }, + 'minecraft:amplified': { + sea_level: 63, + ore_veins_enabled: true, + disable_mob_generation: false, + aquifers_enabled: true, + legacy_random_source: false, + default_block: { + Name: 'minecraft:stone' + }, + default_fluid: { + Properties: { + level: '0' + }, + Name: 'minecraft:water' + }, + noise: { + min_y: -64, + height: 384, + size_horizontal: 1, + size_vertical: 2, + top_slide: { + target: -0.078125, + size: 2, + offset: 8 + }, + bottom_slide: { + target: 0.1171875, + size: 3, + offset: 0 + }, + sampling: { + xz_scale: 0.9999999814507745, + y_scale: 0.9999999814507745, + xz_factor: 80, + y_factor: 160 + }, + terrain_shaper: { + offset: 0, + factor: 0, + jaggedness: 0 + } + }, + noise_router: { + barrier: 0, + fluid_level_floodedness: 0, + fluid_level_spread: 0, + lava: 0, + temperature: 0, + vegetation: 0, + continents: 0, + erosion: 0, + depth: 0, + ridges: 0, + initial_density_without_jaggedness: 0, + final_density: { + type: 'minecraft:interpolated', + argument: 'minecraft:overworld/base_3d_noise' + }, + vein_toggle: 0, + vein_ridged: 0, + vein_gap: 0, + }, + surface_rule: { + type: 'minecraft:sequence', + sequence: [] + }, + }, + 'minecraft:caves': { + sea_level: 32, + ore_veins_enabled: false, + disable_mob_generation: false, + aquifers_enabled: false, + legacy_random_source: true, + default_block: { + Name: 'minecraft:stone' + }, + default_fluid: { + Properties: { + level: '0' + }, + Name: 'minecraft:water' + }, + noise: { + min_y: 0, + height: 128, + size_horizontal: 1, + size_vertical: 2, + top_slide: { + target: 0.9375, + size: 3, + offset: 0 + }, + bottom_slide: { + target: 2.5, + size: 4, + offset: -1 + }, + sampling: { + xz_scale: 1.0, + y_scale: 3.0, + xz_factor: 80.0, + y_factor: 60.0 + }, + terrain_shaper: { + offset: 0, + factor: 0, + jaggedness: 0 + } + }, + noise_router: { + barrier: 0, + fluid_level_floodedness: 0, + fluid_level_spread: 0, + lava: 0, + temperature: 0, + vegetation: 0, + continents: 0, + erosion: 0, + depth: 0, + ridges: 0, + initial_density_without_jaggedness: 0, + final_density: { + type: 'minecraft:interpolated', + argument: 'minecraft:overworld/base_3d_noise' + }, + vein_toggle: 0, + vein_ridged: 0, + vein_gap: 0, + }, + surface_rule: { + type: 'minecraft:sequence', + sequence: [] + } + }, + 'minecraft:floating_islands': { + sea_level: 0, + ore_veins_enabled: false, + disable_mob_generation: false, + aquifers_enabled: false, + legacy_random_source: true, + default_block: { + Name: 'minecraft:stone' + }, + default_fluid: { + Properties: { + level: '0' + }, + Name: 'minecraft:water' + }, + noise: { + min_y: 0, + height: 128, + size_horizontal: 2, + size_vertical: 1, + top_slide: { + target: -23.4375, + size: 64, + offset: -46 + }, + bottom_slide: { + target: -0.234375, + size: 7, + offset: 1 + }, + sampling: { + xz_scale: 2.0, + y_scale: 1.0, + xz_factor: 80.0, + y_factor: 160.0 + }, + terrain_shaper: { + offset: 0, + factor: 1, + jaggedness: 0 + } + }, + noise_router: { + barrier: 0, + fluid_level_floodedness: 0, + fluid_level_spread: 0, + lava: 0, + temperature: 0, + vegetation: 0, + continents: 0, + erosion: 0, + depth: 0, + ridges: 0, + initial_density_without_jaggedness: 0, + final_density: { + type: 'minecraft:interpolated', + argument: 'minecraft:overworld/base_3d_noise' + }, + vein_toggle: 0, + vein_ridged: 0, + vein_gap: 0, + }, + surface_rule: { + type: 'minecraft:sequence', + sequence: [] + } + } + } + ) +} diff --git a/java/1.21.4/src/schemas/Components.ts b/java/1.21.4/src/schemas/Components.ts new file mode 100644 index 00000000..4ac2450f --- /dev/null +++ b/java/1.21.4/src/schemas/Components.ts @@ -0,0 +1,556 @@ +import { + BooleanNode, + Case, + StringNode as RawStringNode, + ListNode, + MapNode, + ObjectNode, + Opt, + Reference as RawReference, + SchemaRegistry, + ChoiceNode, + CollectionRegistry, + NumberNode, + Switch, + SwitchNode, + INode, + Mod, + ModelPath, +} from '@mcschema/core' +import { Filterable, SizeLimitedString, Tag } from './Common' + +export function initComponentsSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('custom_data_component', ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'nbt', params: { registry: { category: 'minecraft:item', id: ['pop', 'pop', { push: 'item' }] } } }), + }, + { + type: 'object', + node: ObjectNode({}) // TODO: any unsafe data + }, + ], { context: 'custom_data' })) + + schemas.register('enchantments_component', ChoiceNode([ + { + type: 'simple', + match: () => true, + node: MapNode( + StringNode({ validator: 'resource', params: { pool: 'enchantment' } }), + NumberNode({ integer: true, min: 0, max: 255 }), + ), + change: v => v.levels ?? {} + }, + { + type: 'full', + match: v => typeof v === 'object' && typeof v?.levels === 'object', + node: ObjectNode({ + levels: MapNode( + StringNode({ validator: 'resource', params: { pool: 'enchantment' } }), + NumberNode({ integer: true, min: 0, max: 255 }), + ), + show_in_tooltip: Opt(BooleanNode()), + }), + change: v => ({ levels: v ?? {} }), + priority: 1, + } + ], { context: 'enchantments' })) + + schemas.register('adventure_mode_predicate', ChoiceNode([ + { + type: 'simple', + match: () => true, + node: Reference('block_predicate'), + change: v => typeof v === 'object' && Array.isArray(v?.predicates) && typeof v.predicates[0] === 'object' ? v.predicates[0] : {} + }, + { + type: 'full', + match: v => typeof v === 'object' && Array.isArray(v?.predicates), + node: ObjectNode({ + predicates: ListNode( + Reference('block_predicate'), + { minLength: 1 }, + ), + show_in_tooltip: Opt(BooleanNode()), + }), + change: v => ({ + predicates: [typeof v === 'object' && v !== null ? v : {}], + }), + priority: 1, + }, + ], { context: 'adventure_mode_predicate' })) + + schemas.register('attribute_modifiers_entry', ObjectNode({ + id: StringNode(), + type: StringNode({ validator: 'resource', params: { pool: 'attribute' } }), + amount: NumberNode(), + operation: StringNode({ enum: 'attribute_modifier_operation' }), + slot: Opt(StringNode({ enum: 'equipment_slot_group' })), + }, { context: 'attribute_modifier' })) + + schemas.register('map_decoration', ObjectNode({ + type: StringNode({ enum: 'map_decoration' }), + x: NumberNode(), + z: NumberNode(), + rotation: NumberNode(), + }, { context: 'map_decoration' })) + + const MobEffectDetails = { + amplifier: Opt(NumberNode({ integer: true, min: 0, max: 255 })), + duration: Opt(NumberNode({ integer: true })), + ambient: Opt(BooleanNode()), + show_particles: Opt(BooleanNode()), + show_icon: Opt(BooleanNode()), + hidden_effect: Opt(Reference('mob_effect_details')) // recusive + } + + schemas.register('mob_effect_details', ObjectNode({ + ...MobEffectDetails, + }, { context: 'mob_effect_instance' } )) + + schemas.register('mob_effect_instance', ObjectNode({ + id: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }), + ...MobEffectDetails, + }, { context: 'mob_effect_instance' })) + + schemas.register('suspicious_stew_effect_instance', ObjectNode({ + id: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }), + duration: NumberNode({ integer: true }), + }, { context: 'suspicious_stew_effect_instance' })) + + schemas.register('firework_explosion', ObjectNode({ + shape: StringNode({ enum: 'firework_explosion_shape' }), + colors: Opt(ListNode( + NumberNode({ color: true }), + )), + fade_colors: Opt(ListNode( + NumberNode({ color: true }), + )), + has_trail: Opt(BooleanNode()), + has_twinkle: Opt(BooleanNode()), + }, { context: 'firework_explosion' })) + + schemas.register('player_name', Mod(SizeLimitedString({ maxLength: 16 }), node => ({ + validate: (path, value, errors, options) => { + value = node.validate(path, value, errors, options) + if (typeof value === 'string' && value.split('').map(c => c.charCodeAt(0)).some(c => c <= 32 || c >= 127)) { + errors.add(path, 'error.invalid_player_name') + } + return value + } + }))) + + schemas.register('consume_effect', ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'consume_effect_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:apply_effects': { + effects: ListNode(Reference('mob_effect_instance')), + probability: Opt(NumberNode({ min: 0, max: 1 })), + }, + 'minecraft:remove_effects': { + effects: Tag({ resource: 'mob_effect' }) + }, + 'minecraft:teleport_randomly': { + diameter: NumberNode({ min: 0 }), + }, + 'minecraft:play_sound': { + sound: Reference('sound_event'), + }, + } + })) + + const Components: Record = { + 'minecraft:attribute_modifiers': ChoiceNode([ + { + type: 'list', + node: ListNode( + Reference('attribute_modifiers_entry'), + ), + change: v => v.modifiers + }, + { + type: 'object', + node: ObjectNode({ + modifiers: ListNode( + Reference('attribute_modifiers_entry'), + ), + show_in_tooltip: Opt(BooleanNode()), + }), + change: v => ({ modifiers: v }) + } + ], { context: 'data_component.attribute_modifiers' }), + 'minecraft:banner_patterns': ListNode( + ObjectNode({ + pattern: ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: 'banner_pattern' } }), + }, + { + type: 'object', + node: Reference('banner_pattern') + }, + ]), + color: StringNode({ enum: 'dye_color' }), + }), + { context: 'data_component.banner_patterns' }, + ), + 'minecraft:base_color': StringNode({ enum: 'dye_color' }), + 'minecraft:bees': ListNode( + ObjectNode({ + entity_data: ObjectNode({ + // TODO: any unsafe data + }), + ticks_in_hive: NumberNode({ integer: true }), + min_ticks_in_hive: NumberNode({ integer: true }), + }), + { context: 'data_component.bees' }, + ), + 'minecraft:block_entity_data': ObjectNode({ + id: StringNode({ validator: 'resource', params: { pool: 'block_entity_type' } }), + // TODO: any unsafe data + }, { context: 'data_component.block_entity_data' }), + 'minecraft:block_state': MapNode( + StringNode(), + StringNode(), + { context: 'data_component.block_state' }, + ), + 'minecraft:bucket_entity_data': ObjectNode({ + // TODO: any unsafe data + }, { context: 'data_component.bucket_entity_data' }), + 'minecraft:bundle_contents': ListNode( + Reference('item_stack'), + { context: 'data_component.bundle_contents', maxLength: 64 }, + ), + 'minecraft:can_break': Reference('adventure_mode_predicate'), + 'minecraft:can_place_on': Reference('adventure_mode_predicate'), + 'minecraft:charged_projectiles': ListNode( + Reference('item_stack'), + { context: 'data_component.charged_projectiles' }, + ), + 'minecraft:consumable': ObjectNode({ + consume_seconds: Opt(NumberNode({ min: 0 })), + animation: Opt(StringNode({ enum: 'use_animation' })), + sound: Opt(Reference('sound_event')), + has_consume_particles: Opt(BooleanNode()), + on_consume_effects: Opt(ListNode(Reference('consume_effect'))) + }), + 'minecraft:container': ListNode( + ObjectNode({ + slot: NumberNode({ integer: true, min: 0, max: 255 }), + item: Reference('item_stack'), + }), + { context: 'data_component.container', maxLength: 256 }, + ), + 'minecraft:container_loot': ObjectNode({ + loot_table: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }), + seed: Opt(NumberNode({ integer: true })), + }, { context: 'data_component.container_loot' }), + 'minecraft:custom_data': Reference('custom_data_component'), + 'minecraft:custom_model_data': NumberNode({ integer: true }), + 'minecraft:custom_name': StringNode(), // text component + 'minecraft:damage': NumberNode({ integer: true, min: 0 }), + 'minecraft:damage_resistant': ObjectNode({ + types: StringNode({ validator: 'resource', params: { pool: '$damage_type', requireTag: true } }) + }), + 'minecraft:death_protection': ObjectNode({ + death_effects: Opt(ListNode(Reference('consume_effect'))), + }), + 'minecraft:debug_stick_state': MapNode( + StringNode({ validator: 'resource', params: { pool: 'block' } }), + StringNode(), // TODO: block state key validation + { context: 'data_component.debug_stick_state' }, + ), + 'minecraft:dyed_color': ChoiceNode([ + { + type: 'number', + node: NumberNode({ color: true }), + change: v => v.rgb + }, + { + type: 'object', + node: ObjectNode({ + rgb: NumberNode({ color: true }), + show_in_tooltip: Opt(BooleanNode()), + }), + change: v => ({ rgb: v }) + } + ], { context: 'data_component.dyed_color' }), + 'minecraft:enchantable': ObjectNode({ + value: NumberNode({ integer: true, min: 1 }), + }), + 'minecraft:enchantment_glint_override': BooleanNode(), + 'minecraft:enchantments': Reference('enchantments_component'), + 'minecraft:entity_data': ObjectNode({ + id: StringNode({ validator: 'resource', params: { pool: 'entity_type' } }), + // TODO: any unsafe data + }, { context: 'data_component.entity_data' }), + 'minecraft:equippable': ObjectNode({ + slot: StringNode({ enum: 'equipment_slot' }), + equip_sound: Opt(Reference('sound_event')), + model: Opt(StringNode()), + allowed_entities: Opt(Tag({ resource: 'entity_type' })), + dispensable: Opt(BooleanNode()), + swappable: Opt(BooleanNode()), + damage_on_hurt: Opt(BooleanNode()), + }), + 'minecraft:firework_explosion': Reference('firework_explosion'), + 'minecraft:fireworks': ObjectNode({ + flight_duration: Opt(NumberNode({ integer: true, min: 0, max: 255 })), + explosions: ListNode( + Reference('firework_explosion'), + { maxLength: 256 }, + ), + }, { context: 'data_component.fireworks' }), + 'minecraft:food': ObjectNode({ + nutrition: NumberNode({ integer: true, min: 0 }), + saturation: NumberNode(), + can_always_eat: Opt(BooleanNode()), + }, { context: 'data_component.food' }), + 'minecraft:glider': ObjectNode({}), + 'minecraft:hide_additional_tooltip': ObjectNode({}), + 'minecraft:hide_tooltip': ObjectNode({}), + 'minecraft:instrument': ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: 'instrument' } }), + }, + { + type: 'object', + node: Reference('instrument'), + }, + ], { context: 'data_component.instrument' }), + 'minecraft:intangible_projectile': ObjectNode({}), + 'minecraft:item_model': StringNode(), + 'minecraft:item_name': StringNode(), // text component + 'minecraft:jukebox_playable': ObjectNode({ + song: ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: 'jukebox_song' } }), + }, + { + type: 'object', + node: Reference('jukebox_song'), + }, + ]), + show_in_tooltip: Opt(BooleanNode()), + }, { context: 'data_component.jukebox_playable' }), + 'minecraft:lock': StringNode(), + 'minecraft:lodestone_tracker': ObjectNode({ + target: Opt(ObjectNode({ + dimension: StringNode({ validator: 'resource', params: { pool: '$dimension' } }), + pos: Reference('block_pos'), + })), + tracked: Opt(BooleanNode()), + }, { context: 'data_component.lodestone_tracker' }), + 'minecraft:lore': ListNode( + StringNode(), // text component + { context: 'data_component.lore', maxLength: 256 }, + ), + 'minecraft:map_color': NumberNode({ color: true }), + 'minecraft:map_decorations': MapNode( + StringNode(), + Reference('map_decoration'), + { context: 'data_component.map_decorations' }, + ), + 'minecraft:map_id': NumberNode({ integer: true }), + 'minecraft:max_damage': NumberNode({ integer: true, min: 1 }), + 'minecraft:max_stack_size': NumberNode({ integer: true, min: 0, max: 99 }), + 'minecraft:note_block_sound': StringNode({ validator: 'resource', params: { pool: [], allowUnknown: true } }), + 'minecraft:ominous_bottle_amplifier': NumberNode({ integer: true, min: 0, max: 4 }), + 'minecraft:pot_decorations': ListNode( + StringNode({ validator: 'resource', params: { pool: 'item' } }), + { context: 'data_component.pot_decorations', maxLength: 4 }, + ), + 'minecraft:potion_contents': ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: 'potion' } }), + change: v => v.potion + }, + { + type: 'object', + node: ObjectNode({ + potion: Opt(StringNode({ validator: 'resource', params: { pool: 'potion' } })), + custom_color: Opt(NumberNode({ color: true })), + custom_name: Opt(StringNode()), + custom_effects: Opt(ListNode( + Reference('mob_effect_instance'), + )), + }), + change: v => ({ potion: v}) + } + ], { context: 'data_component.potion_contents' }), + 'minecraft:profile': ChoiceNode([ + { + type: 'string', + node: Reference('player_name'), + change: v => v.name + }, + { + type: 'object', + node: ObjectNode({ + name: Opt(Reference('player_name')), + id: Opt(ListNode( + NumberNode({ integer: true }), + { minLength: 4, maxLength: 4 }, + )), + properties: Opt(ChoiceNode([ + { + type: 'list', + node: ListNode(ObjectNode({ + name: StringNode(), + value: StringNode(), + signature: Opt(StringNode()), + })), + change: v => { + const result: Record[] = [] + if (typeof v !== 'object' || v === null) return result + for (const [name, values] of Object.entries(v)) { + if (!Array.isArray(values)) continue + result.push(...values.map(value => ({ name, value }))) + } + return result + } + }, + { + type: 'object', + node: MapNode( + StringNode(), + ListNode(StringNode()), + ), + change: v => { + const result: Record = {} + for (const e of Array.isArray(v) ? v : []) { + if (typeof e !== 'object' || e === null) continue + result[e.name ?? ''] = [...(result[e.name ?? ''] ?? []), e.value ?? ''] + } + return result + }, + } + ])), + }), + change: v => ({ name: v }) + } + ], { context: 'data_component.profile' }), + 'minecraft:rarity': StringNode({ enum: 'rarity' }), + 'minecraft:recipes': ListNode( + StringNode({ validator: 'resource', params: { pool: '$recipe' } }), + { context: 'data_component.recipes' }, + ), + 'minecraft:repair_cost': NumberNode({ integer: true, min: 0 }), + 'minecraft:repairable': ObjectNode({ + items: Tag({ resource: 'item' }), + }), + 'minecraft:stored_enchantments': Reference('enchantments_component'), + 'minecraft:suspicious_stew_effects': ListNode( + Reference('suspicious_stew_effect_instance'), + { context: 'data_component.suspicious_stew_effects' }, + ), + 'minecraft:tool': ObjectNode({ + rules: ListNode( + ObjectNode({ + blocks: Tag({ resource: 'block' }), + speed: Opt(NumberNode({ min: 1 })), + correct_for_drops: Opt(BooleanNode()), + }), + ), + default_mining_speed: Opt(NumberNode()), + damage_per_block: Opt(NumberNode({ integer: true, min: 0 })), + }), + 'minecraft:tooltip_style': StringNode(), + 'minecraft:trim': ObjectNode({ + material: ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: '$trim_material' } }), + }, + { + type: 'object', + node: Reference('trim_material'), + }, + ]), + pattern: ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: '$trim_pattern' } }), + }, + { + type: 'object', + node: Reference('trim_pattern'), + }, + ]), + show_in_tooltip: Opt(BooleanNode()), + }, { context: 'data_component.trim' }), + 'minecraft:unbreakable': ObjectNode({ + show_in_tooltip: Opt(BooleanNode()) + }, { context: 'data_component.unbreakable' }), + 'minecraft:use_cooldown': ObjectNode({ + seconds: NumberNode({ min: 0 }), + cooldown_group: StringNode(), + }), + 'minecraft:use_remainder': Reference('item_stack'), + 'minecraft:writable_book_content': ObjectNode({ + pages: Opt(ListNode( + Filterable(SizeLimitedString({ maxLength: 1024 })), + )), + }, { context: 'data_component.writable_book_content' }), + 'minecraft:written_book_content': ObjectNode({ + title: SizeLimitedString({ maxLength: 32 }), + author: StringNode(), + generation: Opt(NumberNode({ integer: true, min: 0, max: 3 })), + pages: Opt(ListNode( + Filterable(SizeLimitedString({ maxLength: 32767 })), // text component + )), + resolved: Opt(BooleanNode()), + }, { context: 'data_component.written_book_content' }), + } + + const keyMatches = (key: string) => (path: ModelPath) => { + let last = path.last().toString() + if (!last.startsWith('minecraft:')) last = 'minecraft:' + last + return last === key + } + + schemas.register('data_component_predicate', MapNode( + StringNode({ validator: 'resource', params: { pool: 'data_component_type' } }), + SwitchNode([ + ...Object.entries(Components).map(([key, value]) => ({ + match: keyMatches(key), + node: value, + })), + { + match: () => true, + node: ObjectNode({}), // default for unknown components + }, + ]), + { context: 'data_component_predicate' } + )) + + schemas.register('data_component_patch', MapNode( + StringNode({ enum: [ + ...collections.get('data_component_type'), + ...collections.get('data_component_type').map(k => '!' + k), + ] }), + SwitchNode([ + { + match: (path: ModelPath) => path.last().toString().startsWith('!'), + node: ObjectNode({}), + }, + ...Object.entries(Components).map(([key, value]) => ({ + match: keyMatches(key), + node: value, + })), + { + match: () => true, + node: ObjectNode({}), // default for unknown components + }, + ]), + { context: 'data_component_patch' } + )) +} diff --git a/java/1.21.4/src/schemas/Condition.ts b/java/1.21.4/src/schemas/Condition.ts new file mode 100644 index 00000000..3f969f69 --- /dev/null +++ b/java/1.21.4/src/schemas/Condition.ts @@ -0,0 +1,32 @@ +import { + Case, + StringNode as RawStringNode, + ObjectNode, + ObjectOrList, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, + Mod, +} from '@mcschema/core' +import { ConditionCases } from './Common' + +export function initConditionSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('predicate', ObjectOrList( + Reference('condition'), { choiceContext: 'condition' } + )) + + schemas.register('condition', Mod(ObjectNode({ + condition: StringNode({ validator: 'resource', params: { pool: 'loot_condition_type' } }), + [Switch]: [{ push: 'condition' }], + [Case]: ConditionCases() + }, { category: 'predicate', context: 'condition' }), { + default: () => ({ + condition: 'minecraft:entity_properties', + entity: 'this' + }) + })) +} diff --git a/java/1.21.4/src/schemas/DamageType.ts b/java/1.21.4/src/schemas/DamageType.ts new file mode 100644 index 00000000..b916ee29 --- /dev/null +++ b/java/1.21.4/src/schemas/DamageType.ts @@ -0,0 +1,27 @@ +import { + StringNode as RawStringNode, + Mod, + NumberNode, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + Opt, +} from '@mcschema/core' + +export function initDamageTypeSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('damage_type', Mod(ObjectNode({ + message_id: StringNode(), + exhaustion: NumberNode(), + scaling: StringNode({ enum: 'damage_scaling' }), + effects: Opt(StringNode({ enum: 'damage_effects' })), + death_message_type: Opt(StringNode({ enum: 'death_message_type' })), + }, { context: 'damage_type' }), { + default: () => ({ + message_id: 'generic', + exhaustion: 0, + scaling: 'when_caused_by_living_non_player', + }) + })) +} diff --git a/java/1.21.4/src/schemas/Dimension.ts b/java/1.21.4/src/schemas/Dimension.ts new file mode 100644 index 00000000..f7a2b1ee --- /dev/null +++ b/java/1.21.4/src/schemas/Dimension.ts @@ -0,0 +1,129 @@ +import { + BooleanNode, + Case, + StringNode as RawStringNode, + ListNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, + Opt, + INode, + ChoiceNode, +} from '@mcschema/core' +import { NoiseSettingsPresets, Tag } from './Common' + +export function initDimensionSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('dimension', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: '$dimension_type' } }), + generator: ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/chunk_generator' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:noise': { + settings: NoiseSettingsPresets(Reference('noise_settings')), + biome_source: ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/biome_source' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:fixed': { + biome: StringNode({ validator: 'resource', params: { pool: '$worldgen/biome' } }) + }, + 'minecraft:multi_noise': { + preset: Opt(StringNode({ validator: 'resource', params: { pool: ['minecraft:overworld', 'minecraft:nether'] } })), + biomes: Mod(ListNode( + Reference('generator_biome') + ), { + enabled: path => path.push('preset').get() === undefined, + default: () => [{ + biome: 'minecraft:plains' + }] + }) + }, + 'minecraft:checkerboard': { + scale: Opt(NumberNode({ integer: true, min: 0, max: 62 })), + biomes: Tag({ resource: '$worldgen/biome' }) + }, + } + }, { category: 'predicate', disableSwitchContext: true }) + }, + 'minecraft:flat': { + settings: Reference('flat_generator_settings') + } + } + }, { disableSwitchContext: true }) + }, { category: 'pool', context: 'dimension' }), { + default: () => { + return { + type: 'minecraft:overworld', + generator: { + type: 'minecraft:noise', + biome_source: { + type: 'minecraft:fixed', + biome: 'minecraft:plains' + }, + settings: 'minecraft:overworld' + } + }} + })) + + schemas.register('flat_generator_settings', ObjectNode({ + biome: Opt(StringNode({ validator: 'resource', params: { pool: '$worldgen/biome' } })), + lakes: Opt(BooleanNode()), + features: Opt(BooleanNode()), + layers: ListNode( + Reference('generator_layer') + ), + structure_overrides: Tag({ resource: '$worldgen/structure_set' }) + })) + + const ClimateParameter = ChoiceNode([ + { + type: 'number', + node: NumberNode({ min: -2, max: 2 }), + change: (v: any) => v[0] ?? 0 + }, + { + type: 'list', + node: ListNode( + NumberNode({ min: -2, max: 2 }), + { minLength: 2, maxLength: 2 }, + ), + change: (v: any) => [v ?? 0, v ?? 0] + } + ]) + + schemas.register('parameter_point', ObjectNode({ + temperature: ClimateParameter, + humidity: ClimateParameter, + continentalness: ClimateParameter, + erosion: ClimateParameter, + weirdness: ClimateParameter, + depth: ClimateParameter, + offset: NumberNode({ min: 0, max: 1 }) + })) + + schemas.register('generator_biome', Mod(ObjectNode({ + biome: StringNode({ validator: 'resource', params: { pool: '$worldgen/biome' } }), + parameters: Reference('parameter_point'), + }, { context: 'generator_biome' }), { + default: () => ({ + biome: 'minecraft:plains', + parameters: { + temperature: 0, + humidity: 0, + continentalness: 0, + erosion: 0, + weirdness: 0, + depth: 0, + offset: 0 + } + }) + })) +} diff --git a/java/1.21.4/src/schemas/DimensionType.ts b/java/1.21.4/src/schemas/DimensionType.ts new file mode 100644 index 00000000..0034679e --- /dev/null +++ b/java/1.21.4/src/schemas/DimensionType.ts @@ -0,0 +1,78 @@ +import { + BooleanNode, + StringNode as RawStringNode, + Mod, + NumberNode, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + Opt, +} from '@mcschema/core' +import { IntProvider } from './Common' + +export function initDimensionTypeSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('dimension_type', Mod(ObjectNode({ + ultrawarm: BooleanNode(), + natural: BooleanNode(), + piglin_safe: BooleanNode(), + respawn_anchor_works: BooleanNode(), + bed_works: BooleanNode(), + has_raids: BooleanNode(), + has_skylight: BooleanNode(), + has_ceiling: BooleanNode(), + coordinate_scale: NumberNode({ min: 0.00001, max: 30000000 }), + ambient_light: NumberNode(), + fixed_time: Opt(NumberNode({ integer: true })), + logical_height: NumberNode({ integer: true, min: 0, max: 4064 }), + effects: Opt(StringNode({ validator: 'resource', params: { pool: ['minecraft:overworld', 'minecraft:the_nether', 'minecraft:the_end'] } })), + infiniburn: StringNode({ validator: 'resource', params: { pool: 'block', requireTag: true } }), + min_y: NumberNode({ integer: true, min: -2032, max: 2031 }), + height: NumberNode({ integer: true, min: 16, max: 4064 }), + monster_spawn_light_level: IntProvider({ min: 0, max: 15 }), + monster_spawn_block_light_limit: NumberNode({ integer: true, min: 0, max: 15 }), + }, { context: 'dimension_type' }), node => ({ + default: () => ({ + ultrawarm: false, + natural: true, + piglin_safe: false, + respawn_anchor_works: false, + bed_works: true, + has_raids: true, + has_skylight: true, + has_ceiling: false, + coordinate_scale: 1, + ambient_light: 0, + logical_height: 384, + effects: 'minecraft:overworld', + infiniburn: '#minecraft:infiniburn_overworld', + min_y: -64, + height: 384, + monster_spawn_block_light_limit: 0, + monster_spawn_light_level: { + type: 'minecraft:uniform', + value: { + min_inclusive: 0, + max_inclusive: 7, + }, + }, + }), + validate: (path, value, errors, options) => { + value = node.validate(path, value, errors, options) + if (value?.min_y + value?.height > 2032) { + errors.add(path.push('height'), 'error.min_y_plus_height', 2032) + } + if (value?.logical_height > value?.height) { + errors.add(path.push('logical_height'), 'error.logical_height') + } + if (value?.height % 16 !== 0) { + errors.add(path.push('height'), 'error.height_multiple', 16) + } + if (value?.min_y % 16 !== 0) { + errors.add(path.push('min_y'), 'error.min_y_multiple', 16) + } + return value + } + }))) +} diff --git a/java/1.21.4/src/schemas/Enchantment.ts b/java/1.21.4/src/schemas/Enchantment.ts new file mode 100644 index 00000000..1dac621d --- /dev/null +++ b/java/1.21.4/src/schemas/Enchantment.ts @@ -0,0 +1,357 @@ +import { + StringNode as RawStringNode, + Reference as RawReference, + Mod, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + NumberNode, + Opt, + ListNode, + MapNode, + SwitchNode, + INode, + ModelPath, + ObjectOrList, + Switch, + Case, + BooleanNode +} from '@mcschema/core' +import { FloatProvider, Tag } from './Common' + +export function initEnchantmentSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('enchantment_value_effect', ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'enchantment_value_effect_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:add': { + value: Reference('level_based_value'), + }, + 'minecraft:all_of': { + effects: ListNode( + Reference('enchantment_value_effect') + ), + }, + 'minecraft:multiply': { + factor: Reference('level_based_value'), + }, + 'minecraft:remove_binomial': { + chance: Reference('level_based_value'), + }, + 'minecraft:set': { + value: Reference('level_based_value'), + }, + }, + }, { context: 'enchantment_value_effect' })) + + const ParticlePositionSource = ObjectNode({ + type: StringNode({ enum: ['entity_position', 'in_bounding_box'] }), + offset: Opt(NumberNode()), + [Switch]: [{ push: 'type' }], + [Case]: { + 'in_bounding_box': { + scale: Opt(NumberNode({ min: 0 })) // TODO: 0 is not valid + } + }, + }, { context: 'particle_position_source' }) + + const ParticleVelocitySource = ObjectNode({ + base: Opt(Reference('float_provider')), + movement_scale: Opt(NumberNode()), + }, { context: 'particle_velocity_source' }) + + const attributeEffectFields = { + id: StringNode(), + attribute: StringNode({ validator: 'resource', params: { pool: 'attribute' } }), + amount: Reference('level_based_value'), + operation: StringNode({ enum: 'attribute_modifier_operation' }), + } + + const SharedEffects = { + 'minecraft:apply_mob_effect': { + to_apply: Tag({ resource: 'mob_effect' }), + min_duration: Reference('level_based_value'), + max_duration: Reference('level_based_value'), + min_amplifier: Reference('level_based_value'), + max_amplifier: Reference('level_based_value'), + }, + 'minecraft:damage_entity': { + damage_type: StringNode({ validator: 'resource', params: { pool: '$damage_type' }}), + min_damage: Reference('level_based_value'), + max_damage: Reference('level_based_value'), + }, + 'minecraft:change_item_damage': { + amount: Reference('level_based_value'), + }, + 'minecraft:explode': { + damage_type: Opt(StringNode({ validator: 'resource', params: { pool: '$damage_type' }})), + radius: Reference('level_based_value'), + offset: Opt(Reference('vec3')), + block_interaction: StringNode({ enum: 'explosion_interaction' }), + small_particle: Reference('particle'), + large_particle: Reference('particle'), + sound: Reference('sound_event'), + immune_blocks: Opt(Tag({ resource: 'block' })), + knockback_multiplier: Opt(Reference('level_based_value')), + attribute_to_user: Opt(BooleanNode()), + create_fire: Opt(BooleanNode()), + }, + 'minecraft:ignite': { + duration: Reference('level_based_value'), + }, + 'minecraft:play_sound': { + sound: Reference('sound_event'), + volume: FloatProvider({ min: 1e-5, max: 10 }), + pitch: FloatProvider({ min: 1e-5, max: 2 }), + }, + 'minecraft:replace_block': { + block_state: Reference('block_state_provider'), + predicate: Opt(Reference('block_predicate_worldgen')), + offset: Opt(Reference('block_pos')), + trigger_game_event: Opt(StringNode({ validator: 'resource', params: { pool: 'game_event' } })), + }, + 'minecraft:replace_disk': { + block_state: Reference('block_state_provider'), + predicate: Opt(Reference('block_predicate_worldgen')), + radius: Reference('level_based_value'), + height: Reference('level_based_value'), + offset: Opt(Reference('block_pos')), + trigger_game_event: Opt(StringNode({ validator: 'resource', params: { pool: 'game_event' } })), + }, + 'minecraft:run_function': { + function: StringNode(), + }, + 'minecraft:set_block_properties': { + properties: MapNode( + StringNode(), + StringNode(), + ), + offset: Opt(Reference('block_pos')), + trigger_game_event: Opt(StringNode({ validator: 'resource', params: { pool: 'game_event' } })), + }, + 'minecraft:spawn_particles': { + particle: Reference('particle'), + horizontal_position: ParticlePositionSource, + vertical_position: ParticlePositionSource, + horizontal_velocity: ParticleVelocitySource, + vertical_velocity: ParticleVelocitySource, + speed: Reference('float_provider'), + }, + 'minecraft:summon_entity': { + entity: Tag({ resource: 'entity_type' }), + join_team: Opt(BooleanNode()), + }, + } + + schemas.register('enchantment_location_effect', ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'enchantment_location_based_effect_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + ...SharedEffects, + 'minecraft:all_of': { + effects: ListNode( + Reference('enchantment_location_effect') + ), + }, + 'minecraft:attribute': { + ...attributeEffectFields, + } + } + }, { context: 'enchantment_effect' })) + + schemas.register('enchantment_entity_effect', ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'enchantment_entity_effect_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + ...SharedEffects, + 'minecraft:all_of': { + effects: ListNode( + Reference('enchantment_entity_effect') + ), + }, + } + }, { context: 'enchantment_effect' })) + + const ConditionalEffect = (effect: INode) => ObjectNode({ + effect: effect, + requirements: Opt(ObjectOrList( + Reference('condition'), { choiceContext: 'condition' } + )), + }) + + const TargetedConditionalEffect = (effect: INode, equipmentDrops?: boolean) => ObjectNode({ + ...equipmentDrops ? { + enchanted: StringNode({ enum: ['attacker', 'victim'] }) + } : { + enchanted: StringNode({ enum: 'enchantment_target' }), + affected: StringNode({ enum: 'enchantment_target' }), + }, + effect: effect, + requirements: Opt(ObjectOrList( + Reference('condition'), { choiceContext: 'condition' } + )), + }) + + const EffectComponents: Record = { + 'minecraft:damage_protection': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:damage_immunity': ListNode( + ConditionalEffect(ObjectNode({})) + ), + 'minecraft:damage': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:smash_damage_per_fallen_block': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:knockback': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:armor_effectiveness': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:post_attack': ListNode( + TargetedConditionalEffect(Reference('enchantment_entity_effect')) + ), + 'minecraft:hit_block': ListNode( + ConditionalEffect(Reference('enchantment_entity_effect')) + ), + 'minecraft:item_damage': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:attributes': ListNode( + ObjectNode(attributeEffectFields), + ), + 'minecraft:equipment_drops': ListNode( + TargetedConditionalEffect(Reference('enchantment_value_effect'), true) + ), + 'minecraft:location_changed': ListNode( + ConditionalEffect(Reference('enchantment_location_effect')) + ), + 'minecraft:tick': ListNode( + ConditionalEffect(Reference('enchantment_entity_effect')) + ), + 'minecraft:ammo_use': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:projectile_piercing': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:projectile_spawned': ListNode( + ConditionalEffect(Reference('enchantment_entity_effect')) + ), + 'minecraft:projectile_spread': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:projectile_count': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:crossbow_charge_time': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:trident_return_acceleration': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:fishing_time_reduction': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:fishing_luck_bonus': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:block_experience': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:mob_experience': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:trident_spin_attack_strength': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:repair_with_xp': ListNode( + ConditionalEffect(Reference('enchantment_value_effect')) + ), + 'minecraft:crossbow_charging_sounds': ListNode( + ObjectNode({ + start: Opt(Reference('sound_event')), + mid: Opt(Reference('sound_event')), + end: Opt(Reference('sound_event')), + }), + ), + 'minecraft:trident_sound': ListNode( + Reference('sound_event'), + ), + 'minecraft:prevent_equipment_drop': ObjectNode({}), + 'minecraft:prevent_armor_change': ObjectNode({}), + } + + const keyMatches = (key: string) => (path: ModelPath) => { + let last = path.last().toString() + if (!last.startsWith('minecraft:')) last = 'minecraft:' + last + return last === key + } + + schemas.register('enchantment_cost', ObjectNode({ + base: NumberNode({ integer: true }), + per_level_above_first: NumberNode({ integer: true }), + }, { context: 'enchantment_cost' })) + + schemas.register('enchantment', Mod(ObjectNode({ + description: Reference('text_component'), + exclusive_set: Opt(Tag({ resource: 'enchantment' })), + supported_items: Tag({ resource: 'item' }), + primary_items: Opt(Tag({ resource: 'item' })), + weight: NumberNode({ integer: true, min: 1, max: 1024 }), + max_level: NumberNode({ integer: true, min: 1, max: 255 }), + min_cost: Reference('enchantment_cost'), + max_cost: Reference('enchantment_cost'), + anvil_cost: NumberNode({ integer: true, min: 0 }), + slots: ListNode( + StringNode({ enum: 'equipment_slot_group' }) + ), + effects: Opt(MapNode( + StringNode({ validator: 'resource', params: { pool: 'enchantment_effect_component_type' }}), + SwitchNode([ + ...Object.entries(EffectComponents).map(([key, value]) => ({ + match: keyMatches(key), + node: value, + })), + { + match: () => true, + node: ObjectNode({}), // default for unknown components + }, + ]), + )) + }, { context: 'enchantment' }), { + default: () => ({ + + }) + })) + + schemas.register('enchantment_provider', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'enchantment_provider_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:by_cost': { + enchantments: Tag({ resource: 'enchantment' }), + cost: Reference('int_provider'), + }, + 'minecraft:by_cost_with_difficulty': { + enchantments: Tag({ resource: 'enchantment' }), + min_cost: NumberNode({ integer: true, min: 1 }), + max_cost_span: NumberNode({ integer: true, min: 0 }), + }, + 'minecraft:single': { + enchantment: StringNode({ validator: 'resource', params: { pool: 'enchantment' } }), + level: Reference('int_provider'), + } + }, + }, { context: 'enchantment_provider' }), { + default: () => ({ + + }) + })) +} diff --git a/java/1.21.4/src/schemas/Instrument.ts b/java/1.21.4/src/schemas/Instrument.ts new file mode 100644 index 00000000..5cc0f808 --- /dev/null +++ b/java/1.21.4/src/schemas/Instrument.ts @@ -0,0 +1,28 @@ +import { + Reference as RawReference, + Mod, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + NumberNode +} from '@mcschema/core' + +export function initInstrumentSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + + schemas.register('instrument', Mod(ObjectNode({ + sound_event: Reference('sound_event'), + description: Reference('text_component'), + range: NumberNode({ min: 0 }), + use_duration: NumberNode({ min: 0 }), + }, { context: 'instrument' }), { + default: () => ({ + sound_event: 'minecraft:item.goat_horn.sound.4', + description: { + translate: 'instrument.minecraft.admire_goat_horn', + }, + range: 256, + use_duration: 7, + }) + })) +} diff --git a/java/1.21.4/src/schemas/ItemModifier.ts b/java/1.21.4/src/schemas/ItemModifier.ts new file mode 100644 index 00000000..9d7f3669 --- /dev/null +++ b/java/1.21.4/src/schemas/ItemModifier.ts @@ -0,0 +1,41 @@ +import { + Case, + StringNode as RawStringNode, + ObjectNode, + ObjectOrList, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, + Mod, + ListNode, + Opt, + NodeChildren, +} from '@mcschema/core' +import { FunctionCases } from './Common' + +export function initItemModifierSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('item_modifier', ObjectOrList( + Reference('function'), { choiceContext: 'function' } + )) + + const conditions: NodeChildren = { + conditions: Opt(ListNode( + Reference('condition') + )) + } + + schemas.register('function', Mod(ObjectNode({ + function: StringNode({ validator: 'resource', params: { pool: 'loot_function_type' } }), + [Switch]: [{ push: 'function' }], + [Case]: FunctionCases(conditions) + }, { category: 'function', context: 'function' }), { + default: () => [{ + function: 'minecraft:set_count', + count: 1 + }] + })) +} diff --git a/java/1.21.4/src/schemas/JukeboxSong.ts b/java/1.21.4/src/schemas/JukeboxSong.ts new file mode 100644 index 00000000..70c7dfa7 --- /dev/null +++ b/java/1.21.4/src/schemas/JukeboxSong.ts @@ -0,0 +1,28 @@ +import { + Reference as RawReference, + Mod, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + NumberNode +} from '@mcschema/core' + +export function initJukeboxSongSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + + schemas.register('jukebox_song', Mod(ObjectNode({ + sound_event: Reference('sound_event'), + description: Reference('text_component'), + length_in_seconds: NumberNode({ min: 1 }), + comparator_output: NumberNode({ integer: true, min: 0, max: 15 }), + }, { context: 'jukebox_song' }), { + default: () => ({ + sound_event: 'minecraft:music_disc.cat', + description: { + translate: 'jukebox_song.minecraft.cat', + }, + length_in_seconds: 185, + comparator_output: 2, + }) + })) +} diff --git a/java/1.21.4/src/schemas/LootTable.ts b/java/1.21.4/src/schemas/LootTable.ts new file mode 100644 index 00000000..7935319e --- /dev/null +++ b/java/1.21.4/src/schemas/LootTable.ts @@ -0,0 +1,214 @@ +import { + BooleanNode, + Case, + StringNode as RawStringNode, + ListNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + StringOrList, + Switch, + SwitchNode, + INode, + Path, + ModelPath, + SchemaRegistry, + CollectionRegistry, + Opt, + NodeChildren, + ChoiceNode, +} from '@mcschema/core' +import { + LootTableTypes, + LootContext, + LootFunctions, + LootConditions, + LootEntitySources, + LootCopySources +} from '../LootContext' +import { ConditionCases, FunctionCases } from './Common' + +export function initLootTableSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + const conditions: NodeChildren = { + conditions: Opt(ListNode( + Reference('loot_condition') + )) + } + + const functionsAndConditions: NodeChildren = { + functions: Opt(ListNode( + Reference('loot_function') + )), + ...conditions + } + + function compileSwitchNode(contextMap: Map, collectionID: string, getNode: (type: string | string[]) => INode): INode { + const cases: { match: (path: ModelPath) => boolean, node: INode }[] = [] + const getAvailableOptions = (providedContext: LootContext[]) => collections + .get(collectionID) + .filter(t => { + const requiredContext = contextMap.get(t) ?? [] + return requiredContext.every(c => providedContext.includes(c)) + }) + for (const [tableType, { allows, requires }] of LootTableTypes) { + const providedContext = [...allows, ...requires] + cases.push({ + match: path => path.getModel().get(new Path(['type'])) === tableType, + node: getNode(getAvailableOptions(providedContext)) + }) + } + cases.push({ match: _ => true, node: getNode(collectionID) }) + return SwitchNode(cases) + } + + const conditionIDSwtichNode = compileSwitchNode(LootConditions, 'loot_condition_type', type => StringNode({ validator: 'resource', params: { pool: type instanceof Array ? type : `loot_condition_type` } })) + const functionIDSwtichNode = compileSwitchNode(LootFunctions, 'loot_function_type', type => StringNode({ validator: 'resource', params: { pool: type instanceof Array ? type : `loot_function_type` } })) + const entitySourceSwtichNode = compileSwitchNode(LootEntitySources, 'entity_source', type => StringNode({ enum: type })) + const copySourceSwtichNode = compileSwitchNode(LootCopySources, 'copy_source', type => StringNode({ enum: type})) + + schemas.register('loot_table', Mod(ObjectNode({ + type: Opt(StringNode({ validator: "resource", params: { pool: collections.get('loot_context_type') } })), + pools: Opt(ListNode( + Mod(ObjectNode({ + rolls: Reference('number_provider'), + bonus_rolls: Opt(Reference('number_provider')), + entries: ListNode( + Reference('loot_entry') + ), + ...functionsAndConditions + }, { category: 'pool', context: 'loot_pool' }), { + default: () => ({ + rolls: 1, + entries: [{ + type: 'minecraft:item', + name: 'minecraft:stone' + }] + }) + }) + )), + functions: Opt(ListNode( + Reference('loot_function') + )), + random_sequence: Opt(StringNode({ validator: 'resource', params: { pool: [], allowUnknown: true } })), + }, { context: 'loot_table' }), { + default: () => ({ + pools: [{ + rolls: 1, + entries: [{ + type: 'minecraft:item', + name: 'minecraft:stone' + }] + }] + }) + })) + + const singletonFields: NodeChildren = { + weight: Opt(NumberNode({ integer: true })), + quality: Opt(NumberNode({ integer: true })), + ...functionsAndConditions, + } + + schemas.register('loot_entry', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'loot_pool_entry_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:alternatives': { + children: ListNode( + Reference('loot_entry') + ), + ...conditions + }, + 'minecraft:dynamic': { + name: StringNode(), + ...singletonFields + }, + 'minecraft:empty': { + ...singletonFields + }, + 'minecraft:group': { + children: ListNode( + Reference('loot_entry') + ), + ...conditions + }, + 'minecraft:item': { + name: StringNode({ validator: 'resource', params: { pool: 'item' } }), + ...singletonFields + }, + 'minecraft:loot_table': { + value: ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }), + change: () => undefined + }, + { + type: 'object', + node: Reference('loot_table'), + change: () => ({ pools: [] }) + } + ]), + ...singletonFields + }, + 'minecraft:sequence': { + children: ListNode( + Reference('loot_entry') + ), + ...conditions + }, + 'minecraft:tag': { + name: StringNode({ validator: 'resource', params: { pool: '$tag/item' } }), + expand: BooleanNode(), + ...singletonFields + } + } + }, { context: 'loot_entry' }), { + default: () => ({ + type: 'minecraft:item', + name: 'minecraft:stone' + }) + })) + + schemas.register('loot_function', Mod(ObjectNode({ + function: functionIDSwtichNode, + [Switch]: [{ push: 'function' }], + [Case]: FunctionCases(conditions, copySourceSwtichNode, entitySourceSwtichNode) + }, { category: 'function', context: 'function' }), { + default: () => ({ + function: 'minecraft:set_count', + count: 1 + }) + })) + + schemas.register('loot_condition', Mod(ObjectNode({ + condition: conditionIDSwtichNode, + [Switch]: [{ push: 'condition' }], + [Case]: ConditionCases(entitySourceSwtichNode) + }, { category: 'predicate', context: 'condition' }), { + default: () => ({ + condition: 'minecraft:random_chance', + chance: 0.5 + }) + })) + + schemas.register('attribute_modifier', Mod(ObjectNode({ + id: StringNode(), + attribute: StringNode({ validator: 'resource', params: { pool: 'attribute' } }), + amount: Reference('number_provider'), + operation: StringNode({ enum: 'attribute_modifier_operation' }), + slot: StringOrList( + StringNode({ enum: 'equipment_slot_group' }) + ) + }, { context: 'attribute_modifier' }), { + default: () => ({ + attribute: 'minecraft:generic.max_health', + amount: 1, + operation: 'addition', + slot: 'mainhand' + }) + })) +} diff --git a/java/1.21.4/src/schemas/PackMcmeta.ts b/java/1.21.4/src/schemas/PackMcmeta.ts new file mode 100644 index 00000000..20fc4ace --- /dev/null +++ b/java/1.21.4/src/schemas/PackMcmeta.ts @@ -0,0 +1,83 @@ +import { + Mod, + NumberNode, + ObjectNode, + ChoiceNode, + Reference as RawReference, + SchemaRegistry, + CollectionRegistry, + ListNode, + StringNode as RawStringNode, + Opt, +} from '@mcschema/core' +import { InclusiveRange } from './Common' + +const CURRENT_PACK_FORMAT = 57 + +export function initPackMcmetaSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('pack_mcmeta', Mod(ObjectNode({ + pack: Mod(ObjectNode({ + pack_format: Mod(NumberNode({ integer: true }), { + default: () => CURRENT_PACK_FORMAT, + }), + supported_formats: Opt(Reference('pack_format_range')), + description: Reference('text_component') + }), { + default: () => ({ + pack_format: CURRENT_PACK_FORMAT, + description: '' + }) + }), + features: Opt(ObjectNode({ + enabled: ListNode( + StringNode({ enum: 'feature_flags' }) + ) + })), + filter: Opt(ObjectNode({ + block: ListNode( + Reference('resource_location_pattern') + ) + })), + overlays: Opt(ObjectNode({ + entries: ListNode( + Reference('pack_overlay') + ) + })) + }, { context: 'pack', category: 'pack' }), { + default: () => ({ + pack: { + pack_format: CURRENT_PACK_FORMAT, + description: '' + } + }) + })) + + schemas.register('pack_format_range', ChoiceNode([ + { + type: 'number', + node: NumberNode({ integer: true }), + change: (v: any) => Array.isArray(v) ? (v[0] ?? 0) : (v?.min_inclusive ?? 0) + }, + { + type: 'list', + node: ListNode( + NumberNode({ integer: true }), + { minLength: 2, maxLength: 2 }, + ), + change: (v: any) => typeof v === 'number' ? [v, v] : [v?.min_inclusive ?? 0, v?.max_inclusive ?? 0] + }, + { + type: 'object', + node: InclusiveRange({ integer: true }), + change: (v: any) => Array.isArray(v) ? {min_inclusive: v[0] ?? 0, max_inclusive: v[1] ?? 0} : {min_inclusive: v ?? 0, max_inclusive: v ?? 0} + } + ])) + + schemas.register('pack_overlay', ObjectNode({ + formats: Reference('pack_format_range'), + directory: StringNode(), + }, { context: 'pack_overlay' })) +} diff --git a/java/1.21.4/src/schemas/PaintingVariant.ts b/java/1.21.4/src/schemas/PaintingVariant.ts new file mode 100644 index 00000000..dd8a030f --- /dev/null +++ b/java/1.21.4/src/schemas/PaintingVariant.ts @@ -0,0 +1,28 @@ +import { + Reference as RawReference, + StringNode as RawStringNode, + Mod, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + NumberNode +} from '@mcschema/core' + +export function initPaintingVariantSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + const Reference = RawReference.bind(undefined, schemas) + + schemas.register('painting_variant', Mod(ObjectNode({ + asset_id: StringNode(), + width: NumberNode({ integer: true, min: 1, max: 16 }), + height: NumberNode({ integer: true, min: 1, max: 16 }), + title: Reference('text_component'), + author: Reference('text_component'), + }, { context: 'painting_variant' }), { + default: () => ({ + asset_id: 'minecraft:backyard', + width: 3, + height: 4, + }) + })) +} diff --git a/java/1.21.4/src/schemas/Predicates.ts b/java/1.21.4/src/schemas/Predicates.ts new file mode 100644 index 00000000..3fa822ec --- /dev/null +++ b/java/1.21.4/src/schemas/Predicates.ts @@ -0,0 +1,400 @@ +import { + BooleanNode, + StringNode as RawStringNode, + ListNode, + MapNode, + ObjectNode, + Opt, + Reference as RawReference, + Switch, + Case, + SchemaRegistry, + ChoiceNode, + CollectionRegistry, + SwitchNode, + INode, + ModelPath, + NumberNode, +} from '@mcschema/core' +import { Tag } from './Common' + +export function initPredicatesSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + function CollectionPredicate(predicate: INode) { + return ObjectNode({ + contains: Opt(ListNode( + predicate, + )), + count: Opt(ListNode( + ObjectNode({ + test: predicate, + count: Reference('int_bounds'), + }), + )), + size: Opt(Reference('int_bounds')), + }, { context: 'collection_predicate' }) + } + + schemas.register('attribute_modifiers_entry_predicate', ObjectNode({ + id: StringNode(), + attribute: Opt(Tag({ resource: 'attribute' })), + amount: Opt(Reference('float_bounds')), + operation: Opt(StringNode({ enum: 'attribute_modifier_operation' })), + slot: Opt(StringNode({ enum: 'equipment_slot_group' })), + }, { context: 'attribute_modifier' })) + + schemas.register('firework_explosion_predicate', ObjectNode({ + shape: Opt(StringNode({ enum: 'firework_explosion_shape' })), + has_trail: Opt(BooleanNode()), + has_twinkle: Opt(BooleanNode()), + }, { context: 'firework_explosion' })) + + schemas.register('item_predicate', ObjectNode({ + items: Opt(Tag({ resource: 'item' })), + count: Opt(Reference('int_bounds')), + components: Opt(Reference('data_component_predicate')), + predicates: Opt(MapNode( + StringNode({ validator: 'resource', params: { pool: 'item_sub_predicate_type' } }), + SwitchNode([ + ...Object.entries({ + 'minecraft:attribute_modifiers': ObjectNode({ + modifiers: Opt(CollectionPredicate( + Reference('attribute_modifiers_entry_predicate') + )), + }), + 'minecraft:bundle_contents': ObjectNode({ + items: Opt(CollectionPredicate( + Reference('item_predicate') + )) + }), + 'minecraft:container': ObjectNode({ + items: Opt(CollectionPredicate( + Reference('item_predicate') + )) + }), + 'minecraft:custom_data': Reference('custom_data_component'), + 'minecraft:damage': ObjectNode({ + durability: Opt(Reference('int_bounds')), + damage: Opt(Reference('int_bounds')), + }), + 'minecraft:enchantments': ListNode( + Reference('enchantment_predicate') + ), + 'minecraft:firework_explosion': Reference('firework_explosion_predicate'), + 'minecraft:fireworks': ObjectNode({ + explosions: Opt(CollectionPredicate( + Reference('firework_explosion_predicate') + )), + flight_duration: Opt(Reference('int_bounds')), + }), + 'minecraft:jukebox_playable': ObjectNode({ + song: Opt(Tag({ resource: 'jukebox_song' })), + }), + 'minecraft:potion_contents': Tag({ resource: 'potion' }), + 'minecraft:stored_enchantments': ListNode( + Reference('enchantment_predicate') + ), + 'minecraft:trim': ObjectNode({ + material: Opt(Tag({ resource: '$trim_material' })), + pattern: Opt(Tag({ resource: '$trim_pattern' })), + }), + 'minecraft:writable_book_content': ObjectNode({ + pages: Opt(CollectionPredicate( + StringNode() + )), + }), + 'minecraft:written_book_content': ObjectNode({ + pages: Opt(CollectionPredicate( + Reference('text_component') + )), + author: Opt(StringNode()), + title: Opt(StringNode()), + generation: Opt(Reference('int_bounds')), + resolved: Opt(BooleanNode()), + }), + }).map(([key, value]) => ({ + match: (path: ModelPath) => { + let last = path.last().toString() + if (!last.startsWith('minecraft:')) last = 'minecraft:' + last + return last === key + }, + node: value, + })), + { + match: () => true, + node: ObjectNode({}), + }, + ]), + )) + }, { context: 'item' })) + + schemas.register('enchantment_predicate', ObjectNode({ + enchantments: Opt(Tag({ resource: 'enchantment' })), + levels: Opt(Reference('int_bounds')) + }, { context: 'enchantment' })) + + schemas.register('block_predicate', ObjectNode({ + blocks: Opt(Tag({ resource: 'block' })), + nbt: Opt(StringNode({ validator: 'nbt', params: { registry: { category: 'minecraft:block', id: ['pop', { push: 'block' }] } } })), + state: Opt(MapNode( + StringNode(), + StringNode(), + { validation: { validator: 'block_state_map', params: { id: ['pop', { push: 'block' }] } } } + )) + }, { context: 'block' })) + + schemas.register('fluid_predicate', ObjectNode({ + fluids: Opt(Tag({ resource: 'fluid' })), + state: Opt(MapNode( + StringNode(), + StringNode() + )) + }, { context: 'fluid' })) + + schemas.register('location_predicate', ObjectNode({ + position: Opt(ObjectNode({ + x: Opt(Reference('float_bounds')), + y: Opt(Reference('float_bounds')), + z: Opt(Reference('float_bounds')) + })), + biomes: Opt(Tag({ resource: '$worldgen/biome' })), + structures: Opt(Tag({ resource: '$worldgen/structure' })), + dimension: Opt(StringNode({ validator: 'resource', params: { pool: '$dimension' } })), + light: Opt(ObjectNode({ + light: Opt(Reference('int_bounds')) + })), + smokey: Opt(BooleanNode()), + can_see_sky: Opt(BooleanNode()), + block: Opt(Reference('block_predicate')), + fluid: Opt(Reference('fluid_predicate')) + }, { context: 'location' })) + + schemas.register('statistic_predicate', ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'stat_type' } }), + stat: StringNode(), + value: Opt(Reference('int_bounds')), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:mined': { + stat: StringNode({ validator: 'resource', params: { pool: 'block' } }) + }, + 'minecraft:crafted': { + stat: StringNode({ validator: 'resource', params: { pool: 'item' } }) + }, + 'minecraft:used': { + stat: StringNode({ validator: 'resource', params: { pool: 'item' } }) + }, + 'minecraft:broken': { + stat: StringNode({ validator: 'resource', params: { pool: 'item' } }) + }, + 'minecraft:picked_up': { + stat: StringNode({ validator: 'resource', params: { pool: 'item' } }) + }, + 'minecraft:dropped': { + stat: StringNode({ validator: 'resource', params: { pool: 'item' } }) + }, + 'minecraft:killed': { + stat: StringNode({ validator: 'resource', params: { pool: 'entity_type' } }) + }, + 'minecraft:killed_by': { + stat: StringNode({ validator: 'resource', params: { pool: 'entity_type' } }) + }, + 'minecraft:custom': { + stat: StringNode({ validator: 'resource', params: { pool: 'custom_stat' } }) + } + } + })) + + schemas.register('status_effect_predicate', ObjectNode({ + amplifier: Opt(Reference('int_bounds')), + duration: Opt(Reference('int_bounds')), + ambient: Opt(BooleanNode()), + visible: Opt(BooleanNode()) + }, { context: 'status_effect' })) + + schemas.register('distance_predicate', ObjectNode({ + x: Opt(Reference('float_bounds')), + y: Opt(Reference('float_bounds')), + z: Opt(Reference('float_bounds')), + absolute: Opt(Reference('float_bounds')), + horizontal: Opt(Reference('float_bounds')) + }, { context: 'distance' })) + + schemas.register('movement_predicate', ObjectNode({ + x: Opt(Reference('float_bounds')), + y: Opt(Reference('float_bounds')), + z: Opt(Reference('float_bounds')), + speed: Opt(Reference('float_bounds')), + horizontal_speed: Opt(Reference('float_bounds')), + vertical_speed: Opt(Reference('float_bounds')), + fall_distance: Opt(Reference('float_bounds')), + }, { context: 'movement_predicate' })) + + schemas.register('entity_predicate', ObjectNode({ + type: Opt(Tag({ resource: 'entity_type' })), + type_specific: Opt(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'entity_sub_predicate_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:axolotl': { + variant: Opt(StringNode({ enum: 'axolotl_variant' })) + }, + 'minecraft:boat': { + variant: Opt(StringNode({ enum: 'boat_variant' })) + }, + 'minecraft:cat': { + variant: Opt(Tag({ resource: 'cat_variant' })) + }, + 'minecraft:fishing_hook': { + in_open_water: Opt(BooleanNode()) + }, + 'minecraft:fox': { + variant: Opt(StringNode({ enum: 'fox_variant' })) + }, + 'minecraft:frog': { + variant: Opt(Tag({ resource: 'frog_variant' })) + }, + 'minecraft:horse': { + variant: Opt(StringNode({ enum: 'horse_variant' })) + }, + 'minecraft:lightning': { + blocks_set_on_fire: Opt(Reference('int_bounds')), + entity_struck: Opt(Reference('entity_predicate')) + }, + 'minecraft:llama': { + variant: Opt(StringNode({ enum: 'llama_variant' })) + }, + 'minecraft:mooshroom': { + variant: Opt(StringNode({ enum: 'mooshroom_variant' })) + }, + 'minecraft:painting': { + variant: Opt(Tag({ resource: 'painting_variant' })) + }, + 'minecraft:parrot': { + variant: Opt(StringNode({ enum: 'parrot_variant' })) + }, + 'minecraft:player': { + gamemode: Opt(ListNode( + StringNode({ enum: 'gamemode' }) + )), + level: Opt(Reference('int_bounds')), + input: Opt(ObjectNode({ + forward: Opt(BooleanNode()), + backward: Opt(BooleanNode()), + left: Opt(BooleanNode()), + right: Opt(BooleanNode()), + jump: Opt(BooleanNode()), + sneak: Opt(BooleanNode()), + sprint: Opt(BooleanNode()), + })), + advancements: Opt(MapNode( + StringNode({ validator: 'resource', params: { pool: '$advancement' } }), + ChoiceNode([ + { + type: 'boolean', + node: BooleanNode(), + change: () => true + }, + { + type: 'object', + node: MapNode( + StringNode(), + BooleanNode() + ) + } + ]) + )), + recipes: Opt(MapNode( + StringNode({ validator: 'resource', params: { pool: '$recipe' } }), + BooleanNode() + )), + stats: Opt(ListNode( + Reference('statistic_predicate') + )), + looking_at: Opt(Reference('entity_predicate')) + }, + 'minecraft:rabbit': { + variant: Opt(StringNode({ enum: 'rabbit_variant' })) + }, + 'minecraft:raider': { + has_raid: Opt(BooleanNode()), + is_captain: Opt(BooleanNode()), + }, + 'minecraft:salmon': { + variant: Opt(StringNode({ enum: 'salmon_variant' })), + }, + 'minecraft:sheep': { + sheared: Opt(BooleanNode()), + color: Opt(StringNode({ enum: 'dye_color' })), + }, + 'minecraft:slime': { + size: Opt(Reference('int_bounds')) + }, + 'minecraft:tropical_fish': { + variant: Opt(StringNode({ enum: 'tropical_fish_variant' })) + }, + 'minecraft:villager': { + variant: Opt(StringNode({ validator: 'resource', params: { pool: 'villager_type' } })) + }, + 'minecraft:wolf': { + variant: Opt(Tag({ resource: '$wolf_variant' })), + }, + } + })), + // TODO: support any unsafe data + nbt: Opt(StringNode({ validator: 'nbt', params: { registry: { category: 'minecraft:entity', id: ['pop', { push: 'type' }] } } })), + team: Opt(StringNode({ validator: 'team' })), + location: Opt(Reference('location_predicate')), + movement: Opt(Reference('movement_predicate')), + movement_affected_by: Opt(Reference('location_predicate')), + stepping_on: Opt(Reference('location_predicate')), + distance: Opt(Reference('distance_predicate')), + slots: Opt(MapNode( + StringNode({ enum: 'slot_range', additional: true }), + Reference('item_predicate') + )), + flags: Opt(ObjectNode({ + is_on_ground: Opt(BooleanNode()), + is_on_fire: Opt(BooleanNode()), + is_sneaking: Opt(BooleanNode()), + is_sprinting: Opt(BooleanNode()), + is_swimming: Opt(BooleanNode()), + is_flying: Opt(BooleanNode()), + is_baby: Opt(BooleanNode()) + })), + equipment: Opt(MapNode( + StringNode({ enum: 'equipment_slot' }), + Reference('item_predicate') + )), + periodic_tick: Opt(NumberNode({ integer: true, min: 1 })), + vehicle: Opt(Reference('entity_predicate')), + passenger: Opt(Reference('entity_predicate')), + targeted_entity: Opt(Reference('entity_predicate')), + effects: Opt(MapNode( + StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }), + Reference('status_effect_predicate') + )) + }, { context: 'entity' })) + + schemas.register('damage_source_predicate', ObjectNode({ + tags: Opt(ListNode( + ObjectNode({ + id: StringNode({ validator: 'resource', params: { pool: '$tag/damage_type' } }), + expected: BooleanNode(), + }, { context: 'tag_predicate' }), + )), + source_entity: Opt(Reference('entity_predicate')), + direct_entity: Opt(Reference('entity_predicate')), + is_direct: Opt(BooleanNode()), + }, { context: 'damage_source' })) + + schemas.register('damage_predicate', ObjectNode({ + dealt: Opt(Reference('float_bounds')), + taken: Opt(Reference('float_bounds')), + blocked: Opt(BooleanNode()), + source_entity: Opt(Reference('entity_predicate')), + type: Opt(Reference('damage_source_predicate')) + }, { context: 'damage' })) +} diff --git a/java/1.21.4/src/schemas/Recipe.ts b/java/1.21.4/src/schemas/Recipe.ts new file mode 100644 index 00000000..8563e2d0 --- /dev/null +++ b/java/1.21.4/src/schemas/Recipe.ts @@ -0,0 +1,103 @@ +import { + Case, + ChoiceNode, + StringNode as RawStringNode, + ListNode, + MapNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, + Opt, + BooleanNode, +} from '@mcschema/core' +import { Tag } from './Common' + +export function initRecipeSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('recipe', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'recipe_serializer' } }), + category: Opt(StringNode({ enum: 'recipe_category' })), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:crafting_shaped': { + group: Opt(StringNode({ enum: 'recipe_group', additional: true })), + pattern: ListNode(StringNode()), // TODO: add validation + key: MapNode( + StringNode(), // TODO: add validation + Reference('recipe_ingredient') + ), + result: Reference('item_stack'), + show_notification: Opt(BooleanNode()), + }, + 'minecraft:crafting_shapeless': { + group: Opt(StringNode()), + ingredients: ListNode(Reference('recipe_ingredient')), + result: Reference('item_stack') + }, + 'minecraft:crafting_transmute': { + group: Opt(StringNode()), + input: Reference('recipe_ingredient'), + material: Reference('recipe_ingredient'), + result: StringNode({ validator: 'resource', params: { pool: 'item' } }) + }, + 'minecraft:smelting': { + group: Opt(StringNode()), + ingredient: Reference('recipe_ingredient'), + result: Reference('single_item_stack'), + experience: Opt(NumberNode()), + cookingtime: Opt(Mod(NumberNode({ integer: true }), { default: () => 200 })) + }, + 'minecraft:blasting': { + group: Opt(StringNode()), + ingredient: Reference('recipe_ingredient'), + result: Reference('single_item_stack'), + experience: Opt(NumberNode()), + cookingtime: Opt(Mod(NumberNode({ integer: true }), { default: () => 100 })) + }, + 'minecraft:smoking': { + group: Opt(StringNode()), + ingredient: Reference('recipe_ingredient'), + result: Reference('single_item_stack'), + experience: Opt(NumberNode()), + cookingtime: Opt(Mod(NumberNode({ integer: true }), { default: () => 100 })) + }, + 'minecraft:campfire_cooking': { + group: Opt(StringNode()), + ingredient: Reference('recipe_ingredient'), + result: Reference('single_item_stack'), + experience: Opt(NumberNode()), + cookingtime: Opt(Mod(NumberNode({ integer: true }), { default: () => 100 })) + }, + 'minecraft:stonecutting': { + group: Opt(StringNode()), + ingredient: Reference('recipe_ingredient'), + result: Reference('item_stack') + }, + 'minecraft:smithing_transform': { + template: Opt(Reference('recipe_ingredient')), + base: Opt(Reference('recipe_ingredient')), + addition: Opt(Reference('recipe_ingredient')), + result: Reference('item_stack') + }, + 'minecraft:smithing_trim': { + template: Opt(Reference('recipe_ingredient')), + base: Opt(Reference('recipe_ingredient')), + addition: Opt(Reference('recipe_ingredient')) + } + } + }, { context: 'recipe', disableSwitchContext: true }), { + default: () => ({ + type: 'minecraft:crafting_shaped' + }) + })) + + schemas.register('recipe_ingredient', Mod(Tag({ resource: 'item' }), { + default: () => 'minecraft:stone' + })) +} diff --git a/java/1.21.4/src/schemas/Tags.ts b/java/1.21.4/src/schemas/Tags.ts new file mode 100644 index 00000000..2b7d5bf8 --- /dev/null +++ b/java/1.21.4/src/schemas/Tags.ts @@ -0,0 +1,84 @@ +import { + BooleanNode, + StringNode as RawStringNode, + ListNode, + Mod, + ObjectNode, + ResourceType, + ChoiceNode, + SchemaRegistry, + CollectionRegistry, + Opt, +} from '@mcschema/core' + +export function initTagsSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + + const TagBase = (type: ResourceType) => Mod(ObjectNode({ + replace: Opt(BooleanNode()), + values: ListNode( + ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: type, allowTag: true } }), + change: v => v.id + }, + { + type: 'object', + node: ObjectNode({ + id: StringNode({ validator: 'resource', params: { pool: type, allowTag: true, allowUnknown: true } }), + required: BooleanNode() + }), + change: v => ({ id: v }) + } + ]) + ), + }, { context: 'tag' }), { + default: () => ({ + values: [] + }) + }) + + schemas.register('block_tag', TagBase('block')) + schemas.register('entity_type_tag', TagBase('entity_type')) + schemas.register('fluid_tag', TagBase('fluid')) + schemas.register('function_tag', TagBase('$function')) + schemas.register('game_event_tag', TagBase('game_event')) + schemas.register('item_tag', TagBase('item')) + + schemas.register('advancement_tag', TagBase('$advancement')) + schemas.register('chat_type_tag', TagBase('$chat_type')) + schemas.register('damage_type_tag', TagBase('$damage_type')) + schemas.register('dimension_tag', TagBase('$dimension')) + schemas.register('dimension_type_tag', TagBase('$dimension_type')) + schemas.register('function_tag', TagBase('$function')) + schemas.register('item_modifier_tag', TagBase('$item_modifier')) + schemas.register('loot_table_tag', TagBase('$loot_table')) + schemas.register('predicate_tag', TagBase('$predicate')) + schemas.register('recipe_tag', TagBase('$recipe')) + schemas.register('structure_tag', TagBase('$structure')) + schemas.register('trim_material_tag', TagBase('$trim_material')) + schemas.register('trim_pattern_tag', TagBase('$trim_pattern')) + + schemas.register('biome_tag', TagBase('$worldgen/biome')) + schemas.register('configured_carver_tag', TagBase('$worldgen/configured_carver')) + schemas.register('configured_feature_tag', TagBase('$worldgen/configured_feature')) + schemas.register('structure_tag', TagBase('$worldgen/structure')) + schemas.register('density_function_tag', TagBase('$worldgen/density_function')) + schemas.register('flat_level_generator_preset_tag', TagBase('$worldgen/flat_level_generator_preset')) + schemas.register('multi_noise_biome_source_parameter_list_tag', TagBase('$worldgen/multi_noise_biome_source_parameter_list')) + schemas.register('noise_tag', TagBase('$worldgen/noise')) + schemas.register('noise_settings_tag', TagBase('$worldgen/noise_settings')) + schemas.register('placed_feature_tag', TagBase('$worldgen/placed_feature')) + schemas.register('processor_list_tag', TagBase('$worldgen/processor_list')) + schemas.register('structure_set_tag', TagBase('$worldgen/structure_set')) + schemas.register('template_pool_tag', TagBase('$worldgen/template_pool')) + schemas.register('world_preset_tag', TagBase('$worldgen/world_preset')) + + schemas.register('banner_pattern_tag', TagBase('banner_pattern')) + schemas.register('cat_variant_tag', TagBase('cat_variant')) + schemas.register('enchantment_tag', TagBase('enchantment')) + schemas.register('instrument_tag', TagBase('instrument')) + schemas.register('painting_variant_tag', TagBase('painting_variant')) + schemas.register('point_of_interest_type_tag', TagBase('point_of_interest_type')) +} diff --git a/java/1.21.4/src/schemas/TextComponent.ts b/java/1.21.4/src/schemas/TextComponent.ts new file mode 100644 index 00000000..e0e6fea6 --- /dev/null +++ b/java/1.21.4/src/schemas/TextComponent.ts @@ -0,0 +1,211 @@ +import { + BooleanNode, + Case, + ChoiceNode, + StringNode as RawStringNode, + ListNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, + Opt, + NodeChildren, +} from '@mcschema/core' + + +export function initTextComponentSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + const getSimpleString = (v: any): string => v instanceof Array ? getSimpleString(v[0]) : v?.text ?? (typeof v === 'object' ? '' : v?.toString()) + + schemas.register('text_component', Mod(ChoiceNode([ + { + type: 'object', + node: Reference('text_component_object'), + change: v => v instanceof Array ? (typeof v[0] === 'object' ? v[0] : { text: getSimpleString(v[0]) }) : typeof v === 'object' ? v : { text: getSimpleString(v) } + }, + { + type: 'list', + node: Reference('text_component_list'), + change: v => [v] + }, + { + type: 'string', + priority: 1, + node: StringNode(), + change: getSimpleString + }, + ], { context: 'text_component' }), { + default: () => ({ + text: "" + }) + })) + + const StyleFields: NodeChildren = { + color: Opt(StringNode()) /* TODO */, + font: Opt(StringNode({ validator: 'resource', params: { pool: 'font' } })), + bold: Opt(BooleanNode()), + italic: Opt(BooleanNode()), + underlined: Opt(BooleanNode()), + strikethrough: Opt(BooleanNode()), + obfuscated: Opt(BooleanNode()), + insertion: Opt(StringNode()), + clickEvent: Opt(ObjectNode({ + action: StringNode({ enum: ['open_url', 'open_file', 'run_command', 'suggest_command', 'change_page', 'copy_to_clipboard'] }), + [Switch]: [{ push: 'action' }], + [Case]: { + 'change_page': { + value: StringNode() + }, + 'copy_to_clipboard': { + value: StringNode() + }, + 'open_file': { + value: StringNode() + }, + 'open_url': { + value: StringNode() + }, + 'run_command': { + value: StringNode({ validator: 'command', params: { leadingSlash: true, allowPartial: true } }) + }, + 'suggest_command': { + value: StringNode({ validator: 'command', params: { leadingSlash: true, allowPartial: true } }) + } + } + })), + hoverEvent: Opt(ObjectNode({ + action: StringNode({ enum: ['show_text', 'show_item', 'show_entity'] }), + [Switch]: [{ push: 'action' }], + [Case]: { + 'show_text': { + value: Opt(Reference('text_component')), + contents: Opt(Reference('text_component')) + }, + 'show_item': { + value: Opt(StringNode({ validator: 'nbt', params: { module: 'util::InventoryItem' } })), + contents: Opt(ChoiceNode([ + { + type: 'string', + node: Reference('item_non_air'), + change: v => typeof v === 'object' && typeof v?.id === 'string' ? v : undefined + }, + { + type: 'object', + node: Reference('item_stack'), + change: v => typeof v === 'string' ? { id: v } : {} + } + ])) + }, + 'show_entity': { + value: Opt(StringNode()), + contents: Opt(Mod(ObjectNode({ + name: Opt(Reference('text_component')), + type: StringNode({ validator: 'resource', params: { pool: 'entity_type' } }), + id: StringNode({ validator: 'uuid' }) + }), { + default: () => ({ + type: 'minecraft:pig', + id: '00000001-0001-0001-0001-000000000001' + }) + })) + } + } + })), + } + + schemas.register('text_style', ObjectNode({ + ...StyleFields + }, { context: 'text_component_object' })) + + const CommonFields: NodeChildren = { + ...StyleFields, + extra: Opt(Reference('text_component_list')) + } + + schemas.register('text_component_object', Mod(ChoiceNode([ + { + type: 'text', + match: v => typeof v === 'object', + change: v => ({text: ''}), + priority: -1, + node: ObjectNode({ + text: StringNode(), + ...CommonFields + }) + }, + { + type: 'translation', + match: v => v?.translate !== undefined, + change: v => ({translate: ''}), + node: ObjectNode({ + translate: StringNode(), + fallback: Opt(StringNode()), + with: Opt(Reference('text_component_list')), + ...CommonFields + }) + }, + { + type: 'score', + match: v => v?.score !== undefined, + change: v => ({score: {}}), + node: ObjectNode({ + score: ObjectNode({ + name: StringNode({ validator: 'entity', params: { amount: 'single', type: 'entities', isScoreHolder: true } }), + objective: StringNode({ validator: 'objective' }), + value: Opt(StringNode()) + }), + ...CommonFields + }) + }, + { + type: 'selector', + match: v => v?.selector !== undefined, + change: v => ({selector: ''}), + node: ObjectNode({ + selector: StringNode({ validator: 'entity', params: { amount: 'multiple', type: 'entities' } }), + separator: Opt(Reference('text_component')), + ...CommonFields + }) + }, + { + type: 'keybind', + match: v => v?.keybind !== undefined, + change: v => ({keybind: ''}), + node: ObjectNode({ + keybind: StringNode({ enum: 'keybind', additional: true }), + ...CommonFields + }) + }, + { + type: 'nbt', + match: v => v?.nbt !== undefined, + change: v => ({nbt: ''}), + node: ObjectNode({ + nbt: StringNode({ validator: 'nbt_path' }), + block: Opt(StringNode({ validator: 'vector', params: { dimension: 3, isInteger: true } })), + entity: Opt(StringNode({ validator: 'entity', params: { amount: 'multiple', type: 'entities' } })), + storage: Opt(StringNode({ validator: 'resource', params: { pool: '$storage' } })), + interpret: Opt(BooleanNode()), + separator: Opt(Reference('text_component')), + ...CommonFields + }) + } + ], { context: 'text_component_object', choiceContext: 'text_component.object' }), { + default: () => ({ + text: "" + }) + })) + + schemas.register('text_component_list', Mod(ListNode( + Reference('text_component') + ), { + default: () => [{ + text: "" + }] + })) +} diff --git a/java/1.21.4/src/schemas/TrialSpawner.ts b/java/1.21.4/src/schemas/TrialSpawner.ts new file mode 100644 index 00000000..2e4915b5 --- /dev/null +++ b/java/1.21.4/src/schemas/TrialSpawner.ts @@ -0,0 +1,105 @@ +import { + Reference as RawReference, + StringNode as RawStringNode, + ChoiceNode, + ListNode, + MapNode, + Mod, + ObjectNode, + Opt, + SchemaRegistry, + CollectionRegistry, + NumberNode +} from '@mcschema/core' +import { InclusiveRange } from './Common' + +export function initTrialSpawnerSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('light_limit', ChoiceNode([ + { + type: 'number', + node: NumberNode({ integer: true, min: 0, max: 15 }), + change: (v: any) => Array.isArray(v) ? (v[0] ?? 0) : (v?.min_inclusive ?? 0) + }, + { + type: 'list', + node: ListNode( + NumberNode({ integer: true, min: 0, max: 15 }), + { minLength: 2, maxLength: 2 }, + ), + change: (v: any) => typeof v === 'number' ? [v, v] : [v?.min_inclusive ?? 0, v?.max_inclusive ?? 0] + }, + { + type: 'object', + node: InclusiveRange({ integer: true, min: 0, max: 15 }), + change: (v: any) => Array.isArray(v) ? {min_inclusive: v[0] ?? 0, max_inclusive: v[1] ?? 0} : {min_inclusive: v ?? 0, max_inclusive: v ?? 0} + } + ])) + + schemas.register('trial_spawner', Mod(ObjectNode({ + spawn_range: Opt(NumberNode({ integer: true, min: 1, max: 128 })), + total_mobs: Opt(NumberNode({ min: 0 })), + simultaneous_mobs: Opt(NumberNode({ min: 0 })), + total_mobs_added_per_player: Opt(NumberNode({ min: 0 })), + simultaneous_mobs_added_per_player: Opt(NumberNode({ min: 0 })), + ticks_between_spawn: Opt(NumberNode({ integer: true, min: 0 })), + spawn_potentials: Opt(ListNode(ObjectNode({ + weight: NumberNode({ integer: true, min: 1 }), + data: ObjectNode({ + entity: ObjectNode({ + id: StringNode({ validator: 'nbt', params: { registry: { category: 'minecraft:entity', id: ['pop', { push: 'type' }] } } }), + // TODO: any data + }), + custom_spawn_rules: Opt(ObjectNode({ + block_light_limit: Opt(Reference('light_limit')), + sky_light_limit: Opt(Reference('light_limit')), + })), + equipment: Opt(ObjectNode({ + loot_table: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }), + slot_drop_chances: Opt(ChoiceNode([ + { + type: 'number', + node: NumberNode(), + }, + { + type: 'object', + node: MapNode( + StringNode({ enum: 'equipment_slot' }), + NumberNode() + ) + } + ])) + })) + }), + }))), + loot_tables_to_eject: Opt(ListNode( + ObjectNode({ + weight: NumberNode({ integer: true, min: 1 }), + data: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }), + }) + )), + items_to_drop_when_ominous: Opt(StringNode({ validator: 'resource', params: { pool: '$loot_table' } })), + }, { context: 'instrument' }), { + default: () => ({ + spawn_range: 4, + total_mobs: 6, + simultaneous_mobs: 2, + total_mobs_added_per_player: 2, + simultaneous_mobs_added_per_player: 1, + ticks_between_spawn: 40, + loot_tables_to_eject: [ + { + data: 'minecraft:spawners/trial_chamber/consumables', + weight: 1 + }, + { + data: 'minecraft:spawners/trial_chamber/key', + weight: 1 + }, + ], + items_to_drop_when_ominous: 'minecraft:spawners/trial_chamber/items_to_drop_when_ominous' + }) + })) +} diff --git a/java/1.21.4/src/schemas/Trims.ts b/java/1.21.4/src/schemas/Trims.ts new file mode 100644 index 00000000..5abb4229 --- /dev/null +++ b/java/1.21.4/src/schemas/Trims.ts @@ -0,0 +1,53 @@ +import { + StringNode as RawStringNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + SchemaRegistry, + CollectionRegistry, + Opt, + MapNode, + BooleanNode, +} from '@mcschema/core' + +export function initTrimsSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('trim_material', Mod(ObjectNode({ + asset_name: StringNode(), + description: Reference('text_component'), + ingredient: StringNode({ validator: 'resource', params: { pool: 'item' } }), + item_model_index: NumberNode(), + override_armor_materials: Opt(MapNode( + StringNode(), + StringNode(), + )), + }, { context: 'trim_material' }), { + default: () => ({ + asset_name: 'copper', + description: { + translate: 'trim_material.minecraft.copper', + color: '#B4684D', + }, + ingredient: 'minecraft:copper_ingot', + item_model_index: 0.5, + }) + })) + + schemas.register('trim_pattern', Mod(ObjectNode({ + asset_id: StringNode({ validator: 'resource', params: { pool: [], allowUnknown: true } }), + description: Reference('text_component'), + template_item: StringNode({ validator: 'resource', params: { pool: 'item' } }), + decal: Opt(BooleanNode()), + }, { context: 'trim_pattern' }), { + default: () => ({ + asset_id: 'minecraft:coast', + description: { + translate: 'trim_pattern.minecraft.coast', + }, + template_item: 'minecraft:coast_armor_trim_smithing_template', + }) + })) +} diff --git a/java/1.21.4/src/schemas/WolfVariant.ts b/java/1.21.4/src/schemas/WolfVariant.ts new file mode 100644 index 00000000..986f6e14 --- /dev/null +++ b/java/1.21.4/src/schemas/WolfVariant.ts @@ -0,0 +1,27 @@ +import { + StringNode as RawStringNode, + Mod, + ObjectNode, + Opt, + SchemaRegistry, + CollectionRegistry +} from '@mcschema/core' +import { Tag } from './Common' + +export function initWolfVariantSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('wolf_variant', Mod(ObjectNode({ + wild_texture: StringNode(), + angry_texture: StringNode(), + tame_texture: StringNode(), + biomes: Tag({ resource: '$worldgen/biome' }), + }, { context: 'wolf_variant' }), { + default: () => ({ + wild_texture: 'minecraft:entity/wolf/wolf', + tame_texture: 'minecraft:entity/wolf/wolf_tame', + angry_texture: 'minecraft:entity/wolf/wolf_angry', + biomes: 'minecraft:taiga' + }) + })) +} diff --git a/java/1.21.4/src/schemas/WorldSettings.ts b/java/1.21.4/src/schemas/WorldSettings.ts new file mode 100644 index 00000000..946832d2 --- /dev/null +++ b/java/1.21.4/src/schemas/WorldSettings.ts @@ -0,0 +1,73 @@ +import { + BooleanNode, + StringNode as RawStringNode, + NumberNode, + ObjectNode, + Reference as RawReference, + SchemaRegistry, + CollectionRegistry, + MapNode, + Mod, +} from '@mcschema/core' + +export function initWorldSettingsSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('world_settings', Mod(ObjectNode({ + generate_features: BooleanNode(), + bonus_chest: BooleanNode(), + seed: NumberNode({ integer: true }), + dimensions: MapNode( + StringNode({ validator: 'resource', params: { pool: '$dimension' } }), + Reference('dimension') + ) + }, { context: 'world_settings' }), { + default: () => { + const seed = Math.floor(Math.random() * (4294967296)) - 2147483648 + return { + generate_features: true, + bonus_chest: false, + seed, + dimensions: { + 'minecraft:overworld': { + type: 'minecraft:overworld', + generator: { + type: 'minecraft:noise', + seed, + biome_source: { + type: 'minecraft:multi_noise', + preset: "minecraft:overworld" + }, + settings: 'minecraft:overworld', + } + }, + 'minecraft:the_nether': { + type: 'minecraft:the_nether', + generator: { + type: 'minecraft:noise', + seed, + biome_source: { + type: 'minecraft:multi_noise', + preset: "minecraft:nether" + }, + settings: 'minecraft:nether' + } + }, + 'minecraft:the_end': { + type: "minecraft:the_end", + generator: { + type: "minecraft:noise", + seed, + biome_source: { + type: "minecraft:the_end", + seed + }, + settings: "minecraft:end" + } + } + } + } + } + })) +} diff --git a/java/1.21.4/src/schemas/assets/Atlas.ts b/java/1.21.4/src/schemas/assets/Atlas.ts new file mode 100644 index 00000000..3047ec00 --- /dev/null +++ b/java/1.21.4/src/schemas/assets/Atlas.ts @@ -0,0 +1,74 @@ +import { + StringNode as RawStringNode, + NumberNode, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + Switch, + Case, + Opt, + Reference as RawReference, + ListNode, + MapNode, + StringOrList, + Mod, +} from '@mcschema/core' + +export function initAtlasSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('atlas', ObjectNode({ + sources: ListNode( + Reference('sprite_source') + ) + }, { context: 'atlas' })) + + schemas.register('sprite_source', Mod(ObjectNode({ + type: StringNode({ enum: 'sprite_source_type' }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'single': { + resource: StringNode({ validator: 'resource', params: { pool: '$texture' } }), + sprite: Opt(StringNode({ validator: 'resource', params: { pool: '$texture' } })), + }, + 'directory': { + source: StringNode(), + prefix: StringNode(), + }, + 'filter': { + pattern: Reference('resource_location_pattern'), + }, + 'unstitch': { + resource: StringNode({ validator: 'resource', params: { pool: '$texture' } }), + divisor_x: Opt(NumberNode()), + divisor_y: Opt(NumberNode()), + regions: ListNode( + ObjectNode({ + sprite: StringNode({ validator: 'resource', params: { pool: '$texture' } }), + x: NumberNode(), + y: NumberNode(), + width: NumberNode(), + height: NumberNode(), + }) + ) + }, + 'paletted_permutations': { + textures: ListNode( + StringNode({ validator: 'resource', params: { pool: '$texture' } }), + ), + palette_key: StringNode({ validator: 'resource', params: { pool: '$texture' } }), + permutations: MapNode( + StringNode(), + StringNode({ validator: 'resource', params: { pool: '$texture' } }), + ), + } + } + }, { context: 'sprite_source' }), { + default: () => ({ + type: 'directory', + source: 'block', + prefix: 'block/' + }) + })) +} diff --git a/java/1.21.4/src/schemas/assets/BlockDefinition.ts b/java/1.21.4/src/schemas/assets/BlockDefinition.ts new file mode 100644 index 00000000..47f9accf --- /dev/null +++ b/java/1.21.4/src/schemas/assets/BlockDefinition.ts @@ -0,0 +1,106 @@ +import { + StringNode as RawStringNode, + Mod, + NumberNode, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + NodeChildren, + Switch, + Case, + Opt, + BooleanNode, + Reference as RawReference, + ChoiceNode, + MapNode, + ListNode, + NumberEnum, +} from '@mcschema/core' + +export function initBlockDefinitionSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('block_definition', Mod(ChoiceNode([ + { + type: 'variants', + match: (v: any) => v.variants !== undefined, + node: ObjectNode({ + variants: MapNode( + StringNode(), + Reference('model_variant') + ) + }), + change: (v: any) => ({ variants: { "": {} } }) + }, + { + type: 'multipart', + match: (v: any) => v.multipart !== undefined, + node: ObjectNode({ + multipart: ListNode( + ObjectNode({ + when: Opt(Reference('multipart_condition')), + apply: Reference('model_variant'), + }) + ) + }), + change: (v: any) => ({ multipart: { when: {}, apply: {} } }) + } + ], { context: 'block_definition' }), { + default: () => ({ + variants: { + "": { + model: 'minecraft:block/stone' + } + } + }) + })) + + const VariantChildren: NodeChildren = { + model: StringNode({ validator: 'resource', params: { pool: '$model' } }), + x: Opt(NumberEnum({ integer: true, values: [0, 90, 180, 270] })), + y: Opt(NumberEnum({ integer: true, values: [0, 90, 180, 270] })), + uvlock: Opt(BooleanNode()), + } + + schemas.register('model_variant', ChoiceNode([ + { + type: 'object', + node: ObjectNode(VariantChildren), + change: (v: any) => Array.isArray(v) && v.length > 0 ? v[0] : ({}) + }, + { + type: 'list', + node: ListNode( + ObjectNode({ + ...VariantChildren, + weight: Opt(NumberNode({ integer: true, min: 1 })) + }, { context: 'model_variant' }) + ), + change: (v: any) => [{ weight: 1, ...v }] + } + ], { context: 'model_variant' })) + + schemas.register('multipart_condition', ChoiceNode([ + { + type: 'object', + priority: -1, + match: () => true, + node: MapNode( + StringNode(), + StringNode() + ), + change: (v: any) => typeof v === 'object' && Array.isArray(v?.OR) && v.OR.length > 0 ? v.OR[0] : ({}) + }, + { + type: 'or', + match: (v: any) => typeof v === 'object' && v?.OR !== undefined, + node: ObjectNode({ + OR: ListNode( + Reference('multipart_condition') + ) + }), + change: (v: any) => ({ OR: [v ?? {}] }) + } + ], { context: 'multipart_condition' })) +} diff --git a/java/1.21.4/src/schemas/assets/Font.ts b/java/1.21.4/src/schemas/assets/Font.ts new file mode 100644 index 00000000..0174b95b --- /dev/null +++ b/java/1.21.4/src/schemas/assets/Font.ts @@ -0,0 +1,83 @@ +import { + StringNode as RawStringNode, + NumberNode, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + Switch, + Case, + Opt, + Reference as RawReference, + ListNode, + MapNode, + StringOrList, + Mod, + BooleanNode, +} from '@mcschema/core' + +export function initFontSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('font', ObjectNode({ + providers: ListNode( + Reference('glyph_provider') + ) + }, { context: 'font' })) + + schemas.register('glyph_provider', Mod(ObjectNode({ + type: StringNode({ enum: 'glyph_provider_type' }), + filter: Opt(MapNode( + StringNode({ enum: 'font_option' }), + BooleanNode(), + )), + [Switch]: [{ push: 'type' }], + [Case]: { + 'bitmap': { + file: StringNode({ validator: 'resource', params: { pool: '$texture', suffix: '.png' } }), + height: Opt(NumberNode({ integer: true })), + ascent: NumberNode({ integer: true }), + chars: ListNode( + StringNode(), + { minLength: 1 } + ) + }, + 'reference': { + id: StringNode({ validator: 'resource', params: { pool: '$font' } }), + }, + 'ttf': { + file: StringNode({ validator: 'resource', params: { pool: '$texture', suffix: '.png' } }), + size: Opt(NumberNode()), + oversample: Opt(NumberNode()), + shift: Opt(ListNode( + NumberNode({ min: -100, max: 100 }), + { minLength: 2, maxLength: 2 } + )), + skip: Opt(StringOrList( + StringNode() + )) + }, + 'space': { + advances: MapNode( + StringNode(), + NumberNode() + ) + }, + 'unihex': { + hex_file: StringNode(), + size_overrides: ListNode( + ObjectNode({ + from: StringNode(), + to: StringNode(), + left: NumberNode({ integer: true }), + right: NumberNode({ integer: true }), + }) + ) + }, + } + }, { context: 'glyph_provider' }), { + default: () => ({ + type: 'bitmap', + }), + })) +} diff --git a/java/1.21.4/src/schemas/assets/Model.ts b/java/1.21.4/src/schemas/assets/Model.ts new file mode 100644 index 00000000..14b91d36 --- /dev/null +++ b/java/1.21.4/src/schemas/assets/Model.ts @@ -0,0 +1,101 @@ +import { + StringNode as RawStringNode, + Mod, + NumberNode, + NumberEnum, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + NodeChildren, + Switch, + Case, + Opt, + BooleanNode, + Reference as RawReference, + MapNode, + ListNode, + ChoiceNode, +} from '@mcschema/core' + +export function initModelSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + const Vec = (length: number, min?: number, max?: number) => + ListNode( + NumberNode({ min, max }), + { minLength: length, maxLength: length } + ) + + const Texture = ChoiceNode([ + { + type: 'alias', + match: (v: any) => typeof v === 'string' && v.startsWith('#'), + node: StringNode(), + change: (v: any) => typeof v === 'string' ? `#${v}` : "#0" + }, + { + type: 'reference', + match: (v: any) => typeof v === 'string', + node: StringNode({ validator: 'resource', params: { pool: '$texture' } }), + change: (v: any) => typeof v === 'string' ? v.replace(/^#/, '') : "" + } + ], { choiceContext: 'texture' }) + + schemas.register('model', Mod(ObjectNode({ + parent: Opt(StringNode({ validator: 'resource', params: { pool: '$model' } })), + ambientocclusion: Opt(BooleanNode()), + gui_light: Opt(StringNode({ enum: 'gui_light' })), + textures: Opt(MapNode( + StringNode(), + Texture + )), + elements: Opt(ListNode( + ObjectNode({ + from: Vec(3, -16, 32), + to: Vec(3, -16, 32), + rotation: Opt(ObjectNode({ + origin: Vec(3), + axis: StringNode({ enum: 'axis' }), + angle: NumberEnum({ values: [-45, -22.5, 0, 22.5, 45] }), + rescale: Opt(BooleanNode()) + })), + shade: Opt(BooleanNode()), + faces: MapNode( + StringNode({ enum: 'direction' }), + ObjectNode({ + texture: Texture, + uv: Opt(Vec(4)), + cullface: Opt(StringNode({ enum: 'direction' })), + rotation: Opt(NumberNode({ integer: true })), + tintindex: Opt(NumberNode({ integer: true })) + }) + ) + }, { context: 'model_element' }) + )), + display: Opt(MapNode( + StringNode({ enum: 'display_position' }), + ObjectNode({ + rotation: Vec(3), + translation: Vec(3, -80, 80), + scale: Vec(3, -4, 4) + }, { context: 'item_transform' }) + )), + overrides: Opt(ListNode( + ObjectNode({ + predicate: MapNode( + StringNode({ enum: 'item_model_predicates' }), + NumberNode() + ), + model: StringNode({ validator: 'resource', params: { pool: '$model' } }) + }, { context: 'model_override' }) + )) + }, { context: 'model' }), { + default: () => ({ + parent: 'minecraft:item/generated', + textures: { + layer0: 'minecraft:item/diamond' + } + }) + })) +} diff --git a/java/1.21.4/src/schemas/assets/index.ts b/java/1.21.4/src/schemas/assets/index.ts new file mode 100644 index 00000000..962e561f --- /dev/null +++ b/java/1.21.4/src/schemas/assets/index.ts @@ -0,0 +1,12 @@ +import { CollectionRegistry, SchemaRegistry } from '@mcschema/core' +import { initModelSchemas } from './Model' +import { initBlockDefinitionSchemas } from './BlockDefinition' +import { initFontSchemas } from './Font' +import { initAtlasSchemas } from './Atlas' + +export function initAssetsSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + initAtlasSchemas(schemas, collections) + initBlockDefinitionSchemas(schemas, collections) + initFontSchemas(schemas, collections) + initModelSchemas(schemas, collections) +} diff --git a/java/1.21.4/src/schemas/index.ts b/java/1.21.4/src/schemas/index.ts new file mode 100644 index 00000000..288070f9 --- /dev/null +++ b/java/1.21.4/src/schemas/index.ts @@ -0,0 +1,57 @@ +import { CollectionRegistry, SchemaRegistry } from '@mcschema/core' +import { initAdvancementSchemas } from './Advancement' +import { initAssetsSchemas } from './assets' +import { initBannerPatternSchemas } from './BannerPattern' +import { initChatTypeSchemas } from './ChatType' +import { initCommonSchemas } from './Common' +import { initConditionSchemas } from './Condition' +import { initDamageTypeSchemas } from './DamageType' +import { initDimensionSchemas } from './Dimension' +import { initDimensionTypeSchemas } from './DimensionType' +import { initItemModifierSchemas } from './ItemModifier' +import { initLootTableSchemas } from './LootTable' +import { initPackMcmetaSchemas } from './PackMcmeta' +import { initPredicatesSchemas } from './Predicates' +import { initRecipeSchemas } from './Recipe' +import { initTagsSchemas } from './Tags' +import { initTextComponentSchemas } from './TextComponent' +import { initTrialSpawnerSchemas } from './TrialSpawner' +import { initTrimsSchemas } from './Trims' +import { initWolfVariantSchemas } from './WolfVariant' +import { initWorldgenSchemas } from './worldgen' +import { initWorldSettingsSchemas } from './WorldSettings' +import { initComponentsSchemas } from './Components' +import { initPaintingVariantSchemas } from './PaintingVariant' +import { initEnchantmentSchemas } from './Enchantment' +import { initJukeboxSongSchemas } from './JukeboxSong' +import { initInstrumentSchemas } from './Instrument' + +export function initSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + // `Common.ts` is the only file that has exports. It should be initialized first. + initCommonSchemas(schemas, collections) + initComponentsSchemas(schemas, collections) + initChatTypeSchemas(schemas, collections) + initAdvancementSchemas(schemas, collections) + initAssetsSchemas(schemas, collections) + initBannerPatternSchemas(schemas, collections) + initConditionSchemas(schemas, collections) + initDamageTypeSchemas(schemas, collections) + initDimensionTypeSchemas(schemas, collections) + initDimensionSchemas(schemas, collections) + initEnchantmentSchemas(schemas, collections) + initInstrumentSchemas(schemas, collections) + initItemModifierSchemas(schemas, collections) + initJukeboxSongSchemas(schemas, collections) + initLootTableSchemas(schemas, collections) + initPackMcmetaSchemas(schemas, collections) + initPaintingVariantSchemas(schemas, collections) + initPredicatesSchemas(schemas, collections) + initRecipeSchemas(schemas, collections) + initTagsSchemas(schemas, collections) + initTextComponentSchemas(schemas, collections) + initTrialSpawnerSchemas(schemas, collections) + initTrimsSchemas(schemas, collections) + initWolfVariantSchemas(schemas, collections) + initWorldgenSchemas(schemas, collections) + initWorldSettingsSchemas(schemas, collections) +} diff --git a/java/1.21.4/src/schemas/worldgen/Biome.ts b/java/1.21.4/src/schemas/worldgen/Biome.ts new file mode 100644 index 00000000..42c36dce --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/Biome.ts @@ -0,0 +1,109 @@ +import { + BooleanNode, + Reference as RawReference, + StringNode as RawStringNode, + ListNode, + MapNode, + Mod, + NumberNode, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + Opt, + INode, +} from '@mcschema/core' +import { Tag } from '../Common' + +export let MobCategorySpawnSettings: INode + +export function initBiomeSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + MobCategorySpawnSettings = Mod(ListNode( + ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'entity_type' } }), + weight: NumberNode({ integer: true }), + minCount: NumberNode({ integer: true }), + maxCount: NumberNode({ integer: true }) + }) + ), { + category: () => 'pool', + default: () => [{ + type: 'minecraft:bat', + weight: 1 + }] + }) + + schemas.register('biome', Mod(ObjectNode({ + temperature: NumberNode(), + downfall: NumberNode(), + has_precipitation: BooleanNode(), + temperature_modifier: Opt(StringNode({ enum: ['none', 'frozen'] })), + creature_spawn_probability: Opt(NumberNode({ min: 0, max: 1 })), + effects: ObjectNode({ + sky_color: NumberNode({ color: true }), + fog_color: NumberNode({ color: true }), + water_color: NumberNode({ color: true }), + water_fog_color: NumberNode({ color: true }), + grass_color: Opt(NumberNode({ color: true })), + foliage_color: Opt(NumberNode({ color: true })), + grass_color_modifier: Opt(StringNode({ enum: ['none', 'dark_forest', 'swamp'] })), + ambient_sound: Opt(Reference('sound_event')), + mood_sound: Opt(ObjectNode({ + sound: Reference('sound_event'), + tick_delay: NumberNode({ integer: true }), + block_search_extent: NumberNode({ integer: true }), + offset: NumberNode() + })), + additions_sound: Opt(ObjectNode({ + sound: Reference('sound_event'), + tick_chance: NumberNode({ min: 0, max: 1 }) + })), + music: Opt(ObjectNode({ + sound: Reference('sound_event'), + min_delay: NumberNode({ integer: true, min: 0 }), + max_delay: NumberNode({ integer: true, min: 0 }), + replace_current_music: BooleanNode() + })), + particle: Opt(ObjectNode({ + options: Reference('particle'), + probability: NumberNode({ min: 0, max: 1 }) + })) + }), + spawners: MapNode( + StringNode({ enum: 'mob_category' }), + MobCategorySpawnSettings + ), + spawn_costs: MapNode( + StringNode({ validator: 'resource', params: { pool: 'entity_type' } }), + Mod(ObjectNode({ + energy_budget: NumberNode(), + charge: NumberNode() + }, { category: 'function' }), { + default: () => ({ + energy_budget: 0.12, + charge: 1.0 + }) + }) + ), + carvers: Tag({ resource: '$worldgen/configured_carver', inlineSchema: 'configured_carver' }), + features: ListNode( + Mod(Tag({ resource: '$worldgen/placed_feature', inlineSchema: 'placed_feature' }), { category: () => 'predicate' }), + { maxLength: 11 } + ) + }, { context: 'biome' }), { + default: () => ({ + temperature: 0.8, + downfall: 0.4, + precipitation: 'rain', + category: 'plains', + effects: { + sky_color: 7907327, + fog_color: 12638463, + water_color: 4159204, + water_fog_color: 329011 + } + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/Carver.ts b/java/1.21.4/src/schemas/worldgen/Carver.ts new file mode 100644 index 00000000..da9e2cf1 --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/Carver.ts @@ -0,0 +1,69 @@ +import { + StringNode as RawStringNode, + Mod, + NumberNode, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + NodeChildren, + Switch, + Case, + Opt, + BooleanNode, + Reference as RawReference, +} from '@mcschema/core' +import { FloatProvider, Tag } from '../Common' + +export function initCarverSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + const CanyonConfig: NodeChildren = { + vertical_rotation: FloatProvider(), + shape: ObjectNode({ + distance_factor: FloatProvider(), + thickness: FloatProvider(), + width_smoothness: NumberNode({ integer: true, min: 0 }), + horizontal_radius_factor: FloatProvider(), + vertical_radius_default_factor: NumberNode(), + vertical_radius_center_factor: NumberNode() + }) + } + + const CaveConfig: NodeChildren = { + horizontal_radius_multiplier: FloatProvider(), + vertical_radius_multiplier: FloatProvider(), + floor_level: FloatProvider({ min: -1, max: 1 }), + } + + schemas.register('configured_carver', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/carver' } }), + config: ObjectNode({ + probability: NumberNode({ min: 0, max: 1 }), + y: Reference('height_provider'), + yScale: FloatProvider(), + lava_level: Reference('vertical_anchor'), + replaceable: Tag({ resource: 'block' }), + debug_settings: Opt(ObjectNode({ + debug_mode: Opt(BooleanNode()), + air_state: Opt(Reference('block_state')), + water_state: Opt(Reference('block_state')), + lava_state: Opt(Reference('block_state')), + barrier_state: Opt(Reference('block_state')) + })), + [Switch]: ['pop', { push: 'type' }], + [Case]: { + 'minecraft:canyon': CanyonConfig, + 'minecraft:cave': CaveConfig, + 'minecraft:nether_cave': CaveConfig + }, + }) + }, { context: 'carver' }), { + default: () => ({ + type: 'minecraft:cave', + config: { + probability: 0.1 + } + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/Decorator.ts b/java/1.21.4/src/schemas/worldgen/Decorator.ts new file mode 100644 index 00000000..12758dc6 --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/Decorator.ts @@ -0,0 +1,77 @@ +import { + Case, + StringNode as RawStringNode, + NumberNode, + ObjectNode, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, + Opt, + ListNode, +} from '@mcschema/core' +import { IntProvider } from '../Common' + +export function initDecoratorSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('decorator', ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/placement_modifier_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:block_predicate_filter': { + predicate: Reference('block_predicate_worldgen') + }, + 'minecraft:rarity_filter': { + chance: NumberNode({ integer: true, min: 0 }) + }, + 'minecraft:count': { + count: IntProvider({ min: 0, max: 256 }) + }, + 'minecraft:count_on_every_layer': { + count: IntProvider({ min: 0, max: 256 }) + }, + 'minecraft:noise_threshold_count': { + noise_level: NumberNode(), + below_noise: NumberNode({ integer: true }), + above_noise: NumberNode({ integer: true }) + }, + 'minecraft:noise_based_count': { + noise_to_count_ratio: NumberNode({ integer: true }), + noise_factor: NumberNode(), + noise_offset: Opt(NumberNode()) + }, + 'minecraft:environment_scan': { + direction_of_search: StringNode({ enum: ['up', 'down'] }), + max_steps: NumberNode({ integer: true, min: 1, max: 32 }), + target_condition: Reference('block_predicate_worldgen'), + allowed_search_condition: Opt(Reference('block_predicate_worldgen')) + }, + 'minecraft:heightmap': { + heightmap: StringNode({ enum: 'heightmap_type' }) + }, + 'minecraft:height_range': { + height: Reference('height_provider') + }, + 'minecraft:random_offset': { + xz_spread: IntProvider({ min: -16, max: 16 }), + y_spread: IntProvider({ min: -16, max: 16 }), + }, + 'minecraft:fixed_placement': { + positions: ListNode(ListNode( + NumberNode({ integer: true }), + { minLength: 3, maxLength: 3 } + )) + }, + 'minecraft:surface_relative_threshold_filter': { + heightmap: StringNode({ enum: 'heightmap_type' }), + min_inclusive: Opt(NumberNode({ integer: true })), + max_inclusive: Opt(NumberNode({ integer: true })) + }, + 'minecraft:surface_water_depth_filter': { + max_water_depth: NumberNode({ integer: true }) + } + } + }, { context: 'decorator', category: 'predicate' })) +} diff --git a/java/1.21.4/src/schemas/worldgen/DensityFunction.ts b/java/1.21.4/src/schemas/worldgen/DensityFunction.ts new file mode 100644 index 00000000..6e5a5f1d --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/DensityFunction.ts @@ -0,0 +1,194 @@ +import { + StringNode as RawStringNode, + Mod, + ObjectNode, + Reference as RawReference, + SchemaRegistry, + CollectionRegistry, + Switch, + Case, + NumberNode, + INode, + ChoiceNode, + ListNode, +} from '@mcschema/core' + +export let DensityFunction: INode + +export function initDensityFunctionSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + const Reference = RawReference.bind(undefined, schemas) + + DensityFunction = Mod(ChoiceNode([ + { + type: 'number', + node: NumberNode(), + change: () => 0 + }, + { + type: 'string', + priority: 1, + node: StringNode({ validator: 'resource', params: { pool: '$worldgen/density_function' }}), + change: () => undefined + }, + { + type: 'object', + node: Reference('density_function'), + change: () => ({}) + } + ], { choiceContext: 'density_function' }), { + default: () => 0 + }) + + const NoiseRange = NumberNode({ min: -1000000, max: 1000000 }) + const YRange = NumberNode({ integer: true, min: -2032 * 2, max: 2031 * 2 }) + + schemas.register('density_function', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/density_function_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:abs': { + argument: DensityFunction, + }, + 'minecraft:add': { + argument1: DensityFunction, + argument2: DensityFunction, + }, + 'minecraft:blend_density': { + argument: DensityFunction, + }, + 'minecraft:cache_2d': { + argument: DensityFunction, + }, + 'minecraft:cache_all_in_cell': { + argument: DensityFunction, + }, + 'minecraft:cache_once': { + argument: DensityFunction, + }, + 'minecraft:clamp': { + input: Reference('density_function'), + min: NoiseRange, + max: NoiseRange, + }, + 'minecraft:constant': { + argument: NumberNode(), + }, + 'minecraft:cube': { + argument: DensityFunction, + }, + 'minecraft:flat_cache': { + argument: DensityFunction, + }, + 'minecraft:half_negative': { + argument: DensityFunction, + }, + 'minecraft:interpolated': { + argument: DensityFunction, + }, + 'minecraft:max': { + argument1: DensityFunction, + argument2: DensityFunction, + }, + 'minecraft:min': { + argument1: DensityFunction, + argument2: DensityFunction, + }, + 'minecraft:mul': { + argument1: DensityFunction, + argument2: DensityFunction, + }, + 'minecraft:noise': { + noise: StringNode({ validator: 'resource', params: { pool: '$worldgen/noise' }}), + xz_scale: NumberNode(), + y_scale: NumberNode(), + }, + 'minecraft:old_blended_noise': { + xz_scale: NumberNode(), + y_scale: NumberNode(), + xz_factor: NumberNode(), + y_factor: NumberNode(), + smear_scale_multiplier: NumberNode({ min: 1, max: 8 }), + }, + 'minecraft:quarter_negative': { + argument: DensityFunction, + }, + 'minecraft:range_choice': { + input: DensityFunction, + min_inclusive: NoiseRange, + max_exclusive: NoiseRange, + when_in_range: DensityFunction, + when_out_of_range: DensityFunction, + }, + 'minecraft:shift': { + argument: StringNode({ validator: 'resource', params: { pool: '$worldgen/noise' }}), + }, + 'minecraft:shift_a': { + argument: StringNode({ validator: 'resource', params: { pool: '$worldgen/noise' }}), + }, + 'minecraft:shift_b': { + argument: StringNode({ validator: 'resource', params: { pool: '$worldgen/noise' }}), + }, + 'minecraft:shifted_noise': { + noise: StringNode({ validator: 'resource', params: { pool: '$worldgen/noise' }}), + xz_scale: NumberNode(), + y_scale: NumberNode(), + shift_x: DensityFunction, + shift_y: DensityFunction, + shift_z: DensityFunction, + }, + 'minecraft:slide': { + argument: DensityFunction, + }, + 'minecraft:spline': { + spline: Reference('cubic_spline'), + }, + 'minecraft:square': { + argument: DensityFunction, + }, + 'minecraft:squeeze': { + argument: DensityFunction, + }, + 'minecraft:weird_scaled_sampler': { + rarity_value_mapper: StringNode({ enum: ['type_1', 'type_2'] }), + noise: StringNode({ validator: 'resource', params: { pool: '$worldgen/noise' }}), + input: DensityFunction, + }, + 'minecraft:y_clamped_gradient': { + from_y: YRange, + to_y: YRange, + from_value: NoiseRange, + to_value: NoiseRange, + }, + } + }, { context: 'density_function', disableSwitchContext: true }), { + default: () => ({ + type: 'minecraft:noise', + noise: 'minecraft:cave_entrance', + xz_scale: 0.75, + y_scale: 0.5 + }) + })) + + schemas.register('cubic_spline', Mod(ChoiceNode([ + { + type: 'number', + node: NumberNode() + }, + { + type: 'object', + node: ObjectNode({ + coordinate: DensityFunction, + points: ListNode( + ObjectNode({ + location: NumberNode(), + derivative: NumberNode(), + value: Reference('cubic_spline') + }) + ) + }, { category: 'function' }) + } + ], { context: 'terrain_spline', choiceContext: 'terrain_spline' }), { + default: () => 0 + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/Feature.ts b/java/1.21.4/src/schemas/worldgen/Feature.ts new file mode 100644 index 00000000..67e49253 --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/Feature.ts @@ -0,0 +1,679 @@ +import { + Case, + StringNode as RawStringNode, + Mod, + NodeChildren, + NumberNode, + ObjectNode, + Reference as RawReference, + Switch, + BooleanNode, + ListNode, + ChoiceNode, + SchemaRegistry, + CollectionRegistry, + Opt, +} from '@mcschema/core' +import { FloatProvider, InclusiveRange, IntProvider, Tag } from '../Common' +import './Decorator' +import './ProcessorList' +import { Processors } from './ProcessorList' + +export function initFeatureSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + const ConfiguredFeature = ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: '$worldgen/configured_feature' } }) + }, + { + type: 'object', + node: Reference('configured_feature') + } + ], { choiceContext: 'feature' }) + + const PlacedFeature = ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: '$worldgen/placed_feature' } }) + }, + { + type: 'object', + node: Reference('placed_feature') + } + ], { choiceContext: 'placed_feature' }) + + const RandomPatchConfig: NodeChildren = { + tries: Opt(NumberNode({ integer: true, min: 1 })), + xz_spread: Opt(NumberNode({ integer: true, min: 0 })), + y_spread: Opt(NumberNode({ integer: true, min: 0 })), + feature: PlacedFeature, + } + + const HugeMushroomConfig: NodeChildren = { + cap_provider: Reference('block_state_provider'), + stem_provider: Reference('block_state_provider'), + foliage_radius: Opt(NumberNode({ integer: true })) + } + + const TargetBlockState = ObjectNode({ + target: Reference('rule_test'), + state: Reference('block_state') + }) + + const OreConfig: NodeChildren = { + size: NumberNode({ integer: true, min: 0, max: 64 }), + discard_chance_on_air_exposure: NumberNode({ min: 0, max: 1 }), + targets: ListNode( + TargetBlockState + ) + } + + const VegetationPatchConfig: NodeChildren = { + surface: StringNode({ enum: 'cave_surface' }), + depth: IntProvider({ min: 1, max: 128 }), + vertical_range: NumberNode({ integer: true, min: 1, max: 256 }), + extra_bottom_block_chance: NumberNode({ min: 0, max: 1}), + extra_edge_column_chance: NumberNode({ min: 0, max: 1}), + vegetation_chance: NumberNode({ min: 0, max: 1}), + xz_radius: IntProvider(), + replaceable: StringNode({ validator: 'resource', params: { pool: 'block', requireTag: true } }), + ground_state: Reference('block_state_provider'), + vegetation_feature: PlacedFeature + } + + schemas.register('configured_feature', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/feature' } }), + config: ObjectNode({ + [Switch]: ['pop', { push: 'type' }], + [Case]: { + 'minecraft:bamboo': { + probability: NumberNode({ min: 0, max: 1 }) + }, + 'minecraft:basalt_columns': { + reach: IntProvider({ min: 0, max: 3 }), + height: IntProvider({ min: 1, max: 10 }) + }, + 'minecraft:block_column': { + direction: StringNode({ enum: ['up', 'down', 'north', 'east', 'south', 'west'] }), + allowed_placement: Reference('block_predicate_worldgen'), + prioritize_tip: BooleanNode(), + layers: ListNode( + ObjectNode({ + height: IntProvider({ min: 0 }), + provider: Reference('block_state_provider') + }) + ) + }, + 'minecraft:block_pile': { + state_provider: Reference('block_state_provider') + }, + 'minecraft:delta_feature': { + contents: Reference('block_state'), + rim: Reference('block_state'), + size: IntProvider({ min: 0, max: 16 }), + rim_size: IntProvider({ min: 0, max: 16 }) + }, + 'minecraft:disk': { + state_provider: Reference('rule_based_block_state_provider'), + target: Reference('block_predicate_worldgen'), + radius: IntProvider({ min: 0, max: 8 }), + half_height: NumberNode({ integer: true, min: 0, max: 4 }), + }, + 'minecraft:dripstone_cluster': { + floor_to_ceiling_search_range: NumberNode({ integer: true, min: 1, max: 512 }), + height: IntProvider({ min: 0, max: 128 }), + radius: IntProvider({ min: 0, max: 128 }), + max_stalagmite_stalactite_height_diff: NumberNode({ integer: true, min: 0, max: 64 }), + height_deviation: NumberNode({ integer: true, min: 1, max: 64 }), + dripstone_block_layer_thickness: IntProvider({ min: 0, max: 128 }), + density: FloatProvider({ min: 0, max: 2 }), + wetness: FloatProvider({ min: 0, max: 2 }), + chance_of_dripstone_column_at_max_distance_from_center: NumberNode({ min: 0, max: 1 }), + max_distance_from_edge_affecting_chance_of_dripstone_column: NumberNode({ integer: true, min: 1, max: 64 }), + max_distance_from_center_affecting_height_bias: NumberNode({ integer: true, min: 1, max: 64 }) + }, + 'minecraft:end_gateway': { + exact: BooleanNode(), + exit: Opt(Reference('block_pos')) + }, + 'minecraft:end_spike': { + crystal_invulnerable: Opt(BooleanNode()), + crystal_beam_target: Opt(Reference('block_pos')), + spikes: ListNode( + ObjectNode({ + centerX: Opt(NumberNode({ integer: true })), + centerZ: Opt(NumberNode({ integer: true })), + radius: Opt(NumberNode({ integer: true })), + height: Opt(NumberNode({ integer: true })), + guarded: Opt(BooleanNode()) + }) + ) + }, + 'minecraft:fill_layer': { + state: Reference('block_state'), + height: NumberNode({ integer: true, min: 0, max: 255 }) + }, + 'minecraft:flower': RandomPatchConfig, + 'minecraft:forest_rock': { + state: Reference('block_state') + }, + 'minecraft:fossil': { + max_empty_corners_allowed: NumberNode({ integer: true, min: 0, max: 7 }), + fossil_structures: ListNode( + StringNode({ validator: 'resource', params: { pool: '$structure' } }) + ), + overlay_structures: ListNode( + StringNode({ validator: 'resource', params: { pool: '$structure' } }) + ), + fossil_processors: Processors, + overlay_processors: Processors, + }, + 'minecraft:geode': { + blocks: ObjectNode({ + filling_provider: Reference('block_state_provider'), + inner_layer_provider: Reference('block_state_provider'), + alternate_inner_layer_provider: Reference('block_state_provider'), + middle_layer_provider: Reference('block_state_provider'), + outer_layer_provider: Reference('block_state_provider'), + inner_placements: ListNode( + Reference('block_state') + ), + cannot_replace: StringNode({ validator: 'resource', params: { pool: 'block', requireTag: true } }), + invalid_blocks: StringNode({ validator: 'resource', params: { pool: 'block', requireTag: true } }) + }), + layers: ObjectNode({ + filling: Opt(NumberNode({ min: 0.01, max: 50 })), + inner_layer: Opt(NumberNode({ min: 0.01, max: 50 })), + middle_layer: Opt(NumberNode({ min: 0.01, max: 50 })), + outer_layer: Opt(NumberNode({ min: 0.01, max: 50 })), + }), + crack: ObjectNode({ + generate_crack_chance: Opt(NumberNode({ min: 0, max: 1 })), + base_crack_size: Opt(NumberNode({ min: 0, max: 5 })), + crack_point_offset: Opt(NumberNode({ min: 0, max: 10 })), + }), + noise_multiplier: Opt(NumberNode({ min: 0, max: 1 })), + use_potential_placements_chance: Opt(NumberNode({ min: 0, max: 1 })), + use_alternate_layer0_chance: Opt(NumberNode({ min: 0, max: 1 })), + placements_require_layer0_alternate: Opt(BooleanNode()), + outer_wall_distance: Opt(IntProvider({ min: 1, max: 20 })), + distribution_points: Opt(IntProvider({ min: 1, max: 20 })), + point_offset: Opt(IntProvider({ min: 0, max: 10 })), + min_gen_offset: Opt(NumberNode({ integer: true })), + max_gen_offset: Opt(NumberNode({ integer: true })), + invalid_blocks_threshold: NumberNode({ integer: true }) + }, + 'minecraft:multiface_growth': { + block: Opt(StringNode({ validator: 'resource', params: { pool: ['minecraft:glow_lichen', 'minecraft:sculk_vein'] } })), + search_range: Opt(NumberNode({ min: 1, max: 64, integer: true })), + chance_of_spreading: Opt(NumberNode({ min: 0, max: 1 })), + can_place_on_floor: Opt(BooleanNode()), + can_place_on_ceiling: Opt(BooleanNode()), + can_place_on_wall: Opt(BooleanNode()), + can_be_placed_on: Tag({ resource: 'block' }) + }, + 'minecraft:huge_brown_mushroom': HugeMushroomConfig, + 'minecraft:huge_fungus': { + hat_state: Reference('block_state'), + decor_state: Reference('block_state'), + stem_state: Reference('block_state'), + valid_base_block: Reference('block_state'), + replaceable_blocks: Reference('block_predicate_worldgen'), + planted: Opt(BooleanNode()) + }, + 'minecraft:huge_red_mushroom': HugeMushroomConfig, + 'minecraft:iceberg': { + state: Reference('block_state') + }, + 'minecraft:lake': { + fluid: Reference('block_state_provider'), + barrier: Reference('block_state_provider') + }, + 'minecraft:large_dripstone': { + floor_to_ceiling_search_range: Opt(NumberNode({ integer: true, min: 1, max: 512 })), + column_radius: IntProvider({ min: 0, max: 60 }), + height_scale: FloatProvider({ min: 0, max: 20 }), + max_column_radius_to_cave_height_ratio: NumberNode({ min: 0, max: 1 }), + stalactite_bluntness: FloatProvider({ min: 0.1, max: 10 }), + stalagmite_bluntness: FloatProvider({ min: 0.1, max: 10 }), + wind_speed: FloatProvider({ min: 0, max: 2 }), + min_radius_for_wind: NumberNode({ integer: true, min: 0, max: 100 }), + min_bluntness_for_wind: NumberNode({ min: 0, max: 5 }) + }, + 'minecraft:nether_forest_vegetation': { + state_provider: Reference('block_state_provider'), + spread_width: NumberNode({ integer: true, min: 1 }), + spread_height: NumberNode({ integer: true, min: 1 }) + }, + 'minecraft:netherrack_replace_blobs': { + state: Reference('block_state'), + target: Reference('block_state'), + radius: IntProvider({ min: 0, max: 12 }) + }, + 'minecraft:no_bonemeal_flower': RandomPatchConfig, + 'minecraft:ore': OreConfig, + 'minecraft:pointed_dripstone': { + chance_of_taller_dripstone: Opt(NumberNode({ min: 0, max: 1 })), + chance_of_directional_spread: Opt(NumberNode({ min: 0, max: 1 })), + chance_of_spread_radius2: Opt(NumberNode({ min: 0, max: 1 })), + chance_of_spread_radius3: Opt(NumberNode({ min: 0, max: 1 })), + }, + 'minecraft:random_patch': RandomPatchConfig, + 'minecraft:random_boolean_selector': { + feature_false: PlacedFeature, + feature_true: PlacedFeature + }, + 'minecraft:random_selector': { + features: ListNode( + ObjectNode({ + chance: NumberNode({ min: 0, max: 1 }), + feature: PlacedFeature + }) + ), + default: PlacedFeature + }, + 'minecraft:replace_single_block': { + targets: ListNode( + TargetBlockState + ) + }, + 'minecraft:root_system': { + required_vertical_space_for_tree: NumberNode({ integer: true, min: 1, max: 64 }), + root_radius: NumberNode({ integer: true, min: 1, max: 64 }), + root_placement_attempts: NumberNode({ integer: true, min: 1, max: 256 }), + root_column_max_height: NumberNode({ integer: true, min: 1, max: 4096 }), + hanging_root_radius: NumberNode({ integer: true, min: 1, max: 64 }), + hanging_roots_vertical_span: NumberNode({ integer: true, min: 0, max: 16 }), + hanging_root_placement_attempts: NumberNode({ integer: true, min: 0, max: 256 }), + allowed_vertical_water_for_tree: NumberNode({ integer: true, min: 1, max: 64 }), + root_replaceable: StringNode({ validator: 'resource', params: { pool: 'block', requireTag: true } }), + root_state_provider: Reference('block_state_provider'), + hanging_root_state_provider: Reference('block_state_provider'), + allowed_tree_position: Reference('block_predicate_worldgen'), + feature: PlacedFeature + }, + 'minecraft:scattered_ore': OreConfig, + 'minecraft:sculk_patch': { + charge_count: NumberNode({ integer: true, min: 1, max: 32 }), + amount_per_charge: NumberNode({ integer: true, min: 1, max: 500 }), + spread_attempts: NumberNode({ integer: true, min: 1, max: 64 }), + growth_rounds: NumberNode({ integer: true, min: 0, max: 8 }), + spread_rounds: NumberNode({ integer: true, min: 0, max: 8 }), + extra_rare_growths: IntProvider(), + catalyst_chance: NumberNode({ min: 0, max: 1 }), + }, + 'minecraft:sea_pickle': { + count: IntProvider({ min: 0, max: 256 }) + }, + 'minecraft:seagrass': { + probability: NumberNode({ min: 0, max: 1 }) + }, + 'minecraft:simple_block': { + to_place: Reference('block_state_provider') + }, + 'minecraft:simple_random_selector': { + features: Tag({ resource: '$worldgen/placed_feature', inlineSchema: 'placed_feature' }), + }, + 'minecraft:spring_feature': { + state: Reference('fluid_state'), + rock_count: NumberNode({ integer: true }), + hole_count: NumberNode({ integer: true }), + requires_block_below: BooleanNode(), + valid_blocks: Tag({ resource: 'block' }) + }, + 'minecraft:tree': { + ignore_vines: Opt(BooleanNode()), + force_dirt: Opt(BooleanNode()), + minimum_size: Reference('feature_size'), + dirt_provider: Reference('block_state_provider'), + trunk_provider: Reference('block_state_provider'), + foliage_provider: Reference('block_state_provider'), + root_placer: Opt(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/root_placer_type' } }), + root_provider: Reference('block_state_provider'), + trunk_offset_y: IntProvider(), + above_root_placement: Opt(ObjectNode({ + above_root_provider: Reference('block_state_provider'), + above_root_placement_chance: NumberNode({ min: 0, max: 1 }) + })), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:mangrove_root_placer': { + mangrove_root_placement: ObjectNode({ + max_root_width: NumberNode({ integer: true, min: 1, max: 12 }), + max_root_length: NumberNode({ integer: true, min: 1, max: 64 }), + random_skew_chance: NumberNode({ min: 0, max: 1 }), + can_grow_through: Tag({ resource: 'block' }), + muddy_roots_in: Tag({ resource: 'block' }), + muddy_roots_provider: Reference('block_state_provider'), + }) + } + } + }, { context: 'root_placer' })), + trunk_placer: ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/trunk_placer_type' } }), + base_height: NumberNode({ integer: true, min: 0, max: 32 }), + height_rand_a: NumberNode({ integer: true, min: 0, max: 24 }), + height_rand_b: NumberNode({ integer: true, min: 0, max: 24 }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:bending_trunk_placer': { + bend_length: IntProvider({ min: 1, max: 64 }), + min_height_for_leaves: Opt(NumberNode({ integer: true, min: 1 })) + }, + 'minecraft:cherry_trunk_placer': { + branch_count: IntProvider({ min: 1, max: 3 }), + branch_horizontal_length: IntProvider({ min: 2, max: 16 }), + branch_start_offset_from_top: ObjectNode({ + min_inclusive: NumberNode({ integer: true, min: -16, max: 0 }), + max_inclusive: NumberNode({ integer: true, min: -16, max: 0 }) + }, { context: 'int_provider.value' }), + branch_end_offset_from_top: IntProvider({ min: -16, max: 16 }) + }, + 'minecraft:upwards_branching_trunk_placer': { + extra_branch_steps: IntProvider({ min: 1 }), + extra_branch_length: IntProvider({ min: 0 }), + place_branch_per_log_probability: NumberNode({ min: 0, max: 1 }), + can_grow_through: Tag({ resource: 'block' }) + } + } + }, { context: 'trunk_placer' }), + foliage_placer: ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/foliage_placer_type' } }), + radius: IntProvider({ min: 0, max: 16 }), + offset: IntProvider({ min: 0, max: 16 }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:blob_foliage_placer': { + height: NumberNode({ integer: true, min: 0, max: 16 }) + }, + 'minecraft:bush_foliage_placer': { + height: NumberNode({ integer: true, min: 0, max: 16 }) + }, + 'minecraft:cherry_foliage_placer': { + height: IntProvider({ min: 4, max: 16 }), + wide_bottom_layer_hole_chance: NumberNode({ min: 0, max: 1 }), + corner_hole_chance: NumberNode({ min: 0, max: 1 }), + hanging_leaves_chance: NumberNode({ min: 0, max: 1 }), + hanging_leaves_extension_chance: NumberNode({ min: 0, max: 1 }) + }, + 'minecraft:fancy_foliage_placer': { + height: NumberNode({ integer: true, min: 0, max: 16 }) + }, + 'minecraft:jungle_foliage_placer': { + height: NumberNode({ integer: true, min: 0, max: 16 }) + }, + 'minecraft:mega_pine_foliage_placer': { + crown_height: IntProvider({ min: 0, max: 24 }) + }, + 'minecraft:pine_foliage_placer': { + height: IntProvider({ min: 0, max: 24 }) + }, + 'minecraft:random_spread_foliage_placer': { + foliage_height: IntProvider({ min: 1, max: 512 }), + leaf_placement_attempts: NumberNode({ integer: true, min: 0, max: 256 }) + }, + 'minecraft:spruce_foliage_placer': { + trunk_height: IntProvider({ min: 0, max: 24 }) + } + } + }, { context: 'foliage_placer', disableSwitchContext: true }), + decorators: ListNode( + ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/tree_decorator_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:alter_ground': { + provider: Reference('block_state_provider') + }, + 'minecraft:attached_to_leaves': { + probability: NumberNode({ min: 0, max: 1 }), + exclusion_radius_xz: NumberNode({ integer: true, min: 0, max: 16 }), + exclusion_radius_y: NumberNode({ integer: true, min: 0, max: 16 }), + required_empty_blocks: NumberNode({ integer: true, min: 1, max: 16 }), + block_provider: Reference('block_state_provider'), + directions: ListNode( + StringNode({ enum: 'direction' }) + ) + }, + 'minecraft:beehive': { + probability: NumberNode({ min: 0, max: 1 }) + }, + 'minecraft:cocoa': { + probability: NumberNode({ min: 0, max: 1 }) + }, + 'minecraft:leave_vine': { + probability: NumberNode({ min: 0, max: 1 }) + } + } + }, { context: 'tree_decorator' }) + ) + }, + 'minecraft:twisting_vines': { + spread_width: NumberNode({ integer: true, min: 1 }), + spread_height: NumberNode({ integer: true, min: 1 }), + max_height: NumberNode({ integer: true, min: 1 }), + }, + 'minecraft:underwater_magma': { + floor_search_range: NumberNode({ integer: true, min: 0, max: 512 }), + placement_radius_around_floor: NumberNode({ integer: true, min: 0, max: 64 }), + placement_probability_per_valid_position: NumberNode({ min: 0, max: 1 }) + }, + 'minecraft:vegetation_patch': VegetationPatchConfig, + 'minecraft:waterlogged_vegetation_patch': VegetationPatchConfig + } + }, { context: 'feature' }) + }, { context: 'feature' }), { + default: () => ({ + type: 'minecraft:tree', + config: { + minimum_size: { + type: 'minecraft:two_layers_feature_size' + }, + trunk_placer: { + type: 'minecraft:straight_trunk_placer', + base_height: 5, + height_rand_a: 2, + height_rand_b: 0 + }, + foliage_placer: { + type: 'minecraft:blob_foliage_placer', + radius: 2, + offset: 0, + height: 3 + } + } + }) + })) + + schemas.register('placed_feature', Mod(ObjectNode({ + feature: ConfiguredFeature, + placement: ListNode( + Reference('decorator') + ) + }, { context: 'placed_feature' }), { + default: () => ({ + feature: 'minecraft:oak', + placement: [ + { + type: 'minecraft:count', + count: 4 + }, + { + type: 'minecraft:in_square' + }, + { + type: 'minecraft:heightmap', + heightmap: 'OCEAN_FLOOR' + } + ] + }) + })) + + schemas.register('feature_size', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/feature_size_type' } }), + min_clipped_height: Opt(NumberNode({ min: 0, max: 80 })), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:two_layers_feature_size': { + limit: Opt(NumberNode({ integer: true, min: 0, max: 81 })), + lower_size: Opt(NumberNode({ integer: true, min: 0, max: 16 })), + upper_size: Opt(NumberNode({ integer: true, min: 0, max: 16 })) + }, + 'minecraft:three_layers_feature_size': { + limit: Opt(NumberNode({ integer: true, min: 0, max: 80 })), + upper_limit: Opt(NumberNode({ integer: true, min: 0, max: 80 })), + lower_size: Opt(NumberNode({ integer: true, min: 0, max: 16 })), + middle_size: Opt(NumberNode({ integer: true, min: 0, max: 16 })), + upper_size: Opt(NumberNode({ integer: true, min: 0, max: 16 })) + } + } + }, { disableSwitchContext: true }), { + default: () => ({ + type: 'minecraft:two_layers_feature_size' + }) + })) + + const NoiseProvider: NodeChildren = { + seed: NumberNode({ integer: true }), + noise: Reference('noise_parameters'), + scale: Mod(NumberNode({ min: Number.MIN_VALUE }), { default: () => 1 }) + } + + schemas.register('block_state_provider', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/block_state_provider_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:dual_noise_provider': { + ...NoiseProvider, + variety: InclusiveRange({ integer: true, min: 1, max: 64 }), + slow_noise: Reference('noise_parameters'), + slow_scale: Mod(NumberNode({ min: Number.MIN_VALUE }), { default: () => 1 }), + states: ListNode( + Reference('block_state') + ) + }, + 'minecraft:noise_threshold_provider': { + ...NoiseProvider, + threshold: NumberNode({ min: -1, max: 1 }), + high_chance: NumberNode({ min: 0, max: 1 }), + default_state: Reference('block_state'), + low_states: ListNode( + Reference('block_state') + ), + high_states: ListNode( + Reference('block_state') + ) + }, + 'minecraft:noise_provider': { + ...NoiseProvider, + states: ListNode( + Reference('block_state') + ) + }, + 'minecraft:randomized_int_state_provider': { + property: StringNode(), + values: IntProvider(), + source: Reference('block_state_provider') + }, + 'minecraft:rotated_block_provider': { + state: Reference('block_state') + }, + 'minecraft:simple_state_provider': { + state: Reference('block_state') + }, + 'minecraft:weighted_state_provider': { + entries: ListNode( + Mod(ObjectNode({ + weight: NumberNode({ integer: true, min: 1 }), + data: Reference('block_state') + }), { + default: () => ({ + data: {} + }) + }) + ) + } + } + }, { context: 'block_state_provider' }), { + default: () => ({ + type: 'minecraft:simple_state_provider' + }) + })) + + schemas.register('rule_based_block_state_provider', Mod(ObjectNode({ + fallback: Reference('block_state_provider'), + rules: ListNode( + ObjectNode({ + if_true: Reference('block_predicate_worldgen'), + then: Reference('block_state_provider') + }) + ) + }, { context: 'block_state_provider' }), { + default: () => ({ + fallback: { + type: 'minecraft:simple_state_provider' + } + }) + })) + + const Offset: NodeChildren = { + offset: Opt(ListNode( + NumberNode({ integer: true, min: -16, max: 16 }), + { minLength: 3, maxLength: 3 } + )) + } + + schemas.register('block_predicate_worldgen', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'block_predicate_type' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:all_of': { + predicates: ListNode( + Reference('block_predicate_worldgen') + ) + }, + 'minecraft:any_of': { + predicates: ListNode( + Reference('block_predicate_worldgen') + ) + }, + 'minecraft:has_sturdy_face': { + ...Offset, + direction: StringNode({ enum: 'direction' }) + }, + 'minecraft:inside_world_bounds': { + ...Offset, + }, + 'minecraft:matching_block_tag': { + ...Offset, + tag: StringNode({ validator: 'resource', params: { pool: '$tag/block' } }) + }, + 'minecraft:matching_blocks': { + ...Offset, + blocks: Tag({ resource: 'block' }) + }, + 'minecraft:matching_fluids': { + ...Offset, + fluids: Tag({ resource: 'fluid' }) + }, + 'minecraft:not': { + predicate: Reference('block_predicate_worldgen') + }, + 'minecraft:unobstructed': { + ...Offset, + }, + 'minecraft:would_survive': { + ...Offset, + state: Reference('block_state') + } + } + }, { context: 'block_predicate' }), { + default: () => ({ + type: 'minecraft:true' + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/NoiseSettings.ts b/java/1.21.4/src/schemas/worldgen/NoiseSettings.ts new file mode 100644 index 00000000..9a6e2d0c --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/NoiseSettings.ts @@ -0,0 +1,95 @@ +import { + BooleanNode, + StringNode as RawStringNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + MapNode, + SchemaRegistry, + CollectionRegistry, + Opt, + INode, + ModelPath, + Errors, + NodeOptions, + ChoiceNode, + ListNode, + Switch, + Case, +} from '@mcschema/core' +import { DefaultNoiseSettings } from '../Common' +import { DensityFunction } from './DensityFunction' + +export function initNoiseSettingsSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('noise_settings', Mod(ObjectNode({ + sea_level: NumberNode({ integer: true }), + disable_mob_generation: BooleanNode(), + aquifers_enabled: BooleanNode(), + ore_veins_enabled: BooleanNode(), + legacy_random_source: BooleanNode(), + default_block: Reference('block_state'), + default_fluid: Reference('block_state'), + noise: ObjectNode({ + min_y: NumberNode({ integer: true, min: -2048, max: 2047 }), + height: NumberNode({ integer: true, min: 0, max: 4096 }), + size_horizontal: NumberNode({ integer: true }), + size_vertical: NumberNode({ integer: true }), + }), + noise_router: ObjectNode({ + barrier: DensityFunction, + fluid_level_floodedness: DensityFunction, + fluid_level_spread: DensityFunction, + lava: DensityFunction, + temperature: DensityFunction, + vegetation: DensityFunction, + continents: DensityFunction, + erosion: DensityFunction, + depth: DensityFunction, + ridges: DensityFunction, + initial_density_without_jaggedness: DensityFunction, + final_density: DensityFunction, + vein_toggle: DensityFunction, + vein_ridged: DensityFunction, + vein_gap: DensityFunction, + }), + spawn_target: ListNode( + Reference('parameter_point') + ), + surface_rule: Reference('material_rule'), + }, { context: 'noise_settings' }), node => ({ + default: () => DefaultNoiseSettings, + validate: (path, value, errors, options) => { + value = node.validate(path, value, errors, options) + if (value?.noise?.min_y + value?.noise?.height > 2047) { + errors.add(path.push('noise').push('height'), 'error.min_y_plus_height', 2047) + } + if (value?.noise?.height % 16 !== 0) { + errors.add(path.push('noise').push('height'), 'error.height_multiple', 16) + } + if (value?.noise?.min_y % 16 !== 0) { + errors.add(path.push('noise').push('min_y'), 'error.min_y_multiple', 16) + } + return value + } + }))) + + schemas.register('noise_slider', ObjectNode({ + target: NumberNode(), + size: NumberNode({ integer: true, min: 0 }), + offset: NumberNode({ integer: true }) + })) + + schemas.register('generator_layer', Mod(ObjectNode({ + block: StringNode({ validator: 'resource', params: { pool: 'block' } }), + height: NumberNode({ integer: true, min: 1 }) + }), { + default: () => ({ + block: 'minecraft:stone', + height: 1 + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/ProcessorList.ts b/java/1.21.4/src/schemas/worldgen/ProcessorList.ts new file mode 100644 index 00000000..ed5da2d1 --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/ProcessorList.ts @@ -0,0 +1,193 @@ +import { + Case, + StringNode as RawStringNode, + ListNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, + Opt, + ChoiceNode, + INode, +} from '@mcschema/core' +import { IntProvider, Tag } from '../Common' + +export let Processors: INode + +export function initProcessorListSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + Processors = ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: '$worldgen/processor_list' }}), + change: v => undefined + }, + { + type: 'list', + node: ListNode( + Reference('processor') + ), + change: v => (typeof v === 'object' && v !== null && Array.isArray(v.processors)) + ? v.processors + : [{ processor_type: 'minecraft:nop' }] + }, + { + type: 'object', + node: Reference('processor_list'), + change: v => ({ + processors: Array.isArray(v) + ? v + : [{ processor_type: 'minecraft:nop' }] + }) + } + ]) + + schemas.register('processor_list', Mod(ObjectNode({ + processors: ListNode( + Reference('processor') + ) + }, { context: 'processor_list' }), { + default: () => ({ + processors: [{ + processor_type: 'minecraft:rule', + rules: [{ + location_predicate: { + predicate_type: 'minecraft:always_true' + }, + input_predicate: { + predicate_type: 'minecraft:always_true' + } + }] + }] + }) + })) + + schemas.register('processor', Mod(ObjectNode({ + processor_type: StringNode({ validator: 'resource', params: { pool: 'worldgen/structure_processor' } }), + [Switch]: [{ push: 'processor_type' }], + [Case]: { + 'minecraft:block_age': { + mossiness: NumberNode() + }, + 'minecraft:block_ignore': { + blocks: ListNode( + Reference('block_state') + ) + }, + 'minecraft:block_rot': { + integrity: NumberNode({ min: 0, max: 1 }), + rottable_blocks: Opt(Tag({ resource: 'block' })) + }, + 'minecraft:capped': { + limit: IntProvider({ min: 1 }), + delegate: Reference('processor') + }, + 'minecraft:gravity': { + heightmap: StringNode({ enum: 'heightmap_type' }), + offset: NumberNode({ integer: true }) + }, + 'minecraft:protected_blocks': { + value: StringNode({ validator: 'resource', params: { pool: 'block', requireTag: true } }) + }, + 'minecraft:rule': { + rules: ListNode( + Reference('processor_rule') + ) + } + } + }, { category: 'function', context: 'processor' }), { + default: () => ({ + processor_type: 'minecraft:rule', + rules: [{ + location_predicate: { + predicate_type: 'minecraft:always_true' + }, + input_predicate: { + predicate_type: 'minecraft:always_true' + } + }] + }) + })) + + schemas.register('processor_rule', Mod(ObjectNode({ + position_predicate: Opt(Reference('pos_rule_test')), + location_predicate: Reference('rule_test'), + input_predicate: Reference('rule_test'), + output_state: Reference('block_state'), + block_entity_modifier: Opt(Reference('rule_block_entity_modifier')) + }, { category: 'predicate', context: 'processor_rule' }), { + default: () => ({ + location_predicate: { + predicate_type: 'minecraft:always_true' + }, + input_predicate: { + predicate_type: 'minecraft:always_true' + } + }) + })) + + schemas.register('rule_block_entity_modifier', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'rule_block_entity_modifier' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:append_loot': { + loot_table: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }) + }, + 'minecraft:append_static': { + data: ObjectNode({}) // TODO: any nbt compound + } + } + }, { context: 'rule_block_entity_modifier' }), { + default: () => ({ + type: 'minecraft:passthrough', + }) + })) + + const posTestFields = { + min_dist: Opt(NumberNode({ min: 0, max: 255, integer: true })), + max_dist: Opt(NumberNode({ min: 0, max: 255, integer: true })), + min_chance: Opt(NumberNode({ min: 0, max: 1 })), + max_chance: Opt(NumberNode({ min: 0, max: 1 })) + } + + schemas.register('pos_rule_test', ObjectNode({ + predicate_type: StringNode({ validator: 'resource', params: { pool: 'pos_rule_test' } }), + [Switch]: [{ push: 'predicate_type' }], + [Case]: { + 'minecraft:axis_aligned_linear_pos': { + axis: StringNode({ enum: ['x', 'y', 'z'] }), + ...posTestFields + }, + 'minecraft:linear_pos': posTestFields + } + }, { context: 'pos_rule_test', disableSwitchContext: true })) + + schemas.register('rule_test', ObjectNode({ + predicate_type: StringNode({ validator: 'resource', params: { pool: 'rule_test' } }), + [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' }}) + } + } + }, { context: 'rule_test', disableSwitchContext: true })) +} diff --git a/java/1.21.4/src/schemas/worldgen/Structure.ts b/java/1.21.4/src/schemas/worldgen/Structure.ts new file mode 100644 index 00000000..819905da --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/Structure.ts @@ -0,0 +1,140 @@ +import { + BooleanNode, + Case, + StringNode as RawStringNode, + Reference as RawReference, + Mod, + NodeChildren, + NumberNode, + ObjectNode, + Switch, + SchemaRegistry, + CollectionRegistry, + Opt, + MapNode, + ListNode, + ChoiceNode, +} from '@mcschema/core' +import { Tag } from '../Common' +import { MobCategorySpawnSettings } from './Biome' + +export function initStructureSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + const Reference = RawReference.bind(undefined, schemas) + + schemas.register('structure', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/structure_type' } }), + biomes: Tag({ resource: '$worldgen/biome' }), + step: StringNode({ enum: 'decoration_step' }), + spawn_overrides: MapNode( + StringNode({ enum: 'mob_category' }), + ObjectNode({ + bounding_box: StringNode({ enum: ['piece', 'full'] }), + spawns: MobCategorySpawnSettings, + }) + ), + terrain_adaptation: Opt(StringNode({ enum: ['none', 'beard_thin', 'beard_box', 'bury', 'encapsulate'] })), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:jigsaw': { + start_pool: StringNode({ validator: 'resource', params: { pool: '$worldgen/template_pool'}}), + size: NumberNode({ integer: true, min: 0, max: 20}), + start_height: Reference('height_provider'), + start_jigsaw_name: Opt(StringNode()), + project_start_to_heightmap: Opt(StringNode({ enum: 'heightmap_type' })), + max_distance_from_center: Mod(NumberNode({ integer: true, min: 1, max: 128 }), { default: () => 80 }), + use_expansion_hack: BooleanNode(), + dimension_padding: Opt(Reference('dimension_padding')), + liquid_settings: StringNode({ enum: ['apply_waterlogging', 'ignore_waterlogging'] }), + pool_aliases: Opt(ListNode(Reference('pool_alias_binding'))) + }, + 'minecraft:mineshaft': { + mineshaft_type: StringNode({ enum: ['normal', 'mesa'] }), + }, + 'minecraft:nether_fossil': { + height: Reference('height_provider') + }, + 'minecraft:ocean_ruin': { + biome_temp: StringNode({ enum: ['cold', 'warm'] }), + large_probability: NumberNode({ min: 0, max: 1 }), + cluster_probability: NumberNode({ min: 0, max: 1 }) + }, + 'minecraft:ruined_portal': { + setups: ListNode( + ObjectNode({ + placement: StringNode({ enum: ['on_land_surface', 'partly_buried', 'on_ocean_floor', 'in_mountain', 'underground', 'in_nether'] }), + air_pocket_probability: NumberNode({ min: 0, max: 1 }), + mossiness: NumberNode({ min: 0, max: 1 }), + overgrown: BooleanNode(), + vines: BooleanNode(), + can_be_cold: BooleanNode(), + replace_with_blackstone: BooleanNode(), + weight: NumberNode({ min: 0 }) + }) + ) + }, + 'minecraft:shipwreck': { + is_beached: Opt(BooleanNode()) + } + } + }, { context: 'structure_feature' }), { + default: () => ({ + type: 'minecraft:jigsaw', + step: 'surface_structures', + size: 6, + max_distance_from_center: 80, + }) + })) + + schemas.register('dimension_padding', ChoiceNode([ + { + type: 'number', + node: NumberNode({ integer: true, min: 0 }), + change: (v) => typeof v === 'object' ? (v?.bottom ?? v?.top ?? 0) : 0, + }, + { + type: 'object', + node: ObjectNode({ + bottom: Opt(NumberNode({ integer: true, min: 0 })), + top: Opt(NumberNode({ integer: true, min: 0 })), + }), + change: (v) => typeof v === 'number' ? ({ bottom: v, top: v }) : ({}), + } + ], { context: 'dimension_padding' })) + + schemas.register('pool_alias_binding', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/pool_alias_binding' } }), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:direct': { + alias: StringNode({ validator: 'resource', params: { pool: '$worldgen/template_pool' } }), + target: StringNode({ validator: 'resource', params: { pool: '$worldgen/template_pool' } }) + }, + 'minecraft:random': { + alias: StringNode({ validator: 'resource', params: { pool: '$worldgen/template_pool' } }), + targets: ListNode( + Mod(ObjectNode({ + weight: NumberNode({ integer: true, min: 1 }), + data: StringNode({ validator: 'resource', params: { pool: '$worldgen/template_pool' } }) + }), { + default: () => ({ + data: {} + }) + }) + ) + }, + 'minecraft:random_group': { + groups: ListNode(ObjectNode({ + weight: NumberNode({ integer: true }), + data: Reference('pool_alias_binding'), + })) + } + } + }, { context: 'pool_alias_binding' }), { + default: () => ({ + type: 'minecraft:direct', + alias: 'minecraft:empty', + target: 'minecraft:empty' + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/StructureSet.ts b/java/1.21.4/src/schemas/worldgen/StructureSet.ts new file mode 100644 index 00000000..ce1a7e67 --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/StructureSet.ts @@ -0,0 +1,88 @@ +import { + StringNode as RawStringNode, + Mod, + ObjectNode, + Reference as RawReference, + SchemaRegistry, + CollectionRegistry, + Switch, + Case, + NumberNode, + INode, + ChoiceNode, + ListNode, + Opt, + ModelPath, + Errors, + NodeOptions, +} from '@mcschema/core' +import { Tag } from '../Common' + +export function initStructureSetSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + const Reference = RawReference.bind(undefined, schemas) + + const StructureSet = ChoiceNode([ + { + type: 'string', + node: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure_set' } }) + }, + { + type: 'object', + node: Reference('structure_set') + } + ], { choiceContext: 'structure_set' }) + + schemas.register('structure_set', ObjectNode({ + structures: ListNode( + ObjectNode({ + structure: StringNode({ validator: 'resource', params: { pool: '$worldgen/configured_structure_feature' } }), + weight: NumberNode({ integer: true, min: 1 }) + }) + ), + placement: Reference('structure_placement') + }, { context: 'structure_set' })) + + schemas.register('structure_placement', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/structure_placement' } }), + salt: NumberNode({ integer: true, min: 0 }), + frequency_reduction_method: Opt(StringNode({ enum: ['default', 'legacy_type_1', 'legacy_type_2', 'legacy_type_3'] })), + frequency: Opt(NumberNode({ min: 0, max: 1 })), + exclusion_zone: Opt(ObjectNode({ + other_set: StructureSet, + chunk_count: NumberNode({ integer: true, min: 1, max: 16 }) + })), + locate_offset: Opt(ListNode( + NumberNode({ integer: true, min: -16, max: 16 }), + { minLength: 3, maxLength: 3 } + )), + [Switch]: [{ push: 'type' }], + [Case]: { + 'minecraft:concentric_rings': { + distance: NumberNode({ integer: true, min: 0, max: 1023 }), + spread: NumberNode({ integer: true, min: 0, max: 1023 }), + count: NumberNode({ integer: true, min: 1, max: 4095 }), + preferred_biomes: Tag({ resource: '$worldgen/biome' }) + }, + 'minecraft:random_spread': { + spread_type: Opt(StringNode({ enum: ['linear', 'triangular'] })), + spacing: NumberNode({ integer: true, min: 0, max: 4096 }), + separation: Mod(NumberNode({ integer: true, min: 0, max: 4096 }), (node: INode) => ({ + validate: (path: ModelPath, value: any, errors: Errors, options: NodeOptions) => { + if (path.pop().push('spacing').get() <= value) { + errors.add(path, 'error.separation_smaller_spacing') + } + return node.validate(path, value, errors, options) + } + })) + } + } + }, { context: 'structure_placement' }), { + default: () => ({ + type: 'minecraft:random_spread', + spacing: 10, + separation: 5, + salt: Math.floor(Math.random() * 2147483647) + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/SurfaceRule.ts b/java/1.21.4/src/schemas/worldgen/SurfaceRule.ts new file mode 100644 index 00000000..acc8a4f0 --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/SurfaceRule.ts @@ -0,0 +1,90 @@ +import { + StringNode as RawStringNode, + Mod, + ObjectNode, + Reference as RawReference, + SchemaRegistry, + CollectionRegistry, + Switch, + Case, + ListNode, + NumberNode, + BooleanNode, +} from '@mcschema/core' + +export function initSurfaceRuleSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + const Reference = RawReference.bind(undefined, schemas) + + schemas.register('material_rule', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/material_rule' } }), + [Switch]: [ { push: 'type' } ], + [Case]: { + 'minecraft:block': { + result_state: Reference('block_state'), + }, + 'minecraft:condition': { + if_true: Reference('material_condition'), + then_run: Reference('material_rule') + }, + 'minecraft:sequence': { + sequence: ListNode( + Reference('material_rule') + ) + } + } + }, { context: 'material_rule', category: 'pool' }), { + default: () => ({ + type: 'minecraft:block', + result_state: { + Name: 'minecraft:stone' + } + }) + })) + + schemas.register('material_condition', Mod(ObjectNode({ + type: StringNode({ validator: 'resource', params: { pool: 'worldgen/material_condition' } }), + [Switch]: [ { push: 'type' } ], + [Case]: { + 'minecraft:biome': { + biome_is: ListNode( + StringNode({ validator: 'resource', params: { pool: '$worldgen/biome' } }) + ) + }, + 'minecraft:noise_threshold': { + noise: StringNode({ validator: 'resource', params: { pool: '$worldgen/noise' } }), + min_threshold: NumberNode(), + max_threshold: NumberNode() + }, + 'minecraft:not': { + invert: Reference('material_condition') + }, + 'minecraft:stone_depth': { + offset: NumberNode({ integer: true }), + surface_type: StringNode({ enum: 'cave_surface' }), + add_surface_depth: BooleanNode(), + secondary_depth_range: NumberNode({ integer: true }), + }, + 'minecraft:vertical_gradient': { + random_name: StringNode(), + true_at_and_below: Reference('vertical_anchor'), + false_at_and_above: Reference('vertical_anchor'), + }, + 'minecraft:water': { + offset: NumberNode({ integer: true }), + surface_depth_multiplier: NumberNode({ integer: true, min: -20, max: 20 }), + add_stone_depth: BooleanNode() + }, + 'minecraft:y_above': { + anchor: Reference('vertical_anchor'), + surface_depth_multiplier: NumberNode({ integer: true, min: -20, max: 20 }), + add_stone_depth: BooleanNode() + } + } + }, { context: 'material_condition' }), { + default: () => ({ + type: 'minecraft:biome', + is_biome: 'minecraft:plains' + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/TemplatePool.ts b/java/1.21.4/src/schemas/worldgen/TemplatePool.ts new file mode 100644 index 00000000..c69b46c9 --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/TemplatePool.ts @@ -0,0 +1,90 @@ +import { + Case, + ChoiceNode, + StringNode as RawStringNode, + ListNode, + Mod, + NumberNode, + ObjectNode, + Reference as RawReference, + Switch, + SchemaRegistry, + CollectionRegistry, +} from '@mcschema/core' +import './ProcessorList' +import { Processors } from './ProcessorList' + +export function initTemplatePoolSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const Reference = RawReference.bind(undefined, schemas) + const StringNode = RawStringNode.bind(undefined, collections) + + schemas.register('template_pool', Mod(ObjectNode({ + fallback: StringNode(), + elements: ListNode( + Reference('template_weighted_element') + ) + }, { context: 'template_pool' }), { + default: () => ({ + fallback: 'minecraft:empty', + elements: [ + { + weight: 1, + element: { + element_type: 'minecraft:single_pool_element', + projection: 'rigid', + processors: 'minecraft:empty' + } + } + ] + }) + })) + + schemas.register('template_weighted_element', Mod(ObjectNode({ + weight: NumberNode({ integer: true, min: 1, max: 150 }), + element: Reference('template_element') + }, { category: 'pool' }), { + default: () => ({ + weight: 1, + element: { + element_type: 'minecraft:single_pool_element', + projection: 'rigid', + processors: 'minecraft:empty' + } + }) + })) + + schemas.register('template_element', Mod(ObjectNode({ + element_type: StringNode({ validator: 'resource', params: { pool: 'worldgen/structure_pool_element' } }), + [Switch]: [{ push: 'element_type' }], + [Case]: { + 'minecraft:feature_pool_element': { + projection: StringNode({ enum: ['rigid', 'terrain_matching'] }), + feature: StringNode({ validator: 'resource', params: { pool: '$worldgen/placed_feature' } }) + }, + 'minecraft:legacy_single_pool_element': { + projection: StringNode({ enum: ['rigid', 'terrain_matching'] }), + location: StringNode({ validator: 'resource', params: { pool: '$structure' }}), + override_liquid_settings: StringNode({ enum: ['apply_waterlogging', 'ignore_waterlogging'] }), + processors: Processors + }, + 'minecraft:list_pool_element': { + projection: StringNode({ enum: ['rigid', 'terrain_matching'] }), + elements: ListNode( + Reference('template_element') + ) + }, + 'minecraft:single_pool_element': { + projection: StringNode({ enum: ['rigid', 'terrain_matching'] }), + location: StringNode({ validator: 'resource', params: { pool: '$structure' }}), + override_liquid_settings: StringNode({ enum: ['apply_waterlogging', 'ignore_waterlogging'] }), + processors: Processors + } + } + }, { context: 'template_element', disableSwitchContext: true }), { + default: () => ({ + element_type: 'minecraft:single_pool_element', + projection: 'rigid', + processors: 'minecraft:empty' + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/WorldPreset.ts b/java/1.21.4/src/schemas/worldgen/WorldPreset.ts new file mode 100644 index 00000000..975094ab --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/WorldPreset.ts @@ -0,0 +1,83 @@ +import { + StringNode as RawStringNode, + Reference as RawReference, + Mod, + ObjectNode, + SchemaRegistry, + CollectionRegistry, + MapNode, +} from '@mcschema/core' + +export function initWorldPresetSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + const StringNode = RawStringNode.bind(undefined, collections) + const Reference = RawReference.bind(undefined, schemas) + + schemas.register('world_preset', Mod(ObjectNode({ + dimensions: MapNode( + StringNode({ validator: 'resource', params: { pool: '$dimension', isDefinition: true } }), + Reference('dimension'), + ) + }, { context: 'world_preset' }), { + default: () => ({ + dimensions: { + 'minecraft:overworld': { + type: 'minecraft:overworld', + generator: { + type: 'minecraft:noise', + biome_source: { + type: 'minecraft:multi_noise', + preset: 'minecraft:overworld', + }, + settings: 'minecraft:overworld', + }, + }, + 'minecraft:the_nether': { + type: 'minecraft:the_nether', + generator: { + type: 'minecraft:noise', + biome_source: { + type: 'minecraft:multi_noise', + preset: 'minecraft:nether', + }, + settings: 'minecraft:nether', + }, + }, + 'minecraft:the_end': { + type: 'minecraft:the_end', + generator: { + type: 'minecraft:noise', + biome_source: { + type: 'minecraft:the_end', + }, + settings: 'minecraft:end', + }, + }, + } + }) + })) + + schemas.register('flat_level_generator_preset', Mod(ObjectNode({ + display: StringNode({ validator: 'resource', params: { pool: 'item' } }), + settings: Reference('flat_generator_settings'), + }), { + default: () => ({ + display: 'minecraft:grass_block', + settings: { + lakes: false, + features: false, + biome: 'minecraft:plains', + structure_overrides: [], + layers: [ + { + height: 3, + block: 'minecraft:dirt' + }, + { + height: 1, + block: 'minecraft:grass_block' + } + ] + } + }) + })) +} diff --git a/java/1.21.4/src/schemas/worldgen/index.ts b/java/1.21.4/src/schemas/worldgen/index.ts new file mode 100644 index 00000000..20f1fbd4 --- /dev/null +++ b/java/1.21.4/src/schemas/worldgen/index.ts @@ -0,0 +1,29 @@ +import { CollectionRegistry, SchemaRegistry } from '@mcschema/core' +import { initBiomeSchemas } from './Biome' +import { initCarverSchemas } from './Carver' +import { initDecoratorSchemas } from './Decorator' +import { initDensityFunctionSchemas } from './DensityFunction' +import { initFeatureSchemas } from './Feature' +import { initNoiseSettingsSchemas } from './NoiseSettings' +import { initProcessorListSchemas } from './ProcessorList' +import { initStructureSchemas } from './Structure' +import { initStructureSetSchemas } from './StructureSet' +import { initSurfaceRuleSchemas } from './SurfaceRule' +import { initTemplatePoolSchemas } from './TemplatePool' +import { initWorldPresetSchemas } from './WorldPreset' + +export function initWorldgenSchemas(schemas: SchemaRegistry, collections: CollectionRegistry) { + // `ProcessorList.ts`, `DensityFunction.ts`, and `Biome.ts` have exports. They should be initialized first. + initProcessorListSchemas(schemas, collections) + initDensityFunctionSchemas(schemas, collections) + initBiomeSchemas(schemas, collections) + initCarverSchemas(schemas, collections) + initDecoratorSchemas(schemas, collections) + initFeatureSchemas(schemas, collections) + initNoiseSettingsSchemas(schemas, collections) + initStructureSchemas(schemas, collections) + initStructureSetSchemas(schemas, collections) + initSurfaceRuleSchemas(schemas, collections) + initTemplatePoolSchemas(schemas, collections) + initWorldPresetSchemas(schemas, collections) +} diff --git a/java/1.21.4/tsconfig.json b/java/1.21.4/tsconfig.json new file mode 100644 index 00000000..39c4e77e --- /dev/null +++ b/java/1.21.4/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig-base", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules" + ] +}