2011-08-23 12 views
7

En realidad estoy respondiendo mi propia pregunta aquí.Unity3D XML (-RPC) y C#

Debo ser la única persona en el mundo que intentó hacer esto, pero dado que me ha tomado alrededor de una semana resolver esto, pensé que si alguna vez otra persona quiere usar XML (-RPC) en Unity - Les ahorraré una molestia de semanas.

Lo que quería hacer es hablar con uno de nuestros servidores de juegos para cosas como tablas de clasificación. Este servidor "habla" XML-RPC y pronto descubrí que eso no es fácil en Unity.

+1

+1 muy detallado y muy bien hecho, estoy seguro de que voy a necesitar en el futuro – Spooks

+0

1 y marcada, impresionando :-) – Kay

Respuesta

3

Generar XML para enviar a nuestros servidores

no pude encontrar una función estándar en la Unidad de hacer esto sin la adición de cantidades muy grandes de sobrecarga. Entonces construyo el siguiente procedimiento en su lugar.

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

El buildXMLRPCRequest se utiliza para crear XML. Usted le da una tabla Hash con los campos que desea codificar, que pueden incluir objetos de los tipos: int, string o Hashtable. Devolverá una cadena XML-RPC bellamente formateada (Simple) que está lista para ir a nuestro servidor.

Enviar

Para enviar XML a nuestros servidores, es necesario emitir una solicitud POST con el tipo MIME se establece en text/xml. Ninguno de los métodos estándar de C# se puede usar en Unity, pero usar esto con el resultado de la lógica buildXMLRPCRequest funciona perfectamente. Lo que hace:

El envío de la Unidad

He utilizado este código:

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); 
    }  
} 
  • codificar el código XML en un ByteArray usando UTF8
  • Crear una nueva Unidad WWWForm
  • Crear una HashTable, almacene los encabezados http actuales (si los hay) y sobrescriba el tipo de contenido a texto/xml
  • Enviar ese lote al servidor
  • Establecer un Coroutine que espera por la respuesta

Envío sin unidad

He encontrado que el desarrollo de una biblioteca en C# (yo uso la versión de las normas de MonoDevelop) es mucho más simple luego usa Unity para todo, por lo que la lógica de envío de Equivelant en C# está debajo si quiere hacer lo mismo. datos

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

fragmento del documento XML

yo escribimos un analizador simple. El constructor de la función findNode siguiente debe tener los datos XML sin procesar y el objeto del nodo secundario que desea buscar. Devolverá el valor de ese nodo (como una cadena) si ese nodo se puede encontrar en el nivel más alto de la cadena XML o nulo si no puede encontrarlo. Este analizador es específico de "Simple XML-RPC" y necesita un poco de trabajo para decodificar los caracteres codificados, pero debería ser fácil de agregar.

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