### Build and Run a Basic OSCQuery Service Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This example demonstrates how to create a minimal OSCQuery service. It configures TCP and UDP ports, sets a service name, and applies default settings. The service runs until a key is pressed. Note that this basic setup does not send or receive OSC messages. ```csharp var tcpPort = Extensions.GetAvailableTcpPort(); var udpPort = Extensions.GetAvailableUdpPort(); var oscQuery = new OSCQueryServiceBuilder() .WithTcpPort(tcpPort) .WithUdpPort(udpPort) .WithServiceName("MyService") .WithDefaults() // Configure settings BEFORE calling WithDefaults() .Build(); // Manually logging the ports to see them without a logger Console.WriteLine($"Started OSCQueryService at TCP {tcpPort}, UDP {udpPort}"); // Stops the program from ending until a key is pressed Console.ReadKey(); ``` -------------------------------- ### Build OSCQuery Service with Full Custom Setup Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Configure custom TCP and UDP ports, service name, and host IP before starting the HTTP server and network advertising. Settings must be configured before calling WithDefaults() or Build(). ```csharp using VRC.OSCQuery; using System.Net; // Full custom setup — configure BEFORE WithDefaults() or Build() int tcpPort = Extensions.GetAvailableTcpPort(); int udpPort = Extensions.GetAvailableUdpPort(); var service = new OSCQueryServiceBuilder() .WithTcpPort(tcpPort) // HTTP/OSCQuery port .WithUdpPort(udpPort) // OSC UDP port .WithServiceName("MyOSCApp") // Zeroconf advertised name .WithHostIP(IPAddress.Parse("192.168.1.5")) // Serve on local IP (not loopback) .WithDefaults() // Start HTTP server + advertise .Build(); Console.WriteLine($"OSCQuery running at http://localhost:{tcpPort}"); Console.WriteLine($"OSC listening on UDP {udpPort}"); // Cleanup when done service.Dispose(); ``` -------------------------------- ### Build OSCQuery Service with Minimal Setup Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Use this minimal setup when you want the library to automatically pick available ports for the HTTP/OSCQuery and OSC UDP services. ```csharp using VRC.OSCQuery; using System.Net; // Minimal setup — picks available ports automatically var service = new OSCQueryServiceBuilder().Build(); ``` -------------------------------- ### Start Default OscQueryService Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md Build and start an OSCQueryService with typical settings. This initializes the HTTP server, discovery system, and advertises both OSCQuery and OSC services. ```csharp var oscQuery = new OSCQueryServiceBuilder().Build(); ``` -------------------------------- ### Initialization and Setup Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Initializes the OSCQuery Explorer by setting up root elements, refresh mechanisms, search functionality, and compact mode. It then fetches API data and renders the explorer. ```javascript (async () => { setupRoot(); setupRefresh(); setupAutoRefresh(); setupSearch(); setupCompactMode(); await fetchApi(); render(); })(); ``` -------------------------------- ### Configure and Start OscQueryService Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md Configure specific settings like TCP port and service name before building and starting the OSCQueryService. Ensure configurations are made before calling .WithDefaults(). ```csharp var oscQuery = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithServiceName("MyService") .WithDefaults() .Build(); ``` -------------------------------- ### OSCQueryServiceBuilder - Fluent Service Construction Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Demonstrates how to create and configure an OSCQueryService using the fluent OSCQueryServiceBuilder API. It covers minimal setup and full custom setup with specific port, service name, and host IP configurations. ```APIDOC ## OSCQueryServiceBuilder - Fluent Service Construction `OSCQueryServiceBuilder` creates and configures an `OSCQueryService` using a chainable fluent interface. Settings must be configured **before** calling `WithDefaults()` or `Build()`, since `WithDefaults()` immediately starts the HTTP server and network advertising. Calling `Build()` without any prior configuration automatically invokes `WithDefaults()`. ```csharp using VRC.OSCQuery; using System.Net; // Minimal setup — picks available ports automatically var service = new OSCQueryServiceBuilder().Build(); // Full custom setup — configure BEFORE WithDefaults() or Build() int tcpPort = Extensions.GetAvailableTcpPort(); int udpPort = Extensions.GetAvailableUdpPort(); var service = new OSCQueryServiceBuilder() .WithTcpPort(tcpPort) // HTTP/OSCQuery port .WithUdpPort(udpPort) // OSC UDP port .WithServiceName("MyOSCApp") // Zeroconf advertised name .WithHostIP(IPAddress.Parse("192.168.1.5")) // Serve on local IP (not loopback) .WithDefaults() // Start HTTP server + advertise .Build(); Console.WriteLine($"OSCQuery running at http://localhost:{tcpPort}"); Console.WriteLine($"OSC listening on UDP {udpPort}"); // Cleanup when done service.Dispose(); ``` ``` -------------------------------- ### Build and Configure OSCQuery Service Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/Readme.md Construct a new OSCQuery service, customizing ports and service name before starting the HTTP server and advertising. Ensure to call `WithDefaults()` after all configurations. ```csharp var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithUdpPort(Extensions.GetAvailableUdpPort()) .WithServiceName("MyService") .WithDefaults() .Build(); ``` -------------------------------- ### Set Up Periodic Service Discovery Refresh Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This example configures a timer to periodically call RefreshServices every 5 seconds, ensuring the list of discovered OSCQuery services stays up-to-date. ```csharp var refreshTimer = new Timer(5000); refreshTimer.Elapsed += (s,e) => { queryService.RefreshServices(); } refreshTimer.Start(); ``` -------------------------------- ### Setup Search Input Listener Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Attaches an event listener to the search input field to trigger filtering of elements as the user types. ```javascript const setupSearch = () => { const searchInput = document.getElementById('pathFilter'); searchInput.addEventListener('input', () => { const search = searchInput.value.toLowerCase(); filterElements(search); }); } ``` -------------------------------- ### Register OSC Method with Explicit Type String Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Register an OSC address path with metadata, including an explicit OSC type string. The path must start with '/'. Duplicate paths are silently skipped with a warning. Returns true on success, false on failure. ```csharp using VRC.OSCQuery; var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithDefaults() .Build(); // Register using explicit OSC type string // OSC type chars: i=int, f=float, s=string, T=bool, etc. service.AddEndpoint( "/avatar/parameters/GestureLeft", "i", // OSC type: integer Attributes.AccessValues.WriteOnly, // app can receive this new object[] { 0 }, // initial value "Left hand gesture index" // description ); // Verify at: http://localhost:{tcpPort}/avatar/parameters/GestureLeft // Expected JSON: // { // "DESCRIPTION": "Left hand gesture index", // "FULL_PATH": "/avatar/parameters/GestureLeft", // "ACCESS": 2, // "TYPE": "i", // "VALUE": [0] // } ``` -------------------------------- ### Add an OSC Endpoint to the Service Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This example shows how to add a new OSC endpoint to an existing OSCQuery service. You must specify the endpoint path, data type, access rights, and a description. The endpoint can be added with or without a generic type parameter. ```csharp queryService.AddEndpoint("/my/fancy/path", "s", Attributes.AccessValues.WriteOnly, "This is my endpoint"); ``` ```csharp queryService.AddEndpoint("/my/fancy/path", Attributes.AccessValues.WriteOnly, "This is my endpoint"); ``` -------------------------------- ### Register OSC Method with C# Generic Type Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Register an OSC address path using C# generic types, which automatically resolve to the correct OSC type string. The path must start with '/'. Duplicate paths are silently skipped with a warning. Returns true on success, false on failure. ```csharp using VRC.OSCQuery; var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithDefaults() .Build(); // Register using C# generic type (auto-resolves OSC type) service.AddEndpoint( "/avatar/parameters/VoiceVolume", Attributes.AccessValues.ReadWrite, new object[] { 0.0f }, "Microphone volume 0.0-1.0" ); service.AddEndpoint( "/avatar/parameters/MuteSelf", Attributes.AccessValues.WriteOnly, new object[] { false }, "Toggle self-mute" ); ``` -------------------------------- ### Get OSC Tree Data Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md Retrieve the full OSC Tree of a service. This operation is asynchronous and requires an async context. ```csharp var tree = await Extensions.GetOSCTree(profile.address, profile.port); ``` -------------------------------- ### Query OSCQuery Server Directly via HTTP Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Examples of querying the OSCQuery server's HTTP endpoints using curl. These endpoints conform to the OSCQuery specification for accessing node trees, specific nodes, and host information. ```bash TCP_PORT=8060 # or whatever port was assigned # Get full OSC node tree (root namespace) curl http://localhost:$TCP_PORT/ # {"DESCRIPTION":"root node","FULL_PATH":"/","ACCESS":0,"CONTENTS":{...}} # Get a specific OSC node curl http://localhost:$TCP_PORT/avatar/parameters/MuteSelf # {"DESCRIPTION":"Toggle self-mute","FULL_PATH":"/avatar/parameters/MuteSelf","ACCESS":2,"TYPE":"T","VALUE":[false]} # Get host info (server name, OSC port, supported extensions) curl "http://localhost:$TCP_PORT?HOST_INFO" # {"NAME":"MyOSCApp","EXTENSIONS":{"ACCESS":true,...},"OSC_IP":"127.0.0.1","OSC_PORT":9001,"OSC_TRANSPORT":"UDP"} # Open the built-in web explorer UI (in a browser) # http://localhost:$TCP_PORT?explorer ``` -------------------------------- ### Initialize OSC Query Explorer Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Sets up the root URL for the OSC Query Explorer. This is typically called on page load. ```javascript const TYPE_MAP = { i: 'int32', u: 'uint32', h: 'int64', f: 'float', d: 'double', s: 'string', c: 'char', '[,\]': 'array', b: 'byte[]', T: 'bool' } const ACCESS_MAP = [ 'none', 'read', 'write', 'read/write' ] const COLUMNS_MAP = [ [1700, 5], [1280, 4], [960, 3], [768, 2], [0, 1], ]; let apiData = {}; let apiIsLoading = false; let apiIsSuccess = false; let apiIsError = false; let apiError = null; let autoRefresh = false; const collapseState = [] let compactModeState = false; const setupRoot = () => { const rootUrl = new URL(window.location.href); rootUrl.pathname = ''; rootUrl.search = ''; document.getElementById('rootUrl').innerText = rootUrl.href; } ``` -------------------------------- ### Initialize OSCQuery Explorer Application Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/Examples/OSCQueryExplorer-Unity/Assets/OSCQuery/Runtime/Plugins/Resources/OSCQueryExplorer.html Initializes the OSCQueryExplorer application by setting up the root element, refresh mechanisms, search functionality, and compact mode, then fetches API data and renders the initial view. ```javascript (async () => { setupRoot(); setupRefresh(); setupAutoRefresh(); setupSearch(); setupCompactMode(); await fetchApi(); render(); })(); ``` -------------------------------- ### Initialize Service Profile List Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This code declares a list to store OSCQueryServiceProfile objects, which represent other running OSCQuery services discovered on the network. ```csharp private List _profiles = new(); ``` -------------------------------- ### Discover and Add Existing Services Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This code iterates through all currently available OSCQuery services discovered by the main service and adds each one to the local profile list. ```csharp foreach(var service in queryService.GetOSCQueryServices()) { AddProfileToList(profile); } ``` -------------------------------- ### Discover OSC and OSCQuery Services Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Discovers OSCQuery or OSC services on the local network and provides event handlers for new service discoveries. Periodically rescans the network. ```csharp using VRC.OSCQuery; using System.Timers; var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithDefaults() .Build(); // Event-driven: fires when a new OSCQuery service appears service.OnOscQueryServiceAdded += profile => { Console.WriteLine($"Found OSCQuery: {profile.name} at {profile.address}:{profile.port}"); }; // Event-driven: fires when a new OSC service appears service.OnOscServiceAdded += profile => { Console.WriteLine($"Found OSC: {profile.name} at {profile.address}:{profile.port}"); }; // Snapshot of currently known services foreach (var profile in service.GetOSCQueryServices()) Console.WriteLine($"Known OSCQuery: {profile.name}"); foreach (var profile in service.GetOSCServices()) Console.WriteLine($"Known OSC: {profile.name}"); // Periodically re-scan (every 5 seconds) var timer = new Timer(5000); timer.Elapsed += (s, e) => service.RefreshServices(); timer.Start(); ``` -------------------------------- ### Initialize Root URL Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/Examples/OSCQueryExplorer-Unity/Assets/OSCQuery/Runtime/Plugins/Resources/OSCQueryExplorer.html Sets the root URL for the OSC Query Explorer based on the current window location. This is typically called on page load. ```javascript const TYPE_MAP = { i: 'int32', u: 'uint32', h: 'int64', f: 'float', d: 'double', s: 'string', c: 'char', '[, ]': 'array', b: 'byte[]', T: 'bool' } const ACCESS_MAP = [ 'none', 'read', 'write', 'read/write' ] const COLUMNS_MAP = [ [1700, 5], [1280, 4], [960, 3], [768, 2], [0, 1], ]; let apiData = {}; let apiIsLoading = false; let apiIsSuccess = false; let apiIsError = false; let apiError = null; let autoRefresh = false; const collapseState = [] let compactModeState = false; const setupRoot = () => { const rootUrl = new URL(window.location.href); rootUrl.pathname = ''; rootUrl.search = ''; document.getElementById('rootUrl').innerText = rootUrl.href; } ``` -------------------------------- ### Fetch Remote Host Info with Extensions.GetHostInfo Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Fetches the HostInfo of a remote OSCQuery service by querying '?HOST_INFO' on its HTTP endpoint. Provides server name, OSC IP, OSC port, and supported extensions. ```csharp using VRC.OSCQuery; using System.Net; // Given a discovered profile var profile = new OSCQueryServiceProfile("VRChat", IPAddress.Loopback, 9001, OSCQueryServiceProfile.ServiceType.OSCQuery); var hostInfo = await Extensions.GetHostInfo(profile.address, profile.port); Console.WriteLine($"Name: {hostInfo.name}"); Console.WriteLine($"OSC IP: {hostInfo.oscIP}, OSC Port: {hostInfo.oscPort}"); Console.WriteLine($"Transport: {hostInfo.oscTransport}"); // Supported extensions foreach (var (ext, supported) in hostInfo.extensions) Console.WriteLine($" {ext}: {supported}"); // Output: // Name: VRChat // OSC IP: 127.0.0.1, OSC Port: 9000 // Transport: UDP // ACCESS: True // CLIPMODE: False // RANGE: True // TYPE: True // VALUE: True ``` -------------------------------- ### Subscribe to New Service Discovery Events Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This snippet shows how to subscribe to the OnOscQueryServiceAdded event. This event fires automatically when a new OSCQuery service is discovered, allowing you to add it to your list. ```csharp queryService.OnOscQueryServiceAdded += (var profile) => { AddProfileToList(profile); } ``` -------------------------------- ### OSCQueryServiceBuilder.WithLogger - Attach a Logger Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Explains how to attach a Microsoft.Extensions.Logging.ILogger to the OSCQueryService for diagnostic output during service startup and operation. ```APIDOC ## OSCQueryServiceBuilder.WithLogger — Attach a Logger Attaches a Microsoft.Extensions.Logging `ILogger` for diagnostic output. By default, a `NullLogger` is used and no output is produced. Useful for debugging service startup, endpoint additions, and HTTP request errors. ```csharp using Microsoft.Extensions.Logging; using VRC.OSCQuery; // Create a console logger factory using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); var logger = loggerFactory.CreateLogger(); var service = new OSCQueryServiceBuilder() .WithServiceName("LoggedService") .WithLogger(logger) .WithDefaults() .Build(); // Console will now show: info/warning/error messages from OSCQueryService ``` ``` -------------------------------- ### OSCQueryService.GetOSCQueryServices / GetOSCServices Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Discovers and returns the currently known set of OSCQuery or OSC services on the local network. Results include service name, IP address, and port. ```APIDOC ## OSCQueryService.GetOSCQueryServices / GetOSCServices ### Description Returns the currently known set of OSCQuery or OSC services discovered on the local network. Results are returned as `HashSet`, each containing the service name, IP address, port, and type. Subscribe to `OnOscQueryServiceAdded` / `OnOscServiceAdded` events to be notified of newly discovered services. ### Method `GetOSCQueryServices()` returns `HashSet` `GetOSCServices()` returns `HashSet` ### Response - **HashSet** - A set of discovered service profiles. - **name** (string) - The name of the service. - **address** (string) - The IP address of the service. - **port** (int) - The port of the service. - **type** (OSCQueryServiceProfile.ServiceType) - The type of the service (OSCQuery or OSC). ### Request Example ```csharp // Snapshot of currently known services foreach (var profile in service.GetOSCQueryServices()) Console.WriteLine($"Known OSCQuery: {profile.name}"); foreach (var profile in service.GetOSCServices()) Console.WriteLine($"Known OSC: {profile.name}"); ``` ``` -------------------------------- ### Discover Available TCP and UDP Ports Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Utilizes helper methods to find available TCP or UDP ports by binding to port 0 and reading the OS-assigned port. Use these before constructing the service to prevent port conflicts. ```csharp using VRC.OSCQuery; int tcpPort = Extensions.GetAvailableTcpPort(); int udpPort = Extensions.GetAvailableUdpPort(); Console.WriteLine($"Will use TCP {tcpPort} for OSCQuery HTTP"); Console.WriteLine($"Will use UDP {udpPort} for OSC"); var service = new OSCQueryServiceBuilder() .WithTcpPort(tcpPort) .WithUdpPort(udpPort) .WithServiceName("MyApp") .WithDefaults() .Build(); ``` -------------------------------- ### Extensions.GetHostInfo Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Fetches the HostInfo of a remote OSCQuery service by querying '?HOST_INFO' on its HTTP endpoint. Includes server name, OSC IP, OSC port, and supported extensions. ```APIDOC ## Extensions.GetHostInfo ### Description Fetches the `HostInfo` of a remote OSCQuery service (server name, OSC IP, OSC port, supported extensions) by querying `?HOST_INFO` on its HTTP endpoint. ### Method `GetHostInfo(string address, int port)` ### Parameters #### Path Parameters - **address** (string) - Required - The IP address of the remote OSCQuery service. - **port** (int) - Required - The port of the remote OSCQuery service. ### Response - **HostInfo** - An object containing host information. - **name** (string) - The name of the host. - **oscIP** (string) - The OSC IP address. - **oscPort** (int) - The OSC port. - **oscTransport** (string) - The OSC transport protocol (e.g., UDP). - **extensions** (Dictionary) - A dictionary of supported extensions and their status. ### Request Example ```csharp var hostInfo = await Extensions.GetHostInfo(profile.address, profile.port); Console.WriteLine($"Name: {hostInfo.name}"); Console.WriteLine($"OSC IP: {hostInfo.oscIP}, OSC Port: {hostInfo.oscPort}"); ``` ``` -------------------------------- ### OSCQueryServiceBuilder.AddListenerForServiceType Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Registers a listener callback for OSC or OSCQuery service discovery events. This allows for early subscription to service discovery before the service is fully built. ```APIDOC ## `OSCQueryServiceBuilder.AddListenerForServiceType` — Listen for Specific Service Type Registers a listener callback on the builder for either `OSC` or `OSCQuery` service discovery events, as an alternative to subscribing on the built service after `Build()`. ```csharp using VRC.OSCQuery; var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithUdpPort(Extensions.GetAvailableUdpPort()) .AddListenerForServiceType( profile => Console.WriteLine($"OSCQuery found: {profile.name}:{profile.port}"), OSCQueryServiceProfile.ServiceType.OSCQuery) .AddListenerForServiceType( profile => Console.WriteLine($"OSC found: {profile.name}:{profile.port}"), OSCQueryServiceProfile.ServiceType.OSC) .WithDefaults() .Build(); ``` ``` -------------------------------- ### Fetch API Data for OSC Query Explorer Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Asynchronously fetches data from the root API endpoint. Updates loading, success, and error states accordingly. ```javascript const fetchApi = async () => { apiIsLoading = true; try { const resp = await fetch('/'); apiData = await resp.json(); apiIsSuccess = true; apiIsError = false; apiError = null; } catch (e) { console.error(e); apiIsError = true; apiError = e; apiIsSuccess = false; } apiIsLoading = false; } ``` -------------------------------- ### Extensions.GetAvailableTcpPort / GetAvailableUdpPort Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Utility methods for discovering available TCP and UDP ports. These methods bind to port 0 and read the OS-assigned port, helping to prevent port conflicts when constructing the OSCQuery service. ```APIDOC ## `Extensions.GetAvailableTcpPort` / `GetAvailableUdpPort` — Port Discovery Utilities Helper methods that find an available TCP or UDP port by binding to port 0 and reading the OS-assigned port. Use these before constructing the service to avoid port conflicts. ```csharp using VRC.OSCQuery; int tcpPort = Extensions.GetAvailableTcpPort(); int udpPort = Extensions.GetAvailableUdpPort(); Console.WriteLine($"Will use TCP {tcpPort} for OSCQuery HTTP"); Console.WriteLine($"Will use UDP {udpPort} for OSC"); var service = new OSCQueryServiceBuilder() .WithTcpPort(tcpPort) .WithUdpPort(udpPort) .WithServiceName("MyApp") .WithDefaults() .Build(); ``` ``` -------------------------------- ### Add Service Profile to List Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This function adds a discovered OSCQueryServiceProfile to a local list and logs the service name to the console for confirmation. ```csharp private void AddProfileToList(OSCQueryServiceProfile profile) { _profiles.Add(profile); Console.WriteLine($"Added {profile.name} to list of profiles"); } ``` -------------------------------- ### Main Rendering Logic Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html The main function to render the OSC query data. It determines the number of columns based on screen width and iterates through data entries, calling appropriate rendering functions for parent groups and individual elements. ```javascript const render = (data, container, nestLevel) => { const totalWidth = document.body.clientWidth; const columns = COLUMNS_MAP.find(c => c[0] < totalWidth)[1]; Object.entries(data).forEach(([key, data], index) => { const shouldSkip = collapseState.find(c => data.FULL_PATH.startsWith(c)); if (data.ACCESS == 0) { renderParentElement(data, container, nestLevel, shouldSkip); if (shouldSkip) return; renderParent(data.CONTENTS, container, nestLevel + 1); } else { if (shouldSkip) return; const noIndent = index % columns !== 0 && compactModeState; renderElement(data, container, noIndent ? 0 : nestLevel, index); }); } ``` -------------------------------- ### Listen for Specific Service Type with OSCQueryServiceBuilder Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Registers callbacks for OSC or OSCQuery service discovery events during builder configuration. This is an alternative to subscribing after the service is built. ```csharp using VRC.OSCQuery; var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithUdpPort(Extensions.GetAvailableUdpPort()) .AddListenerForServiceType( profile => Console.WriteLine($"OSCQuery found: {profile.name}:{profile.port}"), OSCQueryServiceProfile.ServiceType.OSCQuery) .AddListenerForServiceType( profile => Console.WriteLine($"OSC found: {profile.name}:{profile.port}"), OSCQueryServiceProfile.ServiceType.OSC) .WithDefaults() .Build(); ``` -------------------------------- ### OSCQueryServiceBuilder.WithMiddleware Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Injects custom HTTP middleware into the server pipeline. This allows for serving additional pages or intercepting requests before they are handled by the default OSCQuery logic. ```APIDOC ## `OSCQueryServiceBuilder.WithMiddleware` — Custom HTTP Middleware Injects custom middleware into the HTTP server pipeline, allowing you to serve additional pages or intercept requests. Middleware receives the `HttpListenerContext` and a `next` action to pass control downstream. ```csharp using System.Net; using System.Threading.Tasks; using System.IO; using VRC.OSCQuery; var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithMiddleware(async (context, next) => { // Serve a custom /status endpoint if (context.Request.Url.LocalPath == "/status") { string body = "{\"status\": \"ok\"}"; context.Response.ContentType = "application/json"; context.Response.ContentLength64 = body.Length; using var sw = new StreamWriter(context.Response.OutputStream); await sw.WriteAsync(body); // Do NOT call next() — we handled the request return; } // Pass through to default OSCQuery handling next(); }) .WithDefaults() .Build(); ``` ``` -------------------------------- ### OSCQueryService.AddEndpoint - Register an OSC Method Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Details how to register an OSC address path with its metadata (type, access, description, initial value) using the AddEndpoint method. This allows other services to discover and interact with the registered OSC paths. ```APIDOC ## OSCQueryService.AddEndpoint — Register an OSC Method Registers an OSC address path with metadata (type, access, description, initial value) so other services can discover what OSC messages your application can receive. The path must start with `/`. Duplicate paths are silently skipped with a warning. Returns `true` on success, `false` on failure. ```csharp using VRC.OSCQuery; var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithDefaults() .Build(); // Register using explicit OSC type string // OSC type chars: i=int, f=float, s=string, T=bool, etc. service.AddEndpoint( "/avatar/parameters/GestureLeft", "i", // OSC type: integer Attributes.AccessValues.WriteOnly, // app can receive this new object[] { 0 }, // initial value "Left hand gesture index" // description ); // Register using C# generic type (auto-resolves OSC type) service.AddEndpoint( "/avatar/parameters/VoiceVolume", Attributes.AccessValues.ReadWrite, new object[] { 0.0f }, "Microphone volume 0.0-1.0" ); service.AddEndpoint( "/avatar/parameters/MuteSelf", Attributes.AccessValues.WriteOnly, new object[] { false }, "Toggle self-mute" ); // Verify at: http://localhost:{tcpPort}/avatar/parameters/GestureLeft // Expected JSON: // { // "DESCRIPTION": "Left hand gesture index", // "FULL_PATH": "/avatar/parameters/GestureLeft", // "ACCESS": 2, // "TYPE": "i", // "VALUE": [0] // } ``` ``` -------------------------------- ### Import OSCQuery Namespace Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md Import the necessary namespace to use OSCQuery functionalities in your C# class. ```csharp using VRC.OSCQuery ``` -------------------------------- ### Iterate and Check Endpoints Across Profiles Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md Iterate through discovered profiles, retrieve their OSC trees, and check for a specific endpoint. Skips profiles where the endpoint is not found. ```csharp foreach (var profile in _profiles) { var tree = await Extensions.GetOSCTree(profile.address, profile.port); var node = tree.GetNodeWithPath("/cool/endpoint"); // Skip to next if node does not exist if (node == null) continue; // Do something else here, like subscribing to the endpoint } ``` -------------------------------- ### Extensions.GetOSCTree Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Asynchronously fetches the complete OSC node tree of a remote OSCQuery service over HTTP. Returns null if the request fails. ```APIDOC ## Extensions.GetOSCTree ### Description Asynchronously fetches the complete OSC node tree of a remote OSCQuery service over HTTP and deserializes it into an `OSCQueryRootNode`. Returns `null` if the request fails. ### Method `GetOSCTree(string address, int port)` ### Parameters #### Path Parameters - **address** (string) - Required - The IP address of the remote OSCQuery service. - **port** (int) - Required - The port of the remote OSCQuery service. ### Response - **OSCQueryRootNode** - The root node of the OSC tree, or `null` if the request failed. ### Request Example ```csharp var tree = await Extensions.GetOSCTree(profile.address, profile.port); if (tree == null) return; // Check if the remote service has a specific endpoint var chatboxNode = tree.GetNodeWithPath("/chatbox/input"); if (chatboxNode != null) { Console.WriteLine($"Remote has chatbox: type={chatboxNode.OscType}, access={chatboxNode.Access}"); } ``` ``` -------------------------------- ### Fetch Remote OSC Node Tree with Extensions.GetOSCTree Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Asynchronously fetches the complete OSC node tree of a remote OSCQuery service over HTTP. Returns null if the request fails. Allows checking for specific endpoints or iterating through nodes. ```csharp using VRC.OSCQuery; using System.Net; var service = new OSCQueryServiceBuilder().WithDefaults().Build(); service.OnOscQueryServiceAdded += async profile => { // Fetch full OSC method tree from the discovered service var tree = await Extensions.GetOSCTree(profile.address, profile.port); if (tree == null) return; // Check if the remote service has a specific endpoint var chatboxNode = tree.GetNodeWithPath("/chatbox/input"); if (chatboxNode != null) { Console.WriteLine($"Remote has chatbox: type={chatboxNode.OscType}, access={chatboxNode.Access}"); } // Iterate all top-level nodes if (tree.Contents != null) { foreach (var (key, node) in tree.Contents) Console.WriteLine($" {node.FullPath} [{node.OscType}]"); } }; service.RefreshServices(); ``` -------------------------------- ### Manually Refresh Service Discovery Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This code triggers an immediate refresh of OSCQuery services on the network, updating the list of available services. ```csharp queryService.RefreshServices(); ``` -------------------------------- ### Advertise VRChat Full-Body Tracking OSC Endpoints Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Advertises OSC endpoints for VRChat full-body tracking data. These are WriteOnly endpoints, meaning VRChat receives data but does not send it back. Includes positions and rotations for up to 8 trackers and optional head alignment. ```csharp using VRC.OSCQuery; var service = new OSCQueryServiceBuilder().WithDefaults().Build(); // Advertise tracker endpoints (WriteOnly — VRChat receives, doesn't send) for (int i = 1; i <= 8; i++) { service.AddEndpoint($"/tracking/trackers/{i}/position", "fff", Attributes.AccessValues.WriteOnly, new object[] { 0f, 0f, 0f }, $ ``` ```csharp Tracker {i} world-space position (x,y,z in meters)"); service.AddEndpoint($"/tracking/trackers/{i}/rotation", "fff", Attributes.AccessValues.WriteOnly, new object[] { 0f, 0f, 0f }, $ ``` ```csharp Tracker {i} euler rotation (x,y,z in degrees)"); } // Optional head reference for tracking space alignment service.AddEndpoint("/tracking/trackers/head/position", "fff", Attributes.AccessValues.WriteOnly, null, "Head position for space alignment"); service.AddEndpoint("/tracking/trackers/head/rotation", "fff", Attributes.AccessValues.WriteOnly, null, "Head yaw for tracking space alignment"); // Tracker index mapping (conventional): // 1=hip, 2=chest, 3=left foot, 4=right foot, // 5=left knee, 6=right knee, 7=left elbow, 8=right elbow ``` -------------------------------- ### Inject Custom HTTP Middleware with OSCQueryServiceBuilder Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Injects custom middleware into the HTTP server pipeline to serve additional pages or intercept requests. The middleware receives the HttpListenerContext and a 'next' action to pass control downstream. Do not call 'next()' if the request is handled. ```csharp using System.Net; using System.Threading.Tasks; using System.IO; using VRC.OSCQuery; var service = new OSCQueryServiceBuilder() .WithTcpPort(Extensions.GetAvailableTcpPort()) .WithMiddleware(async (context, next) => { // Serve a custom /status endpoint if (context.Request.Url.LocalPath == "/status") { string body = "{\"status\": \"ok\"}"; context.Response.ContentType = "application/json"; context.Response.ContentLength64 = body.Length; using var sw = new StreamWriter(context.Response.OutputStream); await sw.WriteAsync(body); // Do NOT call next() — we handled the request return; } // Pass through to default OSCQuery handling next(); }) .WithDefaults() .Build(); ``` -------------------------------- ### Attach Logger to OSCQuery Service Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Attach a Microsoft.Extensions.Logging.ILogger to the OSCQueryService for diagnostic output. By default, a NullLogger is used. This is useful for debugging service startup, endpoint additions, and HTTP request errors. ```csharp using Microsoft.Extensions.Logging; using VRC.OSCQuery; // Create a console logger factory using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); var logger = loggerFactory.CreateLogger(); var service = new OSCQueryServiceBuilder() .WithServiceName("LoggedService") .WithLogger(logger) .WithDefaults() .Build(); // Console will now show: info/warning/error messages from OSCQueryService ``` -------------------------------- ### Dispose OSCQuery Service Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/getting-started.md This snippet shows the correct way to manually stop and clean up an OSCQuery service instance. ```csharp queryService.Dispose(); ``` -------------------------------- ### Render Parent Element for OSC Groups Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/Examples/OSCQueryExplorer-Unity/Assets/OSCQuery/Runtime/Plugins/Resources/OSCQueryExplorer.html Creates and appends a parent element for OSC groups. It includes the path, a toggler for expanding/collapsing, and attaches a click listener to manage the collapse state and re-render the view. ```javascript const renderParentElement = (data, container, nestLevel, childrenSkipped) => { const entry = document.createElement('div'); entry.classList.add('entry', 'col', 'group'); entry.style.marginLeft = `${nestLevel * 20}px`; const mainInfo = document.createElement('div'); mainInfo.classList.add('entryInfo', 'row', 'align-items-center', 'justify-content-between'); const leftContainer = document.createElement('div'); leftContainer.classList.add('row', 'align-items-center'); const path = document.createElement('div'); path.classList.add('entryPath', 'mono'); path.textContent = data.FULL_PATH; const toggler = document.createElement('div'); toggler.classList.add('toggler'); if (childrenSkipped) { toggler.classList.add('flipped'); } toggler.textContent = "v"; leftContainer.appendChild(path); mainInfo.appendChild(leftContainer); mainInfo.appendChild(toggler); entry.appendChild(mainInfo); container.appendChild(entry); entry.addEventListener('click', () => { if (!collapseState.includes(data.FULL_PATH)) { collapseState.push(data.FULL_PATH); } else { collapseState.splice(collapseState.indexOf(data.FULL_PATH), 1); } render(); }); } ``` -------------------------------- ### Add OSC Endpoint to Service Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/Readme.md Add information about an available OSC method to the OSCQuery service. This library does not handle OSC message sending/receiving directly; an external OSC library is required. ```csharp service.AddEndpoint("/foo/bar"); ``` -------------------------------- ### Render Individual OSC Data Element Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/Examples/OSCQueryExplorer-Unity/Assets/OSCQuery/Runtime/Plugins/Resources/OSCQueryExplorer.html Renders a single OSC data element, displaying its path, type, access level, description, and current value. It formats array values and applies specific CSS classes for styling. ```javascript const renderElement = (data, container, nestLevel, index) => { const entry = document.createElement('div'); entry.classList.add('entry', 'col'); entry.style.marginLeft = `${nestLevel * 20}px`; const mainInfo = document.createElement('div'); mainInfo.classList.add('entryInfo', 'row', 'align-items-center', 'justify-content-between'); const leftContainer = document.createElement('div'); leftContainer.classList.add('entryPathRow', 'row', 'align-items-center'); const path = document.createElement('div'); path.classList.add('entryPath', 'mono'); path.textContent = data.FULL_PATH; const typeInfo = document.createElement('div'); typeInfo.classList.add('row', 'align-items-center'); const type = document.createElement('div'); type.classList.add('entryType', 'code'); type.textContent = TYPE_MAP[data.TYPE] const access = document.createElement('div'); access.classList.add('entryAccess', 'code'); access.textContent = ACCESS_MAP[data.ACCESS] typeInfo.appendChild(type); typeInfo.appendChild(access); leftContainer.appendChild(path); leftContainer.appendChild(typeInfo); mainInfo.appendChild(leftContainer); if (data.DESCRIPTION !== '') { const description = document.createElement('div'); description.classList.add('entryDescription'); description.textContent = data.DESCRIPTION; mainInfo.appendChild(description); } const currValContainer = document.createElement('div'); currValContainer.classList.add('entryCurrValContainer', 'row', 'align-items-center'); const currValLabel = document.createElement('div'); currValLabel.textContent = 'Current Value: '; const currVal = document.createElement('div'); currVal.classList.add('code'); if (Array.isArray(data.VALUE)) { currVal.textContent = `[${data.VALUE.join(', ')}]`; } else { currVal.textContent = data.VALUE; } currValContainer.appendChild(currValLabel); currValContainer.appendChild(currVal); entry.appendChild(mainInfo); entry.appendChild(currValContainer); container.appendChild(entry); } ``` -------------------------------- ### HTTP Endpoints Source: https://context7.com/vrchat-community/vrc-oscquery-lib/llms.txt Directly query the OSCQuery server using standard HTTP requests. These endpoints conform to the OSCQuery specification and can be accessed by any HTTP client. ```APIDOC ## HTTP Endpoints — Querying the OSCQuery Server Directly The HTTP server exposes several queryable endpoints that conform to the OSCQuery spec. These can be accessed with any HTTP client (browser, curl, etc.). ```bash TCP_PORT=8060 # or whatever port was assigned # Get full OSC node tree (root namespace) curl http://localhost:$TCP_PORT/ # {"DESCRIPTION":"root node","FULL_PATH":"/","ACCESS":0,"CONTENTS":{...}} # Get a specific OSC node curl http://localhost:$TCP_PORT/avatar/parameters/MuteSelf # {"DESCRIPTION":"Toggle self-mute","FULL_PATH":"/avatar/parameters/MuteSelf","ACCESS":2,"TYPE":"T","VALUE":[false]} # Get host info (server name, OSC port, supported extensions) curl "http://localhost:$TCP_PORT?HOST_INFO" # {"NAME":"MyOSCApp","EXTENSIONS":{"ACCESS":true,...},"OSC_IP":"127.0.0.1","OSC_PORT":9001,"OSC_TRANSPORT":"UDP"} # Open the built-in web explorer UI (in a browser) # http://localhost:$TCP_PORT?explorer ``` ``` -------------------------------- ### Manual Data Refresh Logic Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Handles the logic for manually refreshing the OSC Query data. Includes UI updates for the refresh button. ```javascript let refreshTimeout = null; const refreshData = async () => { apiIsSuccess = false; apiIsError = false; apiError = null; apiData = {}; await fetchApi(); render(); } const setupRefresh = () => { const refreshBtn = document.getElementById('refresh'); refreshBtn.addEventListener('click', async () => { refreshBtn.textContent = 'Refreshing...'; await refreshData(); refreshBtn.textContent = 'Refreshed!'; if (refreshTimeout != null) { clearTimeout(refreshTimeout); } refreshTiemout = setTimeout(() => { refreshBtn.textContent = 'Refresh'; }, 1000); }); } ``` -------------------------------- ### Toggle Compact Mode Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Handles the logic for toggling the compact mode of the OSC Query Explorer. Updates the UI and re-renders the data. ```javascript const setupCompactMode = () => { const compactMode = document.getElementById('compactMode'); const compactModeIcon = document.getElementById('compactModeIcon'); const dataContainer = document.getElementById('dataContainer'); compactMode.addEventListener('click', () => { compactModeState = !compactModeState; if (compactModeState) { dataContainer.classList.add('compact'); compactModeIcon.classList.add('active'); compactModeIcon.classList.remove('faded'); } else { dataContainer.classList.remove('compact'); compactModeIcon.classList.add('faded'); compactModeIcon.classList.remove('active'); } render(); }); } ``` -------------------------------- ### OSC Tracker Position Addresses Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/osc-trackers.md These are the OSC addresses for sending the world-space positions of up to 8 trackers. Each address expects Vector3 data. ```osc /tracking/trackers/1/position /tracking/trackers/2/position /tracking/trackers/3/position /tracking/trackers/4/position /tracking/trackers/5/position /tracking/trackers/6/position /tracking/trackers/7/position /tracking/trackers/8/position ``` -------------------------------- ### Render OSC Query Data Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Renders the OSC Query data in the main container. Handles both successful data retrieval and error states. Preserves scroll position. ```javascript const render = () => { const dataContainer = document.getElementById('dataContainer'); const scrollPos = window.scrollY; dataContainer.innerHTML = ''; if (apiData?.CONTENTS && !apiIsError) { renderParent(apiData.CONTENTS, dataContainer, 0); const descriptionNode = document.getElementById('nodeDescription'); descriptionNode.innerText = apiData.DESCRIPTION; const search = document.getElementById('pathFilter'); if (search.value != '') { filterElements(search.value.toLowerCase()); } window.scrollTo(0, scrollPos); } if (apiIsError) { renderError(dataContainer); } } ``` -------------------------------- ### Render OSC Data Elements Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Renders OSC data elements, differentiating between parent groups and individual parameters. Handles collapsing and indentation based on hierarchy and settings. ```javascript const renderElement = (data, container, nestLevel, index) => { const entry = document.createElement('div'); entry.classList.add('entry', 'col'); entry.style.marginLeft = `${nestLevel * 20}px`; const mainInfo = document.createElement('div'); mainInfo.classList.add('entryInfo', 'row', 'align-items-center', 'justify-content-between'); const leftContainer = document.createElement('div'); leftContainer.classList.add('entryPathRow', 'row', 'align-items-center'); const path = document.createElement('div'); path.classList.add('entryPath', 'mono'); path.textContent = data.FULL_PATH; const typeInfo = document.createElement('div'); typeInfo.classList.add('row', 'align-items-center'); const type = document.createElement('div'); type.classList.add('entryType', 'code'); type.textContent = TYPE_MAP[data.TYPE] const access = document.createElement('div'); access.classList.add('entryAccess', 'code'); access.textContent = ACCESS_MAP[data.ACCESS] typeInfo.appendChild(type); typeInfo.appendChild(access); leftContainer.appendChild(path); leftContainer.appendChild(typeInfo); mainInfo.appendChild(leftContainer); if (data.DESCRIPTION !== '') { const description = document.createElement('div'); description.classList.add('entryDescription'); description.textContent = data.DESCRIPTION; mainInfo.appendChild(description); } const currValContainer = document.createElement('div'); currValContainer.classList.add('entryCurrValContainer', 'row', 'align-items-center'); const currValLabel = document.createElement('div'); currValLabel.textContent = 'Current Value: '; const currVal = document.createElement('div'); currVal.classList.add('code'); if (Array.isArray(data.VALUE)) { currVal.textContent = `[${data.VALUE.join(', ')}]`; } else { currVal.textContent = data.VALUE; } currValContainer.appendChild(currValLabel); currValContainer.appendChild(currVal); entry.appendChild(mainInfo); entry.appendChild(currValContainer); container.appendChild(entry); } ``` -------------------------------- ### Recursive Rendering of Parent Nodes Source: https://github.com/vrchat-community/vrc-oscquery-lib/blob/main/vrc-oscquery-lib/Resources/OSCQueryExplorer.html Recursively renders parent nodes and their children for the OSC Query data. This function is called by the main render function. ```javascript const renderParent = (data, con ```