mirror of
https://github.com/TemujinCalidius/SurvivorCore.git
synced 2026-08-14 09:02:29 +00:00
Select a Part or Model in Studio, answer "what is this object?", fill a form — it becomes a gatherable node, a mob or a quest giver. Closes the gap between authoring a def and setting up a world object, which until now meant knowing to tag a part and hand-typing PascalCase attributes in the property panel. Engine — components can declare an attribute SCHEMA: - src/components/Schema.luau (new): AttributeSpec/Display/ComponentSchema types, normalize/defaults/get/list, and the schemas for Gatherable, Mob, QuestGiver. Dependency-free ON PURPOSE: the plugin requires it live at edit time, and the component modules themselves can't be required there (Harvesting asserts IsServer; Remotes creates instances in ReplicatedStorage). - Components.define now accepts EITHER the legacy `attr = default` map or a schema array, normalizing both to one ordered spec list; bind() reads the derived default map, so binding is byte-identical. Legacy maps are sorted, as `pairs` order is arbitrary and would make a UI jitter. New getSchema/ listSchemas. The three shipped components pull name/tag/display/attributes from the schema; their onSetup bodies are untouched (defaults verified identical, all 23 attributes). Plugin — the Build page: - Field.luau (new): coerce/format/equalsDefault, lifted from ConfigAdmin (which now delegates), shared by every schema-driven editor. - FieldRow.luau (new): the shared [○/●] label … control + help row, including a ⌄ picker that cycles authored ids for fields declaring `ref`. - BuildAdmin.luau (new): live schema read with three distinct empty states, selection/eligibility/identify, deltas-only attribute writes, applyType (tag + clear any other component) and clear. - BuildAdminUi.luau (new): chooser cards, grouped form, multi-select apply, stale-bind-marker warning, SelectionChanged-driven refresh. - init.server.luau: record()-wrapped buildActions + the page. Docs: docs/admin-plugin.md Build section + a 60-second walkthrough, docs/extending.md schema guide, a CONTRIBUTING rule that new creator components declare one, CHANGELOG. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
221 lines
12 KiB
Markdown
221 lines
12 KiB
Markdown
# 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](#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 (see
|
|
`rokit.toml`). A mismatched plugin can fail to sync. Install from the
|
|
[Rojo plugin page](https://create.roblox.com/store/asset/13916111004/Rojo) or via
|
|
`rojo plugin install`.
|
|
- **[Rokit](https://github.com/rojo-rbx/rokit)** — the toolchain manager. It installs the
|
|
exact pinned versions of `rojo`, `wally`, `stylua`, `selene`, and `luau-lsp` from
|
|
`rokit.toml`.
|
|
- **[Wally](https://wally.run)** — the Roblox package manager (installed by Rokit). The engine
|
|
ships with no dependencies today, but `wally install` keeps you forward-compatible.
|
|
- **Git** — for cloning and version control.
|
|
|
|
## Local Development Setup
|
|
|
|
1. **Fork & clone the repo:**
|
|
```bash
|
|
git clone https://github.com/<you>/SurvivorCore.git
|
|
cd SurvivorCore
|
|
```
|
|
|
|
2. **Install the toolchain** (reads `rokit.toml`):
|
|
```bash
|
|
rokit install
|
|
```
|
|
|
|
3. **Install packages** (no-op until `wally.toml` gains dependencies, but get in the habit):
|
|
```bash
|
|
wally install
|
|
```
|
|
|
|
4. **Serve into Studio.** Open a place in Studio (an empty baseplate is fine), make sure the
|
|
Rojo plugin is connected, then in your terminal:
|
|
```bash
|
|
rojo serve demo.project.json
|
|
```
|
|
Click **Connect** in the Rojo plugin. This mounts the engine at
|
|
`ReplicatedStorage.SurvivorCore` and the demo boot script in `ServerScriptService`.
|
|
|
|
5. **Run the demo place.** Press **Play**. The demo registers a couple of items and a recipe,
|
|
then watches for creator-owned `Gatherable` objects. To try the component layer: add a
|
|
`Part` to the Workspace, tag it `Gatherable` (CollectionService), set the attributes
|
|
`ItemId="reed"`, `Yield=2`, `HP=3`, then play and interact with the prompt.
|
|
|
|
> Working on the engine alone (not the demo)? `rojo serve default.project.json` mounts just
|
|
> `src` as the `SurvivorCore` model.
|
|
|
|
> **Studio + Rojo gotcha — restart before trusting a Play test.** When you change scripts during a
|
|
> `rojo serve` session, Studio updates each script's `Source` in 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.rbxlx` and open that file.
|
|
|
|
## Code Style
|
|
|
|
- **Luau, typed where practical.** Use `--!strict` on new modules when the types are clean;
|
|
fall back to the project default otherwise. Prefer `export type` for public shapes.
|
|
- **Formatting & linting are enforced by CI.** Before you push, run the same checks CI does
|
|
(see [Making a Pull Request](#making-a-pull-request)). `stylua` owns 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 the `Assets` registry
|
|
(`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). **A new creator-facing component must declare an attribute
|
|
schema** (a `Schema.COMPONENTS` entry in `src/components/Schema.luau`, plus a `display` block),
|
|
so the admin plugin's **Build** page can render its setup form — no creator should have to
|
|
memorise attribute names. See [docs/extending.md](docs/extending.md#declaring-a-schema-so-the-builder-can-render-a-form).
|
|
- **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 a `Hooks.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
|
|
merging `dev` → `main`) or for **documentation-only** changes.
|
|
|
|
**In short: code → `dev`, docs → `main`.**
|
|
|
|
- **Code work** (anything under `src/`, `demo/`, workflows, toolchain/build config): fork,
|
|
branch from **`dev`**, and open your PR against **`dev`**.
|
|
- **Documentation only** (`README`, `docs/`, `CONTRIBUTING.md`, code comments, typos): you may
|
|
branch from **`main`** and PR against **`main`** — apply the `skip-changelog` label.
|
|
|
|
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
|
|
|
|
1. **Fork** the repository on GitHub.
|
|
2. **Create a branch from the right base** — `dev` for code, `main` for docs-only:
|
|
```bash
|
|
git checkout -b feat/my-feature dev # code work
|
|
# git checkout -b docs/my-fix main # documentation only
|
|
```
|
|
3. **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).
|
|
4. **Test locally.** Play-test in Studio, then run the same checks CI does:
|
|
```bash
|
|
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.rbxm
|
|
```
|
|
(`stylua src demo` auto-formats; `sourcemap.json`, `globalTypes.d.luau`, and the build
|
|
outputs are git-ignored.)
|
|
5. **Commit** with a clear message describing what the change does and why:
|
|
```bash
|
|
git commit -m "Add Mobs registry kill-event schema"
|
|
```
|
|
6. **Push** and open a pull request against **`dev`** (or `main` for documentation-only
|
|
changes):
|
|
```bash
|
|
git push origin feat/my-feature
|
|
```
|
|
7. 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`](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.
|
|
|
|
**Closing multiple issues from one PR.** Give each issue its own keyword —
|
|
`Closes #1, Closes #2` — not `Closes #1, #2`. GitHub only auto-links the number that directly
|
|
follows a closing keyword, so the bare `#2` in the second form won't auto-close.
|
|
|
|
## 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](https://github.com/TemujinCalidius/SurvivorCore/discussions), not Issues.
|
|
|
|
## Reporting a security issue
|
|
|
|
Found a vulnerability in the engine? **Don't open a public issue, PR, or discussion** — that
|
|
discloses it to every game built on SurvivorCore before a fix exists. Report it privately via
|
|
[**Security → Report a vulnerability**](https://github.com/TemujinCalidius/SurvivorCore/security/advisories/new).
|
|
See [`SECURITY.md`](SECURITY.md) for the full coordinated-disclosure policy, supported versions, and scope.
|
|
|
|
## 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](docs/architecture.md) and the companion
|
|
[Extending SurvivorCore](docs/extending.md) guide. The engine/content **boundary map** (which
|
|
systems are being extracted from the production game [The Counter
|
|
Earth](https://github.com/TemujinCalidius/TheCounterEarth) 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.
|