2011-08-23 36 views
7

我其實在這裏回答我自己的問題。Unity3D XML(-RPC)和C#

我必須是世界上唯一一個試圖這樣做的人,但考慮到它讓我花了大約一週的時間來解決這個問題 - 我想如果有另一個人想要使用XML(-RPC )在團結 - 我會救他們一個星期的麻煩。

我想要做的是與我們的遊戲服務器之一討論諸如排行榜之類的東西。這個服務器「會談」XML-RPC,我很快就發現在Unity中這並不容易。

+1

+1非常詳細,而且非常好做,我相信我會需要這個在未來 – Spooks

+0

+1和書籤,留下深刻印象:-) – Kay

回答

3

構建XML發送到我們的服務器

我無法找到統一的標準功能做到不添加非常大量的開銷。所以我建立了以下過程。

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

buildXMLRPCRequest用於構建XML。你可以用你想要編碼的字段給它一個HashTable,它可能包含類型的對象:int,string或Hashtable。它將返回一個精美的(簡單)XML-RPC字符串,該字符串已準備好進入我們的服務器。

發送

發送XML到我們的服務器,您需要發出設置爲text/xml MIME類型POST請求。在Unity中沒有標準的C#方法可以使用,但使用這種方法可以使buildXMLRPCRequest邏輯的輸出完美地工作。它的作用:

在Unity

發送我用這個代碼:

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); 
    }  
} 
  • 編碼使用UTF8
  • 的XML爲ByteArray創建一個新的統一WWWForm
  • 創建HashTable,存儲當前的http頭(如果有的話)並將內容類型覆蓋爲文本/ xml
  • 發送那很多服務器
  • 建立一個協程,其等待答覆

沒有統一

發送

我發現,在C#開發庫(我用的MonoDevelop的標準版)要簡單得多然後使用Unity處理所有事情,這樣C#中的Equivelant發送邏輯就會在下面,如果wnat也這樣做的話。從XML

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

提取數據我寫了一個簡單的解析器。應該爲下面的findNode函數的構造函數提供原始XML數據和您想要查找的子節點對象。如果該節點可以在XML字符串的最高級別上找到,它將返回該節點的值(作爲字符串),如果找不到該節點,則返回null。此解析器特定於「Simple XML-RPC」,需要一些工作來解碼編碼字符,但應該很容易添加。

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