Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
47cc385
Impl placeholders
ArKaNeMaN Mar 27, 2026
7971661
Fix api & add default placeholders
ArKaNeMaN Mar 27, 2026
dc82118
Gen guideline for claude
ArKaNeMaN Mar 28, 2026
6075e1e
Reformat code
ArKaNeMaN Mar 28, 2026
d988197
Update ph api
ArKaNeMaN May 3, 2026
2a51f81
Impl templates
ArKaNeMaN May 3, 2026
aeea5b9
Fix
ArKaNeMaN May 3, 2026
f588abc
Impl test plugin for ph
ArKaNeMaN May 3, 2026
604fb8e
Add `Regexp` type
ArKaNeMaN Apr 10, 2026
71d9e69
Add pcget and pcsingle for regexp
ArKaNeMaN Apr 10, 2026
90c734c
Fix inc
ArKaNeMaN Apr 10, 2026
2d6bb48
Minor fixes in pcjson
ArKaNeMaN Apr 10, 2026
befb08e
Impl `PCGet_RegexpMatch` func
ArKaNeMaN Apr 10, 2026
4bb086d
Add PCParam-style for params list registering
ArKaNeMaN Apr 10, 2026
dacd504
Remove test plugin
ArKaNeMaN May 3, 2026
3cdb833
Merge branch 'master' into feature/placeholders
ArKaNeMaN May 3, 2026
15ee6d2
Impl default ph keys for global context
ArKaNeMaN May 3, 2026
93e9241
Impl default keys for player context
ArKaNeMaN May 3, 2026
9f93509
Impl proxy ph groups
ArKaNeMaN May 3, 2026
a34aeb0
Add `PH-Template` param type
ArKaNeMaN May 3, 2026
e0389f4
Fix
ArKaNeMaN May 4, 2026
8368599
Add `PlayerModel` type
ArKaNeMaN May 16, 2026
8d2af50
Bump version
ArKaNeMaN May 16, 2026
b621b71
Fix player model type
ArKaNeMaN May 16, 2026
6fe4064
Add missing PCSingle_* helpers
ArKaNeMaN May 21, 2026
d1b866c
Use amxx-builder
ArKaNeMaN May 31, 2026
c31d8f9
Add unknown param type handling
ArKaNeMaN May 31, 2026
7057fb4
Show warn when param with unknown param is specified
ArKaNeMaN May 31, 2026
6e909e5
Bump version
ArKaNeMaN May 31, 2026
31dbbc0
Add OC cfg with amxb
ArKaNeMaN Aug 27, 2026
c430afd
Fix placeholders and cleanup repo
ArKaNeMaN Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .build-config

This file was deleted.

113 changes: 25 additions & 88 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,121 +2,58 @@

on:
push:
branches: [master]
branches: [master, feature/**, fix/**]
paths-ignore:
- "**.md"
- "**.bat"

pull_request:
types: [opened, reopened, synchronize]
release:
types: [published]

env:
PACKAGE_NAME: ParamsController
STORE_READMY: 0

jobs:
build:
name: "Build"
name: Build
runs-on: ubuntu-latest
outputs:
COMMIT_SHA: ${{ steps.declare_sha.outputs.COMMIT_SHA }}
SEMVER: ${{ steps.declare_sha.outputs.SEMVER }}
sha: ${{ steps.sha.outputs.SHORT }}
name: ${{ steps.build.outputs.name }}
steps:
- name: Checkout
uses: actions/checkout@v4.1.1
with:
fetch-depth: 0

- name: Parse SemVer string (release)
id: semver_parser
if: |
github.event_name == 'release' &&
github.event.action == 'published' &&
startsWith(github.ref, 'refs/tags/')
uses: booxmedialtd/ws-action-parse-semver@v1.4.7
with:
input_string: ${{ github.ref }}
version_extractor_regex: 'refs\/tags\/(.*)$'
- uses: actions/checkout@v4

- name: Declare SHA & package name
id: declare_sha
shell: bash
run: |
SHA=$(git rev-parse --short HEAD)
echo "COMMIT_SHA=$SHA" >> $GITHUB_OUTPUT
echo "SEMVER=${{ steps.semver_parser.outputs.fullversion }}" >> $GITHUB_OUTPUT
- id: sha
run: echo "SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT

- name: Setup AMXXPawn Compiler
uses: wopox1337/setup-amxxpawn@v1.1.0
- id: build
uses: AmxxModularEcosystem/amxx-builder@v1
with:
version: "1.10.5428"

- name: Compile plugins
working-directory: amxmodx/scripting/
env:
REAPI_INCLUDE: ${{ env.REAPI_INCLUDE_PATH }}
PARAMS_CONRTOLLER_INCLUDE: ${{ env.PARAMS_CONRTOLLER_INCLUDE_PATH }}
run: |
compile() {
sourcefile=$1
amxxfile="$(echo $sourcefile | sed -e 's/\.sma$/.amxx/')"
output_path="../plugins/$amxxfile"
set: |
output.pack=false
output.dir=./artifact

mkdir -p $(dirname $output_path)

echo -n "Compiling $sourcefile ... "
amxxpc $sourcefile -o"$output_path" \
-i"include" \
-i"$REAPI_INCLUDE" \
-i"$PARAMS_CONRTOLLER_INCLUDE"
}
export -f compile

find . -type f -name "*.sma" -exec bash -c 'compile "$0"' {} \;

- name: Move files
env:
STORE_READMY: ${{ env.STORE_READMY }}
run: |
mkdir -p publish/${{ env.PACKAGE_NAME }}/addons
mv amxmodx/ publish/${{ env.PACKAGE_NAME }}/addons/

if [ ! -z "${STORE_READMY}" ]; then
mv README.md publish/
fi

- name: Deploy artifact
uses: actions/upload-artifact@v4.3.1
- uses: actions/upload-artifact@v4
with:
name: ${{ env.PACKAGE_NAME }}-${{ steps.declare_sha.outputs.COMMIT_SHA }}-dev
path: publish/*
name: ${{ steps.build.outputs.name }}-${{ steps.sha.outputs.SHORT }}-dev
path: artifact/

publish:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
name: "Publish release"
name: Publish release
runs-on: ubuntu-latest
needs: [build]
if: |
github.event_name == 'release' &&
github.event.action == 'published' &&
startsWith(github.ref, 'refs/tags/')
steps:
- name: Download artifact
uses: actions/download-artifact@v4.1.7
- uses: actions/download-artifact@v4
with:
name: ${{ env.PACKAGE_NAME }}-${{ needs.build.outputs.COMMIT_SHA }}-dev
name: ${{ needs.build.outputs.name }}-${{ needs.build.outputs.sha }}-dev
path: artifact/

- name: Packaging binaries
id: packaging
run: 7z a -mm=Deflate -mfb=258 -mpass=15 -r ${{ env.PACKAGE_NAME }}-${{ needs.build.outputs.SEMVER }}.zip
- name: Package for release
run: |
cd artifact
zip -r "../${{ needs.build.outputs.name }}-${{ github.ref_name }}.zip" .

- name: Publish artifacts
uses: softprops/action-gh-release@v2.0.4
id: publish-job
if: |
startsWith(github.ref, 'refs/tags/') &&
steps.packaging.outcome == 'success'
- uses: softprops/action-gh-release@v2
with:
files: |
*.zip
files: "${{ needs.build.outputs.name }}-*.zip"
Comment on lines +39 to +59
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
*.amxx
*.zip
.build
plugins-*.ini
.env
.amxb-cache
.claude
build
dist
.omo
13 changes: 13 additions & 0 deletions .opencode/opencode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"amxx-dep-resolver": {
"type": "local",
"command": [
"amxb",
"mcp"
],
"enabled": true
}
}
}
73 changes: 73 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# AGENTS.md

Guidance for AI agents working with code in this repository.

## Build

**Requirements:** [amxx-builder](https://github.com/AmxxModularEcosystem/amxx-builder) (`amxb`). The manifest is `amxbuild.yml` — it pins the AMXX compiler version (`amxmodx.version`) and declares include dependencies (`deps`).

```bat
build.bat :: local wrapper around `amxb build`
amxb build :: actual build command
```

Output goes to `dist/` (or `ParamsController.zip` at repo root when packaging). Build settings live in `amxbuild.yml`. No `deps` are currently declared — the plugin intentionally has no third-party include dependencies (ReAPI was removed; default placeholder groups use only AMXX core natives).

There are no test or lint commands — the CI pipeline (`.github/workflows/CI.yml`) runs the same `amxb build` as the validation step.

> The plugin must compile without warnings that are errors — CI treats compiler output as the gate. Run `amxb build` before pushing.

## Architecture

ParamsController is an AmxModX plugin library (written in PAWN) that provides:

1. **Parameter parsing** — a JSON-based typed parameter framework. A consumer plugin calls `ParamsController_Init()`, constructs typed parameters with `ParamsController_Param_Construct()`, then reads a JSON object via `ParamsController_Param_ReadList()`. Each parameter is validated against its registered type handler.
2. **Placeholder system** — a global/per-player string substitution framework (groups, keys, contexts, proxy groups, compiled templates). Full design is documented in `PLACEHOLDERS.md`.

### Layer structure

| Layer | Path | Role |
|---|---|---|
| Public API | `amxmodx/scripting/include/ParamsController.inc` | All native/forward declarations + stock helpers; this is what consumers include |
| Main plugin | `amxmodx/scripting/ParamsController.sma` | Entry point, library registration, init flow, forward registration |
| Params API impl | `ParamsController/API/` | Native implementations (General, Param, ParamType, Utils) |
| Placeholder API impl | `ParamsController/Placeholders/API/` | `PCPH_*` native implementations (General, Group, Key) |
| Object model | `ParamsController/Objects/` | `Param.inc` (Array + free-list storage), `ParamType.inc` (Trie-keyed type registry, current-param stack) |
| Placeholder objects | `ParamsController/Placeholders/Objects/` | `PHGroup.inc` (group registry, contexts, values, callback stack), `PHTemplate.inc` (compiled template cache) |
| Placeholder utils | `ParamsController/Placeholders/PHParse.inc` | Shared placeholder parsing (`PHParse_FindClosing`, `PHParse_Placeholder`) used by inline formatting and template compilation |
| Built-in types | `ParamsController/DefaultObjects/ParamType/` | 18 files registering 20 type names: Boolean, Integer, Float, String/ShortString/LongString, RGB, Model, PlayerModel, Sound, Resource, File, Dir, ChatMessage, Time, TimeInterval, WeekDay, Flags, Regexp, PH-Template |
| Default placeholders | `ParamsController/DefaultObjects/Placeholder/` | `Global.inc`, `Player.inc` — built-in groups (`{map}`, `{real-map}`, `{server-name}`, `{server-ip}`, `{date}`, `{time}`, `{datetime}`, `{players-count}`, `{max-players-count}`; `{p:authid}`, `{p:name}`, `{p:ip}`, `{p:userid}`) |
| Registrar | `ParamsController/DefaultObjects/Registrar.inc` | Wires up all default types and placeholder groups on plugin load |
| Forwards | `ParamsController/Forwards.inc` | Generic forward registration utility used throughout |

### Key design patterns

- **Parameter storage:** Array-based with a free-list for handle reuse (`Objects/Param.inc`)
- **Type registry:** Trie-keyed by type name string (`Objects/ParamType.inc`); read callbacks are `CreateOneForward`-based, so plugin-owned
- **Current param context:** `CurrentParams`/`CurrentParamName` globals + a save/restore stack, so read callbacks can nest (types that internally read other types via `PCSingle_*`)
- **Placeholder groups:** Array-backed, Trie-keyed by prefix (`Placeholders/Objects/PHGroup.inc`); values stored as `"key:contextKey"` strings; contexts are per-group stacks
- **Proxy groups:** alias a source group's keys/callbacks/values but keep an independent context stack (two players in one string)
- **Callback reentrancy:** the currently-active callback (group/key/context) is kept on a stack (`S_PHCurrentCallback`), so `PCPH_Format`/`PCPH_Cb_Set*` can be called from inside a callback — nested calls restore the outer state
- **Compiled templates:** `PCPH_CompileTemplate` pre-parses a format string into parts and caches group/forward handles (`Placeholders/Objects/PHTemplate.inc`)
- **Extensibility:** consumers can register custom parameter types (`ParamsController_ParamType_Register` / `ParamsController_RegSimpleType`) and placeholder groups/keys via `PCPH_*` natives
- **Error reporting:** `E_ParamsReadErrorType` enum returned from `ReadList`; error details (param name, expected/got type) accessible via out-params of `ParamsController_Param_ReadList`

### Init flow (`PluginInit` in `ParamsController.sma`)

1. `Forwards_Init()`, `Param_Init()`, `PHGroup_Init()` — allocate storages
2. `DefaultObjects_Register()` — register built-in param types + default placeholder groups
3. Forward `ParamsController_OnRegisterTypes` — consumers register custom param types
4. Forward `PCPH_OnRegisterGroups` — consumers register placeholder groups/keys
5. Forward `PCPH_OnRegisterProxyGroups` — consumers register proxy groups (all normal groups already exist)
6. Forward `ParamsController_OnInited` — everything is ready

### Language notes

PAWN uses `#include` for all modular decomposition. `.inc` files are not independently compiled — they are all included into `ParamsController.sma`. The public header (`ParamsController.inc`) only declares natives/forwards and stock helpers; it contains no plugin state or implementation logic.

## Conventions

- Natives are named `ParamsController_*` (params) and `PCPH_*` (placeholders); implementation handlers use the `@` prefix (e.g. `@API_Param_Construct`) and `register_native(name, "@handler")`.
- All buffers that cross the callback boundary should be local (`new`), not `static` — placeholder callbacks can re-enter the formatter.
- Keep the manifest free of undeclared includes: any `#include <...>` beyond AMXX stdlib must be declared in `amxbuild.yml` `deps` (or removed).
- Do not commit build output — `dist/`, `build/`, `.amxb-cache/` are git-ignored.
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

All repository guidance lives in [AGENTS.md](AGENTS.md) — build instructions, architecture, conventions, and language notes. Read it before working with the code.
Loading
Loading