--- title: "Advanced" disqus: hackmd tags: 'ctrl' --- ## Chapter 7 Advanced Applications ### 7-1 Security It is better to encrypt information when sending it onto the internet. Currently, there are two types of encryption and decryption algorithms : symmetrical and asymmetric. Since an asymmetric algorithm takes up a considerable amount of the CPU’s operation time, the suggestion is to use a symmetrical encryption algorithm to meet the game’s need for processing information quickly. SGC allows developers to tackle the encryption and decryption of the information packets because the keys for symmetrical encrypting and decrypting are the same. Avoiding functioning all the Cloud games under the same encryption and decryption algorithm and key, allowing developers to keep their own ways of encrypting and decrypting can lower the risk of being cracked. In the example program below, we define a global object MessageKey to store the key. With the help of the encryption and decryption functions in the Security object, the message can be encrypted and decrypted while sending and receiving it : ```csharp //TripleDes.cs using System; using System.Security.Cryptography; using System.IO; using System.Text; sealed internal class MessageKey { static MessageKey _context; // The following KEY values are for reference only. Please use an array that is made up of 16-byte numbers as the KEY. internal byte[] bKey={0x13,0xcd,0x8a,0xc5,0x7a,0x9,0xab,0x4f, 0xd6, 0x33, 0x59, 0x2b, 0x65, 0xe3, 0xf1, 0x4d}; //The following IV values are for reference only. Please use an array that is made up of 8-byte numbers as the KEY. internal byte[] bIV = {0x9a,0xdd, 0xef,0x1c,0x88,0x48,0xa0,0xf6}; static public MessageKey instance { get{ lock(typeof(MessageKey)) { if (_context == null) { _context = new MessageKey(); } } return _context; } } } public class Security { public Security() { mCSP.Key = MessageKey.instance.bKey; mCSP.IV = MessageKey.instance.bIV; } public string EncryptString(string Value) { ICryptoTransform ct; MemoryStream ms; CryptoStream cs; byte[] byt; mCSP.Mode = System.Security.Cryptography.CipherMode.ECB; mCSP.Padding = System.Security.Cryptography.PaddingMode.PKCS7; ct = mCSP.CreateEncryptor(mCSP.Key, mCSP.IV); byt = Encoding.UTF8.GetBytes(Value); ms = new MemoryStream(); cs = new CryptoStream(ms, ct, CryptoStreamMode.Write); cs.Write(byt, 0, byt.Length); cs.FlushFinalBlock(); cs.Close(); return Convert.ToBase64String(ms.ToArray()); } public string DecryptString(string Value) { ICryptoTransform ct; MemoryStream ms; CryptoStream cs; byte[] byt; mCSP.Mode = System.Security.Cryptography.CipherMode.ECB; mCSP.Padding = System.Security.Cryptography.PaddingMode.PKCS7; ct = mCSP.CreateDecryptor(mCSP.Key, mCSP.IV); byt = Convert.FromBase64String(Value); ms = new MemoryStream(); cs = new CryptoStream(ms, ct, CryptoStreamMode.Write); cs.Write(byt, 0, byt.Length); cs.FlushFinalBlock(); cs.Close(); return Encoding.UTF8.GetString(ms.ToArray()); } } ``` **Create a Security Object** A `Security` object can be created at the beginning of the program. Since the encryption and decryption functions of a `Security` object are for receiving and sending messages, the object has to be one that the message-receiving and message-sending functions can access. The following is an example of creating a `Security` object in DP’s FormLoad. ```csharp //(DP) dp001.cs Security so; private void dp001_Load(object sender, EventArgs e) { so = new Security(); labelSystemStatus.Text = "not connected yet"; ag = new CloudGame(userid, passwd, gguid, gcert); ag.onCompletion += OnCompletion; ag.onStateChanged += OnStateChanged; ag.onPrivateMessageIn += OnPrivateMessageIn; labelSystemStatus.Text = "connecting"; ag.Launch(); } ``` With a Security object, one can use the encryption and decryption functions in this object to execute encryption and decryption from now on. **Sending and Receiving Messages** The reception of messages processes in the OnPrivateMessageIn event function, and one simply needs to decrypt the received string messages. However, to make sure the messages are decryptable, when there are any problems in the received messages, which make the decryption encounter an Exception, we can simply consider the messages invalid and negligible. The following example is `OnPrivateMessageIn()`. We only added a call decryption function : ```csharp //(DP) dp001.cs void OnPrivateMessageIn(string msg, int d, CloudGame game) { try { string m = so.DecryptString(msg); string[] cmds = m.Split(':'); uint ClientPoid; (The below is ommitted) } catch (Exception) {} } ``` As for sending messages, we can define the `SecureSend()` function by ourselves to replace the `Send()` function and remember to not use `Send()` afterward. ```csharp //(DP) dp001.cs // Private Messages void SecurePrivacySend(string msg, uint poid) { ag.PrivacySend(so.EncryptString(msg), poid); } // Scene Messages void SecureSeceneSend(CloudScene scene, string msg) { scene.Send(so.EncryptString(msg)); } ``` Please be aware that because DP already added the function to encrypt and decrypt when sending and receiving messages, the program in the Client has to be equipped with the ability to encrypt and decrypt as well. Also, the KEY and the IV value have to be exactly the same as the ones DP used. ### 7-2 Log Developers can organize different logs according to their needs to store important data of the system’s operation. Basically, logs can be roughly separated into **program logs**, which are for default tracking, and **game logs**, which are about the game’s operation. * PC execution file The PC execution file’s program can write the record directly into the disk file of the Client. Since the PC disk file has a large capacity, it is less likely to full it. Therefore, there are fewer limitations to recording in practice. * Web Based The Web Based program is limited by the browser’s safety setup, and normally cannot be written into the disk file directly. Therefore, it cannot record the files in practice. * App The App program can write the system record into the memory card of a phone (or laptop), but the memory card’s capacity is limited. In order to prevent having insufficient space in user's memory card after installing the App program we develop, we should avoid writing the system record into the phone’s memory card, except when the program is still in its developing stage. * DP The DP program functions in an independent mainframe or a VM, so it does not encounter the disk capacity problem like the PC execution file. Therefore, it can be used to record any kind of information to meet all kinds of needs. **Example of Log’s Program** The following DPLog.cs example can be used in the DP program : ```csharp using System; using System.IO; sealed internal class DPLogPath { static DPLogPath _context; internal string LogBasePath = null; static public DPLogPath instance { get { lock(typeof(DPLogPath)) { if (_context == null) { _context = new DPLogPath(); } } return _context; } } } public class DBLog { public static void WriteLog(string subdir, int tag, string log) { string LogFileName; try { if (DPLogPath.instance.LogBasePath == null) return; if (subdir == null || subdir == "") { LogFileName = DPLogPath.instance.LogBasePath + "\\"; } else { LogFileName = DPLogPath.instance.LogBasePath + "\\" + subdir + "\\"; } LogFileName += DateTime.Now.Year.ToString("D4") + DateTime.Now.Month.ToString("D2") + DateTime.Now.Day.ToString("D2") + ".log"; string LogData = DateTime.Now.Year.ToString("D4") + "-" + DateTime.Now.Month.ToString("D2") + "-" + DateTime.Now.Day.ToString("D2") + " " + DateTime.Now.Hour.ToString("D2") + ":" + DateTime.Now.Minute.ToString("D2") + ":" + DateTime.Now.Second.ToString("D2") + "," + "[" + tag.ToString("D8") + "]" + log; lock(DPLogPath.instance) { using(StreamWriter sw = File.AppendText(LogFileName)) { sw.WriteLine(LogData); sw.Flush(); sw.Close(); } } } catch (Exception e) {} } } ``` At the beginning of the processing the program, one needs to designate the record file’s route for only once. If the record file is located in the `C:\DP-Log` folder, the program code is: `DPLogPath.instance.LogBasePath = "c:\\DP-Log";` After the route is designated, a record has to be written in, and the program code is: `DBLog.WriteLog(null,theTag,"System Startup!!");` The string "`System Startup!!`" Is the record data we want to write in. Also, since the parameter `subdir` is null, the record file is in `C:\DP-Log` and it is named by the date `(YYYYMMDD.Log)`. `YYYY` is the current year, `MM` is the month, and `DD` is the date. ### 7-3 DP Co-location Since DP is a program that functions independently in the far edge, Cloud has not provided VM’s service yet. Developers are suggested to rent it from the cloud VM providers, but do not use a subscriber line as the circuit connecting DP and Cloud in any situation. This is due to the fact that normal subscriber lines, such as ADSL, FTTB, and more, use a shared bandwidth, so they are affected by other users very easily, which leads to a decrease in DP’s quality and efficacy. In the future, when Cloud’s VM service is set ready, developers are suggested to function DP on Cloud’s VM and connect it to the Cloud’s mainframes directly to increase the efficacy and stability. Currently, there are many providers of VM service. Please select the ones with a stable bandwidth, a quality IDC, and a good reputation, such as Microsoft Azure and Amazon’s AWS service. ### 7-4 Applying New Player Account In the iOS system, there is an unspoken rule in launching an app : When the app requires a third-party log-in account and one needs to go to a Web page to apply for the account, the Web website has to be a simple member system. If the website involves payment services, the app will not be launched. To avoid this problem, the app has to come with its own ==“apply for player’s account”== function. Developers can organize a form for applying for an account in the app, and the following fields should be included: * the log-in account * the password * verifying the password * the email Then, call the ` CloudSystem.ApplyNewUser()` function. Below is its definition: static void ApplyNewUser(string gguid, byte[] gamecode, string userid, string passwd, string email, OnCallCompletion cb, object token) (1) `gguid` is the game’s GGUID. (2) `gamecode` is the game’s verification code. (3) `userid` is the applied player’s account. Please be aware that the account has to be within 3-10 characters, and only alphabets, numbers, “-,” and “.” are acceptable. (4) `passwd` is the password for the account, which should be within 6-18 characters. (5) `email` is the account’s email, which is for verifying the player’s identity. `cb` is the callback function after finishing the API. Below is its definition: void cb(int code, object token) `token` is an object that delivers dependent data with the callback function. Also, if one wants to decrease players’ difficulty logging in to the game or even skip applying for an account, one can use the other Overloading of the `CloudSystem.ApplyNewUser()` function. This function does not require application data, such as applican's account, password, email, and more. Instead, it makes the system allot an account and send back the account’s name and password. This account can also be called a player experience account. static void ApplyNewUser(string gguid, byte[] gamecode, OnCallCompletion cb, object token) After sending back the experience account and its password, the game program has to be stored in the local storage. The player’s data in the game, such as the data stored in CloudItem and CloudScore, can also be taken out for usage the next time the player logs in. Since an experience account cannot obtain the account and password for logging into the website on the official website, it has to be upgraded into a formal account. To upgrade to a formal account, one can call `CloudSystem.UpgradeUser()` and below is its definition: static void UpgradeUser(CloudGame game, string userid,string passwd,string email, OnCallCompletion cb, object token)