2011-08-23 23 views
7

私は実際に私自身の質問に答えています。Unity3D XML(-RPC)とC#

私はこれをやろうとしたが、これを解決するのに約1週間かかりましたが、私はXMLを使用したい別の人がいれば)Unityで - 私は彼らに数週間の面倒を保存します。

私がやりたいことは、リーダーボードなどのゲームサーバーの1つに話すことです。このサーバはXML-RPCを「話す」と私はすぐにそれがUnityでは簡単ではないことを理解しました。当社のサーバーに

を送信するために

+1

+1非常に詳細な、そして非常にうまく行って、私は将来 – Spooks

+0

+1やブックマーク、印加:-)でこれを必要と確信しています – Kay

答えて

3

ビルドXML Iは、オーバーヘッドの非常に大きな金額を追加せずにこれを行うにはユニティでの標準的な機能を見つけることができませんでした。だから私は代わりに次の手順を構築する。

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

XMLを構築するためにbuildXMLRPCRequestが使用されます。エンコードするフィールドを持つHas​​hTableを渡します。エンコードするフィールドには、int型、string型、Hashtable型のオブジェクトが含まれます。それは私たちのサーバーに行く準備ができている美しく形式化された(単純な)XML-RPC文字列を返します。

当社のサーバーにXMLを送信するには

を送るには、テキスト/ xmlに設定MIMEタイプを持つPOSTリクエストを発行する必要があります。 Unityで標準のC#メソッドを使用することはできませんが、これを使用してbuildXMLRPCRequestロジックの出力が完全に機能します。何それがない:UTF8

  • を使用してのByteArrayにXMLが新しいユニティWWWForm
  • を作成します

    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); 
        }  
    } 
    
    • エンコード:

      はユニティ

      に送信する私はこのコードを使用HashTable、現在のhttpヘッダー(存在する場合)を格納し、コンテンツタイプをtext/xmlに上書きする

    • 送信サーバー私は(私はMonoDevelopのの標準バージョンを使用)C#でライブラリを開発することは非常に簡単であることがわかっ返信ユニティ

      せずに送信

    を待っコルーチンを設定

  • にその多くUnityをすべてのものに使用するので、C#でequivelantのロジックを送信すると、以下のようになります。 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; 
    }