Owner relaxed the asset side of the content-free rule — assets in this repo are free to use — so the engine now ships its generated HUD icons as defaults instead of leaving blank slots. Two wins: the HUD is iconed out of the box, and the icons render in Studio's EDIT view (no Play needed), so you can author/tune the HUD and see the real thing — or edit it straight from the Explorer. - StatDefs.luau: each of the 7 stats carries an `icon` default. Putting it here (not as a per-bar HUD attribute) keeps the admin-panel/config override working — a per-bar attribute would shadow it. resolveIcon: per-bar attr > config/def.icon (now the shipped id) > Assets > "". - assets/hud/SurvivalHud.model.json: bake each bar's child `Icon` ImageLabel (Image + Visible=true) so it shows in edit mode; the Credits counter (no StatDefs entry) also gets a per-bar `Icon` attribute to drive its runtime resolution. - Retire demo/client/HudIcons.client.luau (+ its demo.project.json mount): the runtime icon-assigning script is now redundant. - Relax the docs/PR-template "content-free" wording to "design-free": free default ART may ship (overridable per stat); game-specific DESIGN (items/recipes/lore) still never ships. Updated design-language, survival-stats, CONTRIBUTING. - CHANGELOG: record default icons + add the missing admin-plugin entry; note CI now builds all three Rojo targets. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11 KiB
Contributing to SurvivorCore
Thanks for your interest in contributing to SurvivorCore! This guide will help you get set up and understand how the project is organized.
SurvivorCore is an engine of mechanics, not content. Most of what makes a survival game yours — items, world, art, lore — lives in your own game and plugs in through the engine's two extension layers. Contributions to this repo are about the engine: registries, components, hooks, and the foundation. See Architecture Overview.
Prerequisites
- Roblox Studio — to serve the project into and play-test.
- The Rojo Studio plugin, version
7.6.1— pin it to match the CLI the project uses (seerokit.toml). A mismatched plugin can fail to sync. Install from the Rojo plugin page or viarojo plugin install. - Rokit — the toolchain manager. It installs the
exact pinned versions of
rojo,wally,stylua,selene, andluau-lspfromrokit.toml. - Wally — the Roblox package manager (installed by Rokit). The engine
ships with no dependencies today, but
wally installkeeps you forward-compatible. - Git — for cloning and version control.
Local Development Setup
-
Fork & clone the repo:
git clone https://github.com/<you>/SurvivorCore.git cd SurvivorCore -
Install the toolchain (reads
rokit.toml):rokit install -
Install packages (no-op until
wally.tomlgains dependencies, but get in the habit):wally install -
Serve into Studio. Open a place in Studio (an empty baseplate is fine), make sure the Rojo plugin is connected, then in your terminal:
rojo serve demo.project.jsonClick Connect in the Rojo plugin. This mounts the engine at
ReplicatedStorage.SurvivorCoreand the demo boot script inServerScriptService. -
Run the demo place. Press Play. The demo registers a couple of items and a recipe, then watches for creator-owned
Gatherableobjects. To try the component layer: add aPartto the Workspace, tag itGatherable(CollectionService), set the attributesItemId="reed",Yield=2,HP=3, then play and interact with the prompt.
Working on the engine alone (not the demo)?
rojo serve default.project.jsonmounts justsrcas theSurvivorCoremodel.
Studio + Rojo gotcha — restart before trusting a Play test. When you change scripts during a
rojo servesession, Studio updates each script'sSourcein the Edit datamodel, but Play Solo can run cached old bytecode — so your change silently doesn't take effect on Play. If a fix isn't showing up, restart Studio (clears the script cache) and reconnect, or test from a fresh build:rojo build demo.project.json -o /tmp/demo.rbxlxand open that file.
Code Style
- Luau, typed where practical. Use
--!stricton new modules when the types are clean; fall back to the project default otherwise. Preferexport typefor public shapes. - Formatting & linting are enforced by CI. Before you push, run the same checks CI does
(see Making a Pull Request).
styluaowns formatting — don't hand-format around it. - Default art is fine; keep it swappable. Assets in this repo are free to use, so the engine
may ship free default art IDs (e.g. the HUD stat icons in
StatDefs) — provided they stay overridable (config / admin plugin /Assets) and never get buried in logic. For dynamic or owner-supplied art, still route through theAssetsregistry (Assets.register("Sounds", "Harvest", "rbxassetid://…")) with the empty-string fallback. - Keep the core free of game-specific design. No concrete items, recipes, lore, world
strings, or instance-name string matches (
name == "campfire") in the engine — free default art is the one exception (above). Content enters two ways:- Registries — developers call
register()from code (Items,Recipes,Stats,Mobs, …). - Components — creators tag their own objects and set Attributes (
Gatherable, and the component family that follows).
- Registries — developers call
- Extend via Hooks, don't fork. Game-specific flourish (felling physics, station VFX,
custom drops) belongs in a
Hooks.on(...)handler in the game, not baked into the engine. If you need a new extension point, add aHooks.run("…")call and document it. - Mind the two layers. Engine code lives in
src/;demo/is an example consumer and may use concrete content freely (it stands in for a game).
Branching model
SurvivorCore uses two long-lived branches:
dev— the active development / integration branch. All code changes land here.main— the stable, released branch. It only moves when maintainers cut a release (by mergingdev→main) or for documentation-only changes.
In short: code → dev, docs → main.
- Code work (anything under
src/,demo/, workflows, toolchain/build config): fork, branch fromdev, and open your PR againstdev. - Documentation only (
README,docs/,CONTRIBUTING.md, code comments, typos): you may branch frommainand PR againstmain— apply theskip-changeloglabel.
Use branch prefixes: feat/*, fix/*, docs/* (e.g. feat/mob-registry).
main is the default branch, so a fresh PR targets main — retarget code PRs to dev. A
code PR left on main will be asked to retarget. Releases are cut by maintainers: dev is
merged into main (a merge commit, not a squash, so the branches stay in sync),
## Unreleased is promoted to the new version, and main is tagged + a GitHub Release is
published (which auto-posts to Discussions → Announcements).
Making a Pull Request
- Fork the repository on GitHub.
- Create a branch from the right base —
devfor code,mainfor docs-only:git checkout -b feat/my-feature dev # code work # git checkout -b docs/my-fix main # documentation only - Implement your change. Write clear, typed Luau. Add comments where the "why" isn't obvious. Keep the engine free of game-specific design (free default art is fine).
- Test locally. Play-test in Studio, then run the same checks CI does:
(
stylua --check src demo assets plugin selene . rojo sourcemap demo.project.json --output sourcemap.json curl -fsSL -o globalTypes.d.luau https://raw.githubusercontent.com/JohnnyMorganz/luau-lsp/main/scripts/globalTypes.d.luau luau-lsp analyze --sourcemap sourcemap.json --defs globalTypes.d.luau --no-strict-dm-types \ --ignore "Packages/**" --ignore "DevPackages/**" --ignore "ServerPackages/**" src demo assets/client # the admin plugin is a separate Rojo tree — analyze it with its own sourcemap rojo sourcemap plugin.project.json --output plugin-sourcemap.json luau-lsp analyze --sourcemap plugin-sourcemap.json --defs globalTypes.d.luau --no-strict-dm-types plugin rojo build default.project.json --output SurvivorCore.rbxm rojo build demo.project.json --output demo.rbxl rojo build plugin.project.json --output SurvivorCoreStatAdmin.rbxmstylua src demoauto-formats;sourcemap.json,globalTypes.d.luau, and the build outputs are git-ignored.) - Commit with a clear message describing what the change does and why:
git commit -m "Add Mobs registry kill-event schema" - Push and open a pull request against
dev(ormainfor documentation-only changes):git push origin feat/my-feature - In the PR description, explain what the change does, why it's needed, and how to test it in Studio. Add a clip/screenshot for anything visible.
Changelog (required). Every pull request must add an entry to CHANGELOG.md
under the ## Unreleased heading (create it if missing), grouped under
### Added / ### Changed / ### Fixed / ### Security, with (#N) referencing the issue or
PR. CI enforces this. If a change genuinely warrants no entry (a docs-only PR, a CI-config
tweak, a typo fix), apply the skip-changelog label to bypass the check. At release time,
## Unreleased is renamed to the new version.
Tracking staged fixes (fixed-pending-merge). When a PR implements the fix for an open
issue or a security alert, maintainers label it fixed-pending-merge (and the issue it
closes), so it's easy to see at a glance which problems are fixed and just waiting on a merge.
The label needs no cleanup: on merge the PR closes and any linked issue auto-closes via a
Closes #N reference.
Issue Templates
File issues with the forms in .github/ISSUE_TEMPLATE/:
- Bug report — include a clear repro (which project to serve and what to do in Studio),
expected behavior, the SurvivorCore version, your Roblox Studio version, your Rojo plugin
version, how you consume the engine (Rojo / Wally / drop-in
.rbxm), and any Output errors. - Feature request — describe a reusable engine capability (a registry, component, hook, or config section), the use case, and which layer it touches. Game-specific content belongs in your own game, not the engine.
Open-ended questions and "how do I…?" go to Discussions, not Issues.
Architecture Overview
SurvivorCore exposes two extension layers over a small foundation:
- Foundation (
src/foundation/) —Config,Assets,EventBridge,Hooks,Registry. - Registry layer (
src/registries/) — empty registries the game populates at startup (Items,Recipes,Stats,Achievements,Codex,Appearance,Mobs). - Component layer (
src/components/) — behaviors bound to a CollectionService tag and configured by per-instance Attributes (Gatherable, …).
For the full picture, read docs/architecture.md and the companion
Extending SurvivorCore guide. The engine/content boundary map (which
systems are being extracted from the production game The Counter
Earth into this engine) lives in that
game's repo at docs/survivorcore-boundary-map.md.
Code of Conduct
SurvivorCore is a small project, and we want to keep the community welcoming for everyone.
- Be kind. Assume good intent. Disagree respectfully.
- Be inclusive. Welcome newcomers. Avoid jargon without explanation.
- Be constructive. When reviewing code, suggest improvements rather than just pointing out problems. Explain why.
- No harassment, discrimination, or personal attacks. This includes issues, PRs, Discussions, and any project communication channels.
If someone's behavior makes you uncomfortable, reach out to the maintainers. We will address it.