mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-23 07:10:41 +00:00
🔥 Nuke all mcschema related code
This commit is contained in:
263
package-lock.json
generated
263
package-lock.json
generated
@@ -10,22 +10,6 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@giscus/react": "^2.2.3",
|
"@giscus/react": "^2.2.3",
|
||||||
"@mcschema/core": "^0.13.0",
|
|
||||||
"@mcschema/java-1.15": "^0.2.13",
|
|
||||||
"@mcschema/java-1.16": "^0.6.20",
|
|
||||||
"@mcschema/java-1.17": "^0.2.40",
|
|
||||||
"@mcschema/java-1.18": "^0.3.16",
|
|
||||||
"@mcschema/java-1.18.2": "^0.1.26",
|
|
||||||
"@mcschema/java-1.19": "^0.1.54",
|
|
||||||
"@mcschema/java-1.19.3": "^0.0.17",
|
|
||||||
"@mcschema/java-1.19.4": "^0.1.21",
|
|
||||||
"@mcschema/java-1.20": "^0.0.24",
|
|
||||||
"@mcschema/java-1.20.2": "^0.0.15",
|
|
||||||
"@mcschema/java-1.20.3": "^0.0.16",
|
|
||||||
"@mcschema/java-1.20.5": "^0.0.42",
|
|
||||||
"@mcschema/java-1.21": "^0.0.27",
|
|
||||||
"@mcschema/java-1.21.2": "^0.0.13",
|
|
||||||
"@mcschema/locales": "^0.1.104",
|
|
||||||
"@zip.js/zip.js": "^2.4.5",
|
"@zip.js/zip.js": "^2.4.5",
|
||||||
"brace": "^0.11.1",
|
"brace": "^0.11.1",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
@@ -68,9 +52,6 @@
|
|||||||
"vite-plugin-static-copy": "^0.12.0"
|
"vite-plugin-static-copy": "^0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@mcschema/java-1.18@0.3.0-beta": {
|
|
||||||
"extraneous": true
|
|
||||||
},
|
|
||||||
"node_modules/@alloc/quick-lru": {
|
"node_modules/@alloc/quick-lru": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||||
@@ -629,128 +610,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.2.tgz",
|
||||||
"integrity": "sha512-VMOxsWh/QDwrxPsgkSQnuZ+8mfNy1OTjzzUdLBvvZtpahwPTHTeVZ51RZRqO4xfKVrR+btIPA8D01IL3xeG66w=="
|
"integrity": "sha512-VMOxsWh/QDwrxPsgkSQnuZ+8mfNy1OTjzzUdLBvvZtpahwPTHTeVZ51RZRqO4xfKVrR+btIPA8D01IL3xeG66w=="
|
||||||
},
|
},
|
||||||
"node_modules/@mcschema/core": {
|
|
||||||
"version": "0.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.13.0.tgz",
|
|
||||||
"integrity": "sha512-nJRDvdEI2Z7Yw7eWKcbkuLFKtPIyeCc1d04E7EeUOpwxfHffgL5a0VBD2PX692z3igfTHGNXgPkzrleV91Q/ww=="
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.15": {
|
|
||||||
"version": "0.2.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.15/-/java-1.15-0.2.13.tgz",
|
|
||||||
"integrity": "sha512-f3NanEsd48svxZfqquSylXrHZDd5vjvi8D6r15blrCi2J2C42032Ir0dMPD38hLxu9ey0eIrgucDApn/GXO1uA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.16": {
|
|
||||||
"version": "0.6.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.16/-/java-1.16-0.6.20.tgz",
|
|
||||||
"integrity": "sha512-pJihtCYPloVAk6Tq2JVvfk666rPLaNmXkHhD6/a1kk/AUaP2wk/3YuYdXyRqz7/lmxkBpJK/BvhAO4ZtsQfNtg==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.17": {
|
|
||||||
"version": "0.2.40",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.17/-/java-1.17-0.2.40.tgz",
|
|
||||||
"integrity": "sha512-aFlH9APa4JEMsz0y3BKjYGILXn6QQcQDOCHA+qgdXDjc0xsrHffO1l/TVVDi5GQCCqEgGA15guqfLVV+7Vtb2A==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.18": {
|
|
||||||
"version": "0.3.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.18/-/java-1.18-0.3.16.tgz",
|
|
||||||
"integrity": "sha512-crxXl0OW6BYlD+Cjaa8cJEsoehnBa6whrE6JBG37nZ1nQBl0e9vun4fbaZi0buzFjeJGZ0st7Lm0LsFJRe7FEw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.18.2": {
|
|
||||||
"version": "0.1.26",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.18.2/-/java-1.18.2-0.1.26.tgz",
|
|
||||||
"integrity": "sha512-GN6gCyEyTTJTiv0vn4cTVMYVgG2BJpDXJh2KPrg5cT/7gCpHUrt4PJAyzEaYLkDZoW17p2gWuY12gIM/yfuiDA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.19": {
|
|
||||||
"version": "0.1.54",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.19/-/java-1.19-0.1.54.tgz",
|
|
||||||
"integrity": "sha512-eh8kTBs3EAGlWzTbmGzoIRtePd2dUkttooGsmXmjadvwf/yT19Ew+P+U4Y6LLFLnNoWJxruCVhznS1KxzoT0wA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.19.3": {
|
|
||||||
"version": "0.0.17",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.19.3/-/java-1.19.3-0.0.17.tgz",
|
|
||||||
"integrity": "sha512-8V3UpdmVPb2JZJ5A3Mi6J5Lf5dicmY9w11dq8VQm9jSEPwY7IXSnE4l9J3AmFkIgAhiaNTQ+/lbmliR7LNUAlQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.19.4": {
|
|
||||||
"version": "0.1.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.19.4/-/java-1.19.4-0.1.21.tgz",
|
|
||||||
"integrity": "sha512-QZhe7L6t7/Uf6vK0pXWSJYoGA0D+VAv10wrMrwG6jTHoCI1iPkFhet9JqUnOwOv0Ql+jwh7zDNQjTHJIkHwfMQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.20": {
|
|
||||||
"version": "0.0.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.20/-/java-1.20-0.0.24.tgz",
|
|
||||||
"integrity": "sha512-Bcm4n7YTXQ/6mGmj7l9KcRXl8ta9nokNZDyrgEeexsrE9jzj1ef6TsNra2bI9kCka9l4s4XzZ1VDuM/Ag/Ud0g==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.20.2": {
|
|
||||||
"version": "0.0.15",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.20.2/-/java-1.20.2-0.0.15.tgz",
|
|
||||||
"integrity": "sha512-crGOWodFnWT5XGjp2b8kZeXKsHpdQHDaES09NYf/VNWUIdGNYD7hXlIVvQEsqbZt1ABuisIKWvNYZJwH7Fhdew==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.20.3": {
|
|
||||||
"version": "0.0.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.20.3/-/java-1.20.3-0.0.16.tgz",
|
|
||||||
"integrity": "sha512-XNX02G7RHB8u/ibwU0GSo+lsz/5rcduHnWKk/BJcGH6Q2NswLKJDiyt8Ow5KrcmtCBxprCLjg5pJaj/Ql6aKoQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.20.5": {
|
|
||||||
"version": "0.0.42",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.20.5/-/java-1.20.5-0.0.42.tgz",
|
|
||||||
"integrity": "sha512-LgVeCvHQPMQUMPPiJ5tkm8RG7EKowvw6eHxu8RPakeX/Del5OYkQI252hkP2RC3AA46NHE+iKqtsk8A+lvCuow==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.21": {
|
|
||||||
"version": "0.0.27",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.21/-/java-1.21-0.0.27.tgz",
|
|
||||||
"integrity": "sha512-X2o8VJouEv5ZixLrpngyq0srwYB8Bp0lmIBKRJvJ+7/5RB6Mrf9I/Z4y7BkPjpKjw8TME1r5Tw/g7sjrYXvEUQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/java-1.21.2": {
|
|
||||||
"version": "0.0.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.21.2/-/java-1.21.2-0.0.13.tgz",
|
|
||||||
"integrity": "sha512-qGDOczhRd/KY+75oXxoKwZSuJS8k038mkiSqCbe+TFapXCZSv87TOnXCUD+LFK0v51ejwFM6mErv8ZJinuKq9g==",
|
|
||||||
"dependencies": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mcschema/locales": {
|
|
||||||
"version": "0.1.104",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/locales/-/locales-0.1.104.tgz",
|
|
||||||
"integrity": "sha512-fZ9zzb4OvMnxRFtVYpPuFDugd2LGYMfGRsYzl+RG6wFZAUuB6pe+igNmK5o8VfX1ldO2W5FWkddgh/MjtemFOA=="
|
|
||||||
},
|
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -4775,128 +4634,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.2.tgz",
|
||||||
"integrity": "sha512-VMOxsWh/QDwrxPsgkSQnuZ+8mfNy1OTjzzUdLBvvZtpahwPTHTeVZ51RZRqO4xfKVrR+btIPA8D01IL3xeG66w=="
|
"integrity": "sha512-VMOxsWh/QDwrxPsgkSQnuZ+8mfNy1OTjzzUdLBvvZtpahwPTHTeVZ51RZRqO4xfKVrR+btIPA8D01IL3xeG66w=="
|
||||||
},
|
},
|
||||||
"@mcschema/core": {
|
|
||||||
"version": "0.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.13.0.tgz",
|
|
||||||
"integrity": "sha512-nJRDvdEI2Z7Yw7eWKcbkuLFKtPIyeCc1d04E7EeUOpwxfHffgL5a0VBD2PX692z3igfTHGNXgPkzrleV91Q/ww=="
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.15": {
|
|
||||||
"version": "0.2.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.15/-/java-1.15-0.2.13.tgz",
|
|
||||||
"integrity": "sha512-f3NanEsd48svxZfqquSylXrHZDd5vjvi8D6r15blrCi2J2C42032Ir0dMPD38hLxu9ey0eIrgucDApn/GXO1uA==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.16": {
|
|
||||||
"version": "0.6.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.16/-/java-1.16-0.6.20.tgz",
|
|
||||||
"integrity": "sha512-pJihtCYPloVAk6Tq2JVvfk666rPLaNmXkHhD6/a1kk/AUaP2wk/3YuYdXyRqz7/lmxkBpJK/BvhAO4ZtsQfNtg==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.17": {
|
|
||||||
"version": "0.2.40",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.17/-/java-1.17-0.2.40.tgz",
|
|
||||||
"integrity": "sha512-aFlH9APa4JEMsz0y3BKjYGILXn6QQcQDOCHA+qgdXDjc0xsrHffO1l/TVVDi5GQCCqEgGA15guqfLVV+7Vtb2A==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.18": {
|
|
||||||
"version": "0.3.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.18/-/java-1.18-0.3.16.tgz",
|
|
||||||
"integrity": "sha512-crxXl0OW6BYlD+Cjaa8cJEsoehnBa6whrE6JBG37nZ1nQBl0e9vun4fbaZi0buzFjeJGZ0st7Lm0LsFJRe7FEw==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.18.2": {
|
|
||||||
"version": "0.1.26",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.18.2/-/java-1.18.2-0.1.26.tgz",
|
|
||||||
"integrity": "sha512-GN6gCyEyTTJTiv0vn4cTVMYVgG2BJpDXJh2KPrg5cT/7gCpHUrt4PJAyzEaYLkDZoW17p2gWuY12gIM/yfuiDA==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.19": {
|
|
||||||
"version": "0.1.54",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.19/-/java-1.19-0.1.54.tgz",
|
|
||||||
"integrity": "sha512-eh8kTBs3EAGlWzTbmGzoIRtePd2dUkttooGsmXmjadvwf/yT19Ew+P+U4Y6LLFLnNoWJxruCVhznS1KxzoT0wA==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.19.3": {
|
|
||||||
"version": "0.0.17",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.19.3/-/java-1.19.3-0.0.17.tgz",
|
|
||||||
"integrity": "sha512-8V3UpdmVPb2JZJ5A3Mi6J5Lf5dicmY9w11dq8VQm9jSEPwY7IXSnE4l9J3AmFkIgAhiaNTQ+/lbmliR7LNUAlQ==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.19.4": {
|
|
||||||
"version": "0.1.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.19.4/-/java-1.19.4-0.1.21.tgz",
|
|
||||||
"integrity": "sha512-QZhe7L6t7/Uf6vK0pXWSJYoGA0D+VAv10wrMrwG6jTHoCI1iPkFhet9JqUnOwOv0Ql+jwh7zDNQjTHJIkHwfMQ==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.20": {
|
|
||||||
"version": "0.0.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.20/-/java-1.20-0.0.24.tgz",
|
|
||||||
"integrity": "sha512-Bcm4n7YTXQ/6mGmj7l9KcRXl8ta9nokNZDyrgEeexsrE9jzj1ef6TsNra2bI9kCka9l4s4XzZ1VDuM/Ag/Ud0g==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.20.2": {
|
|
||||||
"version": "0.0.15",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.20.2/-/java-1.20.2-0.0.15.tgz",
|
|
||||||
"integrity": "sha512-crGOWodFnWT5XGjp2b8kZeXKsHpdQHDaES09NYf/VNWUIdGNYD7hXlIVvQEsqbZt1ABuisIKWvNYZJwH7Fhdew==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.20.3": {
|
|
||||||
"version": "0.0.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.20.3/-/java-1.20.3-0.0.16.tgz",
|
|
||||||
"integrity": "sha512-XNX02G7RHB8u/ibwU0GSo+lsz/5rcduHnWKk/BJcGH6Q2NswLKJDiyt8Ow5KrcmtCBxprCLjg5pJaj/Ql6aKoQ==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.20.5": {
|
|
||||||
"version": "0.0.42",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.20.5/-/java-1.20.5-0.0.42.tgz",
|
|
||||||
"integrity": "sha512-LgVeCvHQPMQUMPPiJ5tkm8RG7EKowvw6eHxu8RPakeX/Del5OYkQI252hkP2RC3AA46NHE+iKqtsk8A+lvCuow==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.21": {
|
|
||||||
"version": "0.0.27",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.21/-/java-1.21-0.0.27.tgz",
|
|
||||||
"integrity": "sha512-X2o8VJouEv5ZixLrpngyq0srwYB8Bp0lmIBKRJvJ+7/5RB6Mrf9I/Z4y7BkPjpKjw8TME1r5Tw/g7sjrYXvEUQ==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/java-1.21.2": {
|
|
||||||
"version": "0.0.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/java-1.21.2/-/java-1.21.2-0.0.13.tgz",
|
|
||||||
"integrity": "sha512-qGDOczhRd/KY+75oXxoKwZSuJS8k038mkiSqCbe+TFapXCZSv87TOnXCUD+LFK0v51ejwFM6mErv8ZJinuKq9g==",
|
|
||||||
"requires": {
|
|
||||||
"@mcschema/core": "^0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mcschema/locales": {
|
|
||||||
"version": "0.1.104",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mcschema/locales/-/locales-0.1.104.tgz",
|
|
||||||
"integrity": "sha512-fZ9zzb4OvMnxRFtVYpPuFDugd2LGYMfGRsYzl+RG6wFZAUuB6pe+igNmK5o8VfX1ldO2W5FWkddgh/MjtemFOA=="
|
|
||||||
},
|
|
||||||
"@nodelib/fs.scandir": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -16,22 +16,6 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@giscus/react": "^2.2.3",
|
"@giscus/react": "^2.2.3",
|
||||||
"@mcschema/core": "^0.13.0",
|
|
||||||
"@mcschema/java-1.15": "^0.2.13",
|
|
||||||
"@mcschema/java-1.16": "^0.6.20",
|
|
||||||
"@mcschema/java-1.17": "^0.2.40",
|
|
||||||
"@mcschema/java-1.18": "^0.3.16",
|
|
||||||
"@mcschema/java-1.18.2": "^0.1.26",
|
|
||||||
"@mcschema/java-1.19": "^0.1.54",
|
|
||||||
"@mcschema/java-1.19.3": "^0.0.17",
|
|
||||||
"@mcschema/java-1.19.4": "^0.1.21",
|
|
||||||
"@mcschema/java-1.20": "^0.0.24",
|
|
||||||
"@mcschema/java-1.20.2": "^0.0.15",
|
|
||||||
"@mcschema/java-1.20.3": "^0.0.16",
|
|
||||||
"@mcschema/java-1.20.5": "^0.0.42",
|
|
||||||
"@mcschema/java-1.21": "^0.0.27",
|
|
||||||
"@mcschema/java-1.21.2": "^0.0.13",
|
|
||||||
"@mcschema/locales": "^0.1.104",
|
|
||||||
"@zip.js/zip.js": "^2.4.5",
|
"@zip.js/zip.js": "^2.4.5",
|
||||||
"brace": "^0.11.1",
|
"brace": "^0.11.1",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import type { DataModel } from '@mcschema/core'
|
|
||||||
import { Path } from '@mcschema/core'
|
|
||||||
import * as zip from '@zip.js/zip.js'
|
import * as zip from '@zip.js/zip.js'
|
||||||
import type { Identifier, NbtTag, Random } from 'deepslate'
|
import type { Identifier, NbtTag, Random } from 'deepslate'
|
||||||
import { Matrix3, Matrix4, NbtByte, NbtCompound, NbtDouble, NbtInt, NbtList, NbtString, Vector } from 'deepslate'
|
import { Matrix3, Matrix4, NbtByte, NbtCompound, NbtDouble, NbtInt, NbtList, NbtString, Vector } from 'deepslate'
|
||||||
@@ -47,21 +45,6 @@ export function generateColor() {
|
|||||||
return Math.floor(Math.random() * 16777215)
|
return Math.floor(Math.random() * 16777215)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newSeed(model: DataModel) {
|
|
||||||
const seed = Math.floor(Math.random() * (4294967296)) - 2147483648
|
|
||||||
const dimensions = model.get(new Path(['dimensions']))
|
|
||||||
model.set(new Path(['seed']), seed, true)
|
|
||||||
if (isObject(dimensions)) {
|
|
||||||
Object.keys(dimensions).forEach(id => {
|
|
||||||
model.set(new Path(['dimensions', id, 'generator', 'seed']), seed, true)
|
|
||||||
model.set(new Path(['dimensions', id, 'generator', 'biome_source', 'seed']), seed, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
model.set(new Path(['placement', 'salt']), Math.abs(seed), true)
|
|
||||||
model.set(new Path(['generator', 'seed']), seed, true)
|
|
||||||
model.set(new Path(['generator', 'biome_source', 'seed']), seed)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function htmlEncode(str: string) {
|
export function htmlEncode(str: string) {
|
||||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||||
.replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/')
|
.replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/')
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import { Identifier } from 'deepslate/core'
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks'
|
||||||
import { useVersion } from '../contexts/Version.jsx'
|
import { useVersion } from '../contexts/Version.jsx'
|
||||||
import { useAsync } from '../hooks/useAsync.js'
|
import { useAsync } from '../hooks/useAsync.js'
|
||||||
import { fetchItemComponents } from '../services/index.js'
|
import { fetchItemComponents, fetchRegistries } from '../services/index.js'
|
||||||
import { ResolvedItem } from '../services/ResolvedItem.js'
|
import { ResolvedItem } from '../services/ResolvedItem.js'
|
||||||
import { renderItem } from '../services/Resources.js'
|
import { renderItem } from '../services/Resources.js'
|
||||||
import { getCollections } from '../services/Schemas.js'
|
|
||||||
import { jsonToNbt } from '../Utils.js'
|
import { jsonToNbt } from '../Utils.js'
|
||||||
import { ItemTooltip } from './ItemTooltip.jsx'
|
import { ItemTooltip } from './ItemTooltip.jsx'
|
||||||
import { Octicon } from './Octicon.jsx'
|
import { Octicon } from './Octicon.jsx'
|
||||||
@@ -83,14 +82,17 @@ function ItemItself({ item }: ResolvedProps) {
|
|||||||
return Octicon.package
|
return Octicon.package
|
||||||
}
|
}
|
||||||
|
|
||||||
const { value: collections } = useAsync(() => getCollections(version), [])
|
const { value: allModels, loading: loadingModels } = useAsync(async () => {
|
||||||
|
const registries = await fetchRegistries(version)
|
||||||
|
return registries.get('model')
|
||||||
|
}, [version])
|
||||||
|
|
||||||
if (collections === undefined) {
|
if (loadingModels || allModels === undefined) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelPath = `item/${item.id.path}`
|
const modelPath = `item/${item.id.path}`
|
||||||
if (collections.get('model').includes('minecraft:' + modelPath)) {
|
if (allModels && allModels.includes('minecraft:' + modelPath)) {
|
||||||
return <RenderedItem item={item} />
|
return <RenderedItem item={item} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { Identifier } from 'deepslate-1.20.4/core'
|
|||||||
import { useEffect, useRef, useState } from 'preact/hooks'
|
import { useEffect, useRef, useState } from 'preact/hooks'
|
||||||
import { useVersion } from '../contexts/Version.jsx'
|
import { useVersion } from '../contexts/Version.jsx'
|
||||||
import { useAsync } from '../hooks/useAsync.js'
|
import { useAsync } from '../hooks/useAsync.js'
|
||||||
|
import { fetchRegistries } from '../services/index.js'
|
||||||
import { renderItem } from '../services/Resources1204.js'
|
import { renderItem } from '../services/Resources1204.js'
|
||||||
import { getCollections } from '../services/Schemas.js'
|
|
||||||
import { ItemTooltip1204 } from './ItemTooltip1204.jsx'
|
import { ItemTooltip1204 } from './ItemTooltip1204.jsx'
|
||||||
import { Octicon } from './Octicon.jsx'
|
import { Octicon } from './Octicon.jsx'
|
||||||
import { itemHasGlint } from './previews/LootTable1204.js'
|
import { itemHasGlint } from './previews/LootTable1204.js'
|
||||||
@@ -69,14 +69,17 @@ function ItemItself({ item }: Props) {
|
|||||||
return Octicon.package
|
return Octicon.package
|
||||||
}
|
}
|
||||||
|
|
||||||
const { value: collections } = useAsync(() => getCollections(version), [])
|
const { value: allModels, loading: loadingModels } = useAsync(async () => {
|
||||||
|
const registries = await fetchRegistries(version)
|
||||||
|
return registries.get('model')
|
||||||
|
}, [version])
|
||||||
|
|
||||||
if (collections === undefined) {
|
if (loadingModels || allModels === undefined) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelPath = `item/${item.id.path}`
|
const modelPath = `item/${item.id.path}`
|
||||||
if (collections.get('model').includes('minecraft:' + modelPath)) {
|
if (allModels && allModels.includes('minecraft:' + modelPath)) {
|
||||||
return <RenderedItem item={item} hasGlint={hasGlint} />
|
return <RenderedItem item={item} hasGlint={hasGlint} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { NodeChildren } from '@mcschema/core'
|
|
||||||
import { NumberInput, RangeInput } from '../index.js'
|
import { NumberInput, RangeInput } from '../index.js'
|
||||||
import { CustomizedInput } from './CustomizedInput.jsx'
|
import { CustomizedInput } from './CustomizedInput.jsx'
|
||||||
|
|
||||||
@@ -12,7 +11,6 @@ interface Props {
|
|||||||
initial?: number,
|
initial?: number,
|
||||||
error?: string,
|
error?: string,
|
||||||
onChange: (value: number) => void,
|
onChange: (value: number) => void,
|
||||||
children?: NodeChildren,
|
|
||||||
}
|
}
|
||||||
export function CustomizedSlider(props: Props) {
|
export function CustomizedSlider(props: Props) {
|
||||||
const isInteger = (props.step ?? 1) >= 1
|
const isInteger = (props.step ?? 1) >= 1
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { useState } from 'preact/hooks'
|
import { useState } from 'preact/hooks'
|
||||||
import { Analytics } from '../../Analytics.js'
|
import { Analytics } from '../../Analytics.js'
|
||||||
import { useLocale, useProject } from '../../contexts/index.js'
|
import { useLocale, useProject } from '../../contexts/index.js'
|
||||||
|
import type { FileModel } from '../../services/index.js'
|
||||||
import { Btn } from '../Btn.js'
|
import { Btn } from '../Btn.js'
|
||||||
import { TextInput } from '../forms/index.js'
|
import { TextInput } from '../forms/index.js'
|
||||||
import { Modal } from '../Modal.js'
|
import { Modal } from '../Modal.js'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
model: DataModel,
|
model: FileModel,
|
||||||
id: string,
|
id: string,
|
||||||
method: string,
|
method: string,
|
||||||
onClose: () => void,
|
onClose: () => void,
|
||||||
@@ -29,7 +29,7 @@ export function FileCreation({ model, id, method, onClose }: Props) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
Analytics.saveProjectFile(id, projects.length, project.files.length, method as any)
|
Analytics.saveProjectFile(id, projects.length, project.files.length, method as any)
|
||||||
updateFile(id, undefined, { type: id, id: fileId, data: DataModel.unwrapLists(model.data) })
|
updateFile(id, undefined, { type: id, id: fileId, data: model.data })
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +1,59 @@
|
|||||||
import type { DataModel } from '@mcschema/core'
|
import { useVersion } from '../../contexts/Version.jsx'
|
||||||
import { Path } from '@mcschema/core'
|
import type { FileModel } from '../../services/index.js'
|
||||||
import { useState } from 'preact/hooks'
|
|
||||||
import { useModel } from '../../hooks/index.js'
|
|
||||||
import type { VersionId } from '../../services/index.js'
|
|
||||||
import { checkVersion } from '../../services/index.js'
|
import { checkVersion } from '../../services/index.js'
|
||||||
import { BiomeSourcePreview, BlockStatePreview, DecoratorPreview, DensityFunctionPreview, LootTablePreview, ModelPreview, NoisePreview, NoiseSettingsPreview, RecipePreview, StructureSetPreview } from '../previews/index.js'
|
import { BiomeSourcePreview, BlockStatePreview, DecoratorPreview, DensityFunctionPreview, LootTablePreview, ModelPreview, NoisePreview, NoiseSettingsPreview, RecipePreview, StructureSetPreview } from '../previews/index.js'
|
||||||
|
|
||||||
export const HasPreview = ['loot_table', 'recipe', 'dimension', 'worldgen/density_function', 'worldgen/noise', 'worldgen/noise_settings', 'worldgen/configured_feature', 'worldgen/placed_feature', 'worldgen/structure_set', 'block_definition', 'model']
|
export const HasPreview = ['loot_table', 'recipe', 'dimension', 'worldgen/density_function', 'worldgen/noise', 'worldgen/noise_settings', 'worldgen/configured_feature', 'worldgen/placed_feature', 'worldgen/structure_set', 'block_definition', 'model']
|
||||||
|
|
||||||
type PreviewPanelProps = {
|
type PreviewPanelProps = {
|
||||||
model: DataModel | undefined,
|
model: FileModel | undefined,
|
||||||
version: VersionId,
|
|
||||||
id: string,
|
id: string,
|
||||||
shown: boolean,
|
shown: boolean,
|
||||||
onError: (message: string) => unknown,
|
onError: (message: string) => unknown,
|
||||||
}
|
}
|
||||||
export function PreviewPanel({ model, version, id, shown }: PreviewPanelProps) {
|
export function PreviewPanel({ model, id, shown }: PreviewPanelProps) {
|
||||||
const [, setCount] = useState(0)
|
const { version } = useVersion()
|
||||||
|
|
||||||
useModel(model, () => {
|
|
||||||
setCount(count => count + 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!model) return <></>
|
if (!model) return <></>
|
||||||
const data = model.get(new Path([]))
|
|
||||||
if (!data) return <></>
|
|
||||||
|
|
||||||
if (id === 'loot_table') {
|
if (id === 'loot_table') {
|
||||||
return <LootTablePreview {...{ model, version, shown, data }} />
|
return <LootTablePreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'recipe') {
|
if (id === 'recipe') {
|
||||||
return <RecipePreview {...{ model, version, shown, data }} />
|
return <RecipePreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'dimension' && model.get(new Path(['generator', 'type']))?.endsWith('noise')) {
|
if (id === 'dimension' && model.data.generator?.type?.endsWith('noise')) {
|
||||||
return <BiomeSourcePreview {...{ model, version, shown, data }} />
|
return <BiomeSourcePreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'worldgen/density_function') {
|
if (id === 'worldgen/density_function') {
|
||||||
return <DensityFunctionPreview {...{ model, version, shown, data }} />
|
return <DensityFunctionPreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'worldgen/noise') {
|
if (id === 'worldgen/noise') {
|
||||||
return <NoisePreview {...{ model, version, shown, data }} />
|
return <NoisePreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'worldgen/noise_settings' && checkVersion(version, '1.18')) {
|
if (id === 'worldgen/noise_settings' && checkVersion(version, '1.18')) {
|
||||||
return <NoiseSettingsPreview {...{ model, version, shown, data }} />
|
return <NoiseSettingsPreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((id === 'worldgen/placed_feature' || (id === 'worldgen/configured_feature' && checkVersion(version, '1.16', '1.17')))) {
|
if ((id === 'worldgen/placed_feature' || (id === 'worldgen/configured_feature' && checkVersion(version, '1.16', '1.17')))) {
|
||||||
return <DecoratorPreview {...{ model, version, shown, data }} />
|
return <DecoratorPreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'worldgen/structure_set' && checkVersion(version, '1.19')) {
|
if (id === 'worldgen/structure_set' && checkVersion(version, '1.19')) {
|
||||||
return <StructureSetPreview {...{ model, version, shown, data }} />
|
return <StructureSetPreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'block_definition') {
|
if (id === 'block_definition') {
|
||||||
return <BlockStatePreview {...{ model, version, shown, data }} />
|
return <BlockStatePreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'model') {
|
if (id === 'model') {
|
||||||
return <ModelPreview {...{ model, version, shown, data }} />
|
return <ModelPreview {...{ model, shown }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <></>
|
return <></>
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import type { DataModel } from '@mcschema/core'
|
|
||||||
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
||||||
import { Analytics } from '../../Analytics.js'
|
import { Analytics } from '../../Analytics.js'
|
||||||
import config from '../../Config.js'
|
import config from '../../Config.js'
|
||||||
import { disectFilePath, DRAFT_PROJECT, getFilePath, useLocale, useProject, useVersion } from '../../contexts/index.js'
|
import { disectFilePath, DRAFT_PROJECT, getFilePath, useLocale, useProject, useVersion } from '../../contexts/index.js'
|
||||||
import { useFocus } from '../../hooks/useFocus.js'
|
import { useFocus } from '../../hooks/useFocus.js'
|
||||||
import type { VersionId } from '../../services/index.js'
|
|
||||||
import { stringifySource } from '../../services/index.js'
|
import { stringifySource } from '../../services/index.js'
|
||||||
import { Store } from '../../Store.js'
|
import { Store } from '../../Store.js'
|
||||||
import { writeZip } from '../../Utils.js'
|
import { writeZip } from '../../Utils.js'
|
||||||
@@ -15,9 +13,6 @@ import type { TreeViewGroupRenderer, TreeViewLeafRenderer } from '../TreeView.js
|
|||||||
import { TreeView } from '../TreeView.js'
|
import { TreeView } from '../TreeView.js'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
model: DataModel | undefined,
|
|
||||||
version: VersionId,
|
|
||||||
id: string,
|
|
||||||
onError: (message: string) => unknown,
|
onError: (message: string) => unknown,
|
||||||
onRename: (file: { type: string, id: string }) => unknown,
|
onRename: (file: { type: string, id: string }) => unknown,
|
||||||
onCreate: () => unknown,
|
onCreate: () => unknown,
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { DataModel, Path } from '@mcschema/core'
|
|
||||||
import { route } from 'preact-router'
|
import { route } from 'preact-router'
|
||||||
import { useCallback, useEffect, useErrorBoundary, useMemo, useRef, useState } from 'preact/hooks'
|
import { useCallback, useEffect, useErrorBoundary, useMemo, useState } from 'preact/hooks'
|
||||||
import { Analytics } from '../../Analytics.js'
|
import { Analytics } from '../../Analytics.js'
|
||||||
import type { ConfigGenerator } from '../../Config.js'
|
import type { ConfigGenerator } from '../../Config.js'
|
||||||
import config from '../../Config.js'
|
import config from '../../Config.js'
|
||||||
import { DRAFT_PROJECT, useLocale, useProject, useVersion } from '../../contexts/index.js'
|
import { DRAFT_PROJECT, useLocale, useProject, useVersion } from '../../contexts/index.js'
|
||||||
import { AsyncCancel, useActiveTimeout, useAsync, useModel, useSearchParam } from '../../hooks/index.js'
|
import { AsyncCancel, useActiveTimeout, useAsync, useSearchParam } from '../../hooks/index.js'
|
||||||
import { getOutput } from '../../schema/transformOutput.js'
|
import type { FileModel, VersionId } from '../../services/index.js'
|
||||||
import type { VersionId } from '../../services/index.js'
|
import { checkVersion, createMockFileModel, fetchPreset, fetchRegistries, getSnippet, shareSnippet } from '../../services/index.js'
|
||||||
import { checkVersion, fetchPreset, getBlockStates, getCollections, getModel, getSnippet, shareSnippet } from '../../services/index.js'
|
|
||||||
import { Store } from '../../Store.js'
|
import { Store } from '../../Store.js'
|
||||||
import { cleanUrl, deepEqual, genPath } from '../../Utils.js'
|
import { cleanUrl, deepEqual, genPath } from '../../Utils.js'
|
||||||
import { Ad, Btn, BtnMenu, ErrorPanel, FileCreation, FileRenaming, Footer, HasPreview, Octicon, PreviewPanel, ProjectCreation, ProjectDeletion, ProjectPanel, SearchList, SourcePanel, TextInput, Tree, VersionSwitcher } from '../index.js'
|
import { Ad, Btn, BtnMenu, ErrorPanel, FileCreation, FileRenaming, Footer, HasPreview, Octicon, PreviewPanel, ProjectCreation, ProjectDeletion, ProjectPanel, SearchList, SourcePanel, TextInput, Tree, VersionSwitcher } from '../index.js'
|
||||||
@@ -22,7 +20,7 @@ interface Props {
|
|||||||
export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
const { version, changeVersion, changeTargetVersion } = useVersion()
|
const { version, changeVersion, changeTargetVersion } = useVersion()
|
||||||
const { projects, project, file, updateProject, updateFile, closeFile } = useProject()
|
const { projects, project, file, updateProject, closeFile } = useProject()
|
||||||
const [error, setError] = useState<Error | string | null>(null)
|
const [error, setError] = useState<Error | string | null>(null)
|
||||||
const [errorBoundary, errorRetry] = useErrorBoundary()
|
const [errorBoundary, errorRetry] = useErrorBoundary()
|
||||||
if (errorBoundary) {
|
if (errorBoundary) {
|
||||||
@@ -34,16 +32,15 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
|||||||
|
|
||||||
const [currentPreset, setCurrentPreset] = useSearchParam('preset')
|
const [currentPreset, setCurrentPreset] = useSearchParam('preset')
|
||||||
const [sharedSnippetId, setSharedSnippetId] = useSearchParam(SHARE_KEY)
|
const [sharedSnippetId, setSharedSnippetId] = useSearchParam(SHARE_KEY)
|
||||||
const ignoreChange = useRef(false)
|
|
||||||
const backup = useMemo(() => Store.getBackup(gen.id), [gen.id])
|
const backup = useMemo(() => Store.getBackup(gen.id), [gen.id])
|
||||||
|
|
||||||
const loadBackup = () => {
|
const loadBackup = () => {
|
||||||
if (backup !== undefined) {
|
if (backup !== undefined) {
|
||||||
model?.reset(DataModel.wrapLists(backup), false)
|
// TODO: implement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { value } = useAsync(async () => {
|
const {} = useAsync(async () => {
|
||||||
let data: unknown = undefined
|
let data: unknown = undefined
|
||||||
if (currentPreset && sharedSnippetId) {
|
if (currentPreset && sharedSnippetId) {
|
||||||
setSharedSnippetId(undefined)
|
setSharedSnippetId(undefined)
|
||||||
@@ -81,81 +78,65 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
|||||||
}
|
}
|
||||||
data = file.data
|
data = file.data
|
||||||
}
|
}
|
||||||
const [model, blockStates] = await Promise.all([
|
|
||||||
getModel(version, gen.id),
|
|
||||||
getBlockStates(version),
|
|
||||||
])
|
|
||||||
if (data) {
|
if (data) {
|
||||||
ignoreChange.current = true
|
// TODO: set file contents to data
|
||||||
model.reset(DataModel.wrapLists(data), false)
|
|
||||||
}
|
}
|
||||||
Analytics.setGenerator(gen.id)
|
Analytics.setGenerator(gen.id)
|
||||||
return { model, blockStates }
|
return {}
|
||||||
}, [gen.id, version, sharedSnippetId, currentPreset, project.name, file?.id])
|
}, [gen.id, version, sharedSnippetId, currentPreset, project.name, file?.id])
|
||||||
|
|
||||||
const model = value?.model
|
const model: FileModel = createMockFileModel()
|
||||||
const blockStates = value?.blockStates
|
|
||||||
|
|
||||||
useModel(model, model => {
|
// TODO: when contents of file change:
|
||||||
if (!ignoreChange.current) {
|
// - remove preset and share id from url
|
||||||
setCurrentPreset(undefined, true)
|
// - update project
|
||||||
setSharedSnippetId(undefined, true)
|
// - store backup
|
||||||
}
|
|
||||||
if (file && model && blockStates) {
|
|
||||||
const data = getOutput(model, blockStates)
|
|
||||||
updateFile(gen.id, file.id, { id: file.id, data })
|
|
||||||
}
|
|
||||||
ignoreChange.current = false
|
|
||||||
Store.setBackup(gen.id, DataModel.unwrapLists(model.data))
|
|
||||||
setError(null)
|
|
||||||
}, [gen.id, setCurrentPreset, setSharedSnippetId, blockStates, file?.id])
|
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
Analytics.resetGenerator(gen.id, model?.historyIndex ?? 1, 'menu')
|
Analytics.resetGenerator(gen.id, 1, 'menu')
|
||||||
model?.reset(DataModel.wrapLists(model.schema.default()), true)
|
// TODO
|
||||||
}
|
}
|
||||||
const undo = (e: MouseEvent) => {
|
const undo = (e: MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
Analytics.undoGenerator(gen.id, model?.historyIndex ?? 1, 'menu')
|
Analytics.undoGenerator(gen.id, 1, 'menu')
|
||||||
model?.undo()
|
// TODO
|
||||||
}
|
}
|
||||||
const redo = (e: MouseEvent) => {
|
const redo = (e: MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
Analytics.redoGenerator(gen.id, model?.historyIndex ?? 1, 'menu')
|
Analytics.redoGenerator(gen.id, 1, 'menu')
|
||||||
model?.redo()
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
const onKeyUp = (e: KeyboardEvent) => {
|
|
||||||
if (e.ctrlKey && e.key === 'z') {
|
|
||||||
Analytics.undoGenerator(gen.id, model?.historyIndex ?? 1, 'hotkey')
|
|
||||||
model?.undo()
|
|
||||||
} else if (e.ctrlKey && e.key === 'y') {
|
|
||||||
Analytics.redoGenerator(gen.id, model?.historyIndex ?? 1, 'hotkey')
|
|
||||||
model?.redo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const onKeyDown = (e: KeyboardEvent) => {
|
|
||||||
if (e.ctrlKey && e.key === 's') {
|
|
||||||
setFileSaving('hotkey')
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const onKeyUp = (e: KeyboardEvent) => {
|
||||||
|
if (e.ctrlKey && e.key === 'z') {
|
||||||
|
Analytics.undoGenerator(gen.id, 1, 'hotkey')
|
||||||
|
// TODO
|
||||||
|
} else if (e.ctrlKey && e.key === 'y') {
|
||||||
|
Analytics.redoGenerator(gen.id, 1, 'hotkey')
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.ctrlKey && e.key === 's') {
|
||||||
|
setFileSaving('hotkey')
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('keyup', onKeyUp)
|
document.addEventListener('keyup', onKeyUp)
|
||||||
document.addEventListener('keydown', onKeyDown)
|
document.addEventListener('keydown', onKeyDown)
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('keyup', onKeyUp)
|
document.removeEventListener('keyup', onKeyUp)
|
||||||
document.removeEventListener('keydown', onKeyDown)
|
document.removeEventListener('keydown', onKeyDown)
|
||||||
}
|
}
|
||||||
}, [model, blockStates, file])
|
}, [gen.id])
|
||||||
|
|
||||||
const [presets, setPresets] = useState<string[]>([])
|
const { value: presets } = useAsync(async () => {
|
||||||
useEffect(() => {
|
const registries = await fetchRegistries(version)
|
||||||
getCollections(version).then(collections => {
|
const entries = registries.get(gen.id) ?? []
|
||||||
setPresets(collections.get(gen.id).map(p => p.startsWith('minecraft:') ? p.slice(10) : p))
|
return entries.map(e => e.startsWith('minecraft:') ? e.slice(10) : e)
|
||||||
})
|
|
||||||
.catch(e => { console.error(e); setError(e) })
|
|
||||||
}, [version, gen.id])
|
}, [version, gen.id])
|
||||||
|
|
||||||
const selectPreset = (id: string) => {
|
const selectPreset = (id: string) => {
|
||||||
@@ -168,13 +149,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
|||||||
const loadPreset = async (id: string) => {
|
const loadPreset = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
const preset = await fetchPreset(version, genPath(gen, version), id)
|
const preset = await fetchPreset(version, genPath(gen, version), id)
|
||||||
const seed = model?.get(new Path(['generator', 'seed']))
|
// TODO: sync random seed
|
||||||
if (preset?.generator?.seed !== undefined && seed !== undefined) {
|
|
||||||
preset.generator.seed = seed
|
|
||||||
if (preset.generator.biome_source?.seed !== undefined) {
|
|
||||||
preset.generator.biome_source.seed = seed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return preset
|
return preset
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setError(`Cannot load preset ${id} in ${version}`)
|
setError(`Cannot load preset ${id} in ${version}`)
|
||||||
@@ -203,14 +178,14 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
|||||||
setShareUrl(`${location.origin}/${gen.url}/?version=${version}&preset=${currentPreset}`)
|
setShareUrl(`${location.origin}/${gen.url}/?version=${version}&preset=${currentPreset}`)
|
||||||
setShareShown(true)
|
setShareShown(true)
|
||||||
copySharedId()
|
copySharedId()
|
||||||
} else if (model && blockStates) {
|
} else {
|
||||||
const output = getOutput(model, blockStates)
|
// TODO: get contents from file, and compare to default of type
|
||||||
if (deepEqual(output, model.schema.default())) {
|
if (deepEqual(model.data, {})) {
|
||||||
setShareUrl(`${location.origin}/${gen.url}/?version=${version}`)
|
setShareUrl(`${location.origin}/${gen.url}/?version=${version}`)
|
||||||
setShareShown(true)
|
setShareShown(true)
|
||||||
} else {
|
} else {
|
||||||
setShareLoading(true)
|
setShareLoading(true)
|
||||||
shareSnippet(gen.id, version, output, previewShown)
|
shareSnippet(gen.id, version, model.data, previewShown)
|
||||||
.then(({ id, length, compressed, rate }) => {
|
.then(({ id, length, compressed, rate }) => {
|
||||||
Analytics.createSnippet(gen.id, id, version, length, compressed, rate)
|
Analytics.createSnippet(gen.id, id, version, length, compressed, rate)
|
||||||
const url = `${location.origin}/${gen.url}/?${SHARE_KEY}=${id}`
|
const url = `${location.origin}/${gen.url}/?${SHARE_KEY}=${id}`
|
||||||
@@ -298,21 +273,12 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
|||||||
const [projectDeleting, setprojectDeleting] = useState(false)
|
const [projectDeleting, setprojectDeleting] = useState(false)
|
||||||
const [fileSaving, setFileSaving] = useState<string | undefined>(undefined)
|
const [fileSaving, setFileSaving] = useState<string | undefined>(undefined)
|
||||||
const [fileRenaming, setFileRenaming] = useState<{ type: string, id: string } | undefined>(undefined)
|
const [fileRenaming, setFileRenaming] = useState<{ type: string, id: string } | undefined>(undefined)
|
||||||
const [newFileQueued, setNewFileQueued] = useState(false)
|
|
||||||
|
|
||||||
const onNewFile = useCallback(() => {
|
const onNewFile = useCallback(() => {
|
||||||
closeFile()
|
closeFile()
|
||||||
// Need to queue reset because otherwise the useModel hook will update the old file
|
// TODO: create new file with default contents
|
||||||
setNewFileQueued(true)
|
|
||||||
}, [closeFile])
|
}, [closeFile])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (file === undefined && newFileQueued) {
|
|
||||||
model?.reset(DataModel.wrapLists(model.schema.default()), true)
|
|
||||||
setNewFileQueued(false)
|
|
||||||
}
|
|
||||||
}, [model, newFileQueued, file])
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<main class={`generator${previewShown ? ' has-preview' : ''}${projectShown ? ' has-project' : ''}`}>
|
<main class={`generator${previewShown ? ' has-preview' : ''}${projectShown ? ' has-project' : ''}`}>
|
||||||
{!gen.tags?.includes('partners') && <Ad id="data-pack-generator" type="text" />}
|
{!gen.tags?.includes('partners') && <Ad id="data-pack-generator" type="text" />}
|
||||||
@@ -335,7 +301,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
|||||||
</BtnMenu>
|
</BtnMenu>
|
||||||
</div>
|
</div>
|
||||||
{error && <ErrorPanel error={error} onDismiss={() => setError(null)} />}
|
{error && <ErrorPanel error={error} onDismiss={() => setError(null)} />}
|
||||||
<Tree {...{model, version, blockStates}} onError={setError} />
|
<Tree model={model} onError={setError} />
|
||||||
<Footer donate={!gen.tags?.includes('partners')} />
|
<Footer donate={!gen.tags?.includes('partners')} />
|
||||||
</main>
|
</main>
|
||||||
<div class="popup-actions right-actions" style={`--offset: -${8 + actionsShown * 50}px;`}>
|
<div class="popup-actions right-actions" style={`--offset: -${8 + actionsShown * 50}px;`}>
|
||||||
@@ -356,10 +322,10 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class={`popup-preview${previewShown ? ' shown' : ''}`}>
|
<div class={`popup-preview${previewShown ? ' shown' : ''}`}>
|
||||||
<PreviewPanel {...{model, version, id: gen.id}} shown={previewShown} onError={setError} />
|
<PreviewPanel model={model} id={gen.id} shown={previewShown} onError={setError} />
|
||||||
</div>
|
</div>
|
||||||
<div class={`popup-source${sourceShown ? ' shown' : ''}`}>
|
<div class={`popup-source${sourceShown ? ' shown' : ''}`}>
|
||||||
<SourcePanel {...{model, blockStates, doCopy, doDownload, doImport}} name={gen.schema ?? 'data'} copySuccess={copySuccess} onError={setError} />
|
<SourcePanel {...{model, doCopy, doDownload, doImport}} name={gen.schema ?? 'data'} copySuccess={copySuccess} onError={setError} />
|
||||||
</div>
|
</div>
|
||||||
<div class={`popup-share${shareShown ? ' shown' : ''}`}>
|
<div class={`popup-share${shareShown ? ' shown' : ''}`}>
|
||||||
<TextInput value={shareUrl} readonly />
|
<TextInput value={shareUrl} readonly />
|
||||||
@@ -371,7 +337,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class={`popup-project${projectShown ? ' shown' : ''}`}>
|
<div class={`popup-project${projectShown ? ' shown' : ''}`}>
|
||||||
<ProjectPanel {...{model, version, id: gen.id}} onError={setError} onDeleteProject={() => setprojectDeleting(true)} onRename={setFileRenaming} onCreate={() => setProjectCreating(true)} />
|
<ProjectPanel onError={setError} onDeleteProject={() => setprojectDeleting(true)} onRename={setFileRenaming} onCreate={() => setProjectCreating(true)} />
|
||||||
</div>
|
</div>
|
||||||
{projectCreating && <ProjectCreation onClose={() => setProjectCreating(false)} />}
|
{projectCreating && <ProjectCreation onClose={() => setProjectCreating(false)} />}
|
||||||
{projectDeleting && <ProjectDeletion onClose={() => setprojectDeleting(false)} />}
|
{projectDeleting && <ProjectDeletion onClose={() => setprojectDeleting(false)} />}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
||||||
import { useLocale } from '../../contexts/index.js'
|
import { useLocale } from '../../contexts/index.js'
|
||||||
import { useLocalStorage, useModel } from '../../hooks/index.js'
|
import { useLocalStorage } from '../../hooks/index.js'
|
||||||
import { getOutput } from '../../schema/transformOutput.js'
|
import type { FileModel } from '../../services/index.js'
|
||||||
import type { BlockStateRegistry } from '../../services/index.js'
|
|
||||||
import { getSourceFormats, getSourceIndent, getSourceIndents, parseSource, sortData, stringifySource } from '../../services/index.js'
|
import { getSourceFormats, getSourceIndent, getSourceIndents, parseSource, sortData, stringifySource } from '../../services/index.js'
|
||||||
import { Store } from '../../Store.js'
|
import { Store } from '../../Store.js'
|
||||||
import { message } from '../../Utils.js'
|
import { message } from '../../Utils.js'
|
||||||
@@ -18,15 +16,14 @@ interface Editor {
|
|||||||
|
|
||||||
type SourcePanelProps = {
|
type SourcePanelProps = {
|
||||||
name: string,
|
name: string,
|
||||||
model: DataModel | undefined,
|
model: FileModel | undefined,
|
||||||
blockStates: BlockStateRegistry | undefined,
|
|
||||||
doCopy?: number,
|
doCopy?: number,
|
||||||
doDownload?: number,
|
doDownload?: number,
|
||||||
doImport?: number,
|
doImport?: number,
|
||||||
copySuccess: () => unknown,
|
copySuccess: () => unknown,
|
||||||
onError: (message: string | Error) => unknown,
|
onError: (message: string | Error) => unknown,
|
||||||
}
|
}
|
||||||
export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doImport, copySuccess, onError }: SourcePanelProps) {
|
export function SourcePanel({ name, model, doCopy, doDownload, doImport, copySuccess, onError }: SourcePanelProps) {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
const [indent, setIndent] = useState(Store.getIndent())
|
const [indent, setIndent] = useState(Store.getIndent())
|
||||||
const [format, setFormat] = useState(Store.getFormat())
|
const [format, setFormat] = useState(Store.getFormat())
|
||||||
@@ -40,8 +37,8 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
|||||||
const textarea = useRef<HTMLTextAreaElement>(null)
|
const textarea = useRef<HTMLTextAreaElement>(null)
|
||||||
const editor = useRef<Editor>()
|
const editor = useRef<Editor>()
|
||||||
|
|
||||||
const getSerializedOutput = useCallback((model: DataModel, blockStates: BlockStateRegistry) => {
|
const getSerializedOutput = useCallback((model: FileModel) => {
|
||||||
let data = getOutput(model, blockStates)
|
let data = model.data
|
||||||
if (sort === 'alphabetically') {
|
if (sort === 'alphabetically') {
|
||||||
data = sortData(data)
|
data = sortData(data)
|
||||||
}
|
}
|
||||||
@@ -51,9 +48,9 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
retransform.current = () => {
|
retransform.current = () => {
|
||||||
if (!editor.current) return
|
if (!editor.current) return
|
||||||
if (!model || !blockStates) return
|
if (!model) return
|
||||||
try {
|
try {
|
||||||
const output = getSerializedOutput(model, blockStates)
|
const output = getSerializedOutput(model)
|
||||||
editor.current.setValue(output)
|
editor.current.setValue(output)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -72,8 +69,8 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
|||||||
const value = editor.current.getValue()
|
const value = editor.current.getValue()
|
||||||
if (value.length === 0) return
|
if (value.length === 0) return
|
||||||
try {
|
try {
|
||||||
const data = await parseSource(value, format)
|
await parseSource(value, format)
|
||||||
model?.reset(DataModel.wrapLists(data), false)
|
// TODO: import
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
e.message = `Error importing: ${e.message}`
|
e.message = `Error importing: ${e.message}`
|
||||||
@@ -84,7 +81,7 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
|||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [model, blockStates, indent, format, sort, highlighting])
|
}, [model, indent, format, sort, highlighting])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (highlighting) {
|
if (highlighting) {
|
||||||
@@ -145,10 +142,7 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
|||||||
}
|
}
|
||||||
}, [highlighting])
|
}, [highlighting])
|
||||||
|
|
||||||
useModel(model, () => {
|
// TODO: when file contents change, retransform
|
||||||
if (!retransform.current) return
|
|
||||||
retransform.current()
|
|
||||||
})
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!retransform.current) return
|
if (!retransform.current) return
|
||||||
if (model) retransform.current()
|
if (model) retransform.current()
|
||||||
@@ -163,16 +157,16 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
|||||||
}, [indent, format, sort, highlighting, braceLoaded])
|
}, [indent, format, sort, highlighting, braceLoaded])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (doCopy && model && blockStates) {
|
if (doCopy && model) {
|
||||||
navigator.clipboard.writeText(getSerializedOutput(model, blockStates)).then(() => {
|
navigator.clipboard.writeText(getSerializedOutput(model)).then(() => {
|
||||||
copySuccess()
|
copySuccess()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [doCopy])
|
}, [doCopy])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (doDownload && model && blockStates && download.current) {
|
if (doDownload && model && download.current) {
|
||||||
const content = encodeURIComponent(getSerializedOutput(model, blockStates))
|
const content = encodeURIComponent(getSerializedOutput(model))
|
||||||
download.current.setAttribute('href', `data:text/json;charset=utf-8,${content}`)
|
download.current.setAttribute('href', `data:text/json;charset=utf-8,${content}`)
|
||||||
const fileName = name === 'pack_mcmeta' ? 'pack.mcmeta' : `${name}.${format}`
|
const fileName = name === 'pack_mcmeta' ? 'pack.mcmeta' : `${name}.${format}`
|
||||||
download.current.setAttribute('download', fileName)
|
download.current.setAttribute('download', fileName)
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import type { DataModel } from '@mcschema/core'
|
import { useErrorBoundary } from 'preact/hooks'
|
||||||
import { useErrorBoundary, useState } from 'preact/hooks'
|
|
||||||
import { useLocale } from '../../contexts/index.js'
|
import { useLocale } from '../../contexts/index.js'
|
||||||
import { useModel } from '../../hooks/index.js'
|
import type { FileModel } from '../../services/index.js'
|
||||||
import { FullNode } from '../../schema/renderHtml.js'
|
|
||||||
import type { BlockStateRegistry, VersionId } from '../../services/index.js'
|
|
||||||
|
|
||||||
type TreePanelProps = {
|
type TreePanelProps = {
|
||||||
version: VersionId,
|
model: FileModel | undefined,
|
||||||
model: DataModel | undefined,
|
|
||||||
blockStates: BlockStateRegistry | undefined,
|
|
||||||
onError: (message: string) => unknown,
|
onError: (message: string) => unknown,
|
||||||
}
|
}
|
||||||
export function Tree({ version, model, blockStates, onError }: TreePanelProps) {
|
export function Tree({ model, onError }: TreePanelProps) {
|
||||||
const { lang } = useLocale()
|
const { lang } = useLocale()
|
||||||
if (!model || !blockStates || lang === 'none') return <></>
|
if (!model || lang === 'none') return <></>
|
||||||
|
|
||||||
const [error] = useErrorBoundary(e => {
|
const [error] = useErrorBoundary(e => {
|
||||||
onError(`Error rendering the tree: ${e.message}`)
|
onError(`Error rendering the tree: ${e.message}`)
|
||||||
@@ -21,12 +16,7 @@ export function Tree({ version, model, blockStates, onError }: TreePanelProps) {
|
|||||||
})
|
})
|
||||||
if (error) return <></>
|
if (error) return <></>
|
||||||
|
|
||||||
const [, setState] = useState(0)
|
|
||||||
useModel(model, () => {
|
|
||||||
setState(state => state + 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
return <div class="tree" data-cy="tree">
|
return <div class="tree" data-cy="tree">
|
||||||
<FullNode {...{model, lang, version, blockStates}}/>
|
{/* TODO: render tree */}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { clampedMap } from 'deepslate'
|
import { clampedMap } from 'deepslate'
|
||||||
import { mat3 } from 'gl-matrix'
|
import { mat3 } from 'gl-matrix'
|
||||||
import { useCallback, useRef, useState } from 'preact/hooks'
|
import { useCallback, useRef, useState } from 'preact/hooks'
|
||||||
import { getProjectData, useLocale, useProject, useStore } from '../../contexts/index.js'
|
import { getProjectData, useLocale, useProject, useStore, useVersion } from '../../contexts/index.js'
|
||||||
import { useAsync } from '../../hooks/index.js'
|
import { useAsync } from '../../hooks/index.js'
|
||||||
import { checkVersion } from '../../services/Schemas.js'
|
import { checkVersion } from '../../services/Schemas.js'
|
||||||
import { Store } from '../../Store.js'
|
import { Store } from '../../Store.js'
|
||||||
@@ -21,8 +20,9 @@ type Layer = typeof LAYERS[number]
|
|||||||
const DETAIL_DELAY = 300
|
const DETAIL_DELAY = 300
|
||||||
const DETAIL_SCALE = 2
|
const DETAIL_SCALE = 2
|
||||||
|
|
||||||
export const BiomeSourcePreview = ({ data, shown, version }: PreviewProps) => {
|
export const BiomeSourcePreview = ({ model, shown }: PreviewProps) => {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
|
const { version } = useVersion()
|
||||||
const { project } = useProject()
|
const { project } = useProject()
|
||||||
const { biomeColors } = useStore()
|
const { biomeColors } = useStore()
|
||||||
const [seed, setSeed] = useState(randomSeed())
|
const [seed, setSeed] = useState(randomSeed())
|
||||||
@@ -31,13 +31,13 @@ export const BiomeSourcePreview = ({ data, shown, version }: PreviewProps) => {
|
|||||||
const [focused, setFocused] = useState<string[]>([])
|
const [focused, setFocused] = useState<string[]>([])
|
||||||
const [focused2, setFocused2] = useState<string[]>([])
|
const [focused2, setFocused2] = useState<string[]>([])
|
||||||
|
|
||||||
const state = JSON.stringify(data)
|
const state = JSON.stringify(model.data)
|
||||||
const type: string = data?.generator?.biome_source?.type?.replace(/^minecraft:/, '') ?? ''
|
const type: string = model.data?.generator?.biome_source?.type?.replace(/^minecraft:/, '') ?? ''
|
||||||
const hasRandomness = type === 'multi_noise' || type === 'the_end'
|
const hasRandomness = type === 'multi_noise' || type === 'the_end'
|
||||||
|
|
||||||
const { value } = useAsync(async function loadBiomeSource() {
|
const { value } = useAsync(async function loadBiomeSource() {
|
||||||
await DEEPSLATE.loadVersion(version, getProjectData(project))
|
await DEEPSLATE.loadVersion(version, getProjectData(project))
|
||||||
await DEEPSLATE.loadChunkGenerator(DataModel.unwrapLists(data?.generator?.settings), DataModel.unwrapLists(data?.generator?.biome_source), seed)
|
await DEEPSLATE.loadChunkGenerator(model.data?.generator?.settings, model.data?.generator?.biome_source, seed)
|
||||||
return {
|
return {
|
||||||
biomeSource: { loaded: true },
|
biomeSource: { loaded: true },
|
||||||
noiseRouter: checkVersion(version, '1.19') ? DEEPSLATE.getNoiseRouter() : undefined,
|
noiseRouter: checkVersion(version, '1.19') ? DEEPSLATE.getNoiseRouter() : undefined,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { BlockDefinition, Identifier, Structure, StructureRenderer } from 'deepslate/render'
|
import { BlockDefinition, Identifier, Structure, StructureRenderer } from 'deepslate/render'
|
||||||
import type { mat4 } from 'gl-matrix'
|
import type { mat4 } from 'gl-matrix'
|
||||||
import { useCallback, useRef } from 'preact/hooks'
|
import { useCallback, useRef } from 'preact/hooks'
|
||||||
@@ -11,14 +10,14 @@ import { InteractiveCanvas3D } from './InteractiveCanvas3D.jsx'
|
|||||||
|
|
||||||
const PREVIEW_ID = Identifier.parse('misode:preview')
|
const PREVIEW_ID = Identifier.parse('misode:preview')
|
||||||
|
|
||||||
export const BlockStatePreview = ({ data, shown }: PreviewProps) => {
|
export const BlockStatePreview = ({ model, shown }: PreviewProps) => {
|
||||||
const { version } = useVersion()
|
const { version } = useVersion()
|
||||||
const serializedData = JSON.stringify(data)
|
const serializedData = JSON.stringify(model.data)
|
||||||
|
|
||||||
const { value: resources } = useAsync(async () => {
|
const { value: resources } = useAsync(async () => {
|
||||||
if (!shown) return AsyncCancel
|
if (!shown) return AsyncCancel
|
||||||
const resources = await getResources(version)
|
const resources = await getResources(version)
|
||||||
const definition = BlockDefinition.fromJson(DataModel.unwrapLists(data))
|
const definition = BlockDefinition.fromJson(model.data)
|
||||||
const wrapper = new ResourceWrapper(resources, {
|
const wrapper = new ResourceWrapper(resources, {
|
||||||
getBlockDefinition(id) {
|
getBlockDefinition(id) {
|
||||||
if (id.equals(PREVIEW_ID)) return definition
|
if (id.equals(PREVIEW_ID)) return definition
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import type { BlockPos, ChunkPos, PerlinNoise, Random } from 'deepslate/worldgen'
|
import type { BlockPos, ChunkPos, PerlinNoise, Random } from 'deepslate/worldgen'
|
||||||
import type { Color } from '../../Utils.js'
|
|
||||||
import { clamp, isObject, stringToColor } from '../../Utils.js'
|
|
||||||
import type { VersionId } from '../../services/index.js'
|
import type { VersionId } from '../../services/index.js'
|
||||||
import { checkVersion } from '../../services/index.js'
|
import { checkVersion } from '../../services/index.js'
|
||||||
|
import type { Color } from '../../Utils.js'
|
||||||
|
import { clamp, isObject, stringToColor } from '../../Utils.js'
|
||||||
|
|
||||||
export type Placement = [BlockPos, number]
|
export type Placement = [BlockPos, number]
|
||||||
|
|
||||||
@@ -38,9 +37,9 @@ export const featureColors: Color[] = [
|
|||||||
|
|
||||||
export function decorateChunk(pos: ChunkPos, state: any, ctx: PlacementContext): PlacedFeature[] {
|
export function decorateChunk(pos: ChunkPos, state: any, ctx: PlacementContext): PlacedFeature[] {
|
||||||
if (checkVersion(ctx.version, undefined, '1.17')) {
|
if (checkVersion(ctx.version, undefined, '1.17')) {
|
||||||
getPlacements([pos[0] * 16, 0, pos[1] * 16], DataModel.unwrapLists(state), ctx)
|
getPlacements([pos[0] * 16, 0, pos[1] * 16], state, ctx)
|
||||||
} else {
|
} else {
|
||||||
modifyPlacement([pos[0] * 16, 0, pos[1] * 16], DataModel.unwrapLists(state.placement), ctx)
|
modifyPlacement([pos[0] * 16, 0, pos[1] * 16], state.placement, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.placements.map(([pos, i]) => {
|
return ctx.placements.map(([pos, i]) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BlockPos, ChunkPos, LegacyRandom, PerlinNoise } from 'deepslate'
|
import { BlockPos, ChunkPos, LegacyRandom, PerlinNoise } from 'deepslate'
|
||||||
import type { mat3 } from 'gl-matrix'
|
import type { mat3 } from 'gl-matrix'
|
||||||
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
||||||
import { useLocale } from '../../contexts/index.js'
|
import { useLocale, useVersion } from '../../contexts/index.js'
|
||||||
import { computeIfAbsent, iterateWorld2D, randomSeed } from '../../Utils.js'
|
import { computeIfAbsent, iterateWorld2D, randomSeed } from '../../Utils.js'
|
||||||
import { Btn } from '../index.js'
|
import { Btn } from '../index.js'
|
||||||
import type { PlacedFeature, PlacementContext } from './Decorator.js'
|
import type { PlacedFeature, PlacementContext } from './Decorator.js'
|
||||||
@@ -9,10 +9,11 @@ import { decorateChunk } from './Decorator.js'
|
|||||||
import type { PreviewProps } from './index.js'
|
import type { PreviewProps } from './index.js'
|
||||||
import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx'
|
import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx'
|
||||||
|
|
||||||
export const DecoratorPreview = ({ data, version, shown }: PreviewProps) => {
|
export const DecoratorPreview = ({ model, shown }: PreviewProps) => {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
|
const { version } = useVersion()
|
||||||
const [seed, setSeed] = useState(randomSeed())
|
const [seed, setSeed] = useState(randomSeed())
|
||||||
const state = JSON.stringify(data)
|
const state = JSON.stringify(model.data)
|
||||||
|
|
||||||
const { context, chunkFeatures } = useMemo(() => {
|
const { context, chunkFeatures } = useMemo(() => {
|
||||||
const random = new LegacyRandom(seed)
|
const random = new LegacyRandom(seed)
|
||||||
@@ -51,7 +52,7 @@ export const DecoratorPreview = ({ data, version, shown }: PreviewProps) => {
|
|||||||
|
|
||||||
iterateWorld2D(imageData.current, transform, (x, y) => {
|
iterateWorld2D(imageData.current, transform, (x, y) => {
|
||||||
const pos = ChunkPos.create(Math.floor(x / 16), Math.floor(-y / 16))
|
const pos = ChunkPos.create(Math.floor(x / 16), Math.floor(-y / 16))
|
||||||
const features = computeIfAbsent(chunkFeatures, `${pos[0]} ${pos[1]}`, () => decorateChunk(pos, data, context))
|
const features = computeIfAbsent(chunkFeatures, `${pos[0]} ${pos[1]}`, () => decorateChunk(pos, model.data, context))
|
||||||
return features.find(f => f.pos[0] === x && f.pos[2] == -y) ?? { pos: BlockPos.create(x, 0, -y) }
|
return features.find(f => f.pos[0] === x && f.pos[2] == -y) ?? { pos: BlockPos.create(x, 0, -y) }
|
||||||
}, (feature) => {
|
}, (feature) => {
|
||||||
if ('color' in feature) {
|
if ('color' in feature) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import type { Voxel } from 'deepslate/render'
|
import type { Voxel } from 'deepslate/render'
|
||||||
import { clampedMap, VoxelRenderer } from 'deepslate/render'
|
import { clampedMap, VoxelRenderer } from 'deepslate/render'
|
||||||
import type { mat3, mat4 } from 'gl-matrix'
|
import type { mat3, mat4 } from 'gl-matrix'
|
||||||
@@ -19,7 +18,7 @@ import { InteractiveCanvas3D } from './InteractiveCanvas3D.jsx'
|
|||||||
|
|
||||||
const MODES = ['side', 'top', '3d'] as const
|
const MODES = ['side', 'top', '3d'] as const
|
||||||
|
|
||||||
export const DensityFunctionPreview = ({ data, shown }: PreviewProps) => {
|
export const DensityFunctionPreview = ({ model, shown }: PreviewProps) => {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
const { project } = useProject()
|
const { project } = useProject()
|
||||||
const { version } = useVersion()
|
const { version } = useVersion()
|
||||||
@@ -29,11 +28,11 @@ export const DensityFunctionPreview = ({ data, shown }: PreviewProps) => {
|
|||||||
const [seed, setSeed] = useState(randomSeed())
|
const [seed, setSeed] = useState(randomSeed())
|
||||||
const [minY] = useState(0)
|
const [minY] = useState(0)
|
||||||
const [height] = useState(256)
|
const [height] = useState(256)
|
||||||
const serializedData = JSON.stringify(data)
|
const serializedData = JSON.stringify(model.data)
|
||||||
|
|
||||||
const { value: df } = useAsync(async () => {
|
const { value: df } = useAsync(async () => {
|
||||||
await DEEPSLATE.loadVersion(version, getProjectData(project))
|
await DEEPSLATE.loadVersion(version, getProjectData(project))
|
||||||
const df = DEEPSLATE.loadDensityFunction(DataModel.unwrapLists(data), minY, height, seed)
|
const df = DEEPSLATE.loadDensityFunction(model.data, minY, height, seed)
|
||||||
return df
|
return df
|
||||||
}, [version, project, minY, height, seed, serializedData])
|
}, [version, project, minY, height, seed, serializedData])
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { Identifier } from 'deepslate'
|
import { Identifier } from 'deepslate'
|
||||||
import { useMemo, useRef, useState } from 'preact/hooks'
|
import { useMemo, useRef, useState } from 'preact/hooks'
|
||||||
import { useLocale, useVersion } from '../../contexts/index.js'
|
import { useLocale, useVersion } from '../../contexts/index.js'
|
||||||
@@ -12,7 +11,7 @@ import type { PreviewProps } from './index.js'
|
|||||||
import { generateLootTable } from './LootTable.js'
|
import { generateLootTable } from './LootTable.js'
|
||||||
import { generateLootTable as generateLootTable1204 } from './LootTable1204.js'
|
import { generateLootTable as generateLootTable1204 } from './LootTable1204.js'
|
||||||
|
|
||||||
export const LootTablePreview = ({ data }: PreviewProps) => {
|
export const LootTablePreview = ({ model }: PreviewProps) => {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
const { version } = useVersion()
|
const { version } = useVersion()
|
||||||
const use1204 = !checkVersion(version, '1.20.5')
|
const use1204 = !checkVersion(version, '1.20.5')
|
||||||
@@ -35,7 +34,7 @@ export const LootTablePreview = ({ data }: PreviewProps) => {
|
|||||||
])
|
])
|
||||||
}, [version])
|
}, [version])
|
||||||
|
|
||||||
const table = DataModel.unwrapLists(data)
|
const table = model.data
|
||||||
const state = JSON.stringify(table)
|
const state = JSON.stringify(table)
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
if (dependencies === undefined || loading) {
|
if (dependencies === undefined || loading) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { BlockDefinition, BlockModel, Identifier, Structure, StructureRenderer } from 'deepslate/render'
|
import { BlockDefinition, BlockModel, Identifier, Structure, StructureRenderer } from 'deepslate/render'
|
||||||
import type { mat4 } from 'gl-matrix'
|
import type { mat4 } from 'gl-matrix'
|
||||||
import { useCallback, useRef } from 'preact/hooks'
|
import { useCallback, useRef } from 'preact/hooks'
|
||||||
@@ -12,22 +11,22 @@ import { InteractiveCanvas3D } from './InteractiveCanvas3D.jsx'
|
|||||||
const PREVIEW_ID = Identifier.parse('misode:preview')
|
const PREVIEW_ID = Identifier.parse('misode:preview')
|
||||||
const PREVIEW_DEFINITION = new BlockDefinition({ '': { model: PREVIEW_ID.toString() }}, undefined)
|
const PREVIEW_DEFINITION = new BlockDefinition({ '': { model: PREVIEW_ID.toString() }}, undefined)
|
||||||
|
|
||||||
export const ModelPreview = ({ data, shown }: PreviewProps) => {
|
export const ModelPreview = ({ model, shown }: PreviewProps) => {
|
||||||
const { version } = useVersion()
|
const { version } = useVersion()
|
||||||
const serializedData = JSON.stringify(data)
|
const serializedData = JSON.stringify(model.data)
|
||||||
|
|
||||||
const { value: resources } = useAsync(async () => {
|
const { value: resources } = useAsync(async () => {
|
||||||
if (!shown) return AsyncCancel
|
if (!shown) return AsyncCancel
|
||||||
const resources = await getResources(version)
|
const resources = await getResources(version)
|
||||||
const model = BlockModel.fromJson(DataModel.unwrapLists(data))
|
const blockModel = BlockModel.fromJson(model.data)
|
||||||
model.flatten(resources)
|
blockModel.flatten(resources)
|
||||||
const wrapper = new ResourceWrapper(resources, {
|
const wrapper = new ResourceWrapper(resources, {
|
||||||
getBlockDefinition(id) {
|
getBlockDefinition(id) {
|
||||||
if (id.equals(PREVIEW_ID)) return PREVIEW_DEFINITION
|
if (id.equals(PREVIEW_ID)) return PREVIEW_DEFINITION
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
getBlockModel(id) {
|
getBlockModel(id) {
|
||||||
if (id.equals(PREVIEW_ID)) return model
|
if (id.equals(PREVIEW_ID)) return blockModel
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { clampedMap, NoiseParameters, NormalNoise, XoroshiroRandom } from 'deepslate'
|
import { clampedMap, NoiseParameters, NormalNoise, XoroshiroRandom } from 'deepslate'
|
||||||
import type { mat3 } from 'gl-matrix'
|
import type { mat3 } from 'gl-matrix'
|
||||||
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
||||||
@@ -12,14 +11,14 @@ import { ColormapSelector } from './ColormapSelector.jsx'
|
|||||||
import type { PreviewProps } from './index.js'
|
import type { PreviewProps } from './index.js'
|
||||||
import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx'
|
import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx'
|
||||||
|
|
||||||
export const NoisePreview = ({ data, shown }: PreviewProps) => {
|
export const NoisePreview = ({ model, shown }: PreviewProps) => {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
const [seed, setSeed] = useState(randomSeed())
|
const [seed, setSeed] = useState(randomSeed())
|
||||||
const state = JSON.stringify(data)
|
const state = JSON.stringify(model.data)
|
||||||
|
|
||||||
const noise = useMemo(() => {
|
const noise = useMemo(() => {
|
||||||
const random = XoroshiroRandom.create(seed)
|
const random = XoroshiroRandom.create(seed)
|
||||||
const params = NoiseParameters.fromJson(DataModel.unwrapLists(data))
|
const params = NoiseParameters.fromJson(model.data)
|
||||||
return new NormalNoise(random, params)
|
return new NormalNoise(random, params)
|
||||||
}, [state, seed])
|
}, [state, seed])
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { clampedMap } from 'deepslate'
|
import { clampedMap } from 'deepslate'
|
||||||
import type { mat3 } from 'gl-matrix'
|
import type { mat3 } from 'gl-matrix'
|
||||||
import { vec2 } from 'gl-matrix'
|
import { vec2 } from 'gl-matrix'
|
||||||
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
import { useCallback, useRef, useState } from 'preact/hooks'
|
||||||
|
import { getProjectData, useLocale, useProject, useVersion } from '../../contexts/index.js'
|
||||||
|
import { useAsync } from '../../hooks/index.js'
|
||||||
|
import { fetchRegistries } from '../../services/index.js'
|
||||||
import { Store } from '../../Store.js'
|
import { Store } from '../../Store.js'
|
||||||
import { iterateWorld2D, randomSeed } from '../../Utils.js'
|
import { iterateWorld2D, randomSeed } from '../../Utils.js'
|
||||||
import { getProjectData, useLocale, useProject } from '../../contexts/index.js'
|
|
||||||
import { useAsync } from '../../hooks/index.js'
|
|
||||||
import { CachedCollections } from '../../services/index.js'
|
|
||||||
import { Btn, BtnInput, BtnMenu, ErrorPanel } from '../index.js'
|
import { Btn, BtnInput, BtnMenu, ErrorPanel } from '../index.js'
|
||||||
import type { ColormapType } from './Colormap.js'
|
import type { ColormapType } from './Colormap.js'
|
||||||
import { getColormap } from './Colormap.js'
|
import { getColormap } from './Colormap.js'
|
||||||
import { ColormapSelector } from './ColormapSelector.jsx'
|
import { ColormapSelector } from './ColormapSelector.jsx'
|
||||||
import { DEEPSLATE } from './Deepslate.js'
|
import { DEEPSLATE } from './Deepslate.js'
|
||||||
import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx'
|
|
||||||
import type { PreviewProps } from './index.js'
|
import type { PreviewProps } from './index.js'
|
||||||
|
import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx'
|
||||||
|
|
||||||
export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) => {
|
export const NoiseSettingsPreview = ({ model, shown }: PreviewProps) => {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
|
const { version } = useVersion()
|
||||||
const { project } = useProject()
|
const { project } = useProject()
|
||||||
const [seed, setSeed] = useState(randomSeed())
|
const [seed, setSeed] = useState(randomSeed())
|
||||||
const [biome, setBiome] = useState('minecraft:plains')
|
const [biome, setBiome] = useState('minecraft:plains')
|
||||||
const [layer, setLayer] = useState('terrain')
|
const [layer, setLayer] = useState('terrain')
|
||||||
const state = JSON.stringify(data)
|
const state = JSON.stringify(model.data)
|
||||||
|
|
||||||
const { value, error } = useAsync(async () => {
|
const { value, error } = useAsync(async () => {
|
||||||
const unwrapped = DataModel.unwrapLists(data)
|
const unwrapped = model.data
|
||||||
await DEEPSLATE.loadVersion(version, getProjectData(project))
|
await DEEPSLATE.loadVersion(version, getProjectData(project))
|
||||||
const biomeSource = { type: 'fixed', biome }
|
const biomeSource = { type: 'fixed', biome }
|
||||||
await DEEPSLATE.loadChunkGenerator(unwrapped, biomeSource, seed)
|
await DEEPSLATE.loadChunkGenerator(unwrapped, biomeSource, seed)
|
||||||
@@ -86,7 +86,10 @@ export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) =>
|
|||||||
}
|
}
|
||||||
}, [noiseSettings, finalDensity])
|
}, [noiseSettings, finalDensity])
|
||||||
|
|
||||||
const allBiomes = useMemo(() => CachedCollections?.get('worldgen/biome') ?? [], [version])
|
const { value: allBiomes } = useAsync(async () => {
|
||||||
|
const registries = await fetchRegistries(version)
|
||||||
|
return registries.get('worldgen/biome')
|
||||||
|
}, [version])
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <ErrorPanel error={error} prefix="Failed to initialize preview: " />
|
return <ErrorPanel error={error} prefix="Failed to initialize preview: " />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import { Identifier, ItemStack } from 'deepslate'
|
import { Identifier, ItemStack } from 'deepslate'
|
||||||
import { useEffect, useMemo, useRef, useState } from 'preact/hooks'
|
import { useEffect, useMemo, useRef, useState } from 'preact/hooks'
|
||||||
import { useLocale } from '../../contexts/index.js'
|
import { useLocale, useVersion } from '../../contexts/index.js'
|
||||||
import { useAsync } from '../../hooks/useAsync.js'
|
import { useAsync } from '../../hooks/useAsync.js'
|
||||||
import type { VersionId } from '../../services/index.js'
|
import type { VersionId } from '../../services/index.js'
|
||||||
import { checkVersion, fetchAllPresets } from '../../services/index.js'
|
import { checkVersion, fetchAllPresets } from '../../services/index.js'
|
||||||
@@ -12,8 +11,9 @@ import type { PreviewProps } from './index.js'
|
|||||||
|
|
||||||
const ANIMATION_TIME = 1000
|
const ANIMATION_TIME = 1000
|
||||||
|
|
||||||
export const RecipePreview = ({ data, version }: PreviewProps) => {
|
export const RecipePreview = ({ model }: PreviewProps) => {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
|
const { version } = useVersion()
|
||||||
const [advancedTooltips, setAdvancedTooltips] = useState(true)
|
const [advancedTooltips, setAdvancedTooltips] = useState(true)
|
||||||
const [animation, setAnimation] = useState(0)
|
const [animation, setAnimation] = useState(0)
|
||||||
const overlay = useRef<HTMLDivElement>(null)
|
const overlay = useRef<HTMLDivElement>(null)
|
||||||
@@ -29,7 +29,7 @@ export const RecipePreview = ({ data, version }: PreviewProps) => {
|
|||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const recipe = DataModel.unwrapLists(data)
|
const recipe = model.data
|
||||||
const state = JSON.stringify(recipe)
|
const state = JSON.stringify(recipe)
|
||||||
const items = useMemo<Map<Slot, ItemStack>>(() => {
|
const items = useMemo<Map<Slot, ItemStack>>(() => {
|
||||||
return placeItems(version, recipe, animation, itemTags ?? new Map())
|
return placeItems(version, recipe, animation, itemTags ?? new Map())
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
import type { Identifier } from 'deepslate'
|
import type { Identifier } from 'deepslate'
|
||||||
import { ChunkPos } from 'deepslate'
|
import { ChunkPos } from 'deepslate'
|
||||||
import type { mat3 } from 'gl-matrix'
|
import type { mat3 } from 'gl-matrix'
|
||||||
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
||||||
|
import { useLocale, useVersion } from '../../contexts/index.js'
|
||||||
|
import { useAsync } from '../../hooks/useAsync.js'
|
||||||
import type { Color } from '../../Utils.js'
|
import type { Color } from '../../Utils.js'
|
||||||
import { computeIfAbsent, iterateWorld2D, randomSeed, stringToColor } from '../../Utils.js'
|
import { computeIfAbsent, iterateWorld2D, randomSeed, stringToColor } from '../../Utils.js'
|
||||||
import { useLocale } from '../../contexts/index.js'
|
|
||||||
import { useAsync } from '../../hooks/useAsync.js'
|
|
||||||
import { Btn } from '../index.js'
|
import { Btn } from '../index.js'
|
||||||
import { featureColors } from './Decorator.js'
|
import { featureColors } from './Decorator.js'
|
||||||
import { DEEPSLATE } from './Deepslate.js'
|
import { DEEPSLATE } from './Deepslate.js'
|
||||||
import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx'
|
|
||||||
import type { PreviewProps } from './index.js'
|
import type { PreviewProps } from './index.js'
|
||||||
|
import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx'
|
||||||
|
|
||||||
export const StructureSetPreview = ({ data, version, shown }: PreviewProps) => {
|
export const StructureSetPreview = ({ model, shown }: PreviewProps) => {
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
|
const { version } = useVersion()
|
||||||
const [seed, setSeed] = useState(randomSeed())
|
const [seed, setSeed] = useState(randomSeed())
|
||||||
const state = JSON.stringify(data)
|
const state = JSON.stringify(model.data)
|
||||||
|
|
||||||
const { value: structureSet } = useAsync(async () => {
|
const { value: structureSet } = useAsync(async () => {
|
||||||
await DEEPSLATE.loadVersion(version)
|
await DEEPSLATE.loadVersion(version)
|
||||||
const structureSet = DEEPSLATE.loadStructureSet(DataModel.unwrapLists(data), seed)
|
const structureSet = DEEPSLATE.loadStructureSet(model.data, seed)
|
||||||
return structureSet
|
return structureSet
|
||||||
}, [state, version, seed])
|
}, [state, version, seed])
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { DataModel } from '@mcschema/core'
|
import type { FileModel } from '../../services/index.js'
|
||||||
import type { VersionId } from '../../services/index.js'
|
|
||||||
|
|
||||||
export * from './BiomeSourcePreview.js'
|
export * from './BiomeSourcePreview.js'
|
||||||
export * from './BlockStatePreview.jsx'
|
export * from './BlockStatePreview.jsx'
|
||||||
@@ -12,9 +11,7 @@ export * from './NoiseSettingsPreview.js'
|
|||||||
export * from './RecipePreview.jsx'
|
export * from './RecipePreview.jsx'
|
||||||
export * from './StructureSetPreview.jsx'
|
export * from './StructureSetPreview.jsx'
|
||||||
|
|
||||||
export type PreviewProps = {
|
export interface PreviewProps {
|
||||||
model: DataModel,
|
model: FileModel
|
||||||
data: any,
|
shown: boolean
|
||||||
shown: boolean,
|
|
||||||
version: VersionId,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,13 +46,7 @@ async function loadLocale(language: string) {
|
|||||||
const langConfig = config.languages.find(lang => lang.code === language)
|
const langConfig = config.languages.find(lang => lang.code === language)
|
||||||
if (!langConfig) return
|
if (!langConfig) return
|
||||||
const data = await import(`../../locales/${language}.json`)
|
const data = await import(`../../locales/${language}.json`)
|
||||||
const schema = langConfig.schemas !== false
|
Locales[language] = data.default
|
||||||
&& await import(`../../../node_modules/@mcschema/locales/src/${language}.json`)
|
|
||||||
let partners = { default: {} }
|
|
||||||
if (language === 'en') {
|
|
||||||
partners = await import('../partners/locales/en.json')
|
|
||||||
}
|
|
||||||
Locales[language] = { ...data.default, ...schema.default, ...partners.default }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useLocale() {
|
export function useLocale() {
|
||||||
|
|||||||
@@ -5,6 +5,5 @@ export * from './useFocus.js'
|
|||||||
export * from './useHash.js'
|
export * from './useHash.js'
|
||||||
export * from './useLocalStorage.js'
|
export * from './useLocalStorage.js'
|
||||||
export * from './useMediaQuery.js'
|
export * from './useMediaQuery.js'
|
||||||
export * from './useModel.js'
|
|
||||||
export * from './useSearchParam.js'
|
export * from './useSearchParam.js'
|
||||||
export * from './useTags.js'
|
export * from './useTags.js'
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import type { DataModel } from '@mcschema/core'
|
|
||||||
import type { Inputs } from 'preact/hooks'
|
|
||||||
import { useEffect } from 'preact/hooks'
|
|
||||||
|
|
||||||
export function useModel(model: DataModel | undefined | null, invalidated: (model: DataModel) => unknown, inputs?: Inputs) {
|
|
||||||
const listener = {
|
|
||||||
invalidated() {
|
|
||||||
if (model) {
|
|
||||||
invalidated(model)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
model?.addListener(listener)
|
|
||||||
return () => {
|
|
||||||
model?.removeListener(listener)
|
|
||||||
}
|
|
||||||
}, [model, ...inputs ?? []])
|
|
||||||
}
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
import type { CollectionRegistry, ResourceType, SchemaRegistry } from '@mcschema/core'
|
|
||||||
import { BooleanNode, Case, ChoiceNode, ListNode, MapNode, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core'
|
|
||||||
|
|
||||||
const ID = 'immersive_weathering'
|
|
||||||
|
|
||||||
export function initImmersiveWeathering(schemas: SchemaRegistry, collections: CollectionRegistry) {
|
|
||||||
const Reference = RawReference.bind(undefined, schemas)
|
|
||||||
const StringNode = RawStringNode.bind(undefined, collections)
|
|
||||||
|
|
||||||
const Tag = (id: Exclude<ResourceType, `$tag/${string}`>) => ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode({ validator: 'resource', params: { pool: id, allowTag: true } }),
|
|
||||||
change: (v: unknown) => {
|
|
||||||
if (Array.isArray(v) && typeof v[0] === 'string' && !v[0].startsWith('#')) {
|
|
||||||
return v[0]
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: ListNode(
|
|
||||||
StringNode({ validator: 'resource', params: { pool: id } })
|
|
||||||
),
|
|
||||||
change: (v: unknown) => {
|
|
||||||
if (typeof v === 'string' && !v.startsWith('#')) {
|
|
||||||
return [v]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
},
|
|
||||||
},
|
|
||||||
], { choiceContext: 'tag' })
|
|
||||||
|
|
||||||
schemas.register(`${ID}:block_growth`, ObjectNode({
|
|
||||||
area_condition: Reference(`${ID}:area_condition`),
|
|
||||||
position_predicates: Opt(ListNode(
|
|
||||||
Reference(`${ID}:position_test`)
|
|
||||||
)),
|
|
||||||
growth_chance: NumberNode({ min: 0, max: 1 }),
|
|
||||||
growth_for_face: ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
direction: Opt(StringNode({ enum: 'direction' })),
|
|
||||||
weight: Opt(NumberNode({ integer: true })),
|
|
||||||
growth: ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
data: Reference(`${ID}:block_pair`),
|
|
||||||
weight: NumberNode({ integer: true }),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
}, { category: 'pool' })
|
|
||||||
),
|
|
||||||
owners: ListNode(
|
|
||||||
StringNode({ validator: 'resource', params: { pool: 'block' } })
|
|
||||||
),
|
|
||||||
replacing_target: Reference(`${ID}:rule_test`),
|
|
||||||
target_self: Opt(BooleanNode()),
|
|
||||||
destroy_target: Opt(BooleanNode()),
|
|
||||||
}, { context: `${ID}.block_growth` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:area_condition`, ObjectNode({
|
|
||||||
type: StringNode({ enum: ['generate_if_not_too_many', 'neighbor_based_generation'] }),
|
|
||||||
[Switch]: [{ push: 'type' }],
|
|
||||||
[Case]: {
|
|
||||||
generate_if_not_too_many: {
|
|
||||||
radiusX: NumberNode({ integer: true }),
|
|
||||||
radiusY: NumberNode({ integer: true }),
|
|
||||||
radiusZ: NumberNode({ integer: true }),
|
|
||||||
requiredAmount: NumberNode({ integer: true }),
|
|
||||||
yOffset: Opt(NumberNode({ integer: true })),
|
|
||||||
must_have: Opt(Reference(`${ID}:rule_test`)),
|
|
||||||
must_not_have: Opt(Reference(`${ID}:rule_test`)),
|
|
||||||
includes: Opt(Tag('block')),
|
|
||||||
},
|
|
||||||
neighbor_based_generation: {
|
|
||||||
must_have: Reference(`${ID}:rule_test`),
|
|
||||||
must_not_have: Opt(Reference(`${ID}:rule_test`)),
|
|
||||||
required_amount: Opt(NumberNode({ integer: true })),
|
|
||||||
directions: ListNode(
|
|
||||||
StringNode({ enum: 'direction' })
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, { context: `${ID}.area_condition` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:block_pair`, ObjectNode({
|
|
||||||
block: Reference(`${ID}:block_state`),
|
|
||||||
above_block: Opt(Reference(`${ID}:block_state`)),
|
|
||||||
}, { context: `${ID}.block_pair` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:block_state`, ObjectNode({
|
|
||||||
Name: StringNode({ validator: 'resource', params: { pool: 'block' } }),
|
|
||||||
Properties: Opt(MapNode(
|
|
||||||
StringNode(),
|
|
||||||
StringNode(),
|
|
||||||
)),
|
|
||||||
}, { context: 'block_state' }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:position_test`, ObjectNode({
|
|
||||||
predicate_type: StringNode({ enum: ['biome_match', 'day_test', 'nand', 'precipitation_test', 'temperature_range'] }),
|
|
||||||
[Switch]: [{ push: 'predicate_type' }],
|
|
||||||
[Case]: {
|
|
||||||
biome_match: {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
},
|
|
||||||
day_test: {
|
|
||||||
day: BooleanNode(),
|
|
||||||
},
|
|
||||||
nand: {
|
|
||||||
predicates: ListNode(
|
|
||||||
Reference(`${ID}:position_test`)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
precipitation_test: {
|
|
||||||
precipitation: StringNode({ enum: ['none', 'rain', 'snow']}),
|
|
||||||
},
|
|
||||||
temperature_range: {
|
|
||||||
min: NumberNode(),
|
|
||||||
max: NumberNode(),
|
|
||||||
use_local_pos: Opt(BooleanNode()),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, { context: `${ID}.position_test`, category: 'predicate' }))
|
|
||||||
|
|
||||||
collections.register(`${ID}:rule_test`, [
|
|
||||||
...collections.get('rule_test'),
|
|
||||||
'immersive_weathering:block_set_match',
|
|
||||||
'immersive_weathering:fluid_match',
|
|
||||||
'immersive_weathering:tree_log',
|
|
||||||
])
|
|
||||||
|
|
||||||
schemas.register(`${ID}:rule_test`, ObjectNode({
|
|
||||||
predicate_type: StringNode({ validator: 'resource', params: { pool: `${ID}:rule_test` as any } }),
|
|
||||||
[Switch]: [{ push: 'predicate_type' }],
|
|
||||||
[Case]: {
|
|
||||||
'minecraft:block_match': {
|
|
||||||
block: StringNode({ validator: 'resource', params: { pool: 'block' } }),
|
|
||||||
},
|
|
||||||
'minecraft:blockstate_match': {
|
|
||||||
block_state: Reference('block_state'),
|
|
||||||
},
|
|
||||||
'minecraft:random_block_match': {
|
|
||||||
block: StringNode({ validator: 'resource', params: { pool: 'block' } }),
|
|
||||||
probability: NumberNode({ min: 0, max: 1 }),
|
|
||||||
},
|
|
||||||
'minecraft:random_blockstate_match': {
|
|
||||||
block_state: Reference('block_state'),
|
|
||||||
probability: NumberNode({ min: 0, max: 1 }),
|
|
||||||
},
|
|
||||||
'minecraft:tag_match': {
|
|
||||||
tag: StringNode({ validator: 'resource', params: { pool: '$tag/block' }}),
|
|
||||||
},
|
|
||||||
'immersive_weathering:block_set_match': {
|
|
||||||
blocks: Tag('block'),
|
|
||||||
probability: Opt(NumberNode({ min: 0, max: 1 })),
|
|
||||||
},
|
|
||||||
'immersive_weathering:fluid_match': {
|
|
||||||
fluid: StringNode({ validator: 'resource', params: { pool: 'fluid' } }),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, { context: 'rule_test', disableSwitchContext: true }))
|
|
||||||
|
|
||||||
collections.register('block_growth', [
|
|
||||||
'immersive_weathering:brain_coral',
|
|
||||||
'immersive_weathering:bubble_coral',
|
|
||||||
'immersive_weathering:cracked_mud_rivers',
|
|
||||||
'immersive_weathering:crimson_nylium',
|
|
||||||
'immersive_weathering:cryosol',
|
|
||||||
'immersive_weathering:farmland_rare_weeds',
|
|
||||||
'immersive_weathering:farmland_weeds',
|
|
||||||
'immersive_weathering:fire_coral',
|
|
||||||
'immersive_weathering:fire_soot',
|
|
||||||
'immersive_weathering:fluvisol',
|
|
||||||
'immersive_weathering:grass_base',
|
|
||||||
'immersive_weathering:grass_block_badlands',
|
|
||||||
'immersive_weathering:grass_block_bamboo_jungle',
|
|
||||||
'immersive_weathering:grass_block_birch_forest',
|
|
||||||
'immersive_weathering:grass_block_dark_forest',
|
|
||||||
'immersive_weathering:grass_block_flower_forest',
|
|
||||||
'immersive_weathering:grass_block_forest',
|
|
||||||
'immersive_weathering:grass_block_jungle',
|
|
||||||
'immersive_weathering:grass_block_lush_caves',
|
|
||||||
'immersive_weathering:grass_block_old_growth_spruce',
|
|
||||||
'immersive_weathering:grass_block_plains',
|
|
||||||
'immersive_weathering:grass_block_sunflower_plains',
|
|
||||||
'immersive_weathering:grass_block_swamp',
|
|
||||||
'immersive_weathering:grass_block_taiga',
|
|
||||||
'immersive_weathering:grass_block_wooded_badlands',
|
|
||||||
'immersive_weathering:hanging_roots',
|
|
||||||
'immersive_weathering:horn_coral',
|
|
||||||
'immersive_weathering:humus',
|
|
||||||
'immersive_weathering:icicle_growth',
|
|
||||||
'immersive_weathering:large_fern',
|
|
||||||
'immersive_weathering:magma',
|
|
||||||
'immersive_weathering:mycelium',
|
|
||||||
'immersive_weathering:podzol',
|
|
||||||
'immersive_weathering:red_sand_weathering',
|
|
||||||
'immersive_weathering:rooted_dirt',
|
|
||||||
'immersive_weathering:rooted_grass',
|
|
||||||
'immersive_weathering:sand_weathering',
|
|
||||||
'immersive_weathering:sapling',
|
|
||||||
'immersive_weathering:sapling_nether',
|
|
||||||
'immersive_weathering:silt',
|
|
||||||
'immersive_weathering:tall_grass',
|
|
||||||
'immersive_weathering:tall_seagrass',
|
|
||||||
'immersive_weathering:tube_coral',
|
|
||||||
'immersive_weathering:vertisol',
|
|
||||||
'immersive_weathering:warped_nylium',
|
|
||||||
])
|
|
||||||
}
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
import type { CollectionRegistry, ResourceType, SchemaRegistry } from '@mcschema/core'
|
|
||||||
import { BooleanNode, Case, ChoiceNode, ListNode, Mod, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core'
|
|
||||||
import type { VersionId } from '../services/Schemas.js'
|
|
||||||
|
|
||||||
|
|
||||||
const ID = 'lithostitched'
|
|
||||||
|
|
||||||
export function initLithostitched(schemas: SchemaRegistry, collections: CollectionRegistry, version: VersionId) {
|
|
||||||
const Reference = RawReference.bind(undefined, schemas)
|
|
||||||
const StringNode = RawStringNode.bind(undefined, collections)
|
|
||||||
|
|
||||||
const Tag = (id: Exclude<ResourceType, `$tag/${string}`>) =>
|
|
||||||
ChoiceNode(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: id, allowTag: true },
|
|
||||||
}),
|
|
||||||
change: (v: unknown) => {
|
|
||||||
if (
|
|
||||||
Array.isArray(v) &&
|
|
||||||
typeof v[0] === 'string' &&
|
|
||||||
!v[0].startsWith('#')
|
|
||||||
) {
|
|
||||||
return v[0]
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: ListNode(
|
|
||||||
StringNode({ validator: 'resource', params: { pool: id } })
|
|
||||||
),
|
|
||||||
change: (v: unknown) => {
|
|
||||||
if (typeof v === 'string' && !v.startsWith('#')) {
|
|
||||||
return [v]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{ choiceContext: 'tag' }
|
|
||||||
)
|
|
||||||
|
|
||||||
// Worldgen Modifiers
|
|
||||||
const MobCategorySpawnSettings = Mod(
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
collections.register(`${ID}:modifier_type`, [
|
|
||||||
'lithostitched:add_biome_spawns',
|
|
||||||
'lithostitched:add_features',
|
|
||||||
...(version === '1.20.5' || version === '1.21')
|
|
||||||
? ['lithostitched:add_pool_aliases']
|
|
||||||
: [],
|
|
||||||
'lithostitched:add_structure_set_entries',
|
|
||||||
'lithostitched:add_surface_rule',
|
|
||||||
'lithostitched:add_template_pool_elements',
|
|
||||||
'lithostitched:no_op',
|
|
||||||
'lithostitched:redirect_feature',
|
|
||||||
'lithostitched:remove_biome_spawns',
|
|
||||||
'lithostitched:remove_features',
|
|
||||||
'lithostitched:remove_structures_from_structure_set',
|
|
||||||
'lithostitched:replace_climate',
|
|
||||||
'lithostitched:replace_effects',
|
|
||||||
])
|
|
||||||
|
|
||||||
collections.register(`${ID}:modifier_predicate_type`, [
|
|
||||||
'lithostitched:all_of',
|
|
||||||
'lithostitched:any_of',
|
|
||||||
'lithostitched:mod_loaded',
|
|
||||||
'lithostitched:not',
|
|
||||||
'lithostitched:true',
|
|
||||||
])
|
|
||||||
|
|
||||||
schemas.register(`${ID}:worldgen_modifier`, Mod(ObjectNode({
|
|
||||||
type: StringNode({ validator: 'resource', params: { pool: `${ID}:modifier_type` as any } }),
|
|
||||||
predicate: Mod(Opt(Reference(`${ID}:modifier_predicate`)), {
|
|
||||||
enabled: () => version !== '1.21',
|
|
||||||
}),
|
|
||||||
[Switch]: [{ push: 'type' }],
|
|
||||||
[Case]: {
|
|
||||||
'lithostitched:add_biome_spawns': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
spawners: ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: MobCategorySpawnSettings,
|
|
||||||
change: (v: any) => v[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: ListNode(MobCategorySpawnSettings),
|
|
||||||
change: (v: any) => Array(v),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
'lithostitched:add_features': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
features: Tag('$worldgen/configured_feature'),
|
|
||||||
step: StringNode({ enum: 'decoration_step' }),
|
|
||||||
},
|
|
||||||
'lithostitched:add_pool_aliases': {
|
|
||||||
structure: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure' } }),
|
|
||||||
pool_aliases: Reference('pool_alias_binding'),
|
|
||||||
},
|
|
||||||
'lithostitched:add_structure_set_entries': {
|
|
||||||
structure_set: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure_set' } }),
|
|
||||||
entries: ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
structure: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure' } }),
|
|
||||||
weight: NumberNode({ integer: true, min: 1 }),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
},
|
|
||||||
'lithostitched:add_surface_rule': {
|
|
||||||
levels: ListNode(StringNode({ validator: 'resource', params: { pool: '$dimension' } })),
|
|
||||||
surface_rule: Reference('material_rule'),
|
|
||||||
},
|
|
||||||
'lithostitched:add_template_pool_elements': {
|
|
||||||
template_pool: StringNode({ validator: 'resource', params: { pool: '$worldgen/template_pool' } }),
|
|
||||||
elements: ListNode(
|
|
||||||
Reference('template_weighted_element')
|
|
||||||
),
|
|
||||||
},
|
|
||||||
'lithostitched:redirect_feature': {
|
|
||||||
placed_feature: StringNode({ validator: 'resource', params: { pool: '$worldgen/placed_feature' } }),
|
|
||||||
redirect_to: StringNode({ validator: 'resource', params: { pool: '$worldgen/configured_feature' } }),
|
|
||||||
},
|
|
||||||
'lithostitched:remove_biome_spawns': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
mobs: Tag('entity_type'),
|
|
||||||
},
|
|
||||||
'lithostitched:remove_features': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
features: Tag('$worldgen/configured_feature'),
|
|
||||||
step: StringNode({ enum: 'decoration_step' }),
|
|
||||||
},
|
|
||||||
'lithostitched:remove_structures_from_structure_set': {
|
|
||||||
structure_set: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure_set' } }),
|
|
||||||
structures: ListNode(
|
|
||||||
StringNode({ validator: 'resource', params: { pool: '$worldgen/structure' } })
|
|
||||||
),
|
|
||||||
},
|
|
||||||
'lithostitched:replace_climate': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
climate: ObjectNode({
|
|
||||||
temperature: NumberNode(),
|
|
||||||
downfall: NumberNode(),
|
|
||||||
has_precipitation: BooleanNode(),
|
|
||||||
temperature_modifier: Opt(StringNode({ enum: ['none', 'frozen'] })),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
'lithostitched:replace_effects': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
effects: ObjectNode({
|
|
||||||
sky_color: Opt(NumberNode({ color: true })),
|
|
||||||
fog_color: Opt(NumberNode({ color: true })),
|
|
||||||
water_color: Opt(NumberNode({ color: true })),
|
|
||||||
water_fog_color: Opt(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(StringNode()),
|
|
||||||
mood_sound: Opt(ObjectNode({
|
|
||||||
sound: StringNode(),
|
|
||||||
tick_delay: NumberNode({ integer: true }),
|
|
||||||
block_search_extent: NumberNode({ integer: true }),
|
|
||||||
offset: NumberNode(),
|
|
||||||
})),
|
|
||||||
additions_sound: Opt(ObjectNode({
|
|
||||||
sound: StringNode(),
|
|
||||||
tick_chance: NumberNode({ min: 0, max: 1 }),
|
|
||||||
})),
|
|
||||||
music: Opt(ObjectNode({
|
|
||||||
sound: StringNode(),
|
|
||||||
min_delay: NumberNode({ integer: true, min: 0 }),
|
|
||||||
max_delay: NumberNode({ integer: true, min: 0 }),
|
|
||||||
replace_current_music: BooleanNode(),
|
|
||||||
})),
|
|
||||||
particle: Opt(ObjectNode({
|
|
||||||
options: ObjectNode({
|
|
||||||
type: StringNode(),
|
|
||||||
}),
|
|
||||||
probability: NumberNode({ min: 0, max: 1 }),
|
|
||||||
})),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, { context: `${ID}.worldgen_modifier`, disableSwitchContext: true }), {
|
|
||||||
default: () => ({
|
|
||||||
type: `${ID}:add_features`,
|
|
||||||
biomes: '#minecraft:is_overworld',
|
|
||||||
features: 'example:ore_ruby',
|
|
||||||
step: 'underground_ores',
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:modifier_predicate`, ObjectNode({
|
|
||||||
type: StringNode({ validator: 'resource', params: { pool: `${ID}:modifier_predicate_type` as any } }),
|
|
||||||
[Switch]: [{ push: 'type' }],
|
|
||||||
[Case]: {
|
|
||||||
'lithostitched:all_of': {
|
|
||||||
predicates: ListNode(Reference(`${ID}:modifier_predicate`)),
|
|
||||||
},
|
|
||||||
'lithostitched:any_of': {
|
|
||||||
predicates: ListNode(Reference(`${ID}:modifier_predicate`)),
|
|
||||||
},
|
|
||||||
'lithostitched:mod_loaded': {
|
|
||||||
mod_id: StringNode(),
|
|
||||||
},
|
|
||||||
'lithostitched:not': {
|
|
||||||
predicate: Reference(`${ID}:modifier_predicate`),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
context: `${ID}.modifier_predicate`, disableSwitchContext: true,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
import type { CollectionRegistry, INode, ResourceType, SchemaRegistry } from '@mcschema/core'
|
|
||||||
import { BooleanNode, Case, ChoiceNode, ListNode, MapNode, Mod, NumberNode, ObjectNode, Opt, StringNode as RawStringNode, Switch } from '@mcschema/core'
|
|
||||||
import type { VersionId } from '../services/Schemas.js'
|
|
||||||
|
|
||||||
const ID = 'neoforge'
|
|
||||||
|
|
||||||
export function initNeoForge(schemas: SchemaRegistry, collections: CollectionRegistry, _version: VersionId) {
|
|
||||||
const StringNode = RawStringNode.bind(undefined, collections)
|
|
||||||
|
|
||||||
// Homogenous list (ref, list of refs, tag)
|
|
||||||
const Tag = (id: Exclude<ResourceType, `$tag/${string}`>) =>
|
|
||||||
ChoiceNode(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: id, allowTag: true },
|
|
||||||
}),
|
|
||||||
change: (v: unknown) => {
|
|
||||||
if (
|
|
||||||
Array.isArray(v) &&
|
|
||||||
typeof v[0] === 'string' &&
|
|
||||||
!v[0].startsWith('#')
|
|
||||||
) {
|
|
||||||
return v[0]
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: ListNode(
|
|
||||||
StringNode({ validator: 'resource', params: { pool: id } })
|
|
||||||
),
|
|
||||||
change: (v: unknown) => {
|
|
||||||
if (typeof v === 'string' && !v.startsWith('#')) {
|
|
||||||
return [v]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{ choiceContext: 'tag' }
|
|
||||||
)
|
|
||||||
|
|
||||||
// Spawner data
|
|
||||||
const MobCategorySpawnSettings = Mod(
|
|
||||||
ObjectNode({
|
|
||||||
type: StringNode({ validator: 'resource', params: { pool: 'entity_type' }}),
|
|
||||||
weight: NumberNode({ integer: true, min: 0 }),
|
|
||||||
minCount: NumberNode({ integer: true, min: 1 }),
|
|
||||||
maxCount: NumberNode({ integer: true, min: 1 }),
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
category: () => 'pool',
|
|
||||||
default: () => [
|
|
||||||
{
|
|
||||||
type: 'minecraft:bat',
|
|
||||||
weight: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generation step carving
|
|
||||||
const CarvingStep = StringNode({ enum: [ 'air', 'liquid' ] })
|
|
||||||
|
|
||||||
// Mob category
|
|
||||||
const MobCategory = StringNode({ enum: [ 'monster', 'creature', 'ambient', 'axolotls', 'underground_water_creature', 'water_creature', 'water_ambient', 'misc' ], additional: true })
|
|
||||||
|
|
||||||
// Biome modifier types
|
|
||||||
collections.register(`${ID}:biome_modifier_type`, [
|
|
||||||
'neoforge:none',
|
|
||||||
'neoforge:add_features',
|
|
||||||
'neoforge:remove_features',
|
|
||||||
'neoforge:add_spawns',
|
|
||||||
'neoforge:remove_spawns',
|
|
||||||
'neoforge:add_carvers',
|
|
||||||
'neoforge:remove_carvers',
|
|
||||||
'neoforge:add_spawn_costs',
|
|
||||||
'neoforge:remove_spawn_costs',
|
|
||||||
])
|
|
||||||
|
|
||||||
// Biome modifiers
|
|
||||||
schemas.register(`${ID}:biome_modifier`, Mod(
|
|
||||||
ObjectNode({
|
|
||||||
type: StringNode({ validator: 'resource', params: { pool: `${ID}:biome_modifier_type` as any }}),
|
|
||||||
[Switch]: [{ push: 'type' }],
|
|
||||||
[Case]: {
|
|
||||||
'neoforge:none': {},
|
|
||||||
'neoforge:add_features': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
features: Tag('$worldgen/placed_feature'),
|
|
||||||
step: StringNode({ enum: 'decoration_step' }),
|
|
||||||
},
|
|
||||||
'neoforge:remove_features': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
features: Tag('$worldgen/placed_feature'),
|
|
||||||
steps: Opt(ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode({ enum: 'decoration_step' }),
|
|
||||||
change: (v: any) => v[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: ListNode(StringNode({ enum: 'decoration_step' })),
|
|
||||||
change: (v: any) => Array(v),
|
|
||||||
},
|
|
||||||
])),
|
|
||||||
},
|
|
||||||
'neoforge:add_spawns': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
spawners: ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: MobCategorySpawnSettings,
|
|
||||||
change: (v: any) => v[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: MobCategorySpawnSettings,
|
|
||||||
change: (v: any) => Array(v),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
'neoforge:remove_spawns': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
entity_types: Tag('entity_type'),
|
|
||||||
},
|
|
||||||
'neoforge:add_carvers': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
carvers: Tag('$worldgen/configured_carver'),
|
|
||||||
step: CarvingStep,
|
|
||||||
},
|
|
||||||
'neoforge:remove_carvers': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
carvers: Tag('$worldgen/configured_carver'),
|
|
||||||
steps: Opt(ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: CarvingStep,
|
|
||||||
change: (v: any) => v[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: ListNode(CarvingStep),
|
|
||||||
change: (v: any) => Array(v),
|
|
||||||
},
|
|
||||||
])),
|
|
||||||
},
|
|
||||||
'neoforge:add_spawn_costs': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
entity_types: Tag('entity_type'),
|
|
||||||
spawn_cost: ObjectNode({
|
|
||||||
energy_budget: NumberNode(),
|
|
||||||
charge: NumberNode(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
'neoforge:remove_spawn_costs': {
|
|
||||||
biomes: Tag('$worldgen/biome'),
|
|
||||||
entity_types: Tag('entity_type'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, { context: `${ID}.biome_modifier`, disableSwitchContext: true }),
|
|
||||||
{
|
|
||||||
default: () => ({
|
|
||||||
type: `${ID}:add_features`,
|
|
||||||
biomes: '#minecraft:is_overworld',
|
|
||||||
features: 'minecraft:ore_iron_small',
|
|
||||||
step: 'underground_ores',
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
))
|
|
||||||
|
|
||||||
// Structure modifier types
|
|
||||||
collections.register(`${ID}:structure_modifier_type`, [
|
|
||||||
'neoforge:none',
|
|
||||||
'neoforge:add_spawns',
|
|
||||||
'neoforge:remove_spawns',
|
|
||||||
'neoforge:clear_spawns',
|
|
||||||
])
|
|
||||||
|
|
||||||
// Structure modifiers
|
|
||||||
schemas.register(`${ID}:structure_modifier`, Mod(
|
|
||||||
ObjectNode({
|
|
||||||
type: StringNode({ validator: 'resource', params: { pool: `${ID}:structure_modifier_type` as any }}),
|
|
||||||
[Switch]: [{ push: 'type' }],
|
|
||||||
[Case]: {
|
|
||||||
'neoforge:none': {},
|
|
||||||
'neoforge:add_spawns': {
|
|
||||||
structures: Tag('$worldgen/structure'),
|
|
||||||
spawners: ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: MobCategorySpawnSettings,
|
|
||||||
change: (v: any) => v[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: MobCategorySpawnSettings,
|
|
||||||
change: (v: any) => Array(v),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
'neoforge:remove_spawns': {
|
|
||||||
structures: Tag('$worldgen/structure'),
|
|
||||||
entity_types: Tag('entity_type'),
|
|
||||||
},
|
|
||||||
'neoforge:clear_spawns': {
|
|
||||||
structures: Tag('$worldgen/structure'),
|
|
||||||
categories: ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: MobCategory,
|
|
||||||
change: (v: any) => v[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
node: MobCategory,
|
|
||||||
change: (v: any) => Array(v),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, { context: `${ID}.structure_modifier`, disableSwitchContext: true }),
|
|
||||||
{
|
|
||||||
default: () => ({
|
|
||||||
type: `${ID}:add_spawns`,
|
|
||||||
structures: '#minecraft:village',
|
|
||||||
spawners: {
|
|
||||||
type: 'minecraft:bat',
|
|
||||||
weight: 1,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
))
|
|
||||||
|
|
||||||
// Data maps
|
|
||||||
createDataMap(schemas, collections, 'compostables', 'item', ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
node: NumberNode({
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
}),
|
|
||||||
change: (v: any) => v?.chance,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: ObjectNode({
|
|
||||||
chance: NumberNode({
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
}),
|
|
||||||
can_villager_compost: Opt(BooleanNode()),
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
chance: v,
|
|
||||||
can_villager_compost: false,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]), (values) => values['minecraft:apple'] = {
|
|
||||||
chance: 1,
|
|
||||||
can_villager_compost: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
createDataMap(schemas, collections, 'furnace_fuels', 'item', ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
node: NumberNode({
|
|
||||||
min: 1,
|
|
||||||
integer: true,
|
|
||||||
}),
|
|
||||||
change: (v: any) => v?.burn_time,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: ObjectNode({
|
|
||||||
burn_time: NumberNode({
|
|
||||||
min: 1,
|
|
||||||
integer: true,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
burn_time: v,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]), (values) => values['minecraft:chest'] = {
|
|
||||||
burn_time: 300,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
createDataMap(schemas, collections, 'monster_room_mobs', 'entity_type', ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
node: NumberNode({
|
|
||||||
min: 0,
|
|
||||||
integer: true,
|
|
||||||
}),
|
|
||||||
change: (v: any) => v?.weight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: ObjectNode({
|
|
||||||
weight: NumberNode({
|
|
||||||
min: 0,
|
|
||||||
integer: true,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
weight: v,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]), (values) => values['minecraft:bat'] = {
|
|
||||||
weight: 5,
|
|
||||||
})
|
|
||||||
createDataMap(schemas, collections, 'oxidizables', 'block', ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: 'block' },
|
|
||||||
}),
|
|
||||||
change: (v: any) => v?.next_oxidation_stage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: ObjectNode({
|
|
||||||
next_oxidation_stage: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: 'block' },
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
next_oxidation_stage: v,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]), (values) => values['minecraft:grass_block'] = {
|
|
||||||
next_oxidation_stage: 'minecraft:dirt',
|
|
||||||
})
|
|
||||||
createDataMap(schemas, collections, 'parrot_imitations', 'entity_type', ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: 'sound_event' as any },
|
|
||||||
}),
|
|
||||||
change: (v: any) => v?.sound,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: ObjectNode({
|
|
||||||
sound: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: 'sound_event' as any },
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
sound: v,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]), (values) => values['minecraft:allay'] = {
|
|
||||||
sound: 'minecraft:entity.allay.ambient_without_item',
|
|
||||||
})
|
|
||||||
createDataMap(schemas, collections, 'raid_hero_gifts', 'villager_profession', ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: '$loot_table' },
|
|
||||||
}),
|
|
||||||
change: (v: any) => v?.loot_table,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: ObjectNode({
|
|
||||||
loot_table: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: '$loot_table' },
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
loot_table: v,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]), (values) => values['minecraft:cleric'] = {
|
|
||||||
loot_table: 'minecraft:empty',
|
|
||||||
})
|
|
||||||
createDataMap(schemas, collections, 'vibration_frequencies', 'game_event', ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
node: NumberNode({
|
|
||||||
min: 1,
|
|
||||||
max: 15,
|
|
||||||
integer: true,
|
|
||||||
}),
|
|
||||||
change: (v: any) => v?.frequency,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: ObjectNode({
|
|
||||||
frequency: NumberNode({
|
|
||||||
min: 1,
|
|
||||||
max: 15,
|
|
||||||
integer: true,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
frequency: v,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]), (values) => values['minecraft:block_change'] = {
|
|
||||||
frequency: 5,
|
|
||||||
})
|
|
||||||
createDataMap(schemas, collections, 'waxables', 'block', ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: 'block' },
|
|
||||||
}),
|
|
||||||
change: (v: any) => v?.waxed,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: ObjectNode({
|
|
||||||
waxed: StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: 'block' },
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
waxed: v,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]), (values) => values['minecraft:dirt'] = {
|
|
||||||
waxed: 'minecraft:coarse_dirt',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDataMap(schemas: SchemaRegistry, collections: CollectionRegistry, dataMap: string, registry: ResourceType, valueNode: INode<any>, def: (values: any) => void) {
|
|
||||||
const StringNode = RawStringNode.bind(undefined, collections)
|
|
||||||
|
|
||||||
// Ref or tag
|
|
||||||
const Tag = StringNode({
|
|
||||||
validator: 'resource',
|
|
||||||
params: { pool: registry, allowTag: true },
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create data map
|
|
||||||
schemas.register(`${ID}:data_map_${dataMap}`, Mod(
|
|
||||||
ObjectNode({
|
|
||||||
replace: Opt(BooleanNode()),
|
|
||||||
values: MapNode(
|
|
||||||
Tag,
|
|
||||||
ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'direct',
|
|
||||||
match: () => true,
|
|
||||||
node: valueNode,
|
|
||||||
change: (v: any) => v?.value,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'replaceable',
|
|
||||||
match: (v: any) => typeof v === 'object' && v?.value !== undefined,
|
|
||||||
priority: 1,
|
|
||||||
node: ObjectNode({
|
|
||||||
replace: Opt(BooleanNode()),
|
|
||||||
value: valueNode,
|
|
||||||
}),
|
|
||||||
change: (v: any) => ({
|
|
||||||
replace: true,
|
|
||||||
value: v,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
remove: Opt(ListNode(Tag)),
|
|
||||||
}, {context: `${ID}.data_map_${dataMap}`, disableSwitchContext: true}),
|
|
||||||
{
|
|
||||||
default: () => {
|
|
||||||
const result = {
|
|
||||||
values: {},
|
|
||||||
}
|
|
||||||
def(result.values)
|
|
||||||
|
|
||||||
return result
|
|
||||||
},
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
@@ -1,442 +0,0 @@
|
|||||||
import type { CollectionRegistry, SchemaRegistry } from '@mcschema/core'
|
|
||||||
import { BooleanNode, Case, ListNode, MapNode, Mod, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core'
|
|
||||||
|
|
||||||
const ID = 'obsidian'
|
|
||||||
|
|
||||||
export function initObsidian(schemas: SchemaRegistry, collections: CollectionRegistry) {
|
|
||||||
const Reference = RawReference.bind(undefined, schemas)
|
|
||||||
const StringNode = RawStringNode.bind(undefined, collections)
|
|
||||||
|
|
||||||
// ITEMS
|
|
||||||
schemas.register(`${ID}:item`, Mod(ObjectNode({
|
|
||||||
information: Opt(Reference(`${ID}:item_information`)),
|
|
||||||
display: Opt(ObjectNode({
|
|
||||||
model: Opt(Reference(`${ID}:model`)),
|
|
||||||
item_model: Opt(Reference(`${ID}:model`)),
|
|
||||||
lore: Opt(ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
text: Reference(`${ID}:name_information`),
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
})),
|
|
||||||
use_action: Opt(ObjectNode({
|
|
||||||
action: Opt(StringNode({ enum: ['none', 'eat', 'drink', 'block', 'bow', 'spear', 'crossbow', 'spyglass'] })),
|
|
||||||
right_click_action: Opt(StringNode({ enum: ['open_gui', 'run_command', 'open_url']})) ,
|
|
||||||
command: Opt(StringNode()),
|
|
||||||
url: Opt(StringNode()),
|
|
||||||
gui_size: Opt(NumberNode({ integer: true, min: 1, max: 6 })),
|
|
||||||
gui_title: Opt(Reference(`${ID}:name_information`)),
|
|
||||||
})),
|
|
||||||
}, { context: `${ID}:item` }), {
|
|
||||||
default: () => ({}),
|
|
||||||
}))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:item_information`, ObjectNode({
|
|
||||||
rarity: Opt(StringNode({ enum: ['common', 'uncommon', 'rare', 'epic']})),
|
|
||||||
creative_tab: Opt(StringNode()),
|
|
||||||
max_stack_size: Opt(NumberNode({ integer: true, min: 1 })),
|
|
||||||
name: Opt(Reference(`${ID}:name_information`)),
|
|
||||||
has_enchantment_glint: Opt(BooleanNode()),
|
|
||||||
is_enchantable: Opt(BooleanNode()),
|
|
||||||
enchantability: Opt(NumberNode({ integer: true })),
|
|
||||||
use_duration: Opt(NumberNode({ integer: true })),
|
|
||||||
can_place_block: Opt(BooleanNode()),
|
|
||||||
placable_block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })),
|
|
||||||
wearable: Opt(BooleanNode()),
|
|
||||||
default_color: Opt(NumberNode({ color: true })),
|
|
||||||
wearable_slot: Opt(StringNode()),
|
|
||||||
custom_render_mode: Opt(BooleanNode()),
|
|
||||||
render_mode_models: Opt(ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
model: Reference('model_identifier'),
|
|
||||||
modes: ListNode(StringNode()),
|
|
||||||
})
|
|
||||||
)),
|
|
||||||
}, { context: `${ID}:item_information` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:item_model`, ObjectNode({
|
|
||||||
textures: Opt(MapNode(
|
|
||||||
StringNode(),
|
|
||||||
StringNode({ validator: 'resource', params: { pool: '$texture' } }),
|
|
||||||
)),
|
|
||||||
parent: StringNode({ validator: 'resource', params: { pool: '$model'} }),
|
|
||||||
}, { context: `${ID}:item_model` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:block`, Mod(ObjectNode({
|
|
||||||
block_type: Opt(StringNode({ enum: `${ID}:block_type`})),
|
|
||||||
information: Opt(Reference(`${ID}:block_information`)),
|
|
||||||
display: Opt(ObjectNode({
|
|
||||||
model: Opt(Reference(`${ID}:model`)),
|
|
||||||
item_model: Opt(Reference(`${ID}:model`)),
|
|
||||||
block_model: Opt(Reference(`${ID}:model`)),
|
|
||||||
lore: ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
text: Reference(`${ID}:item_name_information`),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
additional_information: Opt(ObjectNode({
|
|
||||||
extraBlocksName: Opt(StringNode()),
|
|
||||||
slab: Opt(BooleanNode()),
|
|
||||||
stairs: Opt(BooleanNode()),
|
|
||||||
walls: Opt(BooleanNode()),
|
|
||||||
fence: Opt(BooleanNode()),
|
|
||||||
fenceGate: Opt(BooleanNode()),
|
|
||||||
button: Opt(BooleanNode()),
|
|
||||||
pressurePlate: Opt(BooleanNode()),
|
|
||||||
door: Opt(BooleanNode()),
|
|
||||||
trapdoor: Opt(BooleanNode()),
|
|
||||||
path: Opt(BooleanNode()),
|
|
||||||
lantern: Opt(BooleanNode()),
|
|
||||||
barrel: Opt(BooleanNode()),
|
|
||||||
leaves: Opt(BooleanNode()),
|
|
||||||
plant: Opt(BooleanNode()),
|
|
||||||
chains: Opt(BooleanNode()),
|
|
||||||
cake_like: Opt(BooleanNode()),
|
|
||||||
waterloggable: Opt(BooleanNode()),
|
|
||||||
dyable: Opt(BooleanNode()),
|
|
||||||
defaultColor: Opt(NumberNode({ color: true })),
|
|
||||||
sittable: Opt(BooleanNode()),
|
|
||||||
isConvertible: Opt(BooleanNode()),
|
|
||||||
convertible: Opt(ObjectNode({
|
|
||||||
drops_item: Opt(BooleanNode()),
|
|
||||||
reversible: Opt(BooleanNode()),
|
|
||||||
parent_block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })),
|
|
||||||
transformed_block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })),
|
|
||||||
dropped_item: Opt(StringNode({ validator: 'resource', params: { pool: 'item' } })),
|
|
||||||
sound: Opt(StringNode()),
|
|
||||||
conversionItem: Opt(ObjectNode({
|
|
||||||
item: StringNode({ validator: 'resource', params: { pool: 'item' } }),
|
|
||||||
tag: StringNode({ validator: 'resource', params: { pool: '$tag/item' } }),
|
|
||||||
})),
|
|
||||||
reversalItem: Opt(ObjectNode({
|
|
||||||
item: StringNode({ validator: 'resource', params: { pool: 'item' } }),
|
|
||||||
tag: StringNode({ validator: 'resource', params: { pool: '$tag/item' } }),
|
|
||||||
})),
|
|
||||||
})),
|
|
||||||
})),
|
|
||||||
functions: Opt(ObjectNode({
|
|
||||||
random_tick: Opt(Reference(`${ID}:function`)),
|
|
||||||
scheduled_tick: Opt(Reference(`${ID}:function`)),
|
|
||||||
random_display_tick: Opt(Reference(`${ID}:function`)),
|
|
||||||
place: Opt(Reference(`${ID}:function`)),
|
|
||||||
break: Opt(Reference(`${ID}:function`)),
|
|
||||||
use: Opt(Reference(`${ID}:function`)),
|
|
||||||
walk_on: Opt(Reference(`${ID}:function`)),
|
|
||||||
})),
|
|
||||||
ore_information: Opt(ObjectNode({
|
|
||||||
test_type: Opt(StringNode({ enum: ['tag', 'always', 'block_match', 'block_state_match', 'random_block_match', 'random_block_state_match']})),
|
|
||||||
target_state: Opt(ObjectNode({
|
|
||||||
block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' }})),
|
|
||||||
tag: Opt(StringNode({ validator: 'resource', params: { pool: '$tag/block' } })),
|
|
||||||
properties: Opt(MapNode(
|
|
||||||
StringNode(),
|
|
||||||
StringNode(),
|
|
||||||
)),
|
|
||||||
probability: Opt(NumberNode({ min: 0, max: 1 })),
|
|
||||||
})),
|
|
||||||
triangleRange: Opt(BooleanNode()),
|
|
||||||
plateau: Opt(NumberNode({ integer: true, min: 0 })),
|
|
||||||
spawnPredicate: Opt(StringNode({ enum: ['built_in', 'vanilla', 'overworld', 'the_nether', 'the_end', 'categories', 'biomes'] })),
|
|
||||||
biomeCategories: ListNode(StringNode()),
|
|
||||||
biomes: ListNode(
|
|
||||||
StringNode({ validator: 'resource', params: { pool: '$worldgen/biome' }})
|
|
||||||
),
|
|
||||||
size: Opt(NumberNode({ integer: true })),
|
|
||||||
chance: Opt(NumberNode({ integer: true, min: 1 })),
|
|
||||||
discardOnAirChance: Opt(NumberNode({ min: 0, max: 1 })),
|
|
||||||
topOffset: Reference(`${ID}:block_y_offset`),
|
|
||||||
bottomOffset: Reference(`${ID}:block_y_offset`),
|
|
||||||
})),
|
|
||||||
food_information: Opt(ObjectNode({
|
|
||||||
hunger: Opt(NumberNode({ integer: true, min: 0 })),
|
|
||||||
saturation: Opt(NumberNode({ integer: true, min: 0 })),
|
|
||||||
effects: ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
effect: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }),
|
|
||||||
duration: Opt(NumberNode({ integer: true, min: 0 })),
|
|
||||||
amplifier: Opt(NumberNode({ integer: true, min: 0 })),
|
|
||||||
chance: Opt(NumberNode({ integer: true, min: 0, max: 1 })),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
cake_slices: Opt(NumberNode({integer: true, min: 1})),
|
|
||||||
campfire_properties: Opt(ObjectNode({
|
|
||||||
emits_particles: Opt(BooleanNode()),
|
|
||||||
fire_damage: Opt(NumberNode({ integer: true })),
|
|
||||||
luminance: Opt(NumberNode({ integer: true })),
|
|
||||||
})),
|
|
||||||
particle_type: Opt(StringNode()),
|
|
||||||
can_plant_on: Opt(ListNode(
|
|
||||||
StringNode({ validator: 'resource', params: { pool: 'block' } })
|
|
||||||
)),
|
|
||||||
growable: Opt(ObjectNode({
|
|
||||||
min_age: Opt(NumberNode({ integer: true, min: 1 })),
|
|
||||||
max_age: Opt(NumberNode({ integer: true })),
|
|
||||||
})),
|
|
||||||
oxidizable_properties: Opt(ObjectNode({
|
|
||||||
stages: Opt(ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
can_be_waxed: Opt(BooleanNode()),
|
|
||||||
stairs: Opt(BooleanNode()),
|
|
||||||
slab: Opt(BooleanNode()),
|
|
||||||
blocks: Opt(ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
name: Opt(Reference(`${ID}:name_information`)),
|
|
||||||
display: Opt(Reference(`${ID}:model`)),
|
|
||||||
})
|
|
||||||
)),
|
|
||||||
})
|
|
||||||
)),
|
|
||||||
})),
|
|
||||||
events: Opt(ListNode(Reference(`${ID}:event`))),
|
|
||||||
drop_information: Opt(ObjectNode({
|
|
||||||
drops: Opt(ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
name: StringNode({ validator: 'resource', params: { pool: 'item' } }),
|
|
||||||
drops_if_silk_touch: Opt(BooleanNode()),
|
|
||||||
})
|
|
||||||
)),
|
|
||||||
survives_explosion: Opt(BooleanNode()),
|
|
||||||
xp_drop_amount: Opt(NumberNode({ integer: true })),
|
|
||||||
})),
|
|
||||||
is_multi_block: Opt(BooleanNode()),
|
|
||||||
multiblock_information: Opt(ObjectNode({
|
|
||||||
width: Opt(NumberNode({ integer: true })),
|
|
||||||
height: Opt(NumberNode({ integer: true })),
|
|
||||||
})),
|
|
||||||
placable_feature: Opt(StringNode({ validator: 'resource', params: { pool: '$worldgen/configured_feature' } })),
|
|
||||||
}, { context: `${ID}:block` }), {
|
|
||||||
default: () => ({}),
|
|
||||||
}))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:block_information`, ObjectNode({
|
|
||||||
rarity: Opt(StringNode({ enum: ['common', 'uncommon', 'rare', 'epic']})),
|
|
||||||
creative_tab: Opt(StringNode()),
|
|
||||||
collidable: Opt(BooleanNode()),
|
|
||||||
max_stack_size: Opt(NumberNode({ integer: true, min: 1 })),
|
|
||||||
name: Opt(Reference(`${ID}:name_information`)),
|
|
||||||
vanilla_sound_group: Opt(StringNode()),
|
|
||||||
custom_sound_group: Opt(Reference(`${ID}:sound_group`)),
|
|
||||||
vanilla_material: Opt(StringNode()),
|
|
||||||
custom_material: Opt(Reference(`${ID}:material`)),
|
|
||||||
has_glint: Opt(BooleanNode()),
|
|
||||||
is_enchantable: Opt(BooleanNode()),
|
|
||||||
enchantability: Opt(NumberNode({ integer: true })),
|
|
||||||
fireproof: Opt(BooleanNode()),
|
|
||||||
translucent: Opt(BooleanNode()),
|
|
||||||
dynamic_boundaries: Opt(BooleanNode()),
|
|
||||||
has_item: Opt(BooleanNode()),
|
|
||||||
dyeable: Opt(BooleanNode()),
|
|
||||||
defaultColor: Opt(NumberNode({ color: true })),
|
|
||||||
wearable: Opt(BooleanNode()),
|
|
||||||
wearble_slot: Opt(StringNode()),
|
|
||||||
custom_render_mode: Opt(BooleanNode()),
|
|
||||||
render_mode_models: Opt(ListNode(
|
|
||||||
ObjectNode({
|
|
||||||
model: Reference('model_identifier'),
|
|
||||||
modes: ListNode(StringNode()),
|
|
||||||
})
|
|
||||||
)),
|
|
||||||
}, { context: `${ID}:block_information` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:block_y_offset`, ObjectNode({
|
|
||||||
type: Opt(StringNode({enum: ['fixed', 'above_bottom', 'below_top', 'bottom', 'top']})),
|
|
||||||
offset: Opt(NumberNode({ integer: true })),
|
|
||||||
}))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:function`, ObjectNode({
|
|
||||||
predicate: Opt(Reference(`${ID}:predicate`)),
|
|
||||||
function_type: StringNode({ enum: ['NONE', 'REQUIRES_SHIFTING', 'REQUIRES_ITEM', 'REQUIRES_SHIFTING_AND_ITEM'] }),
|
|
||||||
[Switch]: [{ push: 'function_type' }],
|
|
||||||
[Case]: {
|
|
||||||
NONE: {},
|
|
||||||
REQUIRES_SHIFTING: {},
|
|
||||||
REQUIRES_ITEM: { item: StringNode({ validator: 'resource', params: { pool: 'item' } }) },
|
|
||||||
REQUIRES_SHIFTING_AND_ITEM: { item: StringNode({ validator: 'resource', params: { pool: 'item' } }) },
|
|
||||||
},
|
|
||||||
function_file: StringNode(),
|
|
||||||
}, { context: `${ID}:function` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:predicate`, ObjectNode({
|
|
||||||
predicate_type: Opt(StringNode({ enum: ['ALWAYS', 'EQUALS', 'NOT_EQUALS', 'CONTAINS', 'NOT_CONTAINS', 'BEGINS_WITH', 'ENDS_WITH', 'REGEX'] })),
|
|
||||||
[Switch]: [{ push: 'predicate_type' }],
|
|
||||||
[Case]: {
|
|
||||||
ALWAYS: {},
|
|
||||||
EQUALS: { left: StringNode(), right: StringNode() },
|
|
||||||
NOT_EQUALS: { left: StringNode(), right: StringNode() },
|
|
||||||
CONTAINS: { left: StringNode(), right: StringNode() },
|
|
||||||
NOT_CONTAINS: { left: StringNode(), right: StringNode() },
|
|
||||||
BEGINS_WITH: { left: StringNode(), right: StringNode() },
|
|
||||||
ENDS_WITH: { left: StringNode(), right: StringNode() },
|
|
||||||
REGEX: { left: StringNode(), right: StringNode() },
|
|
||||||
},
|
|
||||||
}, { context: `${ID}:predicate` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:model`, ObjectNode({
|
|
||||||
textures: Opt(MapNode(
|
|
||||||
StringNode(),
|
|
||||||
StringNode({ validator: 'resource', params: { pool: '$texture' } }),
|
|
||||||
)),
|
|
||||||
parent: StringNode({ validator: 'resource', params: { pool: '$model'} }),
|
|
||||||
}, { context: `${ID}:model` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:sound_group`, ObjectNode({
|
|
||||||
id: Opt(StringNode()),
|
|
||||||
break_sound: Opt(StringNode()),
|
|
||||||
step_sound: Opt(StringNode()),
|
|
||||||
place_sound: Opt(StringNode()),
|
|
||||||
hit_sound: Opt(StringNode()),
|
|
||||||
}, { context: `${ID}:sound_group` }))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:material`, ObjectNode({
|
|
||||||
id: Opt(StringNode()),
|
|
||||||
map_color: Opt(StringNode()),
|
|
||||||
allows_movement: Opt(BooleanNode()),
|
|
||||||
burnable: Opt(BooleanNode()),
|
|
||||||
liquid: Opt(BooleanNode()),
|
|
||||||
allows_light: Opt(BooleanNode()),
|
|
||||||
replacable: Opt(BooleanNode()),
|
|
||||||
solid: Opt(BooleanNode()),
|
|
||||||
piston_behaviour: Opt(StringNode({ enum: ['NORMAL', 'DESTROY', 'BLOCK', 'IGNORE', 'PUSH_ONLY'] })),
|
|
||||||
}, { context: `${ID}:material` }))
|
|
||||||
|
|
||||||
// COMMON
|
|
||||||
schemas.register(`${ID}:name_information`, ObjectNode({
|
|
||||||
id: StringNode(),
|
|
||||||
text: Opt(StringNode()),
|
|
||||||
type: Opt(StringNode({ enum: ['literal', 'translatable'] })),
|
|
||||||
translated: Opt(MapNode(
|
|
||||||
StringNode(),
|
|
||||||
StringNode(),
|
|
||||||
)),
|
|
||||||
color: Opt(StringNode()),
|
|
||||||
formatting: Opt(ListNode(StringNode())),
|
|
||||||
}))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:display_information`, ObjectNode({
|
|
||||||
// TODO
|
|
||||||
}))
|
|
||||||
|
|
||||||
schemas.register(`${ID}:event`, ObjectNode({
|
|
||||||
activations: StringNode({enum: ['use', 'shift_use', 'collide', 'walk_on']}),
|
|
||||||
predicate: Opt(StringNode({ validator: 'resource', params: { pool: '$predicate' } })),
|
|
||||||
type: StringNode({ enum: ['give_effect', 'damage', 'decrement_stack', 'kill', 'play_sound', 'remove_effect', 'run_command', 'set_block', 'set_block_at_pos', 'set_block_property', 'spawn_loot', 'spawn_entity'] }),
|
|
||||||
[Switch]: [{ push: 'type' }],
|
|
||||||
[Case]: {
|
|
||||||
give_effect: {
|
|
||||||
amplifier: NumberNode({ integer: true }),
|
|
||||||
duration: NumberNode(),
|
|
||||||
effect: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }),
|
|
||||||
target: StringNode(),
|
|
||||||
},
|
|
||||||
damage: {
|
|
||||||
amount: NumberNode({ integer: true }),
|
|
||||||
target: StringNode(),
|
|
||||||
damage_type: StringNode(),
|
|
||||||
},
|
|
||||||
decrement_stack: {
|
|
||||||
amount: NumberNode({ integer: true }),
|
|
||||||
},
|
|
||||||
kill: {
|
|
||||||
target: StringNode(),
|
|
||||||
},
|
|
||||||
remove_effect: {
|
|
||||||
effect: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }),
|
|
||||||
target: StringNode(),
|
|
||||||
},
|
|
||||||
run_command: {
|
|
||||||
command: StringNode(),
|
|
||||||
target: StringNode(),
|
|
||||||
},
|
|
||||||
set_block: {
|
|
||||||
block: StringNode({ validator: 'resource', params: { pool: 'block' } }),
|
|
||||||
x_pos: NumberNode(),
|
|
||||||
y_pos: NumberNode(),
|
|
||||||
z_pos: NumberNode(),
|
|
||||||
},
|
|
||||||
set_block_at_pos: {
|
|
||||||
block: StringNode({ validator: 'resource', params: { pool: 'block' } }),
|
|
||||||
x_pos: NumberNode(),
|
|
||||||
y_pos: NumberNode(),
|
|
||||||
z_pos: NumberNode(),
|
|
||||||
},
|
|
||||||
set_block_property: {
|
|
||||||
block: StringNode({ validator: 'resource', params: { pool: 'block' } }),
|
|
||||||
property: StringNode(),
|
|
||||||
value: StringNode(),
|
|
||||||
},
|
|
||||||
spawn_loot: {
|
|
||||||
loot_table: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }),
|
|
||||||
x_pos: NumberNode(),
|
|
||||||
y_pos: NumberNode(),
|
|
||||||
z_pos: NumberNode(),
|
|
||||||
},
|
|
||||||
spawn_entity: {
|
|
||||||
entity_type: StringNode({ validator: 'resource', params: { pool: 'entity_type' } }),
|
|
||||||
x_pos: NumberNode(),
|
|
||||||
y_pos: NumberNode(),
|
|
||||||
z_pos: NumberNode(),
|
|
||||||
amount: NumberNode({ integer: true }),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
|
|
||||||
// COLLECTIONS
|
|
||||||
collections.register(`${ID}:block_type`, [
|
|
||||||
'BLOCK',
|
|
||||||
'HORIZONTAL_FACING_BLOCK',
|
|
||||||
'ROTATABLE_BLOCK',
|
|
||||||
'CAMPFIRE',
|
|
||||||
'STAIRS',
|
|
||||||
'SLAB',
|
|
||||||
'WALL',
|
|
||||||
'FENCE',
|
|
||||||
'FENCE_GATE',
|
|
||||||
'CAKE',
|
|
||||||
'BED',
|
|
||||||
'TRAPDOOR',
|
|
||||||
'METAL_DOOR',
|
|
||||||
'WOODEN_DOOR',
|
|
||||||
'LOG',
|
|
||||||
'STEM',
|
|
||||||
'WOOD',
|
|
||||||
'OXIDIZING_BLOCK',
|
|
||||||
'PLANT',
|
|
||||||
'PILLAR',
|
|
||||||
'HORIZONTAL_FACING_PLANT',
|
|
||||||
'SAPLING',
|
|
||||||
'TORCH',
|
|
||||||
'BEEHIVE',
|
|
||||||
'LEAVES',
|
|
||||||
'LADDER',
|
|
||||||
'PATH',
|
|
||||||
'WOODEN_BUTTON',
|
|
||||||
'STONE_BUTTON',
|
|
||||||
'DOUBLE_PLANT',
|
|
||||||
'HORIZONTAL_FACING_DOUBLE_PLANT',
|
|
||||||
'HANGING_DOUBLE_LEAVES',
|
|
||||||
'EIGHT_DIRECTIONAL_BLOCK',
|
|
||||||
'LANTERN',
|
|
||||||
'CHAIN',
|
|
||||||
'PANE',
|
|
||||||
'DYEABLE',
|
|
||||||
'LOOM',
|
|
||||||
'GRINDSTONE',
|
|
||||||
'CRAFTING_TABLE',
|
|
||||||
'PISTON',
|
|
||||||
'NOTEBLOCK',
|
|
||||||
'JUKEBOX',
|
|
||||||
'SMOKER',
|
|
||||||
'FURNACE',
|
|
||||||
'BLAST_FURNACE',
|
|
||||||
'LECTERN',
|
|
||||||
'FLETCHING_TABLE',
|
|
||||||
'BARREL',
|
|
||||||
'COMPOSTER',
|
|
||||||
'RAILS',
|
|
||||||
'CARTOGRAPHY_TABLE',
|
|
||||||
'CARPET',
|
|
||||||
])
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
import type { CollectionRegistry, SchemaRegistry } from '@mcschema/core'
|
|
||||||
import { Case, ListNode, Mod, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core'
|
|
||||||
|
|
||||||
export function initOhTheTreesYoullGrow(schemas: SchemaRegistry, collections: CollectionRegistry) {
|
|
||||||
const Reference = RawReference.bind(undefined, schemas)
|
|
||||||
const StringNode = RawStringNode.bind(undefined, collections)
|
|
||||||
|
|
||||||
collections.register('ohthetreesyoullgrow:feature', [
|
|
||||||
'ohthetreesyoullgrow:tree_from_nbt_v1',
|
|
||||||
])
|
|
||||||
|
|
||||||
const BlockSet = ListNode(
|
|
||||||
StringNode({ validator: 'resource', params: { pool: 'block' } })
|
|
||||||
)
|
|
||||||
|
|
||||||
schemas.register('ohthetreesyoullgrow:configured_feature', Mod(ObjectNode({
|
|
||||||
type: StringNode({ validator: 'resource', params: { pool: 'ohthetreesyoullgrow:feature' as any } }),
|
|
||||||
config: ObjectNode({
|
|
||||||
[Switch]: ['pop', { push: 'type' }],
|
|
||||||
[Case]: {
|
|
||||||
'ohthetreesyoullgrow:tree_from_nbt_v1': {
|
|
||||||
base_location: StringNode({ validator: 'resource', params: { pool: '$structure' } }),
|
|
||||||
canopy_location: StringNode({ validator: 'resource', params: { pool: '$structure' } }),
|
|
||||||
can_grow_on_filter: Reference('block_predicate_worldgen'),
|
|
||||||
can_leaves_place_filter: Reference('block_predicate_worldgen'),
|
|
||||||
decorators: Opt(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' })
|
|
||||||
)),
|
|
||||||
height: Reference('int_provider'),
|
|
||||||
leaves_provider: Reference('block_state_provider'),
|
|
||||||
leaves_target: BlockSet,
|
|
||||||
log_provider: Reference('block_state_provider'),
|
|
||||||
log_target: BlockSet,
|
|
||||||
max_log_depth: Opt(NumberNode({ integer: true })),
|
|
||||||
place_from_nbt: BlockSet,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, { disableSwitchContext: true }),
|
|
||||||
}, { context: 'ohthetreesyoullgrow.configured_feature' }), {
|
|
||||||
default: () => ({
|
|
||||||
type: 'ohthetreesyoullgrow:tree_from_nbt_v1',
|
|
||||||
config: {
|
|
||||||
can_grow_on_filter: {
|
|
||||||
type: 'minecraft:matching_block_tag',
|
|
||||||
tag: 'minecraft:dirt',
|
|
||||||
},
|
|
||||||
can_leaves_place_filter: {
|
|
||||||
type: 'minecraft:replaceable',
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: 'minecraft:uniform',
|
|
||||||
value: {
|
|
||||||
min_inclusive: 5,
|
|
||||||
max_inclusive: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
leaves_provider: {
|
|
||||||
type: 'minecraft:simple_state_provider',
|
|
||||||
state: {
|
|
||||||
Name: 'minecraft:acacia_leaves',
|
|
||||||
Properties: {
|
|
||||||
distance: '7',
|
|
||||||
persistent: 'false',
|
|
||||||
waterlogged: 'false',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
leaves_target: [
|
|
||||||
'minecraft:oak_leaves',
|
|
||||||
],
|
|
||||||
log_provider: {
|
|
||||||
type: 'minecraft:simple_state_provider',
|
|
||||||
state: {
|
|
||||||
Name: 'minecraft:acacia_log',
|
|
||||||
Properties: {
|
|
||||||
axis: 'y',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
log_target: [
|
|
||||||
'minecraft:oak_log',
|
|
||||||
],
|
|
||||||
place_from_nbt: [],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import type { CollectionRegistry, SchemaRegistry } from '@mcschema/core'
|
|
||||||
import type { VersionId } from '../services/Schemas.js'
|
|
||||||
import { initImmersiveWeathering } from './ImmersiveWeathering.js'
|
|
||||||
import { initLithostitched } from './Lithostitched.js'
|
|
||||||
import { initNeoForge } from './NeoForge.js'
|
|
||||||
import { initObsidian } from './Obsidian.js'
|
|
||||||
import { initOhTheTreesYoullGrow } from './OhTheTreesYoullGrow.js'
|
|
||||||
|
|
||||||
export * from './ImmersiveWeathering.js'
|
|
||||||
export * from './Lithostitched.js'
|
|
||||||
|
|
||||||
export function initPartners(schemas: SchemaRegistry, collections: CollectionRegistry, version: VersionId) {
|
|
||||||
initImmersiveWeathering(schemas, collections)
|
|
||||||
initLithostitched(schemas, collections, version)
|
|
||||||
initNeoForge(schemas, collections, version)
|
|
||||||
initObsidian(schemas, collections)
|
|
||||||
initOhTheTreesYoullGrow(schemas, collections)
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
{
|
|
||||||
"immersive_weathering.area_condition.type": "Type",
|
|
||||||
"immersive_weathering.area_condition.type.generate_if_not_too_many": "Generate if not too many",
|
|
||||||
"immersive_weathering.area_condition.type.neighbor_based_generation": "Neighbor based generation",
|
|
||||||
"immersive_weathering.area_condition.generate_if_not_too_many.radiusX": "Radius X",
|
|
||||||
"immersive_weathering.area_condition.generate_if_not_too_many.radiusY": "Radius Y",
|
|
||||||
"immersive_weathering.area_condition.generate_if_not_too_many.radiusZ": "Radius Z",
|
|
||||||
"immersive_weathering.area_condition.generate_if_not_too_many.requiredAmount": "Required amount",
|
|
||||||
"immersive_weathering.area_condition.generate_if_not_too_many.yOffset": "Y offset",
|
|
||||||
"immersive_weathering.area_condition.generate_if_not_too_many.must_have": "Must have",
|
|
||||||
"immersive_weathering.area_condition.generate_if_not_too_many.must_not_have": "Must not have",
|
|
||||||
"immersive_weathering.area_condition.generate_if_not_too_many.includes": "Includes",
|
|
||||||
"immersive_weathering.area_condition.neighbor_based_generation.must_have": "Must have",
|
|
||||||
"immersive_weathering.area_condition.neighbor_based_generation.must_not_have": "Must not have",
|
|
||||||
"immersive_weathering.area_condition.neighbor_based_generation.required_amount": "Required amount",
|
|
||||||
"immersive_weathering.area_condition.neighbor_based_generation.directions": "Directions",
|
|
||||||
"immersive_weathering.area_condition.neighbor_based_generation.directions.entry": "Direction",
|
|
||||||
"immersive_weathering.block_growth.area_condition": "Area conditions",
|
|
||||||
"immersive_weathering.block_growth.position_predicates": "Position predicates",
|
|
||||||
"immersive_weathering.block_growth.position_predicates.entry": "Position test",
|
|
||||||
"immersive_weathering.block_growth.growth_chance": "Growth chance",
|
|
||||||
"immersive_weathering.block_growth.growth_for_face": "Growth for face",
|
|
||||||
"immersive_weathering.block_growth.growth_for_face.entry": "Face",
|
|
||||||
"immersive_weathering.block_growth.growth_for_face.entry.direction": "Direction",
|
|
||||||
"immersive_weathering.block_growth.growth_for_face.entry.weight": "Weight",
|
|
||||||
"immersive_weathering.block_growth.growth_for_face.entry.growth": "Growth",
|
|
||||||
"immersive_weathering.block_growth.growth_for_face.entry.growth.entry.data": "Block pair",
|
|
||||||
"immersive_weathering.block_growth.growth_for_face.entry.growth.entry.weight": "Weight",
|
|
||||||
"immersive_weathering.block_growth.owners": "Owners",
|
|
||||||
"immersive_weathering.block_growth.owners.entry": "Block",
|
|
||||||
"immersive_weathering.block_growth.replacing_target": "Replacing target",
|
|
||||||
"immersive_weathering.block_growth.target_self": "Target self",
|
|
||||||
"immersive_weathering.block_growth.destroy_target": "Destroy target",
|
|
||||||
"immersive_weathering.block_pair.block": "Block",
|
|
||||||
"immersive_weathering.block_pair.above_block": "Above block",
|
|
||||||
"immersive_weathering.position_test.predicate_type": "Predicate type",
|
|
||||||
"immersive_weathering.position_test.predicate_type.biome_match": "Biome match",
|
|
||||||
"immersive_weathering.position_test.predicate_type.day_test": "Day test",
|
|
||||||
"immersive_weathering.position_test.predicate_type.nand": "NAND",
|
|
||||||
"immersive_weathering.position_test.predicate_type.precipitation_test": "Precipitation test",
|
|
||||||
"immersive_weathering.position_test.predicate_type.temperature_range": "Temperature range",
|
|
||||||
"immersive_weathering.position_test.biome_match.biomes": "Biomes",
|
|
||||||
"immersive_weathering.position_test.day_test.day": "Day",
|
|
||||||
"immersive_weathering.position_test.nand.predicates": "Predicates",
|
|
||||||
"immersive_weathering.position_test.precipitation_test.precipitation": "Precipitation",
|
|
||||||
"immersive_weathering.position_test.temperature_range.min": "Min",
|
|
||||||
"immersive_weathering.position_test.temperature_range.max": "Max",
|
|
||||||
"immersive_weathering.position_test.temperature_range.use_local_pos": "Use local pos",
|
|
||||||
"immersive_weathering:rule_test.always_true": "Always true",
|
|
||||||
"immersive_weathering:rule_test.block_match": "Block match",
|
|
||||||
"immersive_weathering:rule_test.blockstate_match": "Block state match",
|
|
||||||
"immersive_weathering:rule_test.random_block_match": "Random block match",
|
|
||||||
"immersive_weathering:rule_test.random_blockstate_match": "Random block state match",
|
|
||||||
"immersive_weathering:rule_test.tag_match": "Tag match",
|
|
||||||
"immersive_weathering:rule_test.immersive_weathering:block_set_match": "Block set match",
|
|
||||||
"immersive_weathering:rule_test.immersive_weathering:fluid_match": "Fluid match",
|
|
||||||
"immersive_weathering:rule_test.immersive_weathering:tree_log": "Tree log",
|
|
||||||
|
|
||||||
"lithostitched:modifier_type.lithostitched:add_biome_spawns": "Add biome spawns",
|
|
||||||
"lithostitched:modifier_type.lithostitched:add_features": "Add features",
|
|
||||||
"lithostitched:modifier_type.lithostitched:add_pool_aliases": "Add pool aliases",
|
|
||||||
"lithostitched:modifier_type.lithostitched:add_structure_set_entries": "Add structure set entries",
|
|
||||||
"lithostitched:modifier_type.lithostitched:add_surface_rule": "Add surface rule",
|
|
||||||
"lithostitched:modifier_type.lithostitched:add_template_pool_elements": "Add template pool elements",
|
|
||||||
"lithostitched:modifier_type.lithostitched:no_op": "Nothing",
|
|
||||||
"lithostitched:modifier_type.lithostitched:redirect_feature": "Redirect feature",
|
|
||||||
"lithostitched:modifier_type.lithostitched:remove_biome_spawns": "Remove biome spawns",
|
|
||||||
"lithostitched:modifier_type.lithostitched:remove_features": "Remove features",
|
|
||||||
"lithostitched:modifier_type.lithostitched:remove_structures_from_structure_set": "Remove structures from set",
|
|
||||||
"lithostitched:modifier_type.lithostitched:replace_climate": "Replace climate",
|
|
||||||
"lithostitched:modifier_type.lithostitched:replace_effects": "Replace effects",
|
|
||||||
"lithostitched:modifier_predicate_type.lithostitched:all_of": "All of",
|
|
||||||
"lithostitched:modifier_predicate_type.lithostitched:any_of": "Any of",
|
|
||||||
"lithostitched:modifier_predicate_type.lithostitched:mod_loaded": "Mod loaded",
|
|
||||||
"lithostitched:modifier_predicate_type.lithostitched:not": "Not",
|
|
||||||
"lithostitched:modifier_predicate_type.lithostitched:true": "True",
|
|
||||||
|
|
||||||
"neoforge:biome_modifier_type.neoforge:none": "Disable Biome Modifier",
|
|
||||||
"neoforge:biome_modifier_type.neoforge:add_features": "Add Features",
|
|
||||||
"neoforge:biome_modifier_type.neoforge:remove_features": "Remove Features",
|
|
||||||
"neoforge:biome_modifier_type.neoforge:add_spawns": "Add Mob Spawns",
|
|
||||||
"neoforge:biome_modifier_type.neoforge:remove_spawns": "Remove Mob Spawns",
|
|
||||||
"neoforge:biome_modifier_type.neoforge:add_carvers": "Add World Carvers",
|
|
||||||
"neoforge:biome_modifier_type.neoforge:remove_carvers": "Remove World Carvers",
|
|
||||||
"neoforge:biome_modifier_type.neoforge:add_spawn_costs": "Add Mob Spawn Costs",
|
|
||||||
"neoforge:biome_modifier_type.neoforge:remove_spawn_costs": "Remove Mob Spawn Costs",
|
|
||||||
"neoforge:structure_modifier_type.neoforge:none": "Disable Structure Modifier",
|
|
||||||
"neoforge:structure_modifier_type.neoforge:add_spawns": "Add Mob Spawns",
|
|
||||||
"neoforge:structure_modifier_type.neoforge:remove_spawns": "Remove Mob Spawns",
|
|
||||||
"neoforge:structure_modifier_type.neoforge:clear_spawns": "Clear Mob Spawns",
|
|
||||||
|
|
||||||
"obsidian:item.information": "Item Information",
|
|
||||||
"obsidian:item_information.rarity": "Rarity",
|
|
||||||
"obsidian:item_information.creative_tab": "Creative Tab",
|
|
||||||
"obsidian:item_information.max_stack_size": "Max Stack Size",
|
|
||||||
"obsidian:item_information.name": "Item Name",
|
|
||||||
"obsidian:item_information.has_enchantment_glint": "Has Enchantment Glint",
|
|
||||||
"obsidian:item_information.is_enchantable": "Is Enchantable",
|
|
||||||
"obsidian:item_information.enchantability": "Enchantability",
|
|
||||||
"obsidian:item_information.use_duration": "Item Duration",
|
|
||||||
"obsidian:item_information.can_place_block": "Can Place Block",
|
|
||||||
"obsidian:item_information.placable_block": "Placable Block",
|
|
||||||
"obsidian:item_information.wearable": "Wearable",
|
|
||||||
"obsidian:item_information.default_color": "Default Color",
|
|
||||||
"obsidian:item_information.wearable_slot": "Wearable Slot",
|
|
||||||
"obsidian:item_information.custom_render_mode": "Has display-based models",
|
|
||||||
"obsidian:item_information.render_mode_models": "Display-based Models",
|
|
||||||
"obsidian:item.display": "Display",
|
|
||||||
"obsidian:item.display.model": "Model",
|
|
||||||
"obsidian:item.display.item_model": "Item Model",
|
|
||||||
"obsidian:item.display.lore": "Lore",
|
|
||||||
"obsidian:item.use_action": "Use Action",
|
|
||||||
"obsidian:item.use_action.action": "Action",
|
|
||||||
"obsidian:item.use_action.right_click_action": "Right Click Action",
|
|
||||||
"obsidian:item.use_action.command": "Command",
|
|
||||||
"obsidian:item.use_action.url": "URL",
|
|
||||||
"obsidian:item.use_action.gui_size": "GUI Size",
|
|
||||||
"obsidian:item.use_action.gui_title": "GUI Title",
|
|
||||||
|
|
||||||
"obsidian:block.block_type": "Block Type",
|
|
||||||
"obsidian:block.information": "Block Information",
|
|
||||||
"obsidian:block_information.rarity": "Rarity",
|
|
||||||
"obsidian:block_information.creative_tab": "Creative Tab",
|
|
||||||
"obsidian:block_information.max_stack_size": "Max Stack Size",
|
|
||||||
"obsidian:block_information.name": "Item Name",
|
|
||||||
"obsidian:block_information.sound_group_type": "Sound Group Type",
|
|
||||||
"obsidian:block_information.vanilla_sound_group": "Vanilla Sound Group",
|
|
||||||
"obsidian:block_information.custom_sound_group": "Custom Sound Group",
|
|
||||||
"obsidian:block_information.material_type": "Material Type",
|
|
||||||
"obsidian:block_information.vanilla_material": "Vanilla Material",
|
|
||||||
"obsidian:block_information.custom_material": "Custom Material",
|
|
||||||
"obsidian:block.display": "Display",
|
|
||||||
"obsidian:block.additional_information": "Additional Information",
|
|
||||||
"obsidian:block.ore_information": "Ore Information",
|
|
||||||
"obsidian:block.food_information": "Food Information",
|
|
||||||
"obsidian:block.block_type.CAMPFIRE.campfire_properties": "Campfire Properties",
|
|
||||||
"obsidian:block.can_plant_on": "Can Plant On",
|
|
||||||
"obsidian:block.particle_type": "Particle Type",
|
|
||||||
"obsidian:block.growable": "Growable",
|
|
||||||
"obsidian:block.oxidizable_properties": "Oxidizable Properties",
|
|
||||||
"obsidian:block.events": "Events",
|
|
||||||
"obsidian:block.drop_information": "Drop Information",
|
|
||||||
"obsidian:block.is_multi_block": "Is Multi-Block",
|
|
||||||
"obsidian:block.multiblock_information": "Multiblock Information",
|
|
||||||
"obsidian:block.placable_feature": "Placable Feature",
|
|
||||||
"obsidian:block.display.model": "Model",
|
|
||||||
"obsidian:block.display.item_model": "Item Model",
|
|
||||||
"obsidian:block.display.block_model": "Block Model",
|
|
||||||
"obsidian:block.display.lore": "Lore",
|
|
||||||
|
|
||||||
"ohthetreesyoullgrow:feature.ohthetreesyoullgrow:tree_from_nbt_v1": "Tree from NBT v1",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.type": "Type",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config": "Config",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.base_location": "Base location",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.base_location.help": "The path to the trunk structure piece.",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.canopy_location": "Canopy location",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.canopy_location.help": "The path to the canopy structure piece.",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.can_grow_on_filter": "Can grow on filter",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.can_grow_on_filter.help": "Block filter for which this tree is allowed to grow on. Checks all of the red wool positions defined by the trunk.",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.can_leaves_place_filter": "Can leaves place filter",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.can_leaves_place_filter.help": "Block filter for which this tree's leaves are allowed to place.",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.decorators": "Decorators",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.decorators.entry": "Decorator",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.height": "Height",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.height.help": "Int provider defining the height of the tree.",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.leaves_provider": "Leaves provider",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.leaves_target": "Leaves target",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.leaves_target.entry": "Block",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.log_provider": "Log provider",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.log_target": "Log target",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.log_target.entry": "Block",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.max_log_depth": "Max log depth",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.place_from_nbt": "Place from NBT",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.place_from_nbt.help": "Additional blocks from the structure pieces that should be placed in the world.",
|
|
||||||
"ohthetreesyoullgrow.configured_feature.config.place_from_nbt.entry": "Block"
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import type { INode, Path } from '@mcschema/core'
|
|
||||||
import { DataModel } from '@mcschema/core'
|
|
||||||
|
|
||||||
export class ModelWrapper extends DataModel {
|
|
||||||
constructor(
|
|
||||||
schema: INode<any>,
|
|
||||||
private readonly mapper: (path: Path) => Path,
|
|
||||||
private readonly getter: (path: Path) => any,
|
|
||||||
private readonly setter: (path: Path, value: any, silent?: boolean) => any,
|
|
||||||
) {
|
|
||||||
super(schema)
|
|
||||||
}
|
|
||||||
|
|
||||||
map(path: Path) {
|
|
||||||
return this.mapper(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
get(path: Path) {
|
|
||||||
return this.getter(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
set(path: Path, value: any, silent?: boolean) {
|
|
||||||
return this.setter(path, value, silent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,719 +0,0 @@
|
|||||||
import type { BooleanHookParams, EnumOption, Hook, INode, ListHookParams, NodeChildren, NumberHookParams, StringHookParams, ValidationOption } from '@mcschema/core'
|
|
||||||
import { DataModel, ListNode, MapNode, ModelPath, ObjectNode, Path, relativePath, StringNode } from '@mcschema/core'
|
|
||||||
import { Identifier, ItemStack } from 'deepslate/core'
|
|
||||||
import type { ComponentChildren, JSX } from 'preact'
|
|
||||||
import { memo } from 'preact/compat'
|
|
||||||
import { useState } from 'preact/hooks'
|
|
||||||
import { Btn, Octicon } from '../components/index.js'
|
|
||||||
import { ItemDisplay } from '../components/ItemDisplay.jsx'
|
|
||||||
import { VanillaColors } from '../components/previews/BiomeSourcePreview.jsx'
|
|
||||||
import config from '../Config.js'
|
|
||||||
import { localize, useLocale, useStore } from '../contexts/index.js'
|
|
||||||
import { useFocus } from '../hooks/index.js'
|
|
||||||
import type { BlockStateRegistry, VersionId } from '../services/index.js'
|
|
||||||
import { CachedDecorator, CachedFeature, checkVersion } from '../services/index.js'
|
|
||||||
import { deepClone, deepEqual, generateColor, generateUUID, hexId, hexToRgb, isObject, newSeed, rgbToHex, stringToColor } from '../Utils.js'
|
|
||||||
import { ModelWrapper } from './ModelWrapper.js'
|
|
||||||
|
|
||||||
const selectRegistries = ['loot_table.type', 'loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'recipe.type', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'dimension.generator.biome_source.preset', 'carver.type', 'feature.type', 'decorator.type', 'feature.tree.minimum_size.type', 'block_state_provider.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'int_provider.type', 'float_provider.type', 'height_provider.type', 'structure_feature.type', 'surface_builder.type', 'processor.processor_type', 'rule_test.predicate_type', 'pos_rule_test.predicate_type', 'template_element.element_type', 'block_placer.type', 'block_predicate.type', 'material_rule.type', 'material_condition.type', 'structure_placement.type', 'density_function.type', 'root_placer.type', 'entity.type_specific.cat.variant', 'entity.type_specific.frog.variant', 'rule_block_entity_modifier.type', 'pool_alias_binding.type', 'lithostitched.worldgen_modifier.type', 'lithostitched.modifier_predicate.type', 'ohthetreesyoullgrow.configured_feature.type', 'enchantment_provider.type', 'enchantment_value_effect.type', 'level_based_value.type', 'neoforge.biome_modifier.type', 'neoforge.structure_modifier.type']
|
|
||||||
const datalistEnums = ['item_stack.components', 'function.set_components.components']
|
|
||||||
const hiddenFields = ['number_provider.type', 'score_provider.type', 'nbt_provider.type', 'int_provider.type', 'float_provider.type', 'height_provider.type', 'level_based_value.type']
|
|
||||||
const flattenedFields = ['feature.config', 'decorator.config', 'int_provider.value', 'float_provider.value', 'block_state_provider.simple_state_provider.state', 'block_state_provider.rotated_block_provider.state', 'block_state_provider.weighted_state_provider.entries.entry.data', 'rule_test.block_state', 'structure_feature.config', 'surface_builder.config', 'template_pool.elements.entry.element', 'decorator.block_survives_filter.state', 'material_rule.block.result_state', 'enchantment.effects.entry.effect']
|
|
||||||
const inlineFields = ['loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'feature.type', 'decorator.type', 'block_state_provider.type', 'feature.tree.minimum_size.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'block_placer.type', 'rule_test.predicate_type', 'processor.processor_type', 'template_element.element_type', 'nbt_operation.op', 'number_provider.value', 'score_provider.name', 'score_provider.target', 'nbt_provider.source', 'nbt_provider.target', 'generator_biome.biome', 'block_predicate.type', 'material_rule.type', 'material_condition.type', 'density_function.type', 'root_placer.type', 'entity.type_specific.type', 'glyph_provider.type', 'sprite_source.type', 'rule_block_entity_modifier.type', 'immersive_weathering.area_condition.type', 'immersive_weathering.block_growth.growth_for_face.entry.direction', 'immersive_weathering.position_test.predicate_type', 'pool_alias_binding.type', 'item_stack.id', 'data_component.container.entry.slot', 'map_decoration.type', 'suspicious_stew_effect_instance.id', 'enchantment_value_effect.type', 'enchantment_effect.type', 'particle.type']
|
|
||||||
const nbtFields = ['function.set_nbt.tag', 'advancement.display.icon.nbt', 'text_component_object.nbt', 'entity.nbt', 'block.nbt', 'item.nbt']
|
|
||||||
const fixedLists = ['generator_biome.parameters.temperature', 'generator_biome.parameters.humidity', 'generator_biome.parameters.continentalness', 'generator_biome.parameters.erosion', 'generator_biome.parameters.depth', 'generator_biome.parameters.weirdness', 'feature.end_spike.crystal_beam_target', 'feature.end_gateway.exit', 'decorator.block_filter.offset', 'block_predicate.has_sturdy_face.offset', 'block_predicate.inside_world_bounds.offset', 'block_predicate.matching_block_tag.offset', 'block_predicate.matching_blocks.offset', 'block_predicate.matching_fluids.offset', 'block_predicate.would_survive.offset', 'model_element.from', 'model_element.to', 'model_element.rotation.origin', 'model_element.faces.uv', 'item_transform.rotation', 'item_transform.translation', 'item_transform.scale', 'generator_structure.random_spread.locate_offset', 'pack_overlay.formats', 'data_component.profile.id', 'data_component.lodestone_tracker.tracker.pos', 'attribute_modifier.uuid']
|
|
||||||
const collapsedFields = ['noise_settings.surface_rule', 'noise_settings.noise.terrain_shaper']
|
|
||||||
const collapsableFields = ['density_function.argument', 'density_function.argument1', 'density_function.argument2', 'density_function.input', 'density_function.when_in_range', 'density_function.when_out_of_range']
|
|
||||||
const itemPreviewFields = ['loot_pool.entries.entry', 'loot_entry.alternatives.children.entry', 'loot_entry.group.children.entry', 'loot_entry.sequence.children.entry', 'function.set_contents.entries.entry']
|
|
||||||
const forceEnumContexts: Record<string, string> = { 'loot_table.type': 'loot_table.type', 'condition.condition': 'loot_condition_type', 'function.function': 'loot_function_type' }
|
|
||||||
|
|
||||||
const findGenerator = (id: string) => {
|
|
||||||
return config.generators.find(g => g.id === id.replace(/^\$/, ''))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Secondary model used to remember the keys of a map
|
|
||||||
*/
|
|
||||||
const keysModel = new DataModel(MapNode(
|
|
||||||
StringNode(),
|
|
||||||
StringNode()
|
|
||||||
), { historyMax: 0 })
|
|
||||||
|
|
||||||
type JSXTriple = [JSX.Element | null, JSX.Element | null, JSX.Element | null]
|
|
||||||
type RenderHook = Hook<[any, string, VersionId, BlockStateRegistry, Record<string, any>], JSXTriple>
|
|
||||||
|
|
||||||
type NodeProps<T> = T & {
|
|
||||||
node: INode<any>,
|
|
||||||
path: ModelPath,
|
|
||||||
value: any,
|
|
||||||
lang: string,
|
|
||||||
version: VersionId,
|
|
||||||
states: BlockStateRegistry,
|
|
||||||
ctx: Record<string, any>,
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FullNode({ model, lang, version, blockStates }: { model: DataModel, lang: string, version: VersionId, blockStates: BlockStateRegistry }) {
|
|
||||||
const path = new ModelPath(model)
|
|
||||||
const [prefix, suffix, body] = model.schema.hook(renderHtml, path, deepClone(model.data), lang, version, blockStates, {})
|
|
||||||
return suffix?.props?.children.some((c: any) => c) ? <div class={`node ${model.schema.type(path)}-node`} data-category={model.schema.category(path)}>
|
|
||||||
<div class="node-header">{prefix}{suffix}</div>
|
|
||||||
<div class="node-body">{body}</div>
|
|
||||||
</div> : body
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderHtml: RenderHook = {
|
|
||||||
base() {
|
|
||||||
return [null, null, null]
|
|
||||||
},
|
|
||||||
|
|
||||||
boolean(params, path, value, lang, version, states, ctx) {
|
|
||||||
return [null, <BooleanSuffix {...{...params, path, value, lang, version, states, ctx}} />, null]
|
|
||||||
},
|
|
||||||
|
|
||||||
choice({ choices, config, switchNode }, path, value, lang, version, states, ctx) {
|
|
||||||
const choice = switchNode.activeCase(path, true) as typeof choices[number]
|
|
||||||
const contextPath = (config?.context) ? new ModelPath(path.getModel(), new Path(path.getArray(), [config.context])) : path
|
|
||||||
const [prefix, suffix, body] = choice.node.hook(this, contextPath, value, lang, version, states, ctx)
|
|
||||||
if (choices.length === 1) {
|
|
||||||
return [prefix, suffix, body]
|
|
||||||
}
|
|
||||||
const choiceContextPath = config?.choiceContext ? new Path([], [config.choiceContext]) : config?.context ? new Path([], [config.context]) : path
|
|
||||||
const set = (type: string) => {
|
|
||||||
const c = choices.find(c => c.type === type) ?? choice
|
|
||||||
const def = c.node.default()
|
|
||||||
const newValue = c.change
|
|
||||||
? c.change(DataModel.unwrapLists(value))
|
|
||||||
: config.choiceContext === 'feature' && def?.type === 'minecraft:decorated' ? def.config.feature : def
|
|
||||||
path.model.set(path, DataModel.wrapLists(newValue))
|
|
||||||
}
|
|
||||||
const inject = <select value={choice.type} onChange={(e) => set((e.target as HTMLSelectElement).value)}>
|
|
||||||
{choices.map(c => <option value={c.type}>
|
|
||||||
{pathLocale(lang, choiceContextPath.contextPush(c.type))}
|
|
||||||
</option>)}
|
|
||||||
</select>
|
|
||||||
return [prefix, <>{inject}{suffix}</>, body]
|
|
||||||
},
|
|
||||||
|
|
||||||
list({ children, config, node }, path, value, lang, version, states, ctx) {
|
|
||||||
const context = path.getContext().join('.')
|
|
||||||
if (fixedLists.includes(context)) {
|
|
||||||
const prefix = <>
|
|
||||||
{[...Array(config.maxLength!)].map((_, i) =>
|
|
||||||
<ErrorPopup lang={lang} path={path.modelPush(i)} />)}
|
|
||||||
<div class="fixed-list"></div>
|
|
||||||
</>
|
|
||||||
const suffix = <>{[...Array(config.maxLength)].map((_, i) => {
|
|
||||||
const child = children.hook(this, path.modelPush(i), value?.[i]?.node, lang, version, states, ctx)
|
|
||||||
return child[1]
|
|
||||||
})}</>
|
|
||||||
return [prefix, suffix, null]
|
|
||||||
}
|
|
||||||
|
|
||||||
const onAdd = () => {
|
|
||||||
if (!Array.isArray(value)) value = []
|
|
||||||
const node = DataModel.wrapLists(children.default())
|
|
||||||
path.model.set(path, [{ node, id: hexId() }, ...value])
|
|
||||||
}
|
|
||||||
const suffix = <button class="add tooltipped tip-se" aria-label={localize(lang, 'add_top')} onClick={onAdd}>{Octicon.plus_circle}</button>
|
|
||||||
return [null, suffix, <ListBody {...{children, config, node, path, value, lang, version, states, ctx}}/>]
|
|
||||||
},
|
|
||||||
|
|
||||||
map({ children, keys, config }, path, value, lang, version, states, ctx) {
|
|
||||||
const { expand, collapse, isToggled } = useToggles()
|
|
||||||
|
|
||||||
const keyPath = new ModelPath(keysModel, new Path([hashString(path.toString())], path.contextArr))
|
|
||||||
const onAdd = () => {
|
|
||||||
const key = keyPath.get()
|
|
||||||
if (path.model.get(path.push(key)) === undefined) {
|
|
||||||
path.model.set(path.push(key), DataModel.wrapLists(children.default()))
|
|
||||||
}
|
|
||||||
keyPath.set('')
|
|
||||||
}
|
|
||||||
const blockState = config.validation?.validator === 'block_state_map' ? states?.[relativePath(path, config.validation.params.id).get()] : null
|
|
||||||
const keysSchema = blockState?.properties
|
|
||||||
? StringNode(null!, { enum: Object.keys(blockState.properties ?? {}) })
|
|
||||||
: keys
|
|
||||||
if (blockState && path.last() === 'Properties') {
|
|
||||||
if (typeof value !== 'object') value = {}
|
|
||||||
const properties = Object.entries(blockState.properties ?? {})
|
|
||||||
.map(([key, values]) => [key, StringNode(null!, { enum: values })])
|
|
||||||
Object.entries(blockState.properties ?? {}).forEach(([key, values]) => {
|
|
||||||
if (typeof value[key] !== 'string') {
|
|
||||||
path.model.errors.add(path.push(key), 'error.expected_string')
|
|
||||||
} else if (!values.includes(value[key])) {
|
|
||||||
path.model.errors.add(path.push(key), 'error.invalid_enum_option', value[key])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return ObjectNode(Object.fromEntries(properties)).hook(this, path, value, lang, version, states, ctx)
|
|
||||||
}
|
|
||||||
const suffix = <>
|
|
||||||
{keysSchema.hook(this, keyPath, keyPath.get() ?? '', lang, version, states, ctx)[1]}
|
|
||||||
<button class="add tooltipped tip-se" aria-label={localize(lang, 'add')} onClick={onAdd}>{Octicon.plus_circle}</button>
|
|
||||||
</>
|
|
||||||
const body = <>
|
|
||||||
{typeof value === 'object' && Object.entries(value).map(([key, cValue]) => {
|
|
||||||
const pathWithContext = (config?.context) ? new ModelPath(path.getModel(), new Path(path.getArray(), [config.context])) : path
|
|
||||||
const cPath = pathWithContext.modelPush(key)
|
|
||||||
const canToggle = children.type(cPath) === 'object'
|
|
||||||
const toggle = isToggled(key)
|
|
||||||
if (canToggle && (toggle === false || (toggle === undefined && value.length > 20))) {
|
|
||||||
return <div class="node node-header" data-category={children.category(cPath)}>
|
|
||||||
<ErrorPopup lang={lang} path={cPath} nested />
|
|
||||||
<button class="toggle tooltipped tip-se" aria-label={`${localize(lang, 'expand')}\n${localize(lang, 'expand_all', 'Ctrl')}`} onClick={expand(key)}>{Octicon.chevron_right}</button>
|
|
||||||
<label>{key}</label>
|
|
||||||
<Collapsed key={key} path={cPath} value={cValue} schema={children} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
const cSchema = blockState
|
|
||||||
? StringNode(null!, { enum: blockState.properties?.[key] ?? [] })
|
|
||||||
: children
|
|
||||||
if (blockState?.properties?.[key] && typeof cValue === 'string'
|
|
||||||
&& !blockState.properties?.[key].includes(cValue)) {
|
|
||||||
path.model.errors.add(cPath, 'error.invalid_enum_option', cValue)
|
|
||||||
}
|
|
||||||
const onRemove = () => cPath.set(undefined)
|
|
||||||
return <MemoedTreeNode key={key} schema={cSchema} path={cPath} value={cValue} {...{lang, version, states, ctx}} label={key}>
|
|
||||||
{canToggle && <button class="toggle tooltipped tip-se" aria-label={`${localize(lang, 'collapse')}\n${localize(lang, 'collapse_all', 'Ctrl')}`} onClick={collapse(key)}>{Octicon.chevron_down}</button>}
|
|
||||||
<button class="remove tooltipped tip-se" aria-label={localize(lang, 'remove')} onClick={onRemove}>{Octicon.trashcan}</button>
|
|
||||||
</MemoedTreeNode>
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
return [null, suffix, body]
|
|
||||||
},
|
|
||||||
|
|
||||||
number(params, path, value, lang, version, states, ctx) {
|
|
||||||
return [null, <NumberSuffix {...{...params, path, value, lang, version, states, ctx}} />, null]
|
|
||||||
},
|
|
||||||
|
|
||||||
object({ node, config, getActiveFields, getChildModelPath }, path, value, lang, version, states, ctx) {
|
|
||||||
const { expand, collapse, isToggled } = useToggles()
|
|
||||||
|
|
||||||
if (path.getArray().length == 0 && isDecorated(config.context, value)) {
|
|
||||||
const { wrapper, fields } = createDecoratorsWrapper(getActiveFields(path), path, value)
|
|
||||||
value = wrapper.data
|
|
||||||
getActiveFields = () => fields
|
|
||||||
getChildModelPath = (path, key) => new ModelPath(wrapper, new Path(path.getArray(), ['feature'])).push(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
let prefix: JSX.Element | null = null
|
|
||||||
let suffix: JSX.Element | null = null
|
|
||||||
let body: JSX.Element | null = null
|
|
||||||
if (node.optional()) {
|
|
||||||
if (value === undefined) {
|
|
||||||
const onExpand = () => path.set(DataModel.wrapLists(node.default()))
|
|
||||||
suffix = <button class="node-collapse closed tooltipped tip-se" aria-label={localize(lang, 'expand')} onClick={onExpand}>{Octicon.plus_circle}</button>
|
|
||||||
} else if (typeof value === 'object' && value !== null){
|
|
||||||
const onCollapse = () => path.set(undefined)
|
|
||||||
suffix = <button class="node-collapse open tooltipped tip-se" aria-label={localize(lang, 'remove')} onClick={onCollapse}>{Octicon.trashcan}</button>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const context = path.getContext().join('.')
|
|
||||||
if (collapsableFields.includes(context) || collapsedFields.includes(context)) {
|
|
||||||
const toggled = isToggled('')
|
|
||||||
const expanded = collapsedFields.includes(context) ? toggled : !toggled
|
|
||||||
prefix = <>
|
|
||||||
<button class="toggle tooltipped tip-se" aria-label={localize(lang, expanded ? 'collapse' : 'expand')} onClick={toggled ? collapse('') : expand('')}>{expanded ? Octicon.chevron_down : Octicon.chevron_right}</button>
|
|
||||||
</>
|
|
||||||
if (!expanded) {
|
|
||||||
return [prefix, suffix, null]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!(node.optional() && value === undefined)) {
|
|
||||||
if (typeof value === 'object' && value !== null) {
|
|
||||||
const newCtx = (typeof value === 'object' && value !== null && node.default()?.pools)
|
|
||||||
? { ...ctx, loot: value?.type } : ctx
|
|
||||||
body = <>{Object.entries(getActiveFields(path))
|
|
||||||
.filter(([_, child]) => child.enabled(path))
|
|
||||||
.map(([key, child]) => {
|
|
||||||
const cPath = getChildModelPath(path, key)
|
|
||||||
const context = cPath.getContext().join('.')
|
|
||||||
if (hiddenFields.includes(context)) return null
|
|
||||||
const [cPrefix, cSuffix, cBody] = child.hook(this, cPath, value[key], lang, version, states, newCtx)
|
|
||||||
const isFlattened = child.type(cPath) === 'object' && flattenedFields.includes(context)
|
|
||||||
const isInlined = inlineFields.includes(context)
|
|
||||||
if (isFlattened || isInlined) {
|
|
||||||
prefix = <>{prefix}<ErrorPopup lang={lang} path={cPath} /><HelpPopup lang={lang} path={cPath} />{cPrefix}</>
|
|
||||||
suffix = <>{suffix}{cSuffix}</>
|
|
||||||
return isFlattened ? cBody : null
|
|
||||||
}
|
|
||||||
return <MemoedTreeNode key={key} schema={child} path={cPath} value={value[key]} {...{lang, version, states, ctx: newCtx}} />
|
|
||||||
})}</>
|
|
||||||
} else {
|
|
||||||
const onReset = () => path.set(DataModel.wrapLists(node.default()))
|
|
||||||
suffix = <>{suffix}<button class="add tooltipped tip-se" aria-label={localize(lang, 'reset')} onClick={onReset}>{Octicon.history}</button></>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [prefix, suffix, body]
|
|
||||||
},
|
|
||||||
|
|
||||||
string(params, path, value, lang, version, states, ctx) {
|
|
||||||
return [null, <StringSuffix {...{...params, path, value, lang, version, states, ctx}} />, null]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
function Collapsed({ path, value }: { path: ModelPath, value: any, schema: INode<any> }) {
|
|
||||||
const { locale } = useLocale()
|
|
||||||
const context = path.getContext().join('.')
|
|
||||||
switch (context) {
|
|
||||||
case 'loot_table.pools.entry':
|
|
||||||
const count = value?.entries?.length ?? 0
|
|
||||||
return <label>{count} {count == 1 ? 'entry' : 'entries'}</label>
|
|
||||||
case 'function.set_contents.entries.entry':
|
|
||||||
case 'loot_pool.entries.entry':
|
|
||||||
const name = value?.name?.replace(/^minecraft:/, '') ?? value?.type?.replace(/^minecraft:/, '')
|
|
||||||
const weight = value?.weight || undefined
|
|
||||||
return <>
|
|
||||||
<label>{name}</label>
|
|
||||||
{weight !== undefined && <label class="tooltipped tip-se" aria-label={locale('weight')}>{weight}</label>}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
for (const child of Object.values(value ?? {})) {
|
|
||||||
if (typeof child === 'string') {
|
|
||||||
return <label>{child.replace(/^minecraft:/, '')}</label>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
function useToggles() {
|
|
||||||
const [toggleState, setToggleState] = useState(new Map<string, boolean>())
|
|
||||||
const [toggleAll, setToggleAll] = useState<boolean | undefined>(undefined)
|
|
||||||
|
|
||||||
const expand = (key: string) => (evt: MouseEvent) => {
|
|
||||||
if (evt.ctrlKey) {
|
|
||||||
setToggleState(new Map())
|
|
||||||
setToggleAll(true)
|
|
||||||
} else {
|
|
||||||
setToggleState(state => new Map(state.set(key, true)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const collapse = (key: string) => (evt: MouseEvent) => {
|
|
||||||
if (evt.ctrlKey) {
|
|
||||||
setToggleState(new Map())
|
|
||||||
setToggleAll(false)
|
|
||||||
} else {
|
|
||||||
setToggleState(state => new Map(state.set(key, false)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isToggled = (key: string) => {
|
|
||||||
if (!(toggleState instanceof Map)) return false
|
|
||||||
return toggleState.get(key) ?? toggleAll
|
|
||||||
}
|
|
||||||
|
|
||||||
return { expand, collapse, isToggled }
|
|
||||||
}
|
|
||||||
|
|
||||||
function ListBody({ path, value, lang, config, children, version, states, ctx }: NodeProps<ListHookParams>) {
|
|
||||||
const { expand, collapse, isToggled } = useToggles()
|
|
||||||
const [maxShown, setMaxShown] = useState(50)
|
|
||||||
const onAddBottom = () => {
|
|
||||||
if (!Array.isArray(value)) value = []
|
|
||||||
const node = DataModel.wrapLists(children.default())
|
|
||||||
path.model.set(path, [...value, { node, id: hexId() }])
|
|
||||||
}
|
|
||||||
return <>
|
|
||||||
{(value && Array.isArray(value)) && value.map(({ node: cValue, id: cId }, index) => {
|
|
||||||
if (index === maxShown) {
|
|
||||||
return <div class="node node-header">
|
|
||||||
<label>{localize(lang, 'entries_hidden', `${value.length - maxShown}`)}</label>
|
|
||||||
<button onClick={() => setMaxShown(Math.min(maxShown + 50, value.length))}>{localize(lang, 'entries_hidden.more', '50')}</button>
|
|
||||||
<button onClick={() => setMaxShown(value.length)}>{localize(lang, 'entries_hidden.all')}</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
if (index > maxShown) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const pathWithContext = (config?.context) ? new ModelPath(path.getModel(), new Path(path.getArray(), [config.context])) : path
|
|
||||||
const cPath = pathWithContext.push(index).contextPush('entry')
|
|
||||||
const canToggle = children.type(cPath) === 'object'
|
|
||||||
const toggle = isToggled(cId)
|
|
||||||
|
|
||||||
let label: undefined | string | JSX.Element
|
|
||||||
if (itemPreviewFields.includes(cPath.getContext().join('.'))) {
|
|
||||||
if (isObject(cValue) && typeof cValue.type === 'string' && cValue.type.replace(/^minecraft:/, '') === 'item' && typeof cValue.name === 'string') {
|
|
||||||
let itemStack: ItemStack | undefined
|
|
||||||
try {
|
|
||||||
itemStack = new ItemStack(Identifier.parse(cValue.name), 1)
|
|
||||||
} catch (e) {}
|
|
||||||
if (itemStack !== undefined) {
|
|
||||||
label = <ItemDisplay item={itemStack} />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canToggle && (toggle === false || (toggle === undefined && value.length > 20))) {
|
|
||||||
return <div class="node node-header" data-category={children.category(cPath)}>
|
|
||||||
<ErrorPopup lang={lang} path={cPath} nested />
|
|
||||||
<button class="toggle tooltipped tip-se" aria-label={`${localize(lang, 'expand')}\n${localize(lang, 'expand_all', 'Ctrl')}`} onClick={expand(cId)}>{Octicon.chevron_right}</button>
|
|
||||||
<label>{label ?? pathLocale(lang, cPath, `${index}`)}</label>
|
|
||||||
<Collapsed key={cId} path={cPath} value={cValue} schema={children} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const onRemove = () => cPath.set(undefined)
|
|
||||||
const onMoveUp = () => {
|
|
||||||
const v = [...path.get()];
|
|
||||||
[v[index - 1], v[index]] = [v[index], v[index - 1]]
|
|
||||||
path.model.set(path, v)
|
|
||||||
}
|
|
||||||
const onMoveDown = () => {
|
|
||||||
const v = [...path.get()];
|
|
||||||
[v[index + 1], v[index]] = [v[index], v[index + 1]]
|
|
||||||
path.model.set(path, v)
|
|
||||||
}
|
|
||||||
const actions: MenuAction[] = [
|
|
||||||
{
|
|
||||||
icon: 'duplicate',
|
|
||||||
label: 'duplicate',
|
|
||||||
onSelect: () => {
|
|
||||||
const v = [...path.get()]
|
|
||||||
v.splice(index, 0, { id: hexId(), node: deepClone(cValue) })
|
|
||||||
path.model.set(path, v)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
return <MemoedTreeNode key={cId} label={label} path={cPath} schema={children} value={cValue} {...{lang, version, states, actions}} ctx={{...ctx, index: (index === 0 ? 1 : 0) + (index === value.length - 1 ? 2 : 0)}}>
|
|
||||||
{canToggle && <button class="toggle tooltipped tip-se" aria-label={`${localize(lang, 'collapse')}\n${localize(lang, 'collapse_all', 'Ctrl')}`} onClick={collapse(cId)}>{Octicon.chevron_down}</button>}
|
|
||||||
<button class="remove tooltipped tip-se" aria-label={localize(lang, 'remove')} onClick={onRemove}>{Octicon.trashcan}</button>
|
|
||||||
{value.length > 1 && <div class="node-move">
|
|
||||||
<button class="move tooltipped tip-se" aria-label={localize(lang, 'move_up')} onClick={onMoveUp} disabled={index === 0}>{Octicon.chevron_up}</button>
|
|
||||||
<button class="move tooltipped tip-se" aria-label={localize(lang, 'move_down')} onClick={onMoveDown} disabled={index === value.length - 1}>{Octicon.chevron_down}</button>
|
|
||||||
</div>}
|
|
||||||
</MemoedTreeNode>
|
|
||||||
})}
|
|
||||||
{(value && value.length > 0 && value.length <= maxShown) && <div class="node node-header">
|
|
||||||
<button class="add tooltipped tip-se" aria-label={localize(lang, 'add_bottom')} onClick={onAddBottom}>{Octicon.plus_circle}</button>
|
|
||||||
</div>}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function BooleanSuffix({ path, node, value, lang }: NodeProps<BooleanHookParams>) {
|
|
||||||
const set = (target: boolean) => {
|
|
||||||
path.model.set(path, node.optional() && value === target ? undefined : target)
|
|
||||||
}
|
|
||||||
return <>
|
|
||||||
<button class={value === false ? 'selected' : ''} onClick={() => set(false)}>{localize(lang, 'false')}</button>
|
|
||||||
<button class={value === true ? 'selected' : ''} onClick={() => set(true)}>{localize(lang, 'true')}</button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function NumberSuffix({ path, config, integer, value, lang }: NodeProps<NumberHookParams>) {
|
|
||||||
const onChange = (evt: Event) => {
|
|
||||||
const value = (evt.target as HTMLInputElement).value
|
|
||||||
const parsed = integer ? parseInt(value) : parseFloat(value)
|
|
||||||
path.model.set(path, parsed)
|
|
||||||
}
|
|
||||||
const onColor = (evt: Event) => {
|
|
||||||
const value = (evt.target as HTMLInputElement).value
|
|
||||||
const parsed = parseInt(value.slice(1), 16)
|
|
||||||
path.model.set(path, parsed)
|
|
||||||
}
|
|
||||||
return <>
|
|
||||||
<input type="text" value={value ?? ''} onBlur={onChange} onKeyDown={evt => {if (evt.key === 'Enter') onChange(evt)}} />
|
|
||||||
{config?.color && <input type="color" value={'#' + (value?.toString(16).padStart(6, '0') ?? '000000')} onChange={onColor} />}
|
|
||||||
{config?.color && <button onClick={() => path.set(generateColor())} class="tooltipped tip-se" aria-label={localize(lang, 'generate_new_color')}>{Octicon.sync}</button>}
|
|
||||||
{['dimension.generator.seed', 'dimension.generator.biome_source.seed', 'world_settings.seed', 'structure_placement.salt'].includes(path.getContext().join('.')) && <button onClick={() => newSeed(path.model)} class="tooltipped tip-se" aria-label={localize(lang, 'generate_new_seed')}>{Octicon.sync}</button>}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function StringSuffix({ path, getValues, config, node, value, lang, version, states }: NodeProps<StringHookParams>) {
|
|
||||||
const context = path.getContext().join('.')
|
|
||||||
const onChange = (evt: Event) => {
|
|
||||||
evt.stopPropagation()
|
|
||||||
const newValue = (evt.target as HTMLSelectElement).value
|
|
||||||
if (newValue === value) return
|
|
||||||
// Hackfix to support switching between checkerboard and multi_noise biome sources
|
|
||||||
if (context === 'dimension.generator.biome_source.type') {
|
|
||||||
const biomeSourceType = newValue.replace(/^minecraft:/, '')
|
|
||||||
const biomePath = path.pop().push('biomes')
|
|
||||||
const biomes = biomePath.get()
|
|
||||||
if (biomeSourceType === 'multi_noise') {
|
|
||||||
const newBiomes = Array.isArray(biomes)
|
|
||||||
? biomes.flatMap((b: any) => {
|
|
||||||
if (typeof b.node !== 'string') return []
|
|
||||||
return [{ node: { biome: b.node }}]
|
|
||||||
})
|
|
||||||
: [{ node: { biome: 'minecraft:plains' } }]
|
|
||||||
path.model.set(biomePath, newBiomes, true)
|
|
||||||
} else if (biomeSourceType === 'checkerboard') {
|
|
||||||
const newBiomes = typeof biomes === 'string'
|
|
||||||
? biomes
|
|
||||||
: Array.isArray(biomes)
|
|
||||||
? biomes.flatMap((b: any) => {
|
|
||||||
if (typeof b.node !== 'object' || b.node === null || typeof b.node.biome !== 'string') return []
|
|
||||||
return [{ node: b.node.biome }]
|
|
||||||
})
|
|
||||||
: [{ node: 'minecraft:plains' }]
|
|
||||||
path.model.set(biomePath, newBiomes, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
path.model.set(path, newValue.length === 0 ? undefined : newValue)
|
|
||||||
}
|
|
||||||
const values = getValues()
|
|
||||||
const id = !isEnum(config) && config?.validator === 'resource' && typeof config.params.pool === 'string' ? config.params.pool : undefined
|
|
||||||
|
|
||||||
if (nbtFields.includes(context)) {
|
|
||||||
return <textarea value={value ?? ''} onBlur={onChange}></textarea>
|
|
||||||
} else if ((isEnum(config) && !config.additional && !datalistEnums.includes(context)) || selectRegistries.includes(context)) {
|
|
||||||
let childPath = new Path([])
|
|
||||||
if (isEnum(config) && typeof config.enum === 'string') {
|
|
||||||
childPath = childPath.contextPush(config.enum)
|
|
||||||
} else if (id) {
|
|
||||||
childPath = childPath.contextPush(id)
|
|
||||||
} else if (isEnum(config)) {
|
|
||||||
childPath = path
|
|
||||||
} else if (Object.hasOwn(forceEnumContexts, context)) {
|
|
||||||
childPath = childPath.contextPush(forceEnumContexts[context])
|
|
||||||
}
|
|
||||||
return <select value={value ?? ''} onChange={onChange}>
|
|
||||||
{node.optional() && <option value="">{localize(lang, 'unset')}</option>}
|
|
||||||
{values.map(v => <option value={v}>
|
|
||||||
{pathLocale(lang, childPath.contextPush(v.replace(/^minecraft:/, '')))}
|
|
||||||
</option>)}
|
|
||||||
</select>
|
|
||||||
} else if (!isEnum(config) && config?.validator === 'block_state_key') {
|
|
||||||
const blockState = states?.[relativePath(path, config.params.id).get()]
|
|
||||||
const values = Object.keys(blockState?.properties ?? {})
|
|
||||||
return <select value={value ?? ''} onChange={onChange}>
|
|
||||||
{values.map(v => <option>{v}</option>)}
|
|
||||||
</select>
|
|
||||||
} else {
|
|
||||||
const { biomeColors, setBiomeColor } = useStore()
|
|
||||||
const fullId = typeof value === 'string' ? value.includes(':') ? value : 'minecraft:' + value : 'unknown'
|
|
||||||
const datalistId = hexId()
|
|
||||||
const gen = id ? findGenerator(id) : undefined
|
|
||||||
return <>
|
|
||||||
<input value={value ?? ''} onBlur={onChange} onKeyDown={evt => {if (evt.key === 'Enter') onChange(evt)}}
|
|
||||||
list={values.length > 0 ? datalistId : ''} />
|
|
||||||
{values.length > 0 && <datalist id={datalistId}>
|
|
||||||
{values.map(v => <option value={v} />)}
|
|
||||||
</datalist>}
|
|
||||||
{['generator_biome.biome'].includes(context) && <input type="color" value={rgbToHex(biomeColors[fullId] ?? VanillaColors[fullId] ?? stringToColor(fullId))} onChange={v => setBiomeColor(fullId, hexToRgb(v.currentTarget.value))}></input>}
|
|
||||||
{(['text_component_object.hoverEvent.show_entity.contents.id', 'enchantment.effects.entry.uuid'].includes(context) || ('attribute_modifier.id' === context && !checkVersion(version, '1.21'))) && <button onClick={() => path.set(generateUUID())} class="tooltipped tip-se" aria-label={localize(lang, 'generate_new_uuid')}>{Octicon.sync}</button>}
|
|
||||||
{gen && values.includes(value) && value.startsWith('minecraft:') &&
|
|
||||||
<a href={`/${gen.url}/?version=${version}&preset=${value.replace(/^minecraft:/, '')}`} class="tooltipped tip-se" aria-label={localize(lang, 'follow_reference')}>{Octicon.link_external}</a>}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type MenuAction = {
|
|
||||||
label: string,
|
|
||||||
description?: string,
|
|
||||||
icon: keyof typeof Octicon,
|
|
||||||
onSelect: () => unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
type TreeNodeProps = {
|
|
||||||
schema: INode<any>,
|
|
||||||
path: ModelPath,
|
|
||||||
value: any,
|
|
||||||
lang: string,
|
|
||||||
version: VersionId,
|
|
||||||
states: BlockStateRegistry,
|
|
||||||
ctx: Record<string, any>,
|
|
||||||
compare?: any,
|
|
||||||
label?: string | ComponentChildren,
|
|
||||||
actions?: MenuAction[],
|
|
||||||
children?: ComponentChildren,
|
|
||||||
}
|
|
||||||
function TreeNode({ label, schema, path, value, lang, version, states, ctx, actions, children }: TreeNodeProps) {
|
|
||||||
const type = schema.type(path)
|
|
||||||
const category = schema.category(path)
|
|
||||||
const context = path.getContext().join('.')
|
|
||||||
|
|
||||||
const [active, setActive] = useFocus()
|
|
||||||
const onContextMenu = (evt: MouseEvent) => {
|
|
||||||
evt.preventDefault()
|
|
||||||
setActive()
|
|
||||||
}
|
|
||||||
|
|
||||||
const newCtx: Record<string, any> = { ...ctx, depth: (ctx.depth ?? 0) + 1 }
|
|
||||||
delete newCtx.index
|
|
||||||
const [prefix, suffix, body] = schema.hook(renderHtml, path, value, lang, version, states, newCtx)
|
|
||||||
return <div class={`node ${type}-node`} data-category={category}>
|
|
||||||
<div class="node-header" onContextMenu={onContextMenu}>
|
|
||||||
<ErrorPopup lang={lang} path={path} />
|
|
||||||
<HelpPopup lang={lang} path={path} />
|
|
||||||
{children}
|
|
||||||
{prefix}
|
|
||||||
<label>
|
|
||||||
{label ?? pathLocale(lang, path, `${path.last()}`)}
|
|
||||||
{active && <div class="node-menu">
|
|
||||||
{actions?.map(a => <div key={a.label} class="menu-item">
|
|
||||||
<Btn icon={a.icon} tooltip={localize(lang, a.label)} tooltipLoc="se" onClick={() => a.onSelect()}/>
|
|
||||||
<span>{a.description ?? localize(lang, a.label)}</span>
|
|
||||||
</div>)}
|
|
||||||
<div class="menu-item">
|
|
||||||
<Btn icon="copy" tooltip={localize(lang, 'copy_context')} tooltipLoc="se" onClick={() => navigator.clipboard.writeText(context)} />
|
|
||||||
<span>{context}</span>
|
|
||||||
</div>
|
|
||||||
</div>}
|
|
||||||
</label>
|
|
||||||
{suffix}
|
|
||||||
</div>
|
|
||||||
{body && <div class="node-body">{body}</div>}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const MemoedTreeNode = memo(TreeNode, (prev, next) => {
|
|
||||||
return prev.schema === next.schema
|
|
||||||
&& prev.lang === next.lang
|
|
||||||
&& prev.path.equals(next.path)
|
|
||||||
&& deepEqual(prev.ctx, next.ctx)
|
|
||||||
&& deepEqual(prev.value, next.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
function isEnum(value?: ValidationOption | EnumOption): value is EnumOption {
|
|
||||||
return !!(value as any)?.enum
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashString(str: string) {
|
|
||||||
var hash = 0, i, chr
|
|
||||||
for (i = 0; i < str.length; i++) {
|
|
||||||
chr = str.charCodeAt(i)
|
|
||||||
hash = ((hash << 5) - hash) + chr
|
|
||||||
hash |= 0
|
|
||||||
}
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
function pathLocale(lang: string, path: Path, ...params: string[]) {
|
|
||||||
const ctx = path.getContext()
|
|
||||||
for (let i = 0; i < ctx.length; i += 1) {
|
|
||||||
const key = ctx.slice(i).join('.')
|
|
||||||
const result = localize(lang, key, ...params)
|
|
||||||
if (key !== result) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx[ctx.length - 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
function ErrorPopup({ lang, path, nested }: { lang: string, path: ModelPath, nested?: boolean }) {
|
|
||||||
if (path.model instanceof ModelWrapper) {
|
|
||||||
path = path.model.map(path).withModel(path.model)
|
|
||||||
}
|
|
||||||
const e = nested
|
|
||||||
? path.model.errors.getAll().filter(e => e.path.startsWith(path))
|
|
||||||
: path.model.errors.get(path, true)
|
|
||||||
if (e.length === 0) return null
|
|
||||||
const message = localize(lang, e[0].error, ...(e[0].params ?? []))
|
|
||||||
return popupIcon('node-error', 'issue_opened', message)
|
|
||||||
}
|
|
||||||
|
|
||||||
function HelpPopup({ lang, path }: { lang: string, path: Path }) {
|
|
||||||
const key = path.contextPush('help').getContext().join('.')
|
|
||||||
const message = localize(lang, key)
|
|
||||||
if (message === key) return null
|
|
||||||
return popupIcon('node-help', 'info', message)
|
|
||||||
}
|
|
||||||
|
|
||||||
const popupIcon = (type: string, icon: keyof typeof Octicon, popup: string) => {
|
|
||||||
const [active, setActive] = useFocus()
|
|
||||||
|
|
||||||
return <div class={`node-icon ${type}${active ? ' show' : ''}`} onClick={() => setActive()}>
|
|
||||||
{Octicon[icon]}
|
|
||||||
<span class="icon-popup">{popup}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDecorated(context: string | undefined, value: any) {
|
|
||||||
return context === 'feature'
|
|
||||||
&& value?.type?.replace(/^minecraft:/, '') === 'decorated'
|
|
||||||
&& isObject(value?.config)
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDecoratorsWrapper(originalFields: NodeChildren, path: ModelPath, value: any) {
|
|
||||||
const decorators: any[] = []
|
|
||||||
const feature = iterateNestedDecorators(value, decorators)
|
|
||||||
const fields = {
|
|
||||||
type: originalFields.type,
|
|
||||||
config: ObjectNode({
|
|
||||||
decorators: ListNode(CachedDecorator),
|
|
||||||
feature: CachedFeature,
|
|
||||||
}, { context: 'feature.decorated' }),
|
|
||||||
}
|
|
||||||
const schema = ObjectNode(fields, { context: 'feature' })
|
|
||||||
const featurePath = new Path(['config', 'feature'])
|
|
||||||
const decoratorsPath = new Path(['config', 'decorators'])
|
|
||||||
const model = path.getModel()
|
|
||||||
const wrapper: ModelWrapper = new ModelWrapper(schema, path => {
|
|
||||||
if (path.startsWith(featurePath)) {
|
|
||||||
return new Path([...[...Array(decorators.length - 1)].flatMap(() => ['config', 'feature']), ...path.modelArr])
|
|
||||||
} else if (path.startsWith(decoratorsPath)) {
|
|
||||||
if (path.modelArr.length === 2) {
|
|
||||||
return new Path([])
|
|
||||||
}
|
|
||||||
const index = path.modelArr[2]
|
|
||||||
if (typeof index === 'number') {
|
|
||||||
return new Path([...[...Array(index)].flatMap(() => ['config', 'feature']), 'config', 'decorator', ...path.modelArr.slice(3)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}, path => {
|
|
||||||
if (path.equals(decoratorsPath)) {
|
|
||||||
const newDecorators: any[] = []
|
|
||||||
iterateNestedDecorators(model.data, newDecorators)
|
|
||||||
return newDecorators
|
|
||||||
}
|
|
||||||
return model.get(wrapper.map(path))
|
|
||||||
}, (path, value, silent) => {
|
|
||||||
if (path.startsWith(featurePath)) {
|
|
||||||
const newDecorators: any[] = []
|
|
||||||
iterateNestedDecorators(model.data, newDecorators)
|
|
||||||
const newPath =new Path([...[...Array(newDecorators.length - 1)].flatMap(() => ['config', 'feature']), ...path.modelArr])
|
|
||||||
return model.set(newPath, value, silent)
|
|
||||||
} else if (path.startsWith(decoratorsPath)) {
|
|
||||||
const index = path.modelArr[2]
|
|
||||||
if (path.modelArr.length === 2) {
|
|
||||||
const feature = wrapper.get(featurePath)
|
|
||||||
return model.set(new Path(), produceNestedDecorators(feature, value), silent)
|
|
||||||
} else if (typeof index === 'number') {
|
|
||||||
if (path.modelArr.length === 3 && value === undefined) {
|
|
||||||
const feature = wrapper.get(featurePath)
|
|
||||||
const newDecorators: any[] = []
|
|
||||||
iterateNestedDecorators(model.data, newDecorators)
|
|
||||||
newDecorators.splice(index, 1)
|
|
||||||
const newValue = produceNestedDecorators(feature, newDecorators)
|
|
||||||
return model.set(new Path(), newValue, silent)
|
|
||||||
} else {
|
|
||||||
const newPath = new Path([...[...Array(index)].flatMap(() => ['config', 'feature']), 'config', 'decorator', ...path.modelArr.slice(3)])
|
|
||||||
return model.set(newPath, value, silent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
model.set(path, value, silent)
|
|
||||||
})
|
|
||||||
wrapper.data = {
|
|
||||||
type: model.data.type,
|
|
||||||
config: {
|
|
||||||
decorators,
|
|
||||||
feature,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
wrapper.errors = model.errors
|
|
||||||
return { fields, wrapper }
|
|
||||||
}
|
|
||||||
|
|
||||||
function iterateNestedDecorators(value: any, decorators: any[]): any {
|
|
||||||
if (value?.type?.replace(/^minecraft:/, '') !== 'decorated') {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
if (!isObject(value?.config)) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
decorators.push({ id: decorators.length, node: value.config.decorator })
|
|
||||||
return iterateNestedDecorators(value.config.feature ?? '', decorators)
|
|
||||||
}
|
|
||||||
|
|
||||||
function produceNestedDecorators(feature: any, decorators: any[]): any {
|
|
||||||
if (decorators.length === 0) return feature
|
|
||||||
return {
|
|
||||||
type: 'minecraft:decorated',
|
|
||||||
config: {
|
|
||||||
decorator: decorators.shift().node,
|
|
||||||
feature: produceNestedDecorators(feature, decorators),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import type { DataModel, Hook } from '@mcschema/core'
|
|
||||||
import { ModelPath, relativePath } from '@mcschema/core'
|
|
||||||
import type { BlockStateRegistry } from '../services/index.js'
|
|
||||||
|
|
||||||
export function getOutput(model: DataModel, blockStates: BlockStateRegistry): any {
|
|
||||||
return model.schema.hook(transformOutput, new ModelPath(model), model.data, { blockStates })
|
|
||||||
}
|
|
||||||
|
|
||||||
export type OutputProps = {
|
|
||||||
blockStates: BlockStateRegistry,
|
|
||||||
}
|
|
||||||
export const transformOutput: Hook<[any, OutputProps], any> = {
|
|
||||||
base({}, _, value) {
|
|
||||||
return value
|
|
||||||
},
|
|
||||||
|
|
||||||
choice({ switchNode }, path, value, props) {
|
|
||||||
return switchNode.hook(this, path, value, props)
|
|
||||||
},
|
|
||||||
|
|
||||||
list({ children }, path, value, props) {
|
|
||||||
if (!Array.isArray(value)) return value
|
|
||||||
const res = value.map((obj, index) =>
|
|
||||||
children.hook(this, path.push(index), obj.node, props)
|
|
||||||
)
|
|
||||||
for (const a of Object.getOwnPropertySymbols(value)) {
|
|
||||||
res[a as any] = value[a as any]
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
|
|
||||||
map({ children, config }, path, value, props) {
|
|
||||||
if (value === undefined) return undefined
|
|
||||||
const blockState = config.validation?.validator === 'block_state_map'? props.blockStates?.[relativePath(path, config.validation.params.id).get()] : null
|
|
||||||
const res: any = {}
|
|
||||||
Object.keys(value).forEach(f => {
|
|
||||||
if (blockState) {
|
|
||||||
if (!Object.keys(blockState.properties ?? {}).includes(f)) return
|
|
||||||
}
|
|
||||||
res[f] = children.hook(this, path.push(f), value[f], props)
|
|
||||||
})
|
|
||||||
for (const a of Object.getOwnPropertySymbols(value)) {
|
|
||||||
res[a as any] = value[a]
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
|
|
||||||
object({ getActiveFields }, path, value, props) {
|
|
||||||
if (value === undefined || value === null || typeof value !== 'object') {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
const res: any = {}
|
|
||||||
const activeFields = getActiveFields(path)
|
|
||||||
Object.keys(activeFields)
|
|
||||||
.filter(k => activeFields[k].enabled(path))
|
|
||||||
.forEach(f => {
|
|
||||||
const out = activeFields[f].hook(this, path.push(f), value[f], props)
|
|
||||||
if (out !== undefined && out !== null) {
|
|
||||||
res[f] = out
|
|
||||||
}
|
|
||||||
})
|
|
||||||
for (const a of Object.getOwnPropertySymbols(value)) {
|
|
||||||
res[a as any] = value[a]
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,20 +1,13 @@
|
|||||||
import type { CollectionRegistry } from '@mcschema/core'
|
|
||||||
import config from '../Config.js'
|
import config from '../Config.js'
|
||||||
import { Store } from '../Store.js'
|
import { Store } from '../Store.js'
|
||||||
import { message } from '../Utils.js'
|
import { message } from '../Utils.js'
|
||||||
import type { BlockStateRegistry, VersionId } from './Schemas.js'
|
import type { VersionId } from './Schemas.js'
|
||||||
import { checkVersion } from './Schemas.js'
|
import { checkVersion } from './Schemas.js'
|
||||||
|
|
||||||
const CACHE_NAME = 'misode-v2'
|
const CACHE_NAME = 'misode-v2'
|
||||||
const CACHE_LATEST_VERSION = 'cached_latest_version'
|
const CACHE_LATEST_VERSION = 'cached_latest_version'
|
||||||
const CACHE_PATCH = 'misode_cache_patch'
|
const CACHE_PATCH = 'misode_cache_patch'
|
||||||
|
|
||||||
type Version = {
|
|
||||||
id: string,
|
|
||||||
ref?: string,
|
|
||||||
dynamic?: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
declare var __LATEST_VERSION__: string
|
declare var __LATEST_VERSION__: string
|
||||||
export const latestVersion = __LATEST_VERSION__ ?? ''
|
export const latestVersion = __LATEST_VERSION__ ?? ''
|
||||||
const mcmetaUrl = 'https://raw.githubusercontent.com/misode/mcmeta'
|
const mcmetaUrl = 'https://raw.githubusercontent.com/misode/mcmeta'
|
||||||
@@ -46,51 +39,10 @@ async function validateCache(version: RefInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchData(versionId: string, collectionTarget: CollectionRegistry, blockStateTarget: BlockStateRegistry) {
|
|
||||||
const version = config.versions.find(v => v.id === versionId) as Version | undefined
|
|
||||||
if (!version) {
|
|
||||||
console.error(`[fetchData] Unknown version ${version} in ${JSON.stringify(config.versions)}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await validateCache(version)
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
_fetchRegistries(version, collectionTarget),
|
|
||||||
_fetchBlockStateMap(version, blockStateTarget),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
async function _fetchRegistries(version: Version, target: CollectionRegistry) {
|
|
||||||
console.debug(`[fetchRegistries] ${version.id}`)
|
|
||||||
try {
|
|
||||||
const data = await cachedFetch<any>(`${mcmeta(version, 'summary')}/registries/data.min.json`)
|
|
||||||
for (const id in data) {
|
|
||||||
target.register(id, data[id].map((e: string) => 'minecraft:' + e))
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Error occurred while fetching registries:', message(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function _fetchBlockStateMap(version: Version, target: BlockStateRegistry) {
|
|
||||||
console.debug(`[fetchBlockStateMap] ${version.id}`)
|
|
||||||
try {
|
|
||||||
const data = await cachedFetch<any>(`${mcmeta(version, 'summary')}/blocks/data.min.json`)
|
|
||||||
for (const id in data) {
|
|
||||||
target['minecraft:' + id] = {
|
|
||||||
properties: data[id][0],
|
|
||||||
default: data[id][1],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Error occurred while fetching block state map:', message(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchRegistries(versionId: VersionId) {
|
export async function fetchRegistries(versionId: VersionId) {
|
||||||
console.debug(`[fetchRegistries] ${versionId}`)
|
console.debug(`[fetchRegistries] ${versionId}`)
|
||||||
const version = config.versions.find(v => v.id === versionId)!
|
const version = config.versions.find(v => v.id === versionId)!
|
||||||
|
await validateCache(version)
|
||||||
try {
|
try {
|
||||||
const data = await cachedFetch<any>(`${mcmeta(version, 'summary')}/registries/data.min.json`)
|
const data = await cachedFetch<any>(`${mcmeta(version, 'summary')}/registries/data.min.json`)
|
||||||
const result = new Map<string, string[]>()
|
const result = new Map<string, string[]>()
|
||||||
@@ -103,10 +55,16 @@ export async function fetchRegistries(versionId: VersionId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BlockStateData {
|
||||||
|
properties: Record<string, string[]>
|
||||||
|
default: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchBlockStates(versionId: VersionId) {
|
export async function fetchBlockStates(versionId: VersionId) {
|
||||||
console.debug(`[fetchBlockStates] ${versionId}`)
|
console.debug(`[fetchBlockStates] ${versionId}`)
|
||||||
const version = config.versions.find(v => v.id === versionId)!
|
const version = config.versions.find(v => v.id === versionId)!
|
||||||
const result = new Map<string, {properties: Record<string, string[]>, default: Record<string, string>}>()
|
const result = new Map<string, BlockStateData>()
|
||||||
|
await validateCache(version)
|
||||||
try {
|
try {
|
||||||
const data = await cachedFetch<any>(`${mcmeta(version, 'summary')}/blocks/data.min.json`)
|
const data = await cachedFetch<any>(`${mcmeta(version, 'summary')}/blocks/data.min.json`)
|
||||||
for (const id in data) {
|
for (const id in data) {
|
||||||
@@ -128,6 +86,7 @@ export async function fetchItemComponents(versionId: VersionId) {
|
|||||||
if (!checkVersion(versionId, '1.20.5')) {
|
if (!checkVersion(versionId, '1.20.5')) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
await validateCache(version)
|
||||||
try {
|
try {
|
||||||
const data = await cachedFetch<Record<string, Record<string, unknown>>>(`${mcmeta(version, 'summary')}/item_components/data.min.json`)
|
const data = await cachedFetch<Record<string, Record<string, unknown>>>(`${mcmeta(version, 'summary')}/item_components/data.min.json`)
|
||||||
for (const [id, components] of Object.entries(data)) {
|
for (const [id, components] of Object.entries(data)) {
|
||||||
@@ -152,6 +111,7 @@ export async function fetchItemComponents(versionId: VersionId) {
|
|||||||
export async function fetchPreset(versionId: VersionId, registry: string, id: string) {
|
export async function fetchPreset(versionId: VersionId, registry: string, id: string) {
|
||||||
console.debug(`[fetchPreset] ${versionId} ${registry} ${id}`)
|
console.debug(`[fetchPreset] ${versionId} ${registry} ${id}`)
|
||||||
const version = config.versions.find(v => v.id === versionId)!
|
const version = config.versions.find(v => v.id === versionId)!
|
||||||
|
await validateCache(version)
|
||||||
try {
|
try {
|
||||||
let url
|
let url
|
||||||
if (id.startsWith('immersive_weathering:')) {
|
if (id.startsWith('immersive_weathering:')) {
|
||||||
|
|||||||
@@ -1,77 +1,27 @@
|
|||||||
import type { CollectionRegistry, INode, SchemaRegistry } from '@mcschema/core'
|
|
||||||
import { ChoiceNode, DataModel, Reference, StringNode } from '@mcschema/core'
|
|
||||||
import config from '../Config.js'
|
import config from '../Config.js'
|
||||||
import { initPartners } from '../partners/index.js'
|
|
||||||
import { message } from '../Utils.js'
|
import { message } from '../Utils.js'
|
||||||
import { fetchData } from './DataFetcher.js'
|
import type { BlockStateData } from './DataFetcher.js'
|
||||||
|
import { fetchBlockStates, fetchRegistries } from './DataFetcher.js'
|
||||||
|
|
||||||
export const VersionIds = ['1.15', '1.16', '1.17', '1.18', '1.18.2', '1.19', '1.19.3', '1.19.4', '1.20', '1.20.2', '1.20.3', '1.20.5', '1.21', '1.21.2'] as const
|
export const VersionIds = ['1.15', '1.16', '1.17', '1.18', '1.18.2', '1.19', '1.19.3', '1.19.4', '1.20', '1.20.2', '1.20.3', '1.20.5', '1.21', '1.21.2'] as const
|
||||||
export type VersionId = typeof VersionIds[number]
|
export type VersionId = typeof VersionIds[number]
|
||||||
|
|
||||||
export const DEFAULT_VERSION: VersionId = '1.21'
|
export const DEFAULT_VERSION: VersionId = '1.21'
|
||||||
|
|
||||||
export type BlockStateRegistry = {
|
interface VersionData {
|
||||||
[block: string]: {
|
registries: Map<string, string[]>
|
||||||
properties?: {
|
blockStates: Map<string, BlockStateData>
|
||||||
[key: string]: string[],
|
|
||||||
},
|
|
||||||
default?: {
|
|
||||||
[key: string]: string,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type VersionData = {
|
|
||||||
collections: CollectionRegistry,
|
|
||||||
schemas: SchemaRegistry,
|
|
||||||
blockStates: BlockStateRegistry,
|
|
||||||
}
|
|
||||||
const Versions: Record<string, VersionData | Promise<VersionData>> = {}
|
const Versions: Record<string, VersionData | Promise<VersionData>> = {}
|
||||||
|
|
||||||
type ModelData = {
|
|
||||||
model: DataModel,
|
|
||||||
version: VersionId,
|
|
||||||
}
|
|
||||||
const Models: Record<string, ModelData> = {}
|
|
||||||
|
|
||||||
const versionGetter: {
|
|
||||||
[versionId in VersionId]: () => Promise<{
|
|
||||||
getCollections: () => CollectionRegistry,
|
|
||||||
getSchemas: (collections: CollectionRegistry) => SchemaRegistry,
|
|
||||||
}>
|
|
||||||
} = {
|
|
||||||
1.15: () => import('@mcschema/java-1.15'),
|
|
||||||
1.16: () => import('@mcschema/java-1.16'),
|
|
||||||
1.17: () => import('@mcschema/java-1.17'),
|
|
||||||
1.18: () => import('@mcschema/java-1.18'),
|
|
||||||
'1.18.2': () => import('@mcschema/java-1.18.2'),
|
|
||||||
1.19: () => import('@mcschema/java-1.19'),
|
|
||||||
'1.19.3': () => import('@mcschema/java-1.19.3'),
|
|
||||||
'1.19.4': () => import('@mcschema/java-1.19.4'),
|
|
||||||
'1.20': () => import('@mcschema/java-1.20'),
|
|
||||||
'1.20.2': () => import('@mcschema/java-1.20.2'),
|
|
||||||
'1.20.3': () => import('@mcschema/java-1.20.3'),
|
|
||||||
'1.20.5': () => import('@mcschema/java-1.20.5'),
|
|
||||||
1.21: () => import('@mcschema/java-1.21'),
|
|
||||||
'1.21.2': () => import('@mcschema/java-1.21.2'),
|
|
||||||
}
|
|
||||||
|
|
||||||
export let CachedDecorator: INode<any>
|
|
||||||
export let CachedFeature: INode<any>
|
|
||||||
export let CachedCollections: CollectionRegistry
|
|
||||||
export let CachedSchemas: SchemaRegistry
|
|
||||||
|
|
||||||
async function getVersion(id: VersionId): Promise<VersionData> {
|
async function getVersion(id: VersionId): Promise<VersionData> {
|
||||||
if (!Versions[id]) {
|
if (!Versions[id]) {
|
||||||
Versions[id] = (async () => {
|
Versions[id] = (async () => {
|
||||||
try {
|
try {
|
||||||
const mcschema = await versionGetter[id]()
|
const registries = await fetchRegistries(id)
|
||||||
const collections = mcschema.getCollections()
|
const blockStates= await fetchBlockStates(id)
|
||||||
const blockStates: BlockStateRegistry = {}
|
Versions[id] = { registries, blockStates }
|
||||||
await fetchData(id, collections, blockStates)
|
|
||||||
const schemas = mcschema.getSchemas(collections)
|
|
||||||
initPartners(schemas, collections, id)
|
|
||||||
Versions[id] = { collections, schemas, blockStates }
|
|
||||||
return Versions[id]
|
return Versions[id]
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Cannot get version "${id}": ${message(e)}`)
|
throw new Error(`Cannot get version "${id}": ${message(e)}`)
|
||||||
@@ -82,65 +32,26 @@ async function getVersion(id: VersionId): Promise<VersionData> {
|
|||||||
return Versions[id]
|
return Versions[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getModel(version: VersionId, id: string): Promise<DataModel> {
|
export async function getBlockStates(version: VersionId): Promise<Map<string, BlockStateData>> {
|
||||||
if (!Models[id] || Models[id].version !== version) {
|
|
||||||
const versionData = await getVersion(version)
|
|
||||||
|
|
||||||
CachedDecorator = Reference(versionData.schemas, 'configured_decorator')
|
|
||||||
CachedFeature = ChoiceNode([
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
node: StringNode(versionData.collections, { validator: 'resource', params: { pool: '$worldgen/configured_feature' } }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
node: Reference(versionData.schemas, 'configured_feature'),
|
|
||||||
},
|
|
||||||
], { choiceContext: 'feature' })
|
|
||||||
|
|
||||||
const schemaName = config.generators.find(g => g.id === id)?.schema
|
|
||||||
if (!schemaName) {
|
|
||||||
throw new Error(`Cannot find model ${id}`)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const schema = versionData.schemas.get(schemaName)
|
|
||||||
const model = new DataModel(schema, { wrapLists: true })
|
|
||||||
if (Models[id]) {
|
|
||||||
model.reset(Models[id].model.data, false)
|
|
||||||
} else {
|
|
||||||
model.validate(true)
|
|
||||||
model.history = [JSON.stringify(model.data)]
|
|
||||||
}
|
|
||||||
Models[id] = { model, version }
|
|
||||||
} catch (e) {
|
|
||||||
const err = new Error(`Cannot get generator "${id}" for version "${version}": ${message(e)}`)
|
|
||||||
if (e instanceof Error) err.stack = e.stack
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Models[id].model
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCollections(version: VersionId): Promise<CollectionRegistry> {
|
|
||||||
const versionData = await getVersion(version)
|
|
||||||
CachedCollections = versionData.collections
|
|
||||||
return versionData.collections
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getBlockStates(version: VersionId): Promise<BlockStateRegistry> {
|
|
||||||
const versionData = await getVersion(version)
|
const versionData = await getVersion(version)
|
||||||
return versionData.blockStates
|
return versionData.blockStates
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSchemas(version: VersionId): Promise<SchemaRegistry> {
|
|
||||||
const versionData = await getVersion(version)
|
|
||||||
CachedSchemas = versionData.schemas
|
|
||||||
return versionData.schemas
|
|
||||||
}
|
|
||||||
|
|
||||||
export function checkVersion(versionId: string, minVersionId: string | undefined, maxVersionId?: string) {
|
export function checkVersion(versionId: string, minVersionId: string | undefined, maxVersionId?: string) {
|
||||||
const version = config.versions.findIndex(v => v.id === versionId)
|
const version = config.versions.findIndex(v => v.id === versionId)
|
||||||
const minVersion = minVersionId ? config.versions.findIndex(v => v.id === minVersionId) : 0
|
const minVersion = minVersionId ? config.versions.findIndex(v => v.id === minVersionId) : 0
|
||||||
const maxVersion = maxVersionId ? config.versions.findIndex(v => v.id === maxVersionId) : config.versions.length - 1
|
const maxVersion = maxVersionId ? config.versions.findIndex(v => v.id === maxVersionId) : config.versions.length - 1
|
||||||
return minVersion <= version && version <= maxVersion
|
return minVersion <= version && version <= maxVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FileModel {
|
||||||
|
get text(): string
|
||||||
|
get data(): any
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMockFileModel(): FileModel {
|
||||||
|
return {
|
||||||
|
text: '{}',
|
||||||
|
data: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user