| name | manage-world-lifecycle |
| description | Understand and extend the CLOiSim world load → plugin start → reset → save lifecycle. Use when: debugging world loading failures, troubleshooting plugin startup coordination, fixing reset behavior, extending the world save pipeline, diagnosing physics mode switching issues, understanding PluginStartTracker events. |
Manage World Lifecycle
Reference guide for the complete CLOiSim simulation lifecycle: world loading, plugin startup coordination, runtime reset, and world state persistence.
When to Use
- Debugging why a world fails to load or hangs during plugin startup
- Adding a new component that needs to participate in the reset cycle
- Understanding the
PluginStartTracker event system
- Extending
WorldSaver to persist new element types
- Diagnosing physics simulation mode switching issues
- Coordinating multi-plugin dependencies during startup
Lifecycle Overview
┌─────────────────────────────────────────────────────────┐
│ LOAD │
│ Main.LoadWorld() │
│ ├─ SDF.Root.DoParse() → parse SDF XML │
│ ├─ Physics.simulationMode = Script │
│ ├─ Import.Loader.Start() → create GameObjects │
│ ├─ Wait: worldRoot.childCount > 0 │
│ ├─ PluginStartTracker.Bind(worldRoot) │
│ ├─ Physics.SyncTransforms() │
│ ├─ Physics.simulationMode = FixedUpdate │
│ ├─ ResetWorld() [initial reset] │
│ └─ Wait: AllStarted == true │
├─────────────────────────────────────────────────────────┤
│ RUN │
│ Normal simulation loop │
│ ├─ FixedUpdate: physics │
│ ├─ Update: sensors, UI │
│ └─ Plugins: TX/RX threads running │
├─────────────────────────────────────────────────────────┤
│ RESET (Ctrl+R) │
│ Main.ResetSimulation() │
│ ├─ SensorRenderManager.Pause() │
│ ├─ SimulationWorld.SignalReset() │
│ ├─ ResetWorld() │
│ │ ├─ Helper.Base.Reset() for each helper │
│ │ ├─ Device.Reset() for each device │
│ │ └─ CLOiSimPlugin.Reset() for each plugin │
│ ├─ Wait 0.1s │
│ └─ SensorRenderManager.Resume() │
├─────────────────────────────────────────────────────────┤
│ FULL RELOAD (Ctrl+Shift+R) │
│ Destroys world root, reloads from SDF │
├─────────────────────────────────────────────────────────┤
│ SAVE (Ctrl+Shift+S) │
│ WorldSaver.Update() │
│ ├─ ClearAllComments() │
│ ├─ UpdateGUI() → camera pose │
│ ├─ UpdateModels() → model poses, static flags │
│ └─ UpdateRoads() → road spline points │
└─────────────────────────────────────────────────────────┘
Plugin Startup Coordination
PluginStartTracker
Monitors all CLOiSimPlugin instances under a loaded root and fires events as they start:
var tracker = new PluginStartTracker();
tracker.ProgressChanged += OnPluginProgressChanged;
tracker.AllStartedEvent += OnAllPluginsStarted;
tracker.Bind(worldRoot);
Plugin Startup Flow
Each CLOiSimPlugin follows this internal sequence:
Awake()
└─ OnAwake() [abstract] — set _type, cache components
Start() [coroutine]
└─ OnStart() [abstract] — register ports, add threads
├─ RegisterTxDevice() / RegisterRxDevice()
├─ RegisterServiceDevice() / RegisterClientDevice()
├─ AddThread(port, threadFunction, device)
└─ yield return null
└─ Raises Started event → PluginStartTracker.OnPluginStarted()
Waiting for All Plugins
private void OnAllPluginsStarted()
{
_pluginAllStarted = true;
BridgeManager.PrintAllocatedHistory();
}
while (!_pluginAllStarted)
yield return null;
Reset Cascade
ResetWorld() resets components in three phases:
void ResetModel(GameObject targetObject)
{
foreach (var helper in targetObject.GetComponentsInChildren<Helper.Base>())
helper.Reset();
foreach (var device in targetObject.GetComponentsInChildren<Device>())
device.Reset();
foreach (var plugin in targetObject.GetComponentsInChildren<CLOiSimPlugin>())
plugin.Reset();
}
SimulationWorld Reset Signal
SimulationWorld sends a reset signal to the external ROS bridge:
SimulationWorld.SignalReset();
World Save Pipeline
WorldSaver serializes current scene state back to the loaded SDF XML:
Physics Mode Switching
During world loading, physics must be paused to prevent objects from falling while being constructed:
Physics.simulationMode = SimulationMode.Script;
Physics.SyncTransforms();
Physics.simulationMode = SimulationMode.FixedUpdate;
Procedure: Adding a New Resetable Component
To make a new component participate in the reset cycle:
-
If it's a Helper — inherit from Helper.Base and override Reset():
public class MyHelper : Base
{
private float _initialValue;
new protected void Start()
{
base.Start();
_initialValue = currentValue;
}
public new void Reset()
{
base.Reset();
currentValue = _initialValue;
}
}
-
If it's a Device — override OnReset() in your Device subclass:
protected override void OnReset()
{
_accumulatedData = 0;
}
-
If it's a Plugin — override OnReset() in your CLOiSimPlugin subclass:
protected override void OnReset()
{
_commandBuffer.Clear();
}
Procedure: Extending World Save
To save a new element type in WorldSaver:
-
Add an Update*() method:
private void UpdateMyElements()
{
var staleNodes = _worldNode.SelectNodes("my_element");
foreach (XmlNode node in staleNodes)
_worldNode.RemoveChild(node);
foreach (var element in FindMyElements())
{
var xmlNode = _doc.CreateElement("my_element");
xmlNode.SetAttribute("name", element.Name);
var sdfPose = Unity2SDF.Pose(
element.transform.position, element.transform.rotation);
_worldNode.AppendChild(xmlNode);
}
}
-
Call it from Update():
public void Update()
{
ClearAllComments();
UpdateGUI();
UpdateModels();
UpdateRoads();
UpdateMyElements();
}
Critical Pitfalls
| Pitfall | Details | Mitigation |
|---|
| Plugin start order is undefined | Plugins start in parallel via background threads. Do NOT assume plugin A starts before plugin B. | Use AllStartedEvent to synchronize cross-plugin dependencies. |
| Reset called during initial load | ResetWorld() runs once during LoadWorld() before plugins finish starting. | Ensure OnReset() handles being called before OnStart() completes. |
| Stale plugin references | PluginStartTracker.Bind() walks the scene once at load time. Dynamically added plugins are NOT tracked. | Rebind if plugins are added post-load. |
| Transform sync timing | Manual transform changes during runtime are not synced to physics automatically (autoSyncTransforms is off). | Call Physics.SyncTransforms() after manual teleport operations. |
| Physics mode must bracket construction | Objects fall during construction if physics is FixedUpdate. | Set SimulationMode.Script before creating objects, restore after. |
| SensorRenderManager must pause during reset | Sensors continue rendering during reset, producing stale/corrupt data. | Always Pause() before reset, Resume() after. |
| WorldSaver removes XML comments | ClearAllComments() strips all <!-- --> from the saved file. | Don't rely on XML comments surviving save cycles. |
Key Events Reference
| Event | Source | Fired When | Listeners |
|---|
CLOiSimPlugin.Started | Each plugin | OnStart() coroutine completes | PluginStartTracker |
PluginStartTracker.ProgressChanged(int, int) | Tracker | Each plugin starts | Main → UI progress |
PluginStartTracker.AllStartedEvent | Tracker | All plugins started | Main → finalize load |