Unity3D XML (-RPC) и C # - PullRequest
       7

Unity3D XML (-RPC) и C #

7 голосов
/ 23 августа 2011

Я на самом деле отвечаю на свой вопрос здесь.

Я, должно быть, единственный человек в мире, который пытался это сделать, но, учитывая, что мне потребовалось около недели, чтобы разобраться с этим - я понял,что если когда-нибудь найдется другой человек, который захочет использовать XML (-RPC) в Unity, я спасу его от хлопот на несколько недель.

Я хотел бы поговорить с одним из наших игровых серверов на такие вещи, какLeaderboards.Этот сервер «общается» с XML-RPC, и вскоре я понял, что в Unity это непросто.

1 Ответ

3 голосов
/ 15 сентября 2011

Сборка XML для отправки на наши серверы

Я не смог найти стандартную функцию в Unity, чтобы сделать это без добавления очень больших накладных расходов. Поэтому вместо этого я строю следующую процедуру.

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 на наши серверы, вам нужно отправить запрос POST с типом mime, равным text / xml. Ни один из стандартных методов C # не может быть использован в Unity, но использование этого с выводом логики 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);
    }    
}
  • кодирует XML в ByteArray, используя UTF8
  • Создать новый Unity WWWForm
  • Создайте HashTable, сохраните текущие заголовки http (если есть) и перезапишите тип содержимого в text / xml
  • Отправить этот лот на сервер
  • Настройка сопрограммы, которая ждет ответа

Отправка без единства

Я обнаружил, что разработка библиотеки на C # (я использую стандартную версию MonoDevelop) намного проще, чем использование Unity для всего, поэтому логика отправки equivelant в C # ниже, если wnat делает то же самое.

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

Извлечение данных из XML

Я написал простой парсер. Конструктор для функции findNode, приведенной ниже, должен содержать необработанные данные XML и объект дочернего узла, который вы хотите найти. Он вернет значение этого узла (в виде строки), если этот узел может быть найден на самом высоком уровне строки XML, или ноль, если он не может его найти. Этот синтаксический анализатор специфичен для «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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...