Skip to content

Calendar Time v2.1.0

Authoring Safety and Plugin Logging

This guide shows how Calendar Time reports authoring mistakes and how your game can route plugin output into its own logging pipeline. You can skip this guide at first. The default Godot Errors/Output

Status
Current
Version
v2.1.0
Source updated
2026-08-09
Generated on
2026-09-06

This guide shows how Calendar Time reports authoring mistakes and how your game can route plugin output into its own logging pipeline.

You can skip this guide at first. The default Godot Errors/Output panel works fine. Use this when you want plugin errors to appear in your game logs, test harnesses, telemetry, or custom debug UI.

Plugin Output Goes Through CalendarTimeLogger

Runtime addon code uses the static CalendarTimeLogger facade for plugin output:

CalendarTimeLogger.error("GameClock","Clock is missing")
CalendarTimeLogger.warn("TimeEventMessenger","Signal is already disconnected")
CalendarTimeLogger.info("TimeHost","Clock advanced")

If your game does nothing, Calendar Time preserves Godot-style behavior:

Default Godot output mapping:

  • error(source, message)push_error.
  • warn(source, message)push_warning.
  • info(source, message)print.

Route Plugin Logs Into Your Game Logger

Create a LogSink and register it once during game boot:

class_name GameCalendarTimeLogSink
extends LogSink

func error(source :String, message :String)-> void:
    GameLogger.error("[%s]%s" % [source, message])

func warn(source :String, message :String)-> void:
    GameLogger.warn("[%s]%s" % [source, message])

func info(source :String, message :String)-> void:
    GameLogger.info("[%s]%s" % [source, message])

Register the sink before scenes with Calendar Time nodes or services run:

func _ready()-> void:
    CalendarTimeLogger.set_logger(GameCalendarTimeLogSink.new())

The logger is a static singleton, so you do not need to wire a logger into each Calendar Time object.

Release the injected sink during shutdown if it references scene nodes, autoloads, or other game-owned objects:

func _exit_tree()-> void:
    CalendarTimeLogger.clear_logger()

Unbound Clocks Fail Loudly

Calendar Time treats a missing GameClock as an authoring error. Clock-consuming nodes validate their clock binding during _ready() or validate() and stop work if the clock is missing.

Examples of clock-consuming surfaces include:

  • DateTimeDisplay
  • TimeOfDayDisplay
  • CalendarDisplay
  • TimeDial
  • TimeOfDayDirectionalLight2D
  • DayNightCycleService
  • TimeEventMessenger
  • your own scripts that subscribe to clock.signal_bus

When your game has registered a LogSink, those validation failures reach your injected logger. This lets automated tests and game log files capture scene authoring mistakes such as a copied UI template whose clock export was never assigned.

Integration Checklist

  • Create one shared GameClock resource for each timeline your game needs.
  • Add each GameClock to TimeHost.clocks so the host drives it.
  • Assign the same GameClock to UI, lighting, aging components, and gameplay scripts that should follow that timeline.
  • Register CalendarTimeLogger.set_logger(...) during game boot if you want plugin output in your own logs.
  • Call CalendarTimeLogger.clear_logger() during shutdown if your sink holds references to game objects.
  • Treat Calendar Time errors as scene authoring problems to fix in the editor, not as warnings to suppress.
  • In your own scripts, guard duplicate clock.signal_bus connections and disconnect in _exit_tree() when the script owns the connection.

Troubleshooting

Common fixes:

  • Display shows no time — empty clock export. Assign the GameClock from TimeHost.clocks.
  • Time does not advance — empty clocks or MANUAL without a driver. Add a clock or call drive_microseconds().
  • Game logger misses plugin output — set_logger() runs too late. Register in an autoload or early bootstrap.
  • Logger stays alive after shutdown — static logger still holds the sink. Call clear_logger() on exit.
  • Duplicate callbacks — your code connected repeatedly. Guard with is_connected() or disconnect in _exit_tree().
  • Disconnect errors after toggling nodes — disconnect without checking state. Guard with is_connected() first.
  • README.md for setup and the node/resource API surface.
  • examples.md for runtime clock examples.
  • template-ui.md for shipped UI templates.