/// <summary> /// za 종료 /// </summary> private void zaSessionClosed(zaSession session) { try { StringBuilder sb = new StringBuilder(); //219ZA는 종료전 모든 유저의 디스커넥트 패킷을 보내온다. //이후 사용자들이 개발한 za는 그렇제 못하므로 za가 디스커넥트되면 로그인 유저목록을 클리어 해준다. for (int i = g_Config.LoggedInList.Count - 1; i >= 0; i--) { if (g_Config.LoggedInList.ElementAt(i).Value.ServerID == session.ServerID) { sb.Clear(); PrintLogs(sb.AppendFormat("loginuser.destroy {0}", g_Config.LoggedInList.ElementAt(i).Key).ToString()); g_Config.LoggedInList.Remove(g_Config.LoggedInList.ElementAt(i).Key); } } sb.Clear(); PrintLogs(sb.AppendFormat("<ZA>AgentID={0} Disconnected", session.AgentID).ToString()); g_Config.ZoneAgentList.Remove(session.ServerID); session.Dispose(); } catch (Exception ex) { WriteLogs(string.Format("LoginServer.zaSessionClosed:{0}{1}", Environment.NewLine, ex)); } }
/// <summary> /// 메시지 받음 /// </summary> /// <param name="asyncResult"></param> private void RequestReceived(IAsyncResult asyncResult) { zaSession zAgent = (zaSession)asyncResult.AsyncState; try { if (zAgent == null) { return; } int length = zAgent.Stream.EndRead(asyncResult); if (length == 0) { return; } List <Byte[]> buffers; this.zRestBytes = Packet.SplitPackets(zAgent.Buffer, length, out buffers, this.zRestBytes); zAgent.Stream.BeginRead(zAgent.Buffer, 0, zAgent.Buffer.Length, new AsyncCallback(RequestReceived), zAgent); ExecuteCommand(zAgent, buffers); } catch (IOException) { } catch (InvalidOperationException) { } catch (Exception ex) { WriteLogs(string.Format("LoginServer.RequestReceived:{0}{1}", Environment.NewLine, ex)); } }
/// <summary> /// 새 존 에이전트 접속 /// </summary> /// <param name="zAgent"></param> /// <param name="buffer"></param> private void NewZoneAgentConnected(zaSession zAgent, Byte[] buffer) { try { MSG_ZA2LS_CONNECT new_za = new MSG_ZA2LS_CONNECT(); new_za.SetBuffer(buffer); if (!g_Config.ZoneAgentList.ContainsKey(new_za.byServerID)) { //zone agent 정보 저장 zAgent.ServerID = new_za.byServerID; zAgent.AgentID = new_za.byAgentID; zAgent.IPadress = new_za.szIPAdress; zAgent.Port = new_za.dwPort; g_Config.ZoneAgentList.Add(new_za.byServerID, zAgent); PrintLogs(string.Format("<ZA>Receive SID={0} AgentID={1} {2}:{3}", new_za.byServerID, new_za.byAgentID, new_za.szIPAdress, new_za.dwPort)); //정상 등록되면 이벤트 등록 zAgent.StatusUpdate += zaStatusUpdate; zAgent.SessionClosed += zaSessionClosed; } else { //중복 ServerID의 에이전트는 등록 거부 PrintLogs(string.Format("<ZA>Receive AgentID={0} Duplicate ServerID={1}", new_za.byAgentID, new_za.byServerID)); PrintLogs(string.Format("<ZA>AgentID={0} Connection Rejected", new_za.byAgentID)); zAgent.Dispose(); } } catch (Exception ex) { WriteLogs(string.Format("LoginServer.NewZoneAgentConnected:{0}{1}{0}Buffer:{0}{2}", Environment.NewLine, ex, BitConverter.ToString(buffer).Replace("-", " "))); } }
/// <summary> /// 메시지 한개씩 루핑 /// </summary> /// <param name="buffers"></param> private void ExecuteCommand(zaSession zAgent, List <Byte[]> buffers) { foreach (Byte[] buffer in buffers) { MsgAnalysis(zAgent, buffer); } }
/// <summary> /// Server selection packet processing. /// 서버 선택 패킷 /// </summary> /// <param name="client"></param> /// <param name="buffer"></param> private void SelectedServer(cltSession client, Byte[] buffer) { //22패킷(za 정보: ip, port, new uid) 넘겨주고 접속자 정보의 기존uid를 새 uid로 변경하기. short ServerID = buffer[10]; if (g_Config.ZoneAgentList.ContainsKey(ServerID)) { PrintLogs(string.Format("<CT>ServerID={0}", ServerID)); var User = g_Config.LoggedInList.First(x => x.Value.PCID == client.PCID); zaSession zAgent = g_Config.ZoneAgentList[ServerID]; //ZS용 UID발급 uint zs_PCID = g_Config.za_Uid.Uid; PrintPCID(g_Config.za_Uid.dspUid); //유저가 몇번 서버로 이동하는지 기록함. 응답 패킷에는 서버 번호가 없으므로 여기서 미리 변경 User.Value.ServerID = ServerID; //za에게 유저 정보 넘기기 zAgent.Send(Packet.PreparedAccount(zs_PCID, User.Key)); //클라에게 za정보 넘기기 client.Send(Packet.ZoneAgentInfo(client.PCID, zs_PCID, zAgent.IPadress, zAgent.Port, client.Ver)); //za에 유저가 정상 접속한다면 za에서 답패킷(1f,02,e3)이 날아옴 g_Config.inZoneAgent++; client.Step = "ZA"; } else { PrintLogs(string.Format("<CT>ServerID={0} but ZoneAgent is Down", ServerID)); } }
/// <summary> /// Prepared유저가 za에 접속 성공했음 /// </summary> /// <param name="zAgent"></param> /// <param name="buffer"></param> private void PreparedUser2EnterGame(zaSession zAgent, Byte[] buffer) { try { //유저가 za에 정상 접속했음을 알리는 패킷 : 로그인 목록에서 유저의 UID 변경 MSG_ZA2LS_PREPARED_ACC_LOGIN pLogin = new MSG_ZA2LS_PREPARED_ACC_LOGIN(); pLogin.SetBuffer(buffer); g_Config.LoggedInList[pLogin.szAccount].PCID = pLogin.MsgHeader.dwPCID; PrintLogs(string.Format("<ZA>Receive User Login={0} {1}", pLogin.MsgHeader.dwPCID, pLogin.szAccount)); } catch (Exception ex) { WriteLogs(string.Format("LoginServer.PreparedUser2EnterGame:{0}{1}{0}Buffer:{0}{2}", Environment.NewLine, ex, BitConverter.ToString(buffer).Replace("-", " "))); } }
/// <summary> /// Session Handler /// </summary> /// <param name="asyncResult"></param> private void SessionHandler(IAsyncResult asyncResult) { try { TcpClient session = login_Server.EndAcceptTcpClient(asyncResult); zaSession zAgent = new zaSession(session); zAgent.Stream.BeginRead(zAgent.Buffer, 0, zAgent.Buffer.Length, new AsyncCallback(RequestReceived), zAgent); login_Server.BeginAcceptTcpClient(SessionHandler, null); } catch (ObjectDisposedException) { } catch (Exception ex) { WriteLogs(string.Format("LoginServer.ZoneAgentHandler:{0}{1}", Environment.NewLine, ex)); } }
/// <summary> /// 로그인 서버가 종료 후 재접속 했을경우 za가 보내주는 접속 유저 정보 /// </summary> /// <param name="zAgent"></param> /// <param name="buffer"></param> private void LoginUserRecoverMsg(zaSession zAgent, Byte[] buffer) { try { //ZS Login user Recover packet MSG_ZA2LS_LOGIN_USER_LIST pRecover = new MSG_ZA2LS_LOGIN_USER_LIST(); pRecover.SetBuffer(buffer); g_Config.za_Uid.Uid = pRecover.MsgHeader.dwPCID; g_Config.LoggedInList.Add(pRecover.szUserAccount, new LoginUser(pRecover.MsgHeader.dwPCID, zAgent.ServerID)); PrintLogs(string.Format("<ZA>Recover User UID={0} ID={1}", pRecover.MsgHeader.dwPCID, pRecover.szUserAccount)); PrintPCID(g_Config.za_Uid.dspUid); } catch (Exception ex) { WriteLogs(string.Format("LoginServer.LoginUserRecoverMsg:{0}{1}{0}Buffer:{0}{2}", Environment.NewLine, ex, BitConverter.ToString(buffer).Replace("-", " "))); } }
/// <summary> /// 플레이어 로그아웃 처리 /// </summary> /// <param name="zAgent"></param> /// <param name="buffer"></param> private void PlayerLogout(zaSession zAgent, Byte[] buffer) { try { //로그아웃 패킷: 확인 후 로그인 사용자 목록에서 삭제 MSG_ZA2LS_ACC_LOGOUT pLogout = new MSG_ZA2LS_ACC_LOGOUT(); pLogout.SetBuffer(buffer); PrintLogs(string.Format("<ZA>Receive User Logout={0} {1}", pLogout.MsgHeader.dwPCID, pLogout.szAccount)); if (g_Config.LoggedInList.ContainsKey(pLogout.szAccount)) { g_Config.LoggedInList.Remove(pLogout.szAccount); PrintLogs(string.Format("loginuser.destroy {0}", pLogout.szAccount)); } } catch (Exception ex) { WriteLogs(string.Format("LoginServer.PlayerLogout:{0}{1}{0}Buffer:{0}{2}", Environment.NewLine, ex, BitConverter.ToString(buffer).Replace("-", " "))); } }
/// <summary> /// In-game user account validity check: Expiration User enforcement logout. /// 게임 접속상태에서 계정 유효기한이 지난 사용자를 로그아웃 처리 /// </summary> private void CheckExpiredAccount(object sender) { if (g_Config.isExpiration) { for (int i = g_Config.LoggedInList.Count - 1; i >= 0; i--) { string strID = g_Config.LoggedInList.ElementAt(i).Key; if (DateTime.Now >= Acc_DB.getExpireDate(strID)) { if (g_Config.LoggedInList[strID].PCID > g_Config.za_Uid.MinValue) { zaSession zAgent = g_Config.ZoneAgentList[g_Config.LoggedInList[strID].ServerID]; zAgent.Send(Packet.reqDisconnectAcc(g_Config.LoggedInList[strID].PCID, strID)); PrintLogs(string.Format("request userdrop {0} {1} to zoneagent", g_Config.LoggedInList[strID].PCID, strID)); } PrintLogs(string.Format("ExpiredAccount.destroy {0}", strID)); g_Config.LoggedInList.Remove(strID); } Thread.Sleep(200); } } }
/// <summary> /// 메시지 분석 /// </summary> /// <param name="buffer"></param> private void MsgAnalysis(zaSession zAgent, Byte[] buffer) { try { if (buffer[9] == 0xE0 && buffer[8] == 0x02) { NewZoneAgentConnected(zAgent, buffer); } else if (buffer[9] == 0xE1 && buffer[8] == 0x02) { //ZA로부터 도착하는 지속적인 확인 패킷: 답장 필요없음, 4:접속자수, 1:za_count, 1:za_count //해당 za의 패킷 도착시간을 지속적으로 업데이트해서 디스커넥트 확인에 사용함. zAgent.AliveTime = DateTime.Now; } else if (buffer[9] == 0xE2 && buffer[8] == 0x02) { PlayerLogout(zAgent, buffer); } else if (buffer[9] == 0xE3 && buffer[8] == 0x02) { PreparedUser2EnterGame(zAgent, buffer); } else if (buffer[9] == 0xE4 && buffer[8] == 0x02) { LoginUserRecoverMsg(zAgent, buffer); } else { WriteNewPacket("ZA->LS", buffer, "localhost", ClientVer.undefined); } } catch (Exception ex) { WriteLogs(string.Format("LoginServer.MsgAnalysis:{0}{1}{0}Buffer:{0}{2}", Environment.NewLine, ex, BitConverter.ToString(buffer).Replace("-", " "))); } }
/// <summary> /// 접속자 수, 연결된 za 수 업데이트 /// </summary> /// <param name="session"></param> private void zaStatusUpdate(zaSession session) { UpdateStatus(session); }
/// <summary> /// Handling login packets. /// 로그인 요청 패킷 처리 /// </summary> /// <param name="client"></param> /// <param name="buffer"></param> private void ReqUserLogin(cltSession client, Byte[] buffer) { string str_id = Packet.Bytes2String(buffer, 0x0A); string str_pwd = Packet.Bytes2String(buffer, 0x1F); //check blank if (string.IsNullOrEmpty(str_id) || string.IsNullOrEmpty(str_pwd)) { client.Send(Packet.SayMessage(g_Config.Msg_Incorrect, client.PCID, 0, client.Ver)); RemoveClient(client, true, true); #if DEBUG PrintLogs(string.Format("BlankID/PWD: RemoveClient({0})", client.PCID)); #endif } else { //get db info by account AccountInfo AccInfo = Acc_DB.getAccountInfo(str_id); if (string.IsNullOrEmpty(AccInfo.id)) { client.Send(Packet.SayMessage(g_Config.Msg_Incorrect, client.PCID, 0, client.Ver)); RemoveClient(client, true, true); #if DEBUG PrintLogs(string.Format("Wrong ID: RemoveClient({0})", client.PCID)); #endif } else { //check pwd if (!Hasher.Verify(str_pwd, AccInfo.pwd, (HashType)g_Config.hashType)) { client.Send(Packet.SayMessage(g_Config.Msg_Incorrect, client.PCID, 0, client.Ver)); RemoveClient(client, true, true); #if DEBUG PrintLogs(string.Format("Wrong PWD: RemoveClient({0})", client.PCID)); #endif } else { //check status if (g_Config.RejectDB.ContainsKey(AccInfo.status)) { client.Send(Packet.SayMessage(g_Config.RejectDB[AccInfo.status], client.PCID, 3, client.Ver)); RemoveClient(client, true, false); #if DEBUG PrintLogs(string.Format("StatusReject: RemoveClient({0})", client.PCID)); #endif } else { #if DEBUG PrintLogs(string.Format("{0} ExpireDate: {1}", AccInfo.id, AccInfo.expDate)); #endif //check expire if (g_Config.isExpiration && DateTime.Now >= AccInfo.expDate) { client.Send(Packet.SayMessage(g_Config.Msg_Expired, client.PCID, 3, client.Ver)); RemoveClient(client, true, false); } else { //log-in succeed: check duplicate connections if (!g_Config.LoggedInList.ContainsKey(str_id)) { string msg = string.Empty; //아직 서버 선택전이므로 로그인 유저 정보의 Sid = -1로 설정함 g_Config.LoggedInList.Add(str_id, new LoginUser(client.PCID, -1)); if (g_Config.isExpiration) { TimeSpan ts = AccInfo.expDate - DateTime.Now; msg = string.Format(g_Config.Msg_ExpireInfo, ts.Days, ts.Hours, ts.Minutes); } client.Send(Packet.CreateSvrList(client.PCID, client.Ver, g_Config.ZoneAgentList, g_Config.ServerList, msg)); PrintLogs(string.Format("SuccLogin: {0}:{1} {2}", client.IPadress, client.Port, str_id)); #if DEBUG PrintLogs(string.Format("RegLoginUser: {0}", str_id)); #endif } else { //duplication act: in LA OR in ZA - uid가 ZaUID의 최소값(65535)이상이면 이미 za로 넘어간 유저 PrintLogs(string.Format("Duplicate login detected {0} {1}", g_Config.LoggedInList[str_id].PCID, str_id)); //za 유저인경우 za에게 디스커넥트 요청을 해야함: 완료시 30패킷을 되돌려받음 if (g_Config.LoggedInList[str_id].PCID > g_Config.za_Uid.MinValue) { zaSession zAgent = g_Config.ZoneAgentList[g_Config.LoggedInList[str_id].ServerID]; zAgent.Send(Packet.reqDisconnectAcc(g_Config.LoggedInList[str_id].PCID, str_id)); PrintLogs(string.Format("Request UserDrop {0} {1} to ZoneAgent", g_Config.LoggedInList[str_id].PCID, str_id)); } g_Config.LoggedInList.Remove(str_id); PrintLogs(string.Format("Previous LoggedInUser.destroy {0}", str_id)); client.Send(Packet.SayMessage(g_Config.Msg_Duplication, client.PCID, 2, client.Ver)); RemoveClient(client, true, false); } } } } } } }
private void UpdateStatus(zaSession session) { form.UpdateZaStatus(); form.UpdateCurrentUsers(); }