From 9202c0201e1e7053ad7ee3de373ce09f9abb50ba Mon Sep 17 00:00:00 2001 From: Adrian Tracz Date: Mon, 14 Apr 2025 16:54:31 +0200 Subject: [PATCH 1/5] Add LS_NEWS server packet support, load news from DB, and handle default INI values Loads news data from a configured ODBC database table. Implements periodic news refresh with a configurable interval (NewsRefreshInterval) from the INI file. Ensures all required INI values are initialized with defaults if missing. --- src/server/LoginServer/DBProcess.cpp | 57 +++++++++++++++++++++++ src/server/LoginServer/DBProcess.h | 2 + src/server/LoginServer/Define.h | 7 +++ src/server/LoginServer/LoginServerDlg.cpp | 54 +++++++++++++++++++++ src/server/LoginServer/LoginServerDlg.h | 9 ++++ src/server/LoginServer/User.cpp | 42 +++++++++++++++++ 6 files changed, 171 insertions(+) diff --git a/src/server/LoginServer/DBProcess.cpp b/src/server/LoginServer/DBProcess.cpp index 692ffecc..d8d06529 100644 --- a/src/server/LoginServer/DBProcess.cpp +++ b/src/server/LoginServer/DBProcess.cpp @@ -306,3 +306,60 @@ BOOL CDBProcess::IsCurrentUser(const char * szAccountId, char * szServerIp, int return retval; } + +BOOL CDBProcess::LoadNews() { + SQLHSTMT hstmt = NULL; + SQLRETURN retcode; + TCHAR szSQL[1024]; + + memset(szSQL, 0x00, 1024); + wsprintf(szSQL, TEXT("SELECT Title, Content, IsActive FROM NEWS WHERE IsActive = 1 AND " + "(StartsAt IS NULL OR StartsAt <= GETDATE()) AND " + "(ExpiresAt IS NULL OR ExpiresAt > GETDATE())")); + + _NEWS newsItem; + memset(&newsItem, 0, sizeof(newsItem)); + + retcode = SQLAllocHandle(SQL_HANDLE_STMT, m_VersionDB.m_hdbc, &hstmt); + if (retcode != SQL_SUCCESS) { + return FALSE; + } + + retcode = SQLExecDirect(hstmt, (unsigned char *)szSQL, 1024); + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) { + if (DisplayErrorMsg(hstmt) == -1) { + m_VersionDB.Close(); + if (!m_VersionDB.IsOpen()) { + ReConnectODBC(&m_VersionDB, m_pMain->m_ODBCName, m_pMain->m_ODBCLogin, m_pMain->m_ODBCPwd); + return FALSE; + } + } + SQLFreeHandle(SQL_HANDLE_STMT, hstmt); + return FALSE; + } + + while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { + retcode = SQLFetch(hstmt); + if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { + SQLCHAR titleBuffer[256] = {0}; + SQLCHAR contentBuffer[4096] = {0}; + + SQLGetData(hstmt, 1, SQL_C_CHAR, titleBuffer, sizeof(titleBuffer), NULL); + SQLGetData(hstmt, 2, SQL_C_CHAR, contentBuffer, sizeof(contentBuffer), NULL); + SQLGetData(hstmt, 3, SQL_C_BIT, &newsItem.IsActive, 0, NULL); + + titleBuffer[sizeof(titleBuffer) - 1] = '\0'; + contentBuffer[sizeof(contentBuffer) - 1] = '\0'; + + strncpy(newsItem.Title, reinterpret_cast(titleBuffer), sizeof(newsItem.Title) - 1); + strncpy(newsItem.Content, reinterpret_cast(contentBuffer), sizeof(newsItem.Content) - 1); + + if (newsItem.IsActive) { + m_pMain->m_ServerNews.push_back(newsItem); + } + } + } + + SQLFreeHandle(SQL_HANDLE_STMT, hstmt); + return TRUE; +} diff --git a/src/server/LoginServer/DBProcess.h b/src/server/LoginServer/DBProcess.h index 793c2ca1..0ccf0023 100644 --- a/src/server/LoginServer/DBProcess.h +++ b/src/server/LoginServer/DBProcess.h @@ -17,6 +17,8 @@ class CDBProcess { int AccountLogin(const char * szId, const char * szPwd); BOOL LoadVersionList(); BOOL LoadUserCountList(); + BOOL LoadNews(); + CDBProcess(); virtual ~CDBProcess(); diff --git a/src/server/LoginServer/Define.h b/src/server/LoginServer/Define.h index 37b1c1ea..2c16308f 100644 --- a/src/server/LoginServer/Define.h +++ b/src/server/LoginServer/Define.h @@ -71,6 +71,12 @@ struct _SERVER_INFO { }; }; +struct _NEWS { + char Title[256]; + char Content[4096]; + bool IsActive; +}; + // Packet Define... #define LS_VERSION_REQ 0x01 @@ -78,6 +84,7 @@ struct _SERVER_INFO { #define LS_LOGIN_REQ 0xF3 #define LS_MGAME_LOGIN 0xF4 #define LS_SERVERLIST 0xF5 +#define LS_NEWS 0xF6 ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// diff --git a/src/server/LoginServer/LoginServerDlg.cpp b/src/server/LoginServer/LoginServerDlg.cpp index 428bf4f6..0c3cb062 100644 --- a/src/server/LoginServer/LoginServerDlg.cpp +++ b/src/server/LoginServer/LoginServerDlg.cpp @@ -47,6 +47,7 @@ BEGIN_MESSAGE_MAP(CLoginServerDlg, CDialog) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_SETTING, OnVersionSetting) +ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -91,6 +92,12 @@ BOOL CLoginServerDlg::OnInitDialog() { AfxPostQuitMessage(0); return FALSE; } + if (!m_DBProcess.LoadNews()) { + AfxMessageBox("Load News Data Fail!!"); + AfxPostQuitMessage(0); + return FALSE; + } + StartNewsRefreshTimer(); m_OutputList.AddString(szConnectionString); std::string szVersion = std::format("Latest Version : {:d}", m_nLastVersion); @@ -105,6 +112,21 @@ BOOL CLoginServerDlg::GetInfoFromIni() { std::string szIniFile = (n3std::get_app_dir() / "Version.ini").string(); const char * pszIniFile = szIniFile.c_str(); + DWORD dwAttrib = GetFileAttributesA(pszIniFile); + if (dwAttrib == INVALID_FILE_ATTRIBUTES) { + WritePrivateProfileString("DOWNLOAD", "URL", "ftp.your-site.net", pszIniFile); + WritePrivateProfileString("DOWNLOAD", "PATH", "/", pszIniFile); + WritePrivateProfileString("ODBC", "DSN", "kodb", pszIniFile); + WritePrivateProfileString("ODBC", "UID", "kodb_user", pszIniFile); + WritePrivateProfileString("ODBC", "PWD", "kodb_user", pszIniFile); + WritePrivateProfileString("ODBC", "TABLE", "VERSION", pszIniFile); + WritePrivateProfileString("CONFIGURATION", "DEFAULT_PATH", "", pszIniFile); + WritePrivateProfileString("CONFIGURATION", "NewsRefreshInterval", "30", pszIniFile); + WritePrivateProfileString("SERVER_LIST", "COUNT", "1", pszIniFile); + WritePrivateProfileString("SERVER_LIST", "SERVER_00", "127.0.0.1", pszIniFile); + WritePrivateProfileString("SERVER_LIST", "NAME_00", "SERVER_01", pszIniFile); + } + char szBuff[500]{}; GetPrivateProfileString("DOWNLOAD", "URL", "ftp.your-site.net", szBuff, sizeof(szBuff), pszIniFile); m_szFtpUrl = szBuff; @@ -137,6 +159,11 @@ BOOL CLoginServerDlg::GetInfoFromIni() { return FALSE; } + m_NewsRefreshInterval = GetPrivateProfileInt("CONFIGURATION", "NewsRefreshInterval", -1, pszIniFile); + if (m_NewsRefreshInterval <= 0) { + m_NewsRefreshInterval = 15; + } + m_ServerList.reserve(20); for (int i = 0; i < m_nServerCount; i++) { _SERVER_INFO * pInfo = new _SERVER_INFO; @@ -216,3 +243,30 @@ void CLoginServerDlg::OnVersionSetting() { WritePrivateProfileStringW(L"CONFIGURATION", L"DEFAULT_PATH", m_fsDefaultDir.c_str(), fsIniPath.c_str()); } } + +void CLoginServerDlg::StartNewsRefreshTimer() { + if (m_NewsRefreshTimerID != 0) { + KillTimer(m_NewsRefreshTimerID); + } + + m_NewsRefreshTimerID = SetTimer(1, m_NewsRefreshInterval * 60 * 1000, NULL); + if (m_NewsRefreshTimerID == 0) { + TRACE("Timer failed to set!"); + } +} + +void CLoginServerDlg::RefreshNewsData() { + m_ServerNews.clear(); + if (m_DBProcess.LoadNews()) { + TRACE("News Data Refreshed Successfully!"); + } else { + TRACE("Failed to Refresh News Data!"); + } +} + +void CLoginServerDlg::OnTimer(UINT_PTR nIDEvent) { + if (nIDEvent == m_NewsRefreshTimerID) { + RefreshNewsData(); + } + CDialog::OnTimer(nIDEvent); +} diff --git a/src/server/LoginServer/LoginServerDlg.h b/src/server/LoginServer/LoginServerDlg.h index 20dfbc61..6ec57b69 100644 --- a/src/server/LoginServer/LoginServerDlg.h +++ b/src/server/LoginServer/LoginServerDlg.h @@ -14,11 +14,14 @@ typedef CSTLMap VersionInfoList; // TODO: does the key need to be fs::path? typedef std::vector<_SERVER_INFO *> ServerInfoList; +typedef std::vector<_SERVER_INFO *> ServerInfoList; class CLoginServerDlg : public CDialog { // Construction public: BOOL GetInfoFromIni(); + void StartNewsRefreshTimer(); + void RefreshNewsData(); CLoginServerDlg(CWnd * pParent = NULL); // standard constructor @@ -40,6 +43,10 @@ class CLoginServerDlg : public CDialog { VersionInfoList m_VersionList; ServerInfoList m_ServerList; int m_nServerCount; + std::vector<_NEWS> m_ServerNews; + int m_NewsRefreshInterval = 30; // Minutes + UINT_PTR m_NewsRefreshTimerID = 0; + CDBProcess m_DBProcess; @@ -60,6 +67,8 @@ class CLoginServerDlg : public CDialog { protected: virtual void DoDataExchange(CDataExchange * pDX); // DDX/DDV support //}}AFX_VIRTUAL + protected: + afx_msg void OnTimer(UINT_PTR nIDEvent); // Implementation protected: diff --git a/src/server/LoginServer/User.cpp b/src/server/LoginServer/User.cpp index b54d959f..a76e88e4 100644 --- a/src/server/LoginServer/User.cpp +++ b/src/server/LoginServer/User.cpp @@ -71,6 +71,48 @@ void CUser::Parsing(int len, char * pData) { case LS_MGAME_LOGIN: MgameLogin(pData + index); break; + case LS_NEWS: { + constexpr char BOX_START[] = {'#', '\0', '\n'}; + constexpr char LINE_ENDING[] = {'\0', '\n'}; + constexpr char BOX_END[] = {'#', '\0', '\n', '\0', '\n'}; + + std::ostringstream oss; + + for (const auto & news : m_pMain->m_ServerNews) { + if (!news.IsActive) { + continue; + } + + oss << news.Title; + oss.write(BOX_START, sizeof(BOX_START)); + oss << news.Content; + oss.write(LINE_ENDING, sizeof(LINE_ENDING)); + oss.write(BOX_END, sizeof(BOX_END)); + } + + std::string fullContent = oss.str(); + std::string header = "Login Notice"; + + SetByte(buff, LS_NEWS, send_index); + + int headerLen = static_cast(header.size()); + SetShort(buff, headerLen, send_index); + SetString(buff, header.data(), headerLen, send_index); + + if (!fullContent.empty()) { + uint16_t size = static_cast(fullContent.size()); + SetShort(buff, size, send_index); + SetString(buff, fullContent.data(), size, send_index); + } else { + std::string emptyStr = ""; + int emptyLen = static_cast(emptyStr.size()); + SetShort(buff, emptyLen, send_index); + SetString(buff, emptyStr.data(), emptyLen, send_index); + } + + Send(buff, send_index); + break; + } } } void CUser::LogInReq(char * pBuf) { From d8f1d334b88ebd1cd77463841b9732b5f54eaa62 Mon Sep 17 00:00:00 2001 From: Adrian Tracz Date: Mon, 14 Apr 2025 20:01:11 +0200 Subject: [PATCH 2/5] Fix LS_NEWS packet headers --- src/server/LoginServer/DBProcess.cpp | 4 ++-- src/server/LoginServer/User.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/LoginServer/DBProcess.cpp b/src/server/LoginServer/DBProcess.cpp index d8d06529..0ddce699 100644 --- a/src/server/LoginServer/DBProcess.cpp +++ b/src/server/LoginServer/DBProcess.cpp @@ -348,8 +348,8 @@ BOOL CDBProcess::LoadNews() { SQLGetData(hstmt, 2, SQL_C_CHAR, contentBuffer, sizeof(contentBuffer), NULL); SQLGetData(hstmt, 3, SQL_C_BIT, &newsItem.IsActive, 0, NULL); - titleBuffer[sizeof(titleBuffer) - 1] = '\0'; - contentBuffer[sizeof(contentBuffer) - 1] = '\0'; + titleBuffer[sizeof(titleBuffer) - 1]; + contentBuffer[sizeof(contentBuffer) - 1]; strncpy(newsItem.Title, reinterpret_cast(titleBuffer), sizeof(newsItem.Title) - 1); strncpy(newsItem.Content, reinterpret_cast(contentBuffer), sizeof(newsItem.Content) - 1); diff --git a/src/server/LoginServer/User.cpp b/src/server/LoginServer/User.cpp index a76e88e4..68aff9b0 100644 --- a/src/server/LoginServer/User.cpp +++ b/src/server/LoginServer/User.cpp @@ -72,9 +72,9 @@ void CUser::Parsing(int len, char * pData) { MgameLogin(pData + index); break; case LS_NEWS: { - constexpr char BOX_START[] = {'#', '\0', '\n'}; - constexpr char LINE_ENDING[] = {'\0', '\n'}; - constexpr char BOX_END[] = {'#', '\0', '\n', '\0', '\n'}; + constexpr char BOX_START[] = {'#', '\0'}; + constexpr char LINE_ENDING[] = {'\0'}; + constexpr char BOX_END[] = {'#', '\0'}; std::ostringstream oss; From 0f0acf070dfa5a16d1df764727745b1a97bcbbe9 Mon Sep 17 00:00:00 2001 From: Adrian Tracz Date: Mon, 14 Apr 2025 20:04:27 +0200 Subject: [PATCH 3/5] Add LS_NEWS packet handling and news display support on client side Parsing and handling of the LS_NEWS packet received from the login server. News UI rendering and formatting. --- src/game/GameProcLogIn.cpp | 77 +++++++++++++++++++++++++++++++++++++- src/game/GameProcLogIn.h | 2 + src/game/PacketDef.h | 1 + src/game/UILogin.cpp | 37 ++++++++++++++++++ src/game/UILogin.h | 10 +++++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/src/game/GameProcLogIn.cpp b/src/game/GameProcLogIn.cpp index d206cd19..a8229154 100644 --- a/src/game/GameProcLogIn.cpp +++ b/src/game/GameProcLogIn.cpp @@ -326,6 +326,19 @@ bool CGameProcLogIn::MsgSend_AccountLogIn(e_LogInClassification eLIC) { return true; } + +bool CGameProcLogIn::MsgSend_NewsRequestData() { + BYTE byBuffer[4]; + int iOffset = 0; + + CAPISocket::MP_AddByte(byBuffer, iOffset, N3_LOGIN_NEWS_REQUEST); + + s_pSocket->Send(byBuffer, iOffset); + + return true; +} + + void CGameProcLogIn::MsgRecv_GameServerGroupList(DataPack * pDataPack, int & iOffset) { int iServerCount = CAPISocket::Parse_GetByte(pDataPack->m_pData, iOffset); // 서버 갯수 for (int i = 0; i < iServerCount; i++) { @@ -496,10 +509,14 @@ bool CGameProcLogIn::ProcessPacket(DataPack * pDataPack, int & iOffset) { case N3_GAMESERVER_GROUP_LIST: // 접속하면 바로 보내준다.. this->MsgRecv_GameServerGroupList(pDataPack, iOffset); return true; - + case N3_LOGIN_NEWS_REQUEST: + N3_DEBUG("N3_LOGIN_NEWS_REQUEST"); + this->MsgRecv_News(pDataPack, iOffset); + return true; case N3_ACCOUNT_LOGIN: // 계정 접속 성공.. case N3_ACCOUNT_LOGIN_MGAME: // MGame 계정 접속 성공.. this->MsgRecv_AccountLogIn(iCmd, pDataPack, iOffset); + this->MsgSend_NewsRequestData(); return true; } @@ -524,6 +541,64 @@ void CGameProcLogIn::ConnectToGameServer() // 고른 게임 서버에 접속 this->MsgSend_VersionCheck(); } } +void CGameProcLogIn::MsgRecv_News(DataPack * pDataPack, int & iOffset) { + constexpr int maxNewsCount = 3; + int newsCount = 0; + + int headerLen = CAPISocket::Parse_GetShort(pDataPack->m_pData, iOffset); + std::string szHeader; + if (headerLen > 0) { + CAPISocket::Parse_GetString(pDataPack->m_pData, iOffset, szHeader, headerLen); + } + + int fullContentLen = CAPISocket::Parse_GetShort(pDataPack->m_pData, iOffset); + std::string fullContent; + if (fullContentLen > 0) { + CAPISocket::Parse_GetString(pDataPack->m_pData, iOffset, fullContent, fullContentLen); + } + + fullContent.erase(std::remove(fullContent.begin(), fullContent.end(), '\0'), fullContent.end()); + + std::vector tokens; + { + size_t start = 0; + while (true) { + size_t pos = fullContent.find('#', start); + if (pos == std::string::npos) { + std::string token = fullContent.substr(start); + tokens.push_back(token); + break; + } else { + std::string token = fullContent.substr(start, pos - start); + tokens.push_back(token); + start = pos + 1; + } + } + } + + auto trim = [](const std::string & s) -> std::string { + const std::string whitespace = " \t\n\r"; + size_t start = s.find_first_not_of(whitespace); + if (start == std::string::npos) { + return ""; + } + size_t end = s.find_last_not_of(whitespace); + return s.substr(start, end - start + 1); + }; + + for (size_t i = 0; i + 1 < tokens.size() && newsCount < maxNewsCount; i += 2) { + std::string title = trim(tokens[i]); + std::string content = trim(tokens[i + 1]); + + m_pUILogIn->m_loginNewsTitle[newsCount] = title; + m_pUILogIn->m_loginNewsText[newsCount] = content; + newsCount++; + } + + m_pUILogIn->m_loginNewsCount = newsCount; + m_pUILogIn->LoadNewsData(); +} + // By : Ecli666 ( On 2002-07-15 오후 7:35:16 ) // /* diff --git a/src/game/GameProcLogIn.h b/src/game/GameProcLogIn.h index 2aa7f2b9..eeae4647 100644 --- a/src/game/GameProcLogIn.h +++ b/src/game/GameProcLogIn.h @@ -24,8 +24,10 @@ class CGameProcLogIn : public CGameProcedure { void MsgRecv_AccountLogIn(int iCmd, DataPack * pDataPack, int & iOffset); int MsgRecv_VersionCheck(DataPack * pDataPack, int & iOffset); // virtual int MsgRecv_GameServerLogIn(DataPack * pDataPack, int & iOffset); // virtual - 국가 번호를 리턴한다. + void MsgRecv_News(DataPack * pDataPack, int & iOffset); bool MsgSend_AccountLogIn(enum e_LogInClassification eLIC); + bool MsgSend_NewsRequestData(); void Release(); void Init(); diff --git a/src/game/PacketDef.h b/src/game/PacketDef.h index a47639f9..011c1989 100644 --- a/src/game/PacketDef.h +++ b/src/game/PacketDef.h @@ -7,6 +7,7 @@ const int SOCKET_PORT_LOGIN = 15100; // 소켓이 붙을 포트 번호.. #define N3_ACCOUNT_LOGIN_MGAME 0xf4 // !!! LOGIN_REQ !!! 와 동일 #define N3_GAMESERVER_GROUP_LIST \ 0xf5 // Recv - b1(ServerCount) loop( s1(IP길이), str1(IP문자열), s1(서버이름길이), str1(서버이름문자열), s1(서버접속인원) ) +#define N3_LOGIN_NEWS_REQUEST 0xf6 #define N3_GAME_SERVER_LOGIN 0x01 // Game server 에 Log in.. #define N3_NEW_CHARACTER 0x02 // 캐릭터 새로 만들기.. diff --git a/src/game/UILogin.cpp b/src/game/UILogin.cpp index ebf975ac..46264de0 100644 --- a/src/game/UILogin.cpp +++ b/src/game/UILogin.cpp @@ -10,6 +10,7 @@ #include "N3Base/N3UIEdit.h" #include "N3Base/N3UIButton.h" #include "N3Base/N3UIList.h" +#include "N3Base/N3UIString.h"; #include "UIMessageBoxManager.h" #include @@ -35,6 +36,10 @@ CUILogIn::CUILogIn() { m_pImg_MGameLogo = NULL; m_pImg_DaumLogo = NULL; + m_pGroup_Notice[1] = NULL; + m_pGroup_Notice[2] = NULL; + m_pGroup_Notice[3] = NULL; + m_pList_Server = NULL; m_bOpenningNow = false; // 위에서 아래로 스르륵...열려야 한다면.. @@ -369,6 +374,38 @@ void CUILogIn::SetVisibleLogInUIs(bool bEnable) { } } +void CUILogIn::LoadNewsData() { + std::string szGroupNotice = "Group_Notice_"; + + for (size_t i = 0; i < m_loginNewsCount; ++i) { + CN3UIBase * pGroupNotice = GetChildByID(szGroupNotice + std::to_string(i + 1)); + if (pGroupNotice) { + CN3UIString * pNoticeTitle = (CN3UIString *)pGroupNotice->GetChildByID("text_notice_name"); + CN3UIString * pNoticeText = (CN3UIString *)pGroupNotice->GetChildByID("text_notice"); + + if (pNoticeTitle) { + pNoticeTitle->SetString(m_loginNewsTitle[i].c_str()); + } + if (pNoticeText) { + pNoticeText->SetString(m_loginNewsText[i].c_str()); + } + + int x = (CN3Base::s_CameraData.vp.Width / 2) - 40; + int y = (CN3Base::s_CameraData.vp.Height / 2); + + if (i == 0) { + pGroupNotice->MoveOffset(x, 30 + 170 - y); + } else if (i == 1) { + pGroupNotice->MoveOffset(x, 30 + 342 - y); + } else if (i == 2) { + pGroupNotice->MoveOffset(x, 30 + 518 - y); + } + + pGroupNotice->SetVisible(true); + } + } +} + bool CUILogIn::OnKeyPress(int iKey) { if (!m_bLogIn) { switch (iKey) { diff --git a/src/game/UILogin.h b/src/game/UILogin.h index e90fe0cf..bae3c335 100644 --- a/src/game/UILogin.h +++ b/src/game/UILogin.h @@ -44,6 +44,10 @@ class CUILogIn : public CN3UIBase { CN3UIBase * m_pImg_MGameLogo; CN3UIBase * m_pImg_DaumLogo; + CN3UIBase * m_pGroup_Notice[3]; + CN3UIString * m_pNotice_Title[3]; + CN3UIString * m_pNotice_Text[3]; + CN3UIList * m_pList_Server; std::vector<__GameServerInfo> m_ListServerInfos; @@ -53,6 +57,11 @@ class CUILogIn : public CN3UIBase { bool m_bLogIn; // 로그인 중복 방지.. public: + std::string m_loginNewsTitle[3]; + std::string m_loginNewsText[3]; + int m_loginNewsCount = 0; + + void SetRequestedLogIn(bool bLogIn) { m_bLogIn = bLogIn; } bool OnKeyPress(int iKey); void SetVisibleLogInUIs(bool bEnable); // 계정 LogIn 에 필요한 UI 들을 숨긴다.. @@ -70,6 +79,7 @@ class CUILogIn : public CN3UIBase { bool ServerInfoGet(int iIndex, __GameServerInfo & GSI); bool ServerInfoGetCur(__GameServerInfo & GSI); void ServerInfoUpdate(); + void LoadNewsData(); void AccountIDGet(std::string & szID); void AccountPWGet(std::string & szPW); From 3950fce7db07013e3ee0cbc2dce748258f8b4d9f Mon Sep 17 00:00:00 2001 From: Adrian Tracz Date: Mon, 14 Apr 2025 20:26:02 +0200 Subject: [PATCH 4/5] Clang format --- src/game/GameProcLogIn.cpp | 2 -- src/game/PacketDef.h | 2 +- src/game/UILogin.h | 7 +++---- src/server/LoginServer/DBProcess.cpp | 4 ++-- src/server/LoginServer/DBProcess.h | 1 - src/server/LoginServer/LoginServerDlg.cpp | 2 +- src/server/LoginServer/LoginServerDlg.h | 9 ++++----- 7 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/game/GameProcLogIn.cpp b/src/game/GameProcLogIn.cpp index a8229154..599360e0 100644 --- a/src/game/GameProcLogIn.cpp +++ b/src/game/GameProcLogIn.cpp @@ -326,7 +326,6 @@ bool CGameProcLogIn::MsgSend_AccountLogIn(e_LogInClassification eLIC) { return true; } - bool CGameProcLogIn::MsgSend_NewsRequestData() { BYTE byBuffer[4]; int iOffset = 0; @@ -338,7 +337,6 @@ bool CGameProcLogIn::MsgSend_NewsRequestData() { return true; } - void CGameProcLogIn::MsgRecv_GameServerGroupList(DataPack * pDataPack, int & iOffset) { int iServerCount = CAPISocket::Parse_GetByte(pDataPack->m_pData, iOffset); // 서버 갯수 for (int i = 0; i < iServerCount; i++) { diff --git a/src/game/PacketDef.h b/src/game/PacketDef.h index 011c1989..6e5e28b6 100644 --- a/src/game/PacketDef.h +++ b/src/game/PacketDef.h @@ -7,7 +7,7 @@ const int SOCKET_PORT_LOGIN = 15100; // 소켓이 붙을 포트 번호.. #define N3_ACCOUNT_LOGIN_MGAME 0xf4 // !!! LOGIN_REQ !!! 와 동일 #define N3_GAMESERVER_GROUP_LIST \ 0xf5 // Recv - b1(ServerCount) loop( s1(IP길이), str1(IP문자열), s1(서버이름길이), str1(서버이름문자열), s1(서버접속인원) ) -#define N3_LOGIN_NEWS_REQUEST 0xf6 +#define N3_LOGIN_NEWS_REQUEST 0xf6 #define N3_GAME_SERVER_LOGIN 0x01 // Game server 에 Log in.. #define N3_NEW_CHARACTER 0x02 // 캐릭터 새로 만들기.. diff --git a/src/game/UILogin.h b/src/game/UILogin.h index bae3c335..a9fc1e5f 100644 --- a/src/game/UILogin.h +++ b/src/game/UILogin.h @@ -57,10 +57,9 @@ class CUILogIn : public CN3UIBase { bool m_bLogIn; // 로그인 중복 방지.. public: - std::string m_loginNewsTitle[3]; - std::string m_loginNewsText[3]; - int m_loginNewsCount = 0; - + std::string m_loginNewsTitle[3]; + std::string m_loginNewsText[3]; + int m_loginNewsCount = 0; void SetRequestedLogIn(bool bLogIn) { m_bLogIn = bLogIn; } bool OnKeyPress(int iKey); diff --git a/src/server/LoginServer/DBProcess.cpp b/src/server/LoginServer/DBProcess.cpp index 0ddce699..90c5e3e2 100644 --- a/src/server/LoginServer/DBProcess.cpp +++ b/src/server/LoginServer/DBProcess.cpp @@ -341,8 +341,8 @@ BOOL CDBProcess::LoadNews() { while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLFetch(hstmt); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { - SQLCHAR titleBuffer[256] = {0}; - SQLCHAR contentBuffer[4096] = {0}; + SQLCHAR titleBuffer[256] = {0}; + SQLCHAR contentBuffer[4096] = {0}; SQLGetData(hstmt, 1, SQL_C_CHAR, titleBuffer, sizeof(titleBuffer), NULL); SQLGetData(hstmt, 2, SQL_C_CHAR, contentBuffer, sizeof(contentBuffer), NULL); diff --git a/src/server/LoginServer/DBProcess.h b/src/server/LoginServer/DBProcess.h index 0ccf0023..7dde22c5 100644 --- a/src/server/LoginServer/DBProcess.h +++ b/src/server/LoginServer/DBProcess.h @@ -19,7 +19,6 @@ class CDBProcess { BOOL LoadUserCountList(); BOOL LoadNews(); - CDBProcess(); virtual ~CDBProcess(); diff --git a/src/server/LoginServer/LoginServerDlg.cpp b/src/server/LoginServer/LoginServerDlg.cpp index 0c3cb062..30520da5 100644 --- a/src/server/LoginServer/LoginServerDlg.cpp +++ b/src/server/LoginServer/LoginServerDlg.cpp @@ -266,7 +266,7 @@ void CLoginServerDlg::RefreshNewsData() { void CLoginServerDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == m_NewsRefreshTimerID) { - RefreshNewsData(); + RefreshNewsData(); } CDialog::OnTimer(nIDEvent); } diff --git a/src/server/LoginServer/LoginServerDlg.h b/src/server/LoginServer/LoginServerDlg.h index 6ec57b69..5b8e8b00 100644 --- a/src/server/LoginServer/LoginServerDlg.h +++ b/src/server/LoginServer/LoginServerDlg.h @@ -40,13 +40,12 @@ class CLoginServerDlg : public CDialog { char m_ODBCPwd[32]; char m_TableName[32]; - VersionInfoList m_VersionList; - ServerInfoList m_ServerList; - int m_nServerCount; + VersionInfoList m_VersionList; + ServerInfoList m_ServerList; + int m_nServerCount; std::vector<_NEWS> m_ServerNews; int m_NewsRefreshInterval = 30; // Minutes - UINT_PTR m_NewsRefreshTimerID = 0; - + UINT_PTR m_NewsRefreshTimerID = 0; CDBProcess m_DBProcess; From cfd6c72d501f29e5d3869ec20a9bc2f63464a869 Mon Sep 17 00:00:00 2001 From: Adrian Tracz Date: Tue, 15 Apr 2025 07:35:13 +0200 Subject: [PATCH 5/5] Update SQL query to filter by NoticeType and remove unused buffer access --- src/server/LoginServer/DBProcess.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/server/LoginServer/DBProcess.cpp b/src/server/LoginServer/DBProcess.cpp index 90c5e3e2..bc523546 100644 --- a/src/server/LoginServer/DBProcess.cpp +++ b/src/server/LoginServer/DBProcess.cpp @@ -315,7 +315,8 @@ BOOL CDBProcess::LoadNews() { memset(szSQL, 0x00, 1024); wsprintf(szSQL, TEXT("SELECT Title, Content, IsActive FROM NEWS WHERE IsActive = 1 AND " "(StartsAt IS NULL OR StartsAt <= GETDATE()) AND " - "(ExpiresAt IS NULL OR ExpiresAt > GETDATE())")); + "(ExpiresAt IS NULL OR ExpiresAt > GETDATE()) AND " + "NoticeType = 'login'")); _NEWS newsItem; memset(&newsItem, 0, sizeof(newsItem)); @@ -348,9 +349,6 @@ BOOL CDBProcess::LoadNews() { SQLGetData(hstmt, 2, SQL_C_CHAR, contentBuffer, sizeof(contentBuffer), NULL); SQLGetData(hstmt, 3, SQL_C_BIT, &newsItem.IsActive, 0, NULL); - titleBuffer[sizeof(titleBuffer) - 1]; - contentBuffer[sizeof(contentBuffer) - 1]; - strncpy(newsItem.Title, reinterpret_cast(titleBuffer), sizeof(newsItem.Title) - 1); strncpy(newsItem.Content, reinterpret_cast(contentBuffer), sizeof(newsItem.Content) - 1);