MuOnlline增加自动注册

  1. 在 JoinServer.cpp 中添加配置变量

    // 在 main() 函数之前或全局变量区域添加
    int AutoReg; // 自动注册开关

    // 在 main() 函数中读取配置
    AutoReg = GetPrivateProfileInt("AccountInfo", "AutoReg", 0, ".\JoinServer.ini");

  2. 修改 JoinServer.h 添加 extern 声明

    extern int AutoReg;

  3. 在 JoinServerProtocol.cpp 添加辅助函数

    // 添加一个转义函数(如果没有的话)
    static void EscapeString(char output, const char input, int outputSize)
    {

     int i = 0, j = 0;
     for (; input[i] != '\0' && j < outputSize - 1; i++)
     {
         if (input[i] == '\'' || input[i] == '\\')
         {
             if (j + 1 >= outputSize - 1) break;
             output[j++] = '\'';
             output[j++] = input[i];
         }
         else
         {
             output[j++] = input[i];
         }
     }
     output[j] = '\0';

    }

    // 自动注册函数
    static bool DoAutoRegister(const char account, const char password, const char* ipAddress)
    {

     char escapedAccount[64] = { 0 };
     char escapedPassword[64] = { 0 };
     
     EscapeString(escapedAccount, account, sizeof(escapedAccount));
     
     if (MD5Encryption == 1)
     {
         // MD5 加密模式
         MD5 MD5Hash;
         BYTE md5Hash[16] = { 0 };
         
         if (MD5Encryption == 1)
         {
             MD5Hash.MD5_Compute(password, (char*)md5Hash, MakeAccountKey((char*)account));
         }
         else
         {
             MD5Hash.MD5_Compute(password, (char*)md5Hash);
         }
         
         // 将 MD5 哈希转换为十六进制字符串存储
         char hexPassword[33] = { 0 };
         for (int i = 0; i < 16; i++)
         {
             sprintf(hexPassword + (i * 2), "%02X", md5Hash[i]);
         }
         
     #ifndef MYSQL
         char query[1024];
         sprintf(query, "INSERT INTO MEMB_INFO (memb___id, memb__pwd, memb_name, sno__numb, bloc_code, ctl1_code) "
                        "VALUES ('%s', 0x%s, 'client', '1111111111111', 0, 1)", 
                        escapedAccount, hexPassword);
         return gQueryManager.ExecUpdateQuery(query);
     #else
         // MySQL 使用 UNHEX 存储二进制
         char query[1024];
         sprintf(query, "INSERT INTO MEMB_INFO (memb___id, memb__pwd, memb_name, sno__numb, bloc_code, ctl1_code) "
                        "VALUES ('%s', UNHEX('%s'), 'client', '1111111111111', 0, 1)", 
                        escapedAccount, hexPassword);
         return gQueryManager.ExecUpdateQuery(query);
     #endif
     }
     else
     {
         // 明文密码模式
         EscapeString(escapedPassword, password, sizeof(escapedPassword));
         
     #ifndef MYSQL
         char query[1024];
         sprintf(query, "INSERT INTO MEMB_INFO (memb___id, memb__pwd, memb_name, sno__numb, bloc_code, ctl1_code) "
                        "VALUES ('%s', '%s', 'client', '1111111111111', 0, 1)", 
                        escapedAccount, escapedPassword);
         return gQueryManager.ExecUpdateQuery(query);
     #else
         char query[1024];
         sprintf(query, "INSERT INTO MEMB_INFO (memb___id, memb__pwd, memb_name, sno__numb, bloc_code, ctl1_code) "
                        "VALUES ('%s', '%s', 'client', '1111111111111', 0, 1)", 
                        escapedAccount, escapedPassword);
         return gQueryManager.ExecUpdateQuery(query);
     #endif
     }

    }

  4. 修改 GJConnectAccountRecv 函数

    void GJConnectAccountRecv(SDHP_CONNECT_ACCOUNT_RECV* lpMsg, int index)
    {

     SDHP_CONNECT_ACCOUNT_SEND pMsg;
     pMsg.header.set(0x01, sizeof(pMsg));
     pMsg.index = lpMsg->index;
     memcpy(pMsg.account, lpMsg->account, sizeof(pMsg.account));
     pMsg.result = 1;
    
     // 账号密码长度验证 (4-10位)
     int accountLen = strlen(lpMsg->account);
     int passwordLen = strlen(lpMsg->password);
    
     if (accountLen < 4 || accountLen > 10 || passwordLen < 4 || passwordLen > 10)
     {
         pMsg.result = 2;
         gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
         LogAdd(LOG_RED, "[Login] Failed - invalid length (acc:%d, pwd:%d) account:%s", 
                accountLen, passwordLen, lpMsg->account);
         return;
     }
    
     // 检查账号是否已在线
     ACCOUNT_INFO AccountInfo;
     if (gAccountManager.GetAccountInfo(&AccountInfo, lpMsg->account) != false)
     {
         pMsg.result = 3;
         gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
         JGAccountAlreadyConnectedSend(AccountInfo.GameServerCode, AccountInfo.UserIndex, AccountInfo.Account);
         return;
     }
    
     // 检查在线账号数量限制
     if (gAccountManager.GetAccountCount() >= MAX_ACCOUNT)
     {
         pMsg.result = 4;
         gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
         return;
     }
    
     // 转义账号名防止 SQL 注入
     char escapedAccount[64] = { 0 };
     EscapeString(escapedAccount, lpMsg->account, sizeof(escapedAccount));
    
     // 检查账号是否存在
     bool accountExists = false;
     char storedPassword[64] = { 0 };
     

    #ifndef MYSQL

     if (gQueryManager.ExecQuery("SELECT memb__pwd FROM MEMB_INFO WHERE memb___id='%s' COLLATE Latin1_General_BIN", escapedAccount))

    #else

     if (gQueryManager.ExecResultQuery("SELECT memb__pwd FROM MEMB_INFO WHERE memb___id='%s'", escapedAccount))

    #endif

     {
         if (gQueryManager.Fetch() != SQL_NO_DATA)
         {
             accountExists = true;
             
             if (MD5Encryption == 1)
             {
                 // MD5 模式获取二进制密码
                 gQueryManager.GetAsBinary("memb__pwd", (BYTE*)storedPassword, sizeof(storedPassword));
             }
             else
             {
                 // 明文模式获取密码
                 gQueryManager.GetAsString("memb__pwd", storedPassword, sizeof(storedPassword) - 1);
             }
         }
         gQueryManager.Close();
     }
    
     // ========== 账号不存在且开启自动注册 ==========
     if (!accountExists)
     {
         extern int AutoReg;
         
         if (AutoReg == 1)
         {
             LogAdd(LOG_GREEN, "[AutoReg] Account '%s' not exists, auto registering from client", lpMsg->account);
             
             if (DoAutoRegister(lpMsg->account, lpMsg->password, lpMsg->IpAddress))
             {
                 // 注册成功,标记账号现在存在,并设置密码以便后续验证
                 accountExists = true;
                 
                 if (MD5Encryption == 1)
                 {
                     // 重新计算密码哈希用于验证
                     MD5 MD5Hash;
                     BYTE md5Hash[16] = { 0 };
                     if (MD5Encryption == 1)
                     {
                         MD5Hash.MD5_Compute(lpMsg->password, (char*)md5Hash, MakeAccountKey((char*)lpMsg->account));
                     }
                     else
                     {
                         MD5Hash.MD5_Compute(lpMsg->password, (char*)md5Hash);
                     }
                     memcpy(storedPassword, md5Hash, 16);
                 }
                 else
                 {
                     strcpy_s(storedPassword, sizeof(storedPassword), lpMsg->password);
                 }
                 
                 LogAdd(LOG_GREEN, "[AutoReg] Successfully registered account: '%s' from client", lpMsg->account);
             }
             else
             {
                 pMsg.result = 2;
                 gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
                 LogAdd(LOG_RED, "[AutoReg] Failed to register account: '%s'", lpMsg->account);
                 return;
             }
         }
         else
         {
             pMsg.result = 2;
             gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
             return;
         }
     }
    
     // ========== 密码验证 ==========
     if (!accountExists)
     {
         pMsg.result = 2;
         gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
         return;
     }
    
     bool passwordValid = false;
     
     if (MD5Encryption == 1)
     {
         MD5 MD5Hash;
         BYTE md5Hash[16] = { 0 };
         
         if (MD5Encryption == 1)
         {
             MD5Hash.MD5_Compute(lpMsg->password, (char*)md5Hash, MakeAccountKey((char*)lpMsg->account));
         }
         else
         {
             MD5Hash.MD5_Compute(lpMsg->password, (char*)md5Hash);
         }
         
         if (memcmp(md5Hash, storedPassword, 16) == 0)
         {
             passwordValid = true;
         }
     }
     else
     {
         if (strcmp(lpMsg->password, storedPassword) == 0)
         {
             passwordValid = true;
         }
     }
     
     // 检查超级密码
     extern char GlobalPassword[11];
     if (!passwordValid && strcmp(lpMsg->password, GlobalPassword) == 0)
     {
         passwordValid = true;
         LogAdd(LOG_RED, "IP [%s] has logged in '%s' account with global password!", lpMsg->IpAddress, lpMsg->account);
     }
     
     if (!passwordValid)
     {
         pMsg.result = 0;  // 密码错误
         gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
         LogAdd(LOG_RED, "[Login] Password error for account: %s from IP: %s", lpMsg->account, lpMsg->IpAddress);
         return;
     }
    
     // ========== 后续处理(解封、获取账号信息等)==========

    #ifndef MYSQL

     gQueryManager.ExecQuery("EXEC WZ_DesblocAccount '%s'", escapedAccount);
     gQueryManager.Close();
     gQueryManager.ExecQuery("EXEC WZ_DesblocCharacters '%s'", escapedAccount);
     gQueryManager.Close();

    #else

     gQueryManager.ExecUpdateQuery("CALL WZ_DesblocAccount('%s')", escapedAccount);
     gQueryManager.Close();
     gQueryManager.ExecUpdateQuery("CALL WZ_DesblocCharacters('%s')", escapedAccount);
     gQueryManager.Close();

    #endif

     // 获取个人信息

    #ifndef MYSQL

     if (gQueryManager.ExecQuery("SELECT sno__numb, bloc_code FROM MEMB_INFO WHERE memb___id='%s'", escapedAccount) == false || gQueryManager.Fetch() == SQL_NO_DATA)

    #else

     if (gQueryManager.ExecResultQuery("SELECT sno__numb, bloc_code FROM MEMB_INFO WHERE memb___id='%s'", escapedAccount) == false || gQueryManager.Fetch() == false)

    #endif

     {
         gQueryManager.Close();
         pMsg.result = 2;
         gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
         return;
     }
    
     gQueryManager.GetAsString("sno__numb", pMsg.PersonalCode, sizeof(pMsg.PersonalCode));
     pMsg.BlockCode = (BYTE)gQueryManager.GetAsInteger("bloc_code");
     gQueryManager.Close();
    
     // 获取账号等级

    #ifndef MYSQL

     if (gQueryManager.ExecQuery("EXEC WZ_GetAccountLevel '%s'", escapedAccount) == false || gQueryManager.Fetch() == SQL_NO_DATA)

    #else

     if (gQueryManager.ExecResultQuery("CALL WZ_GetAccountLevel('%s')", escapedAccount) == false || gQueryManager.Fetch() == false)

    #endif

     {
         gQueryManager.Close();
         pMsg.AccountLevel = 0;
         memset(pMsg.AccountExpireDate, 0, sizeof(pMsg.AccountExpireDate));
     }
     else
     {
         pMsg.AccountLevel = gQueryManager.GetAsInteger("AccountLevel");
         gQueryManager.GetAsString("AccountExpireDate", pMsg.AccountExpireDate, sizeof(pMsg.AccountExpireDate));
         gQueryManager.Close();
     }
    
     // 记录连接

    #ifndef MYSQL

     gQueryManager.ExecQuery("EXEC WZ_CONNECT_MEMB '%s','%s','%s'", escapedAccount, gServerManager[index].m_ServerName, lpMsg->IpAddress);

    #else

     gQueryManager.ExecUpdateQuery("CALL WZ_CONNECT_MEMB('%s', '%s', '%s')", escapedAccount, gServerManager[index].m_ServerName, lpMsg->IpAddress);

    #endif

     gQueryManager.Close();
    
     // 发送成功响应
     gSocketManager.DataSend(index, (BYTE*)&pMsg, pMsg.header.size);
    
     // 添加到在线账号管理
     strcpy_s(AccountInfo.Account, lpMsg->account);
     strcpy_s(AccountInfo.IpAddress, lpMsg->IpAddress);
     AccountInfo.UserIndex = lpMsg->index;
     AccountInfo.GameServerCode = gServerManager[index].m_ServerCode;
     gAccountManager.InsertAccountInfo(AccountInfo);
    
     gLog.Output(LOG_ACCOUNT, "[AccountInfo] Account connected (Account: %s, IpAddress: %s, GameServerCode: %d)", 
                 AccountInfo.Account, AccountInfo.IpAddress, AccountInfo.GameServerCode);

    }

-- 修改字段类型为 varbinary(16) 存储二进制 MD5
ALTER TABLE MEMB_INFO ALTER COLUMN memb__pwd VARBINARY(16) NULL

相关文章

发表新评论