Introduction

This page gives you a brief introduction to the flow integrating and keypoints of using SGUtil For Unity. Please read this document carefully to avoid mistakes and detours.

Develop Environment

1. Download SGUtil For Unity Package


2. Import SGUtil For Unity Package

Select "Assets" menu → "Import Package" → "Custom Package", pick up the SGUtil For Unity package you downloaded.


Unity will show list of package files.


Click "Import" button to begin importing.

SGUtil For Unity Package Content

1. Directory Structure

The directoy structure of SGUtil For Unity package is as follows.

Plugins\Android\AndroidManifest.xml
	Android project manifest file
Plugins\Android\bin\SGUtil_Context.jar
	Interface class between SGUtil (java) and Unity.
Plugins\Android\libs\SGUtil.aar
	SGUtil software package
Plugins\Android\libs\SGUtil_GameServer.jar
	Implementation of demo game server
Plugins\Android\res\drawable-hdpi\sg_logo.png
	SG Studios logo
Plugins\Android\res\drawable-ldpi\sg_logo.png
	SG Studios logo
Plugins\Android\res\drawable-mdpi\sg_logo.png
	SG Studios logo
Plugins\Android\res\drawable-xhdpi\sg_logo.png
	SG Studios logo
Plugins\Android\res\drawable-xxhdpi\sg_logo.png
	SG Studios logo
Plugins\Android\res\drawable-xxxhdpi\sg_logo.png
	SG Studios logo
Plugins\Android\res\values\strings.xml
	Text resources
Plugins\MiniJSON.cs
	Simple JSON parser

Scripts\SDK\SGU.cs
	Interface APIs to call SGUtil functions
Scripts\SDK\SGUCallback.cs
	Callbacks to be called by SGUtil_Context. Convert JSON data to classes and dispatch to callback implementation (entity).
Scripts\SDK\SGUCallbackEntity.cs
	Callback interface definition
Scripts\SDK\SGUInterface.cs
	Abstract class to define interface APIs to call SGUtil functions
Scripts\SDK\SGUInterfaceAndroid.cs
	Interface API implementation for Android
Scripts\SDK\SGUInterfaceDefault.cs
	Default interface API implementation (not implemented)
Scripts\SDK\SGUInterfaceIOS.cs
	Interface API implementation for iOS (not implemented)
Scripts\SG\SGUCallbackEntityObject.cs
	Callback interface implementation demo

Using SGUtil For Unity

1. Customize AndroidManifest.xml

You may change AndroidManifest.xml except the blue part shown below.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sg.util.unitydemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="18"
        android:maxSdkVersion="25"
        android:targetSdkVersion="23" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/sg_logo"
        android:label="@string/sgutil_unitydemo"
        android:name="com.sg.util.SGApplication">
        
        <activity
            android:name="com.sg.util.SGUtilUnityActivity"
            android:label="@string/sgutil_unitydemo" 
            android:screenOrientation="landscape"
            android:launchMode="singleTask"
            android:configChanges="orientation|keyboardHidden|screenSize">
            
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>            
            
        </activity>
    </application>

</manifest>

Please change/extend SGUtilUnityActivity.java by yourselves if you need to request critical permission dynamically and re-generate SGUtil_Context.jar.

2. Keypoints of Using SGUtil For Unity

We above metioned SGU.cs provides interface APIs to encapsulate SGUtil functions. Calling these APIs results in asynchronous notifcations to be delivered from SGUtil. Therefore there must be an object to receive and handle those notifications. We create a SGUCallbackEntity intance object and pass it as a parameter to SGU.Init. This object must get run at the beginning of application run and be there all the time.

We take SGUCallbackEntityObject as an example to give a little explanation.

public class SGUCallbackEntityObject : MonoBehaviour, SGUCallbackEntity {

    void Start () {
        SGU.initialize(this);	// SGU class initialization
        // call Init interface
        SGU.Init("gameServerClass=com.sg.util.SGUtilUnityGameServer&game=Peep&location=beijing&widget=hidden", false);
    }

Besides MonoBahaviour, SGUCallbackEntityObject extends SGUCallbackEntity interface,implemting methods defined by SGUCallbackEntity. It calls SGU.initialize to initialize SGU at the very beginning, and then calls SGU.Init to initialize SGUtil For Unity.

The parameter of SGU.initialize is a SGUCallbackEntity instance, hence we pass 'this' as argument. Most callbacks has the first parameter of type SGUResult, which is a enumeration defined as below. Please refer to the comment for their meanings.

public enum SGUResult
{
    RESULT_OK,          // operation succeeded
    RESULT_NO_NEED,     // operation already done before
    RESULT_FAIL,        // operation failed
    RESULT_CANCELLED,   // operation cancelled
    RESULT_ASYNC,       // operation is aynchronous, there might be other callbacks later
    RESULT_ITEM_BEGIN,  // beginning of data items
    RESULT_ITEM,        // data item
    RESULT_ITEM_END,    // end of data items
	RESULT_FUNC_REQUEST,// function request sent to application from low level (screenshot, logout, etc.)
    RESULT_ALL
}
        

As all API calls are performed asynchronously, one API call may result in multiple callback notifications. If the first notification is RESULT_ASYNC, there will be more notifications for operation result and data items. All data items are notified with RESULT_ITEM. There may be a RESULT_ITEM_BEGIN before, and a RESULT_ITEM_END after data items if a API returns multiple items.

The first parameter of SGU.Init must contain game server class path specified by "gameServerClass", and location set to "beijing" at present. The other parameters may be specified according to implementation of game server class. Here we only show a string for demo. For details of game server interface, please refer to SGGameServerInterface . The second parameter of SGU.Init specifies whether SGUtil should automatically check order state and let game server mark it as delivered after user completes UI operation to buy a product. If set it to false, you need to manually do the job by calling GetOrderState later. Other function cannot be executed during the automatic checking period. If you think it a problem, please set it to false and call GetOrderState to check order state by yourself.

Below is SGUCallbackEntityObject.cs in SGUtil For Unity package, wchich is a dummy implementation of SGUCallbackEntity. It is shown here for your reference only.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;

public class SGUCallbackEntityObject : MonoBehaviour, SGUCallbackEntity {

    void Start () {
        SGU.initialize(this);	// SGU class initialization
        // call Init interface
        SGU.Init("gameServerClass=com.sg.util.SGUtilUnityGameServer&location=beijing", false);
    }

    void Update()
    {
    }

    public void OnDebugInfo(string info)
    {
        // show debug info 'info' somewhere
    }

    public void OnState(SGUState state)
    {
        // use the state information up to your pleasure
    }

    public void OnOrderState(SGUResult resultCode, SGUOrderState order_state)
    {
        // a series of notification caused by call to Buy and GetOrderState.
    }

    public void OnInit(SGUResult resultCode)
    {
        // result of initialization, proceed if resultCode == RESULT_OK, otherwise, have to exit
    }

    public void OnLogin(SGUResult resultCode, SGUAccount account)
    {
        // result of login
    }

    public void OnLogout(SGUResult resultCode)
    {
        // result of logout
    }

    public void OnPlayerDetails(SGUResult resultCode, SGUPlayerDetails details)
    {
        // result of getting player details
    }

    public void OnProduct(SGUResult resultCode, SGUProduct product)
    {
        // result of call to GetProductList and product info notification if any.
    }

    public void OnPay(SGUResult resultCode)
    {
        // result of payment operation (Buy)
    }

    public void OnTreasureList(SGUResult resultCode, SGUTreasure treasure)
    {
        // result of GetTreasureList
    }

    public void OnRemoteLog(SGUResult resultCode)
    {
        // result of Log
    }

    public void OnRemoteLogValue(SGUResult resultCode, string key, int value, string msg)
    {
        // result of GetLogValue
    }

    public void OnStartGame(SGUResult resultCode)
    {
        // result of StartGame
    }

    public void OnStopGame(SGUResult resultCode)
    {
        // result of StopGame
    }

    public void OnExtMethod(SGUResult resultCode, SGUExtMethodResult result)
    {
        // result of RunExtMethod
    }

    public void OnExitCancelled()
    {
        // result of Exit
    }

    public void OnShareScreen(SGUResult resultCode, string path)
    {
        // result of ShareScreen
    }

    public void OnFuncRequest(SGUResult resultCode, string func, string id, string arg)
    {
        // function request initiated from lower layer
    }

}

Repackaging

After integrating SGUtil For Android, you may let Unity to generate an APK file and run it on Android devices. However, this APK only contains an pre-embedded demo channel, which simply completes all functions with dummy data. In order to bind a real channel, please upload your APK to SG Studios website and download the final APK after repackaging is finished.

Customization

The source files of interfaces between SGUtil and Unity are listed below. You may make proper changes if you need.

com.sg.util.SGUtilUnityActivity.java
package com.sg.util;

import org.json.JSONArray;
import org.json.JSONObject;

import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.KeyEvent;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class SGUtilUnityActivity extends UnityPlayerActivity implements SGAgent.SGClient {

    public final static String CALLBACK_GAMEOBJECT_NAME     = "(sgutil_callback)";

    public final static String CALLBACK_STATE               = "OnState";
    public final static String CALLBACK_INIT                = "OnInit";
    public final static String CALLBACK_LOGIN               = "OnLogin";
    public final static String CALLBACK_LOGOUT              = "OnLogout";
    public final static String CALLBACK_PLAYERDETAILS       = "OnPlayerDetails";
    public final static String CALLBACK_PRODUCT             = "OnProduct";
    public final static String CALLBACK_PAY                 = "OnPay";
    public final static String CALLBACK_ORDERINFO           = "OnOrderInfo";
    public final static String CALLBACK_ORDERSTATE          = "OnOrderState";
    public final static String CALLBACK_DEBUGINFO           = "OnDebugInfo";
    public final static String CALLBACK_TREASURE_LIST       = "OnTreasureList";
    public final static String CALLBACK_TREASURE            = "OnTreasure";
    public final static String CALLBACK_REMOTE_LOG          = "OnRemoteLog";
    public final static String CALLBACK_REMOTE_LOG_VALUE    = "OnRemoteLogValue";
    public final static String CALLBACK_START_GAME          = "OnStartGame";
    public final static String CALLBACK_STOP_GAME           = "OnStopGame";
    public final static String CALLBACK_EXTMETHOD           = "OnExtMethod";
    public final static String CALLBACK_EXIT_CANCELLED      = "OnExitCancelled";
    public final static String CALLBACK_SHARESCREEN         = "OnShareScreen";
    public final static String CALLBACK_FUNC_REQUEST        = "OnFuncRequest";

    public final static int RESULT_OK                       = 0;
    public final static int RESULT_NO_NEED                  = 1;
    public final static int RESULT_FAIL                     = 2;
    public final static int RESULT_CANCELLED                = 3;
    public final static int RESULT_ASYNC                    = 4;
    public final static int RESULT_ITEM_BEGIN               = 5;
    public final static int RESULT_ITEM                     = 6;
    public final static int RESULT_ITEM_END                 = 7;
    public final static int RESULT_FUNC_REQUEST             = 8;

    private SGAgent agent;
    private String channelName;
    private String startupParamString;
    private boolean autoDeliverProduct;

    private boolean initCalled;
    private boolean initOK;
    private boolean agentInitialized;
    private int agentInitializationResult;
    private boolean initialized;

    private class CallbackData {
        String name;
        String json;
    }

    private class CallbackHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                CallbackData data = (CallbackData)msg.obj;
                UnityPlayer.UnitySendMessage(CALLBACK_GAMEOBJECT_NAME, data.name, data.json);
                data = null;
            }
        }
    }

    private CallbackHandler callbackHandler;

    private void postCallback(String name, String json) {
        CallbackData data = new CallbackData();
        data.name = name;
        data.json = json;
        Message msg = callbackHandler.obtainMessage();
        msg.what = 1;
        msg.obj = data;
        callbackHandler.sendMessage(msg);
    }

    private void initUnityLib() {
        if (agentInitialized && initCalled) {
            if (!initialized) {
                if (channelName.equals("SG")) {
                    Map<String, String> spmap = SGMiscUtil.convertQueryToMap(startupParamString, "&");
                    String ws = spmap.get("widget");
                    if ("hidden".equals(ws))
                        agent.runExtMethod(null, "showWidget false");
                }
                initialized = agent.setStartupParams(startupParamString);
                if (initialized) {
                    if (!autoDeliverProduct) {
                        agent.setOrderStateCheckingIntervals(null);
                    }
                    if (agentInitializationResult == SGAgent.STATE_CHANGE_REASON_OP_SUCCESS) {
                        initOK = true;
                        sendCallback(CALLBACK_INIT, RESULT_OK);
                    }
                }
                if (!initOK) {
                    sendCallback(CALLBACK_INIT, RESULT_FAIL);
                }
            }
        }
    }

    public void setSystemProperty(String name, String value) {
        System.setProperty(name, value);
    }

    public String getChannelName() {
        return channelName;
    }

    private HashMap<String, String> parseParamString(String paramString) {
        HashMap<String, String> params = new HashMap<String, String>();
        String ps[] = paramString.split("&");
        for (String p : ps) {
            p = p.trim();
            if (!p.isEmpty()) {
                int i = p.indexOf('=');
                if (i >= 0) {
                    params.put(p.substring(0, i), p.substring(i + 1));
                } else {
                    params.put(p, null);
                }
            }
        }
        return params;
    }

    // called by SGUInterfaceAndroid.Init
    public void init(String paramString, boolean autoDeliverProduct) {
        if (!initCalled) {
            initCalled = true;
            this.startupParamString = paramString;
            this.autoDeliverProduct = autoDeliverProduct;
            sendCallback(CALLBACK_INIT, RESULT_ASYNC);
            initUnityLib();
        } else {
            sendCallback(CALLBACK_INIT, RESULT_NO_NEED);
        }
    }

    // called by SGUInterfaceAndroid.Login
    public void login() {
        if (initOK && agent.login()) {
            sendCallback(CALLBACK_LOGIN, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_LOGIN, RESULT_FAIL);
        }
    }

    // called by SGUInterfaceAndroid.Recover
    public void recover(String recoverToken) {
        if (initOK && agent.recover(recoverToken)) {
            sendCallback(CALLBACK_LOGIN, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_LOGIN, RESULT_FAIL);
        }
    }

    // called by SGUInterfaceAndroid.Logout
    public void logout() {
        if (agent.logout()) {
            sendCallback(CALLBACK_LOGOUT, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_LOGOUT, RESULT_FAIL);
        }
    }

    // called by SGUInterfaceAndroid.Exit
    public void exit(boolean silent) {
        if (silent) {
            finish();
        } else {
            agent.exit();
        }
    }

    // called by SGUInterfaceAndroid.ShareScreen
    public void shareScreen(String imageFile, String title, String content) {
        if (agent.shareScreen(imageFile, title, content)) {
            sendCallback(CALLBACK_SHARESCREEN, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_SHARESCREEN, RESULT_FAIL);
        }
    }

    // called by SGUInterfaceAndroid.GetPlayerDetails
    public void getPlayerDetails() {
        if (agent.getPlayerDetails()) {
            sendCallback(CALLBACK_PLAYERDETAILS, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_PLAYERDETAILS, RESULT_FAIL);
        }
    }

    // called by SGUInterfaceAndroid.GetProductList
    public void getProductList(String filter) {
        if (agent.getProducts(filter, true)) {
            sendCallback(CALLBACK_PRODUCT, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_PRODUCT, RESULT_FAIL);
        }
    }

    // called by SGUInterfaceAndroid.Buy
    public void buyProduct(String productID, int num) {
        if (agent.buyProduct(productID, num, "")) {
            sendCallback(CALLBACK_PAY, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_PAY, RESULT_FAIL);
        }
    }

    // called by SGUInterfaceAndroid.GetOrderState
    public void getOrderState(String orderID) {
        if (agent.getOrderState(orderID)) {
            sendCallback(CALLBACK_ORDERSTATE, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_ORDERSTATE, RESULT_FAIL);
        }
    }

    // called by SGUInterfaceAndroid.GetOrderInfoList
    public void getOrderInfoList(String filter) {
        if (agent.getOrderInfoList(filter)) {
            sendCallback(CALLBACK_ORDERINFO, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_ORDERINFO, RESULT_FAIL);
        }
    }

    public void getTreasureList(String filter) {
        if (agent.getTreasureList(filter)) {
            sendCallback(CALLBACK_TREASURE_LIST, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_TREASURE_LIST, RESULT_FAIL);
        }
    }

    public void incTreasure(String name, int value) {
        if (agent.incTreasure(name, value)) {
            sendCallback(CALLBACK_TREASURE, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_TREASURE, RESULT_FAIL);
        }
    }

    public void log(int action, String key, int value, String message) {
        if (key != null && key.trim().isEmpty())
            key = null;
        if (message != null && message.trim().isEmpty())
            message = null;
        if (agent.log((char)action, key, value, message)) {
            sendCallback(CALLBACK_REMOTE_LOG, RESULT_OK);
        } else {
            sendCallback(CALLBACK_REMOTE_LOG, RESULT_FAIL);
        }
    }

    public void getLogValue(String key, int defaultValue) {
        if (agent.getLogValue(key, defaultValue)) {
            sendCallback(CALLBACK_REMOTE_LOG_VALUE, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_REMOTE_LOG_VALUE, RESULT_FAIL);
        }
    }

    public void startGame() {
        if (agent.startGame()) {
            sendCallback(CALLBACK_START_GAME, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_START_GAME, RESULT_FAIL);
        }
    }

    public void stopGame() {
        if (agent.stopGame()) {
            sendCallback(CALLBACK_STOP_GAME, RESULT_ASYNC);
        } else {
            sendCallback(CALLBACK_STOP_GAME, RESULT_FAIL);
        }
    }

    public String getExtMethodList() {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectInt(jo, "Code", RESULT_OK);
        JSONObject jdata = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectObject(jo, "Data", jdata);
        JSONArray jarr = SGJsonUtil.createJSONArray(jdata, "Methods");
        String s = agent.runExtMethod(null, "?");
        if (s != null && s.startsWith("*s")) {
            String[] ms = s.substring(2).split("\n");
            for (String m : ms) {
                int p = m.lastIndexOf('=');
                String prototype = m.substring(0, p);
                String name = m.substring(m.indexOf(' ') + 1, m.indexOf('('));
                int prop = Integer.parseInt(m.substring(p + 1));
                JSONObject jom = SGJsonUtil.createJSONObject();
                SGJsonUtil.putJSONObjectString(jom, "Name", name);
                SGJsonUtil.putJSONObjectString(jom, "Prototype", prototype);
                SGJsonUtil.putJSONObjectInt(jom, "Prop", prop);
                SGJsonUtil.putJSONArrayObject(jarr, jom);
            }
        }
        return SGJsonUtil.stringifyJSONObject(jo);
    }

    public String runExtMethod(String cookie, String name, String cmdLineArgs) {
        StringBuilder sb = new StringBuilder();
        sb.append(name);
        if (!TextUtils.isEmpty(cmdLineArgs)) {
            sb.append(' ');
            sb.append(cmdLineArgs);
        }
        String s = agent.runExtMethod(cookie, sb.toString());
        String p = s.substring(0, 2);
        s = s.substring(2);
        JSONObject jo = SGJsonUtil.createJSONObject();
        JSONObject jdata = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectString(jdata, "Cookie", cookie == null || cookie.isEmpty() ? name : cookie);
        if (p.startsWith("!")) {
            SGJsonUtil.putJSONObjectInt(jo, "Code", RESULT_FAIL);
        } else {
            if (p.endsWith("v")) {
                SGJsonUtil.putJSONObjectString(jdata, "Type", "Void");
            } else if (p.endsWith("n")) {
                SGJsonUtil.putJSONObjectString(jdata, "Type", "Null");
            } else {
                SGJsonUtil.putJSONObjectString(jdata, "Type", "Text");
                SGJsonUtil.putJSONObjectString(jdata, "Text", s);
            }
            if (p.startsWith("@"))
                SGJsonUtil.putJSONObjectInt(jo, "Code", RESULT_ASYNC);
            else
                SGJsonUtil.putJSONObjectInt(jo, "Code", RESULT_OK);
        }
        SGJsonUtil.putJSONObjectObject(jo, "Data", jdata);
        return SGJsonUtil.stringifyJSONObject(jo);
    }

    public void notifyFuncRequestResult(String func, String id, String result) {
        agent.notifyFuncRequestResult(func, id, result);
    }

    // send callback to SGUCallback
    private void sendCallback(String callbackName, int code, JSONObject dataObj) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectInt(jo, "Code", code);
        if (dataObj == null)
            dataObj = new JSONObject();
        SGJsonUtil.putJSONObjectObject(jo, "Data", dataObj);
        postCallback(callbackName, SGJsonUtil.stringifyJSONObject(jo));
    }

    private void sendCallback(String callbackName, int code) {
        sendCallback(callbackName, code, null);
    }

    private JSONObject makeProductJSON(UProduct product) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectString(jo, "ProductID", product.getProductID());
        SGJsonUtil.putJSONObjectString(jo, "ProductName", product.getProductName());
        SGJsonUtil.putJSONObjectString(jo, "ProductDesc", product.getProductDesc());
        SGJsonUtil.putJSONObjectInt(jo, "Price", product.getPrice());
        SGJsonUtil.putJSONObjectInt(jo, "Coins", product.getCoins());
        SGJsonUtil.putJSONObjectInt(jo, "AppID", product.getAppID());
        SGJsonUtil.putJSONObjectString(jo, "AppName", product.getAppName());
        SGJsonUtil.putJSONObjectString(jo, "ServerID", product.getServerID());
        SGJsonUtil.putJSONObjectString(jo, "ServerName", product.getServerName());
        SGJsonUtil.putJSONObjectString(jo, "RoleID", product.getRoleID());
        SGJsonUtil.putJSONObjectString(jo, "RoleName", product.getRoleName());
        SGJsonUtil.putJSONObjectInt(jo, "RoleLevel", product.getRoleLevel());
        SGJsonUtil.putJSONObjectString(jo, "ChannelProductID", product.getChannelProductID());
        SGJsonUtil.putJSONObjectString(jo, "ChannelProductName", product.getChannelProductName());
        SGJsonUtil.putJSONObjectString(jo, "ChannelProductJSON", product.getChannelProductJSON());
        return jo;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        callbackHandler = new CallbackHandler();
        agent = new SGAgent();
        agent.onCreate(this, this);
        channelName = agent.getChannelName();
    }

    @Override
    protected void onStart() {
        super.onStart();
        agent.onStart();
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        agent.onRestart();
    }

    @Override
    protected void onResume() {
        super.onResume();
        agent.onResume();
    }

    @Override
    protected void onPause() {
        agent.onPause();
        super.onPause();
    }

    @Override
    protected void onStop() {
        agent.onStop();
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        agent.onDestroy();
        super.onDestroy();
        System.exit(0);
    }

    @Override
    public void onNewIntent(Intent newIntent) {
        agent.onNewIntent(newIntent);
        super.onNewIntent(newIntent);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (agent.onKeyDown(keyCode, event))
            return true;
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        agent.onActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }

    @TargetApi(23)
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        agent.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    public void onLog(String text) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectString(jo, "Message", text);
        sendCallback(CALLBACK_DEBUGINFO, RESULT_ITEM, jo);
    }

    @Override
    public void onLog(String text, String postfix) {
        onLog(text + (postfix != null ? postfix : ""));
    }

    @Override
    public void onPlayerDetails(String details) {
        sendCallback(CALLBACK_PLAYERDETAILS, RESULT_ITEM, SGJsonUtil.createJSONObject(details));
    }

    @Override
    public boolean onProductBegin(int num) {
        JSONObject data = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectInt(data, "Count", num);
        sendCallback(CALLBACK_PRODUCT, RESULT_ITEM_BEGIN, data);
        // request for next product
        agent.wantProduct();
        return false;
    }

    @Override
    public void onProductFound(UProduct product) {
        if (product != null) {
            sendCallback(CALLBACK_PRODUCT, RESULT_ITEM, makeProductJSON(product));
            // request for next product
            agent.wantProduct();
        }
    }

    @Override
    public void onProductEnd() {
        sendCallback(CALLBACK_PRODUCT, RESULT_ITEM_END);
    }

    @Override
    public boolean onOrderInfoBegin(int num) {
        JSONObject data = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectInt(data, "Count", num);
        sendCallback(CALLBACK_ORDERINFO, RESULT_ITEM_BEGIN, data);
        // request for next order info
        agent.wantOrderInfo();
        return false;
    }

    @Override
    public void onOrderInfoFound(UOrder order) {
        if (order != null && order.getProduct() != null) {
            JSONObject data = SGJsonUtil.createJSONObject();
            SGJsonUtil.putJSONObjectString(data, "ProductID", order.getProduct().getProductID());
            SGJsonUtil.putJSONObjectInt(data, "Num", order.getPayParams().getBuyNum());
            SGJsonUtil.putJSONObjectString(data, "OrderID", order.getOrderTicket().getOrderID());
            SGJsonUtil.putJSONObjectString(data, "State", order.getState());
            sendCallback(CALLBACK_ORDERINFO, RESULT_ITEM, data);
            // request for next order info
            agent.wantOrderInfo();
        }
    }

    @Override
    public void onOrderInfoEnd() {
        sendCallback(CALLBACK_ORDERINFO, RESULT_ITEM_END);
    }

    @Override
    public void onTreasureListFound(String[] treasures) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectInt(jo, "Count", treasures.length);
        sendCallback(CALLBACK_TREASURE_LIST, RESULT_ITEM_BEGIN, jo);
        for (String t : treasures) {
            String[] kv = t.split("=");
            if (kv.length == 2) {
                jo = SGJsonUtil.createJSONObject();
                SGJsonUtil.putJSONObjectString(jo, "Name", kv[0]);
                SGJsonUtil.putJSONObjectInt(jo, "Value", Integer.parseInt(kv[1]));
                sendCallback(CALLBACK_TREASURE_LIST, RESULT_ITEM, jo);
            }
        }
        sendCallback(CALLBACK_TREASURE_LIST, RESULT_ITEM_END);
    }

    @Override
    public void onTreasureChange(String name, int value) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectString(jo, "Name", name);
        SGJsonUtil.putJSONObjectInt(jo, "Value", value);
        sendCallback(CALLBACK_TREASURE, RESULT_ITEM, jo);
    }

    @Override
    public void onLogValue(String key, int value, String msg) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectString(jo, "Key", key);
        SGJsonUtil.putJSONObjectInt(jo, "Value", value);
        SGJsonUtil.putJSONObjectString(jo, "Msg", msg);
        sendCallback(CALLBACK_REMOTE_LOG_VALUE, RESULT_ITEM, jo);
    }

    @Override
    public void onExtMethodNotification(String cookie, int code, String msg) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectString(jo, "Cookie", cookie);
        SGJsonUtil.putJSONObjectInt(jo, "Code", code);
        if (msg != null)
            SGJsonUtil.putJSONObjectString(jo, "Message", msg);
        sendCallback(CALLBACK_EXTMETHOD, RESULT_ITEM, jo);
    }

    @Override
    public void onOrderStateChange(String productID, int num, String orderID, String state, String message) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectString(jo, "ProductID", productID);
        SGJsonUtil.putJSONObjectInt(jo, "Num", num);
        SGJsonUtil.putJSONObjectString(jo, "OrderID", orderID);
        SGJsonUtil.putJSONObjectString(jo, "State", state);
        SGJsonUtil.putJSONObjectString(jo, "Message", message);
        sendCallback(CALLBACK_ORDERSTATE, RESULT_ITEM, jo);
    }

    @Override
    public void onStateChange(int type, int orgState, int newState, int reason, int op, String arg) {
        onLog("State change: " + agent.getStateName(type, orgState) + " -> " + agent.getStateName(type, newState) + " # " + agent.getStateChangeReasonName(reason) + "\n");
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectInt(jo, "Type", type);
        SGJsonUtil.putJSONObjectString(jo, "TypeName", agent.getStateTypeName(type));
        SGJsonUtil.putJSONObjectInt(jo, "OrgState", orgState);
        SGJsonUtil.putJSONObjectString(jo, "OrgStateName", agent.getStateName(type, orgState));
        SGJsonUtil.putJSONObjectInt(jo, "NewState", newState);
        SGJsonUtil.putJSONObjectString(jo, "NewStateName", agent.getStateName(type, newState));
        SGJsonUtil.putJSONObjectInt(jo, "Reason", reason);
        SGJsonUtil.putJSONObjectString(jo, "ReasonName", agent.getStateChangeReasonName(reason));
        SGJsonUtil.putJSONObjectInt(jo, "Op", op);
        SGJsonUtil.putJSONObjectString(jo, "OpName", agent.getOpName(op));
        sendCallback(CALLBACK_STATE, RESULT_ITEM, jo);
        switch (op) {
            case SGAgent.OP_INIT:
                if (newState == SGAgent.USRSTATE_INITIALIZED) {
                    agentInitialized = true;
                    agentInitializationResult = reason;
                    initUnityLib();
                } else if (newState == SGAgent.USRSTATE_IDLE) {
                    if (initCalled)
                        sendCallback(CALLBACK_INIT, RESULT_FAIL);
                } else /*if (newState == SGAgent.USRSTATE_INITIALIZING)*/ {
                }
                break;
            case SGAgent.OP_LOGIN:
                if (newState == SGAgent.USRSTATE_INITIALIZED && reason == SGAgent.STATE_CHANGE_REASON_OP_CANCELED) {
                    sendCallback(CALLBACK_LOGIN, RESULT_CANCELLED);
                    break;
                }
            case SGAgent.OP_AUTH:
                if (newState == SGAgent.USRSTATE_INITIALIZED) {
                    sendCallback(CALLBACK_LOGIN, RESULT_FAIL);
                }
                break;
            case SGAgent.OP_LOGINGAME:
                if (newState == SGAgent.USRSTATE_INITIALIZED) {
                    sendCallback(CALLBACK_LOGIN, RESULT_FAIL);
                } else if (newState == SGAgent.USRSTATE_LOGINEDGAME) {
                    String recoverToken = agent.getSession().getRecoverToken();
                    String username = agent.getSession().getUsername();
                    String openID;
                    String phone;
                    if (channelName.equals("SG")) {
                        Map<String, String> loginExtMap = SGMiscUtil.convertQueryToMap(agent.getSession().getLoginExtension(), ",");
                        openID = loginExtMap.get("uid");
                    } else {
                        openID = agent.getAccount().getSGOpenID();
                        recoverToken = agent.getAccount().getSGRecoverToken();
                    }
                    phone = agent.getAccount().getPhone();
                    if (openID == null)
                        openID = "";
                    if (recoverToken == null)
                        recoverToken = "";
                    if (phone == null)
                        phone = "";
                    Map<String, String> channelConfigs = agent.getChannelConfigs();
                    jo = SGJsonUtil.createJSONObject();
                    SGJsonUtil.putJSONObjectString(jo, "Version", agent.getApkVersionName());
                    SGJsonUtil.putJSONObjectString(jo, "ChannelName", channelName);
                    SGJsonUtil.putJSONObjectString(jo, "Username", username);
                    SGJsonUtil.putJSONObjectString(jo, "OpenID", openID);
                    SGJsonUtil.putJSONObjectString(jo, "RecoverToken", recoverToken);
                    SGJsonUtil.putJSONObjectString(jo, "Phone", phone);
                    Iterator it = channelConfigs.entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry<String, String> e = (Map.Entry<String, String>)it.next();
                        SGJsonUtil.putJSONObjectString(jo, "ChannelParam_" + e.getKey(), e.getValue());
                    }
                    if (!channelConfigs.containsKey("SG_GameKey")) {
                        SGJsonUtil.putJSONObjectString(jo, "ChannelParam_SG_GameKey", agent.getAccount().getSGGameKey());
                    }
                    String channelLoginExtension = agent.getSession().getLoginExtension();
                    SGJsonUtil.putJSONObjectString(jo, "ChannelLoginExtension", channelLoginExtension == null ? "" : channelLoginExtension);
                    sendCallback(CALLBACK_LOGIN, RESULT_OK, jo);
                }
                break;
            case SGAgent.OP_LOGOUTGAME:
                if (newState == SGAgent.USRSTATE_LOGINEDGAME) {
                    sendCallback(CALLBACK_LOGOUT, RESULT_FAIL);
                }
                break;
            case SGAgent.OP_LOGOUT:
                if (newState == SGAgent.USRSTATE_INITIALIZED) {
                    sendCallback(CALLBACK_LOGOUT, RESULT_OK);
                }
                break;
            case SGAgent.OP_GETORDER:
                if (newState == SGAgent.PAYSTATE_IDLE) {
                    sendCallback(CALLBACK_PAY, RESULT_FAIL);
                }
                break;
            case SGAgent.OP_PAY:
                if (newState == SGAgent.PAYSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_FAILURE) {
                        sendCallback(CALLBACK_PAY, RESULT_FAIL);
                    } else if (reason == SGAgent.STATE_CHANGE_REASON_OP_CANCELED) {
                        sendCallback(CALLBACK_PAY, RESULT_CANCELLED);
                    } else if (reason == SGAgent.STATE_CHANGE_REASON_OP_SUCCESS) {
                        sendCallback(CALLBACK_PAY, RESULT_OK);
                    }
                } else if (newState == SGAgent.PAYSTATE_PAID) {
                    sendCallback(CALLBACK_PAY, RESULT_OK);
                }
                break;
            case SGAgent.OP_GETPLAYERDETAILS:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_FAILURE) {
                        sendCallback(CALLBACK_PLAYERDETAILS, RESULT_FAIL);
                    }
                }
                break;
            case SGAgent.OP_GETPRODUCTS:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_FAILURE) {
                        sendCallback(CALLBACK_PRODUCT, RESULT_FAIL);
                    }
                }
                break;
            case SGAgent.OP_GETORDERINFOLIST:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_FAILURE) {
                        sendCallback(CALLBACK_ORDERINFO, RESULT_FAIL);
                    }
                }
                break;
            case SGAgent.OP_GETTREASURELIST:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_FAILURE) {
                        sendCallback(CALLBACK_TREASURE_LIST, RESULT_FAIL);
                    }
                }
                break;
            case SGAgent.OP_INCTREASURE:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_FAILURE) {
                        sendCallback(CALLBACK_TREASURE, RESULT_FAIL);
                    }
                }
                break;
            case SGAgent.OP_GETLOGVALUE:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_FAILURE) {
                        sendCallback(CALLBACK_REMOTE_LOG_VALUE, RESULT_FAIL);
                    }
                }
                break;
            case SGAgent.OP_STARTGAME:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_SUCCESS) {
                        sendCallback(CALLBACK_START_GAME, RESULT_OK);
                    } else {
                        sendCallback(CALLBACK_START_GAME, RESULT_FAIL);
                    }
                }
                break;
            case SGAgent.OP_STOPGAME:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_SUCCESS) {
                        sendCallback(CALLBACK_STOP_GAME, RESULT_OK);
                    } else {
                        sendCallback(CALLBACK_STOP_GAME, RESULT_FAIL);
                    }
                }
                break;
            case SGAgent.OP_SHARESCREEN:
                if (newState == SGAgent.JOBSTATE_IDLE) {
                    jo = SGJsonUtil.createJSONObject();
                    SGJsonUtil.putJSONObjectString(jo, "File", arg);
                    if (reason == SGAgent.STATE_CHANGE_REASON_OP_SUCCESS) {
                        sendCallback(CALLBACK_SHARESCREEN, RESULT_OK, jo);
                    } else {
                        sendCallback(CALLBACK_SHARESCREEN, RESULT_FAIL, jo);
                    }
                }
                break;
        }
    }

    @Override
    public void onExitCancelled() {
        sendCallback(CALLBACK_EXIT_CANCELLED, RESULT_CANCELLED);
    }

    @Override
    public void onFuncRequest(String func, String id, String arg) {
        JSONObject jo = SGJsonUtil.createJSONObject();
        SGJsonUtil.putJSONObjectString(jo, "Func", func);
        SGJsonUtil.putJSONObjectString(jo, "Id", id);
        SGJsonUtil.putJSONObjectString(jo, "Arg", arg);
        sendCallback(CALLBACK_FUNC_REQUEST, RESULT_FUNC_REQUEST, jo);
    }

}

com.sg.util.SGUtilUnityGameServer.java
package com.sg.util;

import java.util.List;
import java.util.Map;

public class SGUtilUnityGameServer implements SGGameServerInterface {

    private SGGameServerDemo mDemoServer;

    public SGUtilUnityGameServer() {
        mDemoServer = new SGGameServerDemo();
    }

    public void init(SGAgent agent, Map<String, String> params, String cookie) {
        mDemoServer.init(agent, params, cookie);
    }

    public UAccount login() {
        return mDemoServer.login();
    }

    public String getPlayerDetails() {
        return mDemoServer.getPlayerDetails();
    }

    public List<UProduct> getProductList(String filter) {
        return mDemoServer.getProductList(filter);
    }

    public UOrderTicket getOrder(UProduct product, UPayParams pp) {
        return mDemoServer.getOrder(product, pp);
    }

    public void notifyOrderAccepted(String orderID, String productID, int buyNum) {
        mDemoServer.notifyOrderAccepted(orderID, productID, buyNum);
    }

    public String getOrderState(String orderID, String productID, int buyNum) {
        return mDemoServer.getOrderState(orderID, productID, buyNum);
    }

    public String deliverOrder(String orderID, String productID, int buyNum) {
        return mDemoServer.deliverOrder(orderID, productID, buyNum);
    }

    public List<UOrder> getOrderInfoList(String filter) {
        return mDemoServer.getOrderInfoList(filter);
    }

    public String logout(UAccount account) {
        return mDemoServer.logout(account);
    }

    public String[] getTreasureList(String filter) {
        return mDemoServer.getTreasureList(filter);
    }

    public int incTreasure(String name, int count) {
        return mDemoServer.incTreasure(name, count);
    }

    public boolean startGame() {
        return mDemoServer.startGame();
    }

    public boolean stopGame() {
        return mDemoServer.stopGame();
    }

    public boolean log(char action, String key, int value, String message, boolean waitForResp) {
        return mDemoServer.log(action, key, value, message, waitForResp);
    }

    public String getLogValue(String key, int defaultValue) {
        return mDemoServer.getLogValue(key, defaultValue);
    }

}

com.sg.util.SGGameServerDemo.java
package com.sg.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class SGGameServerDemo implements SGGameServerInterface {

    private class OrderInfo {
        String id;
        String productID;
        int num;
        String state;
        long gtime;
        long ctime;
        long dtime;
    }

    private class Treasure {
        String id;
        String name;
        int num;
    }

    private class Log {
        String id;
        int value;
        String msg;
    }

    HashMap<String, UProduct> mProductList;
    HashMap<String, OrderInfo> mOrderList;
    HashMap<String, Treasure> mTreasureList;
    HashMap<String, Log> mLogList;
    boolean initialized;

    private String createID() {
        Random r = new Random();
        byte[] bs = new byte[8];
        for (int i = 0; i < 8; i++) {
            int n = r.nextInt(16);
            if (n < 10)
                bs[i] = (byte) ('0' + n);
            else
                bs[i] = (byte) ('a' + n);
        }
        return new String(bs);
    }

    private String createProductID() {
        String id;
        do {
            id = createID();
        } while (mProductList.get(id) != null);
        return id;
    }

    private String createOrderID() {
        String id;
        do {
            id = createID();
        } while (mOrderList.get(id) != null);
        return id;
    }

    private String createTreasureID() {
        String id;
        do {
            id = createID();
        } while (mTreasureList.get(id) != null);
        return id;
    }

    private void addProduct(String name, int price, int coins) {
        UProduct p = new UProduct();
        p.setProductID(createProductID());
        p.setProductName(name);
        p.setProductDesc("");
        p.setRoleID("");
        p.setRoleName("");
        p.setRoleLevel(0);
        p.setServerID("");
        p.setServerName("");
        p.setPrice(price);
        p.setCoins(coins);
        p.setAppID(4);
        p.setAppName("SGUtil");
        mProductList.put(p.getProductID(), p);
    }

    private void addTreasure(String name, int num) {
        Treasure t = new Treasure();
        t.id = createTreasureID();
        t.name = name;
        t.num = num;
        mTreasureList.put(t.id, t);
    }

    public void init(SGAgent agent, Map<String, String> params, String cookie) {
        if (!initialized) {
            initialized = true;
            mProductList = new HashMap<String, UProduct>(8);
            mOrderList = new HashMap<String, OrderInfo>();
            mTreasureList = new HashMap<String, Treasure>(8);
            mLogList = new HashMap<String, Log>(64);
            addProduct("English 900", 1, 100);
            addProduct("新概念1", 10, 1000);
            addProduct("新概念2", 20, 2000);
            addProduct("新概念3", 30, 3000);
            addProduct("新概念4", 40, 4000);
            addProduct("新概念5", 50, 5000);
            addProduct("新概念6", 60, 6000);
            addProduct("新概念全集", 150, 21000);
            addTreasure("GoldMedal", 10);
            addTreasure("SilverMedal", 100);
        }
    }

    public UAccount login() {
        return new UAccount("sg_demo", "sg_demo");
    }

    public String getPlayerDetails() {
        return null;
    }

    public List<UProduct> getProductList(String filter) {
        List<UProduct> pl = new ArrayList<UProduct>(mProductList.size());
        Iterator it = mProductList.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, UProduct> e = (Map.Entry<String, UProduct>)it.next();
            pl.add(e.getValue());
        }
        Collections.sort(pl, new Comparator<UProduct>() {
            @Override
            public int compare(UProduct lhs, UProduct rhs) {
                int d = lhs.getPrice() - rhs.getPrice();
                if (d == 0)
                    d = lhs.getCoins() - rhs.getCoins();
                return d;
            }
        });
        return pl;
    }

    public UOrderTicket getOrder(UProduct product, UPayParams pp) {
        if (pp.getBuyNum() == 1) {
            UProduct p = mProductList.get(product.getProductID());
            if (p != null) {
                OrderInfo oi = new OrderInfo();
                oi.id = createOrderID();
                oi.productID = product.getProductID();
                oi.num = pp.getBuyNum();
                oi.state = "generated";
                oi.gtime = System.currentTimeMillis();
                oi.ctime = 0;
                oi.dtime = 0;
                mOrderList.put(oi.id, oi);
                // Use a dummy URL below, but it does not work. Hence the payment process cannot finish.
                return new UOrderTicket(oi.id, "http://127.0.0.1/userver/payment_notify.php");
            }
        }
        return null;
    }

    public void notifyOrderAccepted(String orderID, String productID, int buyNum) {
        OrderInfo oi = mOrderList.get(orderID);
        if (oi != null) {
            if (oi.state.equals("generated")) {
                oi.state = "succeeded";
                oi.ctime = System.currentTimeMillis();
            }
        }
    }

    public String getOrderState(String orderID, String productID, int buyNum) {
        OrderInfo oi = mOrderList.get(orderID);
        if (oi != null) {
            return oi.state;
        }
        return null;
    }

    public String deliverOrder(String orderID, String productID, int buyNum) {
        OrderInfo oi = mOrderList.get(orderID);
        if (oi != null) {
            if (oi.state.equals("succeeded")) {
                oi.state = "delivered";
                oi.dtime = System.currentTimeMillis();
                return "OK";
            }
        }
        return "BAD";
    }

    public List<UOrder> getOrderInfoList(String filter) {
        List<UOrder> ol = new ArrayList<UOrder>(mOrderList.size());
        Iterator it = mOrderList.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, OrderInfo> e = (Map.Entry<String, OrderInfo>)it.next();
            OrderInfo oi = e.getValue();
            UProduct p = mProductList.get(oi.productID);
            UOrder order = new UOrder();
            UOrderTicket ticket = new UOrderTicket(null, null);
            UPayParams pp = new UPayParams();
            order.setPayParams(pp);
            order.setOrderTicket(ticket);
            order.setState(oi.state);
            order.setGeneratedTime(oi.gtime);
            order.setCompletedTime(oi.ctime);
            order.setDeliveredTime(oi.dtime);
            ticket.setOrderID(e.getKey());
            ticket.setExtension("");
            pp.setProductID(p.getProductID());
            pp.setBuyNum(oi.num);
            pp.setRatio(1);
            pp.setVip("");
            ol.add(order);
        }
        Collections.sort(ol, new Comparator<UOrder>() {
            @Override
            public int compare(UOrder lhs, UOrder rhs) {
                return (int)(lhs.getGeneratedTime() - rhs.getGeneratedTime());
            }
        });
        return ol;
    }

    public String[] getTreasureList(String filter) {
        String[] treasures = new String[mTreasureList.size()];
        Iterator it = mTreasureList.entrySet().iterator();
        int i = 0;
        while (it.hasNext()) {
            Map.Entry<String, Treasure> e = (Map.Entry<String, Treasure>)it.next();
            Treasure t = e.getValue();
            treasures[i] = t.name + "=" + t.num;
            i++;
        }
        Arrays.sort(treasures, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.substring(0, o1.indexOf('=')).compareTo(o2.substring(0, o1.indexOf('=')));
            }
        });
        return treasures;
    }

    public int incTreasure(String name, int value) {
        Iterator it = mTreasureList.entrySet().iterator();
        int i = 0;
        while (it.hasNext()) {
            Map.Entry<String, Treasure> e = (Map.Entry<String, Treasure>)it.next();
            Treasure t = e.getValue();
            if (t.name.equals(name)) {
                t.num += value;
                if (t.num < 0)
                    t.num = 0;
                return t.num;
            }
        }
        return -1;
    }

    public String logout(UAccount account) {
        return "OK";
    }

    public boolean startGame() {
        return true;
    }

    public boolean stopGame() {
        return true;
    }

    public boolean log(char action, String key, int value, String message, boolean waitForResp) {
        switch (action) {
            case SGGAMESERVER_LOG_ACTION_LOGIN:
            case SGGAMESERVER_LOG_ACTION_LOGOUT:
            case SGGAMESERVER_LOG_ACTION_STARTGAME:
            case SGGAMESERVER_LOG_ACTION_STOPGAME:
                return true;
            default:
                if (key == null)
                    return false;
                boolean newLog = false;
                Log log = mLogList.get(key);
                if (log == null) {
                    log = new Log();
                    log.id = key;
                    log.msg = message;
                    log.value = 0;
                    newLog = true;
                }
                switch (action) {
                    case SGGAMESERVER_LOG_ACTION_INCVALUE:
                        log.value += value;
                        break;
                    case SGGAMESERVER_LOG_ACTION_DECVALUE:
                        log.value -= value;
                        break;
                    case SGGAMESERVER_LOG_ACTION_MAXVALUE:
                        log.value = log.value < value ? value : log.value;
                        break;
                    case SGGAMESERVER_LOG_ACTION_MINVALUE:
                        log.value = log.value > value ? value : log.value;
                        break;
                    case SGGAMESERVER_LOG_ACTION_UPDATEVALUE:
                        log.value = value;
                        break;
                    default:
                        newLog = false;
                }
                if (newLog)
                    mLogList.put(key, log);
                return true;
        }
    }

    public String getLogValue(String key, int defaultValue) {
        if (key != null) {
            Log log = mLogList.get(key);
            if (log != null)
                return "" + log.value + "," + log.msg;
        }
        return "" + defaultValue + ",";
    }

}