2011-08-23 17 views
7

In realtà sto rispondendo alla mia domanda qui.Unity3D XML (-RPC) e C#

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 mai c'è un'altra persona che vuole usare XML (-RPC) in Unity: li salverò per un paio di settimane.

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.

+1

+1 molto dettagliata, e molto ben fatto, sono sicuro che avrò bisogno di questo in futuro – Spooks

+0

+1 e segnalibro, impressionando :-) – Kay

risposta

3

Corporatura XML per inviare ai nostri server

non riuscivo a trovare una funzione standard in Unity per fare questo senza l'aggiunta di grandi quantità di overhead. 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" + "<simpleRPC version=\"0.9\">" + 
         "\n" + "<methodCall>" + 
         "\n" + "<methodName>" + MethodName + "</methodName>" + 
         "\n" + "<vector type=\"struct\">"; 

    ReturnString += buildNode(FieldArray); 

    ReturnString += "\n</vector>" + 
         "\n</methodCall>" + 
         "\n</simpleRPC>"; 
    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 + "</" + NodeType + ">"; 
     ReturnList += ThisNode; 
    } 

    return ReturnList; 
} 

Il 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.

Invia

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 Unity

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 usando UTF8
  • Creare una nuova unità WWWForm
  • Creare un HashTable, archivia le intestazioni HTTP correnti (se presenti) e sovrascrive il tipo di contenuto in text/xml
  • Invia che molto al server
  • Impostare 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 quindi usando Unity per ogni cosa in modo che la logica di invio dell'equivalente in C# sia inferiore se si vuole fare lo stesso. Dati

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; 
    } 
} 

estratto da XML

ho scritto un semplice parser. Il costruttore per la funzione findNode riportata di seguito deve fornire i dati XML non elaborati e l'oggetto 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;i<TagArray.Length;i++) { 

     if (i>175 && 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("</","</"); 
     string [] FieldArray = ThisLine.Split(' '); 
     bool AddLineToResult = FoundTag; 

     // Nest counter is the level we are operating on. We only check the first 
     // Level. When a vector is found we increase the NestCount and we won't 
     // search for the ID 

     if (NestCounter <= 1) { // Initial array we are looking on level 1 
      for (int a=0;a<FieldArray.Length;a++) { 
       string ThisTag = FieldArray[a]; 
       string [] TagValue = ThisTag.Split("=\"".ToCharArray(),5); 

       // Every TagValue is xx=yy pair... we want "ID=\"xxx\" 

       if (TagValue.Length >= 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 == "</scalar>")) { 
       break; 
       // return ReturnValue; 
      } 
      // If we end or leave a vector we change the NestCounter 
      if (ThisField.IndexOf("<vector") >= 0) { 
       NestCounter++; 
      } 
      else if (ThisField.IndexOf("</vector>") >= 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; 
}