A python plugin loader and runtime for AllayMC using GraalPython, inspired by Endstone.
./gradlew shadowJarThis produces build/libs/AllayStone-<version>-shaded.jar.
- Copy the shaded jar into the Allay server
plugins/directory. - Start the server.
AllayStone manages a Python prefix at plugins/.local.
- Wheel files copied into
plugins/*.whlare installed intoplugins/.localautomatically during startup. - Editable installs are also supported:
python -m pip install -e <plugin-project> --prefix <server>/plugins/.localFor ./gradlew runServer, the managed prefix is build/run/plugins/.local.
Stop the server before removing a Python plugin.
- Wheel install: delete the matching
.whlfile fromplugins/. AllayStone will remove that plugin and any now-unreferenced installed dependencies fromplugins/.localon the next startup. - Editable install: uninstall from the managed prefix.
& { $env:PYTHONPATH = "<server>/plugins/.local/Lib/site-packages"; try { python -m pip uninstall allaystone-<plugin-name> } finally { Remove-Item Env:PYTHONPATH -ErrorAction SilentlyContinue } }For ./gradlew runServer, replace <server>/plugins/.local with build/run/plugins/.local.
Python plugins are regular Python distributions.
- Distribution name:
allaystone-<plugin-name> - Exactly one
[project.entry-points.allaystone]entry - Entry point class must inherit from
allaystone.Plugin
allaystone.Plugin exposes:
- metadata fields such as
version,api_version,description,authors,website,depend, andsoft_depend - lifecycle methods
on_load,on_enable, andon_disable - runtime fields injected by AllayStone:
server,logger,data_folder,name, andjava_plugin
/plugin reload <name>
/plugin reloadall
Reloading does this:
- call the current instance's
on_disable() - rebuild the plugin's GraalPy context
- create a new Python plugin instance
- call
on_load()andon_enable()on the new instance
- Wheel plugins are reinstalled from
plugins/*.whlbefore the new context is created. - Editable plugins are reloaded from the current source tree already linked into
plugins/.local. - If reload fails after
on_disable(), the plugin stays disabled until it is reloaded successfully or the server is restarted. - Metadata changes such as
name,version,api_version, and dependencies still require a full restart. - Plugin cleanup is still the plugin author's responsibility. Unregister listeners, commands, and long-lived tasks in
on_disable().
Use the template repository to start a new Python plugin:
https://github.com/smartcmd/AllayStoneTemplate
Typical workflow:
git clone https://github.com/smartcmd/AllayStoneTemplate.git
cd AllayStoneTemplate
python -m pip wheel . --no-deps --wheel-dir dist- Wheel: copy
dist/*.whlinto the serverplugins/directory. - Editable:
python -m pip install -e . --prefix <server>/plugins/.localAllayStone also generates and bundles:
allay.apiPython stubs- the
allaystonehelper package used by Python plugins
Build the local stub package:
./gradlew preparePythonStubPackage
cd build/generated/python-stub-package
python -m pip install -e .GitHub Releases also publish a wheel for the generated stubs:
python -m pip install https://github.com/smartcmd/AllayStone/releases/download/<tag>/<wheel-file>.whlThis wheel is for editor support and GraalPy interop. In normal CPython, importing allay.api.* at runtime will fail because the generated package uses GraalPy java.type() bindings.