Merge branch 'android' of https://github.com/Pathoschild/SMAPI.git into android
# Conflicts: # src/SMAPI/Constants.cs # src/SMAPI/Framework/ContentManagers/ModContentManager.cs # src/SMAPI/Framework/SGame.cs # src/SMAPI/SMAPI.csproj
This commit is contained in:
commit
aa1a71b66f
|
@ -1,6 +1,10 @@
|
||||||
← [README](README.md)
|
← [README](README.md)
|
||||||
|
|
||||||
# Release notes
|
# Release notes
|
||||||
|
## Upcoming release
|
||||||
|
* For the web UI:
|
||||||
|
* Added option to upload files using a file picker.
|
||||||
|
|
||||||
## 3.3.2
|
## 3.3.2
|
||||||
Released 22 February 2020 for Stardew Valley 1.4.1 or later.
|
Released 22 February 2020 for Stardew Valley 1.4.1 or later.
|
||||||
|
|
||||||
|
@ -27,6 +31,10 @@ Released 22 February 2020 for Stardew Valley 1.4.1 or later.
|
||||||
* Fixed warning on MacOS when you have no saves yet.
|
* Fixed warning on MacOS when you have no saves yet.
|
||||||
* Reduced log messages.
|
* Reduced log messages.
|
||||||
|
|
||||||
|
* For the web UI:
|
||||||
|
* Updated the JSON validator and Content Patcher schema for `.tmx` support.
|
||||||
|
* The mod compatibility page now has a sticky table header.
|
||||||
|
|
||||||
* For modders:
|
* For modders:
|
||||||
* Added support for [message sending](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Message_sending) to mods on the current computer (in addition to remote computers).
|
* Added support for [message sending](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Message_sending) to mods on the current computer (in addition to remote computers).
|
||||||
* Added `ExtendImage` method to content API when editing files to resize textures.
|
* Added `ExtendImage` method to content API when editing files to resize textures.
|
||||||
|
@ -37,10 +45,6 @@ Released 22 February 2020 for Stardew Valley 1.4.1 or later.
|
||||||
* Updated dependencies (including Mono.Cecil 0.11.1 → 0.11.2).
|
* Updated dependencies (including Mono.Cecil 0.11.1 → 0.11.2).
|
||||||
* Fixed dialogue propagation clearing marriage dialogue.
|
* Fixed dialogue propagation clearing marriage dialogue.
|
||||||
|
|
||||||
* For the web UI:
|
|
||||||
* Updated the JSON validator and Content Patcher schema for `.tmx` support.
|
|
||||||
* The mod compatibility page now has a sticky table header.
|
|
||||||
|
|
||||||
* For SMAPI/tool developers:
|
* For SMAPI/tool developers:
|
||||||
* Improved support for four-part versions to support SMAPI on Android.
|
* Improved support for four-part versions to support SMAPI on Android.
|
||||||
* The SMAPI log now prefixes the OS name with `Android` on Android.
|
* The SMAPI log now prefixes the OS name with `Android` on Android.
|
||||||
|
|
|
@ -16,6 +16,8 @@ namespace StardewModdingAPI.Web
|
||||||
// configure web server
|
// configure web server
|
||||||
WebHost
|
WebHost
|
||||||
.CreateDefaultBuilder(args)
|
.CreateDefaultBuilder(args)
|
||||||
|
.CaptureStartupErrors(true)
|
||||||
|
.UseSetting("detailedErrors", "true")
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
.Build()
|
.Build()
|
||||||
.Run();
|
.Run();
|
||||||
|
|
|
@ -192,7 +192,6 @@ namespace StardewModdingAPI.Web
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||||
{
|
{
|
||||||
// basic config
|
// basic config
|
||||||
if (env.IsDevelopment())
|
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
app
|
app
|
||||||
.UseCors(policy => policy
|
.UseCors(policy => policy
|
||||||
|
|
|
@ -28,14 +28,16 @@
|
||||||
{
|
{
|
||||||
<meta name="robots" content="noindex" />
|
<meta name="robots" content="noindex" />
|
||||||
}
|
}
|
||||||
<link rel="stylesheet" href="~/Content/css/json-validator.css?r=20191204" />
|
<link rel="stylesheet" href="~/Content/css/file-upload.css?r=202002" />
|
||||||
|
<link rel="stylesheet" href="~/Content/css/json-validator.css?r=202002" />
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/themes/sunlight.default.min.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/themes/sunlight.default.min.css" />
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/sunlight.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/sunlight.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/plugins/sunlight-plugin.linenumbers.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/plugins/sunlight-plugin.linenumbers.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/lang/sunlight.javascript.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/lang/sunlight.javascript.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="~/Content/js/json-validator.js?r=20191204"></script>
|
<script src="~/Content/js/file-upload.js?r=202002"></script>
|
||||||
|
<script src="~/Content/js/json-validator.js?r=202002"></script>
|
||||||
<script>
|
<script>
|
||||||
$(function() {
|
$(function() {
|
||||||
smapi.jsonValidator(@Json.Serialize(this.Url.PlainAction("Index", "JsonValidator", new { schemaName = "$schemaName", id = "$id" })), @Json.Serialize(Model.PasteID));
|
smapi.jsonValidator(@Json.Serialize(this.Url.PlainAction("Index", "JsonValidator", new { schemaName = "$schemaName", id = "$id" })), @Json.Serialize(Model.PasteID));
|
||||||
|
@ -86,6 +88,7 @@ else if (!isEditView && Model.PasteID != null)
|
||||||
{
|
{
|
||||||
<h2>Upload a JSON file</h2>
|
<h2>Upload a JSON file</h2>
|
||||||
<form action="@this.Url.PlainAction("PostAsync", "JsonValidator")" method="post">
|
<form action="@this.Url.PlainAction("PostAsync", "JsonValidator")" method="post">
|
||||||
|
<input id="inputFile" type="file" />
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
Choose the JSON format:<br />
|
Choose the JSON format:<br />
|
||||||
|
@ -97,7 +100,7 @@ else if (!isEditView && Model.PasteID != null)
|
||||||
</select>
|
</select>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Drag the file onto this textbox (or paste the text in):<br />
|
Drag the file onto this textbox <small>(or <a href="#" id="choose-file-link">choose a file</a>)</small>:<br />
|
||||||
<textarea id="input" name="Content" placeholder="paste file here">@Model.Content</textarea>
|
<textarea id="input" name="Content" placeholder="paste file here">@Model.Content</textarea>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -22,10 +22,13 @@
|
||||||
{
|
{
|
||||||
<meta name="robots" content="noindex" />
|
<meta name="robots" content="noindex" />
|
||||||
}
|
}
|
||||||
<link rel="stylesheet" href="~/Content/css/log-parser.css?r=20191127" />
|
<link rel="stylesheet" href="~/Content/css/file-upload.css?r=202002" />
|
||||||
|
<link rel="stylesheet" href="~/Content/css/log-parser.css?r=202002" />
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="~/Content/js/log-parser.js?r=20190515"></script>
|
<script src="~/Content/js/file-upload.js?r=202002"></script>
|
||||||
|
<script src="~/Content/js/log-parser.js?r=202002"></script>
|
||||||
<script>
|
<script>
|
||||||
$(function() {
|
$(function() {
|
||||||
smapi.logParser({
|
smapi.logParser({
|
||||||
|
@ -135,9 +138,10 @@ else if (Model.ParsedLog?.IsValid == true)
|
||||||
|
|
||||||
<h2>How do I share my log?</h2>
|
<h2>How do I share my log?</h2>
|
||||||
<form action="@this.Url.PlainAction("PostAsync", "LogParser")" method="post">
|
<form action="@this.Url.PlainAction("PostAsync", "LogParser")" method="post">
|
||||||
|
<input id="inputFile" type="file" />
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
Drag the file onto this textbox (or paste the text in):<br />
|
Drag the file onto this textbox <small>(or <a href="#" id="choose-file-link">choose a file</a>)</small>:<br />
|
||||||
<textarea id="input" name="input" placeholder="paste log here"></textarea>
|
<textarea id="input" name="input" placeholder="paste log here"></textarea>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#inputFile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -90,28 +90,3 @@
|
||||||
.footer-tip a {
|
.footer-tip a {
|
||||||
color: gray;
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -301,24 +301,3 @@ div[data-os] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* globals $ */
|
||||||
|
var smapi = smapi || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the logic for a log/JSON file upload form.
|
||||||
|
*
|
||||||
|
* @param {object} opts The file upload form options.
|
||||||
|
* @param {jQuery} opts.chooseFileLink The clickable link which shows the file chooser.
|
||||||
|
* @param {jQuery} opts.chooseFileInput The file input element.
|
||||||
|
* @param {jQuery} opts.contentArea The file content area.
|
||||||
|
* @param {jQuery} opts.submitButton The submit button.
|
||||||
|
*/
|
||||||
|
smapi.fileUpload = function (opts) {
|
||||||
|
/**
|
||||||
|
* Toggle the submit button if the form has content.
|
||||||
|
*/
|
||||||
|
var toggleSubmit = function () {
|
||||||
|
var hasText = !!opts.contentArea.val().trim();
|
||||||
|
opts.submitButton.prop("disabled", !hasText);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paste the content of a file into the content area.
|
||||||
|
* @param {File} file The file whose content to paste.
|
||||||
|
*/
|
||||||
|
var pasteFile = function (file) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = $.proxy(function (file, $input, event) {
|
||||||
|
$input.val(event.target.result);
|
||||||
|
toggleSubmit();
|
||||||
|
}, this, file, $("#input"));
|
||||||
|
reader.readAsText(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
if (opts.contentArea.length) {
|
||||||
|
// disable submit button if it's empty
|
||||||
|
opts.contentArea.on("input", toggleSubmit);
|
||||||
|
toggleSubmit();
|
||||||
|
|
||||||
|
// drag & drop file
|
||||||
|
opts.contentArea.on({
|
||||||
|
"dragover dragenter": function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
"drop": function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var transfer = e.originalEvent.dataTransfer;
|
||||||
|
if (transfer && transfer.files.length)
|
||||||
|
pasteFile(transfer.files[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// choose file
|
||||||
|
opts.chooseFileLink.on({
|
||||||
|
"click": function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
opts.chooseFileInput.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
opts.chooseFileInput.on({
|
||||||
|
"change": function (e) {
|
||||||
|
if (!e.target.files || !e.target.files.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pasteFile(e.target.files[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -123,6 +123,8 @@ smapi.jsonValidator = function (urlFormat, fileId) {
|
||||||
* Initialize the JSON validator page.
|
* Initialize the JSON validator page.
|
||||||
*/
|
*/
|
||||||
var init = function () {
|
var init = function () {
|
||||||
|
var input = $("#input");
|
||||||
|
|
||||||
// set initial code formatting
|
// set initial code formatting
|
||||||
selection.parseFromUrlHash(location.hash);
|
selection.parseFromUrlHash(location.hash);
|
||||||
formatCode();
|
formatCode();
|
||||||
|
@ -141,38 +143,13 @@ smapi.jsonValidator = function (urlFormat, fileId) {
|
||||||
location.href = urlFormat.replace("$schemaName", schemaName).replace("$id", fileId);
|
location.href = urlFormat.replace("$schemaName", schemaName).replace("$id", fileId);
|
||||||
});
|
});
|
||||||
|
|
||||||
// upload form
|
|
||||||
var submit = $("#submit");
|
|
||||||
var input = $("#input");
|
|
||||||
if (input.length) {
|
if (input.length) {
|
||||||
// disable submit if it's empty
|
// upload form
|
||||||
var toggleSubmit = function () {
|
smapi.fileUpload({
|
||||||
var hasText = !!input.val().trim();
|
chooseFileLink: $("#choose-file-link"),
|
||||||
submit.prop("disabled", !hasText);
|
chooseFileInput: $("#inputFile"),
|
||||||
};
|
contentArea: input,
|
||||||
input.on("input", toggleSubmit);
|
submitButton: $("#submit")
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -115,12 +115,10 @@ smapi.logParser = function (data, sectionUrl) {
|
||||||
*********/
|
*********/
|
||||||
var input = $("#input");
|
var input = $("#input");
|
||||||
if (input.length) {
|
if (input.length) {
|
||||||
// get elements
|
// instructions per OS
|
||||||
var systemOptions = $("input[name='os']");
|
var systemOptions = $("input[name='os']");
|
||||||
var systemInstructions = $("div[data-os]");
|
var systemInstructions = $("div[data-os]");
|
||||||
var submit = $("#submit");
|
|
||||||
|
|
||||||
// instruction OS chooser
|
|
||||||
var chooseSystem = function () {
|
var chooseSystem = function () {
|
||||||
systemInstructions.hide();
|
systemInstructions.hide();
|
||||||
systemInstructions.filter("[data-os='" + $("input[name='os']:checked").val() + "']").show();
|
systemInstructions.filter("[data-os='" + $("input[name='os']:checked").val() + "']").show();
|
||||||
|
@ -128,34 +126,12 @@ smapi.logParser = function (data, sectionUrl) {
|
||||||
systemOptions.on("click", chooseSystem);
|
systemOptions.on("click", chooseSystem);
|
||||||
chooseSystem();
|
chooseSystem();
|
||||||
|
|
||||||
// disable submit if it's empty
|
// file upload
|
||||||
var toggleSubmit = function () {
|
smapi.fileUpload({
|
||||||
var hasText = !!input.val().trim();
|
chooseFileLink: $("#choose-file-link"),
|
||||||
submit.prop("disabled", !hasText);
|
chooseFileInput: $("#inputFile"),
|
||||||
}
|
contentArea: input,
|
||||||
input.on("input", toggleSubmit);
|
submitButton: $("#submit")
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace StardewModdingAPI
|
||||||
** Public
|
** Public
|
||||||
****/
|
****/
|
||||||
/// <summary>SMAPI's current semantic version.</summary>
|
/// <summary>SMAPI's current semantic version.</summary>
|
||||||
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.3.2");
|
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.3.2.0", allowNonStandard: true);
|
||||||
|
|
||||||
/// <summary>The minimum supported version of Stardew Valley.</summary>
|
/// <summary>The minimum supported version of Stardew Valley.</summary>
|
||||||
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.4.5");
|
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.4.5");
|
||||||
|
@ -29,7 +29,7 @@ namespace StardewModdingAPI
|
||||||
public static ISemanticVersion MaximumGameVersion { get; } = null;
|
public static ISemanticVersion MaximumGameVersion { get; } = null;
|
||||||
|
|
||||||
/// <summary>The target game platform.</summary>
|
/// <summary>The target game platform.</summary>
|
||||||
public static GamePlatform TargetPlatform => (GamePlatform)Constants.Platform;
|
public static GamePlatform TargetPlatform => GamePlatform.Android;
|
||||||
|
|
||||||
/// <summary>The path to the game folder.</summary>
|
/// <summary>The path to the game folder.</summary>
|
||||||
public static string ExecutionPath { get; } = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.Path, "StardewValley/smapi-internal");
|
public static string ExecutionPath { get; } = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.Path, "StardewValley/smapi-internal");
|
||||||
|
@ -101,7 +101,7 @@ namespace StardewModdingAPI
|
||||||
internal static ISemanticVersion GameVersion { get; } = new GameVersion(Game1.version);
|
internal static ISemanticVersion GameVersion { get; } = new GameVersion(Game1.version);
|
||||||
|
|
||||||
/// <summary>The target game platform.</summary>
|
/// <summary>The target game platform.</summary>
|
||||||
internal static Platform Platform { get; } = EnvironmentUtility.DetectPlatform();
|
internal static Platform Platform { get; } = Platform.Android;
|
||||||
|
|
||||||
/// <summary>The game's assembly name.</summary>
|
/// <summary>The game's assembly name.</summary>
|
||||||
internal static string GameAssemblyName => Constants.Platform == Platform.Windows ? "Stardew Valley" : "StardewValley";
|
internal static string GameAssemblyName => Constants.Platform == Platform.Windows ? "Stardew Valley" : "StardewValley";
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Content;
|
using Microsoft.Xna.Framework.Content;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
@ -35,6 +37,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
/// <summary>The language code for language-agnostic mod assets.</summary>
|
/// <summary>The language code for language-agnostic mod assets.</summary>
|
||||||
private readonly LanguageCode DefaultLanguage = Constants.DefaultLanguage;
|
private readonly LanguageCode DefaultLanguage = Constants.DefaultLanguage;
|
||||||
|
|
||||||
|
/// <summary>Reflector used to access xnbs on Android.
|
||||||
|
private readonly Reflector Reflector;
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
|
@ -57,6 +62,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
this.GameContentManager = gameContentManager;
|
this.GameContentManager = gameContentManager;
|
||||||
this.JsonHelper = jsonHelper;
|
this.JsonHelper = jsonHelper;
|
||||||
this.ModName = modName;
|
this.ModName = modName;
|
||||||
|
this.Reflector = reflection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Load an asset that has been processed by the content pipeline.</summary>
|
/// <summary>Load an asset that has been processed by the content pipeline.</summary>
|
||||||
|
@ -131,7 +137,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
this.FixCustomTilesheetPaths(map, relativeMapPath: assetName);
|
this.FixCustomTilesheetPaths(map, relativeMapPath: assetName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
return this.ModedLoad<T>(assetName, language);
|
||||||
|
|
||||||
// unpacked data
|
// unpacked data
|
||||||
case ".json":
|
case ".json":
|
||||||
|
@ -418,5 +424,97 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
// get file
|
// get file
|
||||||
return new FileInfo(path).Exists;
|
return new FileInfo(path).Exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T ModedLoad<T>(string assetName, LanguageCode language)
|
||||||
|
{
|
||||||
|
if (language != LanguageCode.en)
|
||||||
|
{
|
||||||
|
string key = assetName + "." + this.LanguageCodeString(language);
|
||||||
|
Dictionary<string, bool> _localizedAsset = this.Reflector.GetField<Dictionary<string, bool>>(this, "_localizedAsset").GetValue();
|
||||||
|
if (!_localizedAsset.TryGetValue(key, out bool flag) | flag)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_localizedAsset[key] = true;
|
||||||
|
return this.ModedLoad<T>(key);
|
||||||
|
}
|
||||||
|
catch (ContentLoadException)
|
||||||
|
{
|
||||||
|
_localizedAsset[key] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.ModedLoad<T>(assetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T ModedLoad<T>(string assetName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(assetName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("assetName");
|
||||||
|
}
|
||||||
|
T local = default(T);
|
||||||
|
string key = assetName.Replace('\\', '/');
|
||||||
|
Dictionary<string, object> loadedAssets = this.Reflector.GetField<Dictionary<string, object>>(this, "loadedAssets").GetValue();
|
||||||
|
if (loadedAssets.TryGetValue(key, out object obj2) && (obj2 is T))
|
||||||
|
{
|
||||||
|
return (T)obj2;
|
||||||
|
}
|
||||||
|
local = this.ReadAsset<T>(assetName, null);
|
||||||
|
loadedAssets[key] = local;
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Stream OpenStream(string assetName)
|
||||||
|
{
|
||||||
|
Stream stream;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream = new FileStream(Path.Combine(this.RootDirectory, assetName) + ".xnb", FileMode.Open, FileAccess.Read);
|
||||||
|
MemoryStream destination = new MemoryStream();
|
||||||
|
stream.CopyTo(destination);
|
||||||
|
destination.Seek(0L, SeekOrigin.Begin);
|
||||||
|
stream.Close();
|
||||||
|
stream = destination;
|
||||||
|
}
|
||||||
|
catch (Exception exception3)
|
||||||
|
{
|
||||||
|
throw new ContentLoadException("Opening stream error.", exception3);
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
protected new T ReadAsset<T>(string assetName, Action<IDisposable> recordDisposableObject)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(assetName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("assetName");
|
||||||
|
}
|
||||||
|
;
|
||||||
|
string str = assetName;
|
||||||
|
object obj2 = null;
|
||||||
|
if (this.Reflector.GetField<IGraphicsDeviceService>(this, "graphicsDeviceService").GetValue() == null)
|
||||||
|
{
|
||||||
|
this.Reflector.GetField<IGraphicsDeviceService>(this, "graphicsDeviceService").SetValue(this.ServiceProvider.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService);
|
||||||
|
}
|
||||||
|
Stream input = this.OpenStream(assetName);
|
||||||
|
using (BinaryReader reader = new BinaryReader(input))
|
||||||
|
{
|
||||||
|
using (ContentReader reader2 = this.Reflector.GetMethod(this, "GetContentReaderFromXnb").Invoke<ContentReader>(assetName, input, reader, recordDisposableObject))
|
||||||
|
{
|
||||||
|
MethodInfo method = reader2.GetType().GetMethod("ReadAsset", BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic, null, new Type[] { }, new ParameterModifier[] { });
|
||||||
|
obj2 = method.MakeGenericMethod(new Type[] { typeof(T) }).Invoke(reader2, null);
|
||||||
|
if (obj2 is GraphicsResource graphics)
|
||||||
|
{
|
||||||
|
graphics.Name = str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (obj2 == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Could not load " + str + " asset!");
|
||||||
|
}
|
||||||
|
return (T)obj2;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,8 +237,12 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <param name="message">The message to deliver to applicable mods.</param>
|
/// <param name="message">The message to deliver to applicable mods.</param>
|
||||||
private void OnModMessageReceived(ModMessageModel message)
|
private void OnModMessageReceived(ModMessageModel message)
|
||||||
{
|
{
|
||||||
// raise events for applicable mods
|
// get mod IDs to notify
|
||||||
HashSet<string> modIDs = new HashSet<string>(message.ToModIDs ?? this.ModRegistry.GetAll().Select(p => p.Manifest.UniqueID), StringComparer.InvariantCultureIgnoreCase);
|
HashSet<string> modIDs = new HashSet<string>(message.ToModIDs ?? this.ModRegistry.GetAll().Select(p => p.Manifest.UniqueID), StringComparer.InvariantCultureIgnoreCase);
|
||||||
|
if (message.FromPlayerID == Game1.player?.UniqueMultiplayerID)
|
||||||
|
modIDs.Remove(message.FromModID); // don't send a broadcast back to the sender
|
||||||
|
|
||||||
|
// raise events
|
||||||
this.Events.ModMessageReceived.RaiseForMods(new ModMessageReceivedEventArgs(message), mod => mod != null && modIDs.Contains(mod.Manifest.UniqueID));
|
this.Events.ModMessageReceived.RaiseForMods(new ModMessageReceivedEventArgs(message), mod => mod != null && modIDs.Contains(mod.Manifest.UniqueID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,10 +57,10 @@
|
||||||
<HintPath>..\..\..\..\..\AndroidStudioProjects\SMAPI Android Installer\app\src\main\assets\Stardew\0Harmony.dll</HintPath>
|
<HintPath>..\..\..\..\..\AndroidStudioProjects\SMAPI Android Installer\app\src\main\assets\Stardew\0Harmony.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Google.Android.Vending.Expansion.Downloader">
|
<Reference Include="Google.Android.Vending.Expansion.Downloader">
|
||||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.142\assemblies\Google.Android.Vending.Expansion.Downloader.dll</HintPath>
|
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.139\assemblies\Google.Android.Vending.Expansion.Downloader.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Google.Android.Vending.Licensing">
|
<Reference Include="Google.Android.Vending.Licensing">
|
||||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.142\assemblies\Google.Android.Vending.Licensing.dll</HintPath>
|
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.139\assemblies\Google.Android.Vending.Licensing.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
<HintPath>..\..\..\..\..\AndroidStudioProjects\SMAPI Android Installer\app\src\main\assets\Stardew\Mono.Cecil.dll</HintPath>
|
<HintPath>..\..\..\..\..\AndroidStudioProjects\SMAPI Android Installer\app\src\main\assets\Stardew\Mono.Cecil.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="MonoGame.Framework">
|
<Reference Include="MonoGame.Framework">
|
||||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.142\assemblies\MonoGame.Framework.dll</HintPath>
|
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.139\assemblies\MonoGame.Framework.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="MonoMod.RuntimeDetour">
|
<Reference Include="MonoMod.RuntimeDetour">
|
||||||
<HintPath>..\..\..\..\..\AndroidStudioProjects\SMAPI Android Installer\app\src\main\assets\Stardew\MonoMod.RuntimeDetour.dll</HintPath>
|
<HintPath>..\..\..\..\..\AndroidStudioProjects\SMAPI Android Installer\app\src\main\assets\Stardew\MonoMod.RuntimeDetour.dll</HintPath>
|
||||||
|
@ -86,10 +86,10 @@
|
||||||
<HintPath>..\SMAPI.Toolkit.CoreInterfaces\bin\Debug\net4.5\SMAPI.Toolkit.CoreInterfaces.dll</HintPath>
|
<HintPath>..\SMAPI.Toolkit.CoreInterfaces\bin\Debug\net4.5\SMAPI.Toolkit.CoreInterfaces.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="StardewValley">
|
<Reference Include="StardewValley">
|
||||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.142\assemblies\StardewValley.dll</HintPath>
|
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.139\assemblies\StardewValley.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="StardewValley.GameData">
|
<Reference Include="StardewValley.GameData">
|
||||||
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.142\assemblies\StardewValley.GameData.dll</HintPath>
|
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.139\assemblies\StardewValley.GameData.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
@ -200,6 +200,7 @@
|
||||||
<Compile Include="Framework\IModMetadata.cs" />
|
<Compile Include="Framework\IModMetadata.cs" />
|
||||||
<Compile Include="Framework\Input\GamePadStateBuilder.cs" />
|
<Compile Include="Framework\Input\GamePadStateBuilder.cs" />
|
||||||
<Compile Include="Framework\Input\SInputState.cs" />
|
<Compile Include="Framework\Input\SInputState.cs" />
|
||||||
|
<Compile Include="Framework\Input\InputStatus.cs" />
|
||||||
<Compile Include="Framework\InternalExtensions.cs" />
|
<Compile Include="Framework\InternalExtensions.cs" />
|
||||||
<Compile Include="Framework\Logging\ConsoleInterceptionManager.cs" />
|
<Compile Include="Framework\Logging\ConsoleInterceptionManager.cs" />
|
||||||
<Compile Include="Framework\Logging\InterceptingTextWriter.cs" />
|
<Compile Include="Framework\Logging\InterceptingTextWriter.cs" />
|
||||||
|
@ -385,7 +386,6 @@
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="SAlertDialogUtil.cs" />
|
<Compile Include="SAlertDialogUtil.cs" />
|
||||||
<Compile Include="SButton.cs" />
|
<Compile Include="SButton.cs" />
|
||||||
<Compile Include="SButtonState.cs" />
|
|
||||||
<Compile Include="SemanticVersion.cs" />
|
<Compile Include="SemanticVersion.cs" />
|
||||||
<Compile Include="SGameConsole.cs" />
|
<Compile Include="SGameConsole.cs" />
|
||||||
<Compile Include="SMainActivity.cs" />
|
<Compile Include="SMainActivity.cs" />
|
||||||
|
|
Loading…
Reference in New Issue