com.robotsquid.squidhub 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/Editor/SquidHubBuildScan.cs +23 -0
- package/Editor/SquidHubPump.cs +53 -0
- package/Editor/SquidHubPump.cs.meta +11 -0
- package/Editor/SquidHubUploader.cs +24 -23
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.6
|
|
4
|
+
|
|
5
|
+
⚠️ **Fixes an upload that did nothing and said nothing.** `SquidHub → Upload
|
|
6
|
+
Build…` in 0.5.5 ran, reported no error, and uploaded no files.
|
|
7
|
+
|
|
8
|
+
`yield return SomeOtherIEnumerator()` requires the coroutine driver to DESCEND
|
|
9
|
+
into the nested routine. Unity's own runner does; the hand-rolled one here did
|
|
10
|
+
not — it treated the nested iterator as an ordinary yielded value, discarded it,
|
|
11
|
+
and carried on. So the request that starts a build never executed, the response
|
|
12
|
+
stayed null, and the routine exited quietly on a null check.
|
|
13
|
+
|
|
14
|
+
The driver is now `SquidHubPump`, which has no `UnityEditor` dependency and is
|
|
15
|
+
unit-tested for exactly that nesting rule, three levels deep. The JSON field
|
|
16
|
+
reader moved into the tested file too, since a null that looks like an answer is
|
|
17
|
+
the same failure in a different costume, and the run now logs what it is doing
|
|
18
|
+
so a silent no-op cannot recur.
|
|
19
|
+
|
|
3
20
|
## 0.5.5
|
|
4
21
|
|
|
5
22
|
**Upload a build straight from Unity.** `SquidHub → Upload Build…` picks the
|
|
@@ -141,6 +141,29 @@ namespace SquidHub.Editor
|
|
|
141
141
|
return "[" + string.Join(",", parts) + "]";
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
/// <summary>
|
|
145
|
+
/// Reads one flat field out of a JSON response.
|
|
146
|
+
///
|
|
147
|
+
/// ⚠️ A regex, not a parser, and deliberately only for the handful of
|
|
148
|
+
/// flat fields this flow reads. Shipping a parser in a package that
|
|
149
|
+
/// already asks the project to bring its own would be a second, worse
|
|
150
|
+
/// one to maintain.
|
|
151
|
+
///
|
|
152
|
+
/// ⚠️ Returns null when absent, and the caller must treat that as a
|
|
153
|
+
/// failure rather than a value. Every silent no-op in this uploader has
|
|
154
|
+
/// come from a null that looked like an answer.
|
|
155
|
+
/// </summary>
|
|
156
|
+
public static string JsonField(string json, string name)
|
|
157
|
+
{
|
|
158
|
+
if (string.IsNullOrEmpty(json)) return null;
|
|
159
|
+
var match = System.Text.RegularExpressions.Regex.Match(
|
|
160
|
+
json,
|
|
161
|
+
"\"" + System.Text.RegularExpressions.Regex.Escape(name) +
|
|
162
|
+
"\"\\s*:\\s*(?:\"([^\"]*)\"|(true|false|-?[0-9.]+))");
|
|
163
|
+
if (!match.Success) return null;
|
|
164
|
+
return match.Groups[1].Success ? match.Groups[1].Value : match.Groups[2].Value;
|
|
165
|
+
}
|
|
166
|
+
|
|
144
167
|
internal static string JsonString(string value)
|
|
145
168
|
{
|
|
146
169
|
var sb = new System.Text.StringBuilder("\"");
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Running a coroutine outside a Unity player loop.
|
|
2
|
+
//
|
|
3
|
+
// ⚠️ Pure System.* — no UnityEditor — so the nesting rule below is covered by
|
|
4
|
+
// tests under plain dotnet. It exists because getting this wrong produced a
|
|
5
|
+
// completely silent failure: the upload menu item ran, reported nothing, and
|
|
6
|
+
// uploaded nothing.
|
|
7
|
+
using System;
|
|
8
|
+
using System.Collections;
|
|
9
|
+
using System.Collections.Generic;
|
|
10
|
+
|
|
11
|
+
namespace SquidHub.Editor
|
|
12
|
+
{
|
|
13
|
+
/// <summary>
|
|
14
|
+
/// A coroutine driver that understands NESTED iterators.
|
|
15
|
+
///
|
|
16
|
+
/// ⚠️ `yield return SomeOtherIEnumerator()` is the whole reason this is not
|
|
17
|
+
/// three lines. Unity's own runner descends into a yielded IEnumerator and
|
|
18
|
+
/// runs it to completion before resuming the parent. A naive driver that
|
|
19
|
+
/// only calls MoveNext() on the outer routine treats the nested one as an
|
|
20
|
+
/// ordinary value, discards it, and carries straight on — so the nested work
|
|
21
|
+
/// NEVER RUNS and nothing anywhere reports a problem.
|
|
22
|
+
/// </summary>
|
|
23
|
+
internal sealed class SquidHubPump
|
|
24
|
+
{
|
|
25
|
+
private readonly Stack<IEnumerator> _stack = new Stack<IEnumerator>();
|
|
26
|
+
|
|
27
|
+
public SquidHubPump(IEnumerator routine)
|
|
28
|
+
{
|
|
29
|
+
_stack.Push(routine);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public bool Done => _stack.Count == 0;
|
|
33
|
+
|
|
34
|
+
/// <summary>Advances by one step. Returns false once everything has finished.</summary>
|
|
35
|
+
public bool Step()
|
|
36
|
+
{
|
|
37
|
+
if (_stack.Count == 0) return false;
|
|
38
|
+
|
|
39
|
+
var current = _stack.Peek();
|
|
40
|
+
if (!current.MoveNext())
|
|
41
|
+
{
|
|
42
|
+
// ⚠️ Pop and return true, not false. The PARENT still has work
|
|
43
|
+
// left; reporting "done" because a child finished would abandon
|
|
44
|
+
// the rest of the upload silently.
|
|
45
|
+
_stack.Pop();
|
|
46
|
+
return _stack.Count > 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (current.Current is IEnumerator nested) _stack.Push(nested);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -21,7 +21,7 @@ namespace SquidHub.Editor
|
|
|
21
21
|
{
|
|
22
22
|
internal static class SquidHubUploader
|
|
23
23
|
{
|
|
24
|
-
private static
|
|
24
|
+
private static SquidHubPump _pump;
|
|
25
25
|
|
|
26
26
|
[MenuItem("SquidHub/Upload Build…", priority = 11)]
|
|
27
27
|
private static void UploadBuild()
|
|
@@ -79,9 +79,14 @@ namespace SquidHub.Editor
|
|
|
79
79
|
// ⚠️ Driven by EditorApplication.update rather than blocking. Sleeping
|
|
80
80
|
// the main thread would stop UnityWebRequest progressing at all, and the
|
|
81
81
|
// Editor would appear to hang for the length of the upload.
|
|
82
|
+
//
|
|
83
|
+
// ⚠️ SquidHubPump, not a bare MoveNext(). `yield return SomeIEnumerator()`
|
|
84
|
+
// must DESCEND into the nested routine — a driver that does not simply
|
|
85
|
+
// discards it and carries on, which made the entire upload run, report
|
|
86
|
+
// nothing and do nothing. That nesting rule is unit-tested.
|
|
82
87
|
private static void Run(IEnumerator routine)
|
|
83
88
|
{
|
|
84
|
-
|
|
89
|
+
_pump = new SquidHubPump(routine);
|
|
85
90
|
EditorApplication.update -= Pump;
|
|
86
91
|
EditorApplication.update += Pump;
|
|
87
92
|
}
|
|
@@ -90,16 +95,18 @@ namespace SquidHub.Editor
|
|
|
90
95
|
{
|
|
91
96
|
try
|
|
92
97
|
{
|
|
93
|
-
if (
|
|
98
|
+
if (_pump != null && _pump.Step()) return;
|
|
94
99
|
}
|
|
95
100
|
catch (Exception e)
|
|
96
101
|
{
|
|
97
102
|
EditorUtility.ClearProgressBar();
|
|
98
103
|
Debug.LogError("[SquidHub] upload failed: " + e);
|
|
104
|
+
EditorUtility.DisplayDialog("SquidHub — upload failed", e.Message, "OK");
|
|
99
105
|
}
|
|
100
|
-
|
|
106
|
+
_pump = null;
|
|
101
107
|
EditorApplication.update -= Pump;
|
|
102
108
|
EditorUtility.ClearProgressBar();
|
|
109
|
+
Debug.Log("[SquidHub] upload run finished.");
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
private static IEnumerator Upload(BuildScan scan)
|
|
@@ -107,24 +114,31 @@ namespace SquidHub.Editor
|
|
|
107
114
|
var endpoint = SquidHubUploadSettings.Endpoint;
|
|
108
115
|
var key = SquidHubUploadSettings.Key;
|
|
109
116
|
|
|
117
|
+
Debug.Log($"[SquidHub] uploading {scan.Files.Count} file(s) to {endpoint}");
|
|
110
118
|
EditorUtility.DisplayProgressBar("SquidHub", "Registering build…", 0f);
|
|
111
119
|
|
|
112
120
|
var startBody = "{\"intent\":\"start\",\"files\":" + SquidHubBuildScan.FilesJson(scan.Files) + "}";
|
|
113
121
|
string startResponse = null;
|
|
114
122
|
yield return Post(endpoint + "/api/ingest", key, startBody, r => startResponse = r);
|
|
115
123
|
|
|
116
|
-
|
|
124
|
+
// ⚠️ Post reports its own failure and leaves this null. Returning
|
|
125
|
+
// quietly here is what made a failed start look like success.
|
|
126
|
+
if (startResponse == null)
|
|
127
|
+
{
|
|
128
|
+
Debug.LogError("[SquidHub] the start request returned nothing — upload abandoned.");
|
|
129
|
+
yield break;
|
|
130
|
+
}
|
|
117
131
|
|
|
118
|
-
if (
|
|
132
|
+
if (SquidHubBuildScan.JsonField(startResponse, "alreadyPublished") == "true")
|
|
119
133
|
{
|
|
120
134
|
EditorUtility.ClearProgressBar();
|
|
121
135
|
EditorUtility.DisplayDialog("SquidHub",
|
|
122
|
-
"These exact bytes are already published as " +
|
|
136
|
+
"These exact bytes are already published as " + SquidHubBuildScan.JsonField(startResponse, "buildHash") +
|
|
123
137
|
". Nothing to upload.", "OK");
|
|
124
138
|
yield break;
|
|
125
139
|
}
|
|
126
140
|
|
|
127
|
-
var jobId =
|
|
141
|
+
var jobId = SquidHubBuildScan.JsonField(startResponse, "jobId");
|
|
128
142
|
if (string.IsNullOrEmpty(jobId))
|
|
129
143
|
{
|
|
130
144
|
Fail("The server did not start a build job", startResponse);
|
|
@@ -167,7 +181,7 @@ namespace SquidHub.Editor
|
|
|
167
181
|
EditorUtility.ClearProgressBar();
|
|
168
182
|
if (doneResponse == null) yield break;
|
|
169
183
|
|
|
170
|
-
if (
|
|
184
|
+
if (SquidHubBuildScan.JsonField(doneResponse, "ok") != "true")
|
|
171
185
|
{
|
|
172
186
|
Fail("The server refused the build", doneResponse);
|
|
173
187
|
yield break;
|
|
@@ -177,7 +191,7 @@ namespace SquidHub.Editor
|
|
|
177
191
|
// deliberate act in the console — saying "uploaded" without saying
|
|
178
192
|
// that invites someone to assume their players already have it.
|
|
179
193
|
EditorUtility.DisplayDialog("SquidHub",
|
|
180
|
-
"Registered " +
|
|
194
|
+
"Registered " + SquidHubBuildScan.JsonField(doneResponse, "buildHash") + ".\n\n" +
|
|
181
195
|
"It is not live yet — promote it in the publisher console when you are ready.",
|
|
182
196
|
"OK");
|
|
183
197
|
}
|
|
@@ -214,18 +228,5 @@ namespace SquidHub.Editor
|
|
|
214
228
|
EditorUtility.DisplayDialog("SquidHub — " + title, detail, "OK");
|
|
215
229
|
}
|
|
216
230
|
|
|
217
|
-
/// <summary>
|
|
218
|
-
/// ⚠️ A regex, not a JSON parser, and only for the four flat fields this
|
|
219
|
-
/// flow reads. Shipping a parser in a package that already asks the
|
|
220
|
-
/// project to bring its own would be a second, worse one to maintain.
|
|
221
|
-
/// </summary>
|
|
222
|
-
private static string Field(string json, string name)
|
|
223
|
-
{
|
|
224
|
-
var match = Regex.Match(json, "\"" + Regex.Escape(name) + "\"\\s*:\\s*(\"([^\"]*)\"|true|false|[0-9]+)");
|
|
225
|
-
if (!match.Success) return null;
|
|
226
|
-
return match.Groups[2].Success && match.Value.Contains("\"" + match.Groups[2].Value + "\"")
|
|
227
|
-
? match.Groups[2].Value
|
|
228
|
-
: match.Groups[1].Value;
|
|
229
|
-
}
|
|
230
231
|
}
|
|
231
232
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "com.robotsquid.squidhub",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"displayName": "SquidHub",
|
|
5
5
|
"description": "Cloud saves, analytics and the coin economy for Unity WebGL games published on SquidHub. Installs the C# wrapper, the .jslib half and the page bridge, and rewrites the exported index.html so it survives the platform's CSP.",
|
|
6
6
|
"unity": "2021.3",
|