Releases: MLShukai/ResoniteIO
Release list
v0.7.0
✨ Added
resoio launchruns multiple instances:launchno longer refuses when a
Resonite is already running — start as many as you like by giving each its own
--data-path(so they don't share a database).--name <label>is a
convenience that auto-allocates an isolated data tree under
~/.resonite-io/instances/<label>/— a separate WINEPREFIX,-DataPath/
-CachePath/-LogsPath, and a private Camera IPC queue token (so concurrent
instances neither share Resonite data nor cross-talk on the renderer frame
queue). Explicit--prefix/--data-pathstill win over the label defaults;
--nameimposes no guard. Target a specific instance with
--socket ~/.resonite-io/resonite-{pid}.sock.resoio terminate-all: stop every running Resonite instance in one call
(SIGTERM→SIGKILLper process), printing one engine/renderer pair per
instance (--format human|json). The multi-instance counterpart to the
single-instanceterminate(whose pid-only output is unchanged).- Typed Resonite launch options (
resoio.LaunchOptions): pass Resonite's
own command-line launch arguments tolaunch(options=...)as a typed,
immutable value object —data_path(-DataPath),cache_path(-CachePath),
logs_path(-LogsPath),screen,verbose,device,cloud_profile,
load_assembly, and the rest of the documented flags — instead of
hand-assembling rawextra_argsstrings.skip_intro_tutorialdefaults to
True(the previous always-on behaviour);extra_argsremains the escape
hatch for anything not modelled (including-Renderer/-AttachRenderer,
which are intentionally not exposed because they break renderer-PID detection).
The CLI gainsresoio launch --data-path/--cache-path/--logs-path;
other options still go after--. resoio launch --prefix/--proton-path: choose the Wine prefix
(WINEPREFIX) and the Proton build (PROTONPATH) per launch.--proton-path
takes a compatibility-tools name likeGE-Proton(the default) or a path.resoio display setresolution presets andWIDTHxHEIGHT[@FPS]shorthand:
setnow takes an optional positional spec, soresoio display set fhd(1920×1080),
resoio display set qhd@144, orresoio display set 1280x720@60work instead of
spelling out-W/-H/-F. Presets (case-insensitive):hd1280×720,fhd1920×1080,
qhd2560×1440,uhd3840×2160; the@FPSsuffix is optional. The classic
-W/--width/-H/--height/-F/--max-fpsflags still work and override the spec
field-by-field (e.g.fhd -W 1024keeps fhd's height but sets width 1024;fhd@30 -F 144keeps fhd's resolution but caps fps at 144).
🐛 Fixed
- Camera froze right after the engine and renderer came up (no
--name, and
on direct Gale / Steam launches): the engine self-generated a Camera IPC
queue token at runtime that the renderer (a separately-spawned Wine child)
never inherited, so the two bound different queues and the client hung waiting
for frames. The engine no longer self-generates a token. Instead the token is
always placed in the environment before exec:resoio launchinjects a
unique one per instance (named or not), and a direct Gale / Steam launch sets
nothing, so the engine and renderer both fall back to the same fixed default
queue name. Either way both ends agree on one queue. (--namewas already
unaffected because it injected the token before exec.) resoio terminate-allreported a single instance as two: it paired an
engine with its renderer only when the renderer appeared in the engine's host
process subtree, but Wine often reparents the renderer out of it, so one
instance came back as twopid=0rows. Pairing now keys off the Camera IPC
queue token the engine and renderer share (falling back to the process tree),
so a single instance is reported as one engine/renderer pair.resoio launchhung on a host install under$HOME: the renderer never
started (and the engine waited forever) when launching from a Steam install
beneath$HOME. umu/Proton's game-drive feature maps$HOMEonto a Wine drive
letter, so the launch directory (the process's cwd) landed on a non-Z:drive
and the renderer's absolute Unix paths (the renderer executable, the/dev/shm
IPC) misresolved.launchnow setsPROTON_SET_GAME_DRIVE=0(and defaults
GAMEID/PROTONPATH=GE-Proton) so the prefix exposes onlyc:/z:— the
same layout Steam's prefix uses — and a$HOME-resident install launches. The
dev container was unaffected (its install lives outside$HOME).resoio launchrejected a relative mod profile: a relative--profile/
MOD_PATHreached BepInEx as a relative--bepinex-target, which BepisLoader
rejects ("is not an absolute path"). The profile is now resolved to an absolute
path before launch.
v0.6.1
🐛 Fixed
resoio launchcould not find a properly installed mod:launchlooked for
the plugin DLL only at the flat pathBepInEx/plugins/ResoniteIO/ResoniteIO.dll,
but a Thunderstore / Gale install nests it one level deeper —
BepInEx/plugins/ResoniteIO/ResoniteIO/ResoniteIO.dll, with the package's
project files (manifest.json/icon.png/ …) sitting directly under
BepInEx/plugins/ResoniteIO/(the same layout every other mod uses).launch
now searchesBepInEx/plugins/recursively forResoniteIO.dll— the way
BepInEx itself discovers plugins — so it works regardless of how the mod was
installed (nested Thunderstore layout, namespaced package dir, or a flat copy).
🔧 Changed
just deploy-modnow deploys the real distribution artifact: instead of
copying loose DLLs into a flatBepInEx/plugins/ResoniteIO/,deploy-modbuilds
the Thunderstore package (just mod-pack) and unpacks it into the Gale profile
exactly the way Gale's installer does — project files +plugins/→
BepInEx/plugins/ResoniteIO/(DLLs nested under…/ResoniteIO/ResoniteIO/), and
Renderer/→Renderer/BepInEx/plugins/ResoniteIO/(renderer DLLs under
…/ResoniteIO/ResoniteIO.Renderer/). The local dev layout now
matches what users get from Thunderstore. The build-time auto-deploy (the mod and
renderer csprojPostBuildcopies into the Gale profile) is removed; deployment
goes throughjust deploy-modonly.- Documentation: Reworded the
resonite-io(Python) and ResoniteIO (mod) READMEs and the
Thunderstore package description to drop the "runtime / execution environment for AI agents"
framing, describing ResoniteIO neutrally as a bidirectional IPC bridge that lets your Python
code observe and control Resonite (matching the repository README and the docs site). - Documentation: Documented that the optional
resoio launch/resoio terminate
commands require umu-launcher
(umu-run) onPATH, in the installation guide and theresonite-ioREADME.
v0.6.0
✨ Added
-
resoio launch/resoio terminate(start/stop Resonite via umu-launcher):
New commands and Python functions (resoio.launch/resoio.terminate) that
start and force-stop the Resonite client without gRPC.launchspawns the umu-launcher chain and PID-diffs the engine
(resonite_pid) and renderer (renderer_pid) host processes into
existence, returning both as aLaunchResult.terminatestagesSIGTERM→SIGKILLover those two PIDs (or auto-detects
the single running instance when given none, erroring if more than one is
found).RESONITE_EXE(default: the Steam install) andMOD_PATH(the Gale profile
with the mod deployed) select the install; the ResoniteIO mod must be
installed (via Gale / Thunderstore) orlauncherrors with guidance.- The cooperative gRPC quit stays available as
resoio shutdown. - Exposed as
resoio.launch/resoio.terminate/LaunchResult/
LauncherErrorand theresoio launch(-e/--exe/-p/--profile/
--vanilla/--format human|json) andresoio terminate
([resonite_pid] [renderer_pid]) CLI commands.
-
Run Resonite inside the dev container: the dev container can now launch
Resonite itself via the newresoio launch/resoio terminatecommands (and
the thinjust resonite-launch/just resonite-stopwrappers).- The container entrypoint rsyncs the read-only
/resonitebind into a writable
/opt/resonite, andresoio launchstarts Resonite throughumu-run/
Proton with the ResoniteIO mod loaded from the./galeGale profile (the
first run pulls GE-Proton and copies the ~2 GB install).resoio launch --vanillaruns vanilla Resonite with no mod. - Engine side loads via hookfxr, renderer side via a doorstop
winhttp.dll—
resoio launchsetsWINEDLLOVERRIDES="winhttp=n,b"automatically, so no
manual Steam-styleWINEDLLOVERRIDESsetup is needed on this path. - The whole mod loop runs inside the container: the mod (GrpcHost) creates its
gRPC socket under the container's~/.resonite-io/(it makes the directory
itself before binding) and the Python client connects there — no host
bind-share. - The mod's BepInEx log stays at
gale/BepInEx/LogOutput.log(just log);
umu/Proton launch noise is split intogale/BepInEx/umu-launch.log. - Rendering needs a host graphical session (X11 / Xwayland) and
PipeWire/PulseAudio for audio. NVIDIA / AMD / Intel GPUs are all supported
—initialize.shdetects the vendor and selects the matching per-vendor
compose overlay (.devcontainer/compose.{nvidia,amd,intel}.ymlvia the
compose.gpu.ymlsymlink). - Requires
kernel.apparmor_restrict_unprivileged_userns=0on the host
(pressure-vessel needs unprivileged user namespaces, which Ubuntu 24.04+
restricts by default); the container start hard-fails without it. - The dev image base also moved from
debian:bookworm-slimtodebian:13-slim
(trixie), andcompose.ymlmoved from the repo root to
.devcontainer/compose.yml.
- The container entrypoint rsyncs the read-only
-
Contactmodality: A new unary modality that drives the dash "Contacts"
tab by reading/writing the cloud contact list (Engine.Cloud.Contacts/
Engine.Cloud.Users) directly — no UI automation.ListContactsreturns the synced contacts with presence (online status +
current session name / access level) plus the contact / request counts and a
list-loaded flag, with client-sidesearch(username / alternate-username
substring) andfilter(accepted friends / incoming requests). Like the dash
tab it hidesShouldBeHiddencontacts (ignored / blocked / none) by default —
passinclude_hiddento include them, and every contact carries an
is_hiddenflag.GetContactfetches one by user id (absent →found=false).SearchUsers
queries the cloud for users to add (exact or substring, read-only).AddContact(resolving the username mod-side when omitted),AcceptRequest,
andRemoveContactmutate the list; remove declines a request / deletes a
friend, which the engine marksIgnoredso the entry drops out of the default
(hidden) list.- Unknown ids return
NotFound, cloud failuresInternal, and an unavailable
cloudFailedPrecondition. - Exposed as
ContactClientand the nestedresoio contactCLI (list(with
--include-hidden) /get/search/add/accept/remove, each
with--format human|json).
-
Authmodality: A new unary modality for Resonite cloud authentication —
sign in / out and read the auth status — drivingEngine.Cloud.Session
directly (Login/Logout/Status, all returning a unifiedAuthStatus
oflogged_in/user_id/user_name/session_expires_unix_nanos).logintakes a credential (username / email /U-id) and a password (plus
an optionaltotpfor 2FA) andremember_me(default true), which delegates
session persistence to the engine — resoio stores no credentials on disk.- Wrong credentials return
Unauthenticated; a 2FA-enabled account with
no/blank code returnsFailedPrecondition, and the CLI then prompts for the
code and retries once. - Security: the plaintext password is never persisted, logged, placed in an
exception / gRPC status detail, or--format jsonoutput, and there is no
--passwordCLI flag — the password comes only fromRESONITE_IO_PASSWORD,
piped stdin, or a hidden prompt. - All three leaves support
--format human|json; the humanstatusoutput
renders the session expiry as a UTC datetime, and the--format jsondocument
adds a derived ISO-8601session_expires_isonext to the exact
session_expires_unix_nanos. When the credential is omitted, the interactive
prompt readsUsername or Email— a username, email, or user id is accepted. - Exposed as
AuthClientand the nestedresoio auth login/logout/
statusCLI.
-
Sessionmodality: A new unary userspace modality that drives the dash
"Session" dialog — the connected session's Settings, Users, and Permissions
tabs — by reading/writingWorld.Configuration/World.AllUsers/
World.Permissionsdirectly (no UI automation).- Settings use a get + partial-apply model (
GetSettings/
ApplySettings): world name/description, max users, access level,
hide-from-listing, mobile-friendly, away-kick, auto-save, auto-cleanup, and
tags. Partial updates useproto3 optionalpresence, sofalse/0can be
set explicitly and unset fields are left untouched (tagsuse a
replace_tagsgate);ApplySettingsreturns nothing — callGetSettingsto
read the new state. - Users expose
ListUsersplus host-gatedKickUser/BanUser/
SilenceUser/RespawnUser/SetUserRole; targets resolve byuser_id
(preferred),user_name, orlocal(self), andrespawndefaults to self. - Permissions expose
ListRoles(with the default
anonymous/visitor/contact/host/owner roles) andGetUserRoleOverrides. - Host-gated operations return
PermissionDeniedwhen the local user lacks the
right, and out-of-rangemax_usersreturnsInvalidArgument. - Exposed as
SessionClientand the nestedresoio sessionCLI (settings get/set,users list,user kick/ban/silence/respawn/role,roles list,overrides list).
- Settings use a get + partial-apply model (
-
resoio shutdown/resoio.shutdown: The graceful-stop command and
convenience function are now namedshutdown, matching Resonite's terminology
and theLifecycle.ShutdownRPC. Behaviour is unchanged — it reads the engine
PID fromInfo(for reporting) and sendsLifecycle.Shutdown; the engine quits
itself and Steam/Proton reaps the renderer + launch wrappers. Prints / returns
the engine's host PID, or "resonite not running" /Nonewhen no engine is
reachable. -
resoio --format human|json: Commands that return structured data (ping,
info,display,cursor,grabber,context-menu,dash,world,mic,
session) gained a--formatflag.human(default) keeps the existing text
output unchanged;jsonprints one machine-readable document to stdout (proto
field names in snake_case, enums as their name, big ints exact, non-ASCII
preserved).--formatis not added to pid/path-only commands (shutdown/
terminate,screenshot/record/world thumbnail), interactive commands
(drive/grabber interactive/inventory), or the side-effect-only
session user kick/ban/respawnleaves. -
resoio wait/resoio.wait_for_ready: A new startup-readiness gate that
blocks until the Resonite IO server answersConnection.Ping.- The public async
wait_for_ready(socket_path=None, *, timeout=None, interval=0.1)polls until a ping round-trips and returns the resolved socket
path, retrying while the socket is absent, has no listener yet, or the engine
is still warming up (FAILED_PRECONDITION);AmbiguousSocketErrorand other
gRPC errors propagate, andtimeout(None= wait forever) raises
TimeoutError. - The
resoio waitCLI wraps it: it prints the resolved socket path on success,
takes an optionalpidto targetresonite-{pid}.sock, and-T/--timeout
(default 30s,<=0tries once) bounds the wait.--formatis not added
(path-only output).
- The public async
-
Grabber post-grab interactions (
Use/Unuse/Equip/Dequip): The
Grabber service gained four unary RPCs (all returningGrabberGrabState) for
operating what a hand holds.Usepresses a virtual button (primary= left-click /secondary=
right-click) and holds it down untilUnuse, driven by a per-tick
ExternalInputre-injection repeater (Locomotion-style) so the press survives
across RPCs.primaryinjects both the d...
v0.5.0
Adds the Lifecycle modality (graceful shutdown), a one-shot camera
screenshot, inventory thumbnail fetching, and engine/renderer host PIDs in
Info. Also breaking: the Dash modality is redesigned from a flat tree to
a tab/control model (gRPC route / C# surface / Python client + CLI all change),
so update the ResoniteIO mod and the resoio Python package in lockstep.
Added
Lifecycle.ShutdownRPC: A newLifecyclemodality with a unary
ShutdownRPC that asks the engine to quit gracefully
(Engine.RequestShutdown, the in-app Quit path). The mod schedules the
shutdown on the engine tick and ACKs before the process tears down, so the
RPC returns promptly and the engine exits asynchronously. Exposed as
LifecycleClient.shutdown()ServerInfo.resonite_pid/renderer_pid:Info.GetServerInfonow also
reports the engine (Resonite.exe) and renderer (Renderite.Renderer.exe)
host PIDs. The engine runs natively on Linux (is_wine=false), so
resonite_pid(Environment.ProcessId) andrenderer_pid
(RenderSystem.RendererProcess,0when headless) are real host kernel PIDs.
Surfaced onServerInfoand in theresoio infooutput (newresonite_pid=/
renderer_pid=lines)resoio terminate/resoio.terminate: Stops the running Resonite client
gracefully — it reads the engine PID fromInfo(for reporting) and sends
Lifecycle.Shutdown; the engine quits itself and Steam/Proton reaps the
renderer + launch wrappers. A pure gRPC call (no OS signals), so it works from
anywhere the UDS is reachable. Prints / returns the engine's host PID, or
"resonite not running" /Nonewhen no engine is reachableCameraClient.shot()andresoio screenshot: A convenience method that
captures a single Camera frame and closes the stream (instead of opening a
stream and breaking on the first frame), plus a CLI command that saves it as
an opaque PNG.resoio screenshottakes-o/--output(a.pngpath or
-for stdout) and otherwise writes a timestamped
screenshot_<timestamp>.pngto the current directory. The PNG drops the alpha
channel, because the engine framebuffer's non-opaque alpha would otherwise
render washed-out in image viewers. Exposed asCameraClient.shot()Inventory.FetchThumbnailRPC: A unary RPC that returns the thumbnail
image of an inventory item, resolving the item'sRecord.ThumbnailURI
server-side and returning the raw image bytes plus their content type (e.g.
"image/webp", returned verbatim from the Resonite CDN, not re-encoded).
Exposed asInventoryClient.fetch_thumbnail()(returning the new
InventoryThumbnaildataclass) and thethumbcommand in the interactive
resoio inventoryREPL
Changed
- Breaking — the Dash modality is redesigned to a tab/control model: the
flat-tree contract (GetTree/ListScreens/SetScreenwith the
DashTree/DashElement/DashRect/DashScreen/DashScreenList
messages) is replaced by a tab-first model. The bottom tab bar is enumerated
withListTabs→DashTabList, the current tab's controls (Button /
ScrollRect) withListControls→DashControlList, and a newSetTabRPC
switches tabs (Open/Close/GetState/Invoke/Scroll/
Highlightare retained). On the Python side theDashTree/DashElement/
DashScreendataclasses are replaced byDashTab/DashControl, and the
resoio dashCLI is restructured intoopen/close/state/tabs/
tab/ls/invoke/scroll/highlight(tab navigation first, then
control interaction within the current tab). The gRPC route is wire-broken by
the message/RPC changes — update the ResoniteIO mod and theresoioPython
package in lockstep
v0.4.0
A breaking release that renames the Manipulation modality to Grabber
(gRPC route / C# surface / Python module all change), makes Cursor
SetPosition a persistent hold until Release, switches Grab to a
ray-based targeting model, and redesigns the CLI
(manipulate → grab, locomotion drive → drive,
display split into get / set). Update the ResoniteIO mod and the
resoio Python package in lockstep.
Changed
- Breaking — the Manipulation modality is renamed to Grabber: the gRPC
route changed from/resonite_io.v1.Manipulation/*to
/resonite_io.v1.Grabber/*(manipulation.proto→grabber.proto,
Manipulation*messages →Grabber*), the C# surface is now
GrabberService/IGrabberBridge/FrooxEngineGrabberBridge, and the
Python module and client areresoio.grabber/GrabberClient(the
GrabResult/GrabStatedataclasses keep their names). An old mod and a
new client (or vice versa) cannot talk over the renamed route — update the
ResoniteIO mod and theresoioPython package in lockstep - Breaking — Cursor
SetPositionnow holds the cursor untilRelease:
SetPositionwas a one-shot warp (the engine cursor reverted to the OS
pointer on the next frame, especially under Wine/Proton). It now registers a
persistent engine-side cursor lock so the in-engine cursor stays at the set
position across RPCs, while a Harmony patch on
InputInterface.CollectOutputStatemasks the lock from the renderer so the
real OS mouse pointer is never captured (no warp, no confine, no
center-pin).InputInterface.SetMousePosition(OS warp) is no longer called.
While held, real mouse movement does not move the in-engine cursor (clicks
still fire at the held position); switching world focus deactivates the hold - Breaking —
Manipulation.Grabis now ray-based:
ManipulationGrabRequest.point(WorldPoint) was removed (field 2 is
reserved). Grab always targets the point where the desktop cursor ray hits
the world and grabs grabbables withinradiusof that point. A ray miss
returnsgrabbed=false(not an error); VR mode (screen output inactive)
returnsFAILED_PRECONDITION. The Python client'sManipulationClient.grab
lost itspointparameter and the CLI lost--point. Aim with
resoio cursor set X Y(held until release), thenresoio grab - Breaking —
resoio displayis split intoget/setsubcommands: the
implicit branching ("no flags = get, any flag = set") is gone.
resoio display getprints the current snapshot;resoio display set
requires at least one of-W/--width,-H/--height,-F/--max-fpsand
prints the post-apply snapshot - Breaking —
resoio locomotion driveis flattened toresoio drive: the
locomotioncommand group is removed; the flags
(--sprint/--look-rate/--no-wait) are unchanged - Breaking —
resoio manipulateis renamed toresoio grab(following
the modality rename): the action positional
(grab/release/state/interactive) is optional and defaults to
grab, and--hand/--radiusare accepted before or after the action - CLI required arguments are now enforced by argparse:
resoio cursor set <x> <y>,resoio dash invoke <ref_id>, and
resoio context-menu highlight/invoke <index>reject missing positionals as
a usage error at parse time instead of failing mid-command. The argv shape
of valid invocations is unchanged
Added
Cursor.ReleaseRPC: releases the held cursor and returns control to the
OS pointer. Idempotent (releasing while not held succeeds and returns the
current state). Exposed asCursorClient.release()and
resoio cursor releaseCursorState.heldfield: reports whether the cursor is currently held,
returned bySetPosition/GetPosition/Releaseand shown in the CLI
output (held=True/False)
Fixed
- Grabbed objects no longer fly behind the user's head: in desktop mode
the hand moves from its rest pose to a holding pose right after a grab, and
a far-away grab left a large holder-local offset that got swung around with
the hand. The bridge now pins the grabbed object at its grab-time pose (the
cursor position) until the hand settles, so it stays where it was grabbed
and follows the hand from there
v0.3.0
Adds a mod/client version-compatibility check and switches distribution to
GitHub Release only (Thunderstore upload paused).
Added
Connection.GetModVersion: Added a unary RPC that returns the running
mod version. The Python client probes it once per process on first connect
(_BaseClient.__aenter__) and warns when the mod is older than or mismatched
with the client, pointing to the GitHub Release for an aligned build. The
probe never fails the connection (errors are swallowed). Exposed on the Python
side asConnectionClient.get_mod_version(). TheModInfois injected into
the CoreConnectionService(Core ← Mod direction preserved; the Mod supplies
PluginMetadata.VERSIONviaGrpcHost.Start(modVersion))
Changed
- Distribution: Switched to distributing via GitHub Release only.
Thunderstore upload is paused (package unapproved + layout mismatch); the mod
is installed through Gale'sImport > Local mod.... The zip build pipeline
(tcli/PackTS/thunderstore.toml) is retained for future store
re-enablement, and thegithub-releasejob additionally attaches a fixed-name
ResoniteIO.zipfor a stablereleases/latest/download/ResoniteIO.zipURL.
README / installation docs now describe the GitHub Release zip → Gale local
import flow (supporting plugins must be installed beforehand, since local
import does not auto-resolve dependencies)
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
v0.1.1
0.1.0 公開後に判明したパッケージング不備の hotfix。配布物が動作しない 2 件を修正。
Fixed
- Thunderstore mod: 配布パッケージに
ResoniteIO.dll/.pdbしか含まれず、
Core/Mod 二層のResoniteIO.Core.dllや Kestrel/gRPC ランタイム DLL が欠落して
mod がロードできなかった問題を修正。Gale deploy と同じ@(PluginFiles)集合を
staging して同梱するようにし、必須 DLL を漏れなくパッケージする
(StageThunderstorePlugintarget +thunderstore.toml) - Python
resonite-io:pip install resonite-io後のimport resoioが
betterproto2 の version 不整合でImportErrorになる問題を修正。runtime 依存を
生成物の compiler と major.minor 一致させbetterproto2[grpclib]>=0.10,<0.11に
固定 (compiler 0.10.1 で_generated/を再生成、dev も lockstep)
v0.1.0
最初の公開リリース。Resonite を AI エージェントの実行環境として使うための双方向 IPC
ブリッジ (C# Mod ResoniteIO ↔ Python パッケージ resoio、gRPC over Unix Domain
Socket) の基盤一式。
Added
- IPC 基盤: gRPC over Unix Domain Socket による双方向ブリッジ。本番 IPC は
$HOME/.resonite-io/、debug bridge は$HOME/.resonite-io-debug/の UDS を使用 - C# Core/Mod 二層アーキテクチャ: Resonite 非依存のピュアライブラリ
ResoniteIO.Core(gRPC server / Service / 各モダリティのドメインロジック) と、
engine bridging のみを担う薄い BepInEx adapterResoniteIO(BepisLoader) に分離。
依存方向は Core ← Mod - モダリティ群 (各モダリティは独立した非同期ストリーム):
Connection(Ping) /
Camera(server-streaming RGB フレーム) /Speaker(server-streaming 音声、
Resonite → Python) /Microphone(client-streaming 音声、Python → Resonite) /
Locomotion(client-streaming) /Manipulation(Grab/Release unary) /
Display/World/ContextMenu/Dash/Inventory/
Cursor(desktop カーソルを正規化座標で set/get) - Python パッケージ
resoio: モダリティ単位の async クライアント
(ConnectionClient/CameraClient/SpeakerClient/MicrophoneClient/
LocomotionClient/ManipulationClient/DisplayClient/WorldClient/
ContextMenuClient/DashClient/InventoryClient/CursorClient)。
betterproto2 + grpclib ベース、pyright strict 準拠 - CLI
resoio: action 名 flat command (ping/record/mic/
locomotion/manipulate/display/world/context-menu/dash/
inventory/cursor)。recordは--video/--audioフィルタで
Camera/Speaker を mp4/mkv 取得、micは Microphone を Resonite に流す - proto 定義:
proto/resonite_io/v1/を single source of truth とし、Python 側
生成物を commit、C# 側は csproj が build-time 生成 - 開発環境:
debian:bookworm-slimベースの devcontainer
(compose.yml/.devcontainer/)、justfileタスクランナー、container ↔ host
Resonite bridge スクリプト (scripts/host_agent.py/scripts/resonite_cli.py) - CI / リリース / ドキュメント: GitHub Actions の品質ゲート
(pre-commit/test/type-check/dotnet/proto-check)、v*tag 駆動で
Thunderstore mod + PyPI パッケージを同時公開するpublish.yml、mike による
バージョン付きドキュメントサイト (MkDocs Material)