@gamecrate/cli 1.0.0 → 1.2.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/README.md +38 -224
- package/dist/gamecrate.js +3128 -1509
- package/dist/lib.js +66 -14
- package/dist/types/cli/args.d.ts +19 -1
- package/dist/types/cli/game.d.ts +2 -0
- package/dist/types/cli/list.d.ts +2 -0
- package/dist/types/cli/mods.d.ts +16 -0
- package/dist/types/cli/output.d.ts +23 -1
- package/dist/types/cli/profile.d.ts +6 -0
- package/dist/types/config/load.d.ts +5 -3
- package/dist/types/config/read.d.ts +11 -0
- package/dist/types/config/write.d.ts +14 -0
- package/dist/types/docker/run.d.ts +3 -1
- package/dist/types/docker/window.d.ts +30 -1
- package/dist/types/launch/prepare.d.ts +47 -4
- package/dist/types/launch/resolve.d.ts +8 -1
- package/dist/types/launch/supervisor.d.ts +48 -0
- package/dist/types/mods/modindex.d.ts +4 -3
- package/dist/types/mods/source.d.ts +56 -0
- package/dist/types/run/registry.d.ts +22 -0
- package/dist/types/types.d.ts +70 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,238 +20,52 @@ npm install -g ./packages/cli
|
|
|
20
20
|
|
|
21
21
|
The build needs [bun](https://bun.sh) and Node 22 or newer.
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## First launch
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
path moves with it. The file accepts JSONC, so comments and trailing commas are fine.
|
|
25
|
+
Write `~/.config/gamecrate/profiles.yml`. This is the smallest RimWorld config that runs:
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
27
|
+
```yaml
|
|
28
|
+
plugins: ['@gamecrate/rimworld']
|
|
29
|
+
games:
|
|
30
|
+
rimworld:
|
|
31
|
+
# The plugin already says source: mount and container: /game.
|
|
32
|
+
gameFiles:
|
|
33
|
+
host: ~/games/RimWorld
|
|
34
|
+
image:
|
|
35
|
+
ref: ghcr.io/your-org/rimworld:1.6
|
|
36
|
+
acquire: pull
|
|
37
|
+
workshopRoot: ~/.steam/steam/steamapps/workshop/content/294100
|
|
38
|
+
scanRoots:
|
|
39
|
+
- path: ~/projects/mods
|
|
40
|
+
maxDepth: 2
|
|
41
|
+
profiles:
|
|
42
|
+
dev:
|
|
43
|
+
mods: [brrainz.harmony, yourname.yourmod]
|
|
46
44
|
```
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`plugins` is an array of strings. gamecrate loads each one before it reads the rest of the
|
|
53
|
-
file, because a plugin decides what a valid game block looks like.
|
|
54
|
-
|
|
55
|
-
A string that starts with `.` or `/` is a path. gamecrate resolves it against the directory
|
|
56
|
-
holding your config, so `./plugins/mygame` means `~/.config/gamecrate/plugins/mygame`. A `~`
|
|
57
|
-
at the front expands to your home directory.
|
|
58
|
-
|
|
59
|
-
Anything else is a package name. gamecrate walks `node_modules` upward from the config
|
|
60
|
-
directory, the same way Node does. To use a bare name such as `@gamecrate/rimworld`, install it
|
|
61
|
-
where that walk can find it:
|
|
46
|
+
The bare name `@gamecrate/rimworld` only resolves once the package is installed next to the
|
|
47
|
+
config. [Configuration](docs/configuration.md#how-plugins-resolve) shows both ways to point at
|
|
48
|
+
a plugin. Then:
|
|
62
49
|
|
|
63
50
|
```sh
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
npm install @gamecrate/rimworld
|
|
51
|
+
gamecrate doctor
|
|
52
|
+
gamecrate rimworld dev
|
|
67
53
|
```
|
|
68
54
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
### An array you write replaces the plugin's array
|
|
73
|
-
|
|
74
|
-
A plugin ships defaults for its game. Your `games.<name>` block merges on top of those
|
|
75
|
-
defaults, key by key. Objects merge. Scalars overwrite.
|
|
76
|
-
|
|
77
|
-
**Arrays do not concatenate.** The array you write replaces the plugin's array outright. Write
|
|
78
|
-
three entries under `dlc` and the game has three, not the plugin's five plus your three. The
|
|
79
|
-
same holds for `modes`, `scanRoots`, and `saveExtensions`. To add one DLC, copy the plugin's
|
|
80
|
-
full list and append to it.
|
|
81
|
-
|
|
82
|
-
The settings ladder is the one exception. `settings` merges through five layers: the top-level
|
|
83
|
-
`defaults`, then `games.<game>`, then the profile, then the instance, then the command line.
|
|
84
|
-
Arrays inside `settings`, which means `gameArgs` and `dockerArgs`, concatenate at every layer.
|
|
85
|
-
|
|
86
|
-
When a config error points at a key you never wrote, the message says which plugin's defaults
|
|
87
|
-
supplied it.
|
|
88
|
-
|
|
89
|
-
### Profiles
|
|
90
|
-
|
|
91
|
-
A profile names a mod set. `extends` inherits a parent profile's mods and appends its own.
|
|
92
|
-
`exclude` drops entries by glob. `alias` and `aliases` give one profile extra names, and they
|
|
93
|
-
share the parent's data directory. `instances` splits one profile into named sub-runs, each
|
|
94
|
-
with its own saves, logs, lock, and container.
|
|
95
|
-
|
|
96
|
-
gamecrate ships one profile of its own, `modless`. It resolves to the core game plus its
|
|
97
|
-
official DLC, and you cannot redefine it.
|
|
55
|
+
`doctor` checks Docker, the image, the game directory, and the workshop root before anything
|
|
56
|
+
launches. The second line resolves the `dev` profile, stages its two mods, and starts the game.
|
|
57
|
+
`gamecrate rimworld dev --print-plan` shows what it would do without launching.
|
|
98
58
|
|
|
99
|
-
|
|
59
|
+
## Documentation
|
|
100
60
|
|
|
101
|
-
|
|
102
|
-
it stand in for flags you would otherwise type, so a mod repo can pin its own game, profile,
|
|
103
|
-
and extra Docker arguments:
|
|
104
|
-
|
|
105
|
-
```yaml
|
|
106
|
-
game: rimworld
|
|
107
|
-
profile: dev
|
|
108
|
-
mode: headed
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
A flag on the command line beats the file.
|
|
112
|
-
|
|
113
|
-
## Subcommands
|
|
114
|
-
|
|
115
|
-
The subcommand slot defaults to `run`, so `gamecrate rimworld dev` and
|
|
116
|
-
`gamecrate run rimworld dev` do the same thing.
|
|
117
|
-
|
|
118
|
-
| Subcommand | What it does |
|
|
61
|
+
| Read this | When you want to |
|
|
119
62
|
| --- | --- |
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
| `mods
|
|
123
|
-
|
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
| `verify <game> [profile]` | What the running container bound, and whether it looks current |
|
|
130
|
-
| `config edit` | Open `profiles.json` in `$EDITOR`, validate on save |
|
|
131
|
-
| `fix-perms <game> [profile]` | Chown foreign-owned files back to the caller |
|
|
132
|
-
| `help [topic]` | Help for a subcommand or a game |
|
|
133
|
-
| `version` | Print the version |
|
|
134
|
-
|
|
135
|
-
`gamecrate help <game>` lists that game's profiles and modes. `gamecrate help completion bash`
|
|
136
|
-
and `gamecrate help completion zsh` print a shell completion script.
|
|
137
|
-
|
|
138
|
-
Game arguments go after a bare `--` and nowhere else.
|
|
139
|
-
|
|
140
|
-
## Flags
|
|
141
|
-
|
|
142
|
-
Mod set:
|
|
143
|
-
|
|
144
|
-
- `--mod <id>` adds a mod to the profile set. Repeatable.
|
|
145
|
-
- `--without <id>` drops a mod from the resolved set. Repeatable.
|
|
146
|
-
- `--only <id>` restricts the resolved set to these mods. Repeatable.
|
|
147
|
-
- `--use <packageId>=<path>` forces one mod to load from a directory. Repeatable.
|
|
148
|
-
- `--sort <topo|none>` picks the profile order or a topological sort.
|
|
149
|
-
|
|
150
|
-
Worktrees and instances:
|
|
151
|
-
|
|
152
|
-
- `--worktree <path>` promotes mods from a linked git worktree, in its own instance. Repeatable,
|
|
153
|
-
and earlier flags outrank later ones.
|
|
154
|
-
- `--no-worktree` ignores the current directory and `$GAMECRATE_WORKTREE`.
|
|
155
|
-
- `--instance <name>` runs under a named sub-profile with its own saves, logs, and container.
|
|
156
|
-
|
|
157
|
-
Display and lifetime:
|
|
158
|
-
|
|
159
|
-
- `--mode <headed|headless|screenshot>` chooses how the game displays.
|
|
160
|
-
- `--resolution <width>x<height>` overrides the game resolution.
|
|
161
|
-
- `--marker <str>` exits 0 as soon as that string appears in the log.
|
|
162
|
-
- `--timeout <seconds>` kills the container after that long.
|
|
163
|
-
- `--render-wait <seconds>` sets the settle time before a screenshot.
|
|
164
|
-
|
|
165
|
-
Container and build:
|
|
166
|
-
|
|
167
|
-
- `--network <none|bridge|host>` sets the container network mode.
|
|
168
|
-
- `--pull <always|missing|never>` decides when to pull the runtime image.
|
|
169
|
-
- `--build` compiles local C# mods first. `--no-build` never compiles, even when an assembly
|
|
170
|
-
looks stale.
|
|
171
|
-
- `--no-stale-check` drops the warning about sources newer than assemblies. The check still runs.
|
|
172
|
-
- `--docker-arg <arg>` adds one argv element to `docker run`. Repeatable.
|
|
173
|
-
- `--root` runs as root instead of mapping your uid.
|
|
174
|
-
- `--replace` stops whatever holds this profile and instance, then launches. `--no-replace`
|
|
175
|
-
refuses instead.
|
|
176
|
-
|
|
177
|
-
Output and dry runs:
|
|
178
|
-
|
|
179
|
-
- `--dry-run` resolves and validates fully, then writes nothing.
|
|
180
|
-
- `--print-plan` prints the resolved launch plan instead of launching.
|
|
181
|
-
- `--log <path>` routes launch output to one file.
|
|
182
|
-
- `--json` switches to machine-readable output.
|
|
183
|
-
|
|
184
|
-
`clean` adds three tier flags: `--staging` (the default), `--logs`, and `--all`. `--all` deletes
|
|
185
|
-
saves, so it needs `--yes`.
|
|
186
|
-
|
|
187
|
-
## Environment variables
|
|
188
|
-
|
|
189
|
-
Each variable stands in for one flag, and only when you leave that flag off:
|
|
190
|
-
|
|
191
|
-
`GAMECRATE_INSTANCE`, `GAMECRATE_MODE`, `GAMECRATE_MARKER`, `GAMECRATE_TIMEOUT`,
|
|
192
|
-
`GAMECRATE_RENDER_WAIT`, `GAMECRATE_NETWORK`, `GAMECRATE_PULL`, `GAMECRATE_BUILD`,
|
|
193
|
-
`GAMECRATE_SORT`, `GAMECRATE_ROOT`.
|
|
194
|
-
|
|
195
|
-
`GAMECRATE_WORKTREE` names a worktree to promote, and `--no-worktree` cancels it.
|
|
196
|
-
|
|
197
|
-
## Exit codes
|
|
198
|
-
|
|
199
|
-
- `0` success
|
|
200
|
-
- `1` the game itself failed
|
|
201
|
-
- `2` usage error
|
|
202
|
-
- `3` config error
|
|
203
|
-
- `4` resolution error
|
|
204
|
-
- `5` environment problem, such as a missing Docker
|
|
205
|
-
- `6` the marker never appeared before the timeout
|
|
206
|
-
- `7` refused, because this profile and instance already run
|
|
207
|
-
- `8` `verify` found a stale mod
|
|
208
|
-
- `130` interrupted
|
|
209
|
-
|
|
210
|
-
## Write a plugin
|
|
211
|
-
|
|
212
|
-
A plugin is an ES module with a default export that satisfies `GamePlugin`. It takes plain data
|
|
213
|
-
and throws plain `Error` objects, so it never imports the gamecrate runtime at run time.
|
|
214
|
-
|
|
215
|
-
```ts
|
|
216
|
-
import type { GamePlugin } from '@gamecrate/cli'
|
|
217
|
-
|
|
218
|
-
const plugin: GamePlugin = {
|
|
219
|
-
apiVersion: 1,
|
|
220
|
-
game: 'mygame',
|
|
221
|
-
defaults: { /* Partial<GameConfig> */ },
|
|
222
|
-
parseManifest: (text) => null,
|
|
223
|
-
renderModsConfig: (input) => '',
|
|
224
|
-
mergePrefs: (existing, owned) => '',
|
|
225
|
-
windowedPrefs: { fullscreen: 'False' },
|
|
226
|
-
parseVersion: (text) => null,
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export default plugin
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
The members:
|
|
233
|
-
|
|
234
|
-
- `apiVersion` must equal `PLUGIN_API_VERSION`, which is `1`. A mismatch fails the load with a
|
|
235
|
-
message naming both numbers.
|
|
236
|
-
- `game` is the word the command line answers to, such as `rimworld`.
|
|
237
|
-
- `defaults` is a `Partial<GameConfig>`. It holds facts about the game itself, never about one
|
|
238
|
-
machine. Leave install paths, workshop roots, scan roots, and images to the user.
|
|
239
|
-
- `parseManifest(text)` reads one mod manifest and returns a `ModManifest`, or `null` when the
|
|
240
|
-
file is not a manifest at all. A malformed manifest throws.
|
|
241
|
-
- `renderModsConfig(input)` takes a version, a build number, the active package ids in load
|
|
242
|
-
order, and the known expansions, then returns the file the engine reads.
|
|
243
|
-
- `mergePrefs(existing, owned)` folds the keys gamecrate owns into the player's own prefs file.
|
|
244
|
-
`existing` is `null` on the first run.
|
|
245
|
-
- `windowedPrefs` lists the prefs keys that put the game in a window rather than fullscreen.
|
|
246
|
-
- `parseVersion(text)` reads the engine's own version file and returns a version string and a
|
|
247
|
-
build number, or `null` when the text does not parse.
|
|
248
|
-
|
|
249
|
-
Load your plugin by path while you develop it:
|
|
250
|
-
|
|
251
|
-
```jsonc
|
|
252
|
-
{
|
|
253
|
-
"plugins": ["~/projects/gamecrate-mygame/dist/index.js"]
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
[`@gamecrate/rimworld`](../rimworld) is a complete example in about fifty lines.
|
|
63
|
+
| [Configuration](docs/configuration.md) | Write the config file, load a plugin, define profiles, or set per-repository defaults |
|
|
64
|
+
| [Mod sources](docs/mod-sources.md) | Pin a mod to a directory, a workshop item, or a git repository, and learn which copy of a mod wins |
|
|
65
|
+
| [The `mods` commands](docs/mods-commands.md) | Add, remove, and sync library pins from the command line |
|
|
66
|
+
| [Running](docs/running.md) | Launch, run in the background, read the result, close a window, and clean up |
|
|
67
|
+
| [Reference](docs/reference.md) | Every subcommand, flag, environment variable, and exit code |
|
|
68
|
+
| [Writing a plugin](docs/plugins.md) | Teach gamecrate a new game |
|
|
69
|
+
|
|
70
|
+
[`@gamecrate/rimworld`](../rimworld) is the RimWorld plugin, and the one complete example of
|
|
71
|
+
the plugin contract.
|