Skip to content

Kits Overview

The kernel contains no game rules. It ships scheduling, networking, sessions, hooks, the bus, and persistence — and deliberately stops there. Kits are where game rules live: pre-built userspace services in ChloeKernelServer/Kits that implement one genre mechanic each (hitscan guns, mana casting, melee parries, checkpoints, shops, parties) as server-validated gameplay over those primitives. You register a kit, configure it, and extend it through hooks — you do not fork it.

Every kit follows the same contract: the client sends requests, the server decides. A kit’s config is not a suggestion — each field maps to a server-enforced gate. FireRate = 8 in WeaponKit means the ninth shot in a second dies in validation no matter what the client claims.

Config-driven kits are kernel services — build with Kit.service(config), register, done. Kits you interact with at runtime return a handle from Kit.attach(Kernel, options) with methods your code calls.

local Root = game:GetService("ServerScriptService").ChloeKernelServer
local WeaponKit = require(Root.Kits.WeaponKit)
local MeleeKit = require(Root.Kits.MeleeKit)
-- Shape 1: config-driven service. Registered, then driven by client intents.
Kernel:registerService(WeaponKit.service({
Weapons = {
[1] = { Name = "Rifle", Damage = 34, FireRate = 8, MaxRange = 300 },
},
}))
-- Shape 2: attached handle. Client intents auto-wire AND your server code
-- (NPC behaviors, quest scripts) calls the same methods players ride.
local Melee = MeleeKit.attach(Kernel, {
Moveset = {
Jab = { Damage = 8, ComboNext = { "Cross" } },
Cross = { Damage = 12 },
},
})

BehaviorTree is the one exception: a pure helper with no internal scheduling — you build a tree and tick it from your own loop. MoveKit is the other special case: it lives in Shared/ChloeKernel because it has two halves — attach() on the client executes abilities, server() on the server meters them.

Four patterns repeat across every kit. Learn them once and each kit page becomes a table of specifics.

Kits that accept client input define prefixed intent channels on the NetDriverWK_Fire, SK_Cast, ML_Attack, CKMove, INV_Use, SH_Buy. Each channel carries a fixed wire schema and a per-channel rate limit, and every message runs the fail-closed Intent.<Channel> hook chain before any handler executes. Fail-closed means rejection is the default: a validator returning false rejects, and a validator that errors rejects too. An exploiter cannot get a message through by crashing a check.

The kit’s own gate registers at priority 50. Your game rules prepend at lower priority numbers and run first — no forking:

-- "No firing in the lobby" — prepends below WeaponKit's gate (priority 50)
Kernel.Hooks:on("Intent.WK_Fire", function(context)
return Kernel:getService("RoundKit"):current() == "Round"
end, 10)

Where a kit asks a game-rules question — may this damage land, may this craft start, may this companion summon — it fires a fail-open hook point instead: Weapon.CanDamage, Melee.CanHit, Craft.CanCraft, Companion.CanSummon, Party.CanInvite, Inventory.CanEquip/Inventory.CanUse. Returning false vetoes; an erroring handler is skipped and the action proceeds.

The asymmetry is deliberate. An erroring intent validator must not let an exploit message through (fail closed). An erroring damage-policy handler must not silently break all combat in your game (fail open). Input trust and game policy fail in opposite directions.

TeamKit is the pattern’s showcase: FriendlyFire = false registers vetoes on Weapon.CanDamage and Melee.CanHit, and neither combat kit knows teams exist.

Every kit publishes its outcomes on the kernel busWeapon.Hit, Melee.Parried, Craft.Completed, Obby.Checkpoint. That is the extension surface for reactions: kill feeds, VFX replication, leaderboard submits, and QuestKit objectives all subscribe instead of patching kit code. Topics also compose kits that never import each other — QuestKit counts Combat.Kill and Zone.Entered without knowing what publishes them.

Kits that hold long-lived player state write it through the session profile when a DataDriver is attached — session.Profile.Data — and fall back to transient session.Data otherwise. CheckpointKit stages, QuestKit progress, InventoryKit bags, LootKit pity counters, and ShopKit’s default currency field all follow this rule. No kit talks to DataStores directly; the loss-hardening lives in one place.

Kits stack. The composition lines to know:

  • InventoryKit is the item substrate. LootKit awards its rolls through it, CraftingKit consumes inputs and grants outputs through it, and ShopKit sells out of it. Define items once in InventoryKit’s registry; the other three reference ids.
  • CompanionKit rides NPCKit. A companion is an NPCKit archetype spawned and follow-looped by CompanionKit; its optional Aura is an Effects name applied to the owner while summoned.
  • SpellKit delegates its payloads. The kit owns mana, cooldowns, and channels; what a spell does is your Cast function — typically firing Projectiles or applying Effects (SpellKit).
  • TeamKit vetoes into the combat gates and PartyKit deliberately does not — parties are social, not combat policy, and feed Matchmaking group queues instead.
  • BehaviorTree drives NPCKit handles. Trees make decisions; the handle’s helpers (and MeleeKit/Projectiles via movesets) execute them.
Kit Registration Client channels Purpose
WeaponKit service() WK_Fire Server-validated hitscan weapons: fire-rate/ammo gates, lag-compensated hits against rewound capsules
MeleeKit attach() ML_Attack / ML_Block / ML_Parry Server-authoritative melee: windup/hit-frame/recovery state machine, combos, the block/guard-break/parry triangle
SpellKit service() SK_Cast Mana, regen, cooldowns, and channelled casts as killable kernel Processes
MoveKit attach() + server() CKMove Movement-context abilities (double jump, air step, dash) — client executes, server meters
NPCKit attach() NPC lifecycle and per-npc state (skill model, senses memory, cooldowns); your loop makes the decisions
BehaviorTree helper Selector/Sequence/decorator trees with no internal scheduling — tick from your own loop
InventoryKit attach() INV_Move / INV_Equip / INV_Unequip / INV_Use Server-authoritative items: definition registry, slotted stacking bags, equip slots, use handlers
LootKit attach() Server-rolled weighted loot tables with nesting and per-player pity persisted in the profile
CraftingKit attach() CR_Craft / CR_Cancel Recipes over InventoryKit: atomic input checks, timed crafts with cancel refunds, station gating
ShopKit attach() SH_Buy / SH_Sell Soft-currency shops over InventoryKit with deterministic cross-server rotation and stock caps
CompanionKit attach() CP_Summon / CP_Dismiss Owned NPC followers built from NPCKit archetypes, with owner auras via Effects
QuestKit service() Quests as declarative objectives counted off server-published bus topics
RoundKit service() — (replica out) Round loop: ordered phase cycle, duration timers, MinPlayers gates, countdown replication
CheckpointKit service() — (server-side Touched) Validated obby progression: sequence and minimum-time checks, profile persistence
TeamKit attach() Team assignment, team spawns, and friendly-fire vetoes wired into the combat gates
PartyKit attach() PT_Invite / PT_Accept / PT_Decline / PT_Leave / PT_Kick / PT_Promote Same-server parties: invites with expiry, leadership flow, roster pushes
InteractionKit service() prompt triggers via Intent.Interact Exploit-proof ProximityPrompts: every trigger re-validates distance, line of sight, and cooldown server-side
ProceduralKit service() — (wire yours through service:validate) Runtime ProceduralModel spawning with fail-closed parameter validation and rate-limited regeneration

The starter templates are complete minimal games; each server file lists its map requirements at the top.

Template Kits exercised Alongside
GunArena WeaponKit, TeamKit, RoundKit Rewind lag compensation, Leaderboards
WizardDuel SpellKit, MeleeKit, MoveKit Effects curses and wards
Obby CheckpointKit, MoveKit Zones kill bricks, Leaderboards speedrun board