Skip to content

Commit

Permalink
Merge pull request #1269 from ebkr/deprecations
Browse files Browse the repository at this point in the history
Fix deprecations
  • Loading branch information
anttimaki authored Apr 15, 2024
2 parents 2882b5b + e04294a commit ec3e491
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 39 deletions.
7 changes: 2 additions & 5 deletions src/components/navigation/NavigationMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ import R2Error from '../../model/errors/R2Error';
import Game from '../../model/game/Game';
import GameManager from '../../model/game/GameManager';
import Profile from '../../model/Profile';
import ThunderstoreMod from '../../model/ThunderstoreMod';
import {
LaunchMode,
launch,
Expand All @@ -83,11 +82,9 @@ export default class NavigationMenu extends Vue {
private LaunchMode = LaunchMode;
get thunderstoreModCount() {
let mods: ThunderstoreMod[] = this.$store.state.tsMods.mods;
return this.$store.state.modFilters.showDeprecatedPackages
? mods.length
: mods.filter((m) => !m.isDeprecated()).length;
? this.$store.state.tsMods.mods.length
: this.$store.getters['tsMods/undeprecatedModCount'];
}
get localModCount(): number {
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/LocalModList/LocalModCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class LocalModCard extends Vue {
}
get isDeprecated() {
return this.tsMod ? this.tsMod.isDeprecated() : false;
return this.$store.state.tsMods.deprecated.get(this.mod.getName()) || false;
}
get isLatestVersion() {
Expand Down
4 changes: 3 additions & 1 deletion src/components/views/OnlineModView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ export default class OnlineModView extends Vue {
this.searchableThunderstoreModList = this.searchableThunderstoreModList.filter(mod => !mod.getNsfwFlag());
}
if (!showDeprecatedPackages) {
this.searchableThunderstoreModList = this.searchableThunderstoreModList.filter(mod => !mod.isDeprecated());
this.searchableThunderstoreModList = this.searchableThunderstoreModList.filter(
mod => !this.$store.state.tsMods.deprecated.get(mod.getFullName())
);
}
if (filterCategories.length > 0) {
this.searchableThunderstoreModList = this.searchableThunderstoreModList.filter((x: ThunderstoreMod) => {
Expand Down
96 changes: 64 additions & 32 deletions src/r2mm/data/ThunderstorePackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import ConnectionProvider from '../../providers/generic/connection/ConnectionPro
import * as PackageDb from '../manager/PackageDexieStore';

export default class ThunderstorePackages {

public static PACKAGES_MAP: Map<String, ThunderstoreMod> = new Map();
// TODO: would IndexedDB or Vuex be more suitable place for exclusions?
public static EXCLUSIONS: string[] = [];

Expand Down Expand Up @@ -34,50 +32,84 @@ export default class ThunderstorePackages {
}

public static getDeprecatedPackageMap(packages: ThunderstoreMod[]): Map<string, boolean> {
ThunderstorePackages.PACKAGES_MAP = packages.reduce((map, pkg) => {
const packageMap = packages.reduce((map, pkg) => {
map.set(pkg.getFullName(), pkg);
return map;
}, new Map<String, ThunderstoreMod>());
const deprecationMap = new Map<string, boolean>();
const currentChain = new Set<string>();

const result = new Map<string, boolean>();
packages.forEach(pkg => {
this.populateDeprecatedPackageMapForModChain(pkg, result);
this._populateDeprecatedPackageMapForModChain(pkg, packageMap, deprecationMap, currentChain);
});
return result;

return deprecationMap;
}

/**
* TODO: This doesn't really do what the dosctring below says:
* deprecated dependencies do NOT mark the dependant deprecated.
*
* "Smart" package deprecation determination by keeping track of previously determine dependencies.
* "Smart" package deprecation determination by keeping track of previously determined dependencies.
* This ensures that we hit as few iterations as possible to speed up calculation time.
*
* @param mod The mod to check for deprecation status / deprecated dependencies
* @param map A map to record previously hit items
* @private
* @param deprecationMap A map to record previously hit items
* @param currentChain A set to record recursion stack to avoid infinite loops
* @public (to allow tests to mock the function)
*/
private static populateDeprecatedPackageMapForModChain(mod: ThunderstoreMod, map: Map<string, boolean>) {
if (map.get(mod.getFullName()) != undefined) {
return; // Deprecation status has already been decided.
} else {
if (mod.isDeprecated()) {
map.set(mod.getFullName(), true);
} else {
for (const value of mod.getDependencies()) {
const tsVariant = this.PACKAGES_MAP.get(value)
if (tsVariant === undefined) {
continue;
}
this.populateDeprecatedPackageMapForModChain(tsVariant, map);
}
// If mod was not set down the chain then has no deprecated dependencies.
// This means the mod does not result in a deprecation status.
if (map.get(mod.getFullName()) === undefined) {
map.set(mod.getFullName(), false);
}
public static _populateDeprecatedPackageMapForModChain(
mod: ThunderstoreMod,
packageMap: Map<String, ThunderstoreMod>,
deprecationMap: Map<string, boolean>,
currentChain: Set<string>
): boolean {
const previouslyCalculatedValue = deprecationMap.get(mod.getFullName());
if (previouslyCalculatedValue !== undefined) {
return previouslyCalculatedValue;
}

// No need to check dependencies if the mod itself is deprecated.
// Dependencies will be checked by the for-loop in the calling
// function anyway.
if (mod.isDeprecated()) {
deprecationMap.set(mod.getFullName(), true);
return true;
}

for (const dependencyNameAndVersion of mod.getLatestVersion().getDependencies()) {
const dependencyName = dependencyNameAndVersion.substring(0, dependencyNameAndVersion.lastIndexOf('-'));

if (currentChain.has(dependencyName)) {
continue;
}
const dependency = packageMap.get(dependencyName);

// Package isn't available on Thunderstore, so we can't tell
// if it's deprecated or not. This will also include deps of
// packages uploaded into wrong community since the
// packageMap contains only packages from this community.
// Based on manual testing with real data, caching these to
// deprecationMap doesn't seem to improve overall performance.
if (dependency === undefined) {
continue;
}

// Keep track of the dependency chain currently under
// investigation to avoid infinite recursive loops.
currentChain.add(mod.getFullName());
const dependencyDeprecated = this._populateDeprecatedPackageMapForModChain(
dependency, packageMap, deprecationMap, currentChain
);
currentChain.delete(mod.getFullName());
deprecationMap.set(dependencyName, dependencyDeprecated);

// Eject early on the first deprecated dependency for performance.
if (dependencyDeprecated) {
deprecationMap.set(mod.getFullName(), true);
return true;
}
}
}

// Package is not depreceated by itself nor due to dependencies.
deprecationMap.set(mod.getFullName(), false);
return false;
}
}
4 changes: 4 additions & 0 deletions src/store/modules/TsModsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ export const TsModsModule = {
/*** Return ThunderstoreMod representation of a ManifestV2 */
tsMod: (_state, getters) => (mod: ManifestV2): ThunderstoreMod | undefined => {
return getters.cachedMod(mod).tsMod;
},

undeprecatedModCount(state) {
return [...state.deprecated].filter(([_, isDeprecated]) => !isDeprecated).length;
}
},

Expand Down
Loading

0 comments on commit ec3e491

Please sign in to comment.