diff options
| -rw-r--r-- | src/engine/shared/protocol.h | 8 | ||||
| -rw-r--r-- | src/game/server/gamecontext.cpp | 300 | ||||
| -rw-r--r-- | src/game/server/gamecontext.h | 11 | ||||
| -rw-r--r-- | src/game/server/player.cpp | 4 | ||||
| -rw-r--r-- | src/game/server/player.h | 3 | ||||
| -rw-r--r-- | src/game/server/ranking.cpp | 19 | ||||
| -rw-r--r-- | src/game/version.h | 2 |
7 files changed, 250 insertions, 97 deletions
diff --git a/src/engine/shared/protocol.h b/src/engine/shared/protocol.h index ba04da8a..14cac9f7 100644 --- a/src/engine/shared/protocol.h +++ b/src/engine/shared/protocol.h @@ -59,6 +59,7 @@ enum NETMSG_RCON_AUTH, // NETMSG_REQUEST_MAP_DATA,// + NETMSG_AUTH_START, // NETMSG_AUTH_RESPONSE, // @@ -93,5 +94,12 @@ enum MSGFLAG_RECORD=8, MSGFLAG_NOSEND=16 }; +enum +{ + VERSION_VANILLA = 0, + VERSION_DDRACE = 1, + VERSION_DDNET_OLD = 2, + VERSION_DDNET_WHISPER = 217, +}; #endif diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 305a3adf..8c7bac51 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -837,7 +837,37 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) /* begin zCatch*/ if(!str_comp_num("/", pMsg->m_pMessage, 1)) { - if(!str_comp_nocase("info", pMsg->m_pMessage + 1)) + if (str_comp_nocase_num(pMsg->m_pMessage+1, "w ", 2) == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 3, 256); + Whisper(pPlayer->GetCID(), pWhisperMsg); + } + else if (str_comp_nocase_num(pMsg->m_pMessage+1, "whisper ", 8) == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 9, 256); + Whisper(pPlayer->GetCID(), pWhisperMsg); + } + else if (str_comp_nocase_num(pMsg->m_pMessage+1, "c ", 2) == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 3, 256); + Converse(pPlayer->GetCID(), pWhisperMsg); + } + else if (str_comp_nocase_num(pMsg->m_pMessage+1, "converse ", 9) == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 10, 256); + Converse(pPlayer->GetCID(), pWhisperMsg); + } + else if(!str_comp_nocase("w", pMsg->m_pMessage + 1) || !str_comp_nocase("whisper", pMsg->m_pMessage + 1) + || !str_comp_nocase("c", pMsg->m_pMessage + 1) || !str_comp_nocase("converse", pMsg->m_pMessage + 1)){ + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Type /help to see how use this command."); + SendChatTarget(ClientID, aBuf); + } + else if(!str_comp_nocase("info", pMsg->m_pMessage + 1)) { char aBuf[128]; str_format(aBuf, sizeof(aBuf), "zCatch %s by erd and Teetime, modified by Teelevision modified by Savander. See /help.", ZCATCH_VERSION); @@ -848,9 +878,9 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) str_format(aBuf, sizeof(aBuf), "If there are less than %d players, the round does not end and all players are released instead.", g_Config.m_SvLastStandingPlayers); SendChatTarget(ClientID, aBuf); } - SendChatTarget(ClientID, " "); - SendChatTarget(ClientID, "Commands list: top5, rank, help, victims, kills, t, ti"); + SendChatTarget(ClientID, "Commands list: top5, rank, help, victims, kills, w, whisper, c, converse"); } + else if(!str_comp_nocase("top5", pMsg->m_pMessage + 1) || !str_comp_nocase_num("top5 ", pMsg->m_pMessage + 1, 5)) { if(!str_comp_nocase_num("top5 ", pMsg->m_pMessage + 1, 5)){ @@ -881,8 +911,8 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) SendChatTarget(ClientID, "/top5 or /top5 <number> - Top5 winners on server."); SendChatTarget(ClientID, "/victims - who is waiting for your death"); SendChatTarget(ClientID, "/kills - list of players you killed"); - SendChatTarget(ClientID, "/t <name> <msg> - write PM to <name>"); - SendChatTarget(ClientID, "/ti <id> <msg> - write PM via ID"); + SendChatTarget(ClientID, "/whisper <name> <msg>"); + SendChatTarget(ClientID, "/converse <id> <msg>"); } else if(!str_comp_nocase("victims", pMsg->m_pMessage + 1)) { @@ -917,86 +947,6 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) SendChatTarget(ClientID, "You caught no one since your last death."); } } - // tell / PM someone privately - else if(!str_comp_nocase_num("t ", pMsg->m_pMessage + 1, 2) || !str_comp_nocase_num("ti ", pMsg->m_pMessage + 1, 3)) - { - const char *recipientStart, *msgStart; - int recipient = -1; - - // by name - if(!str_comp_nocase_num("t ", pMsg->m_pMessage + 1, 2)) - { - int recipientNameLength; - const char *recipientName; - recipientStart = str_skip_whitespaces((char*)pMsg->m_pMessage + 3); - // check _all_ players (there might be partly identical names) - for(int i = 0; i < MAX_CLIENTS; ++i) - { - if(m_apPlayers[i] - && (recipientName = Server()->ClientName(i)) - && (recipientNameLength = str_length(recipientName)) - && !str_comp_num(recipientName, recipientStart, recipientNameLength) - && recipientStart[recipientNameLength] == ' ' - ) - { - if(recipient >= 0) - { - SendChatTarget(ClientID, "Could not deliver private message. More than one player could be addressed."); - return; - } - msgStart = recipientStart + recipientNameLength + 1; - recipient = i; - } - } - } - - // by id - else if(!str_comp_nocase_num("ti ", pMsg->m_pMessage + 1, 3)) - { - recipientStart = str_skip_whitespaces((char*)pMsg->m_pMessage + 4); - // check if int given - for(const char *c = recipientStart; *c != ' '; ++c) - { - if(*c < '0' || '9' < *c) - { - SendChatTarget(ClientID, "No id given, syntax is: /ti id message."); - return; - } - } - int i = str_toint(recipientStart); - if(i <= MAX_CLIENTS) - if(m_apPlayers[i]) - { - recipient = i; - msgStart = str_skip_whitespaces(str_skip_to_whitespace((char*)recipientStart)); - } - } - - if(recipient >= 0) - { - if(MuteValidation(pPlayer)) - { - // prepare message - const char *msgForm = "/PM -> %s - %s"; - int len = 32 + MAX_NAME_LENGTH + str_length(msgStart); - char *msg = (char*)malloc(len * sizeof(char)); - CNetMsg_Sv_Chat M; - M.m_Team = 0; - M.m_ClientID = ClientID; - // send to sender and recipient - str_format(msg, len * sizeof(char), msgForm, Server()->ClientName(recipient), msgStart); - M.m_pMessage = msg; - Server()->SendPackMsg(&M, MSGFLAG_VITAL, ClientID); - Server()->SendPackMsg(&M, MSGFLAG_VITAL, recipient); - // tidy up - free(msg); - } - } - else - { - SendChatTarget(ClientID, "Could not deliver private message. Player not found."); - } - } else { SendChatTarget(ClientID, "Unknown command, try /info"); @@ -1172,6 +1122,18 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) pPlayer->m_LastVoteCall = Now; } } + else if (MsgID == NETMSGTYPE_CL_ISDDNET) + { + int Version = pUnpacker->GetInt(); + + if (pUnpacker->Error()) + { + if (pPlayer->m_ClientVersion < VERSION_DDRACE) + pPlayer->m_ClientVersion = VERSION_DDRACE; + } + else if(pPlayer->m_ClientVersion < Version) + pPlayer->m_ClientVersion = Version; + } else if(MsgID == NETMSGTYPE_CL_VOTE) { if(!m_VoteCloseTime) @@ -2192,6 +2154,174 @@ bool CGameContext::IsClientAimBot(int ClientID) return m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_IsAimBot; } + +bool CheckClientID2(int ClientID) +{ + dbg_assert(ClientID >= 0 || ClientID < MAX_CLIENTS, + "The Client ID is wrong"); + if (ClientID < 0 || ClientID >= MAX_CLIENTS) + return false; + return true; +} + +void CGameContext::Whisper(int ClientID, char *pStr) +{ + char *pName; + char *pMessage; + int Error = 0; + + pStr = str_skip_whitespaces(pStr); + + int Victim; + + // add token + if(*pStr == '"') + { + pStr++; + + pName = pStr; // we might have to process escape data + while(1) + { + if(pStr[0] == '"') + break; + else if(pStr[0] == '\\') + { + if(pStr[1] == '\\') + pStr++; // skip due to escape + else if(pStr[1] == '"') + pStr++; // skip due to escape + } + else if(pStr[0] == 0) + Error = 1; + + pStr++; + } + + // write null termination + *pStr = 0; + pStr++; + + for(Victim = 0; Victim < MAX_CLIENTS; Victim++) + if (str_comp(pName, Server()->ClientName(Victim)) == 0) + break; + + } + else + { + pName = pStr; + while(1) + { + if(pStr[0] == 0) + { + Error = 1; + break; + } + if(pStr[0] == ' ') + { + pStr[0] = 0; + for(Victim = 0; Victim < MAX_CLIENTS; Victim++) + if (str_comp(pName, Server()->ClientName(Victim)) == 0) + break; + + pStr[0] = ' '; + + if (Victim < MAX_CLIENTS) + break; + } + pStr++; + } + } + + if(pStr[0] != ' ') + { + Error = 1; + } + + *pStr = 0; + pStr++; + + pMessage = pStr; + + char aBuf[256]; + + if (Error) + { + str_format(aBuf, sizeof(aBuf), "Invalid whisper"); + SendChatTarget(ClientID, aBuf); + return; + } + + if (Victim >= MAX_CLIENTS || !CheckClientID2(Victim)) + { + str_format(aBuf, sizeof(aBuf), "No player with name \"%s\" found", pName); + SendChatTarget(ClientID, aBuf); + return; + } + + WhisperID(ClientID, Victim, pMessage); +} + +void CGameContext::WhisperID(int ClientID, int VictimID, char *pMessage) +{ + if (!CheckClientID2(ClientID)) + return; + + if (!CheckClientID2(VictimID)) + return; + + if (m_apPlayers[ClientID]) + m_apPlayers[ClientID]->m_LastWhisperTo = VictimID; + + char aBuf[256]; + if(m_apPlayers[ClientID]->m_LastWhisperTo != ClientID){ + if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion >= VERSION_DDNET_WHISPER) + { + CNetMsg_Sv_Chat Msg; + Msg.m_Team = CHAT_WHISPER_SEND; + Msg.m_ClientID = VictimID; + Msg.m_pMessage = pMessage; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); + } + else + { + str_format(aBuf, sizeof(aBuf), "[→ %s] %s", Server()->ClientName(VictimID), pMessage); + SendChatTarget(ClientID, aBuf); + } + + if (m_apPlayers[VictimID] && m_apPlayers[VictimID]->m_ClientVersion >= VERSION_DDNET_WHISPER) + { + CNetMsg_Sv_Chat Msg2; + Msg2.m_Team = CHAT_WHISPER_RECV; + Msg2.m_ClientID = ClientID; + Msg2.m_pMessage = pMessage; + Server()->SendPackMsg(&Msg2, MSGFLAG_VITAL, VictimID); + } + else + { + str_format(aBuf, sizeof(aBuf), "[← %s] %s", Server()->ClientName(ClientID), pMessage); + SendChatTarget(VictimID, aBuf); + } + }else{ + str_format(aBuf, sizeof(aBuf), "You can't talk to yourself."); + SendChatTarget(ClientID, aBuf); + } +} + +void CGameContext::Converse(int ClientID, char *pStr) +{ + CPlayer *pPlayer = m_apPlayers[ClientID]; + if (!pPlayer) + return; + + if (pPlayer->m_LastWhisperTo < 0) + SendChatTarget(ClientID, "You do not have an ongoing conversation. Whisper to someone to start one"); + else + { + WhisperID(ClientID, pPlayer->m_LastWhisperTo, pStr); + } +} + + const char *CGameContext::GameType() { return m_pController && m_pController->m_pGameType ? m_pController->m_pGameType : ""; } const char *CGameContext::Version() { return GAME_VERSION; } const char *CGameContext::NetVersion() { return GAME_NETVERSION; } diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 41d6d899..3cb6245c 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -19,8 +19,7 @@ #define MAX_MUTES 35 -#define ZCATCH_VERSION "0.4.9 BETA" - +#define ZCATCH_VERSION "0.5" /* Tick Game Context (CGameContext::tick) @@ -79,6 +78,10 @@ class CGameContext : public IGameServer static void ConKill(IConsole::IResult *pResult, void *pUserData); + void Whisper(int ClientID, char *pStr); + void WhisperID(int ClientID, int VictimID, char *pMessage); + void Converse(int ClientID, char *pStr); + CGameContext(int Resetting); void Construct(int Resetting); @@ -150,7 +153,9 @@ public: CHAT_ALL=-2, CHAT_SPEC=-1, CHAT_RED=0, - CHAT_BLUE=1 + CHAT_BLUE=1, + CHAT_WHISPER_SEND=2, + CHAT_WHISPER_RECV=3 }; struct CMutes diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 91a75c9d..2081ad4a 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -47,6 +47,8 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) m_CurrentTarget.y = 0; m_LastTarget.x = 0; m_LastTarget.y = 0; + + m_ClientVersion = VERSION_VANILLA; } CPlayer::~CPlayer() @@ -58,8 +60,10 @@ CPlayer::~CPlayer() delete tmp; } + delete m_pCharacter; m_pCharacter = 0; + } void CPlayer::Tick() diff --git a/src/game/server/player.h b/src/game/server/player.h index 416dccd7..877ae0bc 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -62,7 +62,8 @@ public: int m_LastChangeInfo; int m_LastEmote; int m_LastKill; - + int m_LastWhisperTo; + int m_ClientVersion; // TODO: clean this up struct { diff --git a/src/game/server/ranking.cpp b/src/game/server/ranking.cpp index b5d88514..091c4b78 100644 --- a/src/game/server/ranking.cpp +++ b/src/game/server/ranking.cpp @@ -52,14 +52,14 @@ void CRanking::Init() str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS zcatch_ranks (Name VARCHAR(%d) BINARY NOT NULL, Wins INT DEFAULT 0, UNIQUE KEY Name (Name)) CHARACTER SET utf8 ;", MAX_NAME_LENGTH); m_pStatement->execute(aBuf); - dbg_msg("SQL", "Ranking table was created successfully"); + dbg_msg("SQL", "Ranking table were created successfully"); } catch (sql::SQLException &e) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); dbg_msg("SQL", aBuf); - dbg_msg("SQL", "ERROR: Table was NOT created"); + dbg_msg("SQL", "ERROR: Tables were NOT created"); } // disconnect from database @@ -155,7 +155,7 @@ bool CRanking::Connect() } catch (...) { - dbg_msg("SQL", "Unknown Error cause by the MySQL/C++ Connector, my advice is to compile server_sql_debug and use it"); + dbg_msg("SQL", "Unknown Error cause by the MySQL/C++ Connector, my advice compile server_debug and use it"); dbg_msg("SQL", "ERROR: SQL connection failed"); return false; @@ -212,12 +212,17 @@ void CRanking::SaveRankingThread(void *pUser){ str_format(aBuf2, sizeof(aBuf), "Woah! You won for the first time! Now you have 1 win."); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf2); }else{ - str_format(aBuf2, sizeof(aBuf), "You're the winner! Now you have %d wins!", wins+1); + str_format(aBuf2, sizeof(aBuf), "You're winner! Now you have %d wins!", wins+1); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf2); } } + + } + + + delete pData->m_pSqlData->m_pResults; // if no entry found... create a new one @@ -285,11 +290,11 @@ void CRanking::ShowRankingThread(void *pUser){ if(pData->m_pSqlData->m_pResults->rowsCount() == 1){ int wins = (int)pData->m_pSqlData->m_pResults->getInt("Wins"); int rank = (int)pData->m_pSqlData->m_pResults->getInt("rank"); - str_format(aBuf2, sizeof(aBuf2), "%d. %s has %d %s, requested by %s.", rank,originalName,wins, (wins != 1) ? "wins" : "win", pData->m_aRequestingPlayer); + str_format(aBuf2, sizeof(aBuf2), "%d. %s's have %d %s Requested by %s.", rank,originalName,wins, (wins > 1) ? "wins." : "win.", pData->m_aRequestingPlayer); pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf2); } }else{ - str_format(aBuf2, sizeof(aBuf2), "%s is not ranked.", originalName); + str_format(aBuf2, sizeof(aBuf2), "%s's is not ranked.", originalName); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf2); } @@ -356,7 +361,7 @@ void CRanking::ShowTop5Thread(void *pUser){ while(pData->m_pSqlData->m_pResults->next()){ int wins = (int)pData->m_pSqlData->m_pResults->getInt("Wins"); int rank = (int)pData->m_pSqlData->m_pResults->getInt("rank"); - str_format(aBuf2, sizeof(aBuf2), "%d. %s has %d %s", rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(),wins, (wins != 1) ? "wins" : "win"); + str_format(aBuf2, sizeof(aBuf2), "%d. %s's have %d %s", rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(),wins, (wins > 1) ? "wins." : "win."); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf2); } str_format(aBuf2, sizeof(aBuf2), "------=====]"); diff --git a/src/game/version.h b/src/game/version.h index 76d95dd0..ed00afae 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -4,6 +4,6 @@ #define GAME_VERSION_H #include "generated/nethash.cpp" #define GAME_VERSION "0.6.2" -#define GAME_NETVERSION "0.6 " GAME_NETVERSION_HASH +#define GAME_NETVERSION "0.6 626fce9a778df4d4" static const char GAME_RELEASE_VERSION[8] = {'0', '.', '6', '.', '2', 0}; #endif |