modwright 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +212 -0
- package/assets/claude/agents/mw-explore.md +46 -0
- package/assets/claude/agents/mw-fixture.md +33 -0
- package/assets/claude/agents/mw-rule-author.md +51 -0
- package/assets/claude/agents/mw-scaffold-author.md +48 -0
- package/assets/claude/skills/mw-author.md +59 -0
- package/assets/claude/skills/mw-new-item.md +51 -0
- package/assets/claude/skills/mw-new-spell.md +50 -0
- package/assets/claude/skills/mw-scaffold.md +59 -0
- package/assets/claude/skills/mw-ship.md +57 -0
- package/assets/claude/skills/mw-validate.md +51 -0
- package/assets/claude/skills/mw-verify.md +59 -0
- package/assets/nivalisnights/extract_resources.py +177 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Config.json +5 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/BootstrapClient.lua +55 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/BootstrapServer.lua +81 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Log.lua +96 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Probes.lua +1038 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/ProbesClient.lua +74 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Protocol.lua +985 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Watch.lua +175 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/meta.lsx +45 -0
- package/bridges/bg3-se/README.md +99 -0
- package/bridges/bg3-se/bridge.json +68 -0
- package/bridges/cp2077-cet/ModWrightBridge/init.lua +375 -0
- package/bridges/cp2077-cet/ModWrightBridge/probes.lua +1104 -0
- package/bridges/cp2077-cet/ModWrightBridge/protocol.lua +1306 -0
- package/bridges/cp2077-cet/README.md +165 -0
- package/bridges/cp2077-cet/bridge.json +53 -0
- package/dist/core/bridge/arm.js +175 -0
- package/dist/core/bridge/cet-console.js +26 -0
- package/dist/core/bridge/cet-log.js +362 -0
- package/dist/core/bridge/index.js +11 -0
- package/dist/core/bridge/locate.js +35 -0
- package/dist/core/bridge/paths.js +34 -0
- package/dist/core/bridge/pending.js +89 -0
- package/dist/core/bridge/poll.js +155 -0
- package/dist/core/bridge/stage.js +100 -0
- package/dist/core/bridge/status.js +97 -0
- package/dist/core/bridge/types.js +145 -0
- package/dist/core/bridge/validate.js +107 -0
- package/dist/core/build/index.js +6 -0
- package/dist/core/build/manifest.js +47 -0
- package/dist/core/build/paths.js +32 -0
- package/dist/core/build/run.js +342 -0
- package/dist/core/build/stage.js +265 -0
- package/dist/core/build/steps/bg3.js +129 -0
- package/dist/core/build/steps/core.js +195 -0
- package/dist/core/build/steps/cyberpunk.js +95 -0
- package/dist/core/build/steps/listing.js +35 -0
- package/dist/core/build/types.js +13 -0
- package/dist/core/build/zip.js +148 -0
- package/dist/core/compat/check.js +113 -0
- package/dist/core/compat/pe.js +220 -0
- package/dist/core/compat/types.js +1 -0
- package/dist/core/compat/version.js +70 -0
- package/dist/core/deploy/guards.js +292 -0
- package/dist/core/deploy/index.js +5 -0
- package/dist/core/deploy/plan.js +696 -0
- package/dist/core/deploy/reload-apply.js +361 -0
- package/dist/core/deploy/reload.js +358 -0
- package/dist/core/deploy/sources.js +169 -0
- package/dist/core/deploy/types.js +1 -0
- package/dist/core/epic.js +54 -0
- package/dist/core/fsutil.js +191 -0
- package/dist/core/home.js +70 -0
- package/dist/core/knowledge/facts.js +229 -0
- package/dist/core/knowledge/index.js +4 -0
- package/dist/core/knowledge/search.js +112 -0
- package/dist/core/knowledge/store.js +123 -0
- package/dist/core/knowledge/types.js +1 -0
- package/dist/core/ledger/check.js +42 -0
- package/dist/core/ledger/hooks.js +80 -0
- package/dist/core/ledger/index.js +7 -0
- package/dist/core/ledger/query.js +17 -0
- package/dist/core/ledger/record.js +84 -0
- package/dist/core/ledger/status.js +53 -0
- package/dist/core/ledger/store.js +98 -0
- package/dist/core/ledger/types.js +108 -0
- package/dist/core/logs/index.js +4 -0
- package/dist/core/logs/registry.js +50 -0
- package/dist/core/logs/scan.js +195 -0
- package/dist/core/logs/triage.js +160 -0
- package/dist/core/logs/types.js +1 -0
- package/dist/core/lsx.js +10 -0
- package/dist/core/lz4.js +66 -0
- package/dist/core/mods.js +229 -0
- package/dist/core/process.js +49 -0
- package/dist/core/project/build-steps.js +83 -0
- package/dist/core/project/describe.js +74 -0
- package/dist/core/project/index.js +5 -0
- package/dist/core/project/load.js +146 -0
- package/dist/core/project/schema.js +264 -0
- package/dist/core/project/types.js +3 -0
- package/dist/core/prompts.js +72 -0
- package/dist/core/registry.js +35 -0
- package/dist/core/safety/backup.js +206 -0
- package/dist/core/safety/execute.js +188 -0
- package/dist/core/safety/index.js +5 -0
- package/dist/core/safety/plan.js +122 -0
- package/dist/core/safety/restore.js +172 -0
- package/dist/core/safety/types.js +1 -0
- package/dist/core/scaffold/bootstrap.js +7 -0
- package/dist/core/scaffold/catalog.js +28 -0
- package/dist/core/scaffold/claude-assets.js +271 -0
- package/dist/core/scaffold/collisions.js +64 -0
- package/dist/core/scaffold/emit.js +356 -0
- package/dist/core/scaffold/engine.js +680 -0
- package/dist/core/scaffold/ids.js +78 -0
- package/dist/core/scaffold/index.js +10 -0
- package/dist/core/scaffold/jsonpath.js +121 -0
- package/dist/core/scaffold/load.js +81 -0
- package/dist/core/scaffold/render.js +85 -0
- package/dist/core/scaffold/sequence.js +396 -0
- package/dist/core/scaffold/types.js +309 -0
- package/dist/core/steam.js +63 -0
- package/dist/core/surfaceutil.js +36 -0
- package/dist/core/testplan/check.js +67 -0
- package/dist/core/testplan/index.js +5 -0
- package/dist/core/testplan/load.js +100 -0
- package/dist/core/testplan/registry.js +317 -0
- package/dist/core/testplan/run.js +389 -0
- package/dist/core/testplan/types.js +82 -0
- package/dist/core/text/converters/bg3.js +50 -0
- package/dist/core/text/converters/cyberpunk.js +36 -0
- package/dist/core/text/converters/index.js +261 -0
- package/dist/core/text/diff/cr2w.js +180 -0
- package/dist/core/text/diff/index.js +16 -0
- package/dist/core/text/diff/loca.js +43 -0
- package/dist/core/text/diff/lsx.js +126 -0
- package/dist/core/text/handles.js +86 -0
- package/dist/core/text/index.js +6 -0
- package/dist/core/text/json.js +46 -0
- package/dist/core/text/templates/cache.js +71 -0
- package/dist/core/text/templates/index.js +3 -0
- package/dist/core/text/templates/library.js +245 -0
- package/dist/core/text/templates/types.js +51 -0
- package/dist/core/text/toolchain.js +16 -0
- package/dist/core/text/types.js +1 -0
- package/dist/core/text/validate.js +149 -0
- package/dist/core/text/xml.js +58 -0
- package/dist/core/toolchain/datafetch.js +151 -0
- package/dist/core/toolchain/env.js +63 -0
- package/dist/core/toolchain/features.js +170 -0
- package/dist/core/toolchain/index.js +8 -0
- package/dist/core/toolchain/install.js +191 -0
- package/dist/core/toolchain/locate.js +275 -0
- package/dist/core/toolchain/remember.js +84 -0
- package/dist/core/toolchain/run.js +318 -0
- package/dist/core/toolchain/specs.js +351 -0
- package/dist/core/toolchain/types.js +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/userconfig.js +66 -0
- package/dist/core/validate/context.js +139 -0
- package/dist/core/validate/index.js +5 -0
- package/dist/core/validate/registry.js +58 -0
- package/dist/core/validate/run.js +131 -0
- package/dist/core/validate/suppressions.js +91 -0
- package/dist/core/validate/types.js +2 -0
- package/dist/core/yamledit.js +51 -0
- package/dist/index.js +389 -0
- package/dist/server.js +1914 -0
- package/dist/surfaces/baldursgate3/compat.js +164 -0
- package/dist/surfaces/baldursgate3/identity.js +110 -0
- package/dist/surfaces/baldursgate3/index/build.js +331 -0
- package/dist/surfaces/baldursgate3/index/index.js +17 -0
- package/dist/surfaces/baldursgate3/index/info.js +40 -0
- package/dist/surfaces/baldursgate3/index/parsers/loca.js +41 -0
- package/dist/surfaces/baldursgate3/index/parsers/lsx.js +137 -0
- package/dist/surfaces/baldursgate3/index/parsers/stats.js +45 -0
- package/dist/surfaces/baldursgate3/index/parsers/treasuretable.js +57 -0
- package/dist/surfaces/baldursgate3/index/parsers/xmlutil.js +142 -0
- package/dist/surfaces/baldursgate3/index/queries.js +327 -0
- package/dist/surfaces/baldursgate3/index/schema.js +62 -0
- package/dist/surfaces/baldursgate3/index.js +3 -0
- package/dist/surfaces/baldursgate3/logs.js +278 -0
- package/dist/surfaces/baldursgate3/modsettings.js +115 -0
- package/dist/surfaces/baldursgate3/pak.js +153 -0
- package/dist/surfaces/baldursgate3/settings.js +49 -0
- package/dist/surfaces/baldursgate3/surface.js +232 -0
- package/dist/surfaces/baldursgate3/validators/bridge.js +7 -0
- package/dist/surfaces/baldursgate3/validators/files.js +476 -0
- package/dist/surfaces/baldursgate3/validators/items.js +1001 -0
- package/dist/surfaces/baldursgate3/validators/progression.js +734 -0
- package/dist/surfaces/baldursgate3/validators/stats.js +1076 -0
- package/dist/surfaces/baldursgate3/validators/story.js +790 -0
- package/dist/surfaces/baldursgate3/validators/templates.js +7 -0
- package/dist/surfaces/cyberpunk2077/compat.js +223 -0
- package/dist/surfaces/cyberpunk2077/index/build.js +231 -0
- package/dist/surfaces/cyberpunk2077/index/index.js +17 -0
- package/dist/surfaces/cyberpunk2077/index/info.js +51 -0
- package/dist/surfaces/cyberpunk2077/index/model.js +52 -0
- package/dist/surfaces/cyberpunk2077/index/parsers/tweak.js +483 -0
- package/dist/surfaces/cyberpunk2077/index/parsers/tweakxl-yaml.js +183 -0
- package/dist/surfaces/cyberpunk2077/index/queries.js +504 -0
- package/dist/surfaces/cyberpunk2077/index/resource.js +135 -0
- package/dist/surfaces/cyberpunk2077/index/schema.js +90 -0
- package/dist/surfaces/cyberpunk2077/index.js +3 -0
- package/dist/surfaces/cyberpunk2077/logs.js +742 -0
- package/dist/surfaces/cyberpunk2077/paths.js +12 -0
- package/dist/surfaces/cyberpunk2077/surface.js +218 -0
- package/dist/surfaces/cyberpunk2077/validators/bridge.js +7 -0
- package/dist/surfaces/cyberpunk2077/validators/cr2w.js +349 -0
- package/dist/surfaces/cyberpunk2077/validators/garments.js +1181 -0
- package/dist/surfaces/cyberpunk2077/validators/mesh.js +338 -0
- package/dist/surfaces/cyberpunk2077/validators/modsettings.js +120 -0
- package/dist/surfaces/cyberpunk2077/validators/packaging.js +463 -0
- package/dist/surfaces/cyberpunk2077/validators/red4ext.js +318 -0
- package/dist/surfaces/cyberpunk2077/validators/redscript.js +193 -0
- package/dist/surfaces/cyberpunk2077/validators/templates.js +7 -0
- package/dist/surfaces/cyberpunk2077/validators/tweaks.js +768 -0
- package/dist/surfaces/cyberpunk2077/validators/vehicles.js +531 -0
- package/dist/surfaces/cyberpunk2077/validators/xl.js +308 -0
- package/dist/surfaces/eldenring/binders.js +111 -0
- package/dist/surfaces/eldenring/compat.js +166 -0
- package/dist/surfaces/eldenring/dcx.js +49 -0
- package/dist/surfaces/eldenring/index.js +13 -0
- package/dist/surfaces/eldenring/loaders.js +132 -0
- package/dist/surfaces/eldenring/logs.js +67 -0
- package/dist/surfaces/eldenring/paramdef.js +256 -0
- package/dist/surfaces/eldenring/params.js +129 -0
- package/dist/surfaces/eldenring/surface.js +121 -0
- package/dist/surfaces/eldenring/tools.js +52 -0
- package/dist/surfaces/eldenring/validators/binders.js +193 -0
- package/dist/surfaces/eldenring/validators/packages.js +159 -0
- package/dist/surfaces/eldenring/validators/params.js +334 -0
- package/dist/surfaces/eldenring/validators/profiles.js +287 -0
- package/dist/surfaces/index.js +34 -0
- package/dist/surfaces/nivalisnights/compat.js +229 -0
- package/dist/surfaces/nivalisnights/index/build.js +310 -0
- package/dist/surfaces/nivalisnights/index/index.js +5 -0
- package/dist/surfaces/nivalisnights/index/queries.js +231 -0
- package/dist/surfaces/nivalisnights/index.js +4 -0
- package/dist/surfaces/nivalisnights/logs.js +473 -0
- package/dist/surfaces/nivalisnights/surface.js +195 -0
- package/dist/surfaces/skyrimse/archives.js +229 -0
- package/dist/surfaces/skyrimse/compat.js +163 -0
- package/dist/surfaces/skyrimse/index.js +12 -0
- package/dist/surfaces/skyrimse/loadorder.js +174 -0
- package/dist/surfaces/skyrimse/logs.js +241 -0
- package/dist/surfaces/skyrimse/plugins.js +149 -0
- package/dist/surfaces/skyrimse/records.js +94 -0
- package/dist/surfaces/skyrimse/surface.js +240 -0
- package/dist/surfaces/skyrimse/validators/archive-records.js +165 -0
- package/dist/surfaces/skyrimse/validators/archives.js +110 -0
- package/dist/surfaces/skyrimse/validators/plugins.js +374 -0
- package/dist/surfaces/skyrimse/validators/records.js +117 -0
- package/dist/surfaces/stardewvalley/compat.js +190 -0
- package/dist/surfaces/stardewvalley/config.js +42 -0
- package/dist/surfaces/stardewvalley/contentpatcher.js +203 -0
- package/dist/surfaces/stardewvalley/index/index.js +229 -0
- package/dist/surfaces/stardewvalley/index/wiki.js +190 -0
- package/dist/surfaces/stardewvalley/index.js +12 -0
- package/dist/surfaces/stardewvalley/logs.js +307 -0
- package/dist/surfaces/stardewvalley/mods.js +182 -0
- package/dist/surfaces/stardewvalley/surface.js +244 -0
- package/dist/surfaces/stardewvalley/validators/content.js +321 -0
- package/dist/surfaces/stardewvalley/validators/fields.js +147 -0
- package/dist/surfaces/stardewvalley/validators/loadorder.js +69 -0
- package/dist/surfaces/stardewvalley/validators/manifest.js +221 -0
- package/dist/surfaces/subnautica2/compat.js +138 -0
- package/dist/surfaces/subnautica2/index.js +5 -0
- package/dist/surfaces/subnautica2/logs.js +129 -0
- package/dist/surfaces/subnautica2/paks.js +173 -0
- package/dist/surfaces/subnautica2/surface.js +151 -0
- package/dist/surfaces/subnautica2/validators/packaging.js +298 -0
- package/dist/surfaces/valheim/compat.js +171 -0
- package/dist/surfaces/valheim/index/index.js +301 -0
- package/dist/surfaces/valheim/index/tables.js +103 -0
- package/dist/surfaces/valheim/index.js +11 -0
- package/dist/surfaces/valheim/logs.js +197 -0
- package/dist/surfaces/valheim/packaging.js +131 -0
- package/dist/surfaces/valheim/plugins.js +270 -0
- package/dist/surfaces/valheim/references.js +121 -0
- package/dist/surfaces/valheim/surface.js +223 -0
- package/dist/surfaces/valheim/validators/packaging.js +218 -0
- package/dist/surfaces/valheim/validators/plugins.js +287 -0
- package/dist/surfaces/valheim/validators/references.js +169 -0
- package/knowledge/baldursgate3/animations.gr2.yaml +166 -0
- package/knowledge/baldursgate3/audio.voice.yaml +182 -0
- package/knowledge/baldursgate3/audio.wwise.yaml +104 -0
- package/knowledge/baldursgate3/charactercreation.heads-hair.yaml +193 -0
- package/knowledge/baldursgate3/communitylibrary.shared-content.yaml +129 -0
- package/knowledge/baldursgate3/compatibilityframework.api.yaml +176 -0
- package/knowledge/baldursgate3/crash.causes.yaml +90 -0
- package/knowledge/baldursgate3/dialogue.dialogbank.yaml +120 -0
- package/knowledge/baldursgate3/dialogue.files.yaml +236 -0
- package/knowledge/baldursgate3/dialogue.osiris-goals.yaml +386 -0
- package/knowledge/baldursgate3/dialogue.yaml +122 -0
- package/knowledge/baldursgate3/feats.classes.yaml +164 -0
- package/knowledge/baldursgate3/items.cloth-physics.yaml +167 -0
- package/knowledge/baldursgate3/items.roottemplates.yaml +320 -0
- package/knowledge/baldursgate3/items.visualbank.yaml +628 -0
- package/knowledge/baldursgate3/journal.quests.yaml +214 -0
- package/knowledge/baldursgate3/loadorder.modsettings.yaml +128 -0
- package/knowledge/baldursgate3/localization.handles.yaml +92 -0
- package/knowledge/baldursgate3/misc.encoding-and-practice.yaml +111 -0
- package/knowledge/baldursgate3/origins.companions.yaml +144 -0
- package/knowledge/baldursgate3/osiris.signatures.yaml +211 -0
- package/knowledge/baldursgate3/packaging.meta.yaml +155 -0
- package/knowledge/baldursgate3/packaging.paks.yaml +79 -0
- package/knowledge/baldursgate3/process.launch.yaml +34 -0
- package/knowledge/baldursgate3/progressions.yaml +92 -0
- package/knowledge/baldursgate3/publishing.modio-nexus.yaml +170 -0
- package/knowledge/baldursgate3/publishing.toolkit-modio.yaml +135 -0
- package/knowledge/baldursgate3/races.progression.yaml +228 -0
- package/knowledge/baldursgate3/scriptextender.console.yaml +257 -0
- package/knowledge/baldursgate3/scriptextender.yaml +252 -0
- package/knowledge/baldursgate3/stats.boosts.yaml +293 -0
- package/knowledge/baldursgate3/stats.equipment-kits.yaml +69 -0
- package/knowledge/baldursgate3/stats.interrupts.yaml +45 -0
- package/knowledge/baldursgate3/stats.itemcombos.yaml +65 -0
- package/knowledge/baldursgate3/stats.spells.yaml +293 -0
- package/knowledge/baldursgate3/stats.weapons.yaml +210 -0
- package/knowledge/baldursgate3/toolchain.divine.yaml +179 -0
- package/knowledge/baldursgate3/toolchain.lslib-formats.yaml +242 -0
- package/knowledge/baldursgate3/toolkit.levels.yaml +177 -0
- package/knowledge/baldursgate3/ui.icons-atlas.yaml +78 -0
- package/knowledge/baldursgate3/ui.icons.yaml +412 -0
- package/knowledge/baldursgate3/ui.mcm.yaml +161 -0
- package/knowledge/baldursgate3/vfx.materials.yaml +174 -0
- package/knowledge/baldursgate3/visuals.characterfix.yaml +73 -0
- package/knowledge/cyberpunk2077/animations.anims.yaml +118 -0
- package/knowledge/cyberpunk2077/anims.archivexl.yaml +163 -0
- package/knowledge/cyberpunk2077/anims.authoring.yaml +579 -0
- package/knowledge/cyberpunk2077/anims.encoder.yaml +156 -0
- package/knowledge/cyberpunk2077/anims.graph.yaml +629 -0
- package/knowledge/cyberpunk2077/anims.method.yaml +360 -0
- package/knowledge/cyberpunk2077/anims.pipeline.yaml +352 -0
- package/knowledge/cyberpunk2077/anims.roundtrip.yaml +222 -0
- package/knowledge/cyberpunk2077/anims.sets.yaml +176 -0
- package/knowledge/cyberpunk2077/archivexl.bodytypes.yaml +149 -0
- package/knowledge/cyberpunk2077/archivexl.dynamic-appearances.yaml +218 -0
- package/knowledge/cyberpunk2077/archivexl.manifest.yaml +214 -0
- package/knowledge/cyberpunk2077/audio.events.yaml +168 -0
- package/knowledge/cyberpunk2077/audio.sounds.yaml +164 -0
- package/knowledge/cyberpunk2077/audio.voicesets.yaml +146 -0
- package/knowledge/cyberpunk2077/cet.sandbox.yaml +219 -0
- package/knowledge/cyberpunk2077/clothing.garments.yaml +283 -0
- package/knowledge/cyberpunk2077/clothing.refits.yaml +215 -0
- package/knowledge/cyberpunk2077/codeware.overview.yaml +111 -0
- package/knowledge/cyberpunk2077/codeware.systems.yaml +192 -0
- package/knowledge/cyberpunk2077/drones.combat.yaml +416 -0
- package/knowledge/cyberpunk2077/drones.control.yaml +552 -0
- package/knowledge/cyberpunk2077/entities.appearance.yaml +166 -0
- package/knowledge/cyberpunk2077/equipmentex.slots.yaml +151 -0
- package/knowledge/cyberpunk2077/fx.effects.yaml +246 -0
- package/knowledge/cyberpunk2077/fx.lights.yaml +171 -0
- package/knowledge/cyberpunk2077/hair.modding.yaml +145 -0
- package/knowledge/cyberpunk2077/items.wiring.yaml +246 -0
- package/knowledge/cyberpunk2077/loadorder.archives.yaml +71 -0
- package/knowledge/cyberpunk2077/mesh.blender.yaml +209 -0
- package/knowledge/cyberpunk2077/nativedb.dump.yaml +98 -0
- package/knowledge/cyberpunk2077/npc.appearance.yaml +275 -0
- package/knowledge/cyberpunk2077/npc.behaviour.yaml +128 -0
- package/knowledge/cyberpunk2077/npc.locomotion.yaml +490 -0
- package/knowledge/cyberpunk2077/packaging.deploy.yaml +175 -0
- package/knowledge/cyberpunk2077/packaging.variants.yaml +176 -0
- package/knowledge/cyberpunk2077/player.control.yaml +918 -0
- package/knowledge/cyberpunk2077/process.assetbuild.yaml +154 -0
- package/knowledge/cyberpunk2077/process.lessons.yaml +276 -0
- package/knowledge/cyberpunk2077/quests.minor.yaml +46 -0
- package/knowledge/cyberpunk2077/quests.scenes.yaml +282 -0
- package/knowledge/cyberpunk2077/quickhacks.tiers.yaml +41 -0
- package/knowledge/cyberpunk2077/red4ext.api.yaml +645 -0
- package/knowledge/cyberpunk2077/red4ext.plugins.yaml +124 -0
- package/knowledge/cyberpunk2077/redhottools.hotreload.yaml +245 -0
- package/knowledge/cyberpunk2077/redscript.compile-log.yaml +76 -0
- package/knowledge/cyberpunk2077/redscript.modules.yaml +118 -0
- package/knowledge/cyberpunk2077/toolchain.wolvenkit.yaml +163 -0
- package/knowledge/cyberpunk2077/tweakdb.loot-recipes.yaml +184 -0
- package/knowledge/cyberpunk2077/tweakdb.melee.yaml +352 -0
- package/knowledge/cyberpunk2077/tweakdb.weapons.yaml +325 -0
- package/knowledge/cyberpunk2077/tweakxl.hotreload.yaml +151 -0
- package/knowledge/cyberpunk2077/tweakxl.yaml-syntax.yaml +208 -0
- package/knowledge/cyberpunk2077/ui.icons.yaml +259 -0
- package/knowledge/cyberpunk2077/ui.inkatlas.yaml +81 -0
- package/knowledge/cyberpunk2077/ui.modsettings.yaml +204 -0
- package/knowledge/cyberpunk2077/vehicles.control.yaml +475 -0
- package/knowledge/cyberpunk2077/vehicles.dashboard-ui.yaml +203 -0
- package/knowledge/cyberpunk2077/vehicles.entities.yaml +195 -0
- package/knowledge/cyberpunk2077/vehicles.wiring.yaml +137 -0
- package/knowledge/cyberpunk2077/wolvenkit.cli.yaml +167 -0
- package/knowledge/cyberpunk2077/workspot.system.yaml +55 -0
- package/knowledge/cyberpunk2077/world.interactions.yaml +41 -0
- package/knowledge/cyberpunk2077/world.loot-container.yaml +167 -0
- package/knowledge/cyberpunk2077/world.sectors.yaml +233 -0
- package/knowledge/eldenring/formats.binders.yaml +237 -0
- package/knowledge/eldenring/launcher.me3.yaml +230 -0
- package/knowledge/eldenring/launcher.modengine2.yaml +119 -0
- package/knowledge/eldenring/params.paramdef.yaml +149 -0
- package/knowledge/eldenring/params.regulation.yaml +142 -0
- package/knowledge/eldenring/saves.online.yaml +118 -0
- package/knowledge/eldenring/toolchain.cli.yaml +256 -0
- package/knowledge/nivalisnights/modding.runtime.yaml +157 -0
- package/knowledge/skyrimse/archives.bsa.yaml +212 -0
- package/knowledge/skyrimse/install.detection.yaml +41 -0
- package/knowledge/skyrimse/loadorder.files.yaml +242 -0
- package/knowledge/skyrimse/plugins.esl.yaml +189 -0
- package/knowledge/skyrimse/plugins.format.yaml +224 -0
- package/knowledge/skyrimse/skse.runtime.yaml +283 -0
- package/knowledge/skyrimse/toolchain.xedit-spriggit.yaml +336 -0
- package/knowledge/stardewvalley/contentpatcher.format.yaml +338 -0
- package/knowledge/stardewvalley/distribution.channels.yaml +236 -0
- package/knowledge/stardewvalley/game.versions.yaml +202 -0
- package/knowledge/stardewvalley/install.layout.yaml +277 -0
- package/knowledge/stardewvalley/smapi.loader.yaml +284 -0
- package/knowledge/stardewvalley/smapi.logs.yaml +329 -0
- package/knowledge/stardewvalley/smapi.manifest.yaml +293 -0
- package/knowledge/stardewvalley/toolchain.build.yaml +261 -0
- package/knowledge/stardewvalley/wiki.dataformat.yaml +130 -0
- package/knowledge/subnautica2/game.build.yaml +331 -0
- package/knowledge/subnautica2/modding.state.yaml +210 -0
- package/knowledge/subnautica2/ue5.containers.yaml +72 -0
- package/knowledge/subnautica2/ue5.paks.yaml +203 -0
- package/knowledge/subnautica2/ue5.tooling.yaml +175 -0
- package/knowledge/subnautica2/ue5.ue4ss.yaml +203 -0
- package/knowledge/valheim/bepinex.loader.yaml +247 -0
- package/knowledge/valheim/bepinex.plugins.yaml +251 -0
- package/knowledge/valheim/game.versions.yaml +321 -0
- package/knowledge/valheim/install.layout.yaml +216 -0
- package/knowledge/valheim/jotunn.library.yaml +334 -0
- package/knowledge/valheim/thunderstore.packaging.yaml +217 -0
- package/knowledge/valheim/toolchain.build.yaml +111 -0
- package/package.json +29 -0
- package/scaffolds/baldursgate3/new-class-injection.yaml +148 -0
- package/scaffolds/baldursgate3/new-item.yaml +264 -0
- package/scaffolds/baldursgate3/new-mod.yaml +174 -0
- package/scaffolds/baldursgate3/new-passive.yaml +100 -0
- package/scaffolds/baldursgate3/new-project.yaml +118 -0
- package/scaffolds/baldursgate3/new-quest-stub.yaml +159 -0
- package/scaffolds/baldursgate3/new-spell.yaml +102 -0
- package/scaffolds/baldursgate3/new-status.yaml +119 -0
- package/scaffolds/baldursgate3/sequences/subclass-kit.yaml +50 -0
- package/scaffolds/cyberpunk2077/edit-tweak.yaml +70 -0
- package/scaffolds/cyberpunk2077/mesh-export-preset.yaml +170 -0
- package/scaffolds/cyberpunk2077/new-entity-patch.yaml +255 -0
- package/scaffolds/cyberpunk2077/new-garment-refit.yaml +279 -0
- package/scaffolds/cyberpunk2077/new-item-chain.yaml +548 -0
- package/scaffolds/cyberpunk2077/new-localization.yaml +130 -0
- package/scaffolds/cyberpunk2077/new-mod-settings.yaml +133 -0
- package/scaffolds/cyberpunk2077/new-player-replacer.yaml +1003 -0
- package/scaffolds/cyberpunk2077/new-project.yaml +157 -0
- package/scaffolds/cyberpunk2077/new-sector-node.yaml +156 -0
- package/scaffolds/cyberpunk2077/new-tweak.yaml +121 -0
- package/scaffolds/cyberpunk2077/new-vehicle-livery.yaml +148 -0
- package/scaffolds/cyberpunk2077/new-workspot-entity.yaml +177 -0
- package/scaffolds/cyberpunk2077/sequences/iconic-weapon.yaml +72 -0
- package/scaffolds/cyberpunk2077/sequences/quickhack-takeover.yaml +213 -0
- package/scaffolds/stardewvalley/new-cp-patch.yaml +106 -0
- package/scaffolds/stardewvalley/new-csharp-mod.yaml +195 -0
- package/scaffolds/stardewvalley/new-project.yaml +184 -0
- package/scaffolds/stardewvalley/sequences/content-pack.yaml +31 -0
- package/scripts/mw-tool.mjs +184 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mw-ship
|
|
3
|
+
description: Build, verify the listing, deploy and pre-flight this mod for release.
|
|
4
|
+
model: sonnet
|
|
5
|
+
effort: high
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- modwright:asset modwright-ship@${version} -->
|
|
9
|
+
|
|
10
|
+
Take `${project}` from source to a deployed, checked build. Every step here is dry-run
|
|
11
|
+
first; every apply step is the user's decision, not yours.
|
|
12
|
+
|
|
13
|
+
## 1. Validate
|
|
14
|
+
|
|
15
|
+
`validate` with `includeInfo: true`. `blocking: true` or `counts.block > 0` stops the
|
|
16
|
+
run — explain the findings (see the `mw-validate` skill for how to read `evidence`) and
|
|
17
|
+
hand back. Read `skipped` too: a rule that could not run has not passed.
|
|
18
|
+
|
|
19
|
+
## 2. Build
|
|
20
|
+
|
|
21
|
+
`build` with `mode: "dry-run"` resolves and describes every step without running a tool.
|
|
22
|
+
Check the step list, the `variant` if one applies, and the resolved `version`. Then
|
|
23
|
+
`mode: "apply"`.
|
|
24
|
+
|
|
25
|
+
The cheap validators run again before the first pack step and refuse the build on any
|
|
26
|
+
block. Do not reach for `noValidate` — if a block is wrong, it is a rule to fix or
|
|
27
|
+
suppress with a reason, not a gate to skip. On failure the stage tree is kept: say where
|
|
28
|
+
it is.
|
|
29
|
+
|
|
30
|
+
## 3. Deploy
|
|
31
|
+
|
|
32
|
+
`deploy` with `mode: "dry-run"` first. It resolves every target, refuses an artifact this
|
|
33
|
+
build did not produce or one older than its sources, and reports what it would copy,
|
|
34
|
+
prune and back up. Note `deployMode` (`pak` or `loose`) and, for a pak deploy blocked by
|
|
35
|
+
an existing junction, that `unlink` removes the link only.
|
|
36
|
+
|
|
37
|
+
Then `mode: "apply"` once the user confirms. Report `deferred` targets (the game
|
|
38
|
+
still held the file), the reload recommendation the result carries, and where the backup
|
|
39
|
+
went.
|
|
40
|
+
|
|
41
|
+
## 4. Pre-flight
|
|
42
|
+
|
|
43
|
+
- `reload_plan` on the deployed paths if the deploy result's recommendation needs
|
|
44
|
+
restating, and tell the user what to relaunch.
|
|
45
|
+
- `run_tests` (or the `mw-verify` skill) for anything the release claims to fix.
|
|
46
|
+
- `rollback` with no `backupId` lists the backups this project has, newest stamp last —
|
|
47
|
+
name the one this deploy wrote so the user can undo it. Restoring is
|
|
48
|
+
`rollback` with that `backupId` and `mode: "apply"`.
|
|
49
|
+
|
|
50
|
+
ModWright does not publish. Summarise for the user what was built, what was
|
|
51
|
+
deployed where, what is still on the manual checklist, and the version to tag.
|
|
52
|
+
|
|
53
|
+
## Stop and ask
|
|
54
|
+
|
|
55
|
+
Stop before every apply. Stop on any block finding, any failed build step, any deferred
|
|
56
|
+
deploy target, and before `unlink`, `noValidate` or a rollback — each of those either
|
|
57
|
+
changes the game install or drops a safety check.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mw-validate
|
|
3
|
+
description: Run ModWright's validators over this mod and explain every finding.
|
|
4
|
+
argument-hint: [--changed] [--rule <id>]
|
|
5
|
+
model: sonnet
|
|
6
|
+
effort: medium
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!-- modwright:asset modwright-validate@${version} -->
|
|
10
|
+
|
|
11
|
+
Run ModWright's `validate` tool over this project (`${project}`, game `${game}`) and
|
|
12
|
+
explain what it reports. You are the explainer, not the fixer: nothing is edited until
|
|
13
|
+
the user says so.
|
|
14
|
+
|
|
15
|
+
## Call it
|
|
16
|
+
|
|
17
|
+
`validate` takes `path` (a `modwright.json` or a directory to search up from — omit for
|
|
18
|
+
the repo root), `groups`, `cheapOnly`, `changedFiles`, `includeInfo` and `installPath`.
|
|
19
|
+
|
|
20
|
+
- No argument: `validate` with `includeInfo: true` for the first pass of a session.
|
|
21
|
+
- `--changed`: pass the absolute paths of the files edited so far as `changedFiles`;
|
|
22
|
+
file rules then run only on those. It is a filter, not a faster mode.
|
|
23
|
+
- `--rule <id>`: `validate` has **no** per-rule input. Pass the rule's group in `groups`
|
|
24
|
+
when you know it, otherwise run unfiltered and report only the findings whose
|
|
25
|
+
`validatorId` matches. Say which of the two you did.
|
|
26
|
+
- `cheapOnly: true` is the on-every-edit subset — use it while iterating, never as the
|
|
27
|
+
final word before a build.
|
|
28
|
+
|
|
29
|
+
## Read the report
|
|
30
|
+
|
|
31
|
+
`counts`, `blocking` and `suppressed` are computed over unsuppressed findings only.
|
|
32
|
+
`blocking: false` with `counts.block === 0` is the clean result; `findings: []` alone is
|
|
33
|
+
not, because a rule that could not run is in `skipped`, not in `findings`.
|
|
34
|
+
|
|
35
|
+
- Walk `skipped` first. Each entry names its reason — no index (`build_index`), a tool
|
|
36
|
+
that was not found (`check_toolchain`), no install. Report those as unknowns, never as
|
|
37
|
+
passes.
|
|
38
|
+
- For each finding, quote `file`, `line`, `message` and `fix` as the tool gave them.
|
|
39
|
+
- Every finding carries `evidence`: knowledge fact ids. **Do not explain a rule from
|
|
40
|
+
memory.** For each id call `lookup` with `game: "${game}"`, `kind: "facts"`,
|
|
41
|
+
`query: { text: "<fact id>", full: "1" }`, and report the fact's `status`
|
|
42
|
+
(verified / unverified / contradicted / community / inferred) alongside the claim. An
|
|
43
|
+
`unverified` or `contradicted` fact behind a finding is worth saying out loud.
|
|
44
|
+
- A finding with `suppressedBy` is already a recorded decision. Show it and move on.
|
|
45
|
+
|
|
46
|
+
## Stop and ask
|
|
47
|
+
|
|
48
|
+
Stop and hand back to the user before you: edit any file to fix a finding; add or
|
|
49
|
+
widen an entry in `.modwright/suppressions.json` (a suppression needs their reason, in
|
|
50
|
+
their words, not yours); or conclude a rule is a false positive. Propose the change,
|
|
51
|
+
name the finding it answers, and wait.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mw-verify
|
|
3
|
+
description: Run this mod's test plan against the in-game bridge and record the result.
|
|
4
|
+
argument-hint: [plan] [--rows <ids>]
|
|
5
|
+
model: sonnet
|
|
6
|
+
effort: high
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!-- modwright:asset modwright-verify@${version} -->
|
|
10
|
+
|
|
11
|
+
Verify `${project}` in the running game. ModWright never launches the game, never sends a
|
|
12
|
+
reload command, and never decides on the user's behalf that something is verified.
|
|
13
|
+
|
|
14
|
+
## 1. Is what is deployed the thing you are testing?
|
|
15
|
+
|
|
16
|
+
If a deploy happened this session, pass its destination paths to `reload_plan` as
|
|
17
|
+
`paths` (an array, at least one). It answers `hot`, `hot-degraded`, `save-reload`,
|
|
18
|
+
`restart` or `unknown` ("restart to be sure"). Tell the user what the answer
|
|
19
|
+
requires *before* they load a save — a stale build is the usual mystery failure.
|
|
20
|
+
|
|
21
|
+
## 2. Bridge
|
|
22
|
+
|
|
23
|
+
`bridge` with `action: "status"` and `game: "${game}"` (or `path`) reports heartbeat
|
|
24
|
+
freshness and whether `protocolVersion` matches. If the bridge is not staged in this
|
|
25
|
+
project, `action: "stage"` copies it in (`mode: "dry-run"` first, then `"apply"`), and it
|
|
26
|
+
reaches the game through `deploy --variant withBridge`.
|
|
27
|
+
|
|
28
|
+
A stale or missing heartbeat means the game is not running with the bridge armed. Say so
|
|
29
|
+
and stop; do not run the plan against a bridge that cannot answer.
|
|
30
|
+
|
|
31
|
+
## 3. Run the plan
|
|
32
|
+
|
|
33
|
+
`run_tests` takes `plan` (a planId from a plan's own header, or a path to its YAML —
|
|
34
|
+
usually `tests/authored.yaml`), `rows` (the `--rows` ids, omit for all), `gates`, `mode`,
|
|
35
|
+
`windowSeconds`, `allowMutate` and `timeoutSeconds`.
|
|
36
|
+
|
|
37
|
+
- Always `mode: "dry-run"` first: it shows the combined bridge request and the full
|
|
38
|
+
checklist without arming anything.
|
|
39
|
+
- Then `mode: "apply"`. Rows with a probe go to the bridge in one request; rows without
|
|
40
|
+
one are checklist items for the user to answer.
|
|
41
|
+
- `allowMutate: true` lets mutating probes run. Ask first, every time.
|
|
42
|
+
- A row on an `unverified` probe kind that passes is capped below `verified` on purpose —
|
|
43
|
+
report that cap, do not round it up.
|
|
44
|
+
|
|
45
|
+
## 4. Record
|
|
46
|
+
|
|
47
|
+
`run_tests` on apply writes observed/status/evidence back into the plan YAML and, for a
|
|
48
|
+
row naming a ledger `claim`, records verified/contradicted against that claim itself —
|
|
49
|
+
do not re-record it. Read it back with `ledger` `action: "list"`/`"show"`, and
|
|
50
|
+
`action: "check"` to confirm every verified claim's evidence still exists on disk.
|
|
51
|
+
|
|
52
|
+
Reach for `ledger` `action: "record"` (`mode: "apply"`) only for a manual corroboration
|
|
53
|
+
the user dictates — and nothing reaches verified without an evidence block.
|
|
54
|
+
|
|
55
|
+
## Stop and ask
|
|
56
|
+
|
|
57
|
+
Stop if the bridge heartbeat is stale, if a manual row needs an in-game observation, if a
|
|
58
|
+
probe times out (report which of the named timeout outcomes it was), or if a row failed.
|
|
59
|
+
A failed row is a finding to hand back, not something to re-run until it passes.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Decode Nivalis Nights `Resources/` definition assets to JSONL, for ModWright's vanilla index.
|
|
2
|
+
|
|
3
|
+
Run by the nivalisnights index builder (src/surfaces/nivalisnights/index/build.ts) through
|
|
4
|
+
the `unitypy-python` tool: a Python with UnityPy and TypeTreeGeneratorAPI installed. Reads
|
|
5
|
+
only the user's own install; writes only under --out. The notes below record why each
|
|
6
|
+
step is shaped as it is:
|
|
7
|
+
|
|
8
|
+
- The build strips type trees, so MonoBehaviour fields are decoded with type trees
|
|
9
|
+
generated from Cpp2IL dummy DLLs made with the attributeanalyzer + attributeinjector
|
|
10
|
+
processors (TypeTreeGeneratorAPI's own IL2CPP loader fails on this build).
|
|
11
|
+
- The generated base nodes miss the align after m_Enabled, so m_Script is read from the
|
|
12
|
+
raw bytes (FileID int32 at 16, PathID int64 at 20) and the shifted base fields are dropped.
|
|
13
|
+
- MonoScripts live in globalgamemanagers.assets, which is always loaded.
|
|
14
|
+
- UnityPy loads whole files, so each folder loads only the files its entries live in.
|
|
15
|
+
|
|
16
|
+
Output, under --out:
|
|
17
|
+
<folder>.jsonl one record per decoded object: {"path", "folder", "key", "script", "data"}
|
|
18
|
+
where key is "<file>:<path_id>" and every PPtr inside data is either
|
|
19
|
+
null (path id 0) or {"$ref": "<file>:<path_id>"}.
|
|
20
|
+
summary.json {"unity", "folders": {folder: {"entries", "decoded", "scripts", "errors"}}}
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
python extract_resources.py --data "<root>/Nivalis Nights_Data" --dlls <dummydll dir>
|
|
24
|
+
--out <dir> --folders itemtypes,recipes [--follow peopledatabase]
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
import collections
|
|
29
|
+
import json
|
|
30
|
+
import os
|
|
31
|
+
import struct
|
|
32
|
+
import sys
|
|
33
|
+
|
|
34
|
+
import UnityPy
|
|
35
|
+
from UnityPy.helpers.TypeTreeGenerator import TypeTreeGenerator
|
|
36
|
+
|
|
37
|
+
UNITY_VERSION = "2020.3.44f1"
|
|
38
|
+
BASE_FIELDS = ("m_GameObject", "m_Enabled", "m_Script")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main() -> int:
|
|
42
|
+
ap = argparse.ArgumentParser()
|
|
43
|
+
ap.add_argument("--data", required=True)
|
|
44
|
+
ap.add_argument("--dlls", required=True)
|
|
45
|
+
ap.add_argument("--out", required=True)
|
|
46
|
+
ap.add_argument("--folders", required=True, help="comma-separated top-level Resources folders")
|
|
47
|
+
ap.add_argument("--follow", default="", help="folders whose referenced objects are decoded too, as <folder>.refs")
|
|
48
|
+
args = ap.parse_args()
|
|
49
|
+
|
|
50
|
+
UnityPy.config.FALLBACK_UNITY_VERSION = UNITY_VERSION
|
|
51
|
+
gen = TypeTreeGenerator(UNITY_VERSION)
|
|
52
|
+
gen.load_local_dll_folder(args.dlls)
|
|
53
|
+
os.makedirs(args.out, exist_ok=True)
|
|
54
|
+
|
|
55
|
+
ggm = next(iter(UnityPy.load(os.path.join(args.data, "globalgamemanagers")).files.values()))
|
|
56
|
+
rm = next(o for o in ggm.objects.values() if o.type.name == "ResourceManager").read()
|
|
57
|
+
|
|
58
|
+
def ext_name(pptr) -> str:
|
|
59
|
+
return os.path.basename(ggm.externals[pptr.file_id - 1].path if pptr.file_id else "globalgamemanagers").lower()
|
|
60
|
+
|
|
61
|
+
folders = [f.strip().lower() for f in args.folders.split(",") if f.strip()]
|
|
62
|
+
follow = {f.strip().lower() for f in args.follow.split(",") if f.strip()}
|
|
63
|
+
by_folder = collections.defaultdict(list)
|
|
64
|
+
for path, pptr in rm.m_Container:
|
|
65
|
+
top = path.split("/")[0]
|
|
66
|
+
if top in folders:
|
|
67
|
+
by_folder[top].append((path, pptr))
|
|
68
|
+
|
|
69
|
+
summary = {"unity": UNITY_VERSION, "folders": {}}
|
|
70
|
+
for folder in folders:
|
|
71
|
+
wanted = by_folder.get(folder, [])
|
|
72
|
+
needed = sorted({ext_name(pp) for _, pp in wanted} - {"globalgamemanagers", "globalgamemanagers.assets"})
|
|
73
|
+
env = UnityPy.load(os.path.join(args.data, "globalgamemanagers.assets"), *[os.path.join(args.data, n) for n in needed])
|
|
74
|
+
env.typetree_generator = gen
|
|
75
|
+
files = {os.path.basename(k).lower(): v for k, v in env.files.items()}
|
|
76
|
+
scripts_cache = {}
|
|
77
|
+
|
|
78
|
+
def script_of(obj) -> str:
|
|
79
|
+
sf = obj.assets_file
|
|
80
|
+
fid, pid = struct.unpack_from("<iq", obj.get_raw_data(), 16)
|
|
81
|
+
owner = os.path.basename(sf.externals[fid - 1].path).lower() if fid else os.path.basename(sf.name).lower()
|
|
82
|
+
if (owner, pid) not in scripts_cache:
|
|
83
|
+
ms = files[owner].objects[pid].read_typetree()
|
|
84
|
+
scripts_cache[(owner, pid)] = (ms["m_Namespace"] + "." if ms["m_Namespace"] else "") + ms["m_ClassName"]
|
|
85
|
+
return scripts_cache[(owner, pid)]
|
|
86
|
+
|
|
87
|
+
def decode(obj, path, out, stats, folder_name):
|
|
88
|
+
file_name = os.path.basename(obj.assets_file.name).lower()
|
|
89
|
+
externals = [os.path.basename(e.path).lower() for e in obj.assets_file.externals]
|
|
90
|
+
refs = []
|
|
91
|
+
|
|
92
|
+
def norm(v):
|
|
93
|
+
if isinstance(v, dict):
|
|
94
|
+
if set(v.keys()) == {"m_FileID", "m_PathID"}:
|
|
95
|
+
if not v["m_PathID"]:
|
|
96
|
+
return None
|
|
97
|
+
fid = v["m_FileID"]
|
|
98
|
+
target = file_name if fid == 0 else (externals[fid - 1] if 0 < fid <= len(externals) else f"?{fid}")
|
|
99
|
+
key = f"{target}:{v['m_PathID']}"
|
|
100
|
+
refs.append(key)
|
|
101
|
+
return {"$ref": key}
|
|
102
|
+
return {k: norm(x) for k, x in v.items()}
|
|
103
|
+
if isinstance(v, list):
|
|
104
|
+
return [norm(x) for x in v]
|
|
105
|
+
return v
|
|
106
|
+
|
|
107
|
+
if obj.type.name != "MonoBehaviour":
|
|
108
|
+
stats["errors"][f"not a MonoBehaviour ({obj.type.name})"] += 1
|
|
109
|
+
return refs
|
|
110
|
+
try:
|
|
111
|
+
tree = obj.read_typetree()
|
|
112
|
+
cls = script_of(obj)
|
|
113
|
+
except Exception as e: # one bad object never stops the folder
|
|
114
|
+
stats["errors"][f"{type(e).__name__}: {str(e)[:120]}"] += 1
|
|
115
|
+
return refs
|
|
116
|
+
data = norm({k: v for k, v in tree.items() if k not in BASE_FIELDS})
|
|
117
|
+
stats["scripts"][cls] += 1
|
|
118
|
+
stats["decoded"] += 1
|
|
119
|
+
record = {"path": path, "folder": folder_name, "key": f"{file_name}:{obj.path_id}", "script": cls, "data": data}
|
|
120
|
+
out.write(json.dumps(record, ensure_ascii=False, default=str) + "\n")
|
|
121
|
+
return refs
|
|
122
|
+
|
|
123
|
+
def new_stats(entries):
|
|
124
|
+
return {"entries": entries, "decoded": 0, "scripts": collections.Counter(), "errors": collections.Counter()}
|
|
125
|
+
|
|
126
|
+
stats = new_stats(len(wanted))
|
|
127
|
+
followed = set()
|
|
128
|
+
with open(os.path.join(args.out, f"{folder}.jsonl"), "w", encoding="utf-8") as out:
|
|
129
|
+
for path, pptr in wanted:
|
|
130
|
+
sf = files.get(ext_name(pptr))
|
|
131
|
+
obj = sf.objects.get(pptr.path_id) if sf else None
|
|
132
|
+
if obj is None:
|
|
133
|
+
stats["errors"]["entry's file not loaded or object missing"] += 1
|
|
134
|
+
continue
|
|
135
|
+
for ref in decode(obj, path, out, stats, folder):
|
|
136
|
+
followed.add(ref)
|
|
137
|
+
summary["folders"][folder] = {k: (dict(v) if isinstance(v, collections.Counter) else v) for k, v in stats.items()}
|
|
138
|
+
print(f"{folder}: {stats['decoded']}/{stats['entries']} decoded", flush=True)
|
|
139
|
+
|
|
140
|
+
if folder in follow:
|
|
141
|
+
# Two levels: the folder's references (PeopleDatabase -> the NPCs), then
|
|
142
|
+
# theirs (an NPC -> its demographic group, action list). Only MonoBehaviours
|
|
143
|
+
# are decoded; a reference to a prefab, sprite or mesh is counted, not an error.
|
|
144
|
+
seen = set()
|
|
145
|
+
frontier = followed
|
|
146
|
+
for suffix in ("refs", "refs2"):
|
|
147
|
+
name = f"{folder}.{suffix}"
|
|
148
|
+
todo = sorted(k for k in frontier if k not in seen)
|
|
149
|
+
seen.update(todo)
|
|
150
|
+
fstats = new_stats(0)
|
|
151
|
+
fstats["skipped_non_monobehaviour"] = 0
|
|
152
|
+
nxt = set()
|
|
153
|
+
with open(os.path.join(args.out, f"{name}.jsonl"), "w", encoding="utf-8") as out:
|
|
154
|
+
for key in todo:
|
|
155
|
+
fname, _, pid = key.rpartition(":")
|
|
156
|
+
sf = files.get(fname)
|
|
157
|
+
obj = sf.objects.get(int(pid)) if sf and pid.lstrip("-").isdigit() else None
|
|
158
|
+
if obj is None:
|
|
159
|
+
fstats["skipped_unloaded_file"] = fstats.get("skipped_unloaded_file", 0) + 1
|
|
160
|
+
continue
|
|
161
|
+
if obj.type.name != "MonoBehaviour":
|
|
162
|
+
fstats["skipped_non_monobehaviour"] += 1
|
|
163
|
+
continue
|
|
164
|
+
fstats["entries"] += 1
|
|
165
|
+
nxt.update(decode(obj, key, out, fstats, name))
|
|
166
|
+
summary["folders"][name] = {k: (dict(v) if isinstance(v, collections.Counter) else v) for k, v in fstats.items()}
|
|
167
|
+
print(f"{name}: {fstats['decoded']}/{fstats['entries']} decoded", flush=True)
|
|
168
|
+
frontier = nxt
|
|
169
|
+
del env
|
|
170
|
+
|
|
171
|
+
with open(os.path.join(args.out, "summary.json"), "w", encoding="utf-8") as f:
|
|
172
|
+
json.dump(summary, f, indent=1)
|
|
173
|
+
return 0
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__":
|
|
177
|
+
sys.exit(main())
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
-- MODWRIGHT-BRIDGE-DO-NOT-SHIP
|
|
2
|
+
--
|
|
3
|
+
-- ModWrightBridge — CLIENT bootstrap: heartbeat and client probes only.
|
|
4
|
+
--
|
|
5
|
+
-- It loads at the main menu, which is what makes the `bg3.client.loaded` row answerable
|
|
6
|
+
-- without a save ("main menu is enough; no save needed"), the same client-marker pattern a
|
|
7
|
+
-- shipped SE mod uses.
|
|
8
|
+
--
|
|
9
|
+
-- It deliberately does NOT poll for requests and does NOT write result files. API.md
|
|
10
|
+
-- §"Client / Server States": the client and server Lua states "are completely separated from
|
|
11
|
+
-- each other and cannot access the internal state of each other", and the protocol has
|
|
12
|
+
-- exactly one request file and one result file per request id, so two pollers writing the
|
|
13
|
+
-- same result would race. The server relays this state's answer out of the heartbeat instead
|
|
14
|
+
-- (Probes.lua, `bg3.client.loaded`).
|
|
15
|
+
--
|
|
16
|
+
-- ============================================================================
|
|
17
|
+
-- Ext API used in this file
|
|
18
|
+
-- ============================================================================
|
|
19
|
+
-- API.md §"Getting Started" / "Bootstrap Scripts": "the extender will attempt to load ...
|
|
20
|
+
-- `BootstrapClient.lua` on the client side"; `Ext.Require` at module startup only.
|
|
21
|
+
-- API.md §"Engine and SE Events": `SessionLoaded`, `ResetCompleted`.
|
|
22
|
+
-- API.md §"SE Events": `Ext.Events.<EVENT_NAME>:Subscribe(fun)`.
|
|
23
|
+
|
|
24
|
+
local Log = Ext.Require("ModWrightBridge/Log.lua")
|
|
25
|
+
local Protocol = Ext.Require("ModWrightBridge/Protocol.lua")
|
|
26
|
+
local ProbesClient = Ext.Require("ModWrightBridge/ProbesClient.lua")
|
|
27
|
+
|
|
28
|
+
-- Only the server state writes ModWright_log.json (the protocol has one breadcrumb file);
|
|
29
|
+
-- the client keeps its breadcrumbs on the SE console so the two states never race over it.
|
|
30
|
+
Log.mirrorToFile = false
|
|
31
|
+
|
|
32
|
+
Protocol.init({
|
|
33
|
+
context = "client",
|
|
34
|
+
dispatcher = ProbesClient,
|
|
35
|
+
heartbeatFile = Protocol.FILES.heartbeatClient,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
local function mark(stage)
|
|
39
|
+
ProbesClient.stage = stage
|
|
40
|
+
local res = ProbesClient.run("bg3.client.loaded")
|
|
41
|
+
pcall(function() Protocol.writeHeartbeat() end)
|
|
42
|
+
Log.info(("client %s (marker %s, heartbeat %s)")
|
|
43
|
+
:format(stage, tostring(res.status), Protocol.FILES.heartbeatClient))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
mark("bootstrap")
|
|
47
|
+
pcall(function() Protocol.startHeartbeat() end)
|
|
48
|
+
|
|
49
|
+
Ext.Events.SessionLoaded:Subscribe(function()
|
|
50
|
+
pcall(function() mark("SessionLoaded") end)
|
|
51
|
+
end)
|
|
52
|
+
|
|
53
|
+
Ext.Events.ResetCompleted:Subscribe(function()
|
|
54
|
+
pcall(function() mark("ResetCompleted") end)
|
|
55
|
+
end)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
-- MODWRIGHT-BRIDGE-DO-NOT-SHIP
|
|
2
|
+
--
|
|
3
|
+
-- ModWrightBridge — SERVER bootstrap. This is ModWright's author-only verification bridge,
|
|
4
|
+
-- built to the ModWright request/result protocol. It is never shipped in a mod:
|
|
5
|
+
-- `bg3.history.bridge-never-ships` (src/surfaces/baldursgate3/validators/bridge.ts) blocks a
|
|
6
|
+
-- build whose output carries this folder or the banner on the first line above.
|
|
7
|
+
--
|
|
8
|
+
-- WHAT THIS MOD DOES, IN ONE PARAGRAPH: it writes a heartbeat every 2 s while loaded, polls
|
|
9
|
+
-- for an armed request file every 1 s, runs the probes that request names, and writes a
|
|
10
|
+
-- result followed by a `done` marker carrying the result's byte length. It reads. It never
|
|
11
|
+
-- calls an Osiris action, never forces a cast, and writes no PersistentVars, ModVars or
|
|
12
|
+
-- UserVars (API.md §"Persistence": "PersistentVars are deprecated and should not be used").
|
|
13
|
+
--
|
|
14
|
+
-- ============================================================================
|
|
15
|
+
-- Registration shape and its citations
|
|
16
|
+
-- ============================================================================
|
|
17
|
+
-- API.md §"Getting Started" / "Bootstrap Scripts":
|
|
18
|
+
-- "the extender will attempt to load `BootstrapServer.lua` on the server side"; and
|
|
19
|
+
-- "The `Ext.Require` function is the extender's version of the Lua built-in `require`" —
|
|
20
|
+
-- "`Ext.Require` should only be called during module startup". Hence every require below
|
|
21
|
+
-- is at chunk scope, as a shipped SE mod does it.
|
|
22
|
+
-- API.md §"Engine and SE Events - `Ext.Events`":
|
|
23
|
+
-- `SessionLoaded` | "Thrown when the game session was set up."
|
|
24
|
+
-- `ResetCompleted` | "Thrown when `Ext.Debug.Reset()` or `reset` console command completes
|
|
25
|
+
-- on the client or server. Indicates that the Lua state was reloaded."
|
|
26
|
+
-- API.md §"SE Events":
|
|
27
|
+
-- `Ext.Events.<EVENT_NAME>:Subscribe(fun)`
|
|
28
|
+
--
|
|
29
|
+
-- ONE SessionLoaded subscription, every call inside it `pcall`-guarded: a shape proven in a
|
|
30
|
+
-- shipped SE mod. That pattern also explains why the poll timer exists at all: a mod that
|
|
31
|
+
-- reads its flag file INSIDE SessionLoaded never sees a file dropped after the session is
|
|
32
|
+
-- up, which is why the bridge polls.
|
|
33
|
+
--
|
|
34
|
+
-- ResetCompleted is subscribed for two reasons, both observed in game: a console `reset`
|
|
35
|
+
-- reloads the Lua state WITHOUT re-running the session (so SessionLoaded may never fire
|
|
36
|
+
-- again and the poll timer must be started from here too), and "`reset` mid-session degrades
|
|
37
|
+
-- Osiris ... Osi queries return junk and Osi calls silently no-op afterward" — which is what
|
|
38
|
+
-- `osirisDegraded` records and what probe `bg3.osiris.health` measures.
|
|
39
|
+
|
|
40
|
+
local Log = Ext.Require("ModWrightBridge/Log.lua")
|
|
41
|
+
local Protocol = Ext.Require("ModWrightBridge/Protocol.lua")
|
|
42
|
+
local Watch = Ext.Require("ModWrightBridge/Watch.lua")
|
|
43
|
+
local Probes = Ext.Require("ModWrightBridge/Probes.lua")
|
|
44
|
+
|
|
45
|
+
Protocol.init({
|
|
46
|
+
context = "server",
|
|
47
|
+
dispatcher = Probes,
|
|
48
|
+
heartbeatFile = Protocol.FILES.heartbeatServer,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
local seVersion = "unknown"
|
|
52
|
+
pcall(function() seVersion = tostring(Ext.Utils.Version()) end)
|
|
53
|
+
|
|
54
|
+
local probeKinds = 0
|
|
55
|
+
for _ in pairs(Probes.handlers) do probeKinds = probeKinds + 1 end
|
|
56
|
+
|
|
57
|
+
Log.info(("server bootstrap loaded (bridge %s, protocolVersion %d, SE v%s, %d probe kinds, Watch %s)")
|
|
58
|
+
:format(Protocol.BRIDGE_VERSION, Protocol.PROTOCOL_VERSION, seVersion, probeKinds, tostring(Watch ~= nil)))
|
|
59
|
+
|
|
60
|
+
-- The heartbeat starts at load, not at SessionLoaded: it is rewritten every 2 s while the
|
|
61
|
+
-- bridge is loaded, and `bridge status` needs one at the main menu to
|
|
62
|
+
-- tell "not deployed" from "deployed, game not in a session".
|
|
63
|
+
pcall(function() Protocol.startHeartbeat() end)
|
|
64
|
+
|
|
65
|
+
Ext.Events.SessionLoaded:Subscribe(function()
|
|
66
|
+
pcall(function() Protocol.startPolling() end)
|
|
67
|
+
pcall(function() Protocol.writeHeartbeat() end)
|
|
68
|
+
pcall(function()
|
|
69
|
+
Log.info("SessionLoaded: polling " .. Protocol.FILES.request ..
|
|
70
|
+
" every " .. Protocol.POLL_MS .. " ms; arm with `bridge arm` or by writing the request file by hand")
|
|
71
|
+
end)
|
|
72
|
+
end)
|
|
73
|
+
|
|
74
|
+
Ext.Events.ResetCompleted:Subscribe(function()
|
|
75
|
+
pcall(function() Protocol.markOsirisDegraded() end)
|
|
76
|
+
-- A `reset` clears the in-memory consumed-nonce set with the rest of the Lua state; that
|
|
77
|
+
-- is exactly why a request carries `armedUntil`, so a stale request cannot
|
|
78
|
+
-- silently execute a second time.
|
|
79
|
+
pcall(function() Protocol.startPolling() end)
|
|
80
|
+
pcall(function() Protocol.writeHeartbeat() end)
|
|
81
|
+
end)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
-- MODWRIGHT-BRIDGE-DO-NOT-SHIP
|
|
2
|
+
--
|
|
3
|
+
-- ModWrightBridge / Log.lua — console breadcrumbs with a rolling file mirror.
|
|
4
|
+
--
|
|
5
|
+
-- Follows the common SE mod logging pattern: every line is printed AND appended to a rolling
|
|
6
|
+
-- 250-entry JSON array in the SE storage dir, because the SE console often cannot be watched
|
|
7
|
+
-- live while the game runs. The breadcrumb file is `ModWright_log.json` with a 250-line cap,
|
|
8
|
+
-- and every `Ext.Utils.Print` carries the `[ModWrightBridge]` prefix.
|
|
9
|
+
--
|
|
10
|
+
-- This module deliberately has NO dependencies (not even Protocol) so that a failure anywhere
|
|
11
|
+
-- else can still be reported. It is also the ONE place that uses `Ext.Json.Stringify` on a
|
|
12
|
+
-- table of its own making: the buffer is never empty when it is written, so the empty-table
|
|
13
|
+
-- array/object ambiguity that forced Protocol's own encoder (see Protocol.lua) cannot bite
|
|
14
|
+
-- here, and nothing on the ModWright side parses this file — it is a human breadcrumb.
|
|
15
|
+
--
|
|
16
|
+
-- API.md §"Utils" / §"Helper/aliased functions":
|
|
17
|
+
-- `_P()`: Equivalent to `Ext.Utils.Print()`
|
|
18
|
+
-- API.md §"I/O":
|
|
19
|
+
-- `Ext.IO.SaveFile(path, content): boolean` -- Writes content to a file. Creates missing parent directories.
|
|
20
|
+
-- API.md §"JSON support - `Ext.Json`":
|
|
21
|
+
-- `Ext.Json.Stringify(value, [options])`
|
|
22
|
+
-- API.md §"Timers - `Ext.Timer`":
|
|
23
|
+
-- `Ext.Timer.MonotonicTime(): int64` -- monotonic system time in milliseconds
|
|
24
|
+
|
|
25
|
+
local Log = {}
|
|
26
|
+
|
|
27
|
+
Log.PREFIX = "[ModWrightBridge]"
|
|
28
|
+
Log.FILE = "ModWright_log.json"
|
|
29
|
+
Log.MAX_LINES = 250
|
|
30
|
+
|
|
31
|
+
-- Set false by BootstrapClient: the client Lua state must never write ModWright_log.json,
|
|
32
|
+
-- or the two states race each other over the same file (there is exactly one breadcrumb
|
|
33
|
+
-- file, written by the server).
|
|
34
|
+
Log.mirrorToFile = true
|
|
35
|
+
|
|
36
|
+
Log.buffer = {}
|
|
37
|
+
|
|
38
|
+
--- Monotonic milliseconds; total (never throws, never returns nil).
|
|
39
|
+
--- API.md §"Timers": `Ext.Timer.MonotonicTime(): int64`. Falls back through other clocks
|
|
40
|
+
--- when the call is unavailable on an older extender.
|
|
41
|
+
function Log.monotonic()
|
|
42
|
+
local ok, t = pcall(function() return Ext.Timer.MonotonicTime() end)
|
|
43
|
+
if ok then
|
|
44
|
+
local n = tonumber(t)
|
|
45
|
+
if n then return n end
|
|
46
|
+
end
|
|
47
|
+
local ok2, t2 = pcall(function() return Ext.Utils.MonotonicTime() end)
|
|
48
|
+
if ok2 then
|
|
49
|
+
local n2 = tonumber(t2)
|
|
50
|
+
if n2 then return n2 end
|
|
51
|
+
end
|
|
52
|
+
local ok3, c = pcall(function() return os.clock() end)
|
|
53
|
+
if ok3 and type(c) == "number" then return math.floor(c * 1000) end
|
|
54
|
+
return 0
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
local function mirror(line)
|
|
58
|
+
if not Log.mirrorToFile then return end
|
|
59
|
+
pcall(function()
|
|
60
|
+
local buf = Log.buffer
|
|
61
|
+
buf[#buf + 1] = { t = Log.monotonic(), line = line }
|
|
62
|
+
while #buf > Log.MAX_LINES do table.remove(buf, 1) end
|
|
63
|
+
Ext.IO.SaveFile(Log.FILE, Ext.Json.Stringify(buf))
|
|
64
|
+
end)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
--- Print one prefixed line and mirror it. Never throws.
|
|
68
|
+
function Log.write(msg)
|
|
69
|
+
local line = Log.PREFIX .. " " .. tostring(msg)
|
|
70
|
+
pcall(function() Ext.Utils.Print(line) end)
|
|
71
|
+
mirror(line)
|
|
72
|
+
return line
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
--- Ordinary breadcrumb.
|
|
76
|
+
function Log.info(msg)
|
|
77
|
+
return Log.write(msg)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
--- Error breadcrumb. Same transport; the word ERROR is what a reader greps for.
|
|
81
|
+
function Log.err(where, msg)
|
|
82
|
+
return Log.write("ERROR [" .. tostring(where) .. "] " .. tostring(msg))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
--- Wrap a callback so a raw error can never escape into an engine/Osiris callback.
|
|
86
|
+
--- The guarded-callback pattern from a shipped SE mod; its reason — a bad error path leaves
|
|
87
|
+
--- Osiris returning junk — applies to every listener and timer here.
|
|
88
|
+
function Log.guarded(where, fn)
|
|
89
|
+
return function(...)
|
|
90
|
+
local ok, err = pcall(fn, ...)
|
|
91
|
+
if not ok then Log.err(where, err) end
|
|
92
|
+
return ok
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
return Log
|