Skip to content

Commit

Permalink
Merge other recent LocalModList changes
Browse files Browse the repository at this point in the history
  • Loading branch information
MythicManiac committed Jan 22, 2024
2 parents b64c89c + 64acae7 commit cb39207
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 49 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
### 3.1.46
- Games added:
- Sailwind
- Meeple Station
- Void Crew
- Bug fixes:
- Clicking the version number no longer takes you to a broken link
- Pagination takes up less screen space
- Dependencies can now be uninstalled/disabled without removing the dependent mod

### 3.1.45
- Games added:
- Cities: Skylines II
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "r2modman",
"version": "3.1.45",
"version": "3.1.46",
"description": "A simple and easy to use mod manager for several games using Thunderstore.",
"productName": "r2modman",
"author": "ebkr",
Expand Down
2 changes: 1 addition & 1 deletion src/_managerinf/ManagerInformation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import VersionNumber from '../model/VersionNumber';

export default class ManagerInformation {
public static VERSION: VersionNumber = new VersionNumber('3.1.45');
public static VERSION: VersionNumber = new VersionNumber('3.1.46');
public static IS_PORTABLE: boolean = false;
public static APP_NAME: string = "r2modman";
}
3 changes: 3 additions & 0 deletions src/components/mixins/SplashMixin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class SplashMixin extends Vue {
};
ThunderstorePackages.EXCLUSIONS = await ConnectionProvider.instance.getExclusions(showProgress);
this.getRequestItem('ExclusionsList').setProgress(100);
}
// Get the list of Thunderstore mods from API.
Expand All @@ -62,6 +63,8 @@ export default class SplashMixin extends Vue {
this.isOffline = true;
this.heroTitle = 'Failed to get mods from Thunderstore';
this.loadingText = 'You may be offline or Thunderstore is unavailabe. Checking cache.';
} finally {
this.getRequestItem('ThunderstoreDownload').setProgress(100);
}
if (response) {
Expand Down
9 changes: 9 additions & 0 deletions src/components/mixins/UtilityMixin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ export default class UtilityMixin extends Vue {
}
async refreshThunderstoreModList() {
// Don't do background update on index route since the game
// isn't really chosen yet, nor in the splash screen since it
// proactively updates the package list.
const exemptRoutes = ["index", "splash"];
if (this.$route.name && exemptRoutes.includes(this.$route.name)) {
return;
}
const response = await ThunderstorePackages.update(GameManager.activeGame);
await ApiCacheUtils.storeLastRequest(response.data);
await this.$store.dispatch("updateThunderstoreModList", ThunderstorePackages.PACKAGES);
Expand Down
98 changes: 64 additions & 34 deletions src/components/views/LocalModList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
</template>
<template v-slot:body>
<div v-if="dependencyListDisplayType === 'disable'" class='notification is-warning'>
<p>Other mods depend on this mod. Disabling this mod will disable all other dependants.</p>
<p>Other mods depend on this mod. Select <strong>Disable all</strong> to disable dependent mods which may cause errors.</p>
</div>
<div v-if="dependencyListDisplayType === 'uninstall'" class='notification is-warning'>
<p>Other mods depend on this mod. Uninstalling this mod will remove all mods that depend on it.</p>
<p>Other mods depend on this mod. Select <strong>Uninstall all</strong> to uninstall dependent mods which may cause errors.</p>
</div>
<p v-if="dependencyListDisplayType === 'disable'">Mods to be disabled:</p>
<p v-if="dependencyListDisplayType === 'uninstall'">Mods to be uninstalled:</p>
Expand Down Expand Up @@ -93,14 +93,22 @@
</template>
<template v-slot:footer>
<button v-if="dependencyListDisplayType === 'disable'" class="button is-info"
@click="disableMod(selectedManifestMod)">
Disable
@click="disableModWithDependents(selectedManifestMod)">
Disable all (recommended)
</button>
<button v-if="dependencyListDisplayType === 'uninstall'"
class="button is-info"
<button v-if="dependencyListDisplayType === 'disable'" class="button"
@click="disableModExcludeDependents(selectedManifestMod)">
Disable {{selectedManifestMod.getName()}} only
</button>
<button v-if="dependencyListDisplayType === 'uninstall'" class="button is-info"
:disabled="modBeingUninstalled !== null"
@click="uninstallModWithDependents(selectedManifestMod)">
Uninstall all (recommended)
</button>
<button v-if="dependencyListDisplayType === 'uninstall'" class="button"
:disabled="modBeingUninstalled !== null"
@click="uninstallMod(selectedManifestMod)">
Uninstall
@click="uninstallModExcludeDependents(selectedManifestMod)">
Uninstall {{selectedManifestMod.getName()}} only
</button>
<span v-if="modBeingUninstalled" class="tag is-warning margin-top--1rem margin-left">
Uninstalling {{ modBeingUninstalled }}
Expand Down Expand Up @@ -168,8 +176,8 @@
<i class='fas fa-cloud-upload-alt' v-tooltip.left="'An update is available'"></i>
</span>
<span class='card-header-icon'
v-if="getMissingDependencies(key).length > 0">
<i class='fas fa-exclamation-circle' v-tooltip.left="`Missing ${getMissingDependencies(key).length} dependencies`"></i>
v-if="getDisabledDependencies(key).length > 0 || getMissingDependencies(key).length > 0">
<i class='fas fa-exclamation-circle' v-tooltip.left="`There is an issue with the dependencies for this mod`"></i>
</span>
<span class='card-header-icon'
@click.prevent.stop="() => key.isEnabled() ? disableModRequireConfirmation(key) : enableMod(key)">
Expand Down Expand Up @@ -198,6 +206,10 @@
@click="downloadDependency(getMissingDependencies(key)[0])">
Download dependency
</a>
<a class='card-footer-item' v-if="getDisabledDependencies(key).length > 0"
@click="enableMod(getDisabledDependencies(key)[0])">
Enable {{getDisabledDependencies(key)[0].getDisplayName()}}
</a>
<template v-if="getThunderstoreModFromMod(key) !== undefined">
<template v-if="getThunderstoreModFromMod(key).getDonationLink() !== undefined">
<DonateButton :mod="getThunderstoreModFromMod(key)"/>
Expand Down Expand Up @@ -379,6 +391,21 @@ import SearchUtils from '../../utils/SearchUtils';
});
}
getDisabledDependencies(vueMod: any): ManifestV2[] {
const mod: Mod = new Mod().fromReactive(vueMod);
const installedMods = [...this.modifiableModList];
const installedDependencies = mod.getDependencies().filter((dependency: string) => {
return this.modifiableModList.find((localMod: ManifestV2) => dependency.toLowerCase().startsWith(localMod.getName().toLowerCase() + "-"));
})
.filter(value => installedMods.find(installed => value.toLowerCase().startsWith(installed.getName().toLowerCase() + "-")))
.map(value => installedMods.find(installed => value.toLowerCase().startsWith(installed.getName().toLowerCase() + "-")));
const safeInstalledDependencies = installedDependencies as ManifestV2[];
return safeInstalledDependencies.filter(value => !value.isEnabled());
}
getDependantList(mod: ManifestV2): Set<ManifestV2> {
return Dependants.getDependantList(mod, this.modifiableModList);
}
Expand Down Expand Up @@ -408,10 +435,19 @@ import SearchUtils from '../../utils/SearchUtils';
return modList;
}
async disableMod(vueMod: any) {
async disableModWithDependents(vueMod: any) {
const mod: ManifestV2 = new ManifestV2().fromReactive(vueMod);
await this.disableMods([...Dependants.getDependantList(mod, this.modifiableModList), mod]);
}
async disableModExcludeDependents(vueMod: any) {
const mod: ManifestV2 = new ManifestV2().fromReactive(vueMod);
await this.disableMods([mod]);
}
async disableMods(modsToDisable: ManifestV2[]) {
try {
const result = await this.performDisable([...Dependants.getDependantList(mod, this.modifiableModList), mod]);
const result = await this.performDisable(modsToDisable);
if (result instanceof R2Error) {
this.$emit('error', result);
return;
Expand Down Expand Up @@ -447,13 +483,21 @@ import SearchUtils from '../../utils/SearchUtils';
await this.updateModListAfterChange(updatedList);
}
async uninstallMod(vueMod: any) {
async uninstallModWithDependents(vueMod: any) {
let mod: ManifestV2 = new ManifestV2().fromReactive(vueMod);
await this.uninstallMods([...Dependants.getDependantList(mod, this.modifiableModList), mod]);
}
async uninstallModExcludeDependents(vueMod: any) {
let mod: ManifestV2 = new ManifestV2().fromReactive(vueMod);
await this.uninstallMods([mod]);
}
async uninstallMods(modsToUninstall: ManifestV2[]) {
let lastSuccess: ManifestV2[] | null = null;
try {
for (const dependant of Dependants.getDependantList(mod, this.modifiableModList)) {
this.modBeingUninstalled = dependant.getName();
const result = await this.performUninstallMod(dependant, false);
for (const mod of modsToUninstall) {
const result = await this.performUninstallMod(mod, false);
if (result instanceof R2Error) {
this.$emit('error', result);
this.modBeingUninstalled = null;
Expand All @@ -462,15 +506,6 @@ import SearchUtils from '../../utils/SearchUtils';
lastSuccess = result;
}
}
this.modBeingUninstalled = mod.getName();
const result = await this.performUninstallMod(mod, false);
if (result instanceof R2Error) {
this.$emit('error', result);
this.modBeingUninstalled = null;
return;
} else {
lastSuccess = result;
}
} catch (e) {
// Failed to uninstall mod.
const err: Error = e as Error;
Expand All @@ -495,7 +530,6 @@ import SearchUtils from '../../utils/SearchUtils';
this.selectedManifestMod = new ManifestV2().fromReactive(vueMod);
this.dependencyListDisplayType = displayType;
this.showingDependencyList = true;
console.log(this.dependencyListDisplayType, this.showingDependencyList)
}
uninstallModRequireConfirmation(vueMod: any) {
Expand All @@ -509,17 +543,13 @@ import SearchUtils from '../../utils/SearchUtils';
disableModRequireConfirmation(vueMod: any) {
const mod: ManifestV2 = new ManifestV2().fromReactive(vueMod);
const enabledDependants: ManifestV2[] = [];
this.getDependantList(mod).forEach(value => {
for (const value of this.getDependantList(mod)) {
if (value.isEnabled()) {
enabledDependants.push(value);
this.showDependencyList(mod, DependencyListDisplayType.DISABLE);
return;
}
});
if (enabledDependants.length === 0) {
this.performDisable([mod]);
} else {
this.showDependencyList(mod, DependencyListDisplayType.DISABLE);
}
this.performDisable([mod]);
}
viewDependencyList(vueMod: any) {
Expand Down
6 changes: 5 additions & 1 deletion src/model/game/GameManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,14 @@ export default class GameManager {
"https://thunderstore.io/c/hard-bullet/api/v1/package/", "https://raw.githubusercontent.com/ebkr/r2modmanPlus/master/modExclusions.md",
[new StorePlatformMetadata(StorePlatform.STEAM, "1294760")], "HardBullet.jpg",
GameSelectionDisplayMode.VISIBLE, GameInstanceType.GAME, PackageLoader.MELON_LOADER, ["hb"]),

new Game("20 Minutes Till Dawn", "20MinutesTillDawn", "20MinutesTillDawn",
"20MinuteTillDawn", ["MinutesTillDawn.exe"], "MinutesTillDawn_Data",
"https://thunderstore.io/c/20-minutes-till-dawn/api/v1/package/", "https://raw.githubusercontent.com/ebkr/r2modmanPlus/master/modExclusions.md",
[new StorePlatformMetadata(StorePlatform.STEAM, "1966900")], "20MinutesTillDawn.jpg",
[
new StorePlatformMetadata(StorePlatform.STEAM, "1966900"),
new StorePlatformMetadata(StorePlatform.EPIC_GAMES_STORE, "4656facc740742a39e265b026e13d075")
], "20MinutesTillDawn.jpg",
GameSelectionDisplayMode.VISIBLE, GameInstanceType.GAME, PackageLoader.BEPINEX, ["mtd", "20mtd"]),

new Game("Green Hell VR", "GreenHellVR", "GreenHellVR",
Expand Down
6 changes: 5 additions & 1 deletion src/pages/Splash.vue
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,11 @@ export default class Splash extends mixins(SplashMixin) {
}
retryConnection() {
this.$router.go(0);
this.getRequestItem('UpdateCheck').setProgress(0);
this.getRequestItem('ExclusionsList').setProgress(0);
this.getRequestItem('ThunderstoreDownload').setProgress(0);
this.isOffline = false;
this.checkForUpdates();
}
private async ensureWrapperInGameFolder() {
Expand Down
13 changes: 5 additions & 8 deletions src/r2mm/connection/ConnectionProviderImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import GameManager from '../../model/game/GameManager';
import ConnectionProvider, { DownloadProgressed } from '../../providers/generic/connection/ConnectionProvider';
import LoggerProvider, { LogSeverity } from '../../providers/ror2/logging/LoggerProvider';
import { sleep } from '../../utils/Common';
import { makeLongRunningGetRequest } from '../../utils/HttpUtils';

export default class ConnectionProviderImpl extends ConnectionProvider {

Expand Down Expand Up @@ -37,14 +38,10 @@ export default class ConnectionProviderImpl extends ConnectionProvider {
}

private async getPackagesFromRemote(game: Game, downloadProgressed?: DownloadProgressed) {
const response = await axios.get(game.thunderstoreUrl, {
onDownloadProgress: progress => {
if (downloadProgressed !== undefined) {
downloadProgressed((progress.loaded / progress.total) * 100);
}
},
timeout: 30000
});
const response = await makeLongRunningGetRequest(
game.thunderstoreUrl,
{downloadProgressed}
)

if (isApiResonse(response)) {
return response as ApiResponse;
Expand Down
Loading

0 comments on commit cb39207

Please sign in to comment.