2011-08-23 23 views
7

Tôi thực sự trả lời câu hỏi của riêng mình ở đây.Unity3D XML (-RPC) và C#

Tôi phải là người duy nhất trên thế giới cố gắng làm điều này, nhưng phải mất khoảng một tuần để làm việc này - tôi đã thấy rằng nếu có người khác muốn sử dụng XML (-RPC) trong Unity - Tôi sẽ tiết kiệm cho họ một tuần rắc rối.

Điều tôi muốn làm là nói chuyện với một trong các máy chủ Trò chơi của chúng tôi cho những thứ như bảng xếp hạng. Máy chủ này "nói" XML-RPC và tôi sớm nhận ra rằng điều đó không dễ dàng trong Unity.

+1

+1 rất chi tiết, và rất độc đáo được thực hiện, tôi chắc chắn tôi sẽ cần điều này trong tương lai – Spooks

+0

+1 và đánh dấu, gây ấn tượng :-) – Kay

Trả lời

3

Xây dựng XML để gửi đến máy chủ của chúng tôi

Tôi không thể tìm thấy hàm chuẩn trong Unity để thực hiện việc này mà không cần thêm một lượng rất lớn chi phí. Vì vậy, tôi xây dựng các thủ tục sau đây để thay thế.

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 được sử dụng để xây dựng XML. Bạn đưa nó một HashTable với các trường mà bạn muốn mã hóa có thể bao gồm các đối tượng thuộc loại: int, string hoặc Hashtable. Nó sẽ trả về một chuỗi XML-RPC được định dạng đẹp (Simple) đã sẵn sàng để truy cập vào máy chủ của chúng tôi.

Gửi

Để gửi XML tới máy chủ của chúng tôi, bạn cần gửi yêu cầu POST với loại mime được đặt thành văn bản/xml. Không có phương thức C# chuẩn nào có thể được sử dụng trong Unity nhưng sử dụng phương thức này với đầu ra của logic buildXMLRPCRequest hoạt động hoàn hảo. Những gì nó:

Gửi trong Unity

tôi đã sử dụng mã này:

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); 
    }  
} 
  • mã hóa XML để một ByteArray sử dụng UTF8
  • Tạo một Unity WWWForm mới
  • Tạo HashTable, lưu trữ tiêu đề http hiện tại (nếu có) và ghi đè loại nội dung thành văn bản/xml
  • Gửi mà rất nhiều đến máy chủ
  • Thiết lập một coroutine mà chờ đợi câu trả lời

Sending mà không Unity

tôi thấy rằng việc phát triển một thư viện trong C# (Tôi sử dụng phiên bản tiêu chuẩn của MonoDevelop) là đơn giản hơn nhiều sau đó sử dụng Unity cho tất cả mọi thứ vì vậy logic gửi tương đương trong C# là dưới đây nếu wnat để làm như vậy.

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

Extract dữ liệu từ XML

Tôi đã viết một phân tích cú pháp đơn giản. Hàm khởi tạo cho hàm findNode dưới đây sẽ được cung cấp dữ liệu XML thô và đối tượng nút con mà bạn muốn tìm. Nó sẽ trả về giá trị của nút đó (dưới dạng một chuỗi) nếu nút đó có thể được tìm thấy ở cấp cao nhất của chuỗi XML hoặc null nếu nó không thể tìm thấy nó. Trình phân tích cú pháp này dành riêng cho "Simple XML-RPC" và cần một chút công việc để giải mã các ký tự được mã hóa nhưng điều đó sẽ đơn giản để thêm vào.

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; 
} 
Các vấn đề liên quan