Unity3D XML (-RPC) e C #

In realtà sto rispondendo alla mia domanda qui.

Devo essere l’unica persona al mondo che ha provato a farlo, ma visto che mi ci è voluta circa una settimana per risolvere il problema – ho pensato che se c’è sempre un’altra persona che vuole usare XML (-RPC) in Unity – Li salverò con una settimana di seccature.

Quello che volevo fare è parlare con uno dei nostri server di gioco per cose come le classifiche. Questo server “parla” XML-RPC e presto ho capito che non è facile in Unity.

Crea XML da inviare ai nostri server

Non sono riuscito a trovare una funzione standard in Unity per farlo senza aggiungere grandi quantità di spese generali. Quindi, invece, costruisco la seguente procedura.

public string buildXMLRPCRequest(Hashtable FieldArray,string MethodName) { string ReturnString = ""; ReturnString += "< ?xml version=\"1.0\" encoding=\"iso-8859-1\"?>" + "\n" + "" + "\n" + "" + "\n" + "" + MethodName + "" + "\n" + ""; ReturnString += buildNode(FieldArray); ReturnString += "\n" + "\n" + "\n"; return ReturnString; } public string buildNode(Hashtable FieldArray) { string ReturnList = ""; foreach (DictionaryEntry Item in FieldArray) { string TypeName = "int"; string NodeType = "scalar"; Type myType = Item.Value.GetType(); string fieldValue = ""; if (myType == typeof(string) ) { TypeName = "string"; fieldValue = Item.Value.ToString(); } if (myType == typeof(Hashtable) ) { fieldValue = buildNode(Item.Value as Hashtable); NodeType = "vector"; TypeName = "struct"; } if (myType == typeof(int) ) { fieldValue = Item.Value.ToString(); TypeName = "int"; } var ThisNode = "\n< " + NodeType + " type=\"" + TypeName + "\" id=\"" + Item.Key + "\">" + fieldValue + ""; ReturnList += ThisNode; } return ReturnList; } 

BuildXMLRPCRequest viene utilizzato per creare XML. Consegnagli una HashTable con i campi che vuoi codificare, che possono includere oggetti dei tipi: int, string o Hashtable. Restituirà una stringa XML-RPC splendidamente formattata (semplice) pronta per il nostro server.

Inviare

Per inviare XML ai nostri server, è necessario inviare una richiesta POST con il tipo mime impostato su text / xml. Nessuno dei metodi C # standard può essere utilizzato in Unity, ma l’utilizzo di questo con l’output della logica buildXMLRPCRequest funziona perfettamente. Cosa fa:

Invio in unità

Ho usato questo codice:

  private void UnityPostXML( int Staging, string WebServer, string MethodName, Hashtable FieldArray) { string WebServiceURL = "http://LIVESERVER/"; if (Staging == 1) { WebServiceURL = "http://TESTSERVER"; } // Encode the text to a UTF8 byte arrray string XMLRequest = buildXMLRPCRequest(FieldArray,MethodName); System.Text.Encoding enc = System.Text.Encoding.UTF8; byte[] myByteArray = enc.GetBytes(XMLRequest); // Get the Unity WWWForm object (a post version) var form = new WWWForm(); var url = WebServiceURL; // Add a custom header to the request. // Change the content type to xml and set the character set var headers = form.headers; headers["Content-Type"]="text/xml;charset=UTF-8"; // Post a request to an URL with our rawXMLData and custom headers var www = new WWW(WebServiceURL, myByteArray, headers); // Start a co-routine which will wait until our servers comes back StartCoroutine(WaitForRequest(www)); } IEnumerator WaitForRequest(WWW www) { yield return www; // check for errors if (www.error == null) { Debug.Log("WWW Ok!: " + www.text); } else { Debug.Log("WWW Error: "+ www.error); } } 
  • codificare l’XML in un ByteArray utilizzando UTF8
  • Crea un nuovo Unity WWWForm
  • Crea una HashTable, archivia le intestazioni HTTP correnti (se presenti) e sovrascrive il tipo di contenuto in text / xml
  • Invia quel lotto al server
  • Configura una Coroutine che attende la risposta

Invio senza unità

Ho scoperto che lo sviluppo di una libreria in C # (io uso la versione standard di MonoDevelop) è molto più semplice di usare Unity per tutto, quindi la logica di invio in C # è la seguente: se si vuole fare lo stesso.

  private string NormalXMLCall(int Staging, string WebServer, string MethodName, Hashtable Fields) { // Figure out who to call string WebServiceURL = "http://LIVSERVER"; if (Staging == 1) { WebServiceURL = "http://TESTSERVER"; } WebServiceURL += WebServer; // Build the request XmlRpcParser parser = new XmlRpcParser(); string XMLRequest = parser.buildXMLRPCRequest(Fields,MethodName); // Fire it off HttpWebRequest httpRequest =(HttpWebRequest)WebRequest.Create(WebServiceURL); httpRequest.Method = "POST"; //Defining the type of the posted data as XML httpRequest.ContentType = "text/xml"; // string data = xmlDoc.InnerXml; byte[] bytedata = Encoding.UTF8.GetBytes(XMLRequest); // Get the request stream. Stream requestStream = httpRequest.GetRequestStream(); // Write the data to the request stream. requestStream.Write(bytedata, 0, bytedata.Length); requestStream.Close(); //Get Response HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse(); // Get the stream associated with the response. Stream receiveStream = httpResponse.GetResponseStream (); // Pipes the stream to a higher level stream reader with the required encoding format. StreamReader readStream = new StreamReader (receiveStream, Encoding.UTF8); string ReceivedData = readStream.ReadToEnd (); httpResponse.Close (); readStream.Close (); return ReceivedData; } } 

Estrai dati da XML

Ho scritto un parser semplice. Il costruttore per la funzione findNode riportata di seguito deve fornire i dati XML non elaborati e l’object nodo figlio che si desidera trovare. Restituirà il valore di quel nodo (come una stringa) se quel nodo può essere trovato al livello più alto della stringa XML o null se non riesce a trovarlo. Questo parser è specifico per “Simple XML-RPC” e necessita di un po ‘di lavoro per decodificare i caratteri codificati, ma dovrebbe essere semplice da aggiungere.

 public string findNode(string Xml,string SearchForTag) { int NestCounter = 0; bool FoundTag = false; int FoundTagLevel = 0; string ReturnValue = null; // Break it down by "< " string [] TagArray = Xml.Split('<'); for (int i=0;i175 && i<180) { int Hello=1; } string ThisLine = "< " + TagArray[i]; if (ThisLine.Length <= 1) continue; if ((ThisLine.Length >= 2) && (ThisLine.Substring(0,2) == "< ?")) continue; if ((ThisLine.Length >= 3) && (ThisLine.Substring(0,3) == "< --")) continue; // It can be a vector or a scalar - vectors are full of scalars so we'll ThisLine = ThisLine.Replace(" "," "); ThisLine = ThisLine.Replace("= 3) { string TagName = TagValue[2]; if (TagName == SearchForTag) { FoundTag = true; FoundTagLevel = NestCounter; // This could be a vector or Scalar so find the ">" in this string // and start adding from there int TerminatePos = ThisLine.IndexOf(">"); if ((TerminatePos >= 0) && (TerminatePos < ThisLine.Length)) { ReturnValue = ThisLine.Substring(TerminatePos+1); } break; } } } } if (FieldArray.Length > 0) { string ThisField = FieldArray[0].ToLower(); /* * If we are in the loop where we have found the tag, * we haven't changed level and this is the end of a scalar it must * mean that the tag was a scalar so we can safely leave now. */ if ((FoundTag) && (FoundTagLevel == NestCounter) && (ThisField == "")) { break; // return ReturnValue; } // If we end or leave a vector we change the NestCounter if (ThisField.IndexOf("= 0) { NestCounter++; } else if (ThisField.IndexOf("") >= 0) { NestCounter--; } } // If we have found our tag and the nest counte goes below the level // we where looking at - it's time to leave if (FoundTag) { if (NestCounter < = FoundTagLevel) { break; //return ReturnValue; } } if (AddLineToResult) { ReturnValue += ThisLine; } } // You may wanna do some url decoding here.... return ReturnValue; }