summaryrefslogtreecommitdiff
path: root/Mage/Assets/ThirdParty/Photon/PhotonRealtime/Code/Unity/Editor/AccountService.cs
blob: e3f2397be95c651e7fde785f21ec365059da6d4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
// ----------------------------------------------------------------------------
// <copyright file="AccountService.cs" company="Exit Games GmbH">
//   Photon Cloud Account Service - Copyright (C) 2012 Exit Games GmbH
// </copyright>
// <summary>
//   Provides methods to register a new user-account for the Photon Cloud and
//   get the resulting appId.
// </summary>
// <author>developer@exitgames.com</author>
// ----------------------------------------------------------------------------

#if UNITY_2017_4_OR_NEWER
#define SUPPORTED_UNITY
#endif


#if UNITY_EDITOR

namespace Photon.Realtime
{
    using System;
    using UnityEngine;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    using ExitGames.Client.Photon;


    /// <summary>
    /// Creates a instance of the Account Service to register Photon Cloud accounts.
    /// </summary>
    public class AccountService
    {
        private const string ServiceUrl = "https://partner.photonengine.com/api/{0}/User/RegisterEx";

        private readonly Dictionary<string, string> RequestHeaders = new Dictionary<string, string>
        {
            { "Content-Type", "application/json" },
            { "x-functions-key", "" }
        };

        private const string DefaultContext = "Unity";

        private const string DefaultToken = "VQ920wVUieLHT9c3v1ZCbytaLXpXbktUztKb3iYLCdiRKjUagcl6eg==";

        /// <summary>
        /// third parties custom context, if null, defaults to DefaultContext property value
        /// </summary>
        public string CustomContext = null;     // "PartnerCode" on the server

        /// <summary>
        /// third parties custom token. If null, defaults to DefaultToken property value
        /// </summary>
        public string CustomToken = null;

        /// <summary>
        /// If this AccountService instance is currently waiting for a response. While pending, RegisterByEmail is blocked.
        /// </summary>
        public bool RequestPendingResult = false;

        /// <summary>
        /// Attempts to create a Photon Cloud Account asynchronously. Blocked while RequestPendingResult is true.
        /// </summary>
        /// <remarks>
        /// Once your callback is called, check ReturnCode, Message and AppId to get the result of this attempt.
        /// </remarks>
        /// <param name="email">Email of the account.</param>
        /// <param name="serviceTypes">Defines which type of Photon-service is being requested.</param>
        /// <param name="callback">Called when the result is available.</param>
        /// <param name="errorCallback">Called when the request failed.</param>
        /// <param name="origin">Can be used to identify the origin of the registration (which package is being used).</param>
        public bool RegisterByEmail(string email, List<ServiceTypes> serviceTypes, Action<AccountServiceResponse> callback = null, Action<string> errorCallback = null, string origin = null)
        {
            if (this.RequestPendingResult)
            {
                Debug.LogError("Registration request pending result. Not sending another.");
                return false;
            }

            if (!IsValidEmail(email))
            {
                Debug.LogErrorFormat("Email \"{0}\" is not valid", email);
                return false;
            }

            string serviceTypeString = GetServiceTypesFromList(serviceTypes);
            if (string.IsNullOrEmpty(serviceTypeString))
            {
                Debug.LogError("serviceTypes string is null or empty");
                return false;
            }

            string fullUrl = GetUrlWithQueryStringEscaped(email, serviceTypeString, origin);

            RequestHeaders["x-functions-key"] = string.IsNullOrEmpty(CustomToken) ? DefaultToken : CustomToken;


            this.RequestPendingResult = true;

            PhotonEditorUtils.StartCoroutine(
                PhotonEditorUtils.HttpPost(fullUrl,
                    RequestHeaders,
                    null,
                    s =>
                    {
                        this.RequestPendingResult = false;
                        //Debug.LogWarningFormat("received response {0}", s);
                        if (string.IsNullOrEmpty(s))
                        {
                            if (errorCallback != null)
                            {
                                errorCallback("Server's response was empty. Please register through account website during this service interruption.");
                            }
                        }
                        else
                        {
                            AccountServiceResponse ase = this.ParseResult(s);
                            if (ase == null)
                            {
                                if (errorCallback != null)
                                {
                                    errorCallback("Error parsing registration response. Please try registering from account website");
                                }
                            }
                            else if (callback != null)
                            {
                                callback(ase);
                            }
                        }
                    },
                    e =>
                    {
                        this.RequestPendingResult = false;
                        if (errorCallback != null)
                        {
                            errorCallback(e);
                        }
                    })
            );
            return true;
        }


        private string GetUrlWithQueryStringEscaped(string email, string serviceTypes, string originAv)
        {
            string emailEscaped = UnityEngine.Networking.UnityWebRequest.EscapeURL(email);
            string st = UnityEngine.Networking.UnityWebRequest.EscapeURL(serviceTypes);
            string uv = UnityEngine.Networking.UnityWebRequest.EscapeURL(Application.unityVersion);
            string serviceUrl = string.Format(ServiceUrl, string.IsNullOrEmpty(CustomContext) ? DefaultContext : CustomContext );

            return string.Format("{0}?email={1}&st={2}&uv={3}&av={4}", serviceUrl, emailEscaped, st, uv, originAv);
        }

        /// <summary>
        /// Reads the Json response and applies it to local properties.
        /// </summary>
        /// <param name="result"></param>
        private AccountServiceResponse ParseResult(string result)
        {
            try
            {
                AccountServiceResponse res = JsonUtility.FromJson<AccountServiceResponse>(result);
                // Unity's JsonUtility does not support deserializing Dictionary, we manually parse it, dirty & ugly af, better then using a 3rd party lib
                if (res.ReturnCode == AccountServiceReturnCodes.Success)
                {
                    string[] parts = result.Split(new[] { "\"ApplicationIds\":{" }, StringSplitOptions.RemoveEmptyEntries);
                    parts = parts[1].Split('}');
                    string applicationIds = parts[0];
                    if (!string.IsNullOrEmpty(applicationIds))
                    {
                        parts = applicationIds.Split(new[] { ',', '"', ':' }, StringSplitOptions.RemoveEmptyEntries);
                        res.ApplicationIds = new Dictionary<string, string>(parts.Length / 2);
                        for (int i = 0; i < parts.Length; i = i + 2)
                        {
                            res.ApplicationIds.Add(parts[i], parts[i + 1]);
                        }
                    }
                    else
                    {
                        Debug.LogError("The server did not return any AppId, ApplicationIds was empty in the response.");
                        return null;
                    }
                }
                return res;
            }
            catch (Exception ex) // probably JSON parsing exception, check if returned string is valid JSON
            {
                Debug.LogException(ex);
                return null;
            }
        }

        /// <summary>
        /// Turns the list items to a comma separated string. Returns null if list is null or empty.
        /// </summary>
        /// <param name="appTypes">List of service types.</param>
        /// <returns>Returns null if list is null or empty.</returns>
        private static string GetServiceTypesFromList(List<ServiceTypes> appTypes)
        {
            if (appTypes == null || appTypes.Count <= 0)
            {
                return null;
            }

            string serviceTypes = ((int)appTypes[0]).ToString();
            for (int i = 1; i < appTypes.Count; i++)
            {
                int appType = (int)appTypes[i];
                serviceTypes = string.Format("{0},{1}", serviceTypes, appType);
            }

            return serviceTypes;
        }

        // RFC2822 compliant matching 99.9% of all email addresses in actual use today
        // according to http://www.regular-expressions.info/email.html [22.02.2012]
        private static Regex reg = new Regex("^((?>[a-zA-Z\\d!#$%&'*+\\-/=?^_{|}~]+\\x20*|\"((?=[\\x01-\\x7f])[^\"\\]|\\[\\x01-\\x7f])*\"\\x20*)*(?<angle><))?((?!\\.)(?>\\.?[a-zA-Z\\d!#$%&'*+\\-/=?^_{|}~]+)+|\"((?=[\\x01-\\x7f])[^\"\\]|\\[\\x01-\\x7f])*\")@(((?!-)[a-zA-Z\\d\\-]+(?<!-)\\.)+[a-zA-Z]{2,}|\\[(((?(?<!\\[)\\.)(25[0-5]|2[0-4]\\d|[01]?\\d?\\d)){4}|[a-zA-Z\\d\\-]*[a-zA-Z\\d]:((?=[\\x01-\\x7f])[^\\\\[\\]]|\\[\\x01-\\x7f])+)\\])(?(angle)>)$",
             RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
        public static bool IsValidEmail(string mailAddress)
        {
            if (string.IsNullOrEmpty(mailAddress))
            {
                return false;
            }
            var result = reg.Match(mailAddress);
            return result.Success;
        }
    }

    [Serializable]
    public class AccountServiceResponse
    {
        public int ReturnCode;
        public string Message;
        public Dictionary<string, string> ApplicationIds; // Unity's JsonUtility does not support deserializing Dictionary
    }


    public class AccountServiceReturnCodes
    {
        public static int Success = 0;
        public static int EmailAlreadyRegistered = 8;
        public static int InvalidParameters = 12;
    }

    public enum ServiceTypes
    {
        Realtime = 0,
        Turnbased = 1,
        Chat = 2,
        Voice = 3,
        TrueSync = 4,
        Pun = 5,
        Thunder = 6,
        Quantum = 7,
        Fusion = 8,
        Bolt = 20
    }
}

#endif