Skip to content
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
20 changes: 19 additions & 1 deletion packages/core/src/modloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export async function boot(): Promise<void> {

let virtualPackages = new Map<ModID, semver.SemVer>()
.set('crosscode', gameVersion)
.set('ccloader', modloaderMetadata.version);
.set('ccloader', modloaderMetadata.version)
.set('Simplify', new semver.SemVer('2.14.2'));
if (typeof process !== 'undefined') {
virtualPackages.set('nw', new semver.SemVer(process.versions.nw!));
}
Expand Down Expand Up @@ -178,6 +179,9 @@ export async function boot(): Promise<void> {
console.log("stage 'poststart' reached!");
await executeStage(loadedMods, 'poststart');

// NOTE: LEGACY CCLOADER2 EVENT
document.body.dispatchEvent(new Event('modsLoaded', { bubbles: true }));

activeDelegateFn();
console.log('crosscode with mods is now fully loaded!');
}
Expand Down Expand Up @@ -300,6 +304,20 @@ async function loadModMetadata(baseDirectory: string): Promise<Mod | null> {
} else {
manifestData = manifestData as Manifest;
manifestValidator.validate(manifestData);

// NOTE: During the migration period of legacy mods to the new format,
// their `package.json`s were converted to `ccmod.json`. The problem is
// that apparently nobody involved in this process (even the ones who
// implemented this detail) didn't remember that in the new format, paths
// are assumed to be within `assets/`. So, what happened? The `assets`
// array from `package.json` was copied 1:1 to `ccmod.json`, meaning that
// there are now `ccmod.json`s with paths in `assets` that start with
// `assets/`. This is resolved exclusively for legacy manifests
// (obviously) by the `convertFromLegacy` call in the `if` block above,
// but not for `ccmod.json`s that were incorrectly migrated. Instead of
// going through the hassle of getting those mods updated *again*, I'll
// just fix it once and for all.
manifestData.assets = manifestData.assets?.map((e) => e.replace(/^assets\//, ''));
}
} catch (err) {
if (utils.errorHasMessage(err)) {
Expand Down
3 changes: 3 additions & 0 deletions packages/runtime/src/_main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export default class RuntimeModMainClass implements modloader.Mod.PluginClass {
moduleCache,
resources,
});

// There's probably an issue with this being a non-awaited import.
import('./fixes/ccloader2-polyfill.js');
}

public onImpactInit(): void {
Expand Down
103 changes: 103 additions & 0 deletions packages/runtime/src/fixes/ccloader2-polyfill.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* eslint-disable consistent-return */
// This may be one of the worst pieces of this codebase. Please don't judge too heavily!

import semver from '@ccloader3/common/vendor-libs/semver';
import type { LocalizedString } from 'ultimate-crosscode-typedefs/file-types/mod-manifest';

interface LegacyMod {
baseDirectory: string;
name: LocalizedString;
displayName: LocalizedString;
}

const Plugin = class Plugin {
public preload(): void {}
public postload(): void {}
public prestart(): void {}
public main(): void {}
};

// The de-facto Simplify Compat Layer
// <https://github.com/CCDirectLink/CCLoader/blob/0b23512543bf85ef21757b36492f991c03a16ef7/assets/mods/simplify/mod.js>
const _simplify = {
version: '2.14.2',
updateHandlers: [] as Array<() => void>,

registerUpdate(handler?: () => void) {
if (handler && typeof handler === 'function') {
this.updateHandlers.push(handler);
}
},
// technically never called by us, but let's keep it in just to be sure.
fireUpdate() {
for (const handler of this.updateHandlers) {
handler();
}
},

getMod(name: string): LegacyMod | undefined {
const mod = modloader.loadedMods.get(name);
if (!mod) return;

return {
baseDirectory: mod.baseDirectory,
name: mod.id,
displayName: mod.manifest.title || mod.id,
};
},

// seriously? :harold:
jumpHigh() {
ig.game.playerEntity.doJump(185, 16, 100);
},
};

// For some reason this was just an alias to `window`????
const cc = window;
const simplify = new Proxy(_simplify, {
get(target, p) {
// This is honestly just vile
const prop = target[p as keyof typeof _simplify];
p = String(p);

// Should it ever occur that I've forgotten something in the compat layer, let the user know.
if (!prop) {
console.warn(new Error(`Couldn't find '${p}' in Simplify compat layer. Please report!`));
return;
}

console.debug(new Error(`Simplify property '${p}' requested. This is deprecated.`));
return prop;
},
});

Object.assign(window, {
cc,
simplify,
Plugin,
semver,

versions: Array.from(modloader.installedMods.values()).reduce<Record<string, string>>(
(acc, mod) => {
if (!mod.version) {
console.debug('Mod without a version detected in construction of window.versions');
return acc;
}
acc[mod.id] = mod.version.toString();
return acc;
},
{},
),
activeMods: [...modloader.loadedMods.values()],
inactiveMods: [...modloader.installedMods.values()].filter(
(mod) => !modloader.modDataStorage.isModEnabled(mod.id),
),
});

// For some reason these were distinct events despite the fact that they fire
// right after eachother. After the `main` stage ran, `modsLoaded` was
// dispatched, while Simplify was already listening for it, causing it to
// trigger immediately. Strange decision to even include `simplifyInitialized`.
document.body.addEventListener('modsLoaded', () => {
document.body.dispatchEvent(new Event('simplifyInitialized', { bubbles: true }));
});