| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- using DeepCore;
- using DeepCore.IO;
- using DeepCore.Log;
- using DeepCore.Threading;
- using DeepCrystal;
- using DeepCrystal.ORM.Generic;
- using DeepCrystal.ORM.Query;
- using DeepCrystal.RPC;
- using DeepMMO.Data;
- using DeepMMO.Protocol;
- using DeepMMO.Protocol.Client;
- using DeepMMO.Server.Area;
- using DeepMMO.Server.SystemMessage;
- using System;
- using System.Threading.Tasks;
- namespace DeepMMO.Server.Connect
- {
- /// <summary>
- /// 单个链接服务
- /// </summary>
- public partial class SessionService : IService
- {
- public readonly Logger log;
- public readonly string accountID;
- public readonly TypeCodec client_battle_action_codec;
- public readonly TypeCodec session_battle_action_codec;
- protected ConnectServer.ViewSession session { get; private set; }
- protected ClientEnterServerRequest enter { get; private set; }
- protected IRemoteService remote_logic_service { get; private set; }
- protected IRemoteService remote_area_service { get; private set; }
- protected IRemoteService remote_log_service { get; private set; }
- protected ClientEnterGameRequest enter_game { get; private set; }
- public string Channel { get; private set; }
- protected IDisposable heartbeat_timer;
- protected DateTime last_heartbeat = DateTime.Now;
- protected string sessionToken;
- protected MappingReference<AccountData> accountSave;
- protected QueryMappingReference<RoleSnap> queryRoleSnap;
- protected MappingReference<AccountRoleSnap> accountRoleSnapSave;
- protected QueryMappingReference<RoleDataStatusSnap> queryRoleDataStatusSnap;
- protected bool mDisconnected = true;
-
- public override ServiceProperties Properties
- {
- get
- {
- var ret = base.Properties;
- ret.IgnoreRequestError = true;
- ret.IgnoreResponseError = true;
- return ret;
- }
- }
- public SessionService(ServiceStartInfo start) : base(start)
- {
- this.log = LoggerFactory.GetLogger(start.Address.ServiceName);
- this.accountID = start.Config["accountID"].ToString();
- this.client_battle_action_codec = ConnectServer.ClientCodec.Factory.GetCodec(typeof(ClientBattleAction));
- this.session_battle_action_codec = base.ServerCodec.Factory.GetCodec(typeof(SessionBattleAction));
- this.Channel = start.Config["channel"]?.ToString();
- }
- protected override void OnDisposed()
- {
- this.accountSave.Dispose();
- this.accountRoleSnapSave.Dispose();
- this.queryRoleSnap.Dispose();
- this.queryRoleDataStatusSnap.Dispose();
- this.session = null;
- this.enter = null;
- this.remote_logic_service = null;
- this.remote_area_service = null;
- this.enter_game = null;
- this.heartbeat_timer = null;
- this.sessionToken = null;
- this.accountSave = null;
- this.queryRoleSnap = null;
- this.queryRoleDataStatusSnap = null;
- this.accountRoleSnapSave = null;
- }
- protected override async Task OnStartAsync()
- {
- this.accountSave =
- new MappingReference<AccountData>(RPGServerPersistenceManager.TYPE_ACCOUNT_DATA, accountID, this);
- this.queryRoleSnap =
- new QueryMappingReference<RoleSnap>(RPGServerPersistenceManager.TYPE_ROLE_SNAP_DATA, this);
- this.accountRoleSnapSave =
- new MappingReference<AccountRoleSnap>(RPGServerPersistenceManager.TYPE_ACCOUNT_ROLE_SNAP_DATA,
- accountID, this);
- this.queryRoleDataStatusSnap =
- new QueryMappingReference<RoleDataStatusSnap>(
- RPGServerPersistenceManager.TYPE_ROLE_DATA_STATUS_SNAP_DATA, this);
- this.Provider.AutoDispose(accountSave);
- this.Provider.AutoDispose(queryRoleSnap);
- this.Provider.AutoDispose(accountRoleSnapSave);
- this.Provider.AutoDispose(queryRoleDataStatusSnap);
- this.heartbeat_timer = base.Provider.CreateTimer(CheckHeartbeat, this,
- TimeSpan.FromSeconds(TimerConfig.timer_sec_SessionKeepTimeout / 2),
- TimeSpan.FromSeconds(TimerConfig.timer_sec_SessionKeepTimeout / 2));
- var data = await this.accountSave.LoadDataAsync();
- var roleSnap = await this.accountRoleSnapSave.LoadDataAsync();
- remote_log_service = await this.Provider.GetAsync(ServerNames.LogService);
- }
- protected override async Task OnStopAsync(ServiceStopInfo reason)
- {
- this.heartbeat_timer.Dispose();
- if (session != null)
- {
- this.session.socket.Disconnect(reason.Reason);
- }
- await ShutdownLogicServiceAsync("session destroy");
- await this.accountSave.FlushAsync();
- }
- protected void CheckHeartbeat(object state)
- {
- if (DateTime.Now - last_heartbeat > TimeSpan.FromSeconds(TimerConfig.timer_sec_SessionKeepTimeout))
- {
- if (session == null || session.socket.IsConnected == false)
- {
- this.ShutdownSelf("timeout");
- }
- }
- }
- [RpcHandler(typeof(SystemShutdownNotify))]
- public virtual void system_rpc_Handle(SystemShutdownNotify shutdown)
- {
- var logic = remote_logic_service;
- if (logic != null)
- {
- logic.Invoke(new SessionDisconnectNotify() {sessionName = SelfAddress.ServiceName,});
- }
- this.ShutdownSelf(shutdown.reason);
- }
- [RpcHandler(typeof(KickPlayerNotify))]
- public virtual void rpc_Handle(KickPlayerNotify notify)
- {
- if (session != null)
- {
- this.ShutdownSelf(notify.reason);
- }
- }
- //--------------------------------------------------------------------------------------------------------------------------------------------
- /// <summary>
- /// 首次连接或者重新连接
- /// </summary>
- /// <param name="bind"></param>
- /// <param name="cb"></param>
- [RpcHandler(typeof(LocalBindSessionRequest), typeof(LocalBindSessionResponse), ServerNames.ConnectServerType)]
- public virtual async Task<LocalBindSessionResponse> connect_rpc_Handle(LocalBindSessionRequest bind)
- {
- if (!string.IsNullOrEmpty(bind.enter.c2s_session_token) && !string.IsNullOrEmpty(this.sessionToken) &&
- bind.enter.c2s_session_token != this.sessionToken)
- {
- this.sessionToken = null;
- return (new LocalBindSessionResponse() {s2c_code = Response.CODE_ERROR});
- }
- var savedLoginToken = await accountSave.LoadFieldAsync<string>(nameof(AccountData.lastLoginToken));
- var savedServerGroup = await accountSave.LoadFieldAsync<string>(nameof(AccountData.lastLoginServerGroupID));
- if (savedLoginToken != bind.enter.c2s_login_token)
- {
- this.sessionToken = null;
- return (new LocalBindSessionResponse() {s2c_code = Response.CODE_ERROR});
- }
- var old_session = this.session;
- if (old_session != null)
- {
- var disconnect = new SessionDisconnectNotify()
- {
- socketID = old_session.socket.ID,
- sessionName = SelfAddress.ServiceName,
- };
- if (enter_game != null)
- {
- disconnect.roleID = enter_game.c2s_roleUUID;
- }
- //老Session暂停发包//
- var logic = this.remote_logic_service;
- if (logic != null)
- {
- logic.Invoke(disconnect);
- }
- //log.Log("Reconnect");
- //老Session踢下线//
- old_session.socket.Disconnect("New Session Reconnect");
- }
- else
- {
- //log.Log("Connect");
- }
- this.session = bind.session;
- this.enter = bind.enter;
- //登录成功后,产生新的Token用于断线重连//
- this.sessionToken = Guid.NewGuid().ToString();
- this.last_heartbeat = DateTime.Now;
- return (new LocalBindSessionResponse()
- {
- session = this,
- sessionToken = sessionToken,
- serverGroupID = savedServerGroup,
- });
- }
- /// <summary>
- /// 用户断线
- /// </summary>
- /// <param name="disconnect"></param>
- [RpcHandler(typeof(SessionDisconnectNotify), ServerNames.ConnectServerType)]
- public virtual void connect_rpc_Handle(SessionDisconnectNotify disconnect)
- {
- //log.Log("Disconnect");
- last_heartbeat = DateTime.Now;
- disconnect.sessionName = SelfAddress.ServiceName;
- if (enter_game != null)
- {
- disconnect.roleID = enter_game.c2s_roleUUID;
- }
- // var area = remote_area_service;
- // if (area != null)
- // {
- // area.Invoke(disconnect);
- // }
- //排除老Session踢下线导致的Disconnect//
- if (this.session == null || disconnect.socketID == this.session.socket.ID)
- {
- this.session = null;
- var logic = this.remote_logic_service;
- if (logic != null)
- {
- mDisconnected = true;
- logic.Invoke(disconnect);
- }
- }
- }
- /// <summary>
- /// 从网络线程接受协议
- /// </summary>
- /// <param name="session"></param>
- /// <param name="route_codec"></param>
- /// <param name="binary"></param>
- /// <param name="cb"></param>
- public void connect_OnReceivedBinaryImmediately(TypeCodec route_codec, BinaryMessage binary,
- OnRpcBinaryReturn cb = null)
- {
- last_heartbeat = DateTime.Now;
- if (client_battle_action_codec.MessageID == route_codec.MessageID)
- {
- SendToArea(route_codec, binary);
- }
- else
- {
- this.Provider.Execute(new Action(do_async_OnReceivedBinaryImmediately));
- void do_async_OnReceivedBinaryImmediately()
- {
- SendToLogic(route_codec, binary, cb);
- }
- }
- }
- /// <summary>
- /// 玩家进入游戏
- /// </summary>
- /// <param name="enter"></param>
- /// <param name="cb"></param>
- [RpcHandler(typeof(ClientEnterGameRequest), typeof(ClientEnterGameResponse), ServerNames.ConnectServerType)]
- public virtual async Task<ClientEnterGameResponse> client_rpc_Handle(ClientEnterGameRequest enter)
- {
- //验证此角色UID是否在此账号列表中
- var roleIDMap = accountRoleSnapSave.Data.roleIDMap;
- if (!roleIDMap.ContainsKey(enter.c2s_roleUUID))
- {
- return new ClientEnterGameResponse()
- {
- s2c_code = ClientEnterGameResponse.CODE_ROLEID_INVAILD,
- };
- }
- //第三方/一号通验证//
- var serverPassportResult = await RPGServerManager.Instance.Passport.VerifyEnterGameAsync(this.enter, enter);
- if (!serverPassportResult.Verified)
- {
- return new ClientEnterGameResponse()
- {
- s2c_code = ClientEnterGameResponse.CODE_ROLE_SUSPEND,
- s2c_msg = serverPassportResult.Message
- };
- }
- //log.Log("ClientEnterGameRequest");
- #region 账号封停验证.
- var statusSnap = await queryRoleDataStatusSnap.LoadDataAsync(enter.c2s_roleUUID);
- if (statusSnap != null)
- {
- //没过期代表已封停.
- if (!((DateTime.UtcNow - statusSnap.SuspendDate).TotalMilliseconds > 0))
- {
- return (new ClientEnterGameResponse()
- {
- s2c_code = ClientEnterGameResponse.CODE_ROLE_SUSPEND,
- s2c_suspendTime = statusSnap.SuspendDate
- });
- }
- }
- #endregion
- last_heartbeat = DateTime.Now;
- this.enter_game = enter;
- bool reconnect = false;
- var rec = new SessionReconnectNotify();
- rec.sessionName = SelfAddress.ServiceName;
- if (enter_game != null)
- {
- rec.roleID = enter_game.c2s_roleUUID;
- }
- var logic = remote_logic_service;
- if (logic != null)
- {
- var oldRoleID = logic.Config["roleID"].ToString();
- if (oldRoleID != enter.c2s_roleUUID)
- {
- log.WarnFormat(string.Format("Role Already Login : Acc={0} : Role={1} -> {2}", accountID, oldRoleID,
- enter.c2s_roleUUID));
- //cb(new ClientEnterGameResponse() { s2c_code = ClientEnterGameResponse.CODE_LOGIC_ALREADY_LOGIN });
- await logic.ShutdownAsync("switch role");
- logic = await this.CreateLogicServiceAsync(enter);
- //return;
- }
- else
- {
- //if (DeepCore.Log.Logger.SHOW_LOG)
- {
- log.InfoFormat(string.Format("Role Reconnect : Acc={0} : Role={1}", accountID, enter.c2s_roleUUID));
- }
- reconnect = true;
- rec.config = await InitConfig();
- }
- }
- else
- {
- //if (DeepCore.Log.Logger.SHOW_LOG)
- {
- log.InfoFormat(string.Format("Role Connect : Acc={0} : Role={1}", accountID, enter.c2s_roleUUID));
- }
- logic = await this.CreateLogicServiceAsync(enter);
- }
- if (logic != null)
- {
- accountSave.SetField(nameof(AccountData.lastLoginRoleID), enter.c2s_roleUUID);
- await accountSave.FlushAsync();
- try
- {
- mDisconnected = false;
- var ret = await logic.CallAsync<ClientEnterGameResponse>(enter);
- //log.Log("ClientEnterGameResponse: " + ret.IsSuccess);
- return ret;
- }
- finally
- {
- if (reconnect)
- {
- logic.Invoke(rec);
- }
- }
- }
- else
- {
- return (new ClientEnterGameResponse() {s2c_code = ClientEnterGameResponse.CODE_LOGIC_NOT_FOUND,});
- }
- }
- /// <summary>
- /// 玩家离开游戏
- /// </summary>
- /// <param name="enter"></param>
- /// <param name="cb"></param>
- [RpcHandler(typeof(ClientExitGameRequest), typeof(ClientExitGameResponse), ServerNames.ConnectServerType)]
- public virtual async Task<ClientExitGameResponse> client_rpc_Handle(ClientExitGameRequest exit)
- {
- //log.Log("ClientExitGameRequest");
- last_heartbeat = DateTime.Now;
- await ShutdownLogicServiceAsync("player exit");
- this.remote_area_service = null;
- return new ClientExitGameResponse();
- //return Task.FromResult(new ClientExitGameResponse());
- }
- //--------------------------------------------------------------------------------------------------------------------------------------------
- [RpcHandler(typeof(SessionBindAreaNotify), ServerNames.AreaServiceType)]
- public virtual void area_rpc_Handle(SessionBindAreaNotify bind)
- {
- this.Provider.GetAsync(new RemoteAddress(bind.areaName, bind.areaNode)).ContinueWith(t =>
- {
- remote_area_service = t.GetResultAs();
- });
- }
- [RpcHandler(typeof(SessionUnbindAreaNotify), ServerNames.AreaServiceType)]
- public virtual void area_rpc_Handle(SessionUnbindAreaNotify msg)
- {
- remote_area_service = null;
- }
- // from service: TODO
- [RpcHandler(true)]
- public virtual void rpc_Handle(BinaryMessage msg)
- {
- if (mDisconnected) return;
- var session = this.session;
- if (session != null)
- {
- session.SocketSend(msg);
- }
- }
- public override void OnWormholeTransported(RemoteAddress from, object message)
- {
- var session = this.session;
- if (session != null)
- {
- if (message is BinaryMessage bin)
- {
- session.socket.Send(bin);
- }
- else if (message is ISerializable ser)
- {
- session.socket.Send(ser);
- }
- }
- }
- //--------------------------------------------------------------------------------------------------------------------------------------------
- //--------------------------------------------------------------------------------------------------------------------------------------------
- /// <summary>
- /// 战斗协议 ClientBattleAction 直接发往AreaService
- /// </summary>
- /// <param name="action"></param>
- public virtual void SendToArea(TypeCodec route_codec, BinaryMessage action)
- {
- try
- {
- var area = remote_area_service;
- var enter = enter_game;
- if (area != null && enter != null)
- {
- using (var output = IOStreamObjectPool.AllocOutputAutoRelease(ConnectServer.ClientCodec.Factory))
- {
- output.PutUTF(enter.c2s_roleUUID);
- output.PutBytes(action.Buffer, action.BufferOffset, action.BufferLength);
- var to_area = BinaryMessage.FromBuffer(session_battle_action_codec.MessageID, output.Buffer);
- area.WormholeTransport(to_area);
- }
- }
- }
- catch (Exception err)
- {
- log.Error(err);
- }
- }
- /// <summary>
- /// 逻辑协议发往LogicService
- /// </summary>
- /// <param name="msg"></param>
- /// <param name="callback"></param>
- public virtual void SendToLogic(TypeCodec route_codec, BinaryMessage msg, OnRpcBinaryReturn callback = null)
- {
- var logic = remote_logic_service;
- if (logic != null)
- {
- if (callback != null)
- logic.Call(msg, callback);
- else
- logic.Invoke(msg);
- }
- else
- {
- log.Warn("SendToLogic Error : Logic Service Not Init : " + route_codec);
- if (callback != null) callback(BinaryMessage.NULL);
- }
- }
- protected virtual async Task<IRemoteService> CreateLogicServiceAsync(ClientEnterGameRequest enter_game)
- {
- var cfg = await InitConfig();
- var ret = await this.Provider.CreateAsync(
- ServerNames.GetLogicServiceAddress(enter_game.c2s_roleUUID, this.SelfAddress.ServiceNode), cfg);
- this.remote_logic_service = ret;
- return ret;
- }
- protected virtual async Task ShutdownLogicServiceAsync(string reason)
- {
- var logic = remote_logic_service;
- remote_logic_service = null;
- if (logic != null)
- {
- try
- {
- await logic.CallAsync<SessionBeginLeaveResponse>(new SessionBeginLeaveRequest()
- {
- sessionName = SelfAddress.ServiceName,
- roleID = enter_game.c2s_roleUUID,
- });
- }
- catch (Exception err)
- {
- log.Error(err.Message, err);
- }
- try
- {
- var result = await logic.ShutdownAsync(reason);
- log.Info("ShutdownAsync Complete : " + result);
- }
- catch (Exception err)
- {
- log.Error("ShutdownAsync Error : " + err.Message, err);
- }
- }
- }
- private async Task<HashMap<string, string>> InitConfig()
- {
- HashMap<string, string> cfg = new HashMap<string, string>();
- var serverID = await accountSave.LoadFieldAsync<string>(nameof(AccountData.lastLoginServerID));
- var serverGroupID = await accountSave.LoadFieldAsync<string>(nameof(AccountData.lastLoginServerGroupID));
- //var privilege = await accountSave.LoadFieldAsync<RolePrivilege>(nameof(AccountData.privilege));
- cfg["sessionNode"] = this.SelfAddress.ServiceNode;
- cfg["sessionName"] = this.SelfAddress.ServiceName;
- cfg["accountID"] = enter.c2s_account;
- //cfg["privilege"] = privilege.ToString();
- cfg["roleID"] = enter_game.c2s_roleUUID;
- cfg["serverID"] = serverID;
- cfg["serverGroupID"] = serverGroupID;
- cfg["channel"] = enter.c2s_clientInfo.channel;
- cfg["passport"] = enter.c2s_clientInfo.sdkName;
- cfg["clientVersion"] = enter.c2s_clientInfo.clientVersion;
- cfg["deviceId"] = enter.c2s_clientInfo.deviceId;
- cfg["deviceModel"] = enter.c2s_clientInfo.deviceModel;
- cfg["deviceType"] = enter.c2s_clientInfo.deviceType;
- cfg["network"] = enter.c2s_clientInfo.network;
- cfg["region"] = enter.c2s_clientInfo.region;
- cfg["sdkName"] = enter.c2s_clientInfo.sdkName;
- cfg["sdkVersion"] = enter.c2s_clientInfo.sdkVersion;
- cfg["subChannel"] = enter.c2s_clientInfo.subChannel;
- cfg["userAgent"] = enter.c2s_clientInfo.userAgent;
- cfg["userSource1"] = enter.c2s_clientInfo.userSource1;
- cfg["userSource2"] = enter.c2s_clientInfo.userSource2;
- var ip = (this.session.socket.RemoteAddress as System.Net.IPEndPoint)?.Address?.ToString();
- cfg["clientIp"] = ip;
- cfg["platformAccount"] = enter.c2s_clientInfo.platformAcount;
- return cfg;
- }
- }
- /// <summary>
- /// Connect 进程内,通知SessionService绑定ViewSession
- /// </summary>
- public class LocalBindSessionRequest : Request, IRpcNoneSerializable
- {
- public ConnectServer.ViewSession session;
- public ClientEnterServerRequest enter;
- }
- public class LocalBindSessionResponse : Response, IRpcNoneSerializable
- {
- public SessionService session;
- public string sessionToken;
- public string serverGroupID;
- }
- }
|