Implement updater for Windows.
This commit is contained in:
@@ -18,10 +18,8 @@ using unity = UnityEngine;
|
|||||||
|
|
||||||
namespace Cryville.Crtr {
|
namespace Cryville.Crtr {
|
||||||
public static class Game {
|
public static class Game {
|
||||||
public static string GameDataPath {
|
public static string GameDataPath { get; private set; }
|
||||||
get;
|
public static string UnityDataPath { get; private set; }
|
||||||
private set;
|
|
||||||
}
|
|
||||||
public static IAudioDeviceManager AudioManager;
|
public static IAudioDeviceManager AudioManager;
|
||||||
public static AudioClient AudioClient;
|
public static AudioClient AudioClient;
|
||||||
public static SimpleSequencerSource AudioSequencer;
|
public static SimpleSequencerSource AudioSequencer;
|
||||||
@@ -52,6 +50,7 @@ namespace Cryville.Crtr {
|
|||||||
if (_bcflag) Logger.Log("main", 2, "Game", "Reset all settings");
|
if (_bcflag) Logger.Log("main", 2, "Game", "Reset all settings");
|
||||||
|
|
||||||
GameDataPath = Settings.Default.GameDataPath;
|
GameDataPath = Settings.Default.GameDataPath;
|
||||||
|
UnityDataPath = Application.dataPath;
|
||||||
|
|
||||||
unity::Input.simulateMouseWithTouches = false;
|
unity::Input.simulateMouseWithTouches = false;
|
||||||
var emptyObjectArray = new object[0];
|
var emptyObjectArray = new object[0];
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
|
using Cryville.Common;
|
||||||
using Cryville.Common.Network.Http11;
|
using Cryville.Common.Network.Http11;
|
||||||
using Cryville.Common.Unity;
|
using Cryville.Common.Unity;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -57,64 +59,117 @@ namespace Cryville.Crtr.Network {
|
|||||||
Dialog.Show(null, string.Format("You are playing an unknown version of Cosmo Resona: {0}\nThe latest version is: {1}", _currentVersion, latestVersion.name));
|
Dialog.Show(null, string.Format("You are playing an unknown version of Cosmo Resona: {0}\nThe latest version is: {1}", _currentVersion, latestVersion.name));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (latestVersion.name != _currentVersion) {
|
if (latestVersion.name == _currentVersion) return;
|
||||||
var latestResources = latestVersion.platforms[PlatformConfig.Name].resources;
|
var latestResources = latestVersion.platforms[PlatformConfig.Name].resources;
|
||||||
VersionResourceInfo fullPackage = null;
|
VersionResourceInfo fullPackage = null;
|
||||||
if (latestResources != null) {
|
if (latestResources != null) {
|
||||||
fullPackage = (from r in latestResources where r.upstream == null select r).SingleOrDefault();
|
fullPackage = (from r in latestResources where r.upstream == null select r).SingleOrDefault();
|
||||||
|
}
|
||||||
|
if (fullPackage == null) {
|
||||||
|
Dialog.Show(null, string.Format("A new version is present: {0}\nUpdate is not available.", latestVersion.name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long totalDiffSize = 0;
|
||||||
|
int searchIndex = versionIndex[latestVersion.name], targetIndex = versionIndex[currentVersion.name];
|
||||||
|
var stream = new List<VersionResourceInfo>();
|
||||||
|
while (searchIndex != targetIndex) {
|
||||||
|
var searchVersion = availableVersions[searchIndex];
|
||||||
|
VersionResourceInfo matchedUpstream = null;
|
||||||
|
var resources = searchVersion.platforms[PlatformConfig.Name].resources;
|
||||||
|
if (resources == null) {
|
||||||
|
totalDiffSize = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (fullPackage == null) {
|
foreach (var r in resources) {
|
||||||
Dialog.Show(null, string.Format("A new version is present: {0}\nUpdate is not available.", latestVersion.name));
|
if (r.upstream == null) continue;
|
||||||
return;
|
var upstreamIndex = versionIndex[r.upstream];
|
||||||
|
if (upstreamIndex >= targetIndex && upstreamIndex < searchIndex) {
|
||||||
|
matchedUpstream = r;
|
||||||
|
searchIndex = upstreamIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
long totalDiffSize = 0;
|
if (matchedUpstream != null) {
|
||||||
int searchIndex = versionIndex[latestVersion.name], targetIndex = versionIndex[currentVersion.name];
|
if (matchedUpstream.external) {
|
||||||
while (searchIndex != targetIndex) {
|
totalDiffSize = 0;
|
||||||
var searchVersion = availableVersions[searchIndex];
|
Dialog.ShowAndWait("An error occurred while checking for update.\nPlease report this to the developers.");
|
||||||
VersionResourceInfo matchedUpstream = null;
|
throw new InvalidOperationException("Diff package is external, which is not expected");
|
||||||
var resources = searchVersion.platforms[PlatformConfig.Name].resources;
|
}
|
||||||
if (resources == null) {
|
stream.Add(matchedUpstream);
|
||||||
|
totalDiffSize += matchedUpstream.size;
|
||||||
|
if (totalDiffSize >= fullPackage.size) {
|
||||||
totalDiffSize = 0;
|
totalDiffSize = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
foreach (var r in resources) {
|
|
||||||
if (r.upstream == null) continue;
|
|
||||||
var upstreamIndex = versionIndex[r.upstream];
|
|
||||||
if (upstreamIndex >= targetIndex && upstreamIndex < searchIndex) {
|
|
||||||
matchedUpstream = r;
|
|
||||||
searchIndex = upstreamIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matchedUpstream != null) {
|
|
||||||
if (matchedUpstream.external) {
|
|
||||||
totalDiffSize = 0;
|
|
||||||
Dialog.ShowAndWait("An error occurred while checking for update.\nPlease report this to the developers.");
|
|
||||||
Logger.Log("main", 4, "Network", "Diff package is external, which is not expected");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
totalDiffSize += matchedUpstream.size;
|
|
||||||
if (totalDiffSize >= fullPackage.size) {
|
|
||||||
totalDiffSize = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
totalDiffSize = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (totalDiffSize == 0 || totalDiffSize >= fullPackage.size) {
|
|
||||||
// TODO Check if external
|
|
||||||
if (Dialog.ShowAndWait(string.Format("A new version is available: {0}\nYou have to download the full package.\nOpen the download link now?", latestVersion.name), "Yes", "No") == 0) {
|
|
||||||
UrlOpener.OpenThreaded(fullPackage.url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Dialog.ShowAndWait(string.Format("A new version is available: {0}\nDo you want to update?", latestVersion.name), "Yes", "No");
|
totalDiffSize = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (totalDiffSize == 0) {
|
||||||
|
// TODO Check if external
|
||||||
|
if (Dialog.ShowAndWait(string.Format("A new version is available: {0}\nYou have to download the full package.\nOpen the download link now?", latestVersion.name), "Yes", "No") == 0) {
|
||||||
|
UrlOpener.OpenThreaded(fullPackage.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (Dialog.ShowAndWait(string.Format("A new version is available: {0}\nDo you want to update?", latestVersion.name), "Yes", "No") == 0) {
|
||||||
|
var diffPaths = new List<string>();
|
||||||
|
var updateDir = Directory.CreateDirectory(Path.Combine(Game.GameDataPath, "update")).FullName;
|
||||||
|
foreach (var diff in stream) {
|
||||||
|
string path = Path.Combine(updateDir, StringUtils.EscapeFileName(diff.upstream));
|
||||||
|
diffPaths.Add(path);
|
||||||
|
Download(diff, path);
|
||||||
|
}
|
||||||
|
while (Dialog.ShowAndWait("The new version has been downloaded.\nUpdate now?\n(The game will be shut down)", "OK", "Later") == 1)
|
||||||
|
Thread.Sleep(60000);
|
||||||
|
ExecuteUpdate(diffPaths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger.Log("main", 0, "Network", "Update checker exited");
|
|
||||||
}
|
}
|
||||||
|
void Download(VersionResourceInfo diff, string path) {
|
||||||
|
var uri = new Uri(diff.url);
|
||||||
|
using (var client = new Https11Client(uri)) {
|
||||||
|
client.Connect();
|
||||||
|
using (var response = client.Request("GET", uri)) {
|
||||||
|
var data = response.MessageBody.ReadToEnd();
|
||||||
|
using (var file = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) {
|
||||||
|
file.Write(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ExecuteUpdate(List<string> diffPaths) {
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
Dialog.Show(null, "Could not update the game in editor");
|
||||||
|
#elif UNITY_STANDALONE_WIN
|
||||||
|
var gameDir = new DirectoryInfo(Game.UnityDataPath).Parent;
|
||||||
|
var exe = new FileInfo(Path.Combine(gameDir.FullName, System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe"));
|
||||||
|
var updateDir = Path.Combine(Game.GameDataPath, "update");
|
||||||
|
File.Copy(Path.Combine(Game.UnityDataPath, "Plugins", "x86_64", "hpatchz.dll"), Path.Combine(updateDir, "hpatchz.exe"), true);
|
||||||
|
var batchPath = Path.Combine(updateDir, "update.bat");
|
||||||
|
var oldPath = Path.Combine(updateDir, "old");
|
||||||
|
var newPath = Path.Combine(updateDir, "new");
|
||||||
|
using (var batch = new StreamWriter(batchPath, false, new UTF8Encoding(false))) {
|
||||||
|
batch.WriteLine("@echo off\r\ntitle [Updater] Cosmo Resona\r\necho Waiting for the game to shut down...\r\n:pending\r\ntasklist /fi \"ImageName eq {0}\" | find /i \"{0}\" >nul\r\nif \"%ERRORLEVEL%\" == \"0\" goto pending\r\necho Copying old data...\r\nrmdir /s /q {2}\r\nxcopy \"{1}\" \"{2}\" /s /e /h /i /q /y\r\nif not \"%ERRORLEVEL%\" == \"0\" goto error", exe.Name, gameDir, oldPath);
|
||||||
|
for (int i = 0; i < diffPaths.Count; i++) {
|
||||||
|
string path = diffPaths[i];
|
||||||
|
batch.WriteLine("echo Applying patch... ({0}/{1})\r\nhpatchz -f \"{2}\" \"{3}\" \"{4}\"\r\nif not \"%ERRORLEVEL%\" == \"0\" goto error\r\nrmdir /s /q \"{2}\"\r\nmove /y \"{4}\" \"{2}\"\r\nif not \"%ERRORLEVEL%\" == \"0\" goto error", i + 1, diffPaths.Count, oldPath, path, newPath);
|
||||||
|
}
|
||||||
|
batch.WriteLine("echo Copying new data...\r\nrmdir /s /q {1}\r\nmove /y \"{0}\" \"{1}\"\r\nif not \"%ERRORLEVEL%\" == \"0\" goto error\r\nrmdir /s /q \"{2}\"\r\necho Update succeeded\r\npause\r\nexit\r\n:error\r\necho Update failed\r\npause\r\nexit", oldPath, gameDir, updateDir);
|
||||||
|
}
|
||||||
|
ShellExecute(IntPtr.Zero, "open", batchPath, null, Path.Combine(updateDir), 1);
|
||||||
|
_shutdown = true;
|
||||||
|
#elif UNITY_ANDROID
|
||||||
|
// TODO
|
||||||
|
#else
|
||||||
|
#error No update logic for the selected platform.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
|
||||||
|
[DllImport("shell32.dll")]
|
||||||
|
static extern int ShellExecute(IntPtr hwnd, [MarshalAs(UnmanagedType.LPStr)] string lpOperation, [MarshalAs(UnmanagedType.LPStr)] string lpFile, [MarshalAs(UnmanagedType.LPStr)] string lpParameters, [MarshalAs(UnmanagedType.LPStr)] string lpDirectory, int nShowCmd);
|
||||||
|
#endif
|
||||||
class VersionInfo {
|
class VersionInfo {
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public string name;
|
public string name;
|
||||||
|
BIN
Assets/Plugins/Windows/hpatchz.dll
Normal file
BIN
Assets/Plugins/Windows/hpatchz.dll
Normal file
Binary file not shown.
70
Assets/Plugins/Windows/hpatchz.dll.meta
Normal file
70
Assets/Plugins/Windows/hpatchz.dll.meta
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5caa62ca31e13404ea507d8b08b57ea8
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
: Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Android: 1
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux64: 0
|
||||||
|
Exclude OSXUniversal: 0
|
||||||
|
Exclude Win: 0
|
||||||
|
Exclude Win64: 0
|
||||||
|
- first:
|
||||||
|
Android: Android
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: ARMv7
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: AnyOS
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86_64
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Reference in New Issue
Block a user