1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-14 02:31:51 +00:00

Refactor how mods are loaded to resolve circular dependencies and prepare for future mod loading

This commit is contained in:
tobspr 2022-01-14 07:18:25 +01:00
parent 01b9bf561c
commit 16ecbf9c6d
10 changed files with 159 additions and 139 deletions

View File

@ -1,7 +1,7 @@
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { Signal } from "../../core/signal"; import { Signal } from "../../core/signal";
import { MODS } from "../../mods/modloader"; import { MOD_SIGNALS } from "../../mods/mod_signals";
import { KEYMAPPINGS } from "../key_action_mapper"; import { KEYMAPPINGS } from "../key_action_mapper";
import { MetaBuilding } from "../meta_building"; import { MetaBuilding } from "../meta_building";
import { GameRoot } from "../root"; import { GameRoot } from "../root";
@ -92,7 +92,7 @@ export class GameHUD {
const frag = document.createDocumentFragment(); const frag = document.createDocumentFragment();
for (const key in this.parts) { for (const key in this.parts) {
MODS.signals.hudElementInitialized.dispatch(this.parts[key]); MOD_SIGNALS.hudElementInitialized.dispatch(this.parts[key]);
this.parts[key].createElements(frag); this.parts[key].createElements(frag);
} }
@ -100,7 +100,7 @@ export class GameHUD {
for (const key in this.parts) { for (const key in this.parts) {
this.parts[key].initialize(); this.parts[key].initialize();
MODS.signals.hudElementFinalized.dispatch(this.parts[key]); MOD_SIGNALS.hudElementFinalized.dispatch(this.parts[key]);
} }
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this); this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this);

View File

@ -10,10 +10,14 @@ import { COLOR_ITEM_SINGLETONS } from "./items/color_item";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { enumSubShape } from "./shape_definition"; import { enumSubShape } from "./shape_definition";
import { Rectangle } from "../core/rectangle"; import { Rectangle } from "../core/rectangle";
import { MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS } from "../mods/mod_interface";
const logger = createLogger("map_chunk"); const logger = createLogger("map_chunk");
/**
* @type {Object<string, (distanceToOriginInChunks: number) => number>}
*/
export const MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS = {};
export class MapChunk { export class MapChunk {
/** /**
* *

View File

@ -38,7 +38,7 @@ import { HUDSandboxController } from "../hud/parts/sandbox_controller";
import { queryParamOptions } from "../../core/query_parameters"; import { queryParamOptions } from "../../core/query_parameters";
import { MetaBlockBuilding } from "../buildings/block"; import { MetaBlockBuilding } from "../buildings/block";
import { MetaItemProducerBuilding } from "../buildings/item_producer"; import { MetaItemProducerBuilding } from "../buildings/item_producer";
import { MODS } from "../../mods/modloader"; import { MOD_SIGNALS } from "../../mods/mod_signals";
/** @typedef {{ /** @typedef {{
* shape: string, * shape: string,
@ -512,7 +512,7 @@ export function generateLevelDefinitions(limitedVersion = false) {
]), ]),
]; ];
MODS.signals.modifyLevelDefinitions.dispatch(levelDefinitions); MOD_SIGNALS.modifyLevelDefinitions.dispatch(levelDefinitions);
if (G_IS_DEV) { if (G_IS_DEV) {
levelDefinitions.forEach(({ shape }) => { levelDefinitions.forEach(({ shape }) => {

View File

@ -3,11 +3,23 @@ import { globalConfig } from "../core/config";
import { smoothenDpi } from "../core/dpi_manager"; import { smoothenDpi } from "../core/dpi_manager";
import { DrawParameters } from "../core/draw_parameters"; import { DrawParameters } from "../core/draw_parameters";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { MODS_ADDITIONAL_SUB_SHAPE_DRAWERS } from "../mods/mod_interface";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors"; import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors";
import { THEME } from "./theme"; import { THEME } from "./theme";
/**
* @typedef {{
* context: CanvasRenderingContext2D,
* quadrantSize: number,
* layerScale: number,
* }} SubShapeDrawOptions
*/
/**
* @type {Object<string, (options: SubShapeDrawOptions) => void>}
*/
export const MODS_ADDITIONAL_SUB_SHAPE_DRAWERS = {};
/** /**
* @typedef {{ * @typedef {{
* subShape: enumSubShape, * subShape: enumSubShape,

View File

@ -1,4 +1,4 @@
import { MODS } from "../mods/modloader"; import { MOD_SIGNALS } from "../mods/mod_signals";
export const THEMES = { export const THEMES = {
dark: require("./themes/dark.json"), dark: require("./themes/dark.json"),
@ -9,5 +9,5 @@ export let THEME = THEMES.light;
export function applyGameTheme(id) { export function applyGameTheme(id) {
THEME = THEMES[id]; THEME = THEMES[id];
MODS.signals.preprocessTheme.dispatch({ id, theme: THEME }); MOD_SIGNALS.preprocessTheme.dispatch({ id, theme: THEME });
} }

View File

@ -2,10 +2,8 @@
import { Entity } from "../game/entity"; import { Entity } from "../game/entity";
/* typehints:end */ /* typehints:end */
import { Mod } from "./mod"; export default function ({ Mod, MetaBuilding }) {
import { MetaBuilding } from "../game/meta_building"; class MetaDemoModBuilding extends MetaBuilding {
export class MetaDemoModBuilding extends MetaBuilding {
constructor() { constructor() {
super("demoModBuilding"); super("demoModBuilding");
} }
@ -21,7 +19,7 @@ export class MetaDemoModBuilding extends MetaBuilding {
setupEntityComponents(entity) {} setupEntityComponents(entity) {}
} }
export class DemoMod extends Mod { return class ModImpl extends Mod {
constructor(modLoader) { constructor(modLoader) {
super( super(
{ {
@ -37,17 +35,17 @@ export class DemoMod extends Mod {
init() { init() {
// Add some custom css // Add some custom css
this.modLoader.modInterface.registerCss(` this.modInterface.registerCss(`
* { * {
color: red !important; color: red !important;
} }
`); `);
// Replace a builtin sprite // Replace a builtin sprite
this.modLoader.modInterface.registerSprite("sprites/colors/red.png", RESOURCES["red.png"]); this.modInterface.registerSprite("sprites/colors/red.png", RESOURCES["red.png"]);
// Add a new type of sub shape ("Line", short code "L") // Add a new type of sub shape ("Line", short code "L")
this.modLoader.modInterface.registerSubShapeType({ this.modInterface.registerSubShapeType({
id: "line", id: "line",
shortCode: "L", shortCode: "L",
weightComputation: distanceToOriginInChunks => weightComputation: distanceToOriginInChunks =>
@ -69,17 +67,17 @@ export class DemoMod extends Mod {
}); });
// Modify the theme colors // Modify the theme colors
this.modLoader.signals.preprocessTheme.add(({ theme }) => { this.signals.preprocessTheme.add(({ theme }) => {
theme.map.background = "#eee"; theme.map.background = "#eee";
theme.items.outline = "#000"; theme.items.outline = "#000";
}); });
// Modify the goal of the first level // Modify the goal of the first level
this.modLoader.signals.modifyLevelDefinitions.add(definitions => { this.signals.modifyLevelDefinitions.add(definitions => {
definitions[0].shape = "LuCuLuCu"; definitions[0].shape = "LuCuLuCu";
}); });
this.modLoader.modInterface.registerTranslations("en", { this.modInterface.registerTranslations("en", {
ingame: { ingame: {
interactiveTutorial: { interactiveTutorial: {
title: "Hello", title: "Hello",
@ -91,7 +89,7 @@ export class DemoMod extends Mod {
}); });
// Register the new building // Register the new building
this.modLoader.modInterface.registerNewBuilding({ this.modInterface.registerNewBuilding({
metaClass: MetaDemoModBuilding, metaClass: MetaDemoModBuilding,
buildingIconBase64: RESOURCES["demoBuilding.png"], buildingIconBase64: RESOURCES["demoBuilding.png"],
@ -108,13 +106,14 @@ export class DemoMod extends Mod {
}); });
// Add it to the regular toolbar // Add it to the regular toolbar
this.modLoader.signals.hudElementInitialized.add(element => { this.signals.hudElementInitialized.add(element => {
if (element.constructor.name === "HUDBuildingsToolbar") { if (element.constructor.name === "HUDBuildingsToolbar") {
// @ts-ignore // @ts-ignore
element.primaryBuildings.push(MetaDemoModBuilding); element.primaryBuildings.push(MetaDemoModBuilding);
} }
}); });
} }
};
} }
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////

View File

@ -2,6 +2,8 @@
import { ModLoader } from "./modloader"; import { ModLoader } from "./modloader";
/* typehints:end */ /* typehints:end */
import { MOD_SIGNALS } from "./mod_signals";
export class Mod { export class Mod {
/** /**
* *
@ -17,6 +19,9 @@ export class Mod {
constructor(metadata, modLoader) { constructor(metadata, modLoader) {
this.metadata = metadata; this.metadata = metadata;
this.modLoader = modLoader; this.modLoader = modLoader;
this.signals = MOD_SIGNALS;
this.modInterface = modLoader.modInterface;
} }
init() {} init() {}

View File

@ -6,33 +6,21 @@ import { MetaBuilding } from "../game/meta_building";
import { defaultBuildingVariant } from "../game/meta_building"; import { defaultBuildingVariant } from "../game/meta_building";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { AtlasSprite, SpriteAtlasLink } from "../core/sprites"; import { AtlasSprite, SpriteAtlasLink } from "../core/sprites";
import { enumShortcodeToSubShape, enumSubShape, enumSubShapeToShortcode } from "../game/shape_definition"; import {
enumShortcodeToSubShape,
enumSubShape,
enumSubShapeToShortcode,
MODS_ADDITIONAL_SUB_SHAPE_DRAWERS,
} from "../game/shape_definition";
import { Loader } from "../core/loader"; import { Loader } from "../core/loader";
import { LANGUAGES } from "../languages"; import { LANGUAGES } from "../languages";
import { matchDataRecursive, T } from "../translations"; import { matchDataRecursive, T } from "../translations";
import { registerBuildingVariant } from "../game/building_codes"; import { registerBuildingVariant } from "../game/building_codes";
import { gMetaBuildingRegistry } from "../core/global_registries"; import { gMetaBuildingRegistry } from "../core/global_registries";
import { MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS } from "../game/map_chunk";
const LOG = createLogger("mod-interface"); const LOG = createLogger("mod-interface");
/**
* @type {Object<string, (distanceToOriginInChunks: number) => number>}
*/
export const MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS = {};
/**
* @typedef {{
* context: CanvasRenderingContext2D,
* quadrantSize: number,
* layerScale: number,
* }} SubShapeDrawOptions
*/
/**
* @type {Object<string, (options: SubShapeDrawOptions) => void>}
*/
export const MODS_ADDITIONAL_SUB_SHAPE_DRAWERS = {};
export class ModInterface { export class ModInterface {
/** /**
* *
@ -91,7 +79,7 @@ export class ModInterface {
* @param {string} param0.id * @param {string} param0.id
* @param {string} param0.shortCode * @param {string} param0.shortCode
* @param {(distanceToOriginInChunks: number) => number} param0.weightComputation * @param {(distanceToOriginInChunks: number) => number} param0.weightComputation
* @param {(options: SubShapeDrawOptions) => void} param0.draw * @param {(options: import("../game/shape_definition").SubShapeDrawOptions) => void} param0.draw
*/ */
registerSubShapeType({ id, shortCode, weightComputation, draw }) { registerSubShapeType({ id, shortCode, weightComputation, draw }) {
if (shortCode.length !== 1) { if (shortCode.length !== 1) {

View File

@ -0,0 +1,16 @@
import { Signal } from "../core/signal";
/* typehints:start */
import { BaseHUDPart } from "../game/hud/base_hud_part";
/* typehints:end */
// Single file to avoid circular deps
export const MOD_SIGNALS = {
postInit: new Signal(),
injectSprites: new Signal(),
preprocessTheme: /** @type {TypedSignal<[Object]>} */ (new Signal()),
modifyLevelDefinitions: /** @type {TypedSignal<[Array[Object]]>} */ (new Signal()),
hudElementInitialized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()),
hudElementFinalized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()),
};

View File

@ -1,9 +1,8 @@
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { Signal } from "../core/signal";
import { DemoMod } from "./demo_mod";
import { Mod } from "./mod"; import { Mod } from "./mod";
import { ModInterface } from "./mod_interface"; import { ModInterface } from "./mod_interface";
import { BaseHUDPart } from "../game/hud/base_hud_part"; import { MetaBuilding } from "../game/meta_building";
import { MOD_SIGNALS } from "./mod_signals";
const LOG = createLogger("mods"); const LOG = createLogger("mods");
@ -21,17 +20,14 @@ export class ModLoader {
this.initialized = false; this.initialized = false;
this.signals = { this.signals = MOD_SIGNALS;
postInit: new Signal(),
injectSprites: new Signal(),
preprocessTheme: /** @type {TypedSignal<[Object]>} */ (new Signal()),
modifyLevelDefinitions: /** @type {TypedSignal<[Array[Object]]>} */ (new Signal()),
hudElementInitialized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()), this.registerMod(
hudElementFinalized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()), /** @type {any} */ (require("./demo_mod").default({
}; Mod,
MetaBuilding,
this.registerMod(DemoMod); }))
);
this.initMods(); this.initMods();
} }