Skip to content

Planck v0.2.0-rc.1

Latest
Compare
Choose a tag to compare
@YetAnotherClown YetAnotherClown released this 05 Feb 01:23
· 14 commits to main since this release

Planck v0.2.0-rc.1

Caution

Planck v0.2.0-rc.1 contains a lot of breaking changes from v0.1.0. If you're currently using v0.1.0, read the Breaking Changes section before installing this version so you can migrate your code.

New and Improved Documentation

The Documentation Site now contains a well detailed Getting Started guide for learning all the core concepts of Planck, a Design Guide for designing games with Planck for more advanced users, and Setup Guides for Jecs and Matter.

→ Documentation

Built-in Common Conditions

Planck now provides built-in conditions for use within your Systems or as Run Conditions. These common conditions are functions which generate closures to check if a condition is true or false, some of them provide additional uses that you can use within your systems as well.

OnEvent

The onEvent condition checks for new events each time the conditional function is called. It also provides an additional function to collect the new events.

This can be used in your systems similarly to the Matter.useEvent() hook or a simple collect function.

local onEvent = Planck.onEvent
local hasNewEvent, collectEvents = onEvent(Players.PlayerAdded)

local function handlePlayers()
  for _, player in collectEvents() do
    -- ...
  end
end

return {
  system = handlePlayers,
  runConditions = { hasNewEvent },
  -- This also works, if you don't want to collect the events
  runConditions = { onEvent(Players.PlayerAdded) },
}

TimePassed (Throttle)

The timePassed or throttle condition checks if the given time has passed. This is similar to the Matter.useThrottle() hook or a simple interval function.

local timePassed = Planck.timePassed
local hasTimePassed = timePassed(10)

local function throttled()
  if hasTimePassed() then
    -- We can use this function in our systems
  end

  -- Only runs after 10 seconds because of our condition
end

return {
  system = throttled,
  runConditions = { hasTimePassed },
  -- Or if we don't want to use it in systems
  runConditions = { timePassed(10) },
}

RunOnce

The runOnce condition will only return true once, and then always false afterwards. This is useful for when you need to create startup logic. It is important to note that while Startup Phases use this internally, the Scheduler ensures they always run before other systems.

local runOnce = Planck.runOnce
local hasRanOnce = runOnce()

local function someSystem()
  if not hasRanOnce()
    -- Some startup logic
  end

  -- Or we just want the whole system to run only once
end

return {
  system = someSystem,
  runConditions = { hasRanOnce },
  -- If you only want a 'startup' system
  runConditions = { runOnce() },
}

IsNot

The isNot condition inverses other conditions.

RunService Plugin

This Plugin replaces the built-in Phases from v0.1.0 in an effort to also make Planck runtime agnostic. In the future, any feature which interacts with the Roblox Engine will be a separate Plugin instead of apart of the core library.

Pipelines

Each RunService Event is now it's own Pipeline,

  • PreRender
  • PreAnimation
  • PreSimulation
  • PostSimulation
  • Heartbeat

Phases

And it's own Phase, with the exception of Heartbeat which has many Phases.

Event Phase
PreRender PreRender
PreAnimation PreAnimation
PreSimulation PreSimulation
PostSimulation PostSimulation
Heartbeat Update

Heartbeat Phases

  • First
  • PreUpdate
  • Update
  • PostUpdate
  • Last

Installation

With Wally,

[dependencies]
PlanckRunService = "yetanotherclown/planck-runservice@v0.2.0-rc.1"

Better Scheduling w/ Dependency Management

The library has been refactored to now create and manage dependencies between Pipelines and Phases using Adjacency Matrices, and it will now use these dependencies in addition to order of insertion to determine the order in which Pipelines and Phases run in Planck,

You can learn more about the new behavior under the breaking changes section.

Scheduler:insertBefore() and Pipeline:insertBefore()

These two new methods now exist for creating dependencies between two phases. :insertBefore() will make the first Phase/Pipeline depend on the other, meaning it cannot run until the other does.

Breaking Changes

Ordering Priorities

Ordering is no longer determinant on a fixed position assigned to each Phase or Pipeline with :insert() or :insertAfter within an ordered list. Instead, the Scheduler will now order Phases and Pipelines based on their dependencies.

To explain how the ordering now works,

A dependency is any Phase/Pipeline another Phase/Pipeline depends on. A dependent if the Phase/Pipeline that depends on another Phase/Pipeline.

This looks like, insertAfter(dependent, dependency) or insertBefore(dependent, dependency).

  1. Start with the first Phase/Pipeline inserted
  2. If this Phase/Pipeline has any dependency, skip it and move onto the next one.
  3. Add this Phase/Pipeline to the order
  4. If this Phase/Pipeline has any dependents, repeat this process in order of insertion for each dependent Phase/Pipeline.
  5. Move onto the next node.

insert(dependent) also now works by setting the last added Phase/Pipeline as a dependency of dependent.

Breaking insertAfter

This change affects Scheduler:insertAfter() and Pipeline:insertAfter(). Because Phases/Pipelines no longer have fixed positions in the order of execution, insertAfter no longer inserts a dependent to be immediately after dependency.

To better demonstrate this, consider this code example:

local myScheduler = Scheduler.new()
:insert(PhaseOne)
:insert(PhaseThree)
:insertAfter(PhaseTwo, PhaseOne)

-- Old Behavior:
-- PhaseOne -> PhaseTwo -> PhaseThree

-- New Behavior:
-- PhaseOne -> PhaseThree -> PhaseTwo

This change is desirable because:

  • PhaseThree is not dependent on PhaseTwo (dependencies are defined with :insertAfter())
  • PhaseThree is inserted before PhaseTwo, so we respect the implicit order of insertion

If you find your systems breaking because of this change, you should use insertAfter and insertBefore to explicitly define the dependencies of your Phases/Pipelines. You should not rely on insert to manage dependencies.

Built-in RunService Phases Removed

The following RunService Phases are no longer included in the core library, instead they are now available as a plugin.

Event Phase(s)
PreRender PreRender
PreAnimation PreAnimation
PreSimulation PreSimulation
PostSimulation PostSimulation
Heartbeat First, PreUpdate, Update, PostUpdate, Last

When the Plugin is added, the default Phase will be set to Update.

[dependencies]
PlanckRunService = "yetanotherclown/planck-runservice@v0.2.0-rc.1"

Replacement of Scheduler:setRunCondition

The method Scheduler:setRunCondition has been replaced with a new method Scheduler:addRunCondition which allows for the addition of multiple run conditions.

With this new method, it is no longer possible to overwrite previous run conditions. Run Conditions should ideally be added upon creation of the Scheduler, and not modified afterwards.

Replacement of SystemTable.runCondition

Now that you can add multiple Run Conditions, a System Table now will take SystemTable.runConditions instead which is an array of run conditions.

local function system()
	-- ...
end

local function runIf()
	-- ...
end

return {
	system = system,
	runConditions = { throttle(10), runIf }
}

Changes

Added

  • Scheduler:insertBefore()
  • Pipeline:insertBefore()
  • Conditions (isNot, runOnce, timePassed, onEvent)
  • Scheduler:addRunCondition() for adding multiple run conditions
  • PlanckRunService Plugin which adds built-in Pipelines and Phases for RunService events

Changed

  • Refactored internals to use Adjacency Matrices for managing ordering and dependencies of Phases/Pipelines
  • Breaking: The following methods now create an ordering dependency instead of setting the fixed order of Phases/Pipelines
    • Scheduler:insert()
    • Scheduler:insertAfter()
    • Pipeline:insert()
    • Pipeline:insertAfter()
  • Breaking: Scheduler:runAll() will no longer run in the exact order that Phases were inserted for Phases bound to events. They will be grouped together, and ran together in order of insertion.
  • Breaking: Scheduler:setRunCondition() has been replaced with Scheduler:addRunCondition()
  • Breaking: SystemTable.runCondition has been replaced with SystemTable.runConditions
  • Refactored internals to reuse event logic
  • Replaced Phase.Update as default phase with Default

Removed

  • Built-in RunService Pipelines/Phases, these will be available as a separate plugin

Fixed

  • Fallback System name does not contain the line of the system