diff --git a/src/main/kotlin/json/SchemaProviderFactory.kt b/src/main/kotlin/json/SchemaProviderFactory.kt
new file mode 100644
index 000000000..bb6ce8b15
--- /dev/null
+++ b/src/main/kotlin/json/SchemaProviderFactory.kt
@@ -0,0 +1,67 @@
+/*
+ * Minecraft Dev for IntelliJ
+ *
+ * https://minecraftdev.org
+ *
+ * Copyright (c) 2021 minecraft-dev
+ *
+ * MIT License
+ */
+
+package com.demonwav.mcdev.json
+
+import com.demonwav.mcdev.util.mcDomain
+import com.demonwav.mcdev.util.mcPath
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.VirtualFile
+import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider
+import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory
+import com.jetbrains.jsonSchema.extension.SchemaType
+
+class SchemaProviderFactory : JsonSchemaProviderFactory {
+ override fun getProviders(project: Project) =
+ listOf(
+ SoundsSchemaProvider(),
+ PathBasedSchemaProvider("Minecraft Blockstates JSON", "blockstates", "blockstates/"),
+ PathBasedSchemaProvider("Minecraft Item Model JSON", "model_item", "models/item/"),
+ PathBasedSchemaProvider("Minecraft Block Model JSON", "model_block", "models/block/"),
+ PathBasedSchemaProvider("Minecraft Loot Table JSON", "loot_table", "loot_tables/"),
+ PathBasedSchemaProvider("Minecraft Tag JSON", "tags", "tags/"),
+ PathBasedSchemaProvider("Minecraft Recipes JSON", "recipes", "recipes/"),
+ PathBasedSchemaProvider("Minecraft Particle JSON", "particles", "particles/"),
+ PathBasedSchemaProvider("Minecraft Advancement JSON", "advancement", "advancements/")
+ )
+}
+
+class SoundsSchemaProvider : JsonSchemaFileProvider {
+ companion object {
+ val FILE: VirtualFile = JsonSchemaProviderFactory.getResourceFile(
+ SchemaProviderFactory::class.java,
+ "/jsonSchemas/sounds.schema.json"
+ )
+ }
+
+ override fun getName() = "Minecraft Sounds JSON"
+
+ override fun isAvailable(file: VirtualFile) = file.mcDomain != null && file.mcPath == "sounds.json"
+
+ override fun getSchemaType(): SchemaType = SchemaType.embeddedSchema
+
+ override fun getSchemaFile(): VirtualFile = FILE
+}
+
+class PathBasedSchemaProvider(name: String, schema: String, private val path: String) : JsonSchemaFileProvider {
+ private val _name = name
+ private val file = JsonSchemaProviderFactory.getResourceFile(
+ SchemaProviderFactory::class.java,
+ "/jsonSchemas/$schema.schema.json"
+ )
+
+ override fun getName() = this._name
+
+ override fun isAvailable(file: VirtualFile) = file.mcDomain != null && file.mcPath?.startsWith(path) == true
+
+ override fun getSchemaType(): SchemaType = SchemaType.embeddedSchema
+
+ override fun getSchemaFile(): VirtualFile = file
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index f40bc74f8..de270d8eb 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -881,6 +881,10 @@
+
+
+
+
diff --git a/src/main/resources/jsonSchemas/advancement.schema.json b/src/main/resources/jsonSchemas/advancement.schema.json
new file mode 100644
index 000000000..bded6fea5
--- /dev/null
+++ b/src/main/resources/jsonSchemas/advancement.schema.json
@@ -0,0 +1,126 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Advancement JSON",
+ "type": "object",
+ "properties": {
+ "display": {
+ "type": "object",
+ "properties": {
+ "icon": {
+ "type": "object",
+ "properties": {
+ "item": { "type": "string" },
+ "nbt": { "type": "string" }
+ },
+ "required": [ "item" ]
+ },
+ "title": { "$ref": "common.json#/textComponent" },
+ "frame": {
+ "anyOf": [
+ { "type": "string" },
+ { "enum": [ "task", "goal", "challenge" ], "default": "task" }
+ ]
+ },
+ "background": { "type": "string" },
+ "description": { "$ref": "common.json#/textComponent" },
+ "show_toast": { "type": "boolean" },
+ "announce_to_chat": { "type": "boolean" },
+ "hidden": { "type": "boolean" }
+ },
+ "required": [ "title", "description", "icon" ]
+ },
+ "parent": { "type": "string" },
+ "criteria": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#/definitions/trigger" },
+ "minProperties": 1
+ },
+ "requirements": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": { "type": "string", "minLength": 1 }
+ }
+ },
+ "rewards": {
+ "type": "object",
+ "properties": {
+ "recipes": {
+ "type": "array",
+ "items": { "type": "string", "minLength": 1 }
+ },
+ "loot": {
+ "type": "array",
+ "items": { "type": "string", "minLength": 1 }
+ },
+ "experience": { "type": "integer" },
+ "function": { "type": "string" }
+ }
+ }
+ },
+ "require": [ "criteria" ],
+ "definitions": {
+ "trigger": {
+ "type": "object",
+ "properties": {
+ "trigger": {
+ "anyOf": [
+ { "type": "string" },
+ {
+ "type": "string",
+ "enum": [
+ "minecraft:impossible",
+ "minecraft:bee_nest_destroyed",
+ "minecraft:bred_animals",
+ "minecraft:brewed_potion",
+ "minecraft:changed_dimension",
+ "minecraft:channeled_lightning",
+ "minecraft:construct_beacon",
+ "minecraft:consume_item",
+ "minecraft:cured_zombie_villager",
+ "minecraft:effects_changed",
+ "minecraft:enchanted_item",
+ "minecraft:enter_block",
+ "minecraft:entity_hurt_player",
+ "minecraft:entity_killed_player",
+ "minecraft:fall_from_height",
+ "minecraft:filled_bucket",
+ "minecraft:fishing_rod_hooked",
+ "minecraft:hero_of_the_village",
+ "minecraft:inventory_changed",
+ "minecraft:item_durability_changed",
+ "minecraft:item_used_on_block",
+ "minecraft:killed_by_crossbow",
+ "minecraft:levitation",
+ "minecraft:lightning_strike",
+ "minecraft:location",
+ "minecraft:nether_travel",
+ "minecraft:placed_block",
+ "minecraft:player_generates_container_loot",
+ "minecraft:player_hurt_entity",
+ "minecraft:player_interacted_with_entity",
+ "minecraft:player_killed_entity",
+ "minecraft:recipe_unlocked",
+ "minecraft:shot_crossbow",
+ "minecraft:slept_in_bed",
+ "minecraft:slide_down_block",
+ "minecraft:started_riding",
+ "minecraft:summoned_entity",
+ "minecraft:tame_animal",
+ "minecraft:target_hit",
+ "minecraft:thrown_item_picked_up_by_entity",
+ "minecraft:tick",
+ "minecraft:used_ender_eye",
+ "minecraft:used_totem",
+ "minecraft:using_item",
+ "minecraft:villager_trade",
+ "minecraft:voluntary_exile"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [ "trigger" ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/blockstates.schema.json b/src/main/resources/jsonSchemas/blockstates.schema.json
new file mode 100644
index 000000000..aa6449a17
--- /dev/null
+++ b/src/main/resources/jsonSchemas/blockstates.schema.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Blockstates JSON",
+ "oneOf": [
+ {
+ "allOf": [
+ { "required": [ "forge_marker" ] },
+ { "$ref": "blockstates_forge.schema.json" }
+ ]
+ },
+ {
+ "allOf": [
+ { "not": { "required": [ "forge_marker" ] } },
+ { "$ref": "blockstates_vanilla.schema.json" }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/blockstates_common.schema.json b/src/main/resources/jsonSchemas/blockstates_common.schema.json
new file mode 100644
index 000000000..dd01fdee8
--- /dev/null
+++ b/src/main/resources/jsonSchemas/blockstates_common.schema.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Blockstates JSON",
+ "type": "object",
+ "properties": {
+ "variants": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "properties": {
+ "x": {
+ "type": "number"
+ },
+ "y": {
+ "type": "number"
+ },
+ "uvlock": {
+ "type": "boolean",
+ "default": false
+ }
+ }
+ }
+ ]
+ },
+ "uniqueItems": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/blockstates_forge.schema.json b/src/main/resources/jsonSchemas/blockstates_forge.schema.json
new file mode 100644
index 000000000..ec440b246
--- /dev/null
+++ b/src/main/resources/jsonSchemas/blockstates_forge.schema.json
@@ -0,0 +1,171 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Forge Blockstates JSON",
+ "type": "object",
+ "properties": {
+ "forge_marker": {
+ "type": "integer",
+ "enum": [ 1 ]
+ },
+ "variants": { "$ref": "#/definitions/variants" },
+ "defaults": { "$ref": "#/definitions/variantObject" }
+ },
+ "required": [ "forge_marker" ],
+ "definitions": {
+ "variants": {
+ "type": "object",
+ "patternProperties": {
+ "^([a-z0-9_]+=[^,]*,)*([a-z0-9_]+=[^,]*)$": {
+ "$ref": "#/definitions/variant"
+ },
+ "^[a-z0-9_]+$": {
+ "oneOf": [
+ {
+ "type": "object",
+ "additionalProperties": { "$ref": "#/definitions/variant" }
+ },
+ {
+ "type": "array",
+ "items": { "$ref": "#/definitions/variantObject" }
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "variant": {
+ "anyOf": [
+ { "$ref": "#/definitions/variantObject" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/definitions/variantObject" }
+ }
+ ]
+ },
+ "variantObject": {
+ "allOf": [
+ { "$ref": "blockstates_common.schema.json#/baseVariant" },
+ {
+ "properties": {
+ "transform": { "$ref": "#/definitions/rootTransform" },
+ "weight": { "type": "number" },
+ "submodel": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/definitions/variant"
+ }
+ }
+ ]
+ },
+ "custom": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ ]
+ },
+ "rootTransform": {
+ "oneOf": [
+ { "$ref": "#/definitions/transform" },
+ {
+ "type": "object",
+ "patternProperties": {
+ "(third|first)person_(left|right)hand": { "$ref": "#/definitions/transform" },
+ "gui|head|ground|fixed": { "$ref": "#/definitions/transform" }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "transform": {
+ "oneOf": [
+ {
+ "type": "string",
+ "enum": [ "identity", "forge:default-block", "forge:default-item", "forge:default-tool" ]
+ },
+ {
+ "type": "object",
+ "required": [ "matrix" ],
+ "properties": {
+ "matrix": { "$ref": "#/definitions/transformMatrix" }
+ },
+ "additionalProperties": false
+ },
+ { "$ref": "#/definitions/transformMatrix" },
+ {
+ "type": "object",
+ "properties": {
+ "translation": {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": { "type": "number" }
+ },
+ "scale": {
+ "oneOf": [
+ {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": { "type": "number" }
+ },
+ { "type": "number" }
+ ]
+ },
+ "rotation": { "$ref": "#/definitions/transformRotation" },
+ "post-rotation": { "$ref": "#/definitions/transformRotation" }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "transformMatrix": {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": {
+ "type": "array",
+ "minItems": 4,
+ "maxItems": 4,
+ "items": { "type": "number" }
+ }
+ },
+ "transformRotation": {
+ "oneOf": [
+ {
+ "type": "array",
+ "minItems": 4,
+ "maxItems": 4,
+ "items": { "type": "number" }
+ },
+ {
+ "type": "object",
+ "minProperties": 1,
+ "maxProperties": 1,
+ "properties": {
+ "x": { "type": "number" },
+ "y": { "type": "number" },
+ "z": { "type": "number" }
+ }
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "minProperties": 1,
+ "maxProperties": 1,
+ "properties": {
+ "x": { "type": "number" },
+ "y": { "type": "number" },
+ "z": { "type": "number" }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/blockstates_vanilla.schema.json b/src/main/resources/jsonSchemas/blockstates_vanilla.schema.json
new file mode 100644
index 000000000..f3ac0107d
--- /dev/null
+++ b/src/main/resources/jsonSchemas/blockstates_vanilla.schema.json
@@ -0,0 +1,94 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Vanilla Blockstates JSON",
+ "type": "object",
+ "properties": {
+ "variants": {
+ "type": "object",
+ "patternProperties": {
+ "^([a-z0-9_]+=[^,]*,)*([a-z0-9_]+=[^,]*)$": { "$ref": "#/definitions/variant" }
+ },
+ "additionalProperties": false
+ },
+ "multipart": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/multipartCase" }
+ }
+ },
+ "dependencies": {
+ "variants": {
+ "required": [ "variants" ],
+ "not": { "title": "Multipart and full variant definitions are mutually exclusive", "required": [ "multipart" ] }
+ },
+ "multipart": {
+ "required": [ "multipart" ],
+ "not": { "title": "Multipart and full variant definitions are mutually exclusive", "required": [ "variants" ] }
+ }
+ },
+ "definitions": {
+ "variant": {
+ "anyOf": [
+ {
+ "allOf": [
+ { "$ref": "blockstates_common.schema.json#/baseVariant" },
+ { "required": [ "model" ] }
+ ]
+ },
+ {
+ "type": "array",
+ "items": {
+ "allOf": [
+ { "$ref": "blockstates_common.schema.json#/baseVariant" },
+ { "required": [ "model" ] },
+ {
+ "properties": {
+ "weight": {
+ "type": "number"
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "multipartCase": {
+ "type": "object",
+ "properties": {
+ "apply": {
+ "$ref": "#/definitions/variant"
+ },
+ "when": {
+ "oneOf": [
+ {
+ "type": "object",
+ "patternProperties": {
+ "^[a-z0-9_]*$": { }
+ },
+ "additionalProperties": false,
+ "not": { "required": [ "OR" ] }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "OR": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^[a-z0-9_]*$": { }
+ },
+ "additionalProperties": false
+ }
+ }
+ },
+ "required": [ "OR" ],
+ "additionalProperties": false
+ }
+ ]
+ }
+ },
+ "required": [ "apply" ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/common.schema.json b/src/main/resources/jsonSchemas/common.schema.json
new file mode 100644
index 000000000..8d63d7144
--- /dev/null
+++ b/src/main/resources/jsonSchemas/common.schema.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "integerRange": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "min": { "type": "integer" },
+ "max": { "type": "integer" }
+ },
+ "required": [ "min", "max" ],
+ "additionalProperties": false
+ }
+ ]
+ },
+ "floatRange": {
+ "oneOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "min": { "type": "number" },
+ "max": { "type": "number" }
+ },
+ "required": [ "min", "max" ],
+ "additionalProperties": false
+ }
+ ]
+ },
+ "equipmentSlot": { "type": "string", "enum": [ "mainhand", "offhand", "feet", "legs", "chest", "head" ] },
+ "equipmentSlots": {
+ "oneOf": [
+ { "$ref": "#/equipmentSlot" },
+ { "type": "array", "items": { "$ref": "#/equipmentSlot" } }
+ ]
+ },
+ "textComponent": {
+ "anyOf": [
+ { "type": "string" },
+ { "type": "object" }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/loot_table.schema.json b/src/main/resources/jsonSchemas/loot_table.schema.json
new file mode 100644
index 000000000..c66b685e9
--- /dev/null
+++ b/src/main/resources/jsonSchemas/loot_table.schema.json
@@ -0,0 +1,245 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Loot Table JSON",
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "minecraft:empty",
+ "empty",
+ "minecraft:entity",
+ "entity",
+ "minecraft:block",
+ "block",
+ "minecraft:chest",
+ "chest",
+ "minecraft:fishing",
+ "fishing",
+ "minecraft:gift",
+ "gift",
+ "minecraft:advancement_reward",
+ "advancement_reward",
+ "minecraft:barter",
+ "barter",
+ "minecraft:command",
+ "command",
+ "minecraft:selector",
+ "selector",
+ "minecraft:advancement_entity",
+ "advancement_entity",
+ "minecraft:generic",
+ "generic"
+ ]
+ },
+ "pools": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "conditions": { "$ref": "#/definitions/conditions" },
+ "functions": { "$ref": "#/definitions/functions" },
+ "rolls": { "$ref": "common.schema.json#/integerRange" },
+ "bonus_rolls": { "$ref": "common.schema.json#/integerRange" },
+ "entries": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "conditions": { "$ref": "#/definitions/conditions" },
+ "functions": { "$ref": "#/definitions/functions" },
+ "type": {
+ "type": "string",
+ "enum": [ "item", "tag", "loot_table", "group", "alternatives", "sequence", "dynamic", "empty" ]
+ },
+ "expand": { "type": "boolean" },
+ "weight": { "type": "number" },
+ "quality": { "type": "number" }
+ },
+ "required": [ "type" ],
+ "anyOf": [
+ {
+ "allOf": [
+ { "properties": { "type": { "enum": [ "item" ] } } },
+ {
+ "properties": {
+ "name": { "type": "string" },
+ "functions": { "$ref": "#/definitions/functions" }
+ },
+ "required": [ "name" ]
+ }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "type": { "enum": [ "loot_table" ] } } },
+ {
+ "properties": {
+ "name": { "type": "string" }
+ },
+ "required": [ "name" ]
+ }
+ ]
+ },
+ { }
+ ]
+ }
+ }
+ },
+ "required": [ "entries" ]
+ }
+ }
+ },
+ "definitions": {
+ "conditions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "condition": {
+ "anyOf": [
+ { "type": "string" },
+ {
+ "type": "string",
+ "enum": [
+ "entity_properties",
+ "entity_scores",
+ "killed_by_player",
+ "random_chance",
+ "random_chance_with_looting"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [ "condition" ],
+ "anyOf": [
+ {
+ "allOf": [
+ { "properties": { "condition": { "enum": [ "entity_properties" ] } } },
+ { "$ref": "loot_table_conditions.schema.json#/entityProperties" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "condition": { "enum": [ "entity_scores" ] } } },
+ { "$ref": "loot_table_conditions.schema.json#/entityScores" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "condition": { "enum": [ "killed_by_player" ] } } },
+ { "$ref": "loot_table_conditions.schema.json#/killedByPlayer" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "condition": { "enum": [ "random_chance" ] } } },
+ { "$ref": "loot_table_conditions.schema.json#/randomChance" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "condition": { "enum": [ "random_chance_with_looting" ] } } },
+ { "$ref": "loot_table_conditions.schema.json#/randomChanceWithLooting" }
+ ]
+ },
+ { }
+ ]
+ }
+ },
+ "functions": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/function" }
+ },
+ "function": {
+ "type": "object",
+ "properties": {
+ "function": {
+ "anyOf": [
+ { "type": "string" },
+ {
+ "type": "string",
+ "enum": [
+ "enchant_randomly",
+ "enchant_with_levels",
+ "exploration_map",
+ "furnace_smelt",
+ "looting_enchant",
+ "set_attributes",
+ "set_count",
+ "set_damage",
+ "set_data",
+ "set_nbt"
+ ]
+ }
+ ]
+ },
+ "conditions": { "$ref": "#/definitions/conditions" }
+ },
+ "required": [ "function" ],
+ "anyOf": [
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "enchant_randomly" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/enchantRandomly" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "enchant_with_levels" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/enchantWithLevels" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "exploration_map" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/explorationMap" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "furnace_smelt" ] } } }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "looting_enchant" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/lootingEnchant" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "set_attributes" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/setAttributes" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "set_count" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/setCount" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "set_damage" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/setDamage" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "set_data" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/setData" }
+ ]
+ },
+ {
+ "allOf": [
+ { "properties": { "function": { "enum": [ "set_nbt" ] } } },
+ { "$ref": "loot_table_functions.schema.json#/setNbt" }
+ ]
+ },
+ { }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/loot_table_conditions.schema.json b/src/main/resources/jsonSchemas/loot_table_conditions.schema.json
new file mode 100644
index 000000000..506431f86
--- /dev/null
+++ b/src/main/resources/jsonSchemas/loot_table_conditions.schema.json
@@ -0,0 +1,105 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "block_state_property": {
+ "properties": {
+ "block": {
+ "type": "string"
+ }
+ }
+ },
+ "entityProperties": {
+ "properties": {
+ "entity": {
+ "type": "string",
+ "enum": [ "this", "killer", "killer_player" ]
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "on_fire": { "type": "boolean" }
+ }
+ }
+ }
+ },
+ "entityScores": {
+ "properties": {
+ "entity": {
+ "type": "string",
+ "enum": [ "this", "killer", "killer_player" ]
+ },
+ "scores": {
+ "type": "object",
+ "additionalProperties": { "$ref": "common.schema.json#/integerRange" }
+ }
+ }
+ },
+ "locationCheck": {
+ "properties": {
+ "offsetX": { "type": "number" },
+ "offsetY": { "type": "number" },
+ "offsetZ": { "type": "number" }
+ }
+ },
+ "inverted": {
+ "properties": {
+ "term": {
+ "type": "object"
+ }
+ }
+ },
+ "killedByPlayer": {
+ "properties": {
+ "on_fire": { "type": "inverse" }
+ }
+ },
+ "randomChance": {
+ "properties": {
+ "chance": { "type": "number", "minimum": 0, "maximum": 1 }
+ }
+ },
+ "randomChanceWithLooting": {
+ "properties": {
+ "chance": { "type": "number", "minimum": 0, "maximum": 1 },
+ "looting_multiplier": { "type": "number" }
+ }
+ },
+ "reference": {
+ "properties": {
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "survivesExplosion": {},
+ "value_check": {
+ "properties": {
+ "value": {
+ "type": "integer"
+ },
+ "range": {
+ "type": "object",
+ "properties": {
+ "min": {
+ "type": "integer"
+ },
+ "max": {
+ "type": "integer"
+ }
+ }
+ },
+ "range": {
+ "type": "integer"
+ }
+ }
+ },
+ "weather_check": {
+ "properties": {
+ "raining": {
+ "type": "boolean"
+ },
+ "thundering": {
+ "type": "boolean"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/loot_table_functions.schema.json b/src/main/resources/jsonSchemas/loot_table_functions.schema.json
new file mode 100644
index 000000000..a73269232
--- /dev/null
+++ b/src/main/resources/jsonSchemas/loot_table_functions.schema.json
@@ -0,0 +1,76 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "enchantRandomly": {
+ "properties": {
+ "enchantments": {
+ "type": "array",
+ "items": { "type": "string" }
+ }
+ }
+ },
+ "enchantWithLevels": {
+ "properties": {
+ "treasure": { "type": "boolean" },
+ "levels": { "$ref": "common.schema.json#/integerRange" }
+ }
+ },
+ "explorationMap": {
+ "properties": {
+ "destination": { "type": "string" },
+ "decoration": { "type": "string" },
+ "zoom": { "type": "integer" },
+ "search_radius": { "type": "integer" },
+ "skip_existing_chunks": { "type": "boolean" }
+ }
+ },
+ "lootingEnchant": {
+ "properties": {
+ "limit": { "type": "integer", "minimum": 0 },
+ "count": { "$ref": "common.schema.json#/integerRange" }
+ }
+ },
+ "setAttributes": {
+ "properties": {
+ "modifiers": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "attribute": { "type": "string" },
+ "operation": { "type": "string", "enum": [ "addition", "multiply_base", "multiply_total" ] },
+ "amount": { "$ref": "common.schema.json#/floatRange" },
+ "id": { "type": "string" },
+ "slot": { "$ref": "common.schema.json#/equipmentSlots" }
+ },
+ "additionalProperties": false,
+ "required": [ "name", "attribute", "operation", "amount" ]
+ }
+ }
+ }
+ },
+ "setCount": {
+ "properties": {
+ "count": { "$ref": "common.schema.json#/integerRange" }
+ },
+ "required": [ "count" ]
+ },
+ "setDamage": {
+ "properties": {
+ "damage": { "$ref": "common.schema.json#/floatRange" }
+ },
+ "required": [ "damage" ]
+ },
+ "setData": {
+ "properties": {
+ "data": { "$ref": "common.schema.json#/integerRange" }
+ },
+ "required": [ "data" ]
+ },
+ "setNbt": {
+ "properties": {
+ "tag": { "type": "string" }
+ },
+ "required": [ "tag" ]
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/model_block.schema.json b/src/main/resources/jsonSchemas/model_block.schema.json
new file mode 100644
index 000000000..d4f5990c0
--- /dev/null
+++ b/src/main/resources/jsonSchemas/model_block.schema.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Block Model JSON",
+ "type": "object",
+ "properties": {
+ "parent": { "type": "string" },
+ "textures": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "ambientocclusion": { "type": "boolean" },
+ "elements": { "$ref": "model_common.schema.json#/elements" },
+ "display": { "$ref": "model_common.schema.json#/transforms" }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/model_common.schema.json b/src/main/resources/jsonSchemas/model_common.schema.json
new file mode 100644
index 000000000..932e9f51a
--- /dev/null
+++ b/src/main/resources/jsonSchemas/model_common.schema.json
@@ -0,0 +1,112 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "elements": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "from": { "$ref": "#/elementCoord" },
+ "to": { "$ref": "#/elementCoord" },
+ "rotation": {
+ "type": "object",
+ "properties": {
+ "origin": { "$ref": "#/elementCoord" },
+ "axis": {
+ "type": "string",
+ "enum": [ "x", "y", "z" ]
+ },
+ "angle": {
+ "type": "number",
+ "multipleOf": 22.5,
+ "minimum": -45,
+ "maximum": 45
+ }
+ },
+ "additionalProperties": false,
+ "required": [ "axis" ]
+ },
+ "shade": {
+ "type": "boolean",
+ "default": true
+ },
+ "faces": {
+ "type": "object",
+ "patternProperties": {
+ "down|up|north|south|west|east": {
+ "type": "object",
+ "properties": {
+ "uv": {
+ "type": "array",
+ "minItems": 4,
+ "maxItems": 4,
+ "items": {
+ "type": "number"
+ }
+ },
+ "texture": {
+ "type": "string",
+ "pattern": "^#.*?"
+ },
+ "cullface": {
+ "type": "string",
+ "enum": [ "down", "up", "north", "south", "west", "east" ]
+ },
+ "rotation": {
+ "type": "integer",
+ "enum": [ 0, 90, 180, 270 ]
+ },
+ "tintindex": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "required": [ "from", "to" ]
+ }
+ },
+ "elementCoord": {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": {
+ "type": "number",
+ "minimum": -16,
+ "maximum": 32
+ }
+ },
+ "transforms": {
+ "type": "object",
+ "patternProperties": {
+ "(third|first)person_(left|right)hand": { "$ref": "#/transform" },
+ "gui|head|ground|fixed": { "$ref": "#/transform" }
+ },
+ "additionalProperties": false
+ },
+ "transform": {
+ "type": "object",
+ "properties": {
+ "rotation": {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": { "type": "number" }
+ },
+ "translation": {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": { "type": "number", "minimum": -80, "maximum": 80 }
+ },
+ "scale": {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": { "type": "number", "minimum": -4, "maximum": 4 }
+ }
+ },
+ "additionalProperties": false
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/model_item.schema.json b/src/main/resources/jsonSchemas/model_item.schema.json
new file mode 100644
index 000000000..078ae8a57
--- /dev/null
+++ b/src/main/resources/jsonSchemas/model_item.schema.json
@@ -0,0 +1,35 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Item Model JSON",
+ "type": "object",
+ "properties": {
+ "parent": { "type": "string" },
+ "textures": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "gui_light": {
+ "enum": ["front", "side"],
+ "default": "side"
+ },
+ "elements": { "$ref": "model_common.schema.json#/elements" },
+ "display": { "$ref": "model_common.schema.json#/transforms" },
+ "overrides": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "predicate": {
+ "type": "object",
+ "additionalProperties": { "type": "number" }
+ },
+ "model": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "required": [ "predicate", "model" ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/particles.schema.json b/src/main/resources/jsonSchemas/particles.schema.json
new file mode 100644
index 000000000..34a7f2ca9
--- /dev/null
+++ b/src/main/resources/jsonSchemas/particles.schema.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Particle JSON",
+ "type": "object",
+ "properties": {
+ "textures": {
+ "type": "array",
+ "values": {
+ "type": "array",
+ "items": { "type": "string", "minLength": 1 }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/recipes.schema.json b/src/main/resources/jsonSchemas/recipes.schema.json
new file mode 100644
index 000000000..191c39aea
--- /dev/null
+++ b/src/main/resources/jsonSchemas/recipes.schema.json
@@ -0,0 +1,295 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Minecraft Recipes JSON",
+ "type": "object",
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "minecraft:blasting"
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "ingredient": {
+ "$ref": "#/definitions/item"
+ },
+ "result": {
+ "type": "string"
+ },
+ "experience": {
+ "type": "number"
+ },
+ "cookingtime": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "minecraft:campfire_cooking"
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "ingredient": {
+ "$ref": "#/definitions/item"
+ },
+ "result": {
+ "type": "string"
+ },
+ "experience": {
+ "type": "number"
+ },
+ "cookingtime": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "minecraft:crafting_shaped"
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "pattern": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "maxLength": 3
+ },
+ "maxItems": 3
+ },
+ "key": {
+ "additionalProperties": {
+ "type": ["object", "array"],
+ "properties": {
+ "item": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ },
+ "items": {
+ "properties": {
+ "item": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "result": {
+ "$ref": "#/definitions/result"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "minecraft:crafting_shapeless"
+ }
+ }
+ },
+ "then": {
+ "$ref": "#/definitions/tagsCommonToAllRecipes",
+ "properties": {
+ "ingredients": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "$ref": "#/definitions/ingredient"
+ },
+ {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 9,
+ "items": {
+ "$ref": "#/definitions/ingredient"
+ }
+ }
+ ]
+ }
+ },
+ "result": {
+ "$ref": "#/definitions/result"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "minecraft:smelting"
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "ingredient": {
+ "$ref": "#/definitions/item"
+ },
+ "result": {
+ "type": "string"
+ },
+ "experience": {
+ "type": "number"
+ },
+ "cookingtime": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "minecraft:smithing"
+ }
+ }
+ },
+ "then": {
+ "$ref": "#/definitions/tagsCommonToAllRecipes",
+ "properties": {
+ "base": {
+ "$ref": "#/definitions/item"
+ },
+ "addition": {
+ "$ref": "#/definitions/item"
+ },
+ "result": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "minecraft:smoking"
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "ingredient": {
+ "$ref": "#/definitions/item"
+ },
+ "result": {
+ "type": "string"
+ },
+ "experience": {
+ "type": "number"
+ },
+ "cookingtime": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "minecraft:stonecutting"
+ }
+ }
+ },
+ "then": {
+ "$ref": "#/definitions/tagsCommonToAllRecipes",
+ "properties": {
+ "ingredient": {
+ "$ref": "#/definitions/item"
+ },
+ "result": {
+ "type": "string"
+ },
+ "count": {
+ "type": "integer",
+ "default": 1
+ }
+ }
+ }
+ }
+ ],
+ "definitions": {
+ "item": {
+ "type": "object",
+ "properties": {
+ "item": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ },
+ "ingredient": {
+ "type": "object",
+ "$ref": "#/definitions/item"
+ },
+ "tagsCommonToAllRecipes": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "group": {
+ "type": "string"
+ }
+ }
+ },
+ "result": {
+ "type": "object",
+ "properties": {
+ "count": {
+ "type": "integer",
+ "default": 1
+ },
+ "item": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "minecraft:blasting",
+ "minecraft:campfire_cooking",
+ "minecraft:crafting_shaped",
+ "minecraft:crafting_shapeless",
+ "minecraft:smelting",
+ "minecraft:smithing",
+ "minecraft:smoking",
+ "minecraft:stonecutting"
+ ]
+ },
+ "group": {
+ "type": "string"
+ }
+ }
+}
diff --git a/src/main/resources/jsonSchemas/sounds.schema.json b/src/main/resources/jsonSchemas/sounds.schema.json
new file mode 100644
index 000000000..ab66d9050
--- /dev/null
+++ b/src/main/resources/jsonSchemas/sounds.schema.json
@@ -0,0 +1,61 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Minecraft Sounds JSON",
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "category": { "type": "string" },
+ "replace": {
+ "type": "boolean",
+ "default": false
+ },
+ "subtitle": { "type": "string" },
+ "sounds": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "volume": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 1,
+ "default": 1
+ },
+ "pitch": {
+ "type": "number",
+ "default": 1
+ },
+ "weight": {
+ "type": "number",
+ "default": 1
+ },
+ "stream": {
+ "type": "boolean",
+ "default": false
+ },
+ "attenuation_distance": {
+ "type": "number",
+ "default": 16
+ },
+ "preload": {
+ "type": "boolean",
+ "default": false
+ },
+ "type": {
+ "enum": ["sound", "event"],
+ "default": "sound"
+ }
+ }
+ }
+ ]
+ },
+ "uniqueItems": true
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/jsonSchemas/tags.schema.json b/src/main/resources/jsonSchemas/tags.schema.json
new file mode 100644
index 000000000..2e0a26c39
--- /dev/null
+++ b/src/main/resources/jsonSchemas/tags.schema.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Minecraft Tag JSON",
+ "type": "object",
+ "properties": {
+ "replace": {
+ "type": "boolean",
+ "default": false
+ },
+ "values": {
+ "type": "array",
+ "values": {
+ "type": "array",
+ "items": { "type": "string", "minLength": 1 }
+ }
+ }
+ },
+ "required": ["values"]
+}
\ No newline at end of file