crusader-sdk 0.1.0a1__tar.gz
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.
- crusader_sdk-0.1.0a1/.dockerignore +19 -0
- crusader_sdk-0.1.0a1/.github/ISSUE_TEMPLATE/bug_report.yml +36 -0
- crusader_sdk-0.1.0a1/.github/ISSUE_TEMPLATE/config.yml +5 -0
- crusader_sdk-0.1.0a1/.github/ISSUE_TEMPLATE/feature_request.yml +16 -0
- crusader_sdk-0.1.0a1/.github/dependabot.yml +14 -0
- crusader_sdk-0.1.0a1/.github/release.yml +15 -0
- crusader_sdk-0.1.0a1/.github/workflows/ci.yml +65 -0
- crusader_sdk-0.1.0a1/.github/workflows/release.yml +28 -0
- crusader_sdk-0.1.0a1/.gitignore +19 -0
- crusader_sdk-0.1.0a1/.pre-commit-config.yaml +20 -0
- crusader_sdk-0.1.0a1/ACKNOWLEDGEMENTS.md +3 -0
- crusader_sdk-0.1.0a1/CHANGELOG.md +26 -0
- crusader_sdk-0.1.0a1/CONTRIBUTING.md +24 -0
- crusader_sdk-0.1.0a1/LEGAL.md +5 -0
- crusader_sdk-0.1.0a1/LICENSE +21 -0
- crusader_sdk-0.1.0a1/PKG-INFO +93 -0
- crusader_sdk-0.1.0a1/README.md +72 -0
- crusader_sdk-0.1.0a1/SECURITY.md +7 -0
- crusader_sdk-0.1.0a1/assets/.gitkeep +0 -0
- crusader_sdk-0.1.0a1/assets/hero.jpg +0 -0
- crusader_sdk-0.1.0a1/assets/mark.png +0 -0
- crusader_sdk-0.1.0a1/assets/social-preview.jpg +0 -0
- crusader_sdk-0.1.0a1/docker/.gitkeep +0 -0
- crusader_sdk-0.1.0a1/docker/Dockerfile +35 -0
- crusader_sdk-0.1.0a1/docker/run.sh +36 -0
- crusader_sdk-0.1.0a1/docker/ucp-config.yml +15 -0
- crusader_sdk-0.1.0a1/docs/.gitkeep +0 -0
- crusader_sdk-0.1.0a1/docs/api.md +1278 -0
- crusader_sdk-0.1.0a1/docs/commands/allies.md +49 -0
- crusader_sdk-0.1.0a1/docs/commands/defences.md +103 -0
- crusader_sdk-0.1.0a1/docs/commands/economy.md +153 -0
- crusader_sdk-0.1.0a1/docs/commands/military.md +138 -0
- crusader_sdk-0.1.0a1/docs/commands/observe.md +121 -0
- crusader_sdk-0.1.0a1/docs/commands/siege.md +81 -0
- crusader_sdk-0.1.0a1/docs/commands/time.md +15 -0
- crusader_sdk-0.1.0a1/docs/concepts.md +15 -0
- crusader_sdk-0.1.0a1/docs/how-it-works.md +11 -0
- crusader_sdk-0.1.0a1/docs/index.md +11 -0
- crusader_sdk-0.1.0a1/docs/protocol.md +20 -0
- crusader_sdk-0.1.0a1/docs/quickstart.md +65 -0
- crusader_sdk-0.1.0a1/docs/reference/buildings.md +78 -0
- crusader_sdk-0.1.0a1/docs/reference/errors.md +794 -0
- crusader_sdk-0.1.0a1/docs/reference/events.md +15 -0
- crusader_sdk-0.1.0a1/docs/reference/goods.md +29 -0
- crusader_sdk-0.1.0a1/docs/reference/map-legend.md +17 -0
- crusader_sdk-0.1.0a1/docs/reference/state.md +27 -0
- crusader_sdk-0.1.0a1/docs/reference/units.md +29 -0
- crusader_sdk-0.1.0a1/llms-full.txt +3103 -0
- crusader_sdk-0.1.0a1/llms.txt +26 -0
- crusader_sdk-0.1.0a1/pyproject.toml +40 -0
- crusader_sdk-0.1.0a1/scripts/build-image.sh +5 -0
- crusader_sdk-0.1.0a1/scripts/check-action-pins.sh +30 -0
- crusader_sdk-0.1.0a1/scripts/check-game-files.sh +41 -0
- crusader_sdk-0.1.0a1/scripts/leakcheck.sh +45 -0
- crusader_sdk-0.1.0a1/src/crusader/__init__.py +276 -0
- crusader_sdk-0.1.0a1/src/crusader/bridge/__init__.py +1 -0
- crusader_sdk-0.1.0a1/src/crusader/bridge/game.py +3323 -0
- crusader_sdk-0.1.0a1/src/crusader/bridge/memory.py +260 -0
- crusader_sdk-0.1.0a1/src/crusader/bridge/server.py +466 -0
- crusader_sdk-0.1.0a1/src/crusader/commands.py +700 -0
- crusader_sdk-0.1.0a1/src/crusader/types.py +152 -0
- crusader_sdk-0.1.0a1/tests/test_audit_low.py +80 -0
- crusader_sdk-0.1.0a1/tests/test_client.py +114 -0
- crusader_sdk-0.1.0a1/tests/test_contract.py +31 -0
- crusader_sdk-0.1.0a1/tests/test_docs.py +23 -0
- crusader_sdk-0.1.0a1/tests/test_game.py +221 -0
- crusader_sdk-0.1.0a1/tests/test_gen_docs.py +64 -0
- crusader_sdk-0.1.0a1/tests/test_leakcheck.py +78 -0
- crusader_sdk-0.1.0a1/tests/test_memory.py +172 -0
- crusader_sdk-0.1.0a1/tests/test_server.py +376 -0
- crusader_sdk-0.1.0a1/tests/test_smoke.py +7 -0
- crusader_sdk-0.1.0a1/tools/.gitkeep +0 -0
- crusader_sdk-0.1.0a1/tools/gen_docs.py +241 -0
- crusader_sdk-0.1.0a1/tools/smoke.py +80 -0
- crusader_sdk-0.1.0a1/ucp3/crusader-bridge-0.1.0/definition.yml +6 -0
- crusader_sdk-0.1.0a1/ucp3/crusader-bridge-0.1.0/init.lua +363 -0
- crusader_sdk-0.1.0a1/uv.lock +1829 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
*
|
|
2
|
+
!pyproject.toml
|
|
3
|
+
!uv.lock
|
|
4
|
+
!README.md
|
|
5
|
+
!src/
|
|
6
|
+
!src/**
|
|
7
|
+
!ucp3/
|
|
8
|
+
!ucp3/crusader-bridge-0.1.0/
|
|
9
|
+
!ucp3/crusader-bridge-0.1.0/**
|
|
10
|
+
!docker/
|
|
11
|
+
!docker/run.sh
|
|
12
|
+
!docker/ucp-config.yml
|
|
13
|
+
!scripts/
|
|
14
|
+
!scripts/check-game-files.sh
|
|
15
|
+
**/__pycache__/
|
|
16
|
+
**/*.pyc
|
|
17
|
+
**/*.exe
|
|
18
|
+
**/*.dll
|
|
19
|
+
**/*.sav
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something in the SDK, bridge or Docker setup doesn't work as documented.
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Please don't post game files, save files or access tokens. Security problems go through
|
|
9
|
+
[private vulnerability reporting](../security/advisories/new), not issues.
|
|
10
|
+
- type: textarea
|
|
11
|
+
id: what-happened
|
|
12
|
+
attributes:
|
|
13
|
+
label: What happened
|
|
14
|
+
description: The command or code you ran, what you expected, and what happened instead.
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: error
|
|
19
|
+
attributes:
|
|
20
|
+
label: Error or bridge output
|
|
21
|
+
description: The CrusaderError message, bridge log lines, or a stack trace.
|
|
22
|
+
render: text
|
|
23
|
+
- type: input
|
|
24
|
+
id: version
|
|
25
|
+
attributes:
|
|
26
|
+
label: crusader-sdk version
|
|
27
|
+
placeholder: "0.1.0a1"
|
|
28
|
+
validations:
|
|
29
|
+
required: true
|
|
30
|
+
- type: dropdown
|
|
31
|
+
id: setup
|
|
32
|
+
attributes:
|
|
33
|
+
label: How you run the bridge
|
|
34
|
+
options: [Docker image from this repo, Own Wine setup, Other]
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: A game action, observation or tool you'd like the SDK to support.
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What you want to do
|
|
9
|
+
description: The in-game action or information, and how a human player does or sees it in the game.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: why
|
|
14
|
+
attributes:
|
|
15
|
+
label: Why
|
|
16
|
+
description: What it would let you or your agent do.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: github-actions
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
- package-ecosystem: docker
|
|
8
|
+
directory: /docker
|
|
9
|
+
schedule:
|
|
10
|
+
interval: weekly
|
|
11
|
+
- package-ecosystem: uv
|
|
12
|
+
directory: /
|
|
13
|
+
schedule:
|
|
14
|
+
interval: weekly
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
changelog:
|
|
2
|
+
exclude:
|
|
3
|
+
authors:
|
|
4
|
+
- dependabot
|
|
5
|
+
categories:
|
|
6
|
+
- title: Breaking changes
|
|
7
|
+
labels: [breaking]
|
|
8
|
+
- title: New commands and features
|
|
9
|
+
labels: [enhancement]
|
|
10
|
+
- title: Fixes
|
|
11
|
+
labels: [bug]
|
|
12
|
+
- title: Documentation
|
|
13
|
+
labels: [documentation]
|
|
14
|
+
- title: Other changes
|
|
15
|
+
labels: ["*"]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
lint:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
20
|
+
- run: uv sync --locked --extra dev --extra bridge
|
|
21
|
+
- run: uv run --locked ruff check .
|
|
22
|
+
- run: uv run --locked ruff format --check .
|
|
23
|
+
- run: uv run --locked pyright
|
|
24
|
+
|
|
25
|
+
test:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
strategy:
|
|
28
|
+
fail-fast: false
|
|
29
|
+
matrix:
|
|
30
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
33
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
34
|
+
with:
|
|
35
|
+
python-version: ${{ matrix.python }}
|
|
36
|
+
- run: uv sync --locked --extra dev --extra bridge
|
|
37
|
+
- if: matrix.python != '3.13'
|
|
38
|
+
run: uv run --locked pytest
|
|
39
|
+
- if: matrix.python == '3.13'
|
|
40
|
+
run: uv run --locked pytest --cov=crusader --cov-report=xml
|
|
41
|
+
- if: matrix.python == '3.13'
|
|
42
|
+
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
|
43
|
+
with:
|
|
44
|
+
files: coverage.xml
|
|
45
|
+
fail_ci_if_error: false # tokenless upload needs the repo enabled on codecov.io
|
|
46
|
+
use_oidc: false
|
|
47
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
48
|
+
|
|
49
|
+
docs-security:
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
53
|
+
with:
|
|
54
|
+
fetch-depth: 0
|
|
55
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
56
|
+
- run: uv run --locked python tools/gen_docs.py --check
|
|
57
|
+
- run: sh scripts/leakcheck.sh
|
|
58
|
+
- name: Scan git history for secrets
|
|
59
|
+
run: |
|
|
60
|
+
go install github.com/zricethezav/gitleaks/v8@v8.28.0
|
|
61
|
+
"$(go env GOPATH)/bin/gitleaks" git --redact --no-banner
|
|
62
|
+
- name: Audit locked dependencies
|
|
63
|
+
run: |
|
|
64
|
+
uv export --locked --all-extras --no-emit-project --no-hashes -o /tmp/crusader-requirements.txt
|
|
65
|
+
uvx pip-audit -r /tmp/crusader-requirements.txt
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: release-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build-and-publish:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment:
|
|
19
|
+
name: pypi
|
|
20
|
+
url: https://pypi.org/p/crusader-sdk
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
id-token: write
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
27
|
+
- run: uv build
|
|
28
|
+
- uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.14.14
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff-check
|
|
6
|
+
- id: ruff-format
|
|
7
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
8
|
+
rev: v6.0.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: end-of-file-fixer
|
|
11
|
+
- id: trailing-whitespace
|
|
12
|
+
- id: check-added-large-files
|
|
13
|
+
args: ["--maxkb=500"]
|
|
14
|
+
- repo: local
|
|
15
|
+
hooks:
|
|
16
|
+
- id: leakcheck
|
|
17
|
+
name: leakcheck
|
|
18
|
+
entry: sh scripts/leakcheck.sh
|
|
19
|
+
language: system
|
|
20
|
+
pass_filenames: false
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are listed here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/). Before 1.0, minor versions may change the command set.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0a1] - 2026-11-09
|
|
10
|
+
|
|
11
|
+
First public pre-release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- Python client (`crusader.Game`) with typed results, standard library only.
|
|
15
|
+
- Bridge server (`crusader-bridge`) exposing every command over REST and MCP, bound to 127.0.0.1 by default, with an
|
|
16
|
+
optional bearer token, Host/Origin checks and request size limits.
|
|
17
|
+
- Game driver for Stronghold Crusader HD 1.41 under Wine, with all memory access through a checked boundary
|
|
18
|
+
(exe hash, mapped regions, write allowlist).
|
|
19
|
+
- UCP3 module `crusader-bridge` and a Docker setup that uses your own game files.
|
|
20
|
+
- Command reference, concepts and protocol docs, plus `llms.txt` and `llms-full.txt`.
|
|
21
|
+
|
|
22
|
+
### Known limitations
|
|
23
|
+
- Tested offline only; the first live run against the game is pending.
|
|
24
|
+
|
|
25
|
+
[Unreleased]: https://github.com/smwitkowski/crusader-sdk/compare/v0.1.0a1...HEAD
|
|
26
|
+
[0.1.0a1]: https://github.com/smwitkowski/crusader-sdk/releases/tag/v0.1.0a1
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Issues are welcome for bug reports, questions, and suggestions. Please include steps to reproduce bugs and relevant environment details. Do not post security vulnerabilities publicly; follow [SECURITY.md](SECURITY.md).
|
|
4
|
+
|
|
5
|
+
Pull requests are by invitation. Please open an issue and discuss your proposed change before starting a pull request; wait for a maintainer to invite one.
|
|
6
|
+
|
|
7
|
+
## Local checks
|
|
8
|
+
|
|
9
|
+
From the repository root, install Python 3.10 or later and [uv](https://docs.astral.sh/uv/). Run these checks before submitting an invited pull request:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
uv run ruff check
|
|
13
|
+
uv run ruff format --check
|
|
14
|
+
uv run pyright
|
|
15
|
+
uv run pytest -q
|
|
16
|
+
sh scripts/leakcheck.sh
|
|
17
|
+
uv run python tools/gen_docs.py --check
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Maintainers updating GitHub Actions pins should inspect each pinned revision’s `action.yml` and validate its inputs. With the GitHub CLI (`gh`) installed and authenticated, run `scripts/check-action-pins.sh` to verify that every remote workflow action pin resolves to a commit. This requires GitHub API access and does not verify release tags or attestations.
|
|
21
|
+
|
|
22
|
+
If `actionlint` is installed, also run `actionlint`. Optionally, run `uv run pre-commit run --all-files` for the configured hooks.
|
|
23
|
+
|
|
24
|
+
Do not include game files, credentials, or other material you cannot redistribute.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Legal notice
|
|
2
|
+
|
|
3
|
+
This is an unofficial project and is not affiliated with or endorsed by Firefly Studios. It requires your own legal copy of Stronghold Crusader HD v1.41, not the Definitive Edition. No game files are distributed. The software reads and writes game memory and is intended for offline single-player use only.
|
|
4
|
+
|
|
5
|
+
The screenshot in the README is from Stronghold Crusader, © Firefly Studios, captured from the author's own copy of the game while driven through this SDK. It is used for illustration only.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stephen Witkowski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: crusader-sdk
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: A Python SDK for Stronghold Crusader.
|
|
5
|
+
Author: Stephen Witkowski
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Provides-Extra: bridge
|
|
10
|
+
Requires-Dist: mcp==2.3.0; extra == 'bridge'
|
|
11
|
+
Requires-Dist: starlette; extra == 'bridge'
|
|
12
|
+
Requires-Dist: uvicorn; extra == 'bridge'
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: httpx; extra == 'dev'
|
|
15
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
16
|
+
Requires-Dist: pyright; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
19
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# Crusader SDK
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
<sub>Gameplay screenshot from Stronghold Crusader, © Firefly Studios, captured from a game driven through this SDK.</sub>
|
|
27
|
+
|
|
28
|
+
[](https://github.com/smwitkowski/crusader-sdk/actions/workflows/ci.yml)
|
|
29
|
+
[](LICENSE)
|
|
30
|
+
[](https://docs.astral.sh/ruff/)
|
|
31
|
+
[](https://github.com/microsoft/pyright)
|
|
32
|
+
[](https://pypi.org/project/crusader-sdk/)
|
|
33
|
+
[](https://pypi.org/project/crusader-sdk/)
|
|
34
|
+
[](https://codecov.io/gh/smwitkowski/crusader-sdk)
|
|
35
|
+
|
|
36
|
+
A Python SDK for observing and controlling Stronghold Crusader through a local bridge. Read game state, inspect the map, and issue commands from Python.
|
|
37
|
+
|
|
38
|
+
Unofficial; not affiliated with or endorsed by Firefly Studios. You must provide your own supported game files. See [LEGAL.md](LEGAL.md).
|
|
39
|
+
|
|
40
|
+
## Client example
|
|
41
|
+
|
|
42
|
+
With a bridge running (see [Get started](#get-started)), play a month of the game from Python:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from crusader import CrusaderError, Game
|
|
46
|
+
|
|
47
|
+
game = Game.connect() # CRUSADER_URL and CRUSADER_TOKEN are read from the environment
|
|
48
|
+
print(game.state()["gold"], "gold at tick", game.state()["tick"])
|
|
49
|
+
|
|
50
|
+
site = game.find_sites("granary", limit=1)[0] # the game's own placement check
|
|
51
|
+
game.build("granary", site["x"], site["y"])
|
|
52
|
+
game.set_tax(2)
|
|
53
|
+
|
|
54
|
+
result = game.advance(800) # one game month (800 ticks); the game is paused otherwise
|
|
55
|
+
for event in result["events"]:
|
|
56
|
+
print(event["type"], event.get("message", ""))
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
game.set_tax(42)
|
|
60
|
+
except CrusaderError as exc: # refusals come back as errors, like the game's UI
|
|
61
|
+
print("refused:", exc)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## How it works
|
|
65
|
+
|
|
66
|
+
```mermaid
|
|
67
|
+
flowchart TB
|
|
68
|
+
A["Your code or agent"] -->|"Python SDK, REST or MCP"| B["Bridge server"]
|
|
69
|
+
B -->|"reads game memory"| G["Stronghold Crusader 1.41<br/>under Wine"]
|
|
70
|
+
B -->|"queues commands, controls ticks"| U["UCP3 module inside the game"]
|
|
71
|
+
U -->|"the game's own command queue"| G
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Commands go through the same command queue the game's UI uses, so the game applies its own costs and rules. The game
|
|
75
|
+
stays paused between calls; time moves only when you call `advance`. Details: [how it works](docs/how-it-works.md).
|
|
76
|
+
The bridge and Docker setup have been tested offline; a live run against the game is still pending.
|
|
77
|
+
|
|
78
|
+
## Get started
|
|
79
|
+
|
|
80
|
+
1. Supply your own supported game files and [set up the bridge](docs/quickstart.md#run-the-bridge).
|
|
81
|
+
2. [Install the client](docs/quickstart.md#install-the-client): `pip install --pre crusader-sdk` (pre-release for now).
|
|
82
|
+
3. Connect to the running bridge and [try the client](docs/quickstart.md#install-the-client).
|
|
83
|
+
|
|
84
|
+
See the [quickstart](docs/quickstart.md) for bridge setup, requirements, and the experimental Docker option.
|
|
85
|
+
|
|
86
|
+
## Explore
|
|
87
|
+
|
|
88
|
+
| | |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| [Documentation](docs/index.md) | [API reference](docs/api.md) |
|
|
91
|
+
| [LLM guide](llms.txt) | [Protocol](docs/protocol.md) |
|
|
92
|
+
| [How it works](docs/how-it-works.md) | [Contributing](CONTRIBUTING.md) |
|
|
93
|
+
| [Security](SECURITY.md) | [Legal](LEGAL.md) |
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Crusader SDK
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
<sub>Gameplay screenshot from Stronghold Crusader, © Firefly Studios, captured from a game driven through this SDK.</sub>
|
|
6
|
+
|
|
7
|
+
[](https://github.com/smwitkowski/crusader-sdk/actions/workflows/ci.yml)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://docs.astral.sh/ruff/)
|
|
10
|
+
[](https://github.com/microsoft/pyright)
|
|
11
|
+
[](https://pypi.org/project/crusader-sdk/)
|
|
12
|
+
[](https://pypi.org/project/crusader-sdk/)
|
|
13
|
+
[](https://codecov.io/gh/smwitkowski/crusader-sdk)
|
|
14
|
+
|
|
15
|
+
A Python SDK for observing and controlling Stronghold Crusader through a local bridge. Read game state, inspect the map, and issue commands from Python.
|
|
16
|
+
|
|
17
|
+
Unofficial; not affiliated with or endorsed by Firefly Studios. You must provide your own supported game files. See [LEGAL.md](LEGAL.md).
|
|
18
|
+
|
|
19
|
+
## Client example
|
|
20
|
+
|
|
21
|
+
With a bridge running (see [Get started](#get-started)), play a month of the game from Python:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from crusader import CrusaderError, Game
|
|
25
|
+
|
|
26
|
+
game = Game.connect() # CRUSADER_URL and CRUSADER_TOKEN are read from the environment
|
|
27
|
+
print(game.state()["gold"], "gold at tick", game.state()["tick"])
|
|
28
|
+
|
|
29
|
+
site = game.find_sites("granary", limit=1)[0] # the game's own placement check
|
|
30
|
+
game.build("granary", site["x"], site["y"])
|
|
31
|
+
game.set_tax(2)
|
|
32
|
+
|
|
33
|
+
result = game.advance(800) # one game month (800 ticks); the game is paused otherwise
|
|
34
|
+
for event in result["events"]:
|
|
35
|
+
print(event["type"], event.get("message", ""))
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
game.set_tax(42)
|
|
39
|
+
except CrusaderError as exc: # refusals come back as errors, like the game's UI
|
|
40
|
+
print("refused:", exc)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## How it works
|
|
44
|
+
|
|
45
|
+
```mermaid
|
|
46
|
+
flowchart TB
|
|
47
|
+
A["Your code or agent"] -->|"Python SDK, REST or MCP"| B["Bridge server"]
|
|
48
|
+
B -->|"reads game memory"| G["Stronghold Crusader 1.41<br/>under Wine"]
|
|
49
|
+
B -->|"queues commands, controls ticks"| U["UCP3 module inside the game"]
|
|
50
|
+
U -->|"the game's own command queue"| G
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Commands go through the same command queue the game's UI uses, so the game applies its own costs and rules. The game
|
|
54
|
+
stays paused between calls; time moves only when you call `advance`. Details: [how it works](docs/how-it-works.md).
|
|
55
|
+
The bridge and Docker setup have been tested offline; a live run against the game is still pending.
|
|
56
|
+
|
|
57
|
+
## Get started
|
|
58
|
+
|
|
59
|
+
1. Supply your own supported game files and [set up the bridge](docs/quickstart.md#run-the-bridge).
|
|
60
|
+
2. [Install the client](docs/quickstart.md#install-the-client): `pip install --pre crusader-sdk` (pre-release for now).
|
|
61
|
+
3. Connect to the running bridge and [try the client](docs/quickstart.md#install-the-client).
|
|
62
|
+
|
|
63
|
+
See the [quickstart](docs/quickstart.md) for bridge setup, requirements, and the experimental Docker option.
|
|
64
|
+
|
|
65
|
+
## Explore
|
|
66
|
+
|
|
67
|
+
| | |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| [Documentation](docs/index.md) | [API reference](docs/api.md) |
|
|
70
|
+
| [LLM guide](llms.txt) | [Protocol](docs/protocol.md) |
|
|
71
|
+
| [How it works](docs/how-it-works.md) | [Contributing](CONTRIBUTING.md) |
|
|
72
|
+
| [Security](SECURITY.md) | [Legal](LEGAL.md) |
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Please report vulnerabilities privately using GitHub private vulnerability reporting for this repository. Do not disclose security vulnerabilities in public issues.
|
|
4
|
+
|
|
5
|
+
## Threat model
|
|
6
|
+
|
|
7
|
+
The bridge is a local, single-user tool that reads and writes game process memory. Keep it on loopback, set `CRUSADER_TOKEN`, and do not expose it to untrusted networks. Host, Origin, and JSON content-type checks reduce browser-based attacks; they do not protect against a compromised local process.
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
FROM ghcr.io/astral-sh/uv:0.13.7@sha256:96f40d3904aad8d0bd010539001d36c8e2188b981e1334471a90c4a1d93bb490 AS uv
|
|
2
|
+
|
|
3
|
+
FROM debian:bookworm-slim@sha256:734a72b3d6142a71993b4bc580ca01d8ca687f77a2c422d96a961d3376b179bc
|
|
4
|
+
|
|
5
|
+
# Wine and the game are 32-bit; build on the amd64 platform.
|
|
6
|
+
# hadolint ignore=DL3008
|
|
7
|
+
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y --no-install-recommends \
|
|
8
|
+
ca-certificates curl unzip wine wine32:i386 libgl1:i386 libgl1-mesa-dri:i386 \
|
|
9
|
+
libglx-mesa0:i386 xvfb xdotool x11-utils procps python3 python3-venv \
|
|
10
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
11
|
+
|
|
12
|
+
RUN curl -fsSL -o /tmp/ucp.zip \
|
|
13
|
+
https://github.com/UnofficialCrusaderPatch/UnofficialCrusaderPatch3/releases/download/v3.0.7/UCP3-3.0.7-77c6accf14.zip \
|
|
14
|
+
&& printf "%s\n" "a979816e4475e054b46b2f2793ef6a2109cfd03bf650e58e2b2f764804a8c105 /tmp/ucp.zip" > /tmp/ucp.sha256 \
|
|
15
|
+
&& sha256sum -c /tmp/ucp.sha256 && rm /tmp/ucp.sha256 \
|
|
16
|
+
&& mkdir /opt/ucp && unzip -q /tmp/ucp.zip -d /opt/ucp && rm /tmp/ucp.zip
|
|
17
|
+
|
|
18
|
+
WORKDIR /opt/sdk
|
|
19
|
+
COPY --from=uv /uv /usr/local/bin/uv
|
|
20
|
+
COPY pyproject.toml uv.lock README.md ./
|
|
21
|
+
COPY src ./src
|
|
22
|
+
RUN uv export --frozen --extra bridge --no-dev --no-emit-project -o /tmp/requirements.txt \
|
|
23
|
+
&& python3 -m venv /opt/venv \
|
|
24
|
+
&& /opt/venv/bin/pip install --no-cache-dir --no-deps --require-hashes -r /tmp/requirements.txt \
|
|
25
|
+
&& /opt/venv/bin/pip install --no-cache-dir --no-deps . \
|
|
26
|
+
&& rm /tmp/requirements.txt
|
|
27
|
+
COPY ucp3/crusader-bridge-0.1.0 /opt/sdk/ucp3/crusader-bridge-0.1.0
|
|
28
|
+
COPY docker/ucp-config.yml /opt/sdk/ucp-config.yml
|
|
29
|
+
COPY scripts/check-game-files.sh /opt/sdk/check-game-files.sh
|
|
30
|
+
COPY docker/run.sh /opt/sdk/run.sh
|
|
31
|
+
RUN useradd --create-home --uid 1000 crusader && mkdir /opt/crusader && chown crusader:crusader /opt/crusader
|
|
32
|
+
USER crusader
|
|
33
|
+
ENV HOME=/home/crusader WINEPREFIX=/home/crusader/.wine WINEARCH=win32 WINEDEBUG=-all DISPLAY=:99
|
|
34
|
+
ENV CRUSADER_GAME_DIR=/opt/crusader
|
|
35
|
+
ENTRYPOINT ["/bin/sh", "/opt/sdk/run.sh"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
# Verify the mounted source before copying and verify the copy before launch.
|
|
5
|
+
sh /opt/sdk/check-game-files.sh /game
|
|
6
|
+
cp -R /game/. /opt/crusader/
|
|
7
|
+
chmod -R u+rwX /opt/crusader
|
|
8
|
+
sh /opt/sdk/check-game-files.sh /opt/crusader
|
|
9
|
+
mkdir -p "$HOME/Stronghold Crusader/Saves"
|
|
10
|
+
if [ -d /game/Saves ]; then
|
|
11
|
+
cp -R /game/Saves/. "$HOME/Stronghold Crusader/Saves/"
|
|
12
|
+
chmod -R u+rwX "$HOME/Stronghold Crusader/Saves"
|
|
13
|
+
fi
|
|
14
|
+
cp -R /opt/ucp/. /opt/crusader/
|
|
15
|
+
mv /opt/crusader/binkw32.dll /opt/crusader/binkw32_real.dll
|
|
16
|
+
cp /opt/crusader/binkw32_ucp.dll /opt/crusader/binkw32.dll
|
|
17
|
+
cp /opt/sdk/ucp-config.yml /opt/crusader/ucp-config.yml
|
|
18
|
+
cp -R /opt/sdk/ucp3/crusader-bridge-0.1.0 /opt/crusader/ucp/modules/
|
|
19
|
+
cd /opt/crusader
|
|
20
|
+
Xvfb :99 -screen 0 1024x768x16 &
|
|
21
|
+
xvfb_pid=$!
|
|
22
|
+
attempt=0
|
|
23
|
+
until xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; do
|
|
24
|
+
if ! kill -0 "$xvfb_pid" 2>/dev/null || [ "$attempt" -ge 30 ]; then
|
|
25
|
+
echo "Xvfb failed to start" >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
attempt=$((attempt + 1))
|
|
29
|
+
sleep 1
|
|
30
|
+
done
|
|
31
|
+
wineboot -i
|
|
32
|
+
wineserver -w
|
|
33
|
+
wine reg add 'HKCU\Software\Wine\Drivers' /v Audio /d '' /f
|
|
34
|
+
wineserver -w
|
|
35
|
+
wine 'Stronghold Crusader.exe' --ucp-no-security &
|
|
36
|
+
exec /opt/venv/bin/crusader-bridge "$@"
|
|
File without changes
|