API

StarlightModule

Main module for Starlight.jl - a greedy framework for greedy developers.

Reexports

  • Base: ReentrantLock, lock, unlock
  • FileIO: load
  • Pkg.Artifacts
  • LazyArtifacts
  • DataStructures: Queue, enqueue!, dequeue!
  • DataFrames
  • Colors
  • SimpleDirectMediaLayer
  • SimpleDirectMediaLayer.LibSDL2
  • Telescope
source

Core

App and Events

Starlight.awake!Method
awake!(a) = nothing

Arbitrary startup function.

By default does literally nothing, but methods can be added for any type.

The preferred way to call listenFor is from inside an awake! method.

source
Starlight.shutdown!Method
shutdown!(a) = nothing

Arbitrary shutdown function.

By default does literally nothing, but methods can be added for any type.

The preferred way to call unlistenFrom is from inside a shutdown! method.

source
Starlight.listenersConstant
const listeners = Dict{DataType, Set{Any}}()

Internal dictionary that pairs event types with their listeners.

source
Starlight.listener_lockConstant
const listener_lock = ReentrantLock()

Internal lock used to synchronize access to the listeners dictionary.

source
Starlight.handleMessage!Method
handleMessage!(l, m) = nothing

Invoked on listener l when message m is received.

Currently there are three ways this function can be invoked for a given listener and message:

  • automatically by the message dispatcher, when the listener has been registered with listenFor and the message is of the corresponding type
  • automatically by the Physics subsystem on two colliding objects with message type TS_CollisionEvent
  • manually by user code (discouraged)

By default does (literally) nothing, but methods can be added for any type combination.

source
Starlight.sendMessageMethod
function sendMessage(m)
  if haskey(listeners, typeof(m))
    put!(messages, m)
  end
end

Add a message to the message queue if the message's type has any listeners. Otherwise drop.

The preferred way to send events/messages in Starlight.

source
Starlight.listenForMethod
function listenFor(e::Any, d::DataType)
  lock(listener_lock)
  if !haskey(listeners, d) listeners[d] = Set{Any}() end
  push!(listeners[d], e)
  unlock(listener_lock)
end

Add a listener e for messages of type d. l's handleMessage! method will be invoked when messages of type d are received.

source
Starlight.unlistenFromMethod
function unlistenFrom(e::Any, d::DataType)
  lock(listener_lock)
  if haskey(listeners, d) delete!(listeners[d], e) end
  unlock(listener_lock)
end

Stop listener e from receiving messages of type d.

source
Starlight.dispatchMessageFunction
function dispatchMessage(arg)
  try
    m = take!(messages) # NOTE messages are fully processed in the order they are received
    d = typeof(m)
    if haskey(listeners, d)
      # Threads.@threads doesn't work on raw sets
      # because it needs to use indexing to split
      # up memory, i work around it this way
      Threads.@threads for l in Vector([listeners[d]...])
        handleMessage!(l, m)
      end
    end
  catch
    handleException()
  end
end

Take a single message from the event queue and invoke handleMessage! for all listeners in parallel.

If there are no listeners for the message type (i.e. they were removed after the message was sent), drop.

Normally runs in an infinite loop in a background Task started by awake!(::App).

Danger

This function is called internally by Starlight and documented here for completeness. Do not invoke it yourself unless you know what you are doing.

source
Starlight.AppType

Struct for the "master App" singleton.

Constructor accepts keyword arguments for all fields except systems, and no positional arguments.

Fields

  • systems::Dict{DataType, Any} Dictionary of subsystems by type
  • running::Bool Whether awake! has been called
  • wdth::Int Window width
  • hght::Int Window height
  • bgrd::Colorant Window default background color
source
Starlight.onFunction
on(a::App) = a.running

Check whether awake! has been called.

source
Starlight.system!Function
system!(a::App, s) = a.systems[typeof(s)] = s

Add something to an App's systems dictionary.

source
Starlight.ecsFunction
ecs() = App().systems[ECS]

Get the current Entity Component System.

source
Starlight.tsFunction
ts() = App().systems[TS]

Get the current Telescope backend subsystem.

source
Starlight.systemAwakeOrderFunction
systemAwakeOrder = () -> [clk(), ts(), inp(), phys(), ecs(), scn()]

The order in which App subsystems should be awoken in order to not produce bugs.

source
Starlight.systemShutdownOrderFunction
systemShutdownOrder = () -> [clk(), inp(), phys(), scn(), ecs(), ts()]

The order in which App subsystems should be shut down in order to not produce bugs.

source
Starlight.awake!Method
function awake!(a::App)
  if !on(a)
    job!(clk(), dispatchMessage)
    map(awake!, systemAwakeOrder())
    a.running = true
  end
end

If not on, start the message dispatcher call awake! on all subsystems.

Note that if running from a script the app will still exit when Julia exits, it will never block. Figuring out whether/how to keep it alive is on the user. One method is to use run!, see below.

source
Starlight.shutdown!Method
function shutdown!(a::App)
  if !off(a)
    map(shutdown!, systemShutdownOrder())
    a.running = false
  end
end

If not off, call shutdown! on all subsystems.

source
Starlight.run!Function
function run!(a::App)
  awake!(a)
  if !isinteractive()
    while on(a)
      yield()
    end
  end
end

Call awake! and keep alive until switched off.

source

Clock

Starlight.ClockType
mutable struct Clock
  start_event::Base.Event
  is_stopped::Bool
  frequency::AbstractFloat
  Clock() = new(Base.Event(), true, 0.01667)
end

Fields

  • start_event::Base.Event Whether awake! has been called
  • is_stopped::Bool Whether shutdown! has been called
  • frequency::AbstractFloat Tick frequency
source
Base.sleepMethod
function Base.sleep(s::SLEEP_SEC) ::Float64
  t1 = time()
  while true
    if time() - t1 >= s.Δ break end
    yield()
  end
  return time() - t1
end

sleep(::SLEEP_SEC) -> Float64

Sleep the specified number of seconds.

source
Base.sleepMethod
function Base.sleep(s::SLEEP_NSEC)
  t1 = time_ns()
  while true
    if time_ns() - t1 >= s.Δ break end
    yield()
  end
  return time_ns() - t1
end

sleep(::SLEEP_NSEC) -> UInt64

Sleep the specified number of nanoseconds

source
Starlight.tickFunction
function tick(Δ)
  δ = sleep(SLEEP_NSEC(Δ * 1e9))
  sendMessage(TICK(δ / 1e9))
end

tick(::Any) -> Nothing

Sleep Δ seconds, the raise a TICK event with the actual amount of time slept. Called in a background task in an infinite loop. Primary purpose is to trigger subsystem TICK handlers.

source
Starlight.job!Function
function job!(c::Clock, f, arg=1)
  function job()
    Base.wait(c.start_event)
    while !c.is_stopped
      f(arg)
    end
  end
  schedule(Task(job))
end

job!(::Clock, f::Any, arg::Any) -> Task

Schedule a background task to be run and synchronized with clock state. Used internally to run dispatchMessage and tick.

source
Starlight.oneshot!Function
function oneshot!(c::Clock, f, arg=1)
  function oneshot()
    Base.wait(c.start_event)
    f(arg)
  end
  schedule(Task(oneshot))
end

oneshot!(::Clock, f::Any, arg::Any) -> Task

Schedule a background task to be run once, synchronized with Clock state.

source
Starlight.awake!Method
function awake!(c::Clock)
  job!(c, tick, c.frequency)

  c.is_stopped = false

  Base.notify(c.start_event)
end

awake!(::Clock) -> Nothing

Starts the tick job and signals all waiting Tasks that the clock has started.

source
Starlight.shutdown!Method
function shutdown!(c::Clock)
  c.is_stopped = true
  
  c.start_event = Base.Event()
end

shutdown!(::Clock) -> Nothing

Signal Tasks that the Clock has stopped.

source

ECS and Scene

Telescope Backend

Rendering

Entities

Input

Physics