Skip to content

Commit 4a876d0

Browse files
authored
Merge pull request #2073 from thunderstore-io/shimloader-rule-installer
Remove shimloader default install rules
2 parents 7930cca + 44c7a8e commit 4a876d0

4 files changed

Lines changed: 131 additions & 72 deletions

File tree

src/installers/ShimloaderInstaller.ts

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import FsProvider from "../providers/generic/file/FsProvider";
44
import FileTree from "../model/file/FileTree";
55
import FileUtils from "../utils/FileUtils";
66
import R2Error from "../model/errors/R2Error";
7-
import { InstallRuleInstaller } from "./InstallRuleInstaller";
8-
import { TrackingMethod } from "../model/schema/ThunderstoreSchema";
97

108
export class ShimloaderInstaller implements PackageInstaller {
119
/**
@@ -55,35 +53,3 @@ export class ShimloaderInstaller implements PackageInstaller {
5553
}
5654
}
5755
}
58-
59-
export class ShimloaderPluginInstaller implements PackageInstaller {
60-
readonly installer = () => new InstallRuleInstaller({
61-
gameName: "none" as any, // This isn't acutally used for actual installation but needs some value
62-
rules: [
63-
{
64-
route: path.join("shimloader", "mod"),
65-
isDefaultLocation: true,
66-
defaultFileExtensions: [],
67-
trackingMethod: TrackingMethod.SUBDIR,
68-
subRoutes: [],
69-
},
70-
{
71-
route: path.join("shimloader", "pak"),
72-
defaultFileExtensions: [],
73-
trackingMethod: TrackingMethod.SUBDIR,
74-
subRoutes: [],
75-
},
76-
{
77-
route: path.join("shimloader", "cfg"),
78-
defaultFileExtensions: [],
79-
trackingMethod: TrackingMethod.NONE,
80-
subRoutes: [],
81-
}
82-
],
83-
relativeFileExclusions: null
84-
});
85-
86-
async install(args: InstallArgs) {
87-
await this.installer().install(args);
88-
}
89-
}

src/installers/registry.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { BepInExInstaller } from './BepInExInstaller';
22
import { GodotMLInstaller } from './GodotMLInstaller';
33
import { MelonLoaderInstaller } from './MelonLoaderInstaller';
44
import { PackageInstaller } from './PackageInstaller';
5-
import { ShimloaderInstaller, ShimloaderPluginInstaller } from './ShimloaderInstaller';
5+
import { ShimloaderInstaller } from './ShimloaderInstaller';
66
import { LovelyInstaller, LovelyPluginInstaller } from './LovelyInstaller';
77
import { NorthstarInstaller } from './NorthstarInstaller';
88
import { ReturnOfModdingInstaller, ReturnOfModdingPluginInstaller } from './ReturnOfModdingInstaller';
@@ -46,7 +46,7 @@ export function getPackageLoaderInstaller(loader: PackageLoader): PackageInstall
4646
/**
4747
* Plugin installer registry
4848
*/
49-
type InstallRuleInstallers = PackageLoader.BEPINEX | PackageLoader.BEPISLOADER | PackageLoader.GODOTML | PackageLoader.MELONLOADER | PackageLoader.NORTHSTAR | PackageLoader.UMM;
49+
type InstallRuleInstallers = PackageLoader.BEPINEX | PackageLoader.BEPISLOADER | PackageLoader.GODOTML | PackageLoader.MELONLOADER | PackageLoader.NORTHSTAR | PackageLoader.SHIMLOADER | PackageLoader.UMM;
5050
type PluginInstallers = Exclude<PackageLoader, InstallRuleInstallers>;
5151

5252
const PluginInstallers: Record<PluginInstallers, PackageInstaller> = {
@@ -55,7 +55,6 @@ const PluginInstallers: Record<PluginInstallers, PackageInstaller> = {
5555
[PackageLoader.NONE]: new DirectCopyInstaller(),
5656
[PackageLoader.RECURSIVE_MELONLOADER]: new RecursiveMelonLoaderPluginInstaller(),
5757
[PackageLoader.RETURN_OF_MODDING]: new ReturnOfModdingPluginInstaller(),
58-
[PackageLoader.SHIMLOADER]: new ShimloaderPluginInstaller(),
5958
[PackageLoader.RIVET]: new RivetPluginInstaller(),
6059
};
6160

@@ -66,6 +65,7 @@ function isPluginInstaller(loader: PackageLoader): loader is PluginInstallers {
6665
loader === PackageLoader.GODOTML ||
6766
loader === PackageLoader.MELONLOADER ||
6867
loader === PackageLoader.NORTHSTAR ||
68+
loader === PackageLoader.SHIMLOADER ||
6969
loader === PackageLoader.UMM
7070
);
7171
}

src/r2mm/installing/profile_installers/GenericProfileInstaller.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import FileUtils from '../../../utils/FileUtils';
1919
import { getPackageLoaderInstaller, getPluginInstaller } from "../../../installers/registry";
2020
import { InstallArgs, PackageInstaller } from "../../../installers/PackageInstaller";
2121
import { InstallRuleInstaller } from "../../../installers/InstallRuleInstaller";
22-
import { ShimloaderPluginInstaller } from "../../../installers/ShimloaderInstaller";
2322
import { ReturnOfModdingPluginInstaller } from "../../../installers/ReturnOfModdingInstaller";
2423
import { TrackingMethod } from '../../../model/schema/ThunderstoreSchema';
2524

@@ -44,10 +43,7 @@ export default class GenericProfileInstaller extends ProfileInstallerProvider {
4443
// known case.
4544
let rule = this.rule;
4645
const installer = getPluginInstaller(GameManager.activeGame.packageLoader);
47-
if (
48-
installer instanceof ShimloaderPluginInstaller ||
49-
installer instanceof ReturnOfModdingPluginInstaller
50-
) {
46+
if (installer instanceof ReturnOfModdingPluginInstaller) {
5147
rule = installer.installer().rule;
5248
}
5349
if (!rule) {

test/vitest/tests/unit/Installers/ModLoader/Shimloader.Tests.spec.ts

Lines changed: 127 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,136 @@ import R2Error from '../../../../../../src/model/errors/R2Error';
99
import Profile from '../../../../../../src/model/Profile';
1010
import ProfileInstallerProvider from '../../../../../../src/providers/ror2/installing/ProfileInstallerProvider';
1111
import GenericProfileInstaller from '../../../../../../src/r2mm/installing/profile_installers/GenericProfileInstaller';
12+
import InstallationRules, { RuleSubtype } from '../../../../../../src/r2mm/installing/InstallationRules';
13+
import { TrackingMethod } from '../../../../../../src/model/schema/ThunderstoreSchema';
1214
import {describe, beforeEach, test, expect} from 'vitest';
1315

16+
function getShimloaderRules(includePakExtension: boolean): RuleSubtype[] {
17+
return [
18+
{
19+
route: 'shimloader/mod',
20+
isDefaultLocation: true,
21+
defaultFileExtensions: [],
22+
trackingMethod: TrackingMethod.SUBDIR,
23+
subRoutes: [],
24+
},
25+
{
26+
route: 'shimloader/pak',
27+
defaultFileExtensions: includePakExtension ? ['pak'] : [],
28+
trackingMethod: TrackingMethod.SUBDIR,
29+
subRoutes: [],
30+
},
31+
{
32+
route: 'shimloader/cfg',
33+
defaultFileExtensions: [],
34+
trackingMethod: TrackingMethod.NONE,
35+
subRoutes: [],
36+
},
37+
];
38+
}
39+
40+
function setPalworldInstallRules(rules: RuleSubtype[]) {
41+
const existingRules = InstallationRules.RULES;
42+
const palworldIdx = existingRules.findIndex((rule) => rule.gameName === 'Palworld');
43+
44+
if (palworldIdx === -1) {
45+
throw new Error('Palworld install rules not found');
46+
}
47+
48+
existingRules[palworldIdx] = {
49+
...existingRules[palworldIdx],
50+
rules,
51+
};
52+
InstallationRules.RULES = existingRules;
53+
}
54+
1455
describe('Shimloader Installer Tests', () => {
56+
describe('Schema-defined installRules', () => {
57+
58+
beforeEach(async () => {
59+
await installLogicBeforeEach('Palworld');
60+
setPalworldInstallRules(getShimloaderRules(false));
61+
});
62+
63+
test('Installs and uninstalls a package', async () => {
64+
const pkg = createManifest("test_mod", "auth");
65+
const name = pkg.getName();
66+
const sourceToExpectedDestination = {
67+
"README.md": `shimloader/mod/${name}/README.md`,
68+
"manifest.json": `shimloader/mod/${name}/manifest.json`,
69+
"icon.png": `shimloader/mod/${name}/icon.png`,
70+
"pak/blueprint.pak": `shimloader/pak/${name}/blueprint.pak`,
71+
"mod/scripts/main.lua": `shimloader/mod/${name}/scripts/main.lua`,
72+
"mod/scripts/other.lua": `shimloader/mod/${name}/scripts/other.lua`,
73+
"mod/dll/mod.dll": `shimloader/mod/${name}/dll/mod.dll`,
74+
"cfg/package.cfg": `shimloader/cfg/package.cfg`,
75+
};
76+
const expectedAfterUninstall = [
77+
"shimloader/cfg/package.cfg",
78+
];
79+
await createPackageFilesIntoCache(pkg, Object.keys(sourceToExpectedDestination));
80+
81+
ProfileInstallerProvider.provide(() => new GenericProfileInstaller());
82+
await ProfileInstallerProvider.instance.installMod(pkg, Profile.getActiveProfile().asImmutableProfile());
83+
await expectFilesToBeCopied(sourceToExpectedDestination);
84+
85+
const result = await ProfileInstallerProvider.instance.uninstallMod(pkg, Profile.getActiveProfile().asImmutableProfile());
86+
expect(result instanceof R2Error).toBeFalsy();
87+
await expectFilesToBeRemoved(sourceToExpectedDestination, expectedAfterUninstall);
88+
});
89+
90+
test('Loose .pak files route to schema default location when no extension rule exists', async () => {
91+
const pkg = createManifest("pak_mod", "auth");
92+
const name = pkg.getName();
93+
const sourceToExpectedDestination = {
94+
"manifest.json": `shimloader/mod/${name}/manifest.json`,
95+
"icon.png": `shimloader/mod/${name}/icon.png`,
96+
"loose_mod.pak": `shimloader/mod/${name}/loose_mod.pak`,
97+
};
98+
await createPackageFilesIntoCache(pkg, Object.keys(sourceToExpectedDestination));
99+
100+
ProfileInstallerProvider.provide(() => new GenericProfileInstaller());
101+
await ProfileInstallerProvider.instance.installMod(pkg, Profile.getActiveProfile().asImmutableProfile());
102+
await expectFilesToBeCopied(sourceToExpectedDestination);
103+
});
104+
});
105+
106+
describe('Schema-defined installRules with pak extension rule', () => {
107+
108+
beforeEach(async () => {
109+
await installLogicBeforeEach('Palworld');
110+
setPalworldInstallRules(getShimloaderRules(true));
111+
});
112+
113+
test('Loose .pak files route to shimloader/pak when schema defines .pak extension', async () => {
114+
const pkg = createManifest("pak_mod", "auth");
115+
const name = pkg.getName();
116+
const sourceToExpectedDestination = {
117+
"manifest.json": `shimloader/mod/${name}/manifest.json`,
118+
"icon.png": `shimloader/mod/${name}/icon.png`,
119+
"loose_mod.pak": `shimloader/pak/${name}/loose_mod.pak`,
120+
};
121+
await createPackageFilesIntoCache(pkg, Object.keys(sourceToExpectedDestination));
122+
123+
ProfileInstallerProvider.provide(() => new GenericProfileInstaller());
124+
await ProfileInstallerProvider.instance.installMod(pkg, Profile.getActiveProfile().asImmutableProfile());
125+
await expectFilesToBeCopied(sourceToExpectedDestination);
126+
});
127+
128+
test('Subdirectory pak files still route to shimloader/pak', async () => {
129+
const pkg = createManifest("pak_subdir_mod", "auth");
130+
const name = pkg.getName();
131+
const sourceToExpectedDestination = {
132+
"manifest.json": `shimloader/mod/${name}/manifest.json`,
133+
"pak/blueprint.pak": `shimloader/pak/${name}/blueprint.pak`,
134+
"mod/scripts/main.lua": `shimloader/mod/${name}/scripts/main.lua`,
135+
"cfg/package.cfg": `shimloader/cfg/package.cfg`,
136+
};
137+
await createPackageFilesIntoCache(pkg, Object.keys(sourceToExpectedDestination));
15138

16-
beforeEach(() => {
17-
installLogicBeforeEach('Palworld');
18-
}
19-
);
20-
21-
test('Installs and uninstalls a package', async () => {
22-
const pkg = createManifest("test_mod", "auth");
23-
const name = pkg.getName();
24-
const sourceToExpectedDestination = {
25-
"README.md": `shimloader/mod/${name}/README.md`,
26-
"manifest.json": `shimloader/mod/${name}/manifest.json`,
27-
"icon.png": `shimloader/mod/${name}/icon.png`,
28-
"pak/blueprint.pak": `shimloader/pak/${name}/blueprint.pak`,
29-
"mod/scripts/main.lua": `shimloader/mod/${name}/scripts/main.lua`,
30-
"mod/scripts/other.lua": `shimloader/mod/${name}/scripts/other.lua`,
31-
"mod/dll/mod.dll": `shimloader/mod/${name}/dll/mod.dll`,
32-
"cfg/package.cfg": `shimloader/cfg/package.cfg`,
33-
};
34-
const expectedAfterUninstall = [
35-
"shimloader/cfg/package.cfg",
36-
];
37-
await createPackageFilesIntoCache(pkg, Object.keys(sourceToExpectedDestination));
38-
39-
ProfileInstallerProvider.provide(() => new GenericProfileInstaller());
40-
await ProfileInstallerProvider.instance.installMod(pkg, Profile.getActiveProfile().asImmutableProfile());
41-
await expectFilesToBeCopied(sourceToExpectedDestination);
42-
43-
const result = await ProfileInstallerProvider.instance.uninstallMod(pkg, Profile.getActiveProfile().asImmutableProfile());
44-
expect(result instanceof R2Error).toBeFalsy();
45-
expectFilesToBeRemoved(sourceToExpectedDestination, expectedAfterUninstall)
139+
ProfileInstallerProvider.provide(() => new GenericProfileInstaller());
140+
await ProfileInstallerProvider.instance.installMod(pkg, Profile.getActiveProfile().asImmutableProfile());
141+
await expectFilesToBeCopied(sourceToExpectedDestination);
142+
});
46143
});
47144
});

0 commit comments

Comments
 (0)