diff --git a/IPA.Tests/updater_test.json b/IPA.Tests/updater_test.json
index dda03aec..36fca4a0 100644
--- a/IPA.Tests/updater_test.json
+++ b/IPA.Tests/updater_test.json
@@ -40,7 +40,7 @@
"Mono.Cecil.dll": "762fb07e4d81722f0a766460289c6114cd4b7dae",
"Plugins/SongLoaderPlugin.dll": "f4ab080fbcc5abc6005a868cd965920bac46ddaf"
},
- "url": "https://www.modsaber.ml/cdn/song-loader/4.2.2-default.zip"
+ "url": "file://Z:/Users/aaron/Source/Repos/IPA-Reloaded-BeatSaber/IPA/bin/Debug/Debug.zip"
}
},
"weight": 7,
diff --git a/IllusionInjector/IllusionInjector.csproj b/IllusionInjector/IllusionInjector.csproj
index 9e6b3c5d..be93c80d 100644
--- a/IllusionInjector/IllusionInjector.csproj
+++ b/IllusionInjector/IllusionInjector.csproj
@@ -81,6 +81,7 @@
+
diff --git a/IllusionInjector/Updating/ModsaberML/Updater.cs b/IllusionInjector/Updating/ModsaberML/Updater.cs
index 90709fe1..b430a22d 100644
--- a/IllusionInjector/Updating/ModsaberML/Updater.cs
+++ b/IllusionInjector/Updating/ModsaberML/Updater.cs
@@ -151,49 +151,76 @@ namespace IllusionInjector.Updating.ModsaberML
}
}
- class StreamDownloadHandler : DownloadHandlerScript
+ public class StreamDownloadHandler : DownloadHandlerScript
{
- public MemoryStream Stream { get; set; }
+ public BlockingStream Stream { get; set; }
+
+ public StreamDownloadHandler(BlockingStream stream)
+ {
+ Stream = stream;
+ }
protected void ReceiveContentLength(long contentLength)
{
- Stream.Capacity = (int)contentLength;
+ //(Stream.BaseStream as MemoryStream).Capacity = (int)contentLength;
Logger.log.Debug($"Got content length: {contentLength}");
}
protected void OnContentComplete()
{
+ Stream.Open = false;
Logger.log.Debug("Download complete");
}
protected bool ReceiveData(byte[] data, long dataLength)
{
+ Logger.log.Debug("ReceiveData");
+ if (data == null || data.Length < 1)
+ {
+ Logger.log.Debug("CustomWebRequest :: ReceiveData - received a null/empty buffer");
+ return false;
+ }
+
Stream.Write(data, 0, (int)dataLength);
return true;
}
+
+ protected override byte[] GetData() { return null; }
+
+ protected override float GetProgress()
+ {
+ return 0f;
+ }
+
+ public override string ToString()
+ {
+ return $"{base.ToString()} ({Stream?.ToString()})";
+ }
+
}
- IEnumerator UpdateModCoroutine(string tempdir, UpdateStruct item)
+ private void DownloadPluginAsync(BlockingStream stream, UpdateStruct item, string tempdir)
{
- void DownloadPluginAsync(MemoryStream stream)
- { // embedded because i don't think unity likes it in the top level
- Logger.log.Debug($"Getting ZIP file for {item.plugin.Plugin.Name}");
- //var stream = await httpClient.GetStreamAsync(url);
+ Logger.log.Debug($"Getting ZIP file for {item.plugin.Plugin.Name}");
+ //var stream = await httpClient.GetStreamAsync(url);
- using (var zipFile = new ZipInputStream(new BlockingStream(stream)))
+ using (var zipFile = new ZipInputStream(stream))
+ {
+ Logger.log.Debug("Streams opened");
+ ZipEntry entry;
+ while ((entry = zipFile.GetNextEntry()) != null)
{
- Logger.log.Debug("Streams opened");
- ZipEntry entry;
- while ((entry = zipFile.GetNextEntry()) != null)
- {
- Logger.log.Debug(entry?.FileName);
- }
+ Logger.log.Debug(entry?.FileName ?? "NULL");
}
-
- Logger.log.Debug("Downloader exited");
}
+ Logger.log.Debug("Downloader exited");
+ }
+
+ IEnumerator UpdateModCoroutine(string tempdir, UpdateStruct item)
+ {
+
string url;
if (SteamCheck.IsAvailable || item.externInfo.OculusFile == null)
url = item.externInfo.SteamFile;
@@ -201,33 +228,45 @@ namespace IllusionInjector.Updating.ModsaberML
url = item.externInfo.OculusFile;
Logger.log.Debug($"URL = {url}");
-
- using (var req = UnityWebRequest.Get(url))
- using (var memStream = new MemoryStream())
+
+ using (var memStream = new EchoStream())
+ using (var stream = new BlockingStream(memStream))
+ using (var request = UnityWebRequest.Get(url))
+ using (var taskTokenSource = new CancellationTokenSource())
{
- req.downloadHandler = new StreamDownloadHandler
- {
- Stream = memStream
- };
+ var dlh = new StreamDownloadHandler(stream);
+ request.downloadHandler = dlh;
var downloadTask = Task.Run(() =>
{ // use slightly more multithreaded approach than coroutines
- DownloadPluginAsync(memStream);
- });
+ DownloadPluginAsync(stream, item, tempdir);
+ }, taskTokenSource.Token);
Logger.log.Debug("Sending request");
- yield return req.SendWebRequest();
+ Logger.log.Debug(request?.downloadHandler?.ToString() ?? "DLH==NULL");
+ yield return request.SendWebRequest();
+ Logger.log.Debug("Download finished");
+
+ if (stream.Open)
+ { // anti-hang
+ Logger.log.Warn("Downloader failed to call DownloadHandler");
+ stream.Open = false; // no more writing
+ stream.BaseStream.Write(new byte[] { 0 }, 0, 1);
+ }
- if (req.isNetworkError)
+ if (request.isNetworkError)
{
Logger.log.Error("Network error while trying to update mod");
- Logger.log.Error(req.error);
+ Logger.log.Error(request.error);
+ taskTokenSource.Cancel();
yield break;
}
- if (req.isHttpError)
+ if (request.isHttpError)
{
Logger.log.Error($"Server returned an error code while trying to update mod");
- Logger.log.Error(req.error);
+ Logger.log.Error(request.error);
+ taskTokenSource.Cancel();
+ yield break;
}
downloadTask.Wait(); // wait for the damn thing to finish
diff --git a/IllusionInjector/Utilities/BlockingStream.cs b/IllusionInjector/Utilities/BlockingStream.cs
index 45e4fb41..e23811bb 100644
--- a/IllusionInjector/Utilities/BlockingStream.cs
+++ b/IllusionInjector/Utilities/BlockingStream.cs
@@ -1,5 +1,7 @@
-using System;
+using IllusionInjector.Logging;
+using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@@ -16,11 +18,27 @@ namespace IllusionInjector.Utilities
public Stream BaseStream { get; set; }
- public override bool CanRead => BaseStream.CanRead;
+ private bool _open = true;
+ public bool Open {
+ get
+ {
+ return CanWrite;
+ }
+ set
+ {
+ if (!_open)
+ throw new InvalidOperationException("Blocking stream has already been closed!");
+ else
+ _open = value;
+ }
+ }
+
+ private bool canReadOverride = true;
+ public override bool CanRead => BaseStream.CanRead && canReadOverride;
public override bool CanSeek => BaseStream.CanSeek;
- public override bool CanWrite => BaseStream.CanWrite;
+ public override bool CanWrite => BaseStream.CanWrite && _open;
public override long Length => BaseStream.Length;
@@ -34,13 +52,17 @@ namespace IllusionInjector.Utilities
public override int Read(byte[] buffer, int offset, int count)
{
var read = 0;
- do
+ while (read < count && Open)
{
read += BaseStream.Read(buffer, read, count-read);
}
- while (read < count);
- return count;
+ if (read == 0)
+ {
+ canReadOverride = false;
+ }
+
+ return read;
}
public override long Seek(long offset, SeekOrigin origin)
@@ -57,5 +79,10 @@ namespace IllusionInjector.Utilities
{
BaseStream.Write(buffer, offset, count);
}
+
+ public override string ToString()
+ {
+ return $"{base.ToString()} ({BaseStream?.ToString()})";
+ }
}
}
diff --git a/IllusionInjector/Utilities/EchoStream.cs b/IllusionInjector/Utilities/EchoStream.cs
new file mode 100644
index 00000000..6d1a3ca0
--- /dev/null
+++ b/IllusionInjector/Utilities/EchoStream.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace IllusionInjector.Utilities
+{
+ public class EchoStream : MemoryStream
+ {
+ private ManualResetEvent m_dataReady = new ManualResetEvent(false);
+ private byte[] m_buffer;
+ private int m_offset;
+ private int m_count;
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ m_buffer = buffer;
+ m_offset = offset;
+ m_count = count;
+ m_dataReady.Set();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (m_buffer == null)
+ {
+ // Block until the stream has some more data.
+ m_dataReady.Reset();
+ m_dataReady.WaitOne();
+ }
+
+ Buffer.BlockCopy(m_buffer, m_offset, buffer, offset, (count < m_count) ? count : m_count);
+ m_buffer = null;
+ return (count < m_count) ? count : m_count;
+ }
+ }
+}
diff --git a/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache b/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache
index ef1f81e3..c8be9064 100644
--- a/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache
+++ b/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache
@@ -1 +1 @@
-28cceae1ca85bfd009d7109ef2bb2d8a1412f4b3
+c0ee92dfbaba38e571471f2b468aeb2a80b0f76b