mujoco-react 12.1.0 → 12.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mujoco-react",
3
- "version": "12.1.0",
3
+ "version": "12.1.1",
4
4
  "description": "Composable React Three Fiber building blocks for MuJoCo WASM simulations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,7 +29,7 @@ export class RuntimeClient implements SimulationRuntime {
29
29
  constructor(
30
30
  private worker: WorkerPort,
31
31
  private apply: (display: DisplayState) => void,
32
- private release: () => void,
32
+ private release: (restoreViewer: boolean) => void,
33
33
  private capture?: (options: PolicyCameraFrameCaptureOptions) => ReturnType<typeof capturePolicyCameraFrames>,
34
34
  ) {
35
35
  worker.onmessage = event => {
@@ -132,12 +132,12 @@ export class RuntimeClient implements SimulationRuntime {
132
132
  return this.exclusive(async () => { await this.request({ kind: 'reset' }); return this.state; });
133
133
  }
134
134
  async dispose() { await this.cancel(); this.close(); }
135
- close(error = new Error('Simulation runtime disposed')) {
135
+ close(error = new Error('Simulation runtime disposed'), restoreViewer = true) {
136
136
  if (this.closed) return;
137
137
  this.closed = true; this.generation++;
138
138
  this.worker.terminate();
139
139
  this.pending?.reject(error); this.pending = null;
140
- this.release();
140
+ this.release(restoreViewer);
141
141
  }
142
142
  }
143
143
 
@@ -172,15 +172,25 @@ export async function attachSimulationRuntime(
172
172
  data.time = display.time;
173
173
  invalidate();
174
174
  };
175
- const client = new RuntimeClient(worker, apply, () => {
175
+ const client = new RuntimeClient(worker, apply, restoreViewer => {
176
176
  // Revert partial display frames to the last acknowledged complete state.
177
- if (api.mjModelRef.current === model && complete) apply(complete);
178
- owners.delete(model);
177
+ try {
178
+ if (api.mjModelRef.current === model && complete) {
179
+ apply(complete);
180
+ // Native contact/constraint structures cannot be mirrored as typed arrays.
181
+ // Rebuild once when returning ownership, never on progress frames or unmount.
182
+ if (restoreViewer) {
183
+ module.mj_forward(model, data);
184
+ const state = complete.integration!;
185
+ module.mj_setState(model, data, Array.from(state.values), state.signature);
186
+ }
187
+ }
188
+ } finally { owners.delete(model); }
179
189
  }, async cameras => {
180
190
  await waitForDisplay();
181
191
  return capturePolicyCameraFrames(api, cameras);
182
192
  });
183
- owners.set(model, { close: () => client.close(), contacts: () => client.state.contacts });
193
+ owners.set(model, { close: () => client.close(undefined, false), contacts: () => client.state.contacts });
184
194
  try { await client.initialize({ model: binary, state: initialState }); return client; }
185
195
  catch (error) { client.close(); throw error; }
186
196
  }