Configuration
This page walks the full surface of WorldConfig and explains when to change the defaults. Every field except actorContainer and definitionsModule is optional.
WorldConfig
local world = Hindsight.createWorld({
actorContainer = ServerScriptService, -- required
definitionsModule = ReplicatedStorage.Defs, -- required
visualsContainer = workspace.Bullets,
excludeContainers = { workspace.Bullets, workspace.Characters },
threads = 16,
serverFrameRate = 1 / 60,
frameTimeBudget = 0.5,
interpolation = 0.048,
defaultRaycastFilter = raycastFilter,
rollback = customRollbackConfig,
penetration = customPenetrationConfig,
})
actorContainer: Instance (required)
Where Hindsight parents the simulation Actors. On the server, ServerScriptService is fine. On the client, use Player.PlayerScripts (so the actors die with the player).
definitionsModule: ModuleScript (required)
The ModuleScript returning { [string]: ProjectileDefinition }. Required because everything except standalone rollback queries goes through it. If you're using only standalone rollback, you can pass any module that returns {} — but the real shape is defined here.
visualsContainer: Instance?
Where bullet visuals get parented on the client. The simulation only clones options.visual if visualsContainer is set. Leave it nil on the server.
excludeContainers: { Instance }?
Containers whose descendants are not valid continuation parts for penetration compound walls. This is needed because the penetration overlap probe runs in parallel context, which can't use OverlapParams — instead, Hindsight does a bounds query and filters in Lua against this list. Typical contents: the visuals container and the characters container.
threads: number? (default 16)
How many Actors the World creates. More actors = better parallel throughput, but each actor pays a fixed cost on Initialize. 16 is a good baseline. Bump it if you have hundreds of in-flight projectiles.
serverFrameRate: number? (default 1 / 60)
The time budget per frame each actor expects. Used together with frameTimeBudget to decide when to bail out of the per-frame step loop. If you're targeting 30 Hz, set to 1 / 30.
frameTimeBudget: number? (default 0.5)
Fraction of serverFrameRate an actor will spend stepping projectiles before deferring leftover work to the next tick. Lower this if other server work is starving; raise it (towards 1.0) if the actor pool has slack.
interpolation: number? (default 0.048)
Roblox's client-side replication interpolation delay in seconds. Currently this value is captured in the resolved config but consumer-side use is the typical place to apply it (your server wiring subtracts playerPing + interpolation from the cast timestamp — see Wiring).
defaultRaycastFilter: RaycastParams? (default RaycastParams.new())
The RaycastParams used by every projectile that doesn't supply its own on the definition. Build it once at world setup with FilterType = Exclude and the character/bullet containers in FilterDescendantsInstances.
rollback: RollbackConfig?
Defaults to Hindsight.Defaults.rollback. See below.
penetration: PenetrationConfig?
Defaults to Hindsight.Defaults.penetration. See below.
RollbackConfig
local rollback: Hindsight.RollbackConfig = {
lifetime = 1,
voxelSize = 32,
gridCenter = Vector3.zero,
gridSize = Vector3.new(4096, 512, 4096),
hitboxSize = Vector3.new(3, 3, 3),
rigs = Hindsight.Defaults.rigs,
}
lifetime— how many seconds of snapshots to keep. Higher = tolerates higher player latency but costs more memory and slows queries. 1 s covers ~250 ms ping with margin; raise it only if your peak ping is much higher.voxelSize— broadphase cell size in studs. Smaller voxels mean fewer characters per cell (better query cost) but more cells touched per ray. 32 is fine for most maps.gridCenter/gridSize— the world-space extent of the voxel space. Characters outside this AABB are not findable viaqueryRay. Center on your map and size generously.hitboxSize— half-extents of the AABB used to expand each character's footprint in the voxel grid. Larger = fewer false negatives from fast movement, more candidates per query.(3, 3, 3)covers an R15 with margin.rigs—{ [string]: Rig }of hitbox layouts. The default set coversR15andR6. Add your own if you have non-humanoid creatures.
See Defaults.rigs for the exact part lists used by the built-in R15/R6 entries. To add a custom rig, mirror that shape:
local rollback = table.clone(Hindsight.Defaults.rollback)
rollback.rigs = table.clone(rollback.rigs)
rollback.rigs.Dragon = {
parts = { "Head", "Neck", "Body", "Tail", "LeftWing", "RightWing" },
sizes = {
Vector3.new(2, 2, 3) / 2,
Vector3.new(2, 2, 2) / 2,
Vector3.new(6, 4, 8) / 2,
Vector3.new(2, 2, 6) / 2,
Vector3.new(8, 1, 4) / 2,
Vector3.new(8, 1, 4) / 2,
},
}
parts and sizes are parallel arrays — sizes[i] is the half-extent OBB used to test the part named parts[i]. The character Model is expected to contain a child BasePart per name.
PenetrationConfig
local penetration: Hindsight.PenetrationConfig = {
surfaceHardness = {
[Enum.Material.Wood] = 2,
[Enum.Material.Concrete] = 10,
},
defaultHardness = 10,
ricochetHardness = 10,
mediumGapThreshold = 0.1,
maxCompoundMediumParts = 32,
}
surfaceHardness— hardness perEnum.Material. Cost to cross a medium isthickness * hardness. Unlisted materials fall back todefaultHardness.defaultHardness— fallback for materials not insurfaceHardness.ricochetHardness— minimum hardness for a surface to ricochet at all. Wood (2) never bounces bullets under defaults; concrete (10) can.mediumGapThreshold— gap size (in studs) below which two parts are merged into one compound medium. Useful for walls built out of stacked sub-parts.maxCompoundMediumParts— upper bound on how many parts can join one compound. Caps worst-case probe cost when a projectile enters a deeply layered structure.