Forest Logo
search
package_2

roblox-core

By @hurreman

Roblox

Mirrored from Wally

roblox-core

Small shared building blocks for hurreman's Roblox games. Each module is taken from the game whose copy was best, and generalized so it needs no game modules.

# wally.toml
[dependencies]
Core = "hurreman/roblox-core@0.1.0"
local Core = require(ReplicatedStorage.Packages.Core)
local Format = Core.Format -- modules load on first access

Modules load lazily: requiring the package doesn't run the client-only SoundManager on a server, and doesn't connect Buffs' server listeners on a client.

Modules

ModuleWhere it runsFromWhat it does
Bootstrapserver, clientMagic Neighborhood init.server / init.clientExplicit ordered service init; lifecycle hooks in their own ordered lists, checked both ways; client start list with slow-start warnings
MaidanySlime Nebula Maid.luaTeardown of connections, instances, threads, functions, disposables and promises; labels; debug dump
SignalanySlime Nebula's GoodSignal (stravant, MIT), plus OnceYield-safe signal with RBXScriptSignal semantics
FormatanyMagic Neighborhood FormatNumber (truncating suffixes up to Vg, then scientific), Rate, Money, Robux, Multiplier, Clock, Stopwatch
NumbersanySlime Nebula NumberUtilsNaN-safe IsFinite, ToFiniteInteger, SanitizeCount, RoundToNearest
ResetClockanySlime Nebula StockholmTime, generalizedDaily-reset calendar math for a UTC offset plus an EU or US daylight-saving rule; ResetClock.Stockholm, ResetClock.UTC
HeartbeatanySlime Nebula RunServiceHandlerOne Heartbeat connection for all per-frame callbacks, with disposers and slow-callback warnings
ProfileanyMagic Neighborhood ProfileProfile.Wrap(label, fn): microprofiler scopes that survive yielding handlers; Profile.prefix
BuffsserverSlime Nebula BuffManager, groups configurableStacking timed buffs published as <prefix><Stat> and <prefix><Stat>_Duration attributes
CollisionGroupsserverMagic Neighborhood CollisionGroupsSetup({ groups, noCollide })
SoundManagerclientSlime Nebula + Magic NeighborhoodRegistry-driven playback: categories, mute, cooldowns, sequences, random pools, random pitch, 3D roll-off, looped music that follows volume, Studio keepalive, preload

Bootstrap

-- src/server/init.server.luau
local Bootstrap = require(ReplicatedStorage.Packages.Core).Bootstrap

local app = Bootstrap.Server({
	folder = script.Services,
	init = { "DataService", "PlotService", "SyncService", "ShopService" },
	hooks = {
		onPlayerReady = { "PlotService", "ShopService" }, -- (player, profile)
		onPlayerRemoving = { "PlotService" },             -- (player)
	},
})
app:Init() -- each service's init(app.Services), in init order

Players.PlayerAdded:Connect(function(player)
	local profile = app.Services.DataService.load(player)
	app:Run("onPlayerReady", player, profile)
end)
-- src/client/init.client.luau
Bootstrap.Start({ script.Store, script.Controllers.SoundController, function() ... end })

Server() fails at boot in these cases:

  • a service has a hook method but isn't in that hook's list;
  • a listed service lacks the hook;
  • a name is listed twice;
  • a module is missing.

The player lifecycle itself (data load, plot assignment, spawning) stays in the game's entry script.

Buffs

local buffs = Core.Buffs.new({ prefixes = { Food = "Food_", GamePass = "GamePass_" }, default = "Food" })
buffs:Add(player, "Speed", 4, 30)                       -- Food_Speed = 4, Food_Speed_Duration = 30
buffs:Add(player, "Speed", 2, 60, { group = "GamePass", onExpired = fn })
buffs:Split(player, "Speed")                            -- { Food = 4, GamePass = 2, total = 6 }

SoundManager

local SoundManager = Core.SoundManager
SoundManager.Configure({
	registry = require(ReplicatedStorage.Shared.Audio.SoundRegistry),
	startMuted = false,                 -- true: muted until saved settings unmute it
	volumes = { Music = 0.5 },          -- over Master 1, Music 0.6, Ambient 0.7, SFX 1
	defaultParent = SoundService,       -- where 2D sounds go
})
SoundManager.Play("Coin")
SoundManager.Play("Hit", nil, { Parent = part })        -- 3D, with the entry's rollOffMin/Max

These are the registry entry fields:

FieldMeaning
id, or ids with random / sequence (plus sequenceTimeout, reverse)Which asset plays
volume, categoryBase volume and its volume category
loopedLoops, following later volume and mute changes
cooldownMinimum seconds between plays of this entry
playbackSpeed / pitchFixed playback speed
randomPitch with minPitch / maxPitch (or pitchRange)Random pitch within a range
rollOffMin / rollOffMax3D distance falloff

Moving the games over

  • Slime Nebula: most of these become one-line re-exports of the package:
    • Maid.lua and ClientModules/UI/Signal.lua re-export Core.Maid and Core.Signal. Leave ZonePlus's own Zone/Signal alone.
    • StockholmTime.lua becomes return Core.ResetClock.Stockholm. This was checked hour by hour across 2025–2027 by tests/specs/ResetClock.spec.luau.
    • RunServiceHandler.lua becomes return Core.Heartbeat.
    • NumberUtils.lua re-exports Core.Numbers and keeps its own FormatNumber. Note that Core.Format.Number truncates while FormatNumber rounds (1999 shows "1.99K", not "2K"), so switching it is a separate decision.
    • BuffManager.lua keeps its API as a thin shim over a Core.Buffs set with SN's four prefixes. AddTempBuff maps to Add (opts.prefix becomes opts.group), RemoveBuff to Remove, GetCurrentBuffs to Current, GetStatSplit to Split, and _tick to Tick.
    • ClientModules/Audio/SoundManager.lua becomes Core.SoundManager, plus Configure({ registry = SoundRegistry, startMuted = true, defaultParent = workspace }) before the loading screen applies settings.
    • The bootstrap doesn't change: SN keeps ModuleRegistry.
  • Magic Neighborhood:
    • init.server.luau and init.client.luau build their lists with Bootstrap.Server / Bootstrap.Start.
    • In Format, rename number→Number, rate→Rate, cash→Money, mult→Multiplier, clock→Clock, raceTime→Stopwatch, robux→Robux. candy stays as a local one-liner.
    • Profile becomes Core.Profile with Profile.prefix = "TOTD.".
    • CollisionGroups.setup() becomes Core.CollisionGroups.Setup({...}).
    • shared/Audio/SoundManager.luau becomes Core.SoundManager with Configure({ registry = SoundRegistry }).
  • Steal an Egg: the same moves as Magic Neighborhood for init.server / init.client and Format. Its client/Sounds.luau has a different API (play, at, on, attach, loop); move its cue table into a SoundManager registry when that file is next touched.

Developing

lune run tests/run    # specs under tests/specs, on hurreman/luau-spec (DevPackages, else ../luau-spec)

The ResetClock spec also checks against ../SlimeNebula's StockholmTime when that checkout exists. SoundManager needs real Sound instances, so it was verified in Studio rather than under Lune. That run covered: start-muted, cooldown, looped music following SetVolume and mute immediately (even under deferred SignalBehavior), sequences and ping-pong, random pools, the random pitch range, PlayAssetId, 3D roll-off and defaultParent.

Package Details

Install command (Click to copy)


Version

0.1.0

License

MIT

check_circle

Safe for commercial use

Automated license review — not legal advice.