# Plugin config

Declare typed config in `package.json` under `s2script.config`. The host materializes defaults ⊕ an admin-editable JSON override at load.

```json
{
	"s2script": {
		"config": {
			"greeting": { "type": "string", "default": "hello", "description": "Chat greeting" },
			"max_uses": { "type": "int", "default": 3 },
			"cooldown": { "type": "float", "default": 0.75 },
			"enabled": { "type": "bool", "default": true }
		}
	}
}
```

Supported types: `string`, `int`, `float`, `bool`. Wrong-typed override values degrade to the default and log a warning — never crash.

Each declaration takes these fields. `s2s build` rejects a block that breaks the rules.

| Field         | Applies to      | Meaning                                                                               |
| ------------- | --------------- | ------------------------------------------------------------------------------------- |
| `type`        | all             | `string`, `int`, `float` or `bool`. Required.                                         |
| `default`     | all             | Required, and must match `type`.                                                      |
| `min` / `max` | `int`, `float`  | Bounds the default must sit within. Not combined with `enum`.                         |
| `enum`        | `string`, `int` | Non-empty list of allowed values; the default must be one of them.                    |
| `description` | all             | Shown in the generated file and on the [package page](https://s2script.com/docs/publishing#package-page). |
| `label`       | all             | Short display name for the package page.                                              |
| `group`       | all             | Package-page heading the option is listed under.                                      |
| `sensitive`   | all             | `true` hides the default on the package page. The file still gets it.                 |

An object without a string `type` is a section: its keys are further declarations, read with dotted names such as `config.getString('chat.prefix')`. Keys may not contain `.`.

## Reading values

Read through the `config` object — a plain named import, valid anywhere in your plugin:

```ts
import { config } from '@s2script/sdk';

export function OnPluginStart(): void {
	const greeting = config.getString('greeting');
	const maxUses = config.getInt('max_uses');
	const cooldown = config.getFloat('cooldown');
	const enabled = config.getBool('enabled');
}
```

Undeclared keys return the type's zero value (empty string / `0` / `false`), never throw.

## Live reload

Opt in by subscribing on `config` — the first `onChange` starts watching the override file. Edits re-materialize and fire handlers **without** reloading the plugin:

```ts
import { config } from '@s2script/sdk';

export function OnPluginStart(): void {
	config.onChange(() => {
		console.log('config changed → greeting =', config.getString('greeting'));
	});
}
```

`config.onChange` is not load-window-gated. No subscriber → config is read once at load (zero watch overhead).

## Raw files

`config.readFile` / `config.writeFile` read and write plain files under `addons/s2script/configs/` (e.g. `maplist.txt`) — the name includes its extension, and traversal is rejected.

Override files themselves live at `addons/s2script/configs/<plugin-id>.json` and are auto-generated on first load with defaults and `//` comments.

See the [config module](https://s2script.com/docs/api/modules/config).

## Host process limits

Plugin `s2script.config` is not the host process budget. Operators set `S2SCRIPT_ASYNC_LIMITS_JSON` on the game process before start (JSON object, any subset of fields; unknown or invalid values fall back to full defaults and a warning). The policy is read once per process. Field table, defaults, and `__s2_async_stats` are on [Async & networking](https://s2script.com/docs/concepts/async#async-limits).
