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.
Files changed (77) hide show
  1. crusader_sdk-0.1.0a1/.dockerignore +19 -0
  2. crusader_sdk-0.1.0a1/.github/ISSUE_TEMPLATE/bug_report.yml +36 -0
  3. crusader_sdk-0.1.0a1/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. crusader_sdk-0.1.0a1/.github/ISSUE_TEMPLATE/feature_request.yml +16 -0
  5. crusader_sdk-0.1.0a1/.github/dependabot.yml +14 -0
  6. crusader_sdk-0.1.0a1/.github/release.yml +15 -0
  7. crusader_sdk-0.1.0a1/.github/workflows/ci.yml +65 -0
  8. crusader_sdk-0.1.0a1/.github/workflows/release.yml +28 -0
  9. crusader_sdk-0.1.0a1/.gitignore +19 -0
  10. crusader_sdk-0.1.0a1/.pre-commit-config.yaml +20 -0
  11. crusader_sdk-0.1.0a1/ACKNOWLEDGEMENTS.md +3 -0
  12. crusader_sdk-0.1.0a1/CHANGELOG.md +26 -0
  13. crusader_sdk-0.1.0a1/CONTRIBUTING.md +24 -0
  14. crusader_sdk-0.1.0a1/LEGAL.md +5 -0
  15. crusader_sdk-0.1.0a1/LICENSE +21 -0
  16. crusader_sdk-0.1.0a1/PKG-INFO +93 -0
  17. crusader_sdk-0.1.0a1/README.md +72 -0
  18. crusader_sdk-0.1.0a1/SECURITY.md +7 -0
  19. crusader_sdk-0.1.0a1/assets/.gitkeep +0 -0
  20. crusader_sdk-0.1.0a1/assets/hero.jpg +0 -0
  21. crusader_sdk-0.1.0a1/assets/mark.png +0 -0
  22. crusader_sdk-0.1.0a1/assets/social-preview.jpg +0 -0
  23. crusader_sdk-0.1.0a1/docker/.gitkeep +0 -0
  24. crusader_sdk-0.1.0a1/docker/Dockerfile +35 -0
  25. crusader_sdk-0.1.0a1/docker/run.sh +36 -0
  26. crusader_sdk-0.1.0a1/docker/ucp-config.yml +15 -0
  27. crusader_sdk-0.1.0a1/docs/.gitkeep +0 -0
  28. crusader_sdk-0.1.0a1/docs/api.md +1278 -0
  29. crusader_sdk-0.1.0a1/docs/commands/allies.md +49 -0
  30. crusader_sdk-0.1.0a1/docs/commands/defences.md +103 -0
  31. crusader_sdk-0.1.0a1/docs/commands/economy.md +153 -0
  32. crusader_sdk-0.1.0a1/docs/commands/military.md +138 -0
  33. crusader_sdk-0.1.0a1/docs/commands/observe.md +121 -0
  34. crusader_sdk-0.1.0a1/docs/commands/siege.md +81 -0
  35. crusader_sdk-0.1.0a1/docs/commands/time.md +15 -0
  36. crusader_sdk-0.1.0a1/docs/concepts.md +15 -0
  37. crusader_sdk-0.1.0a1/docs/how-it-works.md +11 -0
  38. crusader_sdk-0.1.0a1/docs/index.md +11 -0
  39. crusader_sdk-0.1.0a1/docs/protocol.md +20 -0
  40. crusader_sdk-0.1.0a1/docs/quickstart.md +65 -0
  41. crusader_sdk-0.1.0a1/docs/reference/buildings.md +78 -0
  42. crusader_sdk-0.1.0a1/docs/reference/errors.md +794 -0
  43. crusader_sdk-0.1.0a1/docs/reference/events.md +15 -0
  44. crusader_sdk-0.1.0a1/docs/reference/goods.md +29 -0
  45. crusader_sdk-0.1.0a1/docs/reference/map-legend.md +17 -0
  46. crusader_sdk-0.1.0a1/docs/reference/state.md +27 -0
  47. crusader_sdk-0.1.0a1/docs/reference/units.md +29 -0
  48. crusader_sdk-0.1.0a1/llms-full.txt +3103 -0
  49. crusader_sdk-0.1.0a1/llms.txt +26 -0
  50. crusader_sdk-0.1.0a1/pyproject.toml +40 -0
  51. crusader_sdk-0.1.0a1/scripts/build-image.sh +5 -0
  52. crusader_sdk-0.1.0a1/scripts/check-action-pins.sh +30 -0
  53. crusader_sdk-0.1.0a1/scripts/check-game-files.sh +41 -0
  54. crusader_sdk-0.1.0a1/scripts/leakcheck.sh +45 -0
  55. crusader_sdk-0.1.0a1/src/crusader/__init__.py +276 -0
  56. crusader_sdk-0.1.0a1/src/crusader/bridge/__init__.py +1 -0
  57. crusader_sdk-0.1.0a1/src/crusader/bridge/game.py +3323 -0
  58. crusader_sdk-0.1.0a1/src/crusader/bridge/memory.py +260 -0
  59. crusader_sdk-0.1.0a1/src/crusader/bridge/server.py +466 -0
  60. crusader_sdk-0.1.0a1/src/crusader/commands.py +700 -0
  61. crusader_sdk-0.1.0a1/src/crusader/types.py +152 -0
  62. crusader_sdk-0.1.0a1/tests/test_audit_low.py +80 -0
  63. crusader_sdk-0.1.0a1/tests/test_client.py +114 -0
  64. crusader_sdk-0.1.0a1/tests/test_contract.py +31 -0
  65. crusader_sdk-0.1.0a1/tests/test_docs.py +23 -0
  66. crusader_sdk-0.1.0a1/tests/test_game.py +221 -0
  67. crusader_sdk-0.1.0a1/tests/test_gen_docs.py +64 -0
  68. crusader_sdk-0.1.0a1/tests/test_leakcheck.py +78 -0
  69. crusader_sdk-0.1.0a1/tests/test_memory.py +172 -0
  70. crusader_sdk-0.1.0a1/tests/test_server.py +376 -0
  71. crusader_sdk-0.1.0a1/tests/test_smoke.py +7 -0
  72. crusader_sdk-0.1.0a1/tools/.gitkeep +0 -0
  73. crusader_sdk-0.1.0a1/tools/gen_docs.py +241 -0
  74. crusader_sdk-0.1.0a1/tools/smoke.py +80 -0
  75. crusader_sdk-0.1.0a1/ucp3/crusader-bridge-0.1.0/definition.yml +6 -0
  76. crusader_sdk-0.1.0a1/ucp3/crusader-bridge-0.1.0/init.lua +363 -0
  77. 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,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Report a security problem
4
+ url: https://github.com/smwitkowski/crusader-sdk/security/advisories/new
5
+ about: Please report vulnerabilities privately.
@@ -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,19 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .pyright/
7
+ .coverage
8
+ htmlcov/
9
+ dist/
10
+ build/
11
+ .venv/
12
+ .env
13
+ .env.*
14
+ *.exe
15
+ *.dll
16
+ *.sav
17
+ games/
18
+ log.key
19
+ scripts/leakcheck-denylist.txt
@@ -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,3 @@
1
+ # Acknowledgements
2
+
3
+ Thanks to the OpenSHC and UCP3 projects and to Wine and its contributors.
@@ -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
+ ![Stronghold Crusader gameplay: a catapult firing across an oasis during a siege, driven through the SDK](assets/hero.jpg)
25
+
26
+ <sub>Gameplay screenshot from Stronghold Crusader, © Firefly Studios, captured from a game driven through this SDK.</sub>
27
+
28
+ [![CI](https://github.com/smwitkowski/crusader-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/smwitkowski/crusader-sdk/actions/workflows/ci.yml)
29
+ [![License](https://img.shields.io/github/license/smwitkowski/crusader-sdk)](LICENSE)
30
+ [![Ruff](https://img.shields.io/badge/lint-ruff-blue)](https://docs.astral.sh/ruff/)
31
+ [![Pyright](https://img.shields.io/badge/type%20checking-pyright-blue)](https://github.com/microsoft/pyright)
32
+ [![PyPI](https://img.shields.io/pypi/v/crusader-sdk)](https://pypi.org/project/crusader-sdk/)
33
+ [![Python versions](https://img.shields.io/pypi/pyversions/crusader-sdk)](https://pypi.org/project/crusader-sdk/)
34
+ [![Codecov](https://codecov.io/gh/smwitkowski/crusader-sdk/graph/badge.svg)](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
+ ![Stronghold Crusader gameplay: a catapult firing across an oasis during a siege, driven through the SDK](assets/hero.jpg)
4
+
5
+ <sub>Gameplay screenshot from Stronghold Crusader, © Firefly Studios, captured from a game driven through this SDK.</sub>
6
+
7
+ [![CI](https://github.com/smwitkowski/crusader-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/smwitkowski/crusader-sdk/actions/workflows/ci.yml)
8
+ [![License](https://img.shields.io/github/license/smwitkowski/crusader-sdk)](LICENSE)
9
+ [![Ruff](https://img.shields.io/badge/lint-ruff-blue)](https://docs.astral.sh/ruff/)
10
+ [![Pyright](https://img.shields.io/badge/type%20checking-pyright-blue)](https://github.com/microsoft/pyright)
11
+ [![PyPI](https://img.shields.io/pypi/v/crusader-sdk)](https://pypi.org/project/crusader-sdk/)
12
+ [![Python versions](https://img.shields.io/pypi/pyversions/crusader-sdk)](https://pypi.org/project/crusader-sdk/)
13
+ [![Codecov](https://codecov.io/gh/smwitkowski/crusader-sdk/graph/badge.svg)](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
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 "$@"
@@ -0,0 +1,15 @@
1
+ meta:
2
+ version: 1.0.0
3
+ active: true
4
+ config-full:
5
+ load-order:
6
+ - extension: crusader-bridge
7
+ version: 0.1.0
8
+ modules:
9
+ crusader-bridge:
10
+ config: {}
11
+ plugins: {}
12
+ config-sparse:
13
+ load-order: []
14
+ modules: {}
15
+ plugins: {}
File without changes