- Core plugin code lives in
src/main/java/me/f0reach/vshop. - Runtime resources are in
src/main/resources:paper-plugin.ymlfor plugin metadataconfig.ymlfor plugin settingslang/messages_*.ymlfor localized text — keepmessages_en.ymlandmessages_ja.ymlin sync, and never print a domain enum withname(): route it throughlocale/EnumLabels, which readsenum.<kebab-class>.<kebab-value>
- Build output is generated under
build/(do not commit generated artifacts). - Documentation:
README.mdis the English project page (also used as the Modrinth description) — refresh it when commands, permissions, requirements or config defaults change.docs/guide/*.mdare the Japanese guides anddocs/guide/en/*.mdthe English ones. Both languages must stay in sync: a change to a Japanese guide requires the matching change indocs/guide/en/, and vice versa.- Guides quote UI button labels and error text. Take the wording from the matching locale file (
lang/messages_ja.yml/lang/messages_en.yml) rather than translating the other guide, so each guide matches what that locale actually renders. spec.mdstays Japanese and is the authoritative behavior spec — update it first when behavior changes, then the guides.
./gradlew buildcompiles Java 21 sources and creates the shaded plugin JAR../gradlew shadowJarbuilds only the fat JAR (used for server deployment)../gradlew runServerlaunches a local Paper 1.21 test server using the run-paper plugin../gradlew cleanremoves generated build outputs.
- Language: Java 21, UTF-8 source encoding.
- Indentation: 4 spaces, no tabs.
- Keep package names lowercase (
me.f0reach.vshop.*), class namesPascalCase, methods/fieldscamelCase, constantsUPPER_SNAKE_CASE. - Prefer small, focused classes by domain (e.g., repositories in
storage, UI concerns inui). - Keep YAML keys stable and descriptive; add new user-facing messages to both English and Japanese files when applicable.
- Tests live under
src/test/java. Stack is JUnit 5 + MockBukkit (mockbukkit-v1.21). - Storage tests follow a "contract per repository, two thin subclasses" pattern: the SQL-agnostic checks live in
me.f0reach.vshop.storage.<Repo>Contract, and each backend has aSqlite<Repo>Test/Mysql<Repo>Testsubclass that picks the data source viaAbstractRepositoryContract. MySQL subclasses are annotated@EnabledIfEnvironmentVariable("VSHOP_TEST_MYSQL_URL")so they skip cleanly when no MySQL is available. - Test naming:
<ClassName>Testand method names describing behavior (e.g.,createsShopWhenVillagerIsValid). - Default:
./gradlew testruns SQLite-backed tests only. Always run./gradlew buildbefore opening a PR. - Locally exercising MySQL tests: start MySQL (
docker run --rm -p 3307:3306 -e MYSQL_ROOT_PASSWORD=rootpw -e MYSQL_USER=vshop -e MYSQL_PASSWORD=vshop mysql:8.4), grant the userCREATE/DROPplusALLonvshop_test_%.*, and run withVSHOP_TEST_MYSQL_URL=jdbc:mysql://127.0.0.1:3307 VSHOP_TEST_MYSQL_USER=vshop VSHOP_TEST_MYSQL_PASSWORD=vshop ./gradlew test. CI does the same via.github/workflows/ci.ymland amysql:8.4service container.
- Commit format is Conventional Commits:
type(scope): short summary(example:feat(ui): add listing pagination controls). This is not cosmetic — release-please derives the version bump and the CHANGELOG from it, sofeatmeans a minor bump,fixa patch, and!or aBREAKING CHANGE:footer a major. - Because merges are squashed, the PR title becomes the commit message on
mainand must follow the same convention..github/workflows/pr-title.ymlenforces it. - Keep commits focused; avoid mixing refactors with feature work.
- PRs should include:
- What changed and why
- Related issue/ticket (if available)
- Validation steps (
./gradlew build, localrunServerchecks) - Screenshots/GIFs for UI or dialog flow changes
Releases are fully automated; nothing is bumped or tagged by hand.
- Merge PRs into
mainas usual..github/workflows/release.ymlruns release-please, which opens (or updates) a "chore: release x.y.z" PR containing the CHANGELOG entries and the new version ingradle.properties. - Review that PR — the CHANGELOG is a normal file, so hand-edit it there if the generated wording needs help.
- Merge it. release-please then creates the
vx.y.ztag and the GitHub release, and the same workflow builds and publishes to GitHub Packages and to Modrinth, attaches the shaded JAR to the GitHub release, and syncsREADME.mdto the Modrinth project description.
Notes:
- The version lives only in
gradle.properties, inside thex-release-please-start-versionblock.processResourcesexpands it intopaper-plugin.ymland Minotaur uses it as the Modrinth version number. - To force a specific version (e.g. going 0.x → 1.0.0), land an empty commit with a
Release-As: 1.0.0footer:git commit --allow-empty -m "chore: release 1.0.0" -m "Release-As: 1.0.0". - The Minecraft versions advertised on Modrinth come from
modrinth.gameVersionsingradle.properties. Keep them aligned with what BedrockDialog supports, since it is a hard dependency. - Because
README.mdis pushed as the Modrinth description, every link in it must be an absolute URL. - Re-publishing an existing tag (e.g. after a transient Modrinth failure): run the Release workflow manually with the tag name as input.
- Required repository setup: the
MODRINTH_TOKENsecret, and Settings → Actions → General → Allow GitHub Actions to create and approve pull requests.
- Never commit real database credentials or server secrets.
- Validate config defaults in
config.ymland ensure fail-safe behavior when values are missing/invalid.