Skip to content

Commit 87bf102

Browse files
ComputerEliteComputerElite
authored andcommitted
finish modding support for now
1 parent 2b0f8d5 commit 87bf102

9 files changed

Lines changed: 149 additions & 25 deletions

File tree

Assets/html/index.html

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,24 @@ <h3>Currently selected game: <div class="inline packageName">some game</div></h3
2828
<div class="textbox" id="patchingTextBox"></div>
2929
</div>
3030
<div class="contentItem hidden" id="mods">
31-
<b>Please note that mod installation is in VERY early access, there are issues. e. g. no blocking of user actions while an operation is ongoing, no indication wether a mod install/disable was successful</b>
31+
<b>Please note that mod installation is in early access, there may be issues. Feel free to give feedback on the OculusDB Discord Server at <code>discord.gg/zwRfHQN2UY</code></b>
3232
<div class="button" onclick="UploadMod()">Install a Mod</div>
33+
<div id="operations">
34+
<h2 id="ongoingCount">Ongoing operations:</h2>
35+
<div class="loaderContainer" style="margin-bottom: 10px;">
36+
<div class="loaderBarRight"></div>
37+
<div class="loaderBarLeft"></div>
38+
<div class="loaderBarTop"></div>
39+
<div class="loaderBarBottom"></div>
40+
<div class="loaderSpinningCircle"></div>
41+
<div class="loaderMiddleCircle"></div>
42+
<div class="loaderCircleHole"></div>
43+
<div class="loaderSquare"></div>
44+
</div>
45+
<div class="infiniteList" id="operationsList">
46+
${operations}
47+
</div>
48+
</div>
3349
<h2>Mods</h2>
3450
<div class="infiniteList" id="modsList">
3551

Assets/html/script.js

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,12 @@ function UpdatePatchingStatus() {
3232
patchStatus.innerHTML = `Loading<br><br>${squareLoader}`
3333
fetch("/patching/getmodstatus").then(res => {
3434
res.json().then(res => {
35-
/*
36-
if(!IsOnQuest()) {
37-
patchStatus.innerHTML = "<h2>To mod your game open QuestAppVersionSwitcher on your Quest</h2>"
38-
}
39-
*/
4035
if(res.isPatched) {
4136
document.getElementById("modsButton").style.visibility = "visible"
4237
patchStatus.innerHTML = "<h2>Game is already patched. You can install mods</h2>"
38+
} else if(!res.isInstalled) {
39+
patchStatus.innerHTML = `<h2>Game is not installed. Please restore a backup or install the app so the game can get patched</h2>`
40+
document.getElementById("modsButton").style.visibility = "hidden"
4341
} else if(res.canBePatched) {
4442
patchStatus.innerHTML = `<h2>Game is not patched.</h2>
4543
<div class="button" onclick="PatchGame()">Patch it now</div>`
@@ -48,6 +46,11 @@ function UpdatePatchingStatus() {
4846
patchStatus.innerHTML = "<h2>Game can not be modded</h2>"
4947
document.getElementById("modsButton").style.visibility = "hidden"
5048
}
49+
50+
if(!IsOnQuest() && !res.isPatched) {
51+
patchStatus.innerHTML = "<h2>To mod your game open QuestAppVersionSwitcher on your Quest</h2>"
52+
return;
53+
}
5154
})
5255
})
5356
}
@@ -56,17 +59,37 @@ setInterval(() => {
5659
UpdateModsAndLibs()
5760
}, 2000);
5861

62+
var operationsOngoing = false
63+
const operationsElement = document.getElementById("operations")
64+
const ongoingCount = document.getElementById("ongoingCount")
65+
const operationsList = document.getElementById("operationsList")
5966
function UpdateModsAndLibs() {
6067
fetch(`/mods/mods`).then(res => {
6168
res.json().then(res => {
69+
operationsOngoing = res.operations.length > 0
6270
var mods = ``
71+
if(!operationsOngoing) {
72+
operationsElement.style.display = "none"
73+
} else {
74+
operationsElement.style.display = "block"
75+
var operations = ""
76+
for(const operation of res.operations){
77+
operations += `
78+
<div class="mod" style="padding: 10px">
79+
${operation.name}
80+
</div>
81+
`
82+
}
83+
operationsList.innerHTML = operations
84+
ongoingCount.innerHTML = `Ongoing operations: ${res.operations.length}`
85+
}
6386
for(const mod of res.mods){
64-
mods += FormatMod(mod)
87+
mods += FormatMod(mod, !operationsOngoing)
6588
}
6689
document.getElementById("modsList").innerHTML = mods
6790
var libs = ``
6891
for(const mod of res.libs){
69-
libs += FormatMod(mod)
92+
libs += FormatMod(mod, !operationsOngoing)
7093
}
7194
document.getElementById("libsList").innerHTML = libs
7295
})
@@ -99,7 +122,7 @@ function UpdateModState(id, enable) {
99122
})
100123
}
101124

102-
function FormatMod(mod) {
125+
function FormatMod(mod, active = true) {
103126
return `
104127
<div class="mod">
105128
<div class="leftRightSplit">
@@ -112,17 +135,20 @@ function FormatMod(mod) {
112135
</div>
113136
<div class="smallText">${mod.Description}</div>
114137
</div>
115-
<div class="button" onclick="DeleteMod('${mod.Id}')">Delete</div>
138+
${active ? `
139+
<div class="button" onclick="DeleteMod('${mod.Id}')">Delete</div>` : ``}
116140
</div>
117141
</div>
118142
<div class="upDownSplit spaceBetween relative">
119143
<div class="smallText margin20">
120144
(by ${mod.Author})
121145
</div>
146+
${active ? `
122147
<label class="switch">
123148
<input onchange="UpdateModState('${mod.Id}', ${!mod.IsInstalled})" type="checkbox" ${mod.IsInstalled ? `checked` : ``}>
124149
<span class="slider round"></span>
125-
</label>
150+
</label>` : ``}
151+
126152
</div>
127153
</div>
128154
`

ClientModels.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace QuestAppVersionSwitcher.ClientModels
55
public class PatchingStatus
66
{
77
public bool isPatched { get; set; } = false;
8+
public bool isInstalled { get; set; } = true;
89
public bool canBePatched { get; set; } = true; // Not implemented yet.
910
public string version { get; set; } = "";
1011
public string versionCode { get; set; } = "";

CoreService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public async void Start()
3434
{
3535
if (await Permissions.RequestAsync<Permissions.StorageRead>() != PermissionStatus.Granted) return;
3636
}
37-
37+
3838
//Set webbrowser settings
3939
browser.SetWebChromeClient(new WebChromeClient());
4040
browser.Settings.JavaScriptEnabled = true;

Mods/QAVSModManager.cs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using System.Collections.Generic;
1+
using ComputerUtils.Android.Logging;
2+
using System.Collections.Generic;
23
using System.IO;
4+
using System.Linq;
35
using System.Text.Json;
46

57
namespace QuestAppVersionSwitcher.Mods
@@ -8,13 +10,32 @@ public class ModsAndLibs
810
{
911
public List<IMod> mods { get; set; } = new List<IMod>();
1012
public List<IMod> libs { get; set; } = new List<IMod>();
13+
public List<QAVSOperation> operations { get; set; } = new List<QAVSOperation>();
1114
}
15+
16+
public enum QAVSOperationType
17+
{
18+
ModInstall,
19+
ModUninstall,
20+
ModDisable,
21+
ModDelete,
22+
DependencyDownload,
23+
Other
24+
}
25+
26+
public class QAVSOperation
27+
{
28+
public QAVSOperationType type { get; set; } = QAVSOperationType.ModInstall;
29+
public string name { get; set; } = "";
30+
}
31+
1232
public class QAVSModManager
1333
{
1434
public static ModManager modManager;
1535
public static OtherFilesManager otherFilesManager;
16-
public static bool operationOngoing = false;
1736
public static JsonSerializerOptions options;
37+
public static int operations = 0;
38+
public static Dictionary<int, QAVSOperation> runningOperations = new Dictionary<int, QAVSOperation>();
1839

1940
public static void Init()
2041
{
@@ -36,16 +57,25 @@ public static void Update()
3657

3758
public static void InstallMod(byte[] modBytes, string fileName)
3859
{
60+
int operationId = operations;
61+
operations++;
62+
runningOperations.Add(operationId, new QAVSOperation { type = QAVSOperationType.ModInstall, name = "Installing " + fileName });
63+
3964
TempFile f = new TempFile(Path.GetExtension(fileName));
4065
File.WriteAllBytes(f.Path, modBytes);
4166
IMod mod = modManager.TryParseMod(f.Path).Result;
42-
mod.Install();
67+
mod.Install().Wait();
68+
runningOperations.Remove(operationId);
4369
modManager.ForceSave();
4470
}
4571

4672
public static void UninstallMod(string id)
4773
{
48-
foreach(IMod m in modManager.AllMods)
74+
int operationId = operations;
75+
operations++;
76+
runningOperations.Add(operationId, new QAVSOperation { type = QAVSOperationType.ModUninstall, name = "Unnstalling " + id });
77+
78+
foreach (IMod m in modManager.AllMods)
4979
{
5080
if(m.Id == id)
5181
{
@@ -54,10 +84,15 @@ public static void UninstallMod(string id)
5484
break;
5585
}
5686
}
87+
88+
runningOperations.Remove(operationId);
5789
}
5890

5991
public static void DeleteMod(string id)
6092
{
93+
int operationId = operations;
94+
operations++;
95+
runningOperations.Add(operationId, new QAVSOperation { type = QAVSOperationType.ModDelete, name = "Deleting " + id });
6196
foreach (IMod m in modManager.AllMods)
6297
{
6398
if (m.Id == id)
@@ -67,27 +102,34 @@ public static void DeleteMod(string id)
67102
break;
68103
}
69104
}
105+
runningOperations.Remove(operationId);
70106
}
71107

72108
public static void EnableMod(string id)
73109
{
110+
int operationId = operations;
111+
operations++;
112+
runningOperations.Add(operationId, new QAVSOperation { type = QAVSOperationType.ModInstall, name = "Installing " + id });
113+
74114
foreach (IMod m in modManager.AllMods)
75115
{
76116
if (m.Id == id)
77117
{
78-
m.Install();
118+
m.Install().Wait();
79119
modManager.ForceSave();
80120
break;
81121
}
82122
}
123+
runningOperations.Remove(operationId);
83124
}
84125

85126
public static string GetMods()
86127
{
87128
return JsonSerializer.Serialize(new ModsAndLibs
88129
{
89130
mods = modManager.Mods,
90-
libs = modManager.Libraries
131+
libs = modManager.Libraries,
132+
operations = runningOperations.Values.ToList()
91133
});
92134
}
93135

Mods/QPMod.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,20 @@ private async Task Install(List<string> installedInBranch)
117117
}
118118
foreach(KeyValuePair<string, string> k in copyPaths)
119119
{
120-
File.Copy(k.Key, k.Value, true);
120+
try
121+
{
122+
string dir = Directory.GetParent(k.Value).FullName;
123+
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
124+
File.Copy(k.Key, k.Value, true);
125+
} catch(Exception e)
126+
{
127+
Logger.Log(e.ToString(), LoggingType.Error);
128+
}
121129
}
122130
IsInstalled = true;
123131
installedInBranch.Remove(Id);
132+
Logger.Log("Install method finished");
133+
return;
124134
}
125135

126136
public async Task Uninstall()
@@ -203,6 +213,9 @@ public byte[] OpenCover()
203213
/// <param name="installedInBranch">The number of mods that are currently downloading down this branch of the install "tree", used to check for cyclic dependencies</param>
204214
private async Task PrepareDependency(Dependency dependency, List<string> installedInBranch)
205215
{
216+
int operationId = QAVSModManager.operations;
217+
QAVSModManager.operations++;
218+
QAVSModManager.runningOperations.Add(operationId, new QAVSOperation { type = QAVSOperationType.DependencyDownload, name = "Downloading Dependency " + dependency.Id });
206219
Logger.Log($"Preparing dependency of {dependency.Id} version {dependency.VersionRange}");
207220
int existingIndex = installedInBranch.FindIndex(downloadedDep => downloadedDep == dependency.Id);
208221
if (existingIndex != -1)
@@ -213,7 +226,7 @@ private async Task PrepareDependency(Dependency dependency, List<string> install
213226
dependMessage += $"{installedInBranch[i]} depends on ";
214227
}
215228
dependMessage += dependency.Id;
216-
229+
QAVSModManager.runningOperations.Remove(operationId);
217230
throw new InstallationException($"Recursive dependency detected: {dependMessage}");
218231
}
219232

@@ -229,6 +242,7 @@ private async Task PrepareDependency(Dependency dependency, List<string> install
229242
Logger.Log($"Installing dependency {dependency.Id} . . .");
230243
await existing.Install(installedInBranch);
231244
}
245+
QAVSModManager.runningOperations.Remove(operationId);
232246
return;
233247
}
234248

@@ -238,11 +252,13 @@ private async Task PrepareDependency(Dependency dependency, List<string> install
238252
}
239253
else
240254
{
255+
QAVSModManager.runningOperations.Remove(operationId);
241256
throw new InstallationException($"Dependency with ID {dependency.Id} is already installed but with an incorrect version ({existing.Version} does not intersect {dependency.VersionRange}). Upgrading was not possible as there was no download link provided");
242257
}
243258
}
244259
else if (dependency.DownloadUrlString == null)
245260
{
261+
QAVSModManager.runningOperations.Remove(operationId);
246262
throw new InstallationException($"Dependency {dependency.Id} is not installed, and the mod depending on it does not specify a download path if missing");
247263
}
248264

@@ -256,25 +272,31 @@ private async Task PrepareDependency(Dependency dependency, List<string> install
256272
catch (WebException ex)
257273
{
258274
// Print a nicer error message
275+
QAVSModManager.runningOperations.Remove(operationId);
259276
throw new InstallationException($"Failed to download dependency from URL {dependency.DownloadIfMissing}: {ex.Message}", ex);
260277
}
261278

262279
installedDependency = (QPMod)await _provider.LoadFromFile(downloadFile.Path);
263280

264281
await installedDependency.Install(installedInBranch);
265282

283+
Logger.Log("Installed Dependency");
284+
266285
// Sanity checks that the download link actually pointed to the right mod
267286
if (dependency.Id != installedDependency.Id)
268287
{
269288
await _provider.DeleteMod(installedDependency);
289+
QAVSModManager.runningOperations.Remove(operationId);
270290
throw new InstallationException($"Downloaded dependency had ID {installedDependency.Id}, whereas the dependency stated ID {dependency.Id}");
271291
}
272292

273293
if (!dependency.VersionRange.IsSatisfied(installedDependency.Version))
274294
{
275295
await _provider.DeleteMod(installedDependency);
296+
QAVSModManager.runningOperations.Remove(operationId);
276297
throw new InstallationException($"Downloaded dependency {installedDependency.Id} v{installedDependency.Version} was not within the version range stated in the dependency info ({dependency.VersionRange})");
277298
}
299+
QAVSModManager.runningOperations.Remove(operationId);
278300
}
279301
}
280302
}

PatchingManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public static void PatchAPK(ZipArchive apkArchive, string appLocation)
117117
QAVSWebserver.patchText = JsonSerializer.Serialize(new MessageAndValue<String>("Almost done. Hang tight", ""));
118118
WebClient c = new WebClient();
119119
PatchingStatus status = JsonSerializer.Deserialize<PatchingStatus>(c.DownloadString("http://127.0.0.1:" + CoreService.coreVars.serverPort + "/patching/getmodstatus")); // seems to be the easiest way
120-
string backupName = status.version + "_patched";
120+
string backupName = QAVSWebserver.MakeFileNameSafe(status.version) + "_patched";
121121
string backupDir = CoreService.coreVars.QAVSBackupDir + CoreService.coreVars.currentApp + "/" + backupName + "/";
122122
FileManager.RecreateDirectoryIfExisting(backupDir);
123123
File.Move(appLocation, backupDir + "app.apk");

QuestAppVersionSwitcher.csproj.user

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<PropertyGroup>
4-
<SelectedDevice>Google Pixel 6</SelectedDevice>
5-
<ActiveDebugProfile>Google Pixel 6</ActiveDebugProfile>
4+
<SelectedDevice>Oculus Quest</SelectedDevice>
5+
<ActiveDebugProfile>Oculus Quest</ActiveDebugProfile>
66
<DefaultDevice>Pixel_5_API_30</DefaultDevice>
77
<AndroidDesignerPreferredTheme>AppTheme</AndroidDesignerPreferredTheme>
88
<AndroidDesignerPreferredDevice>Nexus 4</AndroidDesignerPreferredDevice>

0 commit comments

Comments
 (0)