diff options
Diffstat (limited to 'src/SMAPI.Web/wwwroot')
| -rw-r--r-- | src/SMAPI.Web/wwwroot/Content/css/json-validator.css | 111 | ||||
| -rw-r--r-- | src/SMAPI.Web/wwwroot/Content/css/main.css | 2 | ||||
| -rw-r--r-- | src/SMAPI.Web/wwwroot/Content/css/mods.css | 57 | ||||
| -rw-r--r-- | src/SMAPI.Web/wwwroot/Content/js/json-validator.js | 179 | ||||
| -rw-r--r-- | src/SMAPI.Web/wwwroot/Content/js/log-parser.js | 17 | ||||
| -rw-r--r-- | src/SMAPI.Web/wwwroot/Content/js/mods.js | 3 | ||||
| -rw-r--r-- | src/SMAPI.Web/wwwroot/SMAPI.metadata.json (renamed from src/SMAPI.Web/wwwroot/StardewModdingAPI.metadata.json) | 237 | ||||
| -rw-r--r-- | src/SMAPI.Web/wwwroot/schemas/content-patcher.json | 389 | ||||
| -rw-r--r-- | src/SMAPI.Web/wwwroot/schemas/manifest.json | 147 |
9 files changed, 1008 insertions, 134 deletions
diff --git a/src/SMAPI.Web/wwwroot/Content/css/json-validator.css b/src/SMAPI.Web/wwwroot/Content/css/json-validator.css new file mode 100644 index 00000000..cd117694 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/css/json-validator.css @@ -0,0 +1,111 @@ +/********* +** Main layout +*********/ +#content { + max-width: 100%; +} + +#output { + padding: 10px; + overflow: auto; +} + +#output table td { + font-family: monospace; +} + +#output table tr th, +#output table tr td { + padding: 0 0.75rem; + white-space: pre-wrap; +} + + +/********* +** Result banner +*********/ +.banner { + border: 2px solid gray; + border-radius: 5px; + margin-top: 1em; + padding: 1em; +} + +.banner.success { + border-color: green; + background: #CFC; +} + +.banner.error { + border-color: red; + background: #FCC; +} + +/********* +** Validation results +*********/ +.table { + border-bottom: 1px dashed #888888; + margin-bottom: 5px; +} + +#metadata th, #metadata td { + text-align: left; + padding-right: 0.7em; +} + +.table { + border: 1px solid #000000; + background: #ffffff; + border-radius: 5px; + border-spacing: 1px; + overflow: hidden; + cursor: default; + box-shadow: 1px 1px 1px 1px #dddddd; +} + +.table tr { + background: #eee; +} + +.table tr:nth-child(even) { + background: #fff; +} + +#output div.sunlight-line-highlight-active { + background-color: #eeeacc; +} + +.footer-tip { + color: gray; + font-size: 0.9em; +} + +.footer-tip a { + color: gray; +} + +/********* +** Upload form +*********/ +#input { + width: 100%; + height: 20em; + max-height: 70%; + margin: auto; + box-sizing: border-box; + border-radius: 5px; + border: 1px solid #000088; + outline: none; + box-shadow: inset 0px 0px 1px 1px rgba(0, 0, 192, .2); +} + +#submit { + font-size: 1.5em; + border-radius: 5px; + outline: none; + box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, .2); + cursor: pointer; + border: 1px solid #008800; + background-color: #cfc; +} diff --git a/src/SMAPI.Web/wwwroot/Content/css/main.css b/src/SMAPI.Web/wwwroot/Content/css/main.css index 57eeee88..dcc7a798 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/main.css +++ b/src/SMAPI.Web/wwwroot/Content/css/main.css @@ -73,7 +73,7 @@ a { } #sidebar h4 { - margin: 0 0 0.2em 0; + margin: 1.5em 0 0.2em 0; width: 10em; border-bottom: 1px solid #CCC; font-size: 0.8em; diff --git a/src/SMAPI.Web/wwwroot/Content/css/mods.css b/src/SMAPI.Web/wwwroot/Content/css/mods.css index fc5fff47..1c2b8056 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/mods.css +++ b/src/SMAPI.Web/wwwroot/Content/css/mods.css @@ -15,30 +15,6 @@ border: 3px solid darkgreen; } -table.wikitable { - background-color:#f8f9fa; - color:#222; - border:1px solid #a2a9b1; - border-collapse:collapse -} - -table.wikitable > tr > th, -table.wikitable > tr > td, -table.wikitable > * > tr > th, -table.wikitable > * > tr > td { - border:1px solid #a2a9b1; - padding:0.2em 0.4em -} - -table.wikitable > tr > th, -table.wikitable > * > tr > th { - background-color:#eaecf0; -} - -table.wikitable > caption { - font-weight:bold -} - #options { margin-bottom: 1em; } @@ -73,6 +49,39 @@ table.wikitable > caption { opacity: 0.5; } +div.error { + padding: 2em 0; + color: red; + font-weight: bold; +} + +/********* +** Mod list +*********/ +table.wikitable { + background-color:#f8f9fa; + color:#222; + border:1px solid #a2a9b1; + border-collapse:collapse +} + +table.wikitable > tr > th, +table.wikitable > tr > td, +table.wikitable > * > tr > th, +table.wikitable > * > tr > td { + border:1px solid #a2a9b1; + padding:0.2em 0.4em +} + +table.wikitable > tr > th, +table.wikitable > * > tr > th { + background-color:#eaecf0; +} + +table.wikitable > caption { + font-weight:bold +} + #mod-list { font-size: 0.9em; } diff --git a/src/SMAPI.Web/wwwroot/Content/js/json-validator.js b/src/SMAPI.Web/wwwroot/Content/js/json-validator.js new file mode 100644 index 00000000..5499cef6 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/js/json-validator.js @@ -0,0 +1,179 @@ +/* globals $ */ + +var smapi = smapi || {}; + +/** + * Manages the logic for line range selections. + * @param {int} maxLines The maximum number of lines in the content. + */ +smapi.LineNumberRange = function (maxLines) { + var self = this; + + /** + * @var {int} minLine The first line in the selection, or null if no lines selected. + */ + self.minLine = null; + + /** + * @var {int} maxLine The last line in the selection, or null if no lines selected. + */ + self.maxLine = null; + + /** + * Parse line numbers from a URL hash. + * @param {string} hash the URL hash to parse. + */ + self.parseFromUrlHash = function (hash) { + self.minLine = null; + self.maxLine = null; + + // parse hash + var hashParts = hash.match(/^#L(\d+)(?:-L(\d+))?$/); + if (!hashParts || hashParts.length <= 1) + return; + + // extract min/max lines + self.minLine = parseInt(hashParts[1]); + self.maxLine = parseInt(hashParts[2]) || self.minLine; + }; + + /** + * Generate a URL hash for the current line range. + * @returns {string} The generated URL hash. + */ + self.buildHash = function() { + if (!self.minLine) + return ""; + else if (self.minLine === self.maxLine) + return "#L" + self.minLine; + else + return "#L" + self.minLine + "-L" + self.maxLine; + } + + /** + * Get a list of all selected lines. + * @returns {Array<int>} The selected line numbers. + */ + self.getLinesSelected = function() { + // format + if (!self.minLine) + return []; + + var lines = []; + for (var i = self.minLine; i <= self.maxLine; i++) + lines.push(i); + return lines; + }; + + return self; +}; + +/** + * UI logic for the JSON validator page. + * @param {any} sectionUrl The base JSON validator page URL. + * @param {any} pasteID The Pastebin paste ID for the content being viewed, if any. + */ +smapi.jsonValidator = function (sectionUrl, pasteID) { + /** + * The original content element. + */ + var originalContent = $("#raw-content").clone(); + + /** + * The currently highlighted lines. + */ + var selection = new smapi.LineNumberRange(); + + /** + * Rebuild the syntax-highlighted element. + */ + var formatCode = function () { + // reset if needed + $(".sunlight-container").replaceWith(originalContent.clone()); + + // apply default highlighting + Sunlight.highlightAll({ + lineHighlight: selection.getLinesSelected() + }); + + // fix line links + $(".sunlight-line-number-margin a").each(function() { + var link = $(this); + var lineNumber = parseInt(link.text()); + link + .attr("id", "L" + lineNumber) + .attr("href", "#L" + lineNumber) + .removeAttr("name") + .data("line-number", lineNumber); + }); + }; + + /** + * Scroll the page so the selected range is visible. + */ + var scrollToRange = function() { + if (!selection.minLine) + return; + + var targetLine = Math.max(1, selection.minLine - 5); + $("#L" + targetLine).get(0).scrollIntoView(); + }; + + /** + * Initialize the JSON validator page. + */ + var init = function () { + // set initial code formatting + selection.parseFromUrlHash(location.hash); + formatCode(); + scrollToRange(); + + // update code formatting on hash change + $(window).on("hashchange", function() { + selection.parseFromUrlHash(location.hash); + formatCode(); + scrollToRange(); + }); + + // change format + $("#output #format").on("change", function() { + var schemaName = $(this).val(); + location.href = new URL(schemaName + "/" + pasteID, sectionUrl).toString(); + }); + + // upload form + var input = $("#input"); + if (input.length) { + // disable submit if it's empty + var toggleSubmit = function () { + var hasText = !!input.val().trim(); + submit.prop("disabled", !hasText); + }; + input.on("input", toggleSubmit); + toggleSubmit(); + + // drag & drop file + input.on({ + 'dragover dragenter': function (e) { + e.preventDefault(); + e.stopPropagation(); + }, + 'drop': function (e) { + var dataTransfer = e.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.files.length) { + e.preventDefault(); + e.stopPropagation(); + var file = dataTransfer.files[0]; + var reader = new FileReader(); + reader.onload = $.proxy(function (file, $input, event) { + $input.val(event.target.result); + toggleSubmit(); + }, this, file, $("#input")); + reader.readAsText(file); + } + } + }); + } + }; + init(); +}; diff --git a/src/SMAPI.Web/wwwroot/Content/js/log-parser.js b/src/SMAPI.Web/wwwroot/Content/js/log-parser.js index e87a1a5c..e6c7591c 100644 --- a/src/SMAPI.Web/wwwroot/Content/js/log-parser.js +++ b/src/SMAPI.Web/wwwroot/Content/js/log-parser.js @@ -23,7 +23,7 @@ smapi.logParser = function (data, sectionUrl) { } // set local time started - if(data) + if (data) data.localTimeStarted = ("0" + data.logStarted.getHours()).slice(-2) + ":" + ("0" + data.logStarted.getMinutes()).slice(-2); // init app @@ -100,7 +100,7 @@ smapi.logParser = function (data, sectionUrl) { updateModFilters(); }, - filtersAllow: function(modId, level) { + filtersAllow: function (modId, level) { return this.showMods[modId] !== false && this.showLevels[level] !== false; }, @@ -121,16 +121,15 @@ smapi.logParser = function (data, sectionUrl) { var submit = $("#submit"); // instruction OS chooser - var chooseSystem = function() { + var chooseSystem = function () { systemInstructions.hide(); systemInstructions.filter("[data-os='" + $("input[name='os']:checked").val() + "']").show(); - } + }; systemOptions.on("click", chooseSystem); chooseSystem(); // disable submit if it's empty - var toggleSubmit = function() - { + var toggleSubmit = function () { var hasText = !!input.val().trim(); submit.prop("disabled", !hasText); } @@ -139,18 +138,18 @@ smapi.logParser = function (data, sectionUrl) { // drag & drop file input.on({ - 'dragover dragenter': function(e) { + 'dragover dragenter': function (e) { e.preventDefault(); e.stopPropagation(); }, - 'drop': function(e) { + 'drop': function (e) { var dataTransfer = e.originalEvent.dataTransfer; if (dataTransfer && dataTransfer.files.length) { e.preventDefault(); e.stopPropagation(); var file = dataTransfer.files[0]; var reader = new FileReader(); - reader.onload = $.proxy(function(file, $input, event) { + reader.onload = $.proxy(function (file, $input, event) { $input.val(event.target.result); toggleSubmit(); }, this, file, $("#input")); diff --git a/src/SMAPI.Web/wwwroot/Content/js/mods.js b/src/SMAPI.Web/wwwroot/Content/js/mods.js index 130f60be..0394ac4f 100644 --- a/src/SMAPI.Web/wwwroot/Content/js/mods.js +++ b/src/SMAPI.Web/wwwroot/Content/js/mods.js @@ -44,6 +44,7 @@ smapi.modList = function (mods, enableBeta) { download: { value: { chucklefish: { value: true, label: "Chucklefish" }, + curseforge: { value: true, label: "CurseForge" }, moddrop: { value: true, label: "ModDrop" }, nexus: { value: true, label: "Nexus" }, custom: { value: true } @@ -180,6 +181,8 @@ smapi.modList = function (mods, enableBeta) { if (!filters.download.value.chucklefish.value) ignoreSites.push("Chucklefish"); + if (!filters.download.value.curseforge.value) + ignoreSites.push("CurseForge"); if (!filters.download.value.moddrop.value) ignoreSites.push("ModDrop"); if (!filters.download.value.nexus.value) diff --git a/src/SMAPI.Web/wwwroot/StardewModdingAPI.metadata.json b/src/SMAPI.Web/wwwroot/SMAPI.metadata.json index d0c55552..78918bac 100644 --- a/src/SMAPI.Web/wwwroot/StardewModdingAPI.metadata.json +++ b/src/SMAPI.Web/wwwroot/SMAPI.metadata.json @@ -14,11 +14,6 @@ * other fields if no ID was specified. This doesn't include the latest ID, if any. Multiple * variants can be separated with '|'. * - * - MapLocalVersions and MapRemoteVersions correct local manifest versions and remote versions - * during update checks. For example, if the API returns version '1.1-1078' where '1078' is - * intended to be a build number, MapRemoteVersions can map it to '1.1' when comparing to the - * mod's current version. This is only meant to support legacy mods with injected update keys. - * * Versioned metadata * ================== * Each record can also specify extra metadata using the field keys below. @@ -59,15 +54,15 @@ "Default | UpdateKey": "Nexus:2270" }, - "Content Patcher": { - "ID": "Pathoschild.ContentPatcher", - "Default | UpdateKey": "Nexus:1915" - }, + //"Content Patcher": { + // "ID": "Pathoschild.ContentPatcher", + // "Default | UpdateKey": "Nexus:1915" + //}, - "Custom Farming Redux": { - "ID": "Platonymous.CustomFarming", - "Default | UpdateKey": "Nexus:991" - }, + //"Custom Farming Redux": { + // "ID": "Platonymous.CustomFarming", + // "Default | UpdateKey": "Nexus:991" + //}, "Custom Shirts": { "ID": "Platonymous.CustomShirts", @@ -122,116 +117,175 @@ "Default | UpdateKey": "Nexus:1820" }, + /********* + ** Obsolete + *********/ + "Animal Mood Fix": { + "ID": "GPeters-AnimalMoodFix", + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." + }, + + "Bee House Flower Range Fix": { + "ID": "kirbylink.beehousefix", + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "the bee house flower range was fixed in Stardew Valley 1.4." + }, + + "Colored Chests": { + "ID": "4befde5c-731c-4853-8e4b-c5cdf946805f", + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "colored chests were added in Stardew Valley 1.1." + }, + + "Modder Serialization Utility": { + "ID": "SerializerUtils-0-1", + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "it's no longer maintained or used." + }, + + "No Debug Mode": { + "ID": "NoDebugMode", + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "debug mode was removed in SMAPI 1.0." + }, /********* - ** Map versions + ** Broke in SDV 1.4 *********/ - "Adjust Artisan Prices": { - "ID": "ThatNorthernMonkey.AdjustArtisanPrices", - "FormerIDs": "1e36d4ca-c7ef-4dfb-9927-d27a6c3c8bdc", // changed in 0.0.2-pathoschild-update - "MapRemoteVersions": { "0.01": "0.0.1" } + "Fix Dice": { + "ID": "ashley.fixdice", + "~1.1.2 | Status": "AssumeBroken" // crashes game on startup + }, + + "Fix Dice": { + "ID": "ashley.fixdice", + "~1.1.2 | Status": "AssumeBroken" // crashes game on startup + }, + + "Grass Growth": { + "ID": "bcmpinc.GrassGrowth", + "~1.0 | Status": "AssumeBroken" + }, + + "Invite Code Mod": { + "ID": "KOREJJamJar.InviteCodeMod", + "~1.0.1 | Status": "AssumeBroken" + }, + + "Loved Labels": { + "ID": "Advize.LovedLabels", + "~2.2.1-unofficial.2-pathoschild | Status": "AssumeBroken" }, - "Almighty Farming Tool": { - "ID": "439", - "MapRemoteVersions": { - "1.21": "1.2.1", - "1.22-unofficial.3.mizzion": "1.2.2-unofficial.3.mizzion" - } + "Neat Additions": { + "ID": "ilyaki.neatadditions", + "~1.0.3 | Status": "AssumeBroken" }, - "Basic Sprinkler Improved": { - "ID": "lrsk_sdvm_bsi.0117171308", - "MapRemoteVersions": { "1.0.2": "1.0.1-release" } // manifest not updated + "Remote Fridge Storage": { + "ID": "EternalSoap.RemoteFridgeStorage", + "~1.5 | Status": "AssumeBroken" }, - "Better Shipping Box": { - "ID": "Kithio:BetterShippingBox", - "MapLocalVersions": { "1.0.1": "1.0.2" } + "Stack Everything": { + "ID": "cat.stackeverything", + "~2.15 | Status": "AssumeBroken" }, - "Chefs Closet": { - "ID": "Duder.ChefsCloset", - "MapLocalVersions": { "1.3-1": "1.3" } + "Yet Another Harvest With Scythe Mod": { + "ID": "bcmpinc.HarvestWithScythe", + "~1.1 | Status": "AssumeBroken" }, - "Configurable Machines": { - "ID": "21da6619-dc03-4660-9794-8e5b498f5b97", - "MapLocalVersions": { "1.2-beta": "1.2" } + /********* + ** Broke in SMAPI 3.0 (runtime errors due to lifecycle changes) + *********/ + "Advancing Sprinklers": { + "ID": "warix3.advancingsprinklers", + "~1.0.0 | Status": "AssumeBroken" }, - "Crafting Counter": { - "ID": "lolpcgaming.CraftingCounter", - "MapRemoteVersions": { "1.1": "1.0" } // not updated in manifest + "Arcade 2048": { + "ID": "Platonymous.2048", + "~1.0.6 | Status": "AssumeBroken" // possibly due to PyTK }, - "Custom Linens": { - "ID": "Mevima.CustomLinens", - "MapRemoteVersions": { "1.1": "1.0" } // manifest not updated + "Arcade Snake": { + "ID": "Platonymous.Snake", + "~1.1.0 | Status": "AssumeBroken" // possibly due to PyTK }, - "Dynamic Horses": { - "ID": "Bpendragon-DynamicHorses", - "MapRemoteVersions": { "1.2": "1.1-release" } // manifest not updated + "Better Sprinklers": { + "ID": "Speeder.BetterSprinklers", + "~2.3.1-unofficial.7-pathoschild | Status": "AssumeBroken" }, - "Dynamic Machines": { - "ID": "DynamicMachines", - "MapLocalVersions": { "1.1": "1.1.1" } + "Content Patcher": { + "ID": "Pathoschild.ContentPatcher", + "Default | UpdateKey": "Nexus:1915", + "~1.6.4 | Status": "AssumeBroken" }, - "Multiple Sprites and Portraits On Rotation (File Loading)": { - "ID": "FileLoading", - "MapLocalVersions": { "1.1": "1.12" } + "Current Location (Vrakyas)": { + "ID": "Vrakyas.CurrentLocation", + "~1.5.4 | Status": "AssumeBroken" }, - "Relationship Status": { - "ID": "relationshipstatus", - "MapRemoteVersions": { "1.0.5": "1.0.4" } // not updated in manifest + "Custom Adventure Guild Challenges": { + "ID": "DefenTheNation.CustomGuildChallenges", + "~1.8 | Status": "AssumeBroken" }, - "ReRegeneration": { - "ID": "lrsk_sdvm_rerg.0925160827", - "MapLocalVersions": { "1.1.2-release": "1.1.2" } + "Custom Farming Redux": { + "ID": "Platonymous.CustomFarming", + "Default | UpdateKey": "Nexus:991", + "~2.10.10 | Status": "AssumeBroken" // possibly due to PyTK }, - "Showcase Mod": { - "ID": "Igorious.Showcase", - "MapLocalVersions": { "0.9-500": "0.9" } + "Decrafting Mod": { + "ID": "MSCFC.DecraftingMod", + "~1.0 | Status": "AssumeBroken" // NRE in ModEntry }, - "Siv's Marriage Mod": { - "ID": "6266959802", // official version - "FormerIDs": "Siv.MarriageMod | medoli900.Siv's Marriage Mod", // 1.2.3-unofficial versions - "MapLocalVersions": { "0.0": "1.4" } + "JoJaBan - Arcade Sokoban": { + "ID": "Platonymous.JoJaBan", + "~0.4.3 | Status": "AssumeBroken" // possibly due to PyTK }, + "Level Extender": { + "ID": "DevinLematty.LevelExtender", + "~3.1 | Status": "AssumeBroken" + }, - /********* - ** Obsolete - *********/ - "Animal Mood Fix": { - "ID": "GPeters-AnimalMoodFix", - "~ | Status": "Obsolete", - "~ | StatusReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." + "Mod Update Menu": { + "ID": "cat.modupdatemenu", + "~1.4 | Status": "AssumeBroken" }, - "Colored Chests": { - "ID": "4befde5c-731c-4853-8e4b-c5cdf946805f", - "~ | Status": "Obsolete", - "~ | StatusReasonPhrase": "colored chests were added in Stardew Valley 1.1." + "Quick Start": { + "ID": "WuestMan.QuickStart", + "~1.5 | Status": "AssumeBroken" }, - "Modder Serialization Utility": { - "ID": "SerializerUtils-0-1", - "~ | Status": "Obsolete", - "~ | StatusReasonPhrase": "it's no longer maintained or used." + "Seed Bag": { + "ID": "Platonymous.SeedBag", + "~1.2.7 | Status": "AssumeBroken" // possibly due to PyTK }, - "No Debug Mode": { - "ID": "NoDebugMode", - "~ | Status": "Obsolete", - "~ | StatusReasonPhrase": "debug mode was removed in SMAPI 1.0." + "Stardew Valley ESP": { + "ID": "reimu.sdv-helper", + "~1.1 | Status": "AssumeBroken" + }, + + "Underdark Krobus": { + "ID": "melnoelle.underdarkkrobus", + "~1.0.0 | Status": "AssumeBroken" // NRE in ModEntry + }, + + "Underdark Sewer": { + "ID": "melnoelle.underdarksewer", + "~1.1.0 | Status": "AssumeBroken" // NRE in ModEntry }, /********* @@ -349,11 +403,6 @@ "~0.3 | Status": "AssumeBroken" // broke in 1.3: Exception from HarmonyInstance "bcmpinc.FixScytheExp" [...] Bad label content in ILGenerator. }, - "Grass Growth": { - "ID": "bcmpinc.GrassGrowth", - "~0.6 | Status": "AssumeBroken" // breaks newer versions of bcmpinc mods (per bcmpinc's request) - }, - "More Silo Storage": { "ID": "OrneryWalrus.MoreSiloStorage", "~1.0.1 | Status": "AssumeBroken" // broke in SDV 1.3 @@ -374,12 +423,6 @@ "~1.0.0 | Status": "AssumeBroken" // broke in Stardew Valley 1.3.29 (runtime errors) }, - "Skill Prestige: Cooking Adapter": { - "ID": "Alphablackwolf.CookingSkillPrestigeAdapter", - "FormerIDs": "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63", // changed circa 1.1 - "MapRemoteVersions": { "1.2.3": "1.1" } // manifest not updated - }, - "Skull Cave Saver": { "ID": "cantorsdust.SkullCaveSaver", "FormerIDs": "8ac06349-26f7-4394-806c-95d48fd35774 | community.SkullCaveSaver", // changed in 1.1 and 1.2.2 @@ -398,7 +441,6 @@ "Stephan's Lots of Crops": { "ID": "stephansstardewcrops", - "MapRemoteVersions": { "1.41": "1.1" }, // manifest not updated "~1.1 | Status": "AssumeBroken" // broke in SDV 1.3 (overwrites vanilla items) }, @@ -418,11 +460,6 @@ "~0.6 | Status": "AssumeBroken" // breaks newer versions of bcmpinc mods (per bcmpinc's request) }, - "Yet Another Harvest With Scythe Mod": { - "ID": "bcmpinc.HarvestWithScythe", - "~0.6 | Status": "AssumeBroken" // breaks newer versions of bcmpinc mods (per bcmpinc's request) - }, - /********* ** Broke circa SDV 1.2 *********/ diff --git a/src/SMAPI.Web/wwwroot/schemas/content-patcher.json b/src/SMAPI.Web/wwwroot/schemas/content-patcher.json new file mode 100644 index 00000000..61a633cb --- /dev/null +++ b/src/SMAPI.Web/wwwroot/schemas/content-patcher.json @@ -0,0 +1,389 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://smapi.io/schemas/content-patcher.json", + "title": "Content Patcher content pack", + "description": "Content Patcher content file for mods", + "@documentationUrl": "https://github.com/Pathoschild/StardewMods/tree/develop/ContentPatcher#readme", + "type": "object", + + "properties": { + "Format": { + "title": "Format version", + "description": "The format version. You should always use the latest version to enable the latest features and avoid obsolete behavior.", + "type": "string", + "const": "1.9", + "@errorMessages": { + "const": "Incorrect value '@value'. This should be set to the latest format version, currently '1.9'." + } + }, + "ConfigSchema": { + "title": "Config schema", + "description": "Defines the config.json format, to support more complex mods.", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "AllowValues": { + "title": "Allowed values", + "description": "The values the player can provide, as a comma-delimited string. If omitted, any value is allowed.\nTip: for a boolean flag, use \"true, false\".", + "type": "string" + }, + "AllowBlank": { + "title": "Allow blank", + "description": "Whether the field can be left blank. If false or omitted, blank fields will be replaced with the default value.", + "type": "boolean" + }, + "AllowMultiple": { + "title": "Allow multiple values", + "description": "Whether the player can specify multiple comma-delimited values. Default false.", + "type": "boolean" + }, + "Default": { + "title": "Default value", + "description": "The default values when the field is missing. Can contain multiple comma-delimited values if AllowMultiple is true. If omitted, blank fields are left blank.", + "type": "string" + }, + + "additionalProperties": false + }, + "allOf": [ + { + "if": { + "properties": { + "AllowBlank": { "const": false } + }, + "required": [ "AllowBlank" ] + }, + "then": { + "required": [ "Default" ] + } + } + ], + + "@errorMessages": { + "allOf": "If 'AllowBlank' is false, the 'Default' field is required." + } + } + }, + "DynamicTokens": { + "title": "Dynamic tokens", + "description": "Custom tokens that you can use.", + "type": "array", + "items": { + "type": "object", + "properties": { + "Name": { + "title": "Name", + "description": "The name of the token to use for tokens & conditions.", + "type": "string" + }, + "Value": { + "title": "Token value", + "description": "The value(s) to set. This can be a comma-delimited value to give it multiple values. If any block for a token name has multiple values, it will only be usable in conditions. This field supports tokens, including dynamic tokens defined before this entry.", + "type": "string" + }, + "When": { + "title": "When", + "description": "Only set the value if the given conditions match. If not specified, always matches.", + "$ref": "#/definitions/Condition" + } + }, + + "required": [ "Name", "Value" ], + "additionalProperties": false + } + }, + "Changes": { + "title": "Changes", + "description": "The changes you want to make. Each entry is called a patch, and describes a specific action to perform: replace this file, copy this image into the file, etc. You can list any number of patches.", + "type": "array", + "items": { + "properties": { + "Action": { + "title": "Action", + "description": "The kind of change to make.", + "type": "string", + "enum": [ "Load", "EditImage", "EditData", "EditMap" ] + }, + "Target": { + "title": "Target asset", + "description": "The game asset you want to patch (or multiple comma-delimited assets). This is the file path inside your game's Content folder, without the file extension or language (like Animals/Dinosaur to edit Content/Animals/Dinosaur.xnb). This field supports tokens and capitalization doesn't matter. Your changes are applied in all languages unless you specify a language condition.", + "type": "string", + "not": { + "pattern": "^ *[cC][oO][nN][tT][eE][nN][tT]/|\\.[xX][nN][bB] *$|\\.[a-zA-Z][a-zA-Z]-[a-zA-Z][a-zA-Z](?:.xnb)? *$" + }, + "@errorMessages": { + "not": "Invalid target; it shouldn't include the 'Content/' folder, '.xnb' extension, or locale code." + } + }, + "LogName": { + "title": "Patch log name", + "description": "A name for this patch shown in log messages. This is very useful for understanding errors; if not specified, will default to a name like 'entry #14 (EditImage Animals/Dinosaurs)'.", + "type": "string" + }, + "Enabled": { + "title": "Enabled", + "description": "Whether to apply this patch. Default true. This fields supports immutable tokens (e.g. config tokens) if they return true/false.", + "anyOf": [ + { + "type": "string", + "enum": [ "true", "false" ] + }, + { + "type": "string", + "pattern": "\\{\\{[^{}]+\\}\\}" + }, + { + "type": "boolean" + } + ], + "@errorMessages": { + "anyOf": "Invalid value; must be true, false, or a single token which evaluates to true or false." + } + }, + "FromFile": { + "title": "Source file", + "description": "The relative file path in your content pack folder to load instead (like 'assets/dinosaur.png'). This can be a .json (data), .png (image), .tbin (map), or .xnb file. This field supports tokens and capitalization doesn't matter.", + "type": "string", + "allOf": [ + { + "not": { + "pattern": "\b\\.\\.[/\\]" + } + }, + |
