v0.2.0
A breaking release that refines the public API of the Python client resoio,
cleaning up leaks from implementation details. It also reworks the locomotion
input model into a partial-update scheme and fixes a bug where view rotation
(yaw/pitch) was not applied on the live client. Migrating from 0.1.x requires
following the API changes listed under Removed / Changed below.
Added
- Python
resoio: Added the received-chunk typeSpeakerChunkand the
generated proto response typesListSessionsResponse/ListRecordsResponse/
FetchThumbnailResponsefor theWorldmodality to the top-level exports
Changed
- Python
resoioLocomotion (breaking): Reworked movement input from the
single-shot commandLocomotionCmd(all fields required) into the partial
updateLocomotionClient.send(field=None)that sends only changed fields.
Fields left asNoneare not put on the wire, and the Resonite-side bridge
retains the previous value. The drive summary is obtained from the
drive_summaryproperty after theasync withblock exits. Under the hood,
the 8 control fields of the protoLocomotionCommandwere madeoptional
(field presence), andLocomotionPartialInput+MergeIntowere added to the
C# Core to merge only present fields into the held state - Python
resoioSpeaker (breaking): Renamed the received-chunk type
AudioChunktoSpeakerChunk. Removed the constantsCHANNELS/DTYPE/
SAMPLE_RATEfrom the top-level exports (kept at theresoio.speaker
module level since they collide with microphone names) - Python
resoioMicrophone (breaking): Removed the wrapper type
MicrophoneAudioChunk;stream()/paced()now take a raw NumPy ndarray
directly (frame_id / unix_nanos are managed automatically by the library) - Python
resoioCamera (breaking): ChangedFrame.width/height/
channelsinto read-only properties derived frompixels. Removed the
width/height/fps_limitarguments fromstream()(resolution config
is the responsibility of the Display modality) - Python
resoioWorld (breaking): Removed the output mirror dataclasses
RecordPage/SessionPage/Thumbnailand now expose the generated proto
response types directly. The input-side enum remaps (RecordSort, etc.) are
retained - Python
resoio(breaking): Moved the socket exceptions
AmbiguousSocketError/SocketNotFoundErrorfromresoio.connectionto the
internalresoio._clientand re-exported them from the top level. The
resoio.connectionmodule is now purified toPingonly (the top-level
import names are unchanged, butfrom resoio.connection import AmbiguousSocketErroretc. is breaking) - Thunderstore mod: Bundled
CHANGELOG.mdandLICENSEin the distributed
package - Thunderstore mod: Expanded publish categories (added
tools/audio/
controlsin addition tomods) - Documentation: Documented Linux-only support (no Windows support) in the
README and docs site
Removed
- Python
resoio: RemovedLocomotionCmd/AudioChunk/
MicrophoneAudioChunk/CHANNELS/DTYPE/SAMPLE_RATE/RecordPage/
SessionPage/Thumbnailfrom the top-level exports (following the API
rework under Changed above)
Fixed
- Thunderstore mod: Fixed an issue where the distributed package bundled the
entire ASP.NET Core shared framework, bloating to 131 files / 24MB (including
unused DLLs for Blazor / MVC / Razor / Identity / SignalR, etc.) and getting
rejected by Thunderstore moderation as "files from another mod mixed in"
(Invalid submission). Switched to an allow-list approach (_BundledAspNetCoreDll)
that narrows the bundled DLLs to just the real dependency closure of GrpcHost
(Kestrel + gRPC), reducing it to 67 files / 4.9MB (no change to functionality
or wire compatibility) - Thunderstore mod: Fixed the distributed package not including the Camera v2
Renderer-side plugin (ResoniteIO.Renderer). BecauseUnityEngine.CoreModule
is non-redistributable and the renderer cannot be built in CI, the
locally-built artifact is committed as a prebuilt (mod/prebuilt/renderer/,
guarded by a source-hash drift check injust runand CI) and pack/CI bundle
it verbatim intoRenderer/ResoniteIO.Renderer/, which the Gale BepisLoader
installer routes toRenderer/BepInEx/plugins/ - mod Locomotion: Fixed an issue where view rotation (yaw/pitch) did not reach
the engine while cursor lock was absent, so the avatar would not turn. Because
ScreenCameraInputs.Look.Activeis gated onInputInterface.IsCursorLocked,
a low-priority cursor lock is acquired internally only while look input is
active to satisfy the precondition (released when input is 0 / on Dispose,
etc.; an existing cursor lock is not overwritten) - Python
resoioLocomotion: FixedLocomotionClient.__aexit__leaking the
connection by skipping channel close when the drive task raised; it now always
closes via try/finally