private void SeedRequestCompleteHandler(CapsClient client, OSD result, Exception error) { if (result != null && result.Type == OSDType.Map) { OSDMap respTable = (OSDMap)result; foreach (string cap in respTable.Keys) { _Caps[cap] = respTable[cap].AsUri(); } if (_Caps.ContainsKey("EventQueueGet")) { Logger.DebugLog("Starting event queue for " + Simulator.ToString(), Simulator.Client); _EventQueueCap = new EventQueueClient(_Caps["EventQueueGet"]); _EventQueueCap.OnConnected += EventQueueConnectedHandler; _EventQueueCap.OnEvent += EventQueueEventHandler; _EventQueueCap.Start(); } } else { // The initial CAPS connection failed, try again MakeSeedRequest(); } }
private void SeedRequestCompleteHandler(CapsClient client, OSD result, Exception error) { if (result != null && result.Type == OSDType.Map) { OSDMap respTable = (OSDMap)result; foreach (string cap in respTable.Keys) { _Caps[cap] = respTable[cap].AsUri(); } if (_Caps.ContainsKey("EventQueueGet")) { Logger.DebugLog("Starting event queue for " + Simulator, Simulator.Client); _EventQueueCap = new EventQueueClient(_Caps["EventQueueGet"]); _EventQueueCap.OnConnected += EventQueueConnectedHandler; _EventQueueCap.OnEvent += EventQueueEventHandler; _EventQueueCap.Start(); } OnCapabilitiesReceived(Simulator); } else if ( error != null && error is WebException exception && exception.Response != null && ((HttpWebResponse)exception.Response).StatusCode == HttpStatusCode.NotFound) { // 404 error Logger.Log("Seed capability returned a 404, capability system is aborting", Helpers.LogLevel.Error); }
private void CreateUserResponse(CapsClient client, OSD response, Exception error) { if (response is OSDMap) { // everything is okay // FIXME: //return new UUID(((Dictionary<string, object>)response)["agent_id"].ToString()); } else { // an error happened OSDArray al = (OSDArray)response; StringBuilder sb = new StringBuilder(); foreach (OSD ec in al) { if (sb.Length > 0) { sb.Append("; "); } sb.Append(_errors[ec.AsInteger()]); } // FIXME: //throw new Exception("failed to create user: " + sb.ToString()); } }
protected void MapLayerResponseHandler(CapsClient client, OSD result, Exception error) { if (result == null) { Logger.Log("MapLayerResponseHandler error: " + error.Message + ": " + error.StackTrace, Helpers.LogLevel.Error, Client); return; } OSDMap body = (OSDMap)result; OSDArray layerData = (OSDArray)body["LayerData"]; if (m_GridLayer != null) { foreach (var data in layerData) { OSDMap thisLayerData = (OSDMap)data; GridLayer layer; layer.Bottom = thisLayerData["Bottom"].AsInteger(); layer.Left = thisLayerData["Left"].AsInteger(); layer.Top = thisLayerData["Top"].AsInteger(); layer.Right = thisLayerData["Right"].AsInteger(); layer.ImageID = thisLayerData["ImageID"].AsUUID(); OnGridLayer(new GridLayerEventArgs(layer)); } } if (body.ContainsKey("MapBlocks")) { // TODO: At one point this will become activated Logger.Log("Got MapBlocks through CAPS, please finish this function!", Helpers.LogLevel.Error, Client); } }
private void MakeSeedRequest() { if (Simulator == null || !Simulator.Client.Network.Connected) { return; } // Create a request list LLSDArray req = new LLSDArray(); req.Add("MapLayer"); req.Add("MapLayerGod"); req.Add("NewFileAgentInventory"); req.Add("EventQueueGet"); req.Add("UpdateGestureAgentInventory"); req.Add("UpdateNotecardAgentInventory"); req.Add("UpdateScriptAgentInventory"); req.Add("UpdateGestureTaskInventory"); req.Add("UpdateNotecardTaskInventory"); req.Add("UpdateScriptTaskInventory"); req.Add("SendPostcard"); req.Add("ViewerStartAuction"); req.Add("ParcelGodReserveForNewbie"); req.Add("SendUserReport"); req.Add("SendUserReportWithScreenshot"); req.Add("RequestTextureDownload"); req.Add("UntrustedSimulatorMessage"); req.Add("ParcelVoiceInfoRequest"); req.Add("ChatSessionRequest"); req.Add("ProvisionVoiceAccountRequest"); _SeedRequest = new CapsClient(new Uri(_SeedCapsURI)); _SeedRequest.OnComplete += new CapsClient.CompleteCallback(SeedRequestCompleteHandler); _SeedRequest.StartRequest(req); }
private void SeedRequestCompleteHandler(CapsClient client, LLSD result, Exception error) { if (result != null && result.Type == LLSDType.Map) { LLSDMap respTable = (LLSDMap)result; StringBuilder capsList = new StringBuilder(); foreach (string cap in respTable.Keys) { capsList.Append(cap); capsList.Append(' '); _Caps[cap] = respTable[cap].AsUri(); } Logger.DebugLog("Got capabilities: " + capsList.ToString(), Simulator.Client); if (_Caps.ContainsKey("EventQueueGet")) { Logger.DebugLog("Starting event queue for " + Simulator.ToString(), Simulator.Client); _EventQueueCap = new EventQueueClient(_Caps["EventQueueGet"]); _EventQueueCap.OnConnected += new EventQueueClient.ConnectedCallback(EventQueueConnectedHandler); _EventQueueCap.OnEvent += new EventQueueClient.EventCallback(EventQueueEventHandler); _EventQueueCap.Start(); } } else { // The initial CAPS connection failed, try again MakeSeedRequest(); } }
/// <summary> /// Performas actual mesh and image upload /// </summary> /// <param name="uploader">Uri recieved in the upload prepare stage</param> /// <param name="callback">Callback that will be invoke upon completion of the upload. Null is sent on request failure</param> public void PerformUpload(Uri uploader, ModelUploadCallback callback) { CapsClient request = new CapsClient(uploader); request.OnComplete += (client, result, error) => { if (error != null || result == null || result.Type != OSDType.Map) { Logger.Log("Mesh upload request failure", Helpers.LogLevel.Error); if (callback != null) { callback(null); } return; } OSDMap res = (OSDMap)result; Logger.Log("Response from mesh upload perform:\n" + OSDParser.SerializeLLSDNotationFormatted(result), Helpers.LogLevel.Debug); if (callback != null) { callback(res); } }; request.BeginGetResponse(AssetResources(true), OSDFormat.Xml, 60 * 1000); }
public bool RequestProvisionAccount() { if (Enabled && Client.Network.Connected) { if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.Caps != null) { Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("ProvisionVoiceAccountRequest"); if (url != null) { CapsClient request = new CapsClient(url); request.OnComplete += new CapsClient.CompleteCallback(ProvisionCapsResponse); request.StartRequest(); return(true); } else { Client.Log("VoiceManager.RequestProvisionAccount(): ProvisionVoiceAccountRequest capability is missing", Helpers.LogLevel.Info); return(false); } } } Client.Log("VoiceManager.RequestProvisionAccount(): Voice system is currently disabled", Helpers.LogLevel.Info); return(false); }
private bool RequestVoiceInternal(string me, CapsClient.CompleteCallback callback, string capsName) { if (Enabled && Client.Network.Connected) { if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.Caps != null) { Uri url = Client.Network.CurrentSim.Caps.CapabilityURI(capsName); if (url != null) { CapsClient request = new CapsClient(url); OSDMap body = new OSDMap(); request.OnComplete += new CapsClient.CompleteCallback(callback); request.BeginGetResponse(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); return(true); } else { Logger.Log("VoiceManager." + me + "(): " + capsName + " capability is missing", Helpers.LogLevel.Info, Client); return(false); } } } Logger.Log("VoiceManager.RequestVoiceInternal(): Voice system is currently disabled", Helpers.LogLevel.Info, Client); return(false); }
public bool CheckName(string firstName, LastName lastName) { if (Initializing) { throw new InvalidOperationException("still initializing"); } if (_caps.CheckName == null) { throw new InvalidOperationException("access denied; only approved developers have access to the registration api"); } // Create the POST data OSDMap query = new OSDMap(); query.Add("username", OSD.FromString(firstName)); query.Add("last_name_id", OSD.FromInteger(lastName.ID)); //byte[] postData = OSDParser.SerializeXmlBytes(query); CapsClient request = new CapsClient(_caps.CheckName); request.OnComplete += new CapsClient.CompleteCallback(CheckNameResponse); request.BeginGetResponse(REQUEST_TIMEOUT); // FIXME: return(false); }
private void MapLayerResponseHandler(CapsClient client, LLSD result, Exception error) { LLSDMap body = (LLSDMap)result; LLSDArray layerData = (LLSDArray)body["LayerData"]; if (OnGridLayer != null) { for (int i = 0; i < layerData.Count; i++) { LLSDMap thisLayerData = (LLSDMap)layerData[i]; GridLayer layer; layer.Bottom = thisLayerData["Bottom"].AsInteger(); layer.Left = thisLayerData["Left"].AsInteger(); layer.Top = thisLayerData["Top"].AsInteger(); layer.Right = thisLayerData["Right"].AsInteger(); layer.ImageID = thisLayerData["ImageID"].AsUUID(); try { OnGridLayer(layer); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } if (body.ContainsKey("MapBlocks")) { // TODO: At one point this will become activated Logger.Log("Got MapBlocks through CAPS, please finish this function!", Helpers.LogLevel.Error, Client); } }
/// <summary> /// Returns the new user ID or throws an exception containing the error code /// The error codes can be found here: https://wiki.secondlife.com/wiki/RegAPIError /// </summary> /// <param name="user">New user account to create</param> /// <returns>The UUID of the new user account</returns> public UUID CreateUser(CreateUserParam user) { if (Initializing) { throw new InvalidOperationException("still initializing"); } if (_caps.CreateUser == null) { throw new InvalidOperationException("access denied; only approved developers have access to the registration api"); } // Create the POST data var query = new OSDMap { { "username", OSD.FromString(user.FirstName) }, { "last_name_id", OSD.FromInteger(user.LastName.ID) }, { "email", OSD.FromString(user.Email) }, { "password", OSD.FromString(user.Password) }, { "dob", OSD.FromString(user.Birthdate.ToString("yyyy-MM-dd")) } }; if (user.LimitedToEstate != null) { query.Add("limited_to_estate", OSD.FromInteger(user.LimitedToEstate.Value)); } if (!string.IsNullOrEmpty(user.StartRegionName)) { query.Add("start_region_name", OSD.FromInteger(user.LimitedToEstate.Value)); } if (user.StartLocation != null) { query.Add("start_local_x", OSD.FromReal(user.StartLocation.Value.X)); query.Add("start_local_y", OSD.FromReal(user.StartLocation.Value.Y)); query.Add("start_local_z", OSD.FromReal(user.StartLocation.Value.Z)); } if (user.StartLookAt != null) { query.Add("start_look_at_x", OSD.FromReal(user.StartLookAt.Value.X)); query.Add("start_look_at_y", OSD.FromReal(user.StartLookAt.Value.Y)); query.Add("start_look_at_z", OSD.FromReal(user.StartLookAt.Value.Z)); } //byte[] postData = OSDParser.SerializeXmlBytes(query); // Make the request var request = new CapsClient(_caps.CreateUser); request.OnComplete += CreateUserResponse; request.BeginGetResponse(REQUEST_TIMEOUT); // FIXME: Block return(UUID.Zero); }
private void GatherCaps() { // build post data var postData = Encoding.ASCII.GetBytes( $"first_name={_userInfo.FirstName}&last_name={_userInfo.LastName}&password={_userInfo.Password}"); var request = new CapsClient(RegistrationApiCaps); request.OnComplete += GatherCapsResponse; request.BeginGetResponse(postData, "application/x-www-form-urlencoded", REQUEST_TIMEOUT); }
private void GatherErrorMessages() { if (_caps.GetErrorCodes == null) { throw new InvalidOperationException("access denied"); // this should work even for not-approved users } CapsClient request = new CapsClient(_caps.GetErrorCodes); request.OnComplete += new CapsClient.CompleteCallback(GatherErrorMessagesResponse); request.BeginGetResponse(REQUEST_TIMEOUT); }
private void CheckNameResponse(CapsClient client, OSD response, Exception error) { if (response.Type == OSDType.Boolean) { // FIXME: //(bool)response; } else { // FIXME: } }
/// <summary> /// Ask server for details of cost and impact of the mesh upload /// </summary> /// <param name="callback">Callback that will be invoke upon completion of the upload. Null is sent on request failure</param> public void PrepareUpload(ModelUploadCallback callback) { CapsClient request = null; if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null || (request = Client.Network.CurrentSim.Caps.CreateCapsClient("NewFileAgentInventory")) == null) { Logger.Log("Cannot upload mesh, no connection or NewFileAgentInventory not available", Helpers.LogLevel.Warning); callback?.Invoke(null); return; } Images = new List <byte[]>(); ImgIndex = new Dictionary <string, int>(); OSDMap req = new OSDMap { ["name"] = InvName, ["description"] = InvDescription, ["asset_resources"] = AssetResources(false), ["asset_type"] = "mesh", ["inventory_type"] = "object", ["folder_id"] = Client.Inventory.FindFolderForType(AssetType.Object), ["texture_folder_id"] = Client.Inventory.FindFolderForType(AssetType.Texture), ["everyone_mask"] = (int)PermissionMask.All, ["group_mask"] = (int)PermissionMask.All, ["next_owner_mask"] = (int)PermissionMask.All }; request.OnComplete += (client, result, error) => { if (error != null || result == null || result.Type != OSDType.Map) { Logger.Log("Mesh upload request failure", Helpers.LogLevel.Error); callback?.Invoke(null); return; } OSDMap res = (OSDMap)result; if (res["state"] != "upload") { Logger.Log("Mesh upload failure: " + res["message"], Helpers.LogLevel.Error); callback?.Invoke(null); return; } Logger.Log("Response from mesh upload prepare:" + Environment.NewLine + OSDParser.SerializeLLSDNotationFormatted(result), Helpers.LogLevel.Debug); callback?.Invoke(result); }; request.PostRequestAsync(req, OSDFormat.Xml, 3 * 60 * 1000); }
private void GatherCaps() { // build post data byte[] postData = Encoding.ASCII.GetBytes( String.Format("first_name={0}&last_name={1}&password={2}", _userInfo.FirstName, _userInfo.LastName, _userInfo.Password)); CapsClient request = new CapsClient(RegistrationApiCaps); request.OnComplete += new CapsClient.CompleteCallback(GatherCapsResponse); request.StartRequest(postData); }
private void GatherCaps() { // build post data byte[] postData = Encoding.ASCII.GetBytes( String.Format("first_name={0}&last_name={1}&password={2}", _userInfo.FirstName, _userInfo.LastName, _userInfo.Password)); CapsClient request = new CapsClient(RegistrationApiCaps); request.OnComplete += new CapsClient.CompleteCallback(GatherCapsResponse); request.BeginGetResponse(postData, "application/x-www-form-urlencoded", REQUEST_TIMEOUT); }
private void ProvisionCapsResponse(CapsClient client, LLSD response, Exception error) { if (response is LLSDMap) { LLSDMap respTable = (LLSDMap)response; if (OnProvisionAccount != null) { try { OnProvisionAccount(respTable["username"].AsString(), respTable["password"].AsString()); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } }
private void ProvisionCapsResponse(CapsClient client, OSD response, Exception error) { if (response is OSDMap) { OSDMap respTable = (OSDMap)response; if (OnProvisionAccount != null) { try { OnProvisionAccount(respTable["username"].AsString(), respTable["password"].AsString()); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } }
/// <summary> /// /// </summary> /// <param name="layer"></param> public void RequestMapLayer(GridLayerType layer) { CapsClient request = Client.Network.CurrentSim.Caps.CreateCapsClient("MapLayer"); if (request != null) { OSDMap body = new OSDMap { ["Flags"] = OSD.FromInteger((int)layer) }; request.OnComplete += new CapsClient.CompleteCallback(MapLayerResponseHandler); request.PostRequestAsync(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); } }
/// <summary> /// /// </summary> /// <param name="layer"></param> public void RequestMapLayer(GridLayerType layer) { Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("MapLayer"); if (url != null) { OSDMap body = new OSDMap(); body["Flags"] = OSD.FromInteger((int)layer); CapsClient request = new CapsClient(url); request.OnComplete += new CapsClient.CompleteCallback(MapLayerResponseHandler); request.BeginGetResponse(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); } }
/// <summary> /// /// </summary> /// <param name="layer"></param> public void RequestMapLayer(GridLayerType layer) { Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("MapLayer"); if (url != null) { LLSDMap body = new LLSDMap(); body["Flags"] = LLSD.FromInteger((int)layer); CapsClient request = new CapsClient(url); request.OnComplete += new CapsClient.CompleteCallback(MapLayerResponseHandler); request.StartRequest(body); } }
/// <summary> /// Sends user report first by trying SendUserReport sim cap, falling back to legacy /// </summary> /// <param name="reportType"></param> /// <param name="category"></param> /// <param name="screenshotId"></param> /// <param name="objectId"></param> /// <param name="abuserId"></param> /// <param name="abuseRegionName"></param> /// <param name="abuseRegionId"></param> /// <param name="pos"></param> /// <param name="summary"></param> /// <param name="details"></param> public void SendUserReport(ReportType reportType, int category, UUID screenshotId, UUID objectId, UUID abuserId, string abuseRegionName, UUID abuseRegionId, Vector3 pos, string summary, string details) { OSDMap report = new OSDMap { ["report-type"] = (byte)reportType, ["category"] = (byte)category, ["check-flags"] = (byte)0u, // this is not used ["screenshot-id"] = screenshotId, ["object-id"] = objectId, ["abuser-id"] = abuserId, ["abuse-region-name"] = "", ["abuse-region-id"] = UUID.Zero, ["position"] = pos, ["summary"] = summary, ["version-string"] = $"<3 LibreMetaverse", ["details"] = details }; Uri userReportCap = (screenshotId != UUID.Zero) ? Client.Network.CurrentSim.Caps.CapabilityURI("SendUserReportWithScreenshot") : Client.Network.CurrentSim.Caps.CapabilityURI("SendUserReport"); if (userReportCap != null) { var request = new CapsClient(userReportCap); request.OnComplete += delegate(CapsClient client, OSD result, Exception error) { if (error != null) { Logger.Log($"Failed to send abuse report via {userReportCap}. " + $"({error.Message}) Falling back to legacy protocol.", Helpers.LogLevel.Warning); SendUserReportLegacy(reportType, category, screenshotId, objectId, abuserId, abuseRegionName, abuseRegionId, pos, summary, details); } }; request.PostRequestAsync(report, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); } else { SendUserReportLegacy(reportType, category, screenshotId, objectId, abuserId, abuseRegionName, abuseRegionId, pos, summary, details); } }
private void MakeSeedRequest() { if (Simulator == null || !Simulator.Client.Network.Connected) { return; } // Create a request list OSDArray req = new OSDArray(); req.Add("ChatSessionRequest"); req.Add("CopyInventoryFromNotecard"); req.Add("DispatchRegionInfo"); req.Add("EstateChangeInfo"); req.Add("EventQueueGet"); req.Add("FetchInventoryDescendents"); req.Add("GroupProposalBallot"); req.Add("MapLayer"); req.Add("MapLayerGod"); req.Add("NewFileAgentInventory"); req.Add("ParcelPropertiesUpdate"); req.Add("ParcelVoiceInfoRequest"); req.Add("ProvisionVoiceAccountRequest"); req.Add("RemoteParcelRequest"); req.Add("RequestTextureDownload"); req.Add("SearchStatRequest"); req.Add("SearchStatTracking"); req.Add("SendPostcard"); req.Add("SendUserReport"); req.Add("SendUserReportWithScreenshot"); req.Add("ServerReleaseNotes"); req.Add("StartGroupProposal"); req.Add("UpdateGestureAgentInventory"); req.Add("UpdateNotecardAgentInventory"); req.Add("UpdateScriptAgent"); req.Add("UpdateGestureTaskInventory"); req.Add("UpdateNotecardTaskInventory"); req.Add("UpdateScriptTask"); req.Add("ViewerStartAuction"); req.Add("UntrustedSimulatorMessage"); req.Add("ViewerStats"); _SeedRequest = new CapsClient(new Uri(_SeedCapsURI)); _SeedRequest.OnComplete += new CapsClient.CompleteCallback(SeedRequestCompleteHandler); _SeedRequest.StartRequest(req); }
/// <summary> /// Fetch a list of Abuse Report categories from the simulator /// </summary> /// <param name="lang">language to return categories in</param> /// <returns>Returns Dictionary<string, string> of Abuse Report categories from the server</returns> public Dictionary <string, string> FetchAbuseReportCategories(string lang) { Dictionary <string, string> reportCategories = null; Uri abuseCategoriesCap = Client.Network.CurrentSim.Caps.CapabilityURI("AbuseCategories"); if (abuseCategoriesCap != null) { if (lang != null) { // s***e C# nonsense UriBuilder builder = new UriBuilder(abuseCategoriesCap); builder.Query = $"lc={lang}"; abuseCategoriesCap = builder.Uri; } var request = new CapsClient(abuseCategoriesCap); request.OnComplete += delegate(CapsClient client, OSD result, Exception error) { if (error != null) { Logger.Log($"Could not fetch abuse categories from cap. ({error.Message}", Helpers.LogLevel.Info); return; } if (result != null && result is OSDMap respMap && respMap.ContainsKey("categories")) { reportCategories = new Dictionary <string, string>(); var categories = respMap["categories"] as OSDArray; foreach (OSDMap row in categories) { reportCategories.Add( row["description_localized"].AsString(), row["category"].AsString()); } } }; request.GetRequestAsync(Client.Settings.CAPS_TIMEOUT); } else { Logger.Log("AbuseCategories capability does not exist. Could not fetch categories list.", Helpers.LogLevel.Info); } return(reportCategories); }
public void GatherLastNames() { if (Initializing) { throw new InvalidOperationException("still initializing"); } if (_caps.GetLastNames == null) { throw new InvalidOperationException("access denied: only approved developers have access to the registration api"); } CapsClient request = new CapsClient(_caps.GetLastNames); request.OnComplete += new CapsClient.CompleteCallback(GatherLastNamesResponse); request.BeginGetResponse(REQUEST_TIMEOUT); // FIXME: Block }
/// <summary> /// Performas actual mesh and image upload /// </summary> /// <param name="uploader">Uri recieved in the upload prepare stage</param> /// <param name="callback">Callback that will be invoke upon completion of the upload. Null is sent on request failure</param> public void PerformUpload(Uri uploader, ModelUploadCallback callback) { CapsClient request = new CapsClient(uploader, "MeshUploader"); request.OnComplete += (client, result, error) => { if (error != null || result == null || result.Type != OSDType.Map) { Logger.Log("Mesh upload request failure", Helpers.LogLevel.Error); callback?.Invoke(null); return; } OSDMap res = (OSDMap)result; Logger.Log("Response from mesh upload perform:" + Environment.NewLine + OSDParser.SerializeLLSDNotationFormatted(result), Helpers.LogLevel.Debug); callback?.Invoke(res); }; request.PostRequestAsync(AssetResources(true), OSDFormat.Xml, 60 * 1000); }
private void GatherCapsResponse(CapsClient client, OSD response, Exception error) { if (response is OSDMap) { OSDMap respTable = (OSDMap)response; // parse _caps = new RegistrationCaps(); _caps.CreateUser = respTable["create_user"].AsUri(); _caps.CheckName = respTable["check_name"].AsUri(); _caps.GetLastNames = respTable["get_last_names"].AsUri(); _caps.GetErrorCodes = respTable["get_error_codes"].AsUri(); // finalize _initializing++; GatherErrorMessages(); } }
private void ParcelVoiceInfoResponse(CapsClient client, OSD response, Exception error) { if (!(response is OSDMap)) { return; } var respTable = (OSDMap)response; var regionName = respTable["region_name"].AsString(); var localID = respTable["parcel_local_id"].AsInteger(); string channelURI = null; if (respTable["voice_credentials"] is OSDMap) { var creds = (OSDMap)respTable["voice_credentials"]; channelURI = creds["channel_uri"].AsString(); } OnParcelVoiceInfo?.Invoke(regionName, localID, channelURI); }