/// <summary> /// Calls a component on the Newgrounds.io server. /// Note: This is an abstract method and you are better served using the callWith() method of any object in the io.newgrounds.components namespace. /// </summary> /// <param name="component">The component name to call, ie Gateway.ping</param> /// <param name="parameters">An object containing any properties you need to send to the component.</param> /// <param name="callback">An optional callback action that will run when the server has executed this component call.</param> public void callComponent(string component, object parameters = null, Action <ResultModel> callback = null) { SimpleJSONImportableList single_call = new SimpleJSONImportableList(typeof(objects.call)); single_call.Add(_createCall(component, parameters, callback)); StartCoroutine(_executeCallList(single_call)); }
/// <summary> /// Extracts keys from a SimpleJSON.JSONNode matching public properties in this model object. /// </summary> /// <param name="json">A SimpleJSON.JObject that has been processed from a JSON string.</param> /// <param name="core">The core instance to attach to this object.</param> internal void setPropertiesFromSimpleJSON(JObject json, core core = null) { if (core != null) { this.ngio_core = core; } else { core = this.ngio_core; } Type rType = this.GetType(); PropertyInfo[] rProps = rType.GetProperties(); Dictionary <string, JObject> jdict = json.ObjectValue; SimpleJSONImportable pval; Type ptype; foreach (PropertyInfo property in rProps) { if (!jdict.ContainsKey(property.Name)) { continue; } bool is_model = property.PropertyType.IsSubclassOf(typeof(SimpleJSONImportable)); JObject jprop = jdict[property.Name]; // subclasses can override this method to handle special case properties if (setSpecialPropertyFromJSON(property.Name, jprop, core)) { continue; } if (jprop.Kind == JObjectKind.Null) { property.SetValue(this, null, null); } else if (is_model) { pval = (SimpleJSONImportable)property.GetValue(this, null); pval.setPropertiesFromSimpleJSON(jprop, core); } else if (isMultiTypeProperty(property.Name) && multi_property_map[property.Name][MultiValueFlat].GetType().IsSubclassOf(typeof(SimpleJSONImportable))) { switch (jprop.Kind) { case JObjectKind.Array: SimpleJSONImportableList plist = new SimpleJSONImportableList(); ptype = multi_property_map[property.Name][MultiValueFlat].GetType(); plist.type = ptype; foreach (JObject jrow in jprop.ArrayValue) { pval = (SimpleJSONImportable)Activator.CreateInstance(ptype); pval.setPropertiesFromSimpleJSON(jrow, core); plist.Add(pval); } property.SetValue(this, plist, null); break; case JObjectKind.Object: pval = (SimpleJSONImportable)property.GetValue(this, null); pval.setPropertiesFromSimpleJSON(jprop, core); break; default: Debug.LogWarning("Unexpected property value for " + property.Name + ". Setting to null."); property.SetValue(this, null, null); break; } } else { JObjectKind jk = jprop.Kind; string ppt = property.PropertyType.ToString(); // some bools and numbers may get passed in as strings, this should catch them. if (jk == JObjectKind.String && ppt != "System.String") { switch (ppt) { case "System.Boolean": jk = JObjectKind.Boolean; break; default: jk = JObjectKind.Number; break; } } // and some bools may be set as 0/1 integers too else if (jk == JObjectKind.Number && ppt == "System.Boolean") { jk = JObjectKind.Boolean; } switch (jk) { case JObjectKind.Null: property.SetValue(this, null, null); break; case JObjectKind.Boolean: property.SetValue(this, jprop.BooleanValue, null); break; case JObjectKind.String: property.SetValue(this, jprop.StringValue, null); break; case JObjectKind.Number: // now to figure out what number type to use... if (ppt.StartsWith("System.Int")) { property.SetValue(this, jprop.IntValue, null); } else { property.SetValue(this, jprop.DoubleValue, null); } break; default: Debug.LogWarning("Unexpected property value for " + property.Name + " (Kind=" + jk.ToString() + "). Setting to null."); property.SetValue(this, null, null); break; } } } }
/// This handles the actual transactions with the Newgrounds.io server. This runs as a Coroutine in Unity. private IEnumerator _executeCallList(SimpleJSONImportableList call_list) { objects.input input = new objects.input(); input.app_id = app_id; if (!string.IsNullOrEmpty(session_id)) { input.session_id = session_id; } SimpleJSONImportableList calls = new SimpleJSONImportableList(typeof(objects.call)); objects.call vc; CallModel cm; ResultModel vr; for (int c = 0; c < call_list.Count; c++) { vc = (objects.call)call_list[c]; cm = CallModel.getByComponentName(vc.component); if (cm.require_session && string.IsNullOrEmpty(session_id)) { vr = (ResultModel)ResultModel.getByComponentName(vc.component); vr.setIsLocal(true); vr.setCall(vc); vr.component = vc.component; vr.success = false; vr.error.message = vc.component + " requires a valid user session."; vr.ngio_core = this; debug_log("Call failed local validation:\n" + vc.toJSON()); debug_log("Local Response:\n" + vr.toJSON()); if (vc.callback != null) { vc.callback(vr); } vr.dispatchMe(vc.component); continue; } else { calls.Add(vc); } } if (calls.Count > 0) { input.call = calls; string json = input.toJSON(); debug_log("Sent to Server:\n" + json); WWWForm webform = new WWWForm(); webform.AddField("input", json); WWW json_results = new WWW(GATEWAY_URI, webform); yield return(json_results); debug_log("Server Response:\n" + json_results.text); // hopefully, our results will populate to this objects.output _output = new objects.output(); string default_error = "There was a problem connecting to the server at '" + GATEWAY_URI + "'."; // we'll try decoding what the server sent back. If it's valid JSON there should be no problems try { // decode the overall response JObject jobject = JSONDecoder.Decode(json_results.text); // populate the basic info our output model will need to proceed if (jobject.Kind == JObjectKind.Object) { _output.setPropertiesFromSimpleJSON(jobject); } } // catch any exceptions and update the generic error message we'll spit back. catch (Exception e) { Debug.LogWarning("Caught an exception decoding the data:\n" + e.Message); default_error = e.Message; } _output.ngio_core = this; // We'll typically only get here if the was a server or connection error. if (_output.success != true && _output.error == null) { _output.error = new objects.error(); _output.error.message = default_error; } // cheap way to fill debug info even with debug mode being off _output.debug.input = input; ResultModel _rm; objects.result _result; objects.call call; for (int i = 0; i < calls.Count; i++) { call = (objects.call)calls[i]; if (_output.success != true) { _rm = new ResultModel(); _rm.error = _output.error; } else if (_output.result.GetType() == typeof(SimpleJSONImportableList) && ((SimpleJSONImportableList)_output.result).ElementAtOrDefault <object>(i) != null) { _result = (objects.result)((SimpleJSONImportableList)_output.result)[i]; _result.ngio_core = this; if (_result.component != call.component) { _rm = new ResultModel(); _rm.success = false; _rm.error.message = "Unexpected index mismatch in API response!"; } else { _rm = (ResultModel)_result.data; } } else { _rm = new ResultModel(); _rm.success = false; _rm.error.message = "Unexpected index mismatch in API response!"; } _rm.ngio_core = this; _rm.setCall(call); if (call.callback != null) { call.callback(_rm); } _rm.dispatchMe(call.component); } } }