text:
local sType = require("ServiceType");
local cmdType = require("system/Const/CmdType");
local responce = require("Respones");
local mobaConfig = require("MobaConfig");

local mysql = require("datebase/mysql_game");
local redis = require("datebase/redis_game");

--检查每日登陆奖励
--handler: err,bonuesInfo
local function check_login_bonues( uid,handler )
    mysql.GetBonuesInfo(uid,
        function( err,bonuesInfo )
            if err then
                handler(err,nil);
                return;
            end
            --用户第一次登陆
            if bonuesInfo==nil then
                --插入数据
                mysql.InsertBonuesInfo(uid,
                    function ( e )
                        if e then
	                        handler(e,nil);
                            return;
                        end

                        --重新检查
                        check_login_bonues(uid,handler);
                        return;
                    end)

                return;
            end

            print("check_login_bonues");

            --更新发放奖励
            if bonuesInfo.bonues_time < Utils.Today() then
                --发放
                --执行奖励逻辑
                if bonuesInfo.bonues_time>Utils.Yesterday() then
                    --连读登陆
                    bonuesInfo.days=bonuesInfo.days+1;
                else
                    --重新开始计算
                    bonuesInfo.days=1;
                end

                --登陆奖励循环
                if bonuesInfo.days>#mobaConfig.login_bonues then
                    bonuesInfo.days=1;
                end

                bonuesInfo.status=0;
                bonuesInfo.bonues_time=Utils.TimeStamp();
                bonuesInfo.bonues=mobaConfig.login_bonues[bonuesInfo.days];

                --更新数据库
                mysql.SetBonuesInfo(uid,bonuesInfo,
                    function ( err )
                        if err then
                            handler(err,nil);
                            return;
                        end

                        handler(nil,bonuesInfo);
                    end)
            end

            handler(nil,bonuesInfo);
        end)
end

--获取游戏信息
local function get_ugame_info( s,req )
    if nil==req then
        Debug.LogError("WTF??");
        return;
    end
	local uid = req[3];

    --获取信息
	mysql.GetUgameInfo(uid,
		function ( err,ginfo )

			if err then
                Debug.LogError(err);
				local retMsg = {sType.System,cmdType.eGetMobaInfoRes,uid,
    			{
    				status = responce.SystemError
    			}};
    			Session.SendPackage(s,retMsg);
    			return;
    		end

			--没有查到对应信息
    		if ginfo==nil then
    			--就添加进数据库
    			mysql.InsertUgameInfo(uid,function(err)
    				if err then
    					--告诉客户端某个错误信息
    					print("Insert err",err);
    					local retMsg =  {sType.System,cmdType.eGetMobaInfoRes,uid,
    						{
                            	status = responce.SystemError
    						}};
    					Session.SendPackage(s,retMsg);
    					return;
    				end
    				--重新登陆
    				get_ugame_info(s,req);
    				return;
    			end)
    			return;
    		end

    		--找到对应游戏数据
    		if ginfo.ustatus~=0 then
    			--游客账号被查封
    			print("this key has been frozen");
    			local retMsg =   {sType.System,cmdType.eGetMobaInfoRes,uid,
    				{
    					status = responce.UserIsFreeze
    				}};
    			Session.SendPackage(s,retMsg);
    			return;
    		end

            --更新redis数据库
--			print("update redis ugame info");
			redis.SetUgameInfo(uid,ginfo);

            --检测登陆奖励
            check_login_bonues(uid,
                function( err,bonuesInfo )
                    if err then
                        Debug.LogError(err);
                        local retMsg = {sType.System,cmdType.eGetMobaInfoRes,uid,
                        {
                            status = responce.SystemError
                        }};
                        Session.SendPackage(s,retMsg);
                        return;
                    end

                    --返回客户端

                    local retMsg = {sType.System,cmdType.eGetMobaInfoRes,uid,
                        {
                            status = responce.OK,
	                        uinfo = {
                                ucoin_1=ginfo.ucoin_1,
                                ucoin_2=ginfo.ucoin_2,
                                uexp=ginfo.uexp,
                                uvip=ginfo.uvip,
                                uitem_1=ginfo.uitem_1,
                                uitem_2=ginfo.uitem_2,
                                bonues_status=bonuesInfo.status,
                                bonues=bonuesInfo.bonues,
                                days=bonuesInfo.days,
                            },
                        }};
                    --print("send utag: ",utag);
                    Session.SendPackage(s,retMsg);
                end)
		end)
end

--处理得到每日奖励的消息
local function recv_login_bonues( s,req )
    local uid =req[3];
        --获取信息
    mysql.GetBonuesInfo(uid,
        function ( err,bonuesInfo )
            if err then
                Debug.LogError(err);
                local retMsg = {sType.System,cmdType.eRecvLoginBonuesRes,uid,
                {
                    status = responce.SystemError
                }};
                Session.SendPackage(s,retMsg);
                return;
            end

            if nil==bonuesInfo or bonuesInfo.status~=0 then
                local retMsg = {sType.System,cmdType.eRecvLoginBonuesRes,uid,
                {
                    status = responce.InvalidOprate
                }};
                Session.SendPackage(s,retMsg);
                return;
            end

            --有奖励可以领取
            mysql.UpdateLoginBonuesStatus(uid,function ( err )
                    if err then
                        Debug.LogError(err);
                        local retMsg = {sType.System,cmdType.eRecvLoginBonuesRes,uid,
                        {
                            status = responce.SystemError
                        }};
                        Session.SendPackage(s,retMsg);
                        return;
                    end

                    redis.AddCoin1(uid,bonuesInfo.bonues);

                    mysql.AddCoin1(uid,bonuesInfo.bonues,
                        function ( err )

                            if err then
                                Debug.LogError(err);
                                local retMsg = {sType.System,cmdType.eRecvLoginBonuesRes,uid,
                                {
                                    status = responce.SystemError
                                }};
                                Session.SendPackage(s,retMsg);
                                return;
                            end

                            local retMsg = {sType.System,cmdType.eRecvLoginBonuesRes,uid,
                            {
                                status = responce.OK,
                            }};
                            Session.SendPackage(s,retMsg);
                            return;
                        end);
            end);
        end);
end

mysql.Connect();
redis.Connect();

return{
	GetUgameInfo=get_ugame_info,
    RecvLoginBonues=recv_login_bonues,
};


text:
local cType=require("system/Const/CmdType");

local systemLogic = require("system/SystemLogic");

local cTypeToCallback = {};
cTypeToCallback[cType.eGetMobaInfoReq]=systemLogic.GetUgameInfo;
cTypeToCallback[cType.eRecvLoginBonuesReq]=systemLogic.RecvLoginBonues;

return   {
    OnSessionRecvCmd=function(s,msg)
        --{sType,cType,utag,body}

        if cTypeToCallback[msg[2]] then
	        if nil==msg then
	        	Debug.LogError("????")
	        	return;
	        end
            cTypeToCallback[msg[2]](s,msg);
        else
            Debug.LogError("unregistered cmdType: ",msg[2]);
        end
    end,
    OnSessionDisconnected=function(s,sType)
    	local ip,port = Session.GetAddress(s);
    	print("client ["..ip..":"..port.."] leave");
    end,
};

mime_type: text/plain


text:
copy ..\..\..\..\build\proj_win32\x64\Debug\MobaServer.exe ..\..\..\..\build\bin\
..\..\..\..\build\bin\MobaServer.exe ../ system/system_server_main.lua


text:
local ServiceType = require("ServiceType");

local cmdNameMap={};
cmdNameMap[ServiceType.System]={
	UserLostConn = 0,
	GetMobaInfoReq=1,
	GetMobaInfoRes=2,
	RecvLoginBonuesReq = 3,
	RecvLoginBonuesRes = 4,
};

return cmdNameMap;


text:
return {
	eUserLostConn = 0,
	eGetMobaInfoReq=1,
	eGetMobaInfoRes=2,
	eRecvLoginBonuesReq = 3,
	eRecvLoginBonuesRes = 4,
};


text:
syntax = "proto2";

enum MobaCmd{
	INVALID_LOGIN_CMD = 0;
	eGetMobaInfoReq = 1;
	eGetMobaInfoRes = 2;
	eRecvLoginBonuesReq = 3;
	eRecvLoginBonuesRes = 4;
}

message RecvLoginBonuesRes{
	required int32 status = 1;
}

message UserGameInfo{
	required int32 ucoin_1 = 1;
	required int32 ucoin_2 = 2;
	required int32 uexp = 3;
	required int32 uvip = 4;
	required int32 uitem_1 = 5;
	required int32 uitem_2 = 6;
	required int32 bonues_status = 7;
	required int32 bonues = 8;
	required int32 days = 9;
}

message GetMobaInfoRes{
	required int32 status = 1;
	optional UserGameInfo uinfo = 2;
}


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$/../../../.." vcs="Git" />
  </component>
</project>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/.idea/scripts.iml" filepath="$PROJECT_DIR$/.idea/scripts.iml" />
    </modules>
  </component>
</project>


text:
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
  <component name="NewModuleRootManager" inherit-compiler-output="true">
    <exclude-output />
    <content url="file://$MODULE_DIR$" />
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="library" name="Bundled Protobuf Distribution" level="application" />
  </component>
</module>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="Encoding">
    <file url="file://$PROJECT_DIR$/gateway/Const/CmdNameMap.lua" charset="GBK" />
  </component>
</project>


text:
<component name="ProjectCodeStyleConfiguration">
  <state>
    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
  </state>
</component>


text:
<component name="CopyrightManager">
  <settings>
    <LanguageOptions name="Lua">
      <option name="fileTypeOverride" value="3" />
      <option name="block" value="false" />
      <option name="prefixLines" value="false" />
      <option name="filler" value="=" />
    </LanguageOptions>
  </settings>
</component>


text:
local mysql = require("datebase/mysql_center");
local redis = require("datebase/redis_center");
local sType = require("ServiceType");
local cmdType = require("auth/Const/CmdType");
local responce = require("Respones");
local function OnGuestLogin( s,msg )
		--{sType,cType,utag,body}
    	local key = msg[4].guest_key;
        local utag = msg[3];

        --判断gkey的合法性
        if type(key) ~="string" or string.len(key) ~= 15 then
            print("invalid guest_key", err,key)
            local retMsg = {sType.Auth,cmdType.eGuestLoginRes,utag,
                {
                    status = responce.InvalidParams,
                }};
            Session.SendPackage(s,retMsg);
            return;
        end

    	mysql.GetUinfoByKey(key,function( err,uinfo )
    		if err then
    			--告诉客户端某个错误信息
    			print("something wrong", err)
    			local retMsg = {sType.Auth,cmdType.eGuestLoginRes,utag,
    				{
    					status = responce.SystemError
    				}};
    			Session.SendPackage(s,retMsg);
    			return;
    		end
    		--没有查到对应信息
    		if uinfo==nil then
    			--就添加进数据库
    			mysql.InsertGuestUser(key,function(err)
    				if err then
    					--告诉客户端某个错误信息
    					print("Insert err",err);
    					local retMsg = {sType.Auth,cmdType.eGuestLoginRes,utag,
    						{
    							status = responce.SystemError
    						}};
    					Session.SendPackage(s,retMsg);
    					return;
    				end
    				--重新登陆
    				OnGuestLogin(s,msg);
    				return;
    			end)
    			return;
    		end
    		--找到对应游戏数据
    		if uinfo.status~=0 then
    			--游客账号被查封
    			print("this key has been frozen");
    			local retMsg = {sType.Auth,cmdType.eGuestLoginRes,utag,
    				{
    					status = responce.UserIsFreeze
    				}};
    			Session.SendPackage(s,retMsg);
    			return;
    		end

            --账号已经不是游客账号
    		if uinfo.is_guest~=1 then
    			print("this is not a guest");
    			local retMsg = {sType.Auth,cmdType.eGuestLoginRes,utag,
    				{
    					status = responce.UserIsNotGuest
    				}};
    			Session.SendPackage(s,retMsg);
    			return;
    		end

    		--登陆成功
    		print(uinfo.uid.." "..uinfo.unick.." login");

            --将用户数据保存到Redis
            redis.SetUinfo(uinfo.uid,uinfo);

            local retMsg = {sType.Auth,cmdType.eGuestLoginRes,utag,
    			{
    				status = responce.OK,
    				uinfo = {
    					unick=uinfo.unick,
    					uface=uinfo.uface,
    					usex=uinfo.usex,
    					uvip=uinfo.uvip,
    					uid=uinfo.uid,
    				},
    			}};
            --print("send utag: ",utag);
    		Session.SendPackage(s,retMsg);
    	end);
end

local function DoEditProfile( s,req )
    local uid = req[3];
    local editProfileReq = req[4];
    --print(uid,editProfileReq.unick,editProfileReq.uface,editProfileReq.usex);

    if string.len(editProfileReq.unick)<=0
        or (editProfileReq.uface<0 or editProfileReq.uface>7)
        or (editProfileReq.usex~=0 and editProfileReq.usex~=1) then
        local res = {sType.Auth,cmdType.eEditProfileRes,uid,
        {
            status = responce.InvalidParams,
        }};
        Session.SendPackage(s,res);
        return;
    end

    --更新数据库
    mysql.EditProfile(
        uid,editProfileReq.unick,editProfileReq.uface,editProfileReq.usex,
        function( err )
            local retStatus = responce.OK;
            if err then
                Debug.LogError(err);
                status = responce.SystemError;
            end

            --成功修改

            --修改Redis数据库
            redis.EditProfile(uid,editProfileReq.unick,editProfileReq.uface,editProfileReq.usex);

            local res = {sType.Auth,cmdType.eEditProfileRes,uid,
            {
                status = retStatus,
            }};
            Session.SendPackage(s,res);
        end);
    redis.EditProfile(uid,editProfileReq.unick,editProfileReq.uface,editProfileReq.usex);
end

local function _do_guest_account_upgrade(s,req,uid,uname,pwd )
    mysql.GuestAccountUpgrade(uid,uname,pwd,
        function ( err )
            --发生错误
            if err then
                local res = {sType.Auth,cmdType.eAccountUpgradeRes,uid,
                {
                    status = responce.SystemError,
                }};
                Session.SendPackage(s,res);
                return;
            end

            --更新成功
            local res = {sType.Auth,cmdType.eAccountUpgradeRes,uid,
            {
                status = responce.OK,
            }};
            Session.SendPackage(s,res);
            print("[ "..uname.." : "..pwd.." ] upgrade success");
            return;
        end)
end

local function account_upgrade( s,req )
    local uid = req[3];
    local preq = req[4];

    local uname = preq.uname;
    local pwd = preq.upwd_md5;

    --检验参数合法性
    if string.len(uname) <=0
        or string.len(pwd) ~= 32 then
        print("upgrade failed: InvalidParams",uname,pwd);
        local res = {sType.Auth,cmdType.eAccountUpgradeReq,uid,
        {
            status = responce.InvalidParams,
        }};
        Session.SendPackage(s,res);
        return;
    end

    --检查uid是否为游客
    mysql.CheckUnameExist(uname,
        function ( err, foundUname)
            --发生错误
            if err then
                Debug.LogError(err);
                local res = {sType.Auth,cmdType.eAccountUpgradeRes,uid,
                {
                    status = responce.SystemError,
                }};
                Session.SendPackage(s,res);
                return;
            end
            --uname已经被使用
            if foundUname then
                print("Upgrade failed: uname["..uname.."] has exist");
                local res = {sType.Auth,cmdType.eAccountUpgradeRes,uid,
                {
                    status = responce.UnameHasExist,
                }};
                Session.SendPackage(s,res);
                return;
            end

            --检测是否已经不是游客账号
            mysql.GetUinfoByUid(uid,
                function ( err,uinfo )
                    if err then
                        Debug.LogError(err);
                        local res = {sType.Auth,cmdType.eAccountUpgradeRes,uid,
                        {
                            status = responce.SystemError,
                        }};
                        Session.SendPackage(s,res);
                        return;
                    end
                    --如果已经不是游客账号
                    if uinfo.is_guest~=1 then
                        Debug.LogError("Upgrade failed: this is not a guest");
                        local res = {sType.Auth,cmdType.eAccountUpgradeRes,uid,
                        {
                            status = responce.UserIsNotGuest,
                        }};
                        Session.SendPackage(s,res);
                        return;
                    end

                    --可以升级
                    _do_guest_account_upgrade(s,req, uid,uname,pwd);
                end);

        end)
end

local function user_login( s,req )

    local uid = req[3];
    local preq = req[4];

    local uname = preq.uname;
    local pwd = preq.upwd_md5;
    --检验参数合法性
    if string.len(uname) <=0
        or string.len(pwd) ~= 32 then
        print("upgrade failed: InvalidParams",uname,pwd);
        local res = {sType.Auth,cmdType.eUserLoginRes,uid,
        {
            status = responce.InvalidParams,
        }};
        Session.SendPackage(s,res);
        return;
    end

    --检查用户名与密码是否正确

    mysql.GetUinfoByUnamePwd(uname,pwd,function( err,uinfo )
            if err then
                --告诉客户端某个错误信息
                print("something wrong", err)
                local retMsg = {sType.Auth,cmdType.eUserLoginRes,uid,
                    {
                        status = responce.SystemError
                    }};
                Session.SendPackage(s,retMsg);
                return;
            end
            --没有查到对应信息
            if uinfo==nil then
                print("UnameOrPwdError");
                local retMsg = {sType.Auth,cmdType.eUserLoginRes,uid,
                    {
                        status = responce.UnameOrPwdError,
                    }};
                Session.SendPackage(s,retMsg);
                return;
            end
            --找到对应游戏数据
            if uinfo.status~=0 then
                --游客账号被查封
                print("this key has been frozen");
                local retMsg = {sType.Auth,cmdType.eUserLoginRes,uid,
                    {
                        status = responce.UserIsFreeze
                    }};
                Session.SendPackage(s,retMsg);
                return;
            end

            --登陆成功
            print(uinfo.uid.." "..uinfo.unick.." login");

            --将用户数据保存到Redis
            redis.SetUinfo(uinfo.uid,uinfo);

            local retMsg = {sType.Auth,cmdType.eUserLoginRes,uid,
                {
                    status = responce.OK,
                    uinfo = {
                        unick=uinfo.unick,
                        uface=uinfo.uface,
                        usex=uinfo.usex,
                        uvip=uinfo.uvip,
                        uid=uinfo.uid,
                    },
                }};
            --print("send utag: ",utag);
            Session.SendPackage(s,retMsg);
        end);
end

local function user_unregister( s,req )
    local uid = req[3];

    local res = {sType.Auth,cmdType.eUserUnregisterRes,uid,
    {
        status = responce.OK,
    }};
    Session.SendPackage(s,res);
end

mysql.Connect();
redis.Connect();

return {
	GuestLogin = OnGuestLogin,
    UserLogin = user_login,
    UserUnregister=user_unregister,
    EditProfile=DoEditProfile,
    AccountUpgrade=account_upgrade,
};


text:
--初始化日志模块
Debug.LogInit("logger/auth","auth",true);
--初始化协议模块
local ProtoType={
    Json=0,
    Protobuf=1,
};
ProtoManager.Init(ProtoType.Protobuf,"F:\\Projects\\unity\\Moba\\Server\\apps\\lua_test\\scripts\\auth\\Const");
--如果是Protobuf协议，还需要注册映射表
if ProtoManager.ProtoType()==ProtoType.Protobuf then
    local cmdNameMap=require("auth/Const/CmdNameMap");
    if cmdNameMap then
        ProtoManager.RegisterCmdMap(cmdNameMap);
    else
    	Debug.LogError("register cmdNameMap failed");
    end
end

--读取配置文件
local config = require("GameConfig");
local sType = require("ServiceType");

local servers=config.servers;

--开启服务器
Netbus.TcpListen(servers[sType.Auth].port
    ,function(s)
        local ip,port = Session.GetAddress(s);
        print("new client come ["..ip..":"..port.."]");
    end);

print("Auth server [tcp] listen at: ",servers[sType.Auth].port);

--注册服务
local authService = require("auth/AuthService");
local ret = Service.Register(sType.Auth,authService);
if ret then
    print("register Auth Service:[" .. sType.Auth .. "] service success");
else
    print("register Auth Service:[" .. sType.Auth .. "] service failed");
end


text:
local sType=require("ServiceType");
local cType=require("auth/Const/CmdType");

local guestLogic = require("auth/Guest");

local cTypeToCallback = {};
cTypeToCallback[cType.eGuestLoginReq]=guestLogic.GuestLogin;
cTypeToCallback[cType.eEditProfileReq]=guestLogic.EditProfile;
cTypeToCallback[cType.eAccountUpgradeReq]=guestLogic.AccountUpgrade;
cTypeToCallback[cType.eUserLoginReq]=guestLogic.UserLogin;
cTypeToCallback[cType.eUserUnregisterReq]=guestLogic.UserUnregister;

return   {
    OnSessionRecvCmd=function(s,msg)
        --{sType,cType,utag,body}
        if cTypeToCallback[msg[2]] then
            cTypeToCallback[msg[2]](s,msg);
        else
            Debug.LogError("unregistered cmdType: ",msg[2]);
        end
    end,
    OnSessionDisconnected=function(s,sType)
    	local ip,port = Session.GetAddress(s);
    	print("client ["..ip..":"..port.."] leave");
    end,
};


text:
copy ..\..\..\..\build\proj_win32\x64\Debug\MobaServer.exe ..\..\..\..\build\bin\
..\..\..\..\build\bin\MobaServer.exe ../ auth/auth_server_main.lua


text:
syntax = "proto2";

enum LoginCmd{
	INVALID_LOGIN_CMD = 0;
	eGuestLoginReq = 1;
	eGuestLoginRes = 2;
	eReLogin = 3;
	eUserLostConn = 4;
	eEditProfileReq = 5;
	eEditProfileRes = 6;
	eAccountUpgradeReq = 7;
	eAccountUpgradeRes = 8;
	eUserLoginReq = 9;
	eUserLoginRes = 10;
	eUserUnregisterReq = 11;
	eUserUnregisterRes = 12;
}

message UserUnregisterRes{
	required int32 status = 1;
}

message GuestLoginReq{
	required string guest_key = 1;
}

message UserCenterInfo{
	required string unick = 1;
	required int32 uface = 2;
	required int32 usex = 3;
	required int32 uvip = 4;
	required int32 uid = 5;
}

message GuestLoginRes{
	required int32 status = 1;
	optional UserCenterInfo uinfo = 2;
}

message EditProfileReq{
	required string unick = 1;
	required int32 uface = 2;
	required int32 usex = 3;
}

message EditProfileRes{
	required int32 status = 1;
}

message AccountUpgradeReq{
	required string uname = 1;
	required string upwd_md5 = 2;
}

message AccountUpgradeRes{
	required int32 status = 1;
}

message UserLoginReq{
	required string uname = 1;
	required string upwd_md5 = 2;
}

message UserLoginRes{
	required int32 status = 1;
	optional UserCenterInfo uinfo = 2;
}


text:
local ServiceType = require("ServiceType");

local cmdNameMap={};
cmdNameMap[ServiceType.Auth]={
	UserLostConn = 0,
	GuestLoginReq = 1,
	GuestLoginRes = 2,
	ReLogin = 3,
	EditProfileReq = 5,
	EditProfileRes = 6,
	AccountUpgradeReq = 7,
	AccountUpgradeRes = 8,
	UserLoginReq = 9,
	UserLoginRes = 10,
	UserUnregisterReq = 11,
	UserUnregisterRes = 12,
};

return cmdNameMap;


text:
return {
	eUserLostConn = 0,
	eGuestLoginReq = 1,
	eGuestLoginRes = 2,
	eReLogin = 3,
	eEditProfileReq = 5,
	eEditProfileRes = 6,
	eAccountUpgradeReq = 7,
	eAccountUpgradeRes = 8,
	eUserLoginReq = 9,
	eUserLoginRes = 10,
	eUserUnregisterReq = 11,
	eUserUnregisterRes = 12,
};


text:
local config=require("GameConfig");
local mysqlConn=nil;

--链接数据库
local function mysql_connect_to_auth_center()

	if mysqlConn then
		return;
	end

	local authConfig = config.auth_mysql;
	Mysql.Connect(
		authConfig.host,
		authConfig.port,
		authConfig.dbName,
		authConfig.uname,
		authConfig.upwd,
		function ( err,conn )
			if err then
				Debug.LogError(err);
				Timer.Once(mysql_connect_to_auth_center,5000);
				return;
			end
			Debug.Log("connect to mysql [ auth_center_db ] success");
			mysqlConn=conn;
		end);
end

--根据guest_key获取uinfo
--handler:err,uinfo
function get_uinfo_by_key( key,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected",nil);
		end
		return;
	end
	local sql="select uid,unick,usex,uface,uvip,status,is_guest from uinfo where guest_key=\"%s\" limit 1";
	sql=string.format(sql,key);
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err,nil);
			end
			return;
		end
		--没有查到数据
		if ret==nil or #ret <=0 then
			if handler then
				handler(nil,nil);
			end
			return;
		end
		--查到数据
		local result = ret[1];
		local uinfo =
		{
			uid= tonumber(result[1]),
			unick=result[2],
			usex=tonumber(result[3]),
			uface=tonumber(result[4]),
			uvip=tonumber(result[5]),
			status=tonumber(result[6]),
			is_guest=tonumber(result[7]),
		};
		if handler then
			handler(nil,uinfo);
		end
	end);
end

--根据uid获取用户信息
function get_uinfo_by_uid( uid,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected",nil);
		end
		return;
	end
	local sql="select uid,unick,usex,uface,uvip,status,is_guest from uinfo where uid=\"%d\" limit 1";
	sql=string.format(sql,uid);
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err,nil);
			end
			return;
		end
		--没有查到数据
		if ret==nil or #ret <=0 then
			if handler then
				handler(nil,nil);
			end
			return;
		end
		--查到数据
		local result = ret[1];
		local uinfo =
		{
			uid= tonumber(result[1]),
			unick=result[2],
			usex=tonumber(result[3]),
			uface=tonumber(result[4]),
			uvip=tonumber(result[5]),
			status=tonumber(result[6]),
			is_guest=tonumber(result[7]),
		};
		if handler then
			handler(nil,uinfo);
		end
	end);
end

--插入guest_key
--handler: err
function insert_guest_user( key,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected");
		end
		return;
	end
	local sql="insert into uinfo(guest_key,unick,uface,usex,is_guest)values(\"%s\",\"%s\",%d,%d,1)";
	local unick = "gst"..math.random(100000,999999);
	local uface = math.random(1,9);
	local usex = math.random(0,1);
	sql=string.format(sql,key,unick,uface,usex);
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err);
			end
			return;
		end
		if handler then
			handler(nil);
		end
	end);
end

--修改用户信息
--handler: err
function edit_profile(uid,unick,uface,usex,handler)
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected");
		end
		return;
	end

	local sql = 'update uinfo set unick="%s",usex=%d,uface=%d where uid=%d';
	sql=string.format(sql,unick,usex,uface,uid);
	Mysql.Query(mysqlConn,sql,
		function( err,ret )
			if err then
				handler(err);
			else
				handler(nil);
			end
		end);
end

--判断用户名是否存在
--handler: err,foundUid
function check_uname_exist(	uname,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected",nil);
		end
		return;
	end
	local sql = 'select uid from uinfo where uname="%s"';
	sql=string.format(sql,uname);
	Mysql.Query(mysqlConn,sql,
		function( err,ret )
			if err then
				if handler then
					handler(err,nil);
				end
				return;
			end
			if ret==nil or #ret<=0 then
				if handler then
					handler(err,nil);
				end
				return;
			end
			if handler then
				local result = ret[1];
				handler(err,result);
			end
		end);
end

--升级游客账户
--handler:err
function guest_account_upgrade( uid,uname,upwd,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected");
		end
		return;
	end

	local sql = 'update uinfo set uname="%s",upwd="%s",is_guest=0 where uid=%d';
	sql=string.format(sql,uname,upwd,uid);
	Mysql.Query(mysqlConn,sql,
		function( err,ret )
			if err then
				if handler then
					handler(err);
				end
				return;
			end

			if ret==nil or #ret<=0 then
				if handler then
					handler(err);
				end
				return;
			end

			if handler then
				handler(nil);
			end
		end);
end

--根据用户名密码查找用户信息
--handler:err,uinfo
function get_uinfo_by_uname_pwd( uname,upwd,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected",nil);
		end
		return;
	end

	local sql="select uid,unick,usex,uface,uvip,status,is_guest from uinfo where uname=\"%s\" and upwd=\"%s\" limit 1";
	sql=string.format(sql,uname,upwd);
	--print(sql);
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err,nil);
			end
			return;
		end
		--没有查到数据
		if ret==nil or #ret <=0 then
			if handler then
				handler(nil,nil);
			end
			return;
		end
		--查到数据
		local result = ret[1];
		local uinfo =
		{
			uid= tonumber(result[1]),
			unick=result[2],
			usex=tonumber(result[3]),
			uface=tonumber(result[4]),
			uvip=tonumber(result[5]),
			status=tonumber(result[6]),
			is_guest=tonumber(result[7]),
		};
		if handler then
			handler(nil,uinfo);
		end
	end);
end

return {
	Connect=mysql_connect_to_auth_center,
	GetUinfoByKey=get_uinfo_by_key,
	GetUinfoByUid=get_uinfo_by_uid,
	GetUinfoByUnamePwd=get_uinfo_by_uname_pwd,
	InsertGuestUser=insert_guest_user,
	EditProfile=edit_profile,
	CheckUnameExist=check_uname_exist,
	GuestAccountUpgrade =guest_account_upgrade,
	IsConnect=function() return mysqlConn~=nil end
};


text:
local config=require("GameConfig");
local mobaConfig = require("MobaConfig");
local mysqlConn=nil;

--链接数据库
local function mysql_connect_to_game_center()

	if mysqlConn then
		return;
	end

	local gameConfig = config.game_mysql;
	Mysql.Connect(
		gameConfig.host,
		gameConfig.port,
		gameConfig.dbName,
		gameConfig.uname,
		gameConfig.upwd,
		function ( err,conn )
			if err then
				Debug.LogError(err);
				Timer.Once(mysql_connect_to_game_center,5000);
				return;
			end
			Debug.Log("connect to mysql [ moba_game ] success");
			mysqlConn=conn;
		end);
end

--获取游戏数据
--handler: err,uinfo
local function get_ugame_info( uid,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected",nil);
		end
		return;
	end
	local sql="select ucoin_1,ucoin_2,uexp,uvip,uitem_1,uitem_2,ustatus from ugame where uid=%d limit 1";
	sql=string.format(sql,uid);
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err,nil);
			end
			return;
		end
		--没有查到数据
		if ret==nil or #ret <=0 then
			if handler then
				handler(nil,nil);
			end
			return;
		end
		--查到数据
		local result = ret[1];
		local uinfo =
		{
			ucoin_1= tonumber(result[1]),
			ucoin_2=tonumber(result[2]),
			uexp=tonumber(result[3]),
			uvip=tonumber(result[4]),
			uitem_1=tonumber(result[5]),
			uitem_2=tonumber(result[6]),
			ustatus=tonumber(result[7]),
		};
		if handler then
			handler(nil,uinfo);
		end
	end);
end

--插入游戏数据
--handler: err
local function insert_ugame_info( uid,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected");
		end
		return;
	end
	local sql="insert into ugame(uid,ucoin_1,ucoin_2,uvip,uexp)values(%d,%d,%d,%d,%d)";
	sql=string.format(sql,uid,mobaConfig.ugame.ucoin_1,mobaConfig.ugame.ucoin_2,mobaConfig.ugame.uvip,mobaConfig.ugame.uexp);
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err);
			end
			return;
		end
		if handler then
			handler(nil);
		end
	end);
end

--获取登陆奖励信息
--handler: err,bonuesInfo
local function get_bonues_info( uid,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected",nil);
		end
		return;
	end
	local sql="select bonues,status,bonues_time,days from login_bonues where uid=%d limit 1";
	sql=string.format(sql,uid);
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err,nil);
			end
			return;
		end
		--没有查到数据
		if ret==nil or #ret <=0 then
			if handler then
				handler(nil,nil);
			end
			return;
		end
		--查到数据
		local result = ret[1];
		local bonuesInfo =
		{
			bonues= tonumber(result[1]),
			status=tonumber(result[2]),
			bonues_time=tonumber(result[3]),
			days=tonumber(result[4]),
		};
		if handler then
			handler(nil,bonuesInfo);
		end
	end);
end

--插入登陆奖励
--handler: err
local function insert_bonues_info( uid,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected");
		end
		return;
	end
	local sql="insert into login_bonues(uid,bonues_time,status)values(%d,%d,1)";
	sql=string.format(sql,uid,Utils.TimeStamp());
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err);
			end
			return;
		end
		if handler then
			handler(nil);
		end
	end);
end

--更新登陆信息
--handler: err
local function set_bonues_info( uid,uinfo,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected");
		end
		return;
	end

	local sql = 'update login_bonues set status=0,bonues=%d,bonues_time=%d,days=%d where uid=%d';
	sql=string.format(sql,uinfo.bonues,uinfo.bonues_time,uinfo.days,uid);
	Mysql.Query(mysqlConn,sql,
		function( err,ret )
			if err then
				handler(err);
			else
				handler(nil);
			end
		end);
end

--添加货币一
local function add_coin( uid,coin_1,handler )
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected");
		end
		return;
	end

	local sql = 'update ugame set ucoin_1=ucoin_1+%d where uid=%d';
	sql=string.format(sql,coin_1,uid);
	Mysql.Query(mysqlConn,sql,
		function( err,ret )
			if err then
				handler(err);
			else

				handler(nil);
			end
		end);
end

--修改登陆奖励信息
--handle: err
local function update_login_bonues_status( uid,handler )

	if handler ==nil then
		Debug.LogWarning("[update_login_bonues_status( uid,handler )]Handler is nil ??");
		return;
	end
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected");
		end
		return;
	end

	local sql = 'update login_bonues set status=1 where uid=%d';
	sql=string.format(sql,uid);
	Mysql.Query(mysqlConn,sql,
		function( err,ret )
			if err then
				if handler then
					handler(err);
				end
			else
				if handler then
					handler(nil);
				end
			end
		end);
end

--获取机器人信息
--handle: err,ret
local function get_robot_ugame_info(handler)
	if mysqlConn==nil then
		--数据库还没有联好
		if handler then
			handler("mysql is not connected",nil);
		end
		return;
	end
	local sql="select ucoin_1,ucoin_2,uexp,uvip,uitem_1,uitem_2,ustatus,uid from ugame where is_robot=1";
	Mysql.Query(mysqlConn,sql,function( err,ret )
		--出现错误
		if err then
			if handler then
				handler(err,nil);
			end
			return;
		end
		--没有查到数据
		if ret==nil or #ret <=0 then
--			print("no data found");
			if handler then
				handler(nil,nil);
			end
			return;
		end
		--查到数据

		local robots={};

		for k, result in pairs(ret) do
			local uinfo =
			{
				ucoin_1= tonumber(result[1]),
				ucoin_2=tonumber(result[2]),
				uexp=tonumber(result[3]),
				uvip=tonumber(result[4]),
				uitem_1=tonumber(result[5]),
				uitem_2=tonumber(result[6]),
				ustatus=tonumber(result[7]),
				uid=tonumber(result[8]),
			};
			table.insert(robots,uinfo);
		end

		if handler then
--			print("Get data :",#robots);
			handler(nil,robots);
		end
	end);
end

return {
	Connect=mysql_connect_to_game_center,
	GetUgameInfo=get_ugame_info,
	GetBonuesInfo=get_bonues_info,
	InsertBonuesInfo=insert_bonues_info,
	SetBonuesInfo=set_bonues_info,
	InsertUgameInfo=insert_ugame_info,
	UpdateLoginBonuesStatus=update_login_bonues_status,
	AddCoin1=add_coin,
	IsConnect=function() return mysqlConn~=nil end,
	GetRobotUgameInfo=get_robot_ugame_info,
}


text:
local config=require("GameConfig");
local conn=nil;

local function redis_connect_to_center()

	if conn then
		return;
	end

	local host = config.center_redis.host;
	local port = config.center_redis.port;
	local index = config.center_redis.db_index;

	Redis.Connect(host,port,
		function( err,redisConn )
			--链接错误，重连
			if err~=nil then
				Debug.LogError(err);
				Timer.Once(redis_connect_to_center,5000);
				return;
			end
			--连接成功
			conn=redisConn;
			Redis.Query(conn,"select "..index,
				function( err,ret )
					if err then
						Debug.LogError(err);
						return;
					end
					print("connect to redis [ "..index.." ] success");
				end)
		end);
end

local function set_uinfo( uid,uinfo,handler )
	if nil==conn then
		Debug.LogError("redis is not connected yet");
		return;
	end

	local cmd = "hmset moba_auth_center_user_uid_"..uid..
				" unick "..uinfo.unick..
				" usex ".. uinfo.usex..
				" uface "..uinfo.uface..
				" uvip ".. uinfo.uvip..
				" is_guest "..uinfo.is_guest;
	Redis.Query(conn,cmd,
		function ( err,ret )
			if err then
				Debug.LogError(err);
				return;
			end
			--print("[Redis]",ret);
			if handler then
				handler();
			end
		end);
end

--handler: err,uinfo
local function get_uinfo( uid,handler )
	if nil==conn then
		Debug.LogError("redis is not connected yet");
		return;
	end
	local cmd = "hgetall moba_auth_center_user_uid_"..uid;
	Redis.Query(conn,cmd,
		function ( err,ret )
			if err then
				Debug.LogError(err);
				if handler then
					handler(err,nil);
				end
				return;
			end

			if nil==ret or #ret < 10 then
				Debug.LogError("get ret error, wrong uid: ",uid,err);
				if handler then
					handler("get ret error",nil);
				end
				return;
			end

			local uinfo = {};
			uinfo.uid=uid;
			uinfo.unick=ret[2];
			uinfo.uface=tonumber(ret[4]);
			uinfo.uvip=tonumber(ret[6]);
			uinfo.usex=tonumber(ret[8]);
			uinfo.is_guest=tonumber(ret[10]);
			handler(nil,uinfo);
		end);
end

local function edit_profile( uid,unick,usex,uface )
	get_uinfo(uid,
		function ( err,uinfo )
			if err then
				Debug.LogError("redis get error");
				return;
			end
			uinfo.unick=unick;
			uinfo.usex=usex;
			uinfo.uface=uface;

			set_uinfo(uid,uinfo);
		end)
end

return {
	Connect=redis_connect_to_center,
	GetUinfo=get_uinfo,
	SetUinfo=set_uinfo,
	EditProfile=edit_profile,
	IsConnect=function() return conn~=nil end
};


text:
local config=require("GameConfig");
local conn=nil;

local function redis_connect_to_game()

	if conn then
		return;
	end

	local host = config.game_redis.host;
	local port = config.game_redis.port;
	local index = config.game_redis.db_index;

	Redis.Connect(host,port,
		function( err,redisConn )
			--链接错误，重连
			if err~=nil then
				Debug.LogError("Redis Connect Error",err);
				Timer.Once(redis_connect_to_game,5000);
				return;
			end
			--连接成功
			conn=redisConn;
			Redis.Query(conn,"select "..index,
				function( err,ret )
					if err then
						Debug.LogError(err);
						return;
					end
					print("connect to redis [ "..index.." ] success");
				end)
		end);
end

--设置用户游戏信息
local function set_ugame_info(uid,ugameinfo,handler)

--	print("flag 1");
	if conn==nil then
		--数据库还没有联好
		Debug.LogError("redis is not connected");
		return;
	end
	local cmd = "hmset moba_ugame_user_uid_"..uid..
			" ucoin_1 "..ugameinfo.ucoin_1..
			" uexp "..ugameinfo.uexp..
			" uvip ".. ugameinfo.uvip;
	Redis.Query(conn,cmd,
		function ( err,ret )
			if err then
				Debug.LogError(err);
				return;
			end
--			print("[Redis]",ret);
			if handler then
				handler();
			end
		end);
end

--获取用户游戏信息
--handler: err,ginfo
local function get_ugame_info(uid,handler)
	if nil==conn then
		Debug.LogError("redis is not connected yet");
		return;
	end
	local cmd = "hgetall moba_ugame_user_uid_"..uid;
	Redis.Query(conn,cmd,
		function ( err,ret )
			if err then
				Debug.LogError(err);
				if handler then
					handler(err,nil);
				end
				return;
			end
			local ginfo = {};
			ginfo.uid=uid;
			ginfo.ucoin_1=tonumber(ret[2]);
			ginfo.uexp=tonumber(ret[4]);
			ginfo.uvip=tonumber(ret[6]);
			handler(nil,ginfo);
		end);
end

--添加货币1
local function add_coin_1(uid,add_coin)
	get_ugame_info(uid,
		function(err,ginfo)
			if err then
				Debug.LogError(err);
				return;
			end
			ginfo.ucoin_1 = ginfo.ucoin_1 + add_coin;

			set_ugame_info(uid,ginfo);
		end);
end

return {
	Connect=redis_connect_to_game,
	IsConnect=function() return conn~=nil end,
	GetUgameInfo=get_ugame_info,
	SetUgameInfo=set_ugame_info,
	AddCoin1=add_coin_1,
};


text:
local sessionSet={};

function broadcast_except(msg,exceptSession)
    print("broad...")
    for i = 1,#sessionSet do
        if exceptSession ~= sessionSet[i] then
            print("broad !",msg[2]);
            Session.SendPackage(sessionSet[i],msg);
        end
    end
end

function OnRecvLoginReq(s)
    --当前是否已经在集合中
    for i = 1,#sessionSet do
        if s == sessionSet[i] then
            local pk = {2,2,0,{status = -1}};
            Session.SendPackage(s,pk);
            return;
        end
    end

    --加入当前集合
    --print("server welcome");
    table.insert(sessionSet,s);

    local pk = {2,2,0,{status = 1}};
    Session.SendPackage(s,pk);
    local s_ip,s_port=Session.GetAddress(s);
    local msg={2,7,0,{ip=s_ip,port=s_port}};
    broadcast_except(msg,s);
end

function OnRecvExitReq(s)
    for i = 1,#sessionSet do
        if s == sessionSet[i] then
            table.remove(sessionSet,i);
            --顺利离开聊天室
            local ip,port=Session.GetAddress(s);
            print(ip,":",port," leave chat_room");

            Session.SendPackage(s,{2,4,0,{status = 1}});

            local s_ip,s_port=Session.GetAddress(s);
            local msg={2,8,0,{ip=s_ip,port=s_port}};
            broadcast_except(msg,s);
            return;
        end
    end

    --点击离开的时候已经不再聊天室了
    Session.SendPackage(s,{2,4,0,{status = -1}});
end

function OnRecvSendReq(s,msg)
    for i = 1,#sessionSet do
        if s == sessionSet[i] then
            --顺利发送消息
            --print("server get data");
            Session.SendPackage(s,{2,6,0,{status = 1}});

            local s_ip,s_port=Session.GetAddress(s);
            local m={2,9,0,{ip=s_ip,port=s_port,content=msg[4].content}};
            broadcast_except(m,s);
            return;
        end
    end
    --发送失败
    --print("failed to send");
    Session.SendPackage(s,{2,6,0,{status = -1}});
end

return  {
    serviceType = 2,
    service = {
        OnSessionRecvCmd=function(s,msg)
            local ctype=msg[2];
            if ctype==1 then
                OnRecvLoginReq(s);
            elseif ctype==3 then
                OnRecvExitReq(s);
            elseif ctype==5 then
                OnRecvSendReq(s,msg);
            end
        end,
        OnSessionDisconnected=function(s,sType)
            local ip,port=Session.GetAddress(s);
            print(ip,":",port," leave chat_room");
            for i = 1,#sessionSet do
                if s == sessionSet[i] then
                    table.remove(sessionSet,i);
                    local s_ip,s_port=Session.GetAddress(s);
                    local msg={2,8,0,{ip=s_ip,port=s_port}};
                    broadcast_except(msg,s);
                    return;
                end
            end
        end,
    };
};


text:
copy ..\..\..\..\build\proj_win32\x64\Debug\MobaServer.exe ..\..\..\..\build\bin\
..\..\..\..\build\bin\MobaServer.exe ../ ChatRoom/chat_room_main.lua


text:
--初始化日志模块
Debug.LogInit("logger/ChatRoom","ChatRoom",true);
--初始化协议模块
local ProtoType={
    Json=0,
    Protobuf=1,
};
ProtoManager.Init(ProtoType.Protobuf,"F:\\Projects\\unity\\Moba\\Server\\apps\\lua_test\\scripts\\ChatRoom\\Const");
--如果是Protobuf协议，还需要注册映射表
if ProtoManager.ProtoType()==ProtoType.Protobuf then
    local cmdNameMap=require("ChatRoom/Const/CmdNameMap");
    if cmdNameMap then
        ProtoManager.RegisterCmdMap(cmdNameMap);
    else
    	Debug.LogError("register cmdNameMap failed");
    end
end

--开启网络服务
Netbus.TcpListen(6080);

print("start all service success");

--注册服务
----第一个参数：sType
----第二个参数：表：{OnSessionRecvCmd，OnSessionDisconnected}
local s1=require("ChatRoom/ChatServer");
local ret =  Service.Register(s1.serviceType,s1.service);
if ret then
    print("register chat_server success");
end


text:
syntax = "proto2";

enum ChatCmd {
	  INVALID_CHAT_CMD = 0;
	  eLoginReq = 1;
	  eLoginRes = 2;
    eExitReq = 3;
    eExitRes = 4;
    eSendMsgReq = 5;
    eSendMsgRes = 6;
    eOnUserLogin = 7;
    eOnUserExit = 8;
    eOnSendMsg = 9;
}

message LoginRes {
  required int32 status = 1;
}

message ExitRes{
  required int32 status = 1;
}

message SendMsgReq{
  required string content = 1;
}

message SendMsgRes{
  required int32 status = 1;
}

message OnUserLogin{
  required string ip = 1;
  required int32 port =2;
}

message OnUserExit{
  required string ip = 1;
  required int32 port =2;
}

message OnSendMsg{
  required string ip = 1;
  required int32 port = 2;
  required string content =3;
}


text:
return {
	eLoginReq = 1,
	eLoginRes = 2,
    eExitReq = 3,
    eExitRes = 4,
    eSendMsgReq = 5,
    eSendMsgRes = 6,
    eOnUserLogin = 7,
    eOnUserExit = 8,
    eOnSendMsg = 9,
};


text:
copy ..\..\..\..\build\proj_win32\x64\Debug\MobaServer.exe ..\..\..\..\build\bin\
..\..\..\..\build\bin\MobaServer.exe ../ gateway/gateway_server_main.lua


text:
--初始化日志模块
Debug.LogInit("logger/gateway","gateway",true);
--初始化协议模块
local ProtoType={
    Json=0,
    Protobuf=1,
};
Debug.Log("hello");
ProtoManager.Init(ProtoType.Protobuf,"F:\\Projects\\unity\\Moba\\Server\\apps\\lua_test\\scripts\\gateway\\Const");
--如果是Protobuf协议，还需要注册映射表
if ProtoManager.ProtoType()==ProtoType.Protobuf then
    local cmdNameMap=require("gateway/Const/CmdNameMap");
    if cmdNameMap then
        ProtoManager.RegisterCmdMap(cmdNameMap);
    else
        Debug.LogError("register cmdNameMap failed");
    end
end
--读取配置文件
local config = require("GameConfig");

--开启网关
Netbus.TcpListen(config.gateway_tcp_port
    ,function(s)
            local ip,port = Session.GetAddress(s);
            print("new client come ["..ip..":"..port.."]");
    end);
print("Gateway Server [tcp] listen at: ",config.gateway_tcp_port);

--注册服务
local servers=config.servers;
local gatewayService = require("gateway/GatewayService");
for k,v in pairs(servers) do

    --注意这里每一个服务器都注册了一个网关服务，所以，网关服务的代码会被多个服务器使用和访问
    --需注意可能引发的矛盾冲突

    local ret = Service.RegisterRaw(v.serviceType,gatewayService);
    if ret then
        print("register Gateway Service:[" .. v.serviceType .. "] service success");
    else
        print("register Gateway Service:[" .. v.serviceType .. "] service failed");
    end
end


text:
local config = require("GameConfig");
local CmdType = require("gateway/Const/CmdType");
local ServiceType = require("ServiceType");
local Responce = require("Respones");

-- stype -> session 映射表
local sessionDic={}
-- 当前正在链接的服务器
local connectingSession={};
local g_ukey=1;
local tag_sessionDic={};
local uid_sessionDic={};

--连接到指定服务器
local function ConnectToServer(stype,ip,port)
	Netbus.TcpConnect(ip,port,
		--链接成功的回调
		function(session)
			connectingSession[stype]=false;
			if not session then
				Debug.LogError("connect to server ["..config.servers[stype].descrip.."]"..ip..port.." failed, reconnecting...");
				return;
			end
			--链接成功
			print("connect to server ["..config.servers[stype].descrip.."]"..ip..port.." success");
			sessionDic[stype]=session;
		end)
end

--检查服务器的链接
local function CheckServerConnect()
	-- 检查服务的链接，每秒进行一次
	for k,v in pairs(config.servers) do
		if sessionDic[v.serviceType]==nil
			and connectingSession[v.serviceType]==false
			then
			--如果没有链接，就链接服务器
			connectingSession[v.serviceType]=true;
			ConnectToServer(v.serviceType,v.ip,v.port);
		end
	end
end

--初始化网关服务器
local function GatewayServiceInit()
	for k,v in pairs(config.servers) do
		sessionDic[v.serviceType]=nil;
		connectingSession[v.serviceType]=false;
	end
	--启动一个定时器
	Timer.Repeat(CheckServerConnect,1000,-1,5000);
end

--是否为登陆返回消息
local function _IsLoginAuthRes(stype, ctype )
	if stype ~= ServiceType.Auth then
		return false;
	end

    if CmdType.eGuestLoginRes == ctype
    or CmdType.eUserLoginRes == ctype then
        return true;
    end
    return false;
end

--是否为登陆请求
local function _IsLoginAuthReq(stype, ctype )
	if stype ~= ServiceType.Auth then
		return false;
	end
    if CmdType.eGuestLoginReq == ctype
        or CmdType.eUserLoginReq == ctype then
        return true;
    end
    return false;
end

--是否为登陆逻辑服务器的请求
local function _IsLoginLogicReq(stype, ctype)
	return stype ==ServiceType.Logic and ctype == CmdType.eLoginLogicReq;
end

-- 发给客户端
local function _server_send_to_client(s,raw)

	local stype,ctype,utag = RawCmd.ReadHeader(raw);
	--print("from server tag: ",utag);
	local clientSession = nil;
	--如果是登陆命令的回复
	if _IsLoginAuthRes(stype, ctype) then
		--print("AAA");
		--取消tag_sessionDIc中的引用
		clientSession=tag_sessionDic[utag];
		tag_sessionDic[utag]=nil;

		if clientSession==nil then
			print("clientSession is null, uTag:", utag);
			return;
		end

		local body = RawCmd.ReadBody(raw);

		--如果返回结果不正常，直接返回
		--print("status: ",body.status,"Responce.Ok",Responce.OK);
		if body.status~=Responce.OK then
			print("Responce is not ok",body.status);
			RawCmd.SetUTag(raw,0);
			Session.SendRawPackage(clientSession,raw);
			return;
		end

		local uid = body.uinfo.uid;

		--判断是否有session已经用这个id登陆
		if uid_sessionDic[uid] and uid_sessionDic[uid]~=clientSession then
			--说明重复登陆
			print("somebody relogin");
			local reloginMsg = {ServiceType.Auth,CmdType.eReLogin,0,nil};
			Session.SendPackage(uid_sessionDic[uid],reloginMsg);
			Session.Close(uid_sessionDic[uid]);
			-- uid_sessionDic[uid]=nil;
		end

		--记录uid
		--print("remember uid: ",uid);
		uid_sessionDic[uid]=clientSession;
		Session.SetUId(clientSession,uid);

		--发送回客户端
		--print("send back to client");
		body.uinfo.uid=0;
		local loginRes={stype,ctype,0,body};
		Session.SendPackage(clientSession,loginRes);
		return;
	end

	--print("BBB");
	clientSession=uid_sessionDic[utag];

	--发给用户
	if clientSession then
		--print("flag");
		--永远不要让用户知道utag
		RawCmd.SetUTag(raw,0);
		Session.SendRawPackage(clientSession,raw);

		--如果是注销消息
		if stype==ServiceType.Auth and ctype==CmdType.eUserUnregisterRes then
			Session.SetUId(clientSession,0);
			uid_sessionDic[utag]=nil;
		end
	else
		print("clientSession is null? utag: ",utag);
	end
end

-- 发给服务器
local function _client_send_to_server(s,raw)

	local stype,ctype,utag = RawCmd.ReadHeader(raw);
	local targetServerSession = sessionDic[stype];
	if nil==targetServerSession then
		print("server is null, stype:"..stype);
		return;
	end

	if _IsLoginAuthReq(stype,ctype) then
		--如果是登陆用户服务器请求
		utag = Session.GetUTag(s);
		if utag==0 then
			--print("is loginRequest");
			utag=g_ukey;
			g_ukey=g_ukey+1;
			Session.SetUTag(s,utag);
		end
		tag_sessionDic[utag]=s;
		--print("get utag: ",utag);
	elseif _IsLoginLogicReq(stype,ctype) then
		--如果是登陆逻辑服务器请求
		utag = Session.GetUId(s);
		if uid==0 then
			--该操作需要先登陆
			print("you need to login first");
			return;
		end

		local ip,port=Session.GetAddress(s);
		local body=RawCmd.ReadBody(raw);
		body.ip=ip;
	else
		utag = Session.GetUId(s);
		if utag==0 then
			--该操作需要先登陆
			print("you need to login first");
			return;
		end
		--uid_sessionDic[uid]=s;
	end

	--打上utag然后发给我们的服务器
	--print("client tag: "..utag.." send to server")
	RawCmd.SetUTag(raw,utag);
	Session.SendRawPackage(targetServerSession,raw);
end

GatewayServiceInit();

return   {
    OnSessionRecvRaw=function(s,raw)
    	if Session.IsClient(s) then
    		_server_send_to_client(s,raw);
    	else
		    _client_send_to_server(s,raw);
    	end
    end,
    OnSessionDisconnected=function(s,stype)
    	if Session.IsClient(s) then
    		for k,v in pairs(sessionDic) do
    			if v==s then
    				Debug.LogError("disconnected from: ["..config.servers[k].descrip.."]");
    				sessionDic[k]=nil;
    			end
    		end
    		return;
    	else
    		--print("client leave");

            local ip,port = Session.GetAddress(s);
            print("client ["..ip..":"..port.."] leave");

    		--1.把客户端从utag临时映射表删除
    		local utag = Session.GetUTag(s);
    		if tag_sessionDic[utag] and tag_sessionDic[utag]==s then
    			tag_sessionDic[utag]=nil;
    			Session.SetUTag(s,0);
    		end
            --2.把客户端从uid映射表删除
    		local uid = Session.GetUId(s);
    		if uid_sessionDic[uid] and uid_sessionDic[uid]==s then
    			uid_sessionDic[uid]=nil;
                --这里不能把用户uid值为零，用户注册的每个服务都会走这段代码
                --如果第一次我们就把uid置为零，后面走这段代码的服务器就不知道
                --是哪个用户掉线了
    			--Session.SetUId(s,0);
    			--table.remove(uid_sessionDic,uid);
    		end

            local targetServer = sessionDic[stype];
            if nil==targetServer then
                return;
            end

            --客户端uid用户掉线，我要把这个事件告诉中转服务器
            if uid~=0 then
                local userLostMsg = {stype,CmdType.eUserLostConn,uid,nil};
                Session.SendPackage(targetServer,userLostMsg);
            end
    	end
    end,
};


text:
syntax = "proto2";

message GuestLoginReq{
	required string guest_key = 1;
}
message UserCenterInfo{
	 required string unick = 1;
	 required int32 uface = 2;
	 required int32 usex = 3;
	 required int32 uvip = 4;
	 required int32 uid = 5;
}
message GuestLoginRes{
	required int32 status = 1;
	optional UserCenterInfo uinfo = 2;
}
message UserLoginReq{
	required string uname = 1;
	required string upwd_md5 = 2;
}

message UserLoginRes{
	required int32 status = 1;
	optional UserCenterInfo uinfo = 2;
}

message LoginLogicReq{
	required string ip = 1;
	required int32 udp_port = 2;
}


text:
--
-- Created by IntelliJ IDEA.
-- User: ReadyGamerOne
-- Date: 2020/3/1
-- Time: 11:16
-- To change this template use File | Settings | File Templates.
--
local ServiceType = require("ServiceType");

local cmdNameMap={};
cmdNameMap[ServiceType.Logic]={
	LoginLogicReq = 1,
};

cmdNameMap[ServiceType.Auth]={
	UserLostConn = 0,
	GuestLoginReq = 1,
	GuestLoginRes = 2,
	ReLogin = 3,
	UserLoginReq = 9,
	UserLoginRes = 10,
	UserUnregisterRes = 12,
}

return cmdNameMap;


text:
--
-- Created by IntelliJ IDEA.
-- User: ReadyGamerOne
-- Date: 2020/3/1
-- Time: 10:34
-- To change this template use File | Settings | File Templates.
--

return {
	eUserLostConn = 0,
	eGuestLoginReq = 1,
	eGuestLoginRes = 2,
	eReLogin = 3,
	eUserLoginReq = 9,
	eUserLoginRes = 10,
	eUserUnregisterRes = 12,
	eLoginLogicReq = 1,
}


text:
--初始化日志模块
Debug.LogInit("logger/logic","logic",true);
--初始化协议模块
local ProtoType={
    Json=0,
    Protobuf=1,
};

ProtoManager.Init(ProtoType.Protobuf,"F:\\Projects\\unity\\Moba\\Server\\apps\\lua_test\\scripts\\logic\\Const");
--如果是Protobuf协议，还需要注册映射表
if ProtoManager.ProtoType()==ProtoType.Protobuf then
    local cmdNameMap=require("logic/Const/CmdNameMap");
    if cmdNameMap then
        ProtoManager.RegisterCmdMap(cmdNameMap);
    else
    	Debug.LogError("register cmdNameMap failed");
    end
end

--读取配置文件
local config = require("GameConfig");
local sType = require("ServiceType");

local servers=config.servers;

--开启服务器
Netbus.TcpListen(servers[sType.Logic].port
    ,function(s)
        local ip,port = Session.GetAddress(s);
        print("new client come ["..ip..":"..port.."]");
    end);
Netbus.UdpListen(config.logic_udp.port);

print("Logic server [tcp] listen at: ",servers[sType.Logic].port);

--注册服务
local authService = require("logic/LogicService");
local ret = Service.Register(sType.Logic,authService);
if ret then
    print("register Logic Service:[" .. sType.Logic .. "] service success");
else
    print("register Logic Service:[" .. sType.Logic .. "] service failed");
end


text:
local mysql = require("datebase/mysql_game");
local Respones = require("Respones");
local RoomState = require("logic/Const/RoomState");
local Redis = require("datebase/redis_center");

local Player={};

function Player:New(instant)
	if not instant then
		instant={};
	end

	setmetatable(instant, self)
	self.__index = self

	return instant;
end

function Player:Init(uid,s,handler)

	self.session=s;
	self.uid=uid;
	--不再任何游戏场
	self.zoneid=-1;
	--不在任何房间
	self.roomid=-1;
	--默认旁观状态
	self.state=RoomState.InView;
	--默认不是机器人
	self.isRobot=false;
	--座位号
	self.seatid=-1;
	--那一边,0 左边，1 右边
	self.side=-1;
	--玩家英雄id
	self.heroid=-1;
	--玩家客户端ip
	self.clientIp=-1;
	--玩家udpPort
	self.clientPort=-1;

	--玩家同步到哪一帧
	self.sync_frame=0;

	--从数据库读取玩家信息
	mysql.GetUgameInfo(uid,
		function ( err,info )
			if err then
				Debug.LogError(err);
				if handler then
					handler(Respones.SystemError);
				end
				return;
			end

			self.gameInfo=info;

			Redis.GetUinfo(self.uid,function ( err,uinfo )
				if err then
					Debug.LogError(err);
					if handler then
						handler(Respones.SystemError);
					end
					return;
				end
				self.uinfo=uinfo;
				if handler then
					handler(Respones.OK);
				end
			end);
		end);
end

function Player:SetSession( s )
	self.session=s;
end

function Player:SendPackage(sType,cType,body)
	if not self.session or self.isRobot then
		return;
	end

	local package = {sType,cType,self.uid,body};
	Session.SendPackage(self.session,package);
end

function Player:UdpSendPackage(sType,cType,body)
	--玩家已经断线或是机器人
	if not self.session or self.isRobot then
		return;
	end

	if self.clientPort==-1 or self.clientIp==-1 then
		return;
	end

	local msg= {sType,cType,0,body};
	Session.UdpSendPackage(self.clientIp,self.clientPort,msg);
end
function Player:GetInfo()
	return {
		unick = self.uinfo.unick,
		uface = self.uinfo.uface,
		usex = self.uinfo.usex,
		seatid = self.seatid,
		side=self.side,
	};
end

function Player:OnEnterRoom(roomid,seatid)
	self.roomid=roomid;
	self.seatid=seatid;
	self.state=RoomState.InView;

	--分边，是否是偶数
	local num1,num2=math.modf(seatid/2)--返回整数和小数部分
	if(num2==0)then
		self.side=-1;
	else
		self.side=1;
	end

end

function Player:OnExitRoom()
	self.roomid=-1;
	self.zoneid=-1;
	self.seatid=-1;
	self.side=-1;
	self.heroid=-1;
	self.sync_frame=0;
	self.state=RoomState.InView;
end

function Player:SetAddr(ip,port)
	self.clientIp=ip;
	self.clientPort=port;
end

return Player;


text:
--
-- Created by IntelliJ IDEA.
-- User: ReadyGamerOne
-- Date: 2020/2/24
-- Time: 19:56
-- To change this template use File | Settings | File Templates.
--

local Player=require("logic/Player");

local RobotPlayer=Player:New();

function RobotPlayer:Init(uid,s,handler)
	--调用基类Init
	Player.Init(self,uid,s,handler);

	self.isRobot=true;
end

return RobotPlayer;


text:
local ServiceType = require("ServiceType");
local CmdType = require("logic/Const/CmdType");
local Respones = require("Respones");
local Player = require("logic/Player");
local Zone = require("logic/Const/Zone");
local RoomState = require("logic/Const/RoomState");

local g_roomId = 1;
local PlayerNum = 3;
local LOGIC_FRAME_TIME = 50; -- 15 FPS

local roomMgr={};

function roomMgr:New(instant)
	if not instant then
		instant={};
	end

	setmetatable(instant,{__index=self});

	return instant;
end

function roomMgr:Init(zoneid)
	--区域ID
	self.zoneid=zoneid;
	--房间ID
	self.roomid=g_roomId;
	g_roomId=g_roomId+1;

	--集结状态
	self.state=RoomState.InView;

	--正在集结列表
	self.inviewPlayers={};
	self.leftPlayers={};
	self.rightPlayers={};

	--当前帧id
	self.frameid=1;
	--所有帧操作
	self.allframes={};
	--当前帧操作
	self.nextframe={
		frameid = self.frameid,
		opts = {},
	};

	print("room init ",self.roomid,self.zoneid);
end

function roomMgr:send_unsync_frames( player )
	local frames = {};

	for i = (player.sync_frame+1), #self.allframes do
		table.insert(frames,self.allframes[i]);
	end
--	print("player.frame:"..player.sync_frame.."self.frameid"..self.frameid.."#self.allframes:"..#self.allframes);
	local body = {frameid=self.frameid,unsync_frames=frames}
	player:UdpSendPackage(ServiceType.Logic,CmdType.eLogicFrame,body);
end

--帧同步函数
function roomMgr:on_frame_synce()

	table.insert(self.allframes,self.nextframe);
--	print("self.frameid "..self.frameid.." #self.allframe: "..#self.allframes);

	for k, player in pairs(self.inviewPlayers) do
		self:send_unsync_frames(player);
	end

	self.frameid=self.frameid+1;
	--当前帧操作
	self.nextframe={
		frameid = self.frameid,
		opts = {},
	};
end
--广播TCP消息
function roomMgr:BroadcastInviewPlayers(stype,ctype,body,except)
	for i,player in ipairs(self.inviewPlayers) do
		if not player.isRobot and player~=except then
			player:SendPackage(stype,ctype,body);
		end
	end
end
--添加玩家
function roomMgr:AddPlayer( player )

	if not player then
		print("AddPlayer: Player is nil");
		return;
	end

	if #self.inviewPlayers>=2*PlayerNum then
		Debug.LogError("room has full");
		return;
	end
--	print("add player: ",player.uinfo.unick);

	if self.state~=RoomState.InView
		or player.roomid~=-1
		or player.state~=RoomState.InView then
		return false;
	end

	--获取座位号
	local index;
	for index = 1, PlayerNum*2 do
		if self.inviewPlayers[index]==nil then
			self.inviewPlayers[index]=player;
			player:OnEnterRoom(self.roomid,index);
			break;
		end
	end

	if not player.isRobot then

		--告诉客户端进入房间
		player:SendPackage(ServiceType.Logic,CmdType.eEnterRoom,
			{
				zoneid=player.zoneid,
				roomid=player.roomid,
				seatid=player.seatid,
				side=player.side,
			});
		--告诉客户端在它之前已经有一些人进来了
		for k,p in pairs(self.inviewPlayers) do
			if p~=player then
				player:SendPackage(
					ServiceType.Logic,
					CmdType.ePlayerEnterRoom,
					p:GetInfo());
			end
		end

	end
	--end

	--告诉房间内其他客户端有人进来
	self:BroadcastInviewPlayers(
		ServiceType.Logic,
		CmdType.ePlayerEnterRoom,
		player:GetInfo(),
		player);

	--是否集结结束
	if #self.inviewPlayers >= PlayerNum*2 then
		self:StartGame();
	end

	return true;
end
--移除玩家
function roomMgr:RemovePlayer(player)
	if not player then
		Debug.LogError("player is nil");
		return;
	end

	--从列表中移除player
	self.inviewPlayers[player.seatid]=nil;

	--告诉其他人有人离开
	self:BroadcastInviewPlayers(
		ServiceType.Logic,
		CmdType.ePlayerExitRoom,
		{seatid=player.seatid},
		player);

	--离开成功
	player:SendPackage(
		ServiceType.Logic,
		CmdType.eExitRoomRes,
		{status=Respones.OK,});

	player:OnExitRoom();
end
--开始游戏
function roomMgr:StartGame()

	--房间进入Ready状态
	self.state=RoomState.Playing ;
	--更新玩家状态
	for i,p in ipairs(self.inviewPlayers) do
		p.state=RoomState.Playing;
	end

	--可能进入选择英雄界面，直到所有玩家选择完毕
	--我们随机选择【大乱斗】
	local heros={};
	for k, player in pairs(self.inviewPlayers) do
		local randomHeroId=math.floor(math.random()*5+1);
		local info =
		{
			heroid = randomHeroId,
			seatid = player.seatid,
			side   = player.side,
		}
		table.insert(heros,info);
	end

	--广播消息游戏开始
	self:BroadcastInviewPlayers(
		ServiceType.Logic,
		CmdType.eGameStart,
		{players=heros});

	--5 秒以后开始第一个逻辑帧
	self.frameTimer=Timer.Repeat(
	function()
		self:on_frame_synce();
	end,5000,-1,LOGIC_FRAME_TIME);
end
--收到客户端下一帧消息
function roomMgr:OnNextFrame(nextFrameOpts)
	--是否过时
	if nextFrameOpts.frameid~=self.frameid then
		return;
	end
	--是否内容无效
	if #nextFrameOpts.opts <= 0 then
		Debug.LogError("get empty NextFrameOpts");
		return;
	end
	--是否不对应玩家
	local player = self.inviewPlayers[nextFrameOpts.seatid];
	if not player then
		Debug.LogError("OnNextFrame_get player nil, seatid:",nextFrameOpts.seatid);
		return;
	end

	--更新客户端同步到帧数
	if player.sync_frame< nextFrameOpts.frameid-1 then
		player.sync_frame=nextFrameOpts.frameid-1;
	end

	--加入到当前帧操作
	for k, opt in pairs(nextFrameOpts.opts) do
		table.insert(self.nextframe.opts,opt);
	end

--	print("player.frame: "..player.sync_frame.."nextframe.opts:"..#self.nextframe.opts.."self.frameid"..self.frameid.."#self.allframes:"..#self.allframes);
end

return roomMgr;


text:
local ServiceType = require("ServiceType");
local CmdType = require("logic/Const/CmdType");
local Respones = require("Respones");
local Player = require("logic/Player");
local RobotPlayer =require("logic/RobotPlayer");
local Zone = require("logic/Const/Zone");
local MatchMgr = require("logic/MatchMgr");
local RoomState = require("logic/Const/RoomState");
local CenterRedis = require("datebase/redis_center");
local GameRedis = require("datebase/redis_game");
local CenterMysql = require("datebase/mysql_center");
local GameMysql = require("datebase/mysql_game");

--在线玩家【uid->Player】
local onlinePlayers = {};
local onlinePlayerCount = 0;
--地区等待列表【zoneWaitingPlayerList[Zone.Sgyd]={},uid->Player]
local zoneWaitingPlayerList = {};
zoneWaitingPlayerList[Zone.Sgyd]={};
zoneWaitingPlayerList[Zone.Assy]={};
--机器人列表
local zoneRobotList={};
zoneRobotList[Zone.Sgyd]={};
zoneRobotList[Zone.Assy]={};
--当前房间列表【roomList[Zone.Sgyd]={},uid->room]
local zoneRoomList = {};
zoneRoomList[Zone.Sgyd]={};
zoneRoomList[Zone.Assy]={};

local _robot_game_infos_={};
local _robot_load_index_=1;

--实例化机器人对象
local function _do_new_robotplayers(robots)
	local halfLen = math.floor(#robots * 0.5);

	local index = 0;
	for k, robot in pairs(robots) do
		local rp = RobotPlayer:New();
		rp:Init(robot.uid,nil,nil);

		if index < halfLen then
			zoneRobotList[Zone.Sgyd][robot.uid]=rp;
		else
			zoneRobotList[Zone.Assy][robot.uid]=rp;
		end

		index = index + 1;
	end
end

--加载机器人角色信息到Redis【uinfo】
local function _do_load_robot_uinfo(uid)
	CenterMysql.GetUinfoByUid(uid,
		function(err,uinfo)
			if err or not uinfo then
				Debug.LogError(err);
				return;
			end

			--设置到Redis中去
			CenterRedis.SetUinfo(uid,uinfo,
				function()
					--加载完最后一个的时候开始实例化机器人对象
					if _robot_load_index_ == #_robot_game_infos_ then
						_do_new_robotplayers(_robot_game_infos_);
						_robot_game_infos_=nil;
					end
					_robot_load_index_ = _robot_load_index_ + 1;
				end);
		end);
end

--加载机器人游戏信息Redis【ginfo】
local function _do_load_robot_ugame_info()
--	print("_do_load_robot_ugame_info");
	GameMysql.GetRobotUgameInfo(
	function(err,robots)
		if err then
			Debug.LogError(err);
			return;
		end
		if not robots or #robots<=0 then
			Debug.LogError("not robots or #robots<=0");
			return;
		end

		--保存到全局变量
		_robot_game_infos_=robots;

		--写入Redis
		for k, robot in pairs(robots) do
			GameRedis.SetUgameInfo(robot.uid,robot,
				function()
					_do_load_robot_uinfo(robot.uid);
				end);
		end
	end
	);
end

--加载机器人信息
local function _load_robots()
--	print("_load_robots");
	if not CenterRedis.IsConnect() or
	   not GameRedis.IsConnect() or
	   not CenterMysql.IsConnect() or
	   not GameMysql.IsConnect() then
	   Timer.Once(_load_robots,5000);
		return;
	end
	_do_load_robot_ugame_info();

end

--获取空闲机器人
local function _search_idle_robot(zid)
	for k, robot in pairs(zoneRobotList[zid]) do
		if robot.roomid==-1 then
			return robot;
		end
	end

	return nil;
end

--把空闲机器人匹配到房间中去
local function _do_push_robots_to_room()
	for zid, roomList in pairs(zoneRoomList) do
--		print("zoneType",zid,"rooms",#roomList);
		for roomid, room in pairs(roomList) do
			if room.state==RoomState.InView then

--				print("find room",room.roomid,roomid);
				--找一个空闲机器人
				local robot=_search_idle_robot(zid);
				if robot then
					room:AddPlayer(robot);
--					test
--					Timer.Once(function()
--						room:RemovePlayer(robot)
--					end,5000);
				end
			end
		end
	end
end

--查询还在集结状态的房间，没有就创建
local function _search_inview_room( zoneType )
	local targetRooms = zoneRoomList[zoneType];

	for roomid,room in ipairs(targetRooms) do
		if room.state==RoomState.InView then
			return room;
		end
	end

	--没有就创建房间
	local room = MatchMgr:New();
	room:Init(zoneType);

	if not room then
		Debug.LogError("create room failed");
		return;
	elseif not room.roomid then
		Debug.LogError("room.roomid is nil");
		return;
	end

	targetRooms[room.roomid]=room;

	print("create room",room.roomid);

	return room;
end

--匹配玩家
local function _do_match_players()

	for zoneType,waitlist in pairs(zoneWaitingPlayerList) do
		if not waitlist then
			Debug.LogError("waiting list is null，zoneType: ",zoneType);
			return;
		end

		for uid,player in pairs(waitlist) do
			local room = _search_inview_room(zoneType);
			if room then
				if not room:AddPlayer(player) then
					Debug.LogError("player enter room error,player.state",player.state,"room.state",room.state);
				end

				--进入房间成功，将玩家移出等待队列
				waitlist[player.uid]=nil;
			end
		end
	end

end

--返回区域ID的合法性
local function _check_zoneid( zid )
	for k,v in pairs(Zone) do
		if v==zid then
			return true;
		end
	end
	return false;
end

--发送消息
local function _send_status( s,stype,ctype,uid,_status)

	local msg = {stype,ctype,uid,
	{
		status=_status,
	}};
	Session.SendPackage(s,msg);
end

--玩家登陆请求
local function on_login_logic_req( s,req )
	local uid = req[3];
	local stype = req[1];
	local p = onlinePlayers[uid];
	local msg=req[4];

	--玩家已经存在
	if p then
		--更新session
		p:SetSession(s);
		p:SetAddr(msg.ip,msg.udp_port);
		_send_status(s,stype,CmdType.eLoginLogicRes,uid,Respones.OK);
		return;
	end

	--保存Player对象
	p=Player:New();
	p:Init(uid,s,function ( status )
		--出错
		if status~=Respones.OK then
			_send_status(s,stype,CmdType.eLoginLogicRes,uid,status);
			return;
		end
		--成功
		onlinePlayers[uid]=p;
		onlinePlayerCount=onlinePlayerCount+1;
		print("Player [ "..uid.." ] come in");
		_send_status(s,stype,CmdType.eLoginLogicRes,uid,Respones.OK);
		p:SetAddr(msg.ip,msg.udp_port);
	end);
end

--玩家短线
local function on_player_lost_conn( s,req )
	local uid = req[3];

	local p = onlinePlayers[uid];
	if not p then
		return;
	end

	p:SetSession(nil);
	p:SetAddr(-1,-1);
	--游戏中的玩家
	if p.zoneid~=-1 then
		--是否在地区中
		if zoneWaitingPlayerList[p.zoneid][uid]
			and zoneWaitingPlayerList[p.zoneid][uid]==p then
			--移除玩家
			zoneWaitingPlayerList[p.zoneid][uid]=nil;
			p.zoneid=-1;
		end
	end

	--玩家下线
	print("Player [ "..uid.." ] leave");
	onlinePlayers[uid]=nil;
	onlinePlayerCount=onlinePlayerCount-1;
end

--网关掉线
local function on_gateway_disconnect(s)
	local k,v;
	for k,v in pairs(onlinePlayers) do
		v:SetSession(nil);
	end
end

--网关连接
local function on_gateway_connect( s,stype )
	print("gateway connect to logic");
	local k,v;
	for k,v in pairs(onlinePlayers) do
		v:SetSession(s);
	end

end

--进入区域
local function enter_zone( s,req )
	local stype = req[1];
	local uid = req[3];

	local p = onlinePlayers[uid];
	--检查Player以及所在区域的合法性
	if not p then
		Debug.LogError("player is null ???");
		_send_status(s,stype,CmdType.eEnterZoneRes,uid,Respones.InvalidOprate);
		return;
	elseif p.zoneid~=-1 then
		Debug.LogError("player has entered zone: ",p.zoneid);
		_send_status(s,stype,CmdType.eEnterZoneRes,uid,Respones.InvalidOprate);
		return;
	end

	local zid = req[4].zoneid;
	if not _check_zoneid(zid) then
		_send_status(s,stype,CmdType.eEnterZoneRes,uid,Respones.InvalidParams);
		return;
	end

	--加入等待列表
	if not zoneWaitingPlayerList[zid] then
		zoneWaitingPlayerList[zid]={};
	end

	zoneWaitingPlayerList[zid][uid]=p;
	p.zoneid=zid;
	_send_status(s,stype,CmdType.eEnterZoneRes,uid,Respones.OK);
	return;
end

--离开房间
local function on_player_exit_room(s,req)
	local uid = req[3];
	local player = onlinePlayers[uid];
	if not player or player.roomid==-1 or player.zoneid==-1 or player.seatid==-1 or player.state~=RoomState.InView
		then
		_send_status(s,req[1],CmdType.eExitRoomRes,Respones.InvalidOprate);
		return;
	end

	local room = zoneRoomList[player.zoneid][player.roomid];
	if not room or room.state~=RoomState.InView then
		_send_status(s,req[1],CmdType.eExitRoomRes,Respones.InvalidOprate);
		return;
	end

	room:RemovePlayer(player);

end

--Udp测试消息处理
local function on_udp_test(s,req)
	print("get conent: ", req[4].content);
	Session.SendPackage(s,req);
end

local function on_next_frame_opts(s,req)
	local stype = req[1];
	local uid = req[3];
	local body = req[4];

	local zone = zoneRoomList[body.zoneid];
	if not zone then
		Debug.LogError("zone is invalid: ",body.zoneid);
		return;
	end
	local room =zone[body.roomid];
	if not room then
		Debug.LogError("room is invalid: ",body.zoneid.." : "..body.roomid);
		return;
	end

	if room.state~=RoomState.Playing then
		Debug.LogError("room state wrong: "..room.state);
		return;
	end

	room:OnNextFrame(body);
end

GameRedis.Connect();
GameMysql.Connect();
CenterRedis.Connect();
CenterMysql.Connect();

--加载机器人信息
_load_robots();

--启动一个定时器，定期匹配玩家
Timer.Repeat(_do_match_players,1000,-1,5000);
--定期匹配机器人
Timer.Repeat(_do_push_robots_to_room,1000,-1,2000);

return{
	OnLoginLogicReq     =   on_login_logic_req,
	OnPlayerLostConn    =   on_player_lost_conn,
	OnGatewayLostConn   =   on_gateway_disconnect,
	OnGatewayConn       =   on_gateway_connect,
	EnterZone           =   enter_zone,
	OnPlayerExitRoom    =   on_player_exit_room,
	OnUdpTest           =   on_udp_test,
	OnNextFrameOpts		=	on_next_frame_opts,
};


text:
copy ..\..\..\..\build\proj_win32\x64\Debug\MobaServer.exe ..\..\..\..\build\bin\
..\..\..\..\build\bin\MobaServer.exe ../ logic/logic_server_main.lua


text:
local cType=require("logic/Const/CmdType");
local gameMgr = require("logic/GameMgr");

local cTypeToCallback = {};
cTypeToCallback[cType.eLoginLogicReq]=gameMgr.OnLoginLogicReq;
cTypeToCallback[cType.eEnterZoneReq]=gameMgr.EnterZone;
cTypeToCallback[cType.eUserLostConn]=gameMgr.OnPlayerLostConn;
cTypeToCallback[cType.eExitRoomReq]=gameMgr.OnPlayerExitRoom;
cTypeToCallback[cType.eUdpTest]=gameMgr.OnUdpTest;
cTypeToCallback[cType.eNextFrameOpts]=gameMgr.OnNextFrameOpts;

return   {
    OnSessionRecvCmd=function(s,msg)
        --{sType,cType,utag,body}
        if cTypeToCallback[msg[2]] then
            cTypeToCallback[msg[2]](s,msg);
        else
            Debug.LogError("unregistered cmdType: ",msg[2]);
        end
    end,
    OnSessionDisconnected=function(s,sType)
    	local ip,port = Session.GetAddress(s);
    	print("client ["..ip..":"..port.."] leave");
    end,

    OnSessionConnected=function ( s,sType )
        gameMgr.OnGatewayConn(s,sType);
    end
};


text:
return {
	InView = 1,--集结，自由进出
	Ready = 2,
	Start = 3,
	Playing = 4,--游戏中
	Checkout =5,--游戏结算
};


text:
local ServiceType = require("ServiceType");

local cmdNameMap={};

cmdNameMap[ServiceType.Logic]={
	UserLostConn = 0,
	LoginLogicReq = 1,
	LoginLogicRes = 2,
	EnterZoneReq = 3,
	EnterZoneRes = 4,
	EnterRoom = 5,
	PlayerEnterRoom = 6,
	PlayerExitRoom = 7,
	ExitRoomReq = 8,
	ExitRoomRes = 9,
	GameStart = 10,
	UdpTest=11,
	LogicFrame = 12,
	NextFrameOpts = 13,
};

return cmdNameMap;


text:
return {
	eUserLostConn = 0,
	eLoginLogicReq = 1,
	eLoginLogicRes = 2,
	eEnterZoneReq = 3,
	eEnterZoneRes = 4,
	eEnterRoom = 5,
	ePlayerEnterRoom = 6,
	ePlayerExitRoom = 7,
	eExitRoomReq = 8,
	eExitRoomRes = 9,
	eGameStart = 10,
	eUdpTest=11,
	eLogicFrame = 12,
	eNextFrameOpts = 13,
};


text:
return {
	Sgyd = 1,
	Assy = 2,
};


text:
syntax = "proto2";

enum LogicCmd{
	Invalid_Logic_Cmd = 0;
	eLoginLogicReq = 1;
	eLoginLogicRes = 2;
	eEnterZoneReq = 3;
	eEnterZoneRes = 4;
	eEnterRoom = 5;
	ePlayerEnterRoom = 6;
	ePlayerExitRoom = 7;

	eExitRoomReq = 8;
	eExitRoomRes = 9;

	eGameStart = 10;

	eUdpTest = 11;
	eLogicFrame = 12;
	eNextFrameOpts = 13;
}

//每帧某个客户端操作合集
message OptionEvent{
	required int32 seatid = 1;
	required int32 optype = 2;
	optional int32 x = 3;
	optional int32 y = 4;
}

//每帧所有客户端操作合集
message FrameOptionEvent{
	required int32 frameid = 1;
	repeated OptionEvent opts = 2;
}

//每帧服务器广播的消息
message LogicFrame{
	//当前frameid
	required int32 frameid = 1;
	//客户端所有还没有同步的帧
	repeated FrameOptionEvent unsync_frames = 2;
}

//下一帧的操作
message NextFrameOpts{
	required int32 frameid = 1;
	repeated OptionEvent opts = 2;
	required int32 seatid = 3;
	required int32 zoneid = 4;
	required int32 roomid = 5;
}

message UdpTest{
	required int32 content = 1;
}

message LoginLogicReq{
	required string ip = 1;
	required int32 udp_port = 2;
}

message LoginLogicRes{
	required int32 status = 1;
}

message EnterZoneReq{
	required int32 zoneid = 1;
}

message EnterZoneRes{
	required int32 status = 1;
}

message EnterRoom{
	required int32 zoneid = 1;
	required int32 roomid = 2;
	required int32 seatid = 3;
	required int32 side = 4;
}

message PlayerEnterRoom{
	required string unick = 1;
	required int32 uface = 2;
	required int32 usex = 3;
	required int32 seatid = 4;
	required int32 side = 5;
}

message PlayerExitRoom{
	required int32 seatid = 1 ;
}

message EnterRoomRes{
	required int32 status = 1;
}

message ExitRoomRes{
	required int32 status = 1;
}

message PlayerMatchInfo{
	required int32 heroid = 1;
	required int32 seatid = 2;
	required int32 side = 3;
}

message GameStart{
	repeated PlayerMatchInfo players = 1;
}


text:
Debug.LogInit("ClientLogs","client",true);
Debug.Log("hello");
Netbus.TcpConnect("121.196.178.141",6080,
		--链接成功的回调
        function(session)
            if session then
                print("succeed to connect to [121.196.178.141:6080]");

                return;
            end
            print("failed to connect to [121.196.178.141:6080]")
        end);


text:
copy ..\..\..\..\build\proj_win32\x64\Debug\MobaServer.exe ..\..\..\..\build\bin\
..\..\..\..\build\bin\MobaServer.exe ../ Debug/Debug.lua


text:
#include "pf_cmd_map.h"
#include "../../netbus/protocol/CmdPackageProtocol.h"
#include <map>
#include <string>
using std::map;
using std::string;

static map<int, string> cmd_map = {
	{0,"LoginReq"},
	{1,"LoginRes"}
};

void InitPfCmdMap()
{
	CmdPackageProtocol::RegisterProtobufCmdMap(cmd_map);
}


text:
#include<iostream>
#include<string>
#include "../../netbus/Netbus.h"
#include "../../utils/timer/time_list.h"
#include "../../utils/logger/logger.h"
#include "../../utils/timestamp/timestamp.h"
#include "../../database/mysqlwarpper.h"
#include "../../database/rediswarpper.h"

#include "proto/game.pb.h"
#include "pf_cmd_map.h"
using namespace std;

#pragma region 回调

static void on_redis_query(const char* err, RedisReply* result)
{
	if (err)
	{
		log_debug("RedisError: %s", err);
		return;
	}

	log_debug("RedisQuery: %s", result->reply->str);
}

static void on_mysql_connect(const char* error,MysqlContext* context)
{
	if (error)
	{
		log_debug("Database error:\t%s",error);
		return;
	}
	log_debug("mysql connect successful");

	mysql_wrapper::query(
		(MysqlContext*)context,
		(char*)"update test_class set name = 'hello' where id = 1");

	mysql_wrapper::close((MysqlContext*)context);
}

static void on_redis_connect(const char* err, RedisContext* context)
{
	if (err)
	{
		log_debug("RedisError: %s", err);
		return;
	}
	log_debug("redis connect success");
	redis_wrapper::query(context, (char*)"select 12", on_redis_query);
	//redis_wrapper::close_redis((RedisContext *) context);
}

#pragma endregion

#pragma region 模块测试

static void TestTimer(void* data)
{
	log_debug("当前时间戳 %d", timestamp());
	log_debug("今天零时时间戳 %d", timestamp_today());
	log_debug("时间字符串转时间戳: %d", date2timestamp("%Y-%m-%d %H:%M:%S", "2020-02-01 00:00:00"));

	static char outBuf[64];
	timestamp2date(timestamp_today(), "%Y-%m-%d %H:%M:%S", outBuf, sizeof(outBuf));
	log_debug(outBuf);
}

static void TestMySql()
{
	mysql_wrapper::connect(
		(char*)"127.0.0.1",
		3306,
		(char*)"test_mysql",
		(char*)"root",
		(char*)"Zzl5201314...",
		on_mysql_connect);
}

static void TestRedis()
{
	redis_wrapper::connect(
		(char*)"127.0.0.1",
		7999,
		on_redis_connect);

}
#pragma endregion

int main(int argc, char** argv)
{
	InitPfCmdMap();
	Netbus::Instance()->Init();
	logger::init("logger", "netbus_log", true);

	//TestMySql();
	//TestRedis();
	//schedule(TestTimer, NULL, 3000, 4);

	Netbus::Instance()->TcpListen(6080);
	Netbus::Instance()->WebSocketListen(8001);
	Netbus::Instance()->UdpListen(8002);

	Netbus::Instance()->Run();

	system("pause");
	return 0;
}


text:
#ifndef __PF_CMD_MAP_H__
#define __PF_CMD_MAP_H__

//注册数字到Message名字的映射
void InitPfCmdMap();

#endif // !__PF_CMD_MAP_H__


text:
#pragma once
#ifndef __Encoding_H__
#define __Encoding_H__
#include <windows.h>
#include <string>
#include <iostream>
#include <locale>
#include <codecvt>
#include <fstream>
using namespace std;
class Encoding
{
public:
	static string UnicodeToUTF8(const wstring& sour);
	static wstring UTF8ToUnicode(const string& sour);
	static wstring AsciiToUnicode(const string& sour);
	static string UnicodeToAscii(const wstring& sour);
	static string AsciiToUTF8(const string& sour);
	static string UTF8ToAscii(const string& sour);
};
#endif // !__Encoding_H__


text:
#include "Encoding.h"

string Encoding::UnicodeToUTF8(const wstring& sour)
{
	std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
	return conv.to_bytes(sour);
}

wstring Encoding::UTF8ToUnicode(const string& sour)
{
	std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
	return conv.from_bytes(sour);
}

wstring Encoding::AsciiToUnicode(const string& sour)
{
	int unicodeLen = MultiByteToWideChar(CP_ACP, 0, sour.c_str(), -1, nullptr, 0);
	wchar_t* pUnicode = (wchar_t*)malloc(sizeof(wchar_t) * unicodeLen);
	MultiByteToWideChar(CP_ACP, 0, sour.c_str(), -1, pUnicode, unicodeLen);
	std::wstring ret_str = pUnicode;
	free(pUnicode);
	return ret_str;
}

string Encoding::UnicodeToAscii(const wstring& sour)
{
	int ansiiLen = WideCharToMultiByte(CP_ACP, 0, sour.c_str(), -1, nullptr, 0, nullptr, nullptr);
	char* pAssii = (char*)malloc(sizeof(char) * ansiiLen);
	WideCharToMultiByte(CP_ACP, 0, sour.c_str(), -1, pAssii, ansiiLen, nullptr, nullptr);
	std::string ret_str = pAssii;
	free(pAssii);
	return ret_str;
}

string Encoding::AsciiToUTF8(const string& sour)
{
	return UnicodeToUTF8(AsciiToUnicode(sour));
}

string Encoding::UTF8ToAscii(const string& sour)
{
	return UnicodeToAscii(UTF8ToUnicode(sour));
}


text:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "../logger/logger.h"
#include "cache_alloc.h"

struct node {
	struct node* next;
};

struct cache_allocer {
	unsigned char* cache_mem;
	int capacity;
	struct node* free_list;
	int elem_size;
};

struct cache_allocer*
create_cache_allocer(int capacity, int elem_size) {
	struct cache_allocer* allocer =(struct cache_allocer*)malloc(sizeof(struct cache_allocer));
	memset(allocer, 0, sizeof(struct cache_allocer));

	elem_size = (elem_size < sizeof(struct node)) ? sizeof(struct node) : elem_size;
	allocer->capacity = capacity;
	allocer->elem_size = elem_size;
	allocer->cache_mem = (unsigned char*)malloc(capacity * elem_size);
	memset(allocer->cache_mem, 0, capacity * elem_size);

	allocer->free_list = NULL;

	for (int i = 0; i < capacity; i++) {
		struct node*walk = (struct node*)(allocer->cache_mem + i * elem_size);
		walk->next = allocer->free_list;
		allocer->free_list = walk;
	}

	return allocer;
}

void
destroy_cache_allocer(struct cache_allocer* allocer) {
	if (allocer->cache_mem != NULL) {
		free(allocer->cache_mem);
	}

	free(allocer);
}

void*
cache_alloc(struct cache_allocer* allocer, int elem_size) {
	if (allocer->elem_size < elem_size) {
		void* ret = malloc(elem_size);
		if (!ret)
		{
			log_debug("mallocÊ§°Ü");
		}
		return ret;
	}

	if (allocer->free_list != NULL) {
		void* now = allocer->free_list;
		allocer->free_list = allocer->free_list->next;
		return now;
	}

	return malloc(elem_size);
}

void cache_free(struct cache_allocer* allocer, void* mem) {
	if (((unsigned char*)mem) >= allocer->cache_mem &&
		((unsigned char*)mem) < allocer->cache_mem + allocer->capacity * allocer->elem_size) {
		struct node* node = (struct node*)mem;
		node->next = allocer->free_list;
		allocer->free_list = node;
		return;
	}

	free(mem);
}


text:
#ifndef __SMALL_ALLOC_H__
#define __SMALL_ALLOC_H__

#ifdef __cplusplus
extern "C" {
#endif
	void* small_alloc(int size);
	void small_free(void* mem);

#ifdef __cplusplus
}
#endif

#endif // !__SMALL_ALLOC_H__


text:
#include "small_alloc.h"
#include <stdlib.h>
#include "cache_alloc.h"

#define SMALL_ELEM_NUM 10*1024
#define SMALL_ELEM_SIZE 128

static struct cache_allocer* allocer = NULL;

void* small_alloc(int size)
{
	if (allocer == NULL)
	{
		allocer = create_cache_allocer(SMALL_ELEM_NUM, SMALL_ELEM_SIZE);
	}
	return cache_alloc(allocer, size);

}

void small_free(void* mem)
{
	if (allocer == NULL)
	{
		allocer = create_cache_allocer(SMALL_ELEM_NUM, SMALL_ELEM_SIZE);
	}
	cache_free(allocer, mem);
}


text:
#ifndef __CACHE_ALLOC_H__
#define __CACHE_ALLOC_H__

#ifdef __cplusplus
extern "C" {
#endif
	struct cache_allocer* create_cache_allocer(int capacity, int elem_size);
	void destroy_cache_allocer(struct cache_allocer* allocer);

	void* cache_alloc(struct cache_allocer* allocer, int elem_size);
	void cache_free(struct cache_allocer* allocer, void* mem);

#ifdef __cplusplus
}
#endif

#endif


text:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <string>
using namespace std;

#include "logger.h"
#include "uv.h"

static string g_log_path;
static string g_prefix;
static uv_fs_t g_file_handle;
static uint32_t g_current_day;
static uint32_t g_last_second;
static char g_format_time[64] = { 0 };
static char* g_log_level[] = { (char*)"Debug", (char*)"Warning", (char*)"Error"};
static bool g_std_out = false;
static bool isInit = false;

static void
open_file(tm* time_struct) {
	int result = 0;
	char fileName[128] = { 0 };

	if (g_file_handle.result != 0) {
		uv_fs_close(uv_default_loop(), &g_file_handle, g_file_handle.result, NULL);
		uv_fs_req_cleanup(&g_file_handle);
		g_file_handle.result = 0;
	}

	sprintf(fileName, "%s%s.%4d%02d%02d.log", g_log_path.c_str(), g_prefix.c_str(), time_struct->tm_year + 1900, time_struct->tm_mon + 1, time_struct->tm_mday);
	result = uv_fs_open(NULL, &g_file_handle, fileName, O_CREAT | O_RDWR | O_APPEND, S_IREAD | S_IWRITE, NULL);
	if (result < 0) {
		fprintf(stderr, "open file failed! name=%s, reason=%s", fileName, uv_strerror(result));
	}
}

static void
prepare_file() {
	time_t  now = time(NULL);
	now += 8 * 60 * 60;
	tm* time_struct = gmtime(&now);
	if (g_file_handle.result == 0) {
		g_current_day = time_struct->tm_mday;
		open_file(time_struct);
	}
	else {
		if (g_current_day != time_struct->tm_mday) {
			g_current_day = time_struct->tm_mday;
			open_file(time_struct);
		}
	}

}

static void
format_time() {
	time_t  now = time(NULL);
	now += 8 * 60 * 60;
	tm* time_struct = gmtime(&now);
	if (now != g_last_second) {
		g_last_second = (uint32_t)now;
		memset(g_format_time, 0, sizeof(g_format_time));
		sprintf(g_format_time, "%4d%02d%02d %02d:%02d:%02d ",
			time_struct->tm_year + 1900, time_struct->tm_mon + 1, time_struct->tm_mday,
			time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec);
	}
}

void
logger::init(const char* path,const char* prefix, bool std_output) {
	if (isInit)
		return;
	isInit = true;

	g_prefix = prefix;
	g_log_path = path;
	g_std_out = std_output;

	if (*(g_log_path.end() - 1) != '/') {
		g_log_path += "/";
	}

	std::string tmp_path = g_log_path;
	int find = tmp_path.find("/");
	uv_fs_t req;
	int result;

	while (find != std::string::npos) {
		result = uv_fs_mkdir(uv_default_loop(), &req, tmp_path.substr(0, find).c_str(), 0755, NULL);
		find = tmp_path.find("/", find + 1);
	}
	uv_fs_req_cleanup(&req);
}

void
logger::log(const char* file_name,
	int line_num,
	int level, const char* msg, ...) {
	prepare_file();
	format_time();
	static char msg_meta_info[1024] = { 0 };
	static char msg_content[1024 * 10] = { 0 };
	static char new_line = '\n';
	static char tabKey = '\t';

	va_list args;
	va_start(args, msg);
	vsnprintf(msg_content, sizeof(msg_content), msg, args);
	va_end(args);

	sprintf(msg_meta_info, "%s:%u  ", file_name, line_num);
	uv_buf_t buf[6]; // time level content fileandline newline
	buf[0] = uv_buf_init(g_format_time, static_cast<unsigned int>(strlen(g_format_time)));
	buf[1] = uv_buf_init(g_log_level[level], static_cast<unsigned int>(strlen(g_log_level[level])));
	buf[2] = uv_buf_init(msg_meta_info, static_cast<unsigned int>(strlen(msg_meta_info)));
	buf[3] = uv_buf_init(&tabKey, static_cast<unsigned int>(1));
	buf[4] = uv_buf_init(msg_content, static_cast<unsigned int>(strlen(msg_content)));
	buf[5] = uv_buf_init(&new_line, static_cast<unsigned int>(1));

	uv_fs_t writeReq;
	int result = uv_fs_write(NULL, &writeReq, g_file_handle.result, buf, sizeof(buf) / sizeof(buf[0]), -1, NULL);
	if (result < 0) {
		fprintf(stderr, "log failed %s%s%s%s", g_format_time, g_log_level[level], msg_meta_info, msg_content);
	}

	uv_fs_req_cleanup(&writeReq);

	if (g_std_out) {
		printf("[%s]\t %s\t\t<%s:%u>\n", g_log_level[level], msg_content, file_name, line_num);
	}
}


text:
#ifndef __LOGGER_H__
#define __LOGGER_H__

enum {
	tag_DEBUG = 0,
	tag_WARNING,
	tag_ERROR,
};

#define log_debug(msg, ...) logger::log(__FILE__, __LINE__, tag_DEBUG, (msg), ## __VA_ARGS__);
#define log_warning(msg, ...) logger::log(__FILE__, __LINE__, tag_WARNING, (msg), ## __VA_ARGS__);
#define log_error(msg, ...) logger::log(__FILE__, __LINE__, tag_ERROR, (msg), ## __VA_ARGS__);

class logger {
public:
	static void init(const char* path, const char* prefix, bool std_output = false);
	static void log(const char* file_name,
	                int line_num,
	                int level, const char* msg, ...);
};

#endif


text:
#include "WinUtil.h"
#include "../Encoding.h"

string WinUtil::GetDirPath(const string& filePath)
{
	auto index = filePath.find_last_of("\\");
	return filePath.substr(0, index);
}

//返回值：
//如果callback返回false终止了搜索，则enumsubfiles也返回false，
//否则搜索完毕后返回true
bool WinUtil::SearchFromDir(
	const std::wstring& dir_with_back_slant,  //根目录，比如: L"C:\\", L"E:\\test\\"
	const std::wstring& filename,             //比如: L"*", L"123.txt", L"*.exe", L"123.???"
	unsigned int maxdepth,                    //最大深度。0则不搜索子文件夹，-1则搜索所有子文件夹
	SearchType flags,                          //返回结果为文件，还是文件夹，还是都返回
	std::function<bool(const std::wstring & dir, _wfinddata_t & attrib)> callback
)
{
	auto dir = dir_with_back_slant;
	if (*(dir.end() - 1) != '\\')
		dir += '\\';

	_wfinddata_t dat;
	size_t hfile;
	std::wstring fullname = dir + filename;
	std::wstring tmp;
	bool ret = true;

	hfile = _wfindfirst(fullname.c_str(), &dat);
	if (hfile == -1) goto a;
	do {
		if (!(wcscmp(L".", dat.name) && wcscmp(L"..", dat.name))) continue;
		if (
			((dat.attrib & _A_SUBDIR) && (!((int)flags & (int)SearchType::ENUM_DIR))) ||
			((!(dat.attrib & _A_SUBDIR)) && (!((int)flags & (int)SearchType::ENUM_FILE)))
			) continue;
		ret = callback(dir, dat);
		if (!ret) {
			_findclose(hfile);
			return ret;
		}
	} while (_wfindnext(hfile, &dat) == 0);
	_findclose(hfile);

a:

	if (!maxdepth) return ret;

	tmp = dir + L"*";
	hfile = _wfindfirst(tmp.c_str(), &dat);
	if (hfile == -1) return ret;
	do {
		if (!(wcscmp(L".", dat.name) && wcscmp(L"..", dat.name))) continue;
		if (!(dat.attrib & _A_SUBDIR)) continue;
		tmp = dir + dat.name + L"\\";
		ret = SearchFromDir(tmp, filename, maxdepth - 1, flags, callback);
		if (!ret) {
			_findclose(hfile);
			return ret;
		}

	} while (_wfindnext(hfile, &dat) == 0);
	_findclose(hfile);

	return ret;

}

bool WinUtil::SearchFromDir(
	const std::string& dir_with_back_slant,
	const std::string& filename,
	unsigned int maxdepth,
	SearchType flags,
	std::function<bool(const std::string & dir, const string & fileName)> callback)
{
	std::wstring dir = Encoding::UTF8ToUnicode(dir_with_back_slant);
	std::wstring fileName = Encoding::UTF8ToUnicode(filename);
	return WinUtil::SearchFromDir(dir, fileName, maxdepth, flags,
		[=](const std::wstring& dir, _wfinddata_t& attrib)->bool
		{
			//dir末尾有一个反斜杠'\\'
			auto _dir = Encoding::UnicodeToUTF8(dir);
			auto wName = wstring(attrib.name);
			//return true继续搜索，return false停止搜索
			return callback(_dir,Encoding::UnicodeToUTF8(wName));
		});
}


text:
#ifndef __WIN_UTIL_H__
#define __WIN_UTIL_H__

#include <functional>
#include <io.h>
#include <string>
using std::string;
//――――――――――――――――
//版权声明：本文为CSDN博主「liuqx0717」的原创文章，遵循 CC 4.0 BY - SA 版权协议，转载请附上原文出处链接及本声明。
//原文链接：https ://blog.csdn.net/liuqx97bb/article/details/77074833
enum class SearchType {
	ENUM_FILE = 1,
	ENUM_DIR,
	ENUM_BOTH
};

class WinUtil
{
public:
	static string GetDirPath(const string& filePath);
	static bool SearchFromDir(
		const std::wstring& dir_with_back_slant,  //根目录，比如: "L"C:\\", L"E:\\test\\"
		const std::wstring& filename,             //比如: L"*", L"123.txt", L"*.exe", L"123.???"
		unsigned int maxdepth,                    //最大深度。0则不搜索子文件夹，-1则搜索所有子文件夹
		SearchType flags,                          //返回结果为文件，还是文件夹，还是都返回
		std::function<bool(const std::wstring & dir, _wfinddata_t & attrib)> callback //return true继续搜索，return false停止搜索
	);

	static bool SearchFromDir(
		const std::string& dir_with_back_slant,  //根目录，比如: "L"C:\\", L"E:\\test\\"
		const std::string& filename,             //比如: L"*", L"123.txt", L"*.exe", L"123.???"
		unsigned int maxdepth,                    //最大深度。0则不搜索子文件夹，-1则搜索所有子文件夹
		SearchType flags,                          //返回结果为文件，还是文件夹，还是都返回
		std::function<bool(const std::string & dirPath, const string & fileName)> callback //return true继续搜索，return false停止搜索
	);
};

//返回值：
//如果callback返回false终止了搜索，则enumsubfiles也返回false，
//否则搜索完毕后返回true

#endif // !__WIN_UTIL_H__