MuOnlline增加自动注册
在 JoinServer.cpp 中添加配置变量
// 在 main() 函数之前或全局变量区域添加
int AutoReg; // 自动注册开关// 在 main() 函数中读取配置
AutoReg = GetPrivateProfileInt("AccountInfo", "AutoReg", 0, ".\JoinServer.ini");修改 JoinServer.h 添加 extern 声明
extern int AutoReg;
在 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 }}
修改 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
最后更新于 2026-03-20 21:54:06 并被添加「源码 muonline 自动注册」标签,已有 0 位童鞋阅读过。
本站使用「署名 4.0 国际」创作共享协议,可自由转载、引用,但需署名作者且注明文章出处