diff --git a/src/game/collision.cpp b/src/game/collision.cpp index 71bb7aac0..40d79d00a 100644 --- a/src/game/collision.cpp +++ b/src/game/collision.cpp @@ -574,6 +574,17 @@ int CCollision::IsCp(int x, int y) return 0; } +int CCollision::IsCheckpoint(int Index) +{ + if(Index < 0) + return -1; + + int z = m_pTiles[Index].m_Index; + if(z >= 35 && z <= 59) + return z-35; + return -1; +} + vec2 CCollision::CpSpeed(int Index) { diff --git a/src/game/collision.h b/src/game/collision.h index ae64d4a37..0cddec681 100644 --- a/src/game/collision.h +++ b/src/game/collision.h @@ -55,6 +55,9 @@ public: int IsThrough(int x, int y); int IsNoLaser(int x, int y); int IsFNoLaser(int x, int y); + + int IsCheckpoint(int Index); + int IsCp(int x, int y); vec2 CpSpeed(int index); diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 437e245c2..4134bab6c 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1,10 +1,14 @@ #include +#include #include #include #include #include #include #include + +#include + #include "character.h" #include "laser.h" #include "light.h" @@ -591,50 +595,142 @@ void CCharacter::Tick() //DDRace char aBuftime[128]; float time = (float)(Server()->Tick() - m_StartTime) / ((float)Server()->TickSpeed()); - int z = GameServer()->Collision()->IsTeleport(m_Pos.x, m_Pos.y); + CPlayerData *pData = GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); + if(Server()->Tick() - m_RefreshTime >= Server()->TickSpeed()) { - //GameServer()->SendBroadcast("FIRST_IF", m_pPlayer->GetCID()); if (m_RaceState == RACE_STARTED) { - //GameServer()->SendBroadcast("SECOND_IF", m_pPlayer->GetCID()); - int int_time = (int)time; - str_format(aBuftime, sizeof(aBuftime), "%d m %d s\n%s", int_time/60,(int_time%60), ( g_Config.m_SvBroadcast[0] == 0) ? "" : g_Config.m_SvBroadcast); - GameServer()->SendBroadcast(aBuftime, m_pPlayer->GetCID()); + int IntTime = (int)time; + if(m_pPlayer->m_IsUsingRaceClient) + { + CNetMsg_Sv_RaceTime Msg; + Msg.m_Time = IntTime; + Msg.m_Check = 0; + + if(m_CpActive != -1 && m_CpTick > Server()->Tick()) + { + if(pData->m_BestTime && pData->m_aBestCpTime[m_CpActive] != 0) + { + float Diff = (m_CpCurrent[m_CpActive] - pData->m_aBestCpTime[m_CpActive])*100; + Msg.m_Check = (int)Diff; + } + } + + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, m_pPlayer->GetCID()); + } else { + str_format(aBuftime, sizeof(aBuftime), "Current Time: %d min %d sec", IntTime/60, IntTime%60); + + if(m_CpActive != -1 && m_CpTick > Server()->Tick()) + { + if(pData->m_BestTime && pData->m_aBestCpTime[m_CpActive] != 0) + { + char aTmp[128]; + float Diff = m_CpCurrent[m_CpActive] - pData->m_aBestCpTime[m_CpActive]; + str_format(aTmp, sizeof(aTmp), "\nCheckpoint | Diff : %+5.2f", Diff); + strcat(aBuftime, aTmp); + } + } + + if( g_Config.m_SvBroadcast[0] != 0) { + char aTmp[128]; + str_format(aTmp, sizeof(aTmp), "\n%s\n", g_Config.m_SvBroadcast); + strcat(aBuftime, aTmp); + } + GameServer()->SendBroadcast(aBuftime, m_pPlayer->GetCID()); + } } else { - if (g_Config.m_SvBroadcast[0] != 0) + if( g_Config.m_SvBroadcast[0] != 0) { + char aTmp[128]; + str_format(aTmp, sizeof(aTmp), "%s\n", g_Config.m_SvBroadcast); + strcat(aBuftime, aTmp); GameServer()->SendBroadcast(g_Config.m_SvBroadcast, m_pPlayer->GetCID()); + } + } m_RefreshTime = Server()->Tick(); } + + + int cp = GameServer()->Collision()->IsCheckpoint(MapIndex); + if(cp != -1 && m_RaceState == RACE_STARTED) + { + m_CpActive = cp; + m_CpCurrent[cp] = time; + m_CpTick = Server()->Tick() + Server()->TickSpeed()*2; + } if(((TileIndex1 == TILE_BEGIN) || (TileIndex2 == TILE_BEGIN)) && (m_RaceState == RACE_NONE || m_RaceState == RACE_STARTED)) { m_StartTime = Server()->Tick(); m_RefreshTime = Server()->Tick(); m_RaceState = RACE_STARTED; } + int z = GameServer()->Collision()->IsTeleport(m_Pos.x, m_Pos.y); if(((TileIndex1 == TILE_END) || (TileIndex2 == TILE_END)) && m_RaceState == RACE_STARTED) { char aBuf[128]; - if ((int)time / 60 != 0) - str_format(aBuf, sizeof(aBuf), "%s finished in: %d minute(s) %5.3f second(s)", (!g_Config.m_SvHideScore)?Server()->ClientName(m_pPlayer->GetCID()):"You", (int)time/60, time - ((int)time/60*60)); - else - str_format(aBuf, sizeof(aBuf), "%s finished in: %5.3f second(s)", (!g_Config.m_SvHideScore) ? Server()->ClientName(m_pPlayer->GetCID()):"You", time - ((int)time/60*60)); - if (!g_Config.m_SvHideScore) - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); - else + str_format(aBuf, sizeof(aBuf), "%s finished in: %d minute(s) %5.2f second(s)", Server()->ClientName(m_pPlayer->GetCID()), (int)time/60, time-((int)time/60*60)); + if(!g_Config.m_SvHideScore) GameServer()->SendChatTarget(m_pPlayer->GetCID(), aBuf); - CPlayerScore *pPScore = ((CGameControllerDDRace*) GameServer()->m_pController)->m_Score.SearchName(Server()->ClientName(m_pPlayer->GetCID())); - if(pPScore && time - pPScore->m_Score < 0) + else + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + + if(time - pData->m_BestTime < 0) { - str_format(aBuf, sizeof(aBuf), "New record: %5.3f second(s) better", time - pPScore->m_Score); - if (!g_Config.m_SvHideScore) - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); - else + // new record \o/ + str_format(aBuf, sizeof(aBuf), "New record: %5.2f second(s) better", time - pData->m_BestTime); + if(!g_Config.m_SvHideScore) GameServer()->SendChatTarget(m_pPlayer->GetCID(), aBuf); + else + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); } + + if(!pData->m_BestTime || time < pData->m_BestTime) + { + // update the score + pData->Set(time, m_CpCurrent); + + if(str_comp_num(Server()->ClientName(m_pPlayer->GetCID()), "nameless tee", 12) != 0) + GameServer()->Score()->SaveScore(m_pPlayer->GetCID(), time, this); + } + + // update server best time + if(!GameServer()->m_pController->m_CurrentRecord || time < GameServer()->m_pController->m_CurrentRecord) + { + // check for nameless + if(str_comp_num(Server()->ClientName(m_pPlayer->GetCID()), "nameless tee", 12) != 0) + GameServer()->m_pController->m_CurrentRecord = time; + } + m_RaceState = RACE_NONE; - if(strncmp(Server()->ClientName(m_pPlayer->GetCID()), "nameless tee", 12) != 0) - ((CGameControllerDDRace*)GameServer()->m_pController)->m_Score.ParsePlayer(Server()->ClientName(m_pPlayer->GetCID()), (float)time); + // set player score + if(!GameServer()->Score()->PlayerData(m_pPlayer->GetCID())->m_CurrentTime || GameServer()->Score()->PlayerData(m_pPlayer->GetCID())->m_CurrentTime > time) + { + GameServer()->Score()->PlayerData(m_pPlayer->GetCID())->m_CurrentTime = time; + + // send it to all players + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_IsUsingRaceClient) + { + if(g_Config.m_SvHideScore || i == m_pPlayer->GetCID()) + { + CNetMsg_Sv_PlayerTime Msg; + char aBuf[16]; + str_format(aBuf, sizeof(aBuf), "%.0f", time*100.0f); // damn ugly but the only way i know to do it + int TimeToSend; + sscanf(aBuf, "%d", &TimeToSend); + Msg.m_Time = TimeToSend; + Msg.m_Cid = m_pPlayer->GetCID(); + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, i); + } + } + } + } + + int TTime = 0-(int)time; + if(m_pPlayer->m_Score < TTime) + m_pPlayer->m_Score = TTime; + } if(((TileIndex1 == TILE_FREEZE) || (TileIndex2 == TILE_FREEZE)) && !m_Super) { diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 9561ecfb2..270f219db 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -167,7 +167,7 @@ public: // checkpoints int m_CpTick; int m_CpActive; - float m_CpCurrent[149]; + float m_CpCurrent[25]; diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 51748fdfa..a467dca95 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -11,6 +12,9 @@ #include #include "gamemodes/DDRace.h" +#include "score.h" +#include "score/file_score.h" + enum { RESET, @@ -32,7 +36,7 @@ void CGameContext::Construct(int Resetting) if(Resetting==NO_RESET) m_pVoteOptionHeap = new CHeap(); - //m_Cheats = g_Config.m_SvCheats; + m_pScore = 0; } CGameContext::CGameContext(int Resetting) @@ -705,7 +709,7 @@ void CGameContext::OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientId) str_format(buf, sizeof(buf), "/Info /Credits %s",g_Config.m_SvPauseable?"/pause":""); SendChatTarget(ClientId, buf); SendChatTarget(ClientId, "/rank /top5 /top5 5 or any number"); - } + }/* else if(!strncmp(pMsg->m_pMessage, "/top5", 5) && !g_Config.m_SvHideScore) { const char *pt = pMsg->m_pMessage; @@ -751,7 +755,7 @@ void CGameContext::OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientId) str_format(buf, sizeof(buf), "%s is not ranked", Server()->ClientName(ClientId)); SendChatTarget(ClientId, buf); - } + } finish this later */ else if (!str_comp_nocase(pMsg->m_pMessage, "/emotepain")&&g_Config.m_SvEmotionalTees) { CCharacter* pChr = p->GetCharacter(); @@ -853,6 +857,25 @@ void CGameContext::OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientId) } + } + else if(MsgId == NETMSGTYPE_CL_ISRACE) + { + p->m_IsUsingRaceClient = true; + // send time of all players + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_apPlayers[i] && Score()->PlayerData(i)->m_CurrentTime > 0) + { + char aBuf[16]; + str_format(aBuf, sizeof(aBuf), "%.0f", Score()->PlayerData(i)->m_CurrentTime*100.0f); // damn ugly but the only way i know to do it + int TimeToSend; + sscanf(aBuf, "%d", &TimeToSend); + CNetMsg_Sv_PlayerTime Msg; + Msg.m_Time = TimeToSend; + Msg.m_Cid = i; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); + } + } } else if(MsgId == NETMSGTYPE_CL_CALLVOTE) { @@ -1134,6 +1157,7 @@ void CGameContext::OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientId) pChr->m_EmoteStop = Server()->Tick() + 2 * Server()->TickSpeed(); } } + else if (MsgId == NETMSGTYPE_CL_KILL && !m_World.m_Paused) { if(p->m_Last_Kill && p->m_Last_Kill+Server()->TickSpeed()*3 > Server()->Tick()) @@ -1738,6 +1762,16 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) Server()->SetBrowseInfo(m_pController->m_pGameType, -1); + + // delete old score object + if(m_pScore) + delete m_pScore; + + // create score object (add sql later) + //if(g_Config.m_SvUseSQL) + // m_pScore = new CSqlScore(this); + //else + m_pScore = new CFileScore(this); // setup core world //for(int i = 0; i < MAX_CLIENTS; i++) // game.players[i].core.world = &game.world.core; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 8a84ff7d9..20492c193 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -11,6 +11,7 @@ #include "gamecontroller.h" #include "gameworld.h" #include "player.h" +#include "score.h" /* Tick @@ -41,6 +42,7 @@ class CGameContext : public IGameServer CCollision m_Collision; CNetObjHandler m_NetObjHandler; CTuningParams m_Tuning; + class IScore *m_pScore; static void ConMute(IConsole::IResult *pResult, void *pUserData, int cid); static void ConSetlvl(IConsole::IResult *pResult, void *pUserData, int cid); @@ -97,6 +99,8 @@ public: class IConsole *Console() { return m_pConsole; } CCollision *Collision() { return &m_Collision; } CTuningParams *Tuning() { return &m_Tuning; } + + class IScore *Score() { return m_pScore; } CGameContext(); ~CGameContext(); diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h index fcb39e2d2..4d77794d2 100644 --- a/src/game/server/gamecontroller.h +++ b/src/game/server/gamecontroller.h @@ -46,6 +46,7 @@ protected: char m_aMapWish[128]; + int m_RoundStartTick; int m_GameOverTick; @@ -63,6 +64,8 @@ protected: public: const char *m_pGameType; + float m_CurrentRecord; + //bool IsTeamplay() const; IGameController(class CGameContext *pGameServer); diff --git a/src/game/server/gamemodes/DDRace.h b/src/game/server/gamemodes/DDRace.h index 861cd8cad..28e7fcd7e 100644 --- a/src/game/server/gamemodes/DDRace.h +++ b/src/game/server/gamemodes/DDRace.h @@ -15,7 +15,6 @@ public: vec2 *m_pTeleporter; void InitTeleporter(); - CScore m_Score; virtual void Tick(); }; diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 96b456239..5db58f918 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -20,7 +20,6 @@ CPlayer::CPlayer(CGameContext *pGameServer, int CID, int Team) m_DieTick = Server()->Tick(); m_ScoreStartTick = Server()->Tick(); Character = 0; - m_CharacterCopy = 0; m_Muted = 0; this->m_ClientID = CID; m_Team = GameServer()->m_pController->ClampTeam(Team); @@ -32,6 +31,11 @@ CPlayer::CPlayer(CGameContext *pGameServer, int CID, int Team) m_SentAfkWarning2 = 0; m_PauseInfo.m_Respawn = false; + + m_IsUsingRaceClient = false; + m_LastSentTime = 0; + + GameServer()->Score()->PlayerData(CID)->Reset(); } CPlayer::~CPlayer() @@ -42,17 +46,6 @@ CPlayer::~CPlayer() void CPlayer::Tick() { - int pos=0; - CPlayerScore *pscore = ((CGameControllerDDRace*)GameServer()->m_pController)->m_Score.SearchName(Server()->ClientName(m_ClientID), pos); - if(pscore && pos > -1 && pscore->m_Score != -1) - { - float time = pscore->m_Score; - //if (!config.sv_hide_score) - m_Score = time * 100; - //else - // score=authed; - } else - m_Score = 0.0f; Server()->SetClientAuthed(m_ClientID, m_Authed); Server()->SetClientScore(m_ClientID, m_Score); @@ -95,6 +88,23 @@ void CPlayer::Tick() } else if(m_Spawning && m_RespawnTick <= Server()->Tick()) TryRespawn(); + + // send best time + if(m_IsUsingRaceClient) + { + if(m_LastSentTime > GameServer()->m_pController->m_CurrentRecord || (!m_LastSentTime && GameServer()->m_pController->m_CurrentRecord)) + { + char aBuf[16]; + str_format(aBuf, sizeof(aBuf), "%.0f", GameServer()->m_pController->m_CurrentRecord*100.0f); // damn ugly but the only way i know to do it + int TimeToSend; + sscanf(aBuf, "%d", &TimeToSend); + CNetMsg_Sv_Record Msg; + Msg.m_Time = TimeToSend; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, m_ClientID); + + m_LastSentTime = GameServer()->m_pController->m_CurrentRecord; + } + } } void CPlayer::Snap(int SnappingClient) @@ -112,11 +122,18 @@ void CPlayer::Snap(int SnappingClient) Info->m_LatencyFlux = m_Latency.m_Max-m_Latency.m_Min; Info->m_Local = 0; Info->m_ClientId = m_ClientID; - Info->m_Score = m_Score; - Info->m_Team = m_Team; + if(m_ClientID == SnappingClient) Info->m_Local = 1; + + // send 0 if times of otheres are not shown + if(SnappingClient != m_ClientID) + Info->m_Score = 0; + else + Info->m_Score = m_Score; + + Info->m_Team = m_Team; } void CPlayer::OnDisconnect() diff --git a/src/game/server/player.h b/src/game/server/player.h index a736e38be..0b59efac4 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -32,7 +32,6 @@ public: void KillCharacter(int Weapon = WEAPON_GAME); CCharacter *GetCharacter(); - CCharacter* m_CharacterCopy; struct PauseInfo { CCharacterCore m_Core; int m_StartTime; @@ -112,6 +111,14 @@ public: int m_ScoreStartTick; bool m_ForceBalanced; + float m_BestTime; + float m_aBestCpTime[25]; + + bool m_ResetPickups; + + bool m_IsUsingRaceClient; + float m_LastSentTime; + // afk timer void AfkTimer(int new_target_x, int new_target_y); int64 m_LastPlaytime; diff --git a/src/game/server/score.h b/src/game/server/score.h index c6c1f1786..087c1fd26 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -1,38 +1,53 @@ -/* copyright (c) 2008 rajh and gregwar. Score stuff */ +#ifndef GAME_SERVER_INTERFACE_SCORE_H +#define GAME_SERVER_INTERFACE_SCORE_H -#ifndef SCORE_H_RACE -#define SCORE_H_RACE -#include -#include -#include -#include +#include "entities/character.h" +#include "gamecontext.h" +#define NUM_TELEPORT 25 -class CPlayerScore +class CPlayerData { public: - char name[MAX_NAME_LENGTH]; - float m_Score; - - CPlayerScore(const char *name, float score); - - bool operator==(const CPlayerScore& other) { return (this->m_Score == other.m_Score); } - bool operator<(const CPlayerScore& other) { return (this->m_Score < other.m_Score); } + CPlayerData() + { + Reset(); + } + + void Reset() + { + m_BestTime = 0; + m_CurrentTime = 0; + for(int i = 0; i < NUM_TELEPORT; i++) + m_aBestCpTime[i] = 0; + } + + void Set(float Time, float CpTime[NUM_TELEPORT]) + { + m_BestTime = Time; + for(int i = 0; i < NUM_TELEPORT; i++) + m_aBestCpTime[i] = CpTime[i]; + } + + float m_BestTime; + float m_CurrentTime; + float m_aBestCpTime[NUM_TELEPORT]; }; -class CScore +class IScore { - class CGameContext *m_pGameServer; - std::string SaveFile(); + CPlayerData m_aPlayerData[MAX_CLIENTS]; + public: - CScore(class CGameContext *pGameServer); - CScore(); - void Save(); - void Load(); - CPlayerScore *SearchName(const char *name, int &pos); - CPlayerScore *SearchName(const char *name); - void ParsePlayer(const char *name, float score); - std::list Top5Draw(int id, int debut); + virtual ~IScore() {} + + CPlayerData *PlayerData(int ID) { return &m_aPlayerData[ID]; } + + virtual void LoadScore(int ClientID) = 0; + virtual void SaveScore(int ClientID, float Time, CCharacter *pChar) = 0; + + virtual void ShowTop5(int ClientID, int Debut=1) = 0; + virtual void ShowRank(int ClientID, const char* pName, bool Search=false) = 0; }; #endif diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp new file mode 100644 index 000000000..371b23d67 --- /dev/null +++ b/src/game/server/score/file_score.cpp @@ -0,0 +1,273 @@ +/* copyright (c) 2008 rajh and gregwar. Score stuff */ + +#include +#include +#include +#include +#include "../gamemodes/DDRace.h" +#include "file_score.h" + +static LOCK gs_ScoreLock = 0; + +CFileScore::CPlayerScore::CPlayerScore(const char *pName, float Score, const char *pIP, float aCpTime[NUM_TELEPORT]) +{ + str_copy(m_aName, pName, sizeof(m_aName)); + m_Score = Score; + str_copy(m_aIP, pIP, sizeof(m_aIP)); + for(int i = 0; i < NUM_TELEPORT; i++) + m_aCpTime[i] = aCpTime[i]; +} + +CFileScore::CFileScore(CGameContext *pGameServer) : m_pGameServer(pGameServer), m_pServer(pGameServer->Server()) +{ + if(gs_ScoreLock == 0) + gs_ScoreLock = lock_create(); + + Init(); +} + +CFileScore::~CFileScore() +{ + lock_wait(gs_ScoreLock); + + // clear list + m_Top.clear(); + + lock_release(gs_ScoreLock); +} + +std::string SaveFile() +{ + std::ostringstream oss; + //if(g_Config.m_SvScoreFolder[0]) + // oss << g_Config.m_SvScoreFolder << "/" << g_Config.m_SvMap << "_record.dtb"; + //else + oss << g_Config.m_SvMap << "_record.dtb"; + return oss.str(); +} + +void CFileScore::SaveScoreThread(void *pUser) +{ + CFileScore *pSelf = (CFileScore *)pUser; + lock_wait(gs_ScoreLock); + std::fstream f; + f.open(SaveFile().c_str(), std::ios::out); + if(!f.fail()) + { + int t = 0; + for(sorted_array::range r = pSelf->m_Top.all(); !r.empty(); r.pop_front()) + { + f << r.front().m_aName << std::endl << r.front().m_Score << std::endl << r.front().m_aIP << std::endl; + //if(g_Config.m_SvCheckpointSave) + //{ + for(int c = 0; c < NUM_TELEPORT; c++) + f << r.front().m_aCpTime[c] << " "; + f << std::endl; + //} + t++; + if(t%50 == 0) + thread_sleep(1); + } + } + f.close(); + lock_release(gs_ScoreLock); +} + +void CFileScore::Save() +{ + void *pSaveThread = thread_create(SaveScoreThread, this); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)pSaveThread); +#endif +} + +void CFileScore::Init() +{ + lock_wait(gs_ScoreLock); + + // create folder if not exist + //if(g_Config.m_SvScoreFolder[0]) + // fs_makedir(g_Config.m_SvScoreFolder); + + std::fstream f; + f.open(SaveFile().c_str(), std::ios::in); + + while(!f.eof() && !f.fail()) + { + std::string TmpName, TmpScore, TmpIP, TmpCpLine; + std::getline(f, TmpName); + if(!f.eof() && TmpName != "") + { + std::getline(f, TmpScore); + std::getline(f, TmpIP); + float aTmpCpTime[NUM_TELEPORT] = {0}; + //if(g_Config.m_SvCheckpointSave) + //{ + std::getline(f, TmpCpLine); + char *pTime = strtok((char*)TmpCpLine.c_str(), " "); + int i = 0; + while(pTime != NULL && i < NUM_TELEPORT) + { + aTmpCpTime[i] = atof(pTime); + pTime = strtok(NULL, " "); + i++; + } + //} + m_Top.add(*new CPlayerScore(TmpName.c_str(), atof(TmpScore.c_str()), TmpIP.c_str(), aTmpCpTime)); + } + } + f.close(); + lock_release(gs_ScoreLock); + + // save the current best score + if(m_Top.size()) + ((CGameControllerDDRace*)GameServer()->m_pController)->m_CurrentRecord = m_Top[0].m_Score; +} + +CFileScore::CPlayerScore *CFileScore::SearchScore(int ID, bool ScoreIP, int *pPosition) +{ + char aIP[16]; + Server()->GetClientIP(ID, aIP, sizeof(aIP)); + + int Pos = 1; + for(sorted_array::range r = m_Top.all(); !r.empty(); r.pop_front()) + { + if(!strcmp(r.front().m_aIP, aIP) /*&& g_Config.m_SvScoreIP*/ && ScoreIP) + { + if(pPosition) + *pPosition = Pos; + return &r.front(); + } + Pos++; + } + + return SearchName(Server()->ClientName(ID), pPosition, 0); +} + +CFileScore::CPlayerScore *CFileScore::SearchName(const char *pName, int *pPosition, bool NoCase) +{ + CPlayerScore *pPlayer = 0; + int Pos = 1; + int Found = 0; + for(sorted_array::range r = m_Top.all(); !r.empty(); r.pop_front()) + { + if(str_find_nocase(r.front().m_aName, pName)) + { + if(pPosition) + *pPosition = Pos; + if(NoCase) + { + Found++; + pPlayer = &r.front(); + } + if(!strcmp(r.front().m_aName, pName)) + return &r.front(); + } + Pos++; + } + if(Found > 1) + { + if(pPosition) + *pPosition = -1; + return 0; + } + return pPlayer; +} + +void CFileScore::UpdatePlayer(int ID, float Score, float aCpTime[NUM_TELEPORT]) +{ + const char *pName = Server()->ClientName(ID); + char aIP[16]; + Server()->GetClientIP(ID, aIP, sizeof(aIP)); + + lock_wait(gs_ScoreLock); + CPlayerScore *pPlayer = SearchScore(ID, 1, 0); + + if(pPlayer) + { + for(int c = 0; c < NUM_TELEPORT; c++) + pPlayer->m_aCpTime[c] = aCpTime[c]; + + pPlayer->m_Score = Score; + str_copy(pPlayer->m_aName, pName, sizeof(pPlayer->m_aName)); + + sort(m_Top.all()); + } + else + m_Top.add(*new CPlayerScore(pName, Score, aIP, aCpTime)); + + lock_release(gs_ScoreLock); + Save(); +} + +void CFileScore::LoadScore(int ClientID) +{ + char aIP[16]; + Server()->GetClientIP(ClientID, aIP, sizeof(aIP)); + CPlayerScore *pPlayer = SearchScore(ClientID, 0, 0); + if(pPlayer && strcmp(pPlayer->m_aIP, aIP) != 0) + { + lock_wait(gs_ScoreLock); + str_copy(pPlayer->m_aIP, aIP, sizeof(pPlayer->m_aIP)); + lock_release(gs_ScoreLock); + Save(); + } + + // set score + if(pPlayer) + PlayerData(ClientID)->Set(pPlayer->m_Score, pPlayer->m_aCpTime); +} + +void CFileScore::SaveScore(int ClientID, float Time, CCharacter *pChar) +{ + UpdatePlayer(ClientID, Time, pChar->m_CpCurrent); +} + +void CFileScore::ShowTop5(int ClientID, int Debut) +{ + char aBuf[512]; + GameServer()->SendChatTarget(ClientID, "----------- Top 5 -----------"); + for(int i = 0; i < 5; i++) + { + if(i+Debut > m_Top.size()) + break; + CPlayerScore *r = &m_Top[i+Debut-1]; + str_format(aBuf, sizeof(aBuf), "%d. %s Time: %d minute(s) %5.2f second(s)", + i+Debut, r->m_aName, (int) r->m_Score/60, r->m_Score-((int)r->m_Score/60*60)); + GameServer()->SendChatTarget(ClientID, aBuf); + } + GameServer()->SendChatTarget(ClientID, "------------------------------"); +} + +void CFileScore::ShowRank(int ClientID, const char* pName, bool Search) +{ + CPlayerScore *pScore; + int Pos; + char aBuf[512]; + + if(!Search) + pScore = SearchScore(ClientID, 1, &Pos); + else + pScore = SearchName(pName, &Pos, 1); + + if(pScore && Pos > -1) + { + float Time = pScore->m_Score; + char aClientName[128]; + str_format(aClientName, sizeof(aClientName), " (%s)", Server()->ClientName(ClientID)); + if(!g_Config.m_SvHideScore) + str_format(aBuf, sizeof(aBuf), "Your time: %d minute(s) %5.2f second(s)", (int)Time/60, Time-((int)Time/60*60)); + else + str_format(aBuf, sizeof(aBuf), "%d. %s Time: %d minute(s) %5.2f second(s)", Pos, pScore->m_aName, (int)Time/60, Time-((int)Time/60*60)); + if(Search) + strcat(aBuf, aClientName); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + return; + } + else if(Pos == -1) + str_format(aBuf, sizeof(aBuf), "Several players were found."); + else + str_format(aBuf, sizeof(aBuf), "%s is not ranked", Search?pName:Server()->ClientName(ClientID)); + + GameServer()->SendChatTarget(ClientID, aBuf); +} diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h new file mode 100644 index 000000000..e88ff06a8 --- /dev/null +++ b/src/game/server/score/file_score.h @@ -0,0 +1,54 @@ +/* copyright (c) 2008 rajh and gregwar. Score stuff */ + +#ifndef GAME_SERVER_FILESCORE_H +#define GAME_SERVER_FILESCORE_H + +#include + +#include "../score.h" + +class CFileScore : public IScore +{ + CGameContext *m_pGameServer; + IServer *m_pServer; + + class CPlayerScore + { + public: + char m_aName[MAX_NAME_LENGTH]; + float m_Score; + char m_aIP[16]; + float m_aCpTime[NUM_TELEPORT]; + + CPlayerScore() {}; + CPlayerScore(const char *pName, float Score, const char *pIP, float aCpTime[NUM_TELEPORT]); + + bool operator<(const CPlayerScore& other) { return (this->m_Score < other.m_Score); } + }; + + sorted_array m_Top; + + CGameContext *GameServer() { return m_pGameServer; } + IServer *Server() { return m_pServer; } + + CPlayerScore *SearchScore(int ID, bool ScoreIP, int *pPosition); + CPlayerScore *SearchName(const char *pName, int *pPosition, bool MatchCase); + void UpdatePlayer(int ID, float Score, float aCpTime[NUM_TELEPORT]); + + void Init(); + void Save(); + static void SaveScoreThread(void *pUser); + +public: + + CFileScore(CGameContext *pGameServer); + ~CFileScore(); + + virtual void LoadScore(int ClientID); + virtual void SaveScore(int ClientID, float Time, CCharacter *pChar); + + virtual void ShowTop5(int ClientID, int Debut=1); + virtual void ShowRank(int ClientID, const char* pName, bool Search=false); +}; + +#endif