diff --git a/packages/core/src/modloader.ts b/packages/core/src/modloader.ts index 58fc1b4..7424948 100644 --- a/packages/core/src/modloader.ts +++ b/packages/core/src/modloader.ts @@ -56,7 +56,8 @@ export async function boot(): Promise { let virtualPackages = new Map() .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!)); } @@ -178,6 +179,9 @@ export async function boot(): Promise { 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!'); } @@ -300,6 +304,20 @@ async function loadModMetadata(baseDirectory: string): Promise { } 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)) { diff --git a/packages/runtime/src/_main.ts b/packages/runtime/src/_main.ts index ae9e781..f8f2b17 100644 --- a/packages/runtime/src/_main.ts +++ b/packages/runtime/src/_main.ts @@ -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 { diff --git a/packages/runtime/src/fixes/ccloader2-polyfill.ts b/packages/runtime/src/fixes/ccloader2-polyfill.ts new file mode 100644 index 0000000..e2d4a43 --- /dev/null +++ b/packages/runtime/src/fixes/ccloader2-polyfill.ts @@ -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 +// +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>( + (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 })); +});