Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix deprecations #1269

Merged
merged 3 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading