Compare commits

..

10 commits

Author SHA1 Message Date
Robert Müller 6df1d251d7
Merge pull request #7947 from ChillerDragon/pr_coll_tele
Move tele vector init from gamecontroller to collision
2024-03-14 17:02:09 +00:00
Dennis Felsing 38a4c918cd
Merge pull request #8100 from furo321/new-formatting-rev
Add #8019 to `formatting-revs.txt`
2024-03-14 16:35:34 +00:00
furo 1561db66c0 Add #8019 to formatting-revs.txt 2024-03-14 17:17:08 +01:00
Dennis Felsing 681b66b563 Version 18.1 2024-03-14 17:14:07 +01:00
Dennis Felsing 7d890aa1a5
Merge pull request #8099 from heinrich5991/pr_ddnet_http_age
Take serverlist age into account when choosing master
2024-03-14 15:42:52 +00:00
heinrich5991 ff7b6ffe14 Take serverlist age into account when choosing master
Prefer masters with newer server lists, and try re-selecting the chosen
master if the result is older than 5 minutes.
2024-03-14 12:31:41 +01:00
heinrich5991 5603d284bf Parse Date and Last-Modified HTTP headers 2024-03-14 12:31:41 +01:00
heinrich5991 2d17097c91 Use log_* instead of Console()->Log() in HTTP serverbrowser 2024-03-14 11:19:32 +01:00
ChillerDragon 4da4ca5bdd Make tele getters const 2024-03-14 08:50:35 +08:00
ChillerDragon 552d466197 Move tele vector init from gamecontroller to collision 2024-03-14 08:46:22 +08:00
19 changed files with 204 additions and 107 deletions

@ -1 +1 @@
Subproject commit a4fbe0e52338a129bc3ac2e3a1de54de1d175279 Subproject commit 87fbb57839080f40d16cd77d8ad11ae91bb9c849

View file

@ -11,3 +11,4 @@ d4da82f977abb34cd0ed9a77e707b4c42f26673d
3be8a592e52a03e555b6aa29a8dea529797bb645 3be8a592e52a03e555b6aa29a8dea529797bb645
01edaec628ec325d47d74a667ccaa2aed43f30c9 01edaec628ec325d47d74a667ccaa2aed43f30c9
a69dc599a99ef7393985e96a98c80f8217f66998 a69dc599a99ef7393985e96a98c80f8217f66998
17402cc43fdf51c8cb81b724228f11c423d17007

View file

@ -102,7 +102,7 @@ void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVers
void CServerBrowser::OnInit() void CServerBrowser::OnInit()
{ {
m_pHttp = CreateServerBrowserHttp(m_pEngine, m_pConsole, m_pStorage, m_pHttpClient, g_Config.m_BrCachedBestServerinfoUrl); m_pHttp = CreateServerBrowserHttp(m_pEngine, m_pStorage, m_pHttpClient, g_Config.m_BrCachedBestServerinfoUrl);
} }
void CServerBrowser::RegisterCommands() void CServerBrowser::RegisterCommands()

View file

@ -11,6 +11,7 @@
#include <engine/storage.h> #include <engine/storage.h>
#include <base/lock.h> #include <base/lock.h>
#include <base/log.h>
#include <base/system.h> #include <base/system.h>
#include <memory> #include <memory>
@ -20,6 +21,28 @@
using namespace std::chrono_literals; using namespace std::chrono_literals;
static int SanitizeAge(std::optional<int64_t> Age)
{
// A year is of course pi*10**7 seconds.
if(!(Age && 0 <= *Age && *Age < 31415927))
{
return 31415927;
}
return *Age;
}
// Classify HTTP responses into buckets, treat 15 seconds as fresh, 1 minute as
// less fresh, etc. This ensures that differences in the order of seconds do
// not affect master choice.
static int ClassifyAge(int AgeSeconds)
{
return 0 //
+ (AgeSeconds >= 15) // 15 seconds
+ (AgeSeconds >= 60) // 1 minute
+ (AgeSeconds >= 300) // 5 minutes
+ (AgeSeconds / 3600); // 1 hour
}
class CChooseMaster class CChooseMaster
{ {
public: public:
@ -184,9 +207,11 @@ void CChooseMaster::CJob::Run()
// fail. // fail.
CTimeout Timeout{10000, 0, 8000, 10}; CTimeout Timeout{10000, 0, 8000, 10};
int aTimeMs[MAX_URLS]; int aTimeMs[MAX_URLS];
int aAgeS[MAX_URLS];
for(int i = 0; i < m_pData->m_NumUrls; i++) for(int i = 0; i < m_pData->m_NumUrls; i++)
{ {
aTimeMs[i] = -1; aTimeMs[i] = -1;
aAgeS[i] = SanitizeAge({});
const char *pUrl = m_pData->m_aaUrls[aRandomized[i]]; const char *pUrl = m_pData->m_aaUrls[aRandomized[i]];
std::shared_ptr<CHttpRequest> pHead = HttpHead(pUrl); std::shared_ptr<CHttpRequest> pHead = HttpHead(pUrl);
pHead->Timeout(Timeout); pHead->Timeout(Timeout);
@ -200,7 +225,7 @@ void CChooseMaster::CJob::Run()
pHead->Wait(); pHead->Wait();
if(pHead->State() == EHttpState::ABORTED || State() == IJob::STATE_ABORTED) if(pHead->State() == EHttpState::ABORTED || State() == IJob::STATE_ABORTED)
{ {
dbg_msg("serverbrowse_http", "master chooser aborted"); log_debug("serverbrowse_http", "master chooser aborted");
return; return;
} }
if(pHead->State() != EHttpState::DONE) if(pHead->State() != EHttpState::DONE)
@ -223,7 +248,7 @@ void CChooseMaster::CJob::Run()
auto Time = std::chrono::duration_cast<std::chrono::milliseconds>(time_get_nanoseconds() - StartTime); auto Time = std::chrono::duration_cast<std::chrono::milliseconds>(time_get_nanoseconds() - StartTime);
if(pGet->State() == EHttpState::ABORTED || State() == IJob::STATE_ABORTED) if(pGet->State() == EHttpState::ABORTED || State() == IJob::STATE_ABORTED)
{ {
dbg_msg("serverbrowse_http", "master chooser aborted"); log_debug("serverbrowse_http", "master chooser aborted");
return; return;
} }
if(pGet->State() != EHttpState::DONE) if(pGet->State() != EHttpState::DONE)
@ -242,39 +267,44 @@ void CChooseMaster::CJob::Run()
{ {
continue; continue;
} }
dbg_msg("serverbrowse_http", "found master, url='%s' time=%dms", pUrl, (int)Time.count()); int AgeS = SanitizeAge(pGet->ResultAgeSeconds());
log_info("serverbrowse_http", "found master, url='%s' time=%dms age=%ds", pUrl, (int)Time.count(), AgeS);
aTimeMs[i] = Time.count(); aTimeMs[i] = Time.count();
aAgeS[i] = AgeS;
} }
// Determine index of the minimum time. // Determine index of the minimum time.
int BestIndex = -1; int BestIndex = -1;
int BestTime = 0; int BestTime = 0;
int BestAge = 0;
for(int i = 0; i < m_pData->m_NumUrls; i++) for(int i = 0; i < m_pData->m_NumUrls; i++)
{ {
if(aTimeMs[i] < 0) if(aTimeMs[i] < 0)
{ {
continue; continue;
} }
if(BestIndex == -1 || aTimeMs[i] < BestTime) if(BestIndex == -1 || std::tuple(ClassifyAge(aAgeS[i]), aTimeMs[i]) < std::tuple(ClassifyAge(BestAge), BestTime))
{ {
BestTime = aTimeMs[i]; BestTime = aTimeMs[i];
BestAge = aAgeS[i];
BestIndex = aRandomized[i]; BestIndex = aRandomized[i];
} }
} }
if(BestIndex == -1) if(BestIndex == -1)
{ {
dbg_msg("serverbrowse_http", "WARNING: no usable masters found"); log_error("serverbrowse_http", "WARNING: no usable masters found");
return; return;
} }
dbg_msg("serverbrowse_http", "determined best master, url='%s' time=%dms", m_pData->m_aaUrls[BestIndex], BestTime); log_info("serverbrowse_http", "determined best master, url='%s' time=%dms age=%ds", m_pData->m_aaUrls[BestIndex], BestTime, BestAge);
m_pData->m_BestIndex.store(BestIndex); m_pData->m_BestIndex.store(BestIndex);
} }
class CServerBrowserHttp : public IServerBrowserHttp class CServerBrowserHttp : public IServerBrowserHttp
{ {
public: public:
CServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IHttp *pHttp, const char **ppUrls, int NumUrls, int PreviousBestIndex); CServerBrowserHttp(IEngine *pEngine, IHttp *pHttp, const char **ppUrls, int NumUrls, int PreviousBestIndex);
~CServerBrowserHttp() override; ~CServerBrowserHttp() override;
void Update() override; void Update() override;
bool IsRefreshing() override { return m_State != STATE_DONE; } bool IsRefreshing() override { return m_State != STATE_DONE; }
@ -310,7 +340,6 @@ private:
static bool Validate(json_value *pJson); static bool Validate(json_value *pJson);
static bool Parse(json_value *pJson, std::vector<CServerInfo> *pvServers, std::vector<NETADDR> *pvLegacyServers); static bool Parse(json_value *pJson, std::vector<CServerInfo> *pvServers, std::vector<NETADDR> *pvLegacyServers);
IConsole *m_pConsole;
IHttp *m_pHttp; IHttp *m_pHttp;
int m_State = STATE_DONE; int m_State = STATE_DONE;
@ -321,8 +350,7 @@ private:
std::vector<NETADDR> m_vLegacyServers; std::vector<NETADDR> m_vLegacyServers;
}; };
CServerBrowserHttp::CServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IHttp *pHttp, const char **ppUrls, int NumUrls, int PreviousBestIndex) : CServerBrowserHttp::CServerBrowserHttp(IEngine *pEngine, IHttp *pHttp, const char **ppUrls, int NumUrls, int PreviousBestIndex) :
m_pConsole(pConsole),
m_pHttp(pHttp), m_pHttp(pHttp),
m_pChooseMaster(new CChooseMaster(pEngine, pHttp, Validate, ppUrls, NumUrls, PreviousBestIndex)) m_pChooseMaster(new CChooseMaster(pEngine, pHttp, Validate, ppUrls, NumUrls, PreviousBestIndex))
{ {
@ -346,7 +374,7 @@ void CServerBrowserHttp::Update()
{ {
if(!m_pChooseMaster->IsRefreshing()) if(!m_pChooseMaster->IsRefreshing())
{ {
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "serverbrowse_http", "no working serverlist URL found"); log_error("serverbrowse_http", "no working serverlist URL found");
m_State = STATE_NO_MASTER; m_State = STATE_NO_MASTER;
} }
return; return;
@ -372,12 +400,20 @@ void CServerBrowserHttp::Update()
Success = Success && pJson; Success = Success && pJson;
Success = Success && !Parse(pJson, &m_vServers, &m_vLegacyServers); Success = Success && !Parse(pJson, &m_vServers, &m_vLegacyServers);
json_value_free(pJson); json_value_free(pJson);
int Age = SanitizeAge(pGetServers->ResultAgeSeconds());
if(!Success) if(!Success)
{ {
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "serverbrowse_http", "failed getting serverlist, trying to find best URL"); log_error("serverbrowse_http", "failed getting serverlist, trying to find best URL");
m_pChooseMaster->Reset(); m_pChooseMaster->Reset();
m_pChooseMaster->Refresh(); m_pChooseMaster->Refresh();
} }
// Try to find new master if the current one returns results
// that are 5 minutes old.
else if(Age > 300)
{
log_info("serverbrowse_http", "got stale serverlist, age=%ds, trying to find best URL", Age);
m_pChooseMaster->Refresh();
}
} }
} }
void CServerBrowserHttp::Refresh() void CServerBrowserHttp::Refresh()
@ -435,7 +471,7 @@ bool CServerBrowserHttp::Parse(json_value *pJson, std::vector<CServerInfo> *pvSe
} }
if(CServerInfo2::FromJson(&ParsedInfo, &Info)) if(CServerInfo2::FromJson(&ParsedInfo, &Info))
{ {
//dbg_msg("dbg/serverbrowser", "skipped due to info, i=%d", i); log_debug("serverbrowser_http", "skipped due to info, i=%d", i);
// Only skip the current server on parsing // Only skip the current server on parsing
// failure; the server info is "user input" by // failure; the server info is "user input" by
// the game server and can be set to arbitrary // the game server and can be set to arbitrary
@ -455,7 +491,7 @@ bool CServerBrowserHttp::Parse(json_value *pJson, std::vector<CServerInfo> *pvSe
NETADDR ParsedAddr; NETADDR ParsedAddr;
if(ServerbrowserParseUrl(&ParsedAddr, Addresses[a])) if(ServerbrowserParseUrl(&ParsedAddr, Addresses[a]))
{ {
//dbg_msg("dbg/serverbrowser", "unknown address, i=%d a=%d", i, a); log_debug("dbg/serverbrowser", "unknown address, i=%d a=%d", i, a);
// Skip unknown addresses. // Skip unknown addresses.
continue; continue;
} }
@ -495,7 +531,7 @@ static const char *DEFAULT_SERVERLIST_URLS[] = {
"https://master4.ddnet.org/ddnet/15/servers.json", "https://master4.ddnet.org/ddnet/15/servers.json",
}; };
IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IStorage *pStorage, IHttp *pHttp, const char *pPreviousBestUrl) IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IStorage *pStorage, IHttp *pHttp, const char *pPreviousBestUrl)
{ {
char aaUrls[CChooseMaster::MAX_URLS][256]; char aaUrls[CChooseMaster::MAX_URLS][256];
const char *apUrls[CChooseMaster::MAX_URLS] = {0}; const char *apUrls[CChooseMaster::MAX_URLS] = {0};
@ -532,5 +568,5 @@ IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole
break; break;
} }
} }
return new CServerBrowserHttp(pEngine, pConsole, pHttp, ppUrls, NumUrls, PreviousBestIndex); return new CServerBrowserHttp(pEngine, pHttp, ppUrls, NumUrls, PreviousBestIndex);
} }

View file

@ -3,7 +3,6 @@
#include <base/types.h> #include <base/types.h>
class CServerInfo; class CServerInfo;
class IConsole;
class IEngine; class IEngine;
class IStorage; class IStorage;
class IHttp; class IHttp;
@ -26,5 +25,5 @@ public:
virtual const NETADDR &LegacyServer(int Index) const = 0; virtual const NETADDR &LegacyServer(int Index) const = 0;
}; };
IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IStorage *pStorage, IHttp *pHttp, const char *pPreviousBestUrl); IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IStorage *pStorage, IHttp *pHttp, const char *pPreviousBestUrl);
#endif // ENGINE_CLIENT_SERVERBROWSER_HTTP_H #endif // ENGINE_CLIENT_SERVERBROWSER_HTTP_H

View file

@ -148,6 +148,8 @@ bool CHttpRequest::ConfigureHandle(void *pHandle)
curl_easy_setopt(pH, CURLOPT_USERAGENT, GAME_NAME " " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")"); curl_easy_setopt(pH, CURLOPT_USERAGENT, GAME_NAME " " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")");
curl_easy_setopt(pH, CURLOPT_ACCEPT_ENCODING, ""); // Use any compression algorithm supported by libcurl. curl_easy_setopt(pH, CURLOPT_ACCEPT_ENCODING, ""); // Use any compression algorithm supported by libcurl.
curl_easy_setopt(pH, CURLOPT_HEADERDATA, this);
curl_easy_setopt(pH, CURLOPT_HEADERFUNCTION, HeaderCallback);
curl_easy_setopt(pH, CURLOPT_WRITEDATA, this); curl_easy_setopt(pH, CURLOPT_WRITEDATA, this);
curl_easy_setopt(pH, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(pH, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(pH, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(pH, CURLOPT_NOPROGRESS, 0L);
@ -206,6 +208,52 @@ bool CHttpRequest::ConfigureHandle(void *pHandle)
return true; return true;
} }
size_t CHttpRequest::OnHeader(char *pHeader, size_t HeaderSize)
{
// `pHeader` is NOT null-terminated.
// `pHeader` has a trailing newline.
if(HeaderSize <= 1)
{
m_HeadersEnded = true;
return HeaderSize;
}
if(m_HeadersEnded)
{
// redirect, clear old headers
m_HeadersEnded = false;
m_ResultDate = {};
m_ResultLastModified = {};
}
static const char DATE[] = "Date: ";
static const char LAST_MODIFIED[] = "Last-Modified: ";
// Trailing newline and null termination evens out.
if(HeaderSize - 1 >= sizeof(DATE) - 1 && str_startswith_nocase(pHeader, DATE))
{
char aValue[128];
str_truncate(aValue, sizeof(aValue), pHeader + (sizeof(DATE) - 1), HeaderSize - (sizeof(DATE) - 1) - 1);
int64_t Value = curl_getdate(aValue, nullptr);
if(Value != -1)
{
m_ResultDate = Value;
}
}
if(HeaderSize - 1 >= sizeof(LAST_MODIFIED) - 1 && str_startswith_nocase(pHeader, LAST_MODIFIED))
{
char aValue[128];
str_truncate(aValue, sizeof(aValue), pHeader + (sizeof(LAST_MODIFIED) - 1), HeaderSize - (sizeof(LAST_MODIFIED) - 1) - 1);
int64_t Value = curl_getdate(aValue, nullptr);
if(Value != -1)
{
m_ResultLastModified = Value;
}
}
return HeaderSize;
}
size_t CHttpRequest::OnData(char *pData, size_t DataSize) size_t CHttpRequest::OnData(char *pData, size_t DataSize)
{ {
// Need to check for the maximum response size here as curl can only // Need to check for the maximum response size here as curl can only
@ -244,6 +292,12 @@ size_t CHttpRequest::OnData(char *pData, size_t DataSize)
} }
} }
size_t CHttpRequest::HeaderCallback(char *pData, size_t Size, size_t Number, void *pUser)
{
dbg_assert(Size == 1, "invalid size parameter passed to header callback");
return ((CHttpRequest *)pUser)->OnHeader(pData, Number);
}
size_t CHttpRequest::WriteCallback(char *pData, size_t Size, size_t Number, void *pUser) size_t CHttpRequest::WriteCallback(char *pData, size_t Size, size_t Number, void *pUser)
{ {
return ((CHttpRequest *)pUser)->OnData(pData, Size * Number); return ((CHttpRequest *)pUser)->OnData(pData, Size * Number);
@ -390,6 +444,22 @@ int CHttpRequest::StatusCode() const
return m_StatusCode; return m_StatusCode;
} }
std::optional<int64_t> CHttpRequest::ResultAgeSeconds() const
{
dbg_assert(State() == EHttpState::DONE, "Request not done");
if(!m_ResultDate || !m_ResultLastModified)
{
return {};
}
return *m_ResultDate - *m_ResultLastModified;
}
std::optional<int64_t> CHttpRequest::ResultLastModified() const
{
dbg_assert(State() == EHttpState::DONE, "Request not done");
return m_ResultLastModified;
}
bool CHttp::Init(std::chrono::milliseconds ShutdownDelay) bool CHttp::Init(std::chrono::milliseconds ShutdownDelay)
{ {
m_ShutdownDelay = ShutdownDelay; m_ShutdownDelay = ShutdownDelay;

View file

@ -118,6 +118,9 @@ class CHttpRequest : public IHttpRequest
std::atomic<bool> m_Abort{false}; std::atomic<bool> m_Abort{false};
int m_StatusCode = 0; int m_StatusCode = 0;
bool m_HeadersEnded = false;
std::optional<int64_t> m_ResultDate = {};
std::optional<int64_t> m_ResultLastModified = {};
// Abort the request with an error if `BeforeInit()` returns false. // Abort the request with an error if `BeforeInit()` returns false.
bool BeforeInit(); bool BeforeInit();
@ -125,11 +128,15 @@ class CHttpRequest : public IHttpRequest
// `pHandle` can be nullptr if no handle was ever created for this request. // `pHandle` can be nullptr if no handle was ever created for this request.
void OnCompletionInternal(void *pHandle, unsigned int Result); // void * == CURL *, unsigned int == CURLcode void OnCompletionInternal(void *pHandle, unsigned int Result); // void * == CURL *, unsigned int == CURLcode
// Abort the request if `OnHeader()` returns something other than
// `DataSize`. `pHeader` is NOT null-terminated.
size_t OnHeader(char *pHeader, size_t HeaderSize);
// Abort the request if `OnData()` returns something other than // Abort the request if `OnData()` returns something other than
// `DataSize`. // `DataSize`.
size_t OnData(char *pData, size_t DataSize); size_t OnData(char *pData, size_t DataSize);
static int ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr); static int ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr);
static size_t HeaderCallback(char *pData, size_t Size, size_t Number, void *pUser);
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser); static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser);
protected: protected:
@ -207,6 +214,8 @@ public:
const SHA256_DIGEST &ResultSha256() const; const SHA256_DIGEST &ResultSha256() const;
int StatusCode() const; int StatusCode() const;
std::optional<int64_t> ResultAgeSeconds() const;
std::optional<int64_t> ResultLastModified() const;
}; };
inline std::unique_ptr<CHttpRequest> HttpHead(const char *pUrl) inline std::unique_ptr<CHttpRequest> HttpHead(const char *pUrl)

View file

@ -131,6 +131,33 @@ void CCollision::Init(class CLayers *pLayers)
} }
} }
} }
m_TeleIns.clear();
m_TeleOuts.clear();
m_TeleCheckOuts.clear();
if(m_pTele)
{
for(int i = 0; i < m_Width * m_Height; i++)
{
int Number = TeleLayer()[i].m_Number;
int Type = TeleLayer()[i].m_Type;
if(Number > 0)
{
if(Type == TILE_TELEIN)
{
m_TeleIns[Number - 1].emplace_back(i % m_Width * 32.0f + 16.0f, i / m_Width * 32.0f + 16.0f);
}
else if(Type == TILE_TELEOUT)
{
m_TeleOuts[Number - 1].emplace_back(i % m_Width * 32.0f + 16.0f, i / m_Width * 32.0f + 16.0f);
}
else if(Type == TILE_TELECHECKOUT)
{
m_TeleCheckOuts[Number - 1].emplace_back(i % m_Width * 32.0f + 16.0f, i / m_Width * 32.0f + 16.0f);
}
}
}
}
} }
void CCollision::FillAntibot(CAntibotMapData *pMapData) void CCollision::FillAntibot(CAntibotMapData *pMapData)

View file

@ -6,6 +6,7 @@
#include <base/vmath.h> #include <base/vmath.h>
#include <engine/shared/protocol.h> #include <engine/shared/protocol.h>
#include <map>
#include <vector> #include <vector>
enum enum
@ -116,7 +117,15 @@ public:
class CLayers *Layers() { return m_pLayers; } class CLayers *Layers() { return m_pLayers; }
int m_HighestSwitchNumber; int m_HighestSwitchNumber;
const std::vector<vec2> &TeleIns(int Number) { return m_TeleIns[Number]; }
const std::vector<vec2> &TeleOuts(int Number) { return m_TeleOuts[Number]; }
const std::vector<vec2> &TeleCheckOuts(int Number) { return m_TeleCheckOuts[Number]; }
private: private:
std::map<int, std::vector<vec2>> m_TeleIns;
std::map<int, std::vector<vec2>> m_TeleOuts;
std::map<int, std::vector<vec2>> m_TeleCheckOuts;
class CTeleTile *m_pTele; class CTeleTile *m_pTele;
class CSpeedupTile *m_pSpeedup; class CSpeedupTile *m_pSpeedup;
class CTile *m_pFront; class CTile *m_pFront;

View file

@ -121,11 +121,10 @@ float VelocityRamp(float Value, float Start, float Range, float Curvature)
return 1.0f / std::pow(Curvature, (Value - Start) / Range); return 1.0f / std::pow(Curvature, (Value - Start) / Range);
} }
void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams, std::map<int, std::vector<vec2>> *pTeleOuts) void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams)
{ {
m_pWorld = pWorld; m_pWorld = pWorld;
m_pCollision = pCollision; m_pCollision = pCollision;
m_pTeleOuts = pTeleOuts;
m_pTeams = pTeams; m_pTeams = pTeams;
m_Id = -1; m_Id = -1;
@ -378,14 +377,14 @@ void CCharacterCore::Tick(bool UseInput, bool DoDeferredTick)
m_HookState = HOOK_RETRACT_START; m_HookState = HOOK_RETRACT_START;
} }
if(GoingThroughTele && m_pWorld && m_pTeleOuts && !m_pTeleOuts->empty() && !(*m_pTeleOuts)[teleNr - 1].empty()) if(GoingThroughTele && m_pWorld && !m_pCollision->TeleOuts(teleNr - 1).empty())
{ {
m_TriggeredEvents = 0; m_TriggeredEvents = 0;
SetHookedPlayer(-1); SetHookedPlayer(-1);
m_NewHook = true; m_NewHook = true;
int RandomOut = m_pWorld->RandomOr0((*m_pTeleOuts)[teleNr - 1].size()); int RandomOut = m_pWorld->RandomOr0(m_pCollision->TeleOuts(teleNr - 1).size());
m_HookPos = (*m_pTeleOuts)[teleNr - 1][RandomOut] + TargetDirection * PhysicalSize() * 1.5f; m_HookPos = m_pCollision->TeleOuts(teleNr - 1)[RandomOut] + TargetDirection * PhysicalSize() * 1.5f;
m_HookDir = TargetDirection; m_HookDir = TargetDirection;
m_HookTeleBase = m_HookPos; m_HookTeleBase = m_HookPos;
} }
@ -728,11 +727,6 @@ void CCharacterCore::SetTeamsCore(CTeamsCore *pTeams)
m_pTeams = pTeams; m_pTeams = pTeams;
} }
void CCharacterCore::SetTeleOuts(std::map<int, std::vector<vec2>> *pTeleOuts)
{
m_pTeleOuts = pTeleOuts;
}
bool CCharacterCore::IsSwitchActiveCb(int Number, void *pUser) bool CCharacterCore::IsSwitchActiveCb(int Number, void *pUser)
{ {
CCharacterCore *pThis = (CCharacterCore *)pUser; CCharacterCore *pThis = (CCharacterCore *)pUser;

View file

@ -177,7 +177,6 @@ class CCharacterCore
{ {
CWorldCore *m_pWorld = nullptr; CWorldCore *m_pWorld = nullptr;
CCollision *m_pCollision; CCollision *m_pCollision;
std::map<int, std::vector<vec2>> *m_pTeleOuts;
public: public:
static constexpr float PhysicalSize() { return 28.0f; }; static constexpr float PhysicalSize() { return 28.0f; };
@ -225,7 +224,7 @@ public:
int m_TriggeredEvents; int m_TriggeredEvents;
void Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams = nullptr, std::map<int, std::vector<vec2>> *pTeleOuts = nullptr); void Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams = nullptr);
void SetCoreWorld(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams); void SetCoreWorld(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams);
void Reset(); void Reset();
void TickDeferred(); void TickDeferred();
@ -246,7 +245,6 @@ public:
// DDNet Character // DDNet Character
void SetTeamsCore(CTeamsCore *pTeams); void SetTeamsCore(CTeamsCore *pTeams);
void SetTeleOuts(std::map<int, std::vector<vec2>> *pTeleOuts);
void ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet); void ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet);
bool m_Solo; bool m_Solo;
bool m_Jetpack; bool m_Jetpack;

View file

@ -361,13 +361,13 @@ void CGameContext::ConToTeleporter(IConsole::IResult *pResult, void *pUserData)
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
unsigned int TeleTo = pResult->GetInteger(0); unsigned int TeleTo = pResult->GetInteger(0);
if(!pSelf->m_pController->m_TeleOuts[TeleTo - 1].empty()) if(!pSelf->Collision()->TeleOuts(TeleTo - 1).empty())
{ {
CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientId); CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientId);
if(pChr) if(pChr)
{ {
int TeleOut = pSelf->m_World.m_Core.RandomOr0(pSelf->m_pController->m_TeleOuts[TeleTo - 1].size()); int TeleOut = pSelf->m_World.m_Core.RandomOr0(pSelf->Collision()->TeleOuts(TeleTo - 1).size());
pSelf->Teleport(pChr, pSelf->m_pController->m_TeleOuts[TeleTo - 1][TeleOut]); pSelf->Teleport(pChr, pSelf->Collision()->TeleOuts(TeleTo - 1)[TeleOut]);
} }
} }
} }
@ -377,13 +377,13 @@ void CGameContext::ConToCheckTeleporter(IConsole::IResult *pResult, void *pUserD
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
unsigned int TeleTo = pResult->GetInteger(0); unsigned int TeleTo = pResult->GetInteger(0);
if(!pSelf->m_pController->m_TeleCheckOuts[TeleTo - 1].empty()) if(!pSelf->Collision()->TeleCheckOuts(TeleTo - 1).empty())
{ {
CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientId); CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientId);
if(pChr) if(pChr)
{ {
int TeleOut = pSelf->m_World.m_Core.RandomOr0(pSelf->m_pController->m_TeleCheckOuts[TeleTo - 1].size()); int TeleOut = pSelf->m_World.m_Core.RandomOr0(pSelf->Collision()->TeleCheckOuts(TeleTo - 1).size());
pSelf->Teleport(pChr, pSelf->m_pController->m_TeleCheckOuts[TeleTo - 1][TeleOut]); pSelf->Teleport(pChr, pSelf->Collision()->TeleCheckOuts(TeleTo - 1)[TeleOut]);
pChr->m_TeleCheckpoint = TeleTo; pChr->m_TeleCheckpoint = TeleTo;
} }
} }

View file

@ -794,7 +794,7 @@ void CCharacter::TickDeferred()
// advance the dummy // advance the dummy
{ {
CWorldCore TempWorld; CWorldCore TempWorld;
m_ReckoningCore.Init(&TempWorld, Collision(), &Teams()->m_Core, m_pTeleOuts); m_ReckoningCore.Init(&TempWorld, Collision(), &Teams()->m_Core);
m_ReckoningCore.m_Id = m_pPlayer->GetCid(); m_ReckoningCore.m_Id = m_pPlayer->GetCid();
m_ReckoningCore.Tick(false); m_ReckoningCore.Tick(false);
m_ReckoningCore.Move(); m_ReckoningCore.Move();
@ -1293,13 +1293,6 @@ int CCharacter::Team()
return Teams()->m_Core.Team(m_pPlayer->GetCid()); return Teams()->m_Core.Team(m_pPlayer->GetCid());
} }
void CCharacter::SetTeleports(std::map<int, std::vector<vec2>> *pTeleOuts, std::map<int, std::vector<vec2>> *pTeleCheckOuts)
{
m_pTeleOuts = pTeleOuts;
m_pTeleCheckOuts = pTeleCheckOuts;
m_Core.SetTeleOuts(pTeleOuts);
}
void CCharacter::FillAntibot(CAntibotCharacterData *pData) void CCharacter::FillAntibot(CAntibotCharacterData *pData)
{ {
pData->m_Pos = m_Pos; pData->m_Pos = m_Pos;
@ -1834,12 +1827,12 @@ void CCharacter::HandleTiles(int Index)
} }
int z = Collision()->IsTeleport(MapIndex); int z = Collision()->IsTeleport(MapIndex);
if(!g_Config.m_SvOldTeleportHook && !g_Config.m_SvOldTeleportWeapons && z && !(*m_pTeleOuts)[z - 1].empty()) if(!g_Config.m_SvOldTeleportHook && !g_Config.m_SvOldTeleportWeapons && z && !Collision()->TeleOuts(z - 1).empty())
{ {
if(m_Core.m_Super) if(m_Core.m_Super)
return; return;
int TeleOut = GameWorld()->m_Core.RandomOr0((*m_pTeleOuts)[z - 1].size()); int TeleOut = GameWorld()->m_Core.RandomOr0(Collision()->TeleOuts(z - 1).size());
m_Core.m_Pos = (*m_pTeleOuts)[z - 1][TeleOut]; m_Core.m_Pos = Collision()->TeleOuts(z - 1)[TeleOut];
if(!g_Config.m_SvTeleportHoldHook) if(!g_Config.m_SvTeleportHoldHook)
{ {
ResetHook(); ResetHook();
@ -1849,12 +1842,12 @@ void CCharacter::HandleTiles(int Index)
return; return;
} }
int evilz = Collision()->IsEvilTeleport(MapIndex); int evilz = Collision()->IsEvilTeleport(MapIndex);
if(evilz && !(*m_pTeleOuts)[evilz - 1].empty()) if(evilz && !Collision()->TeleOuts(evilz - 1).empty())
{ {
if(m_Core.m_Super) if(m_Core.m_Super)
return; return;
int TeleOut = GameWorld()->m_Core.RandomOr0((*m_pTeleOuts)[evilz - 1].size()); int TeleOut = GameWorld()->m_Core.RandomOr0(Collision()->TeleOuts(evilz - 1).size());
m_Core.m_Pos = (*m_pTeleOuts)[evilz - 1][TeleOut]; m_Core.m_Pos = Collision()->TeleOuts(evilz - 1)[TeleOut];
if(!g_Config.m_SvOldTeleportHook && !g_Config.m_SvOldTeleportWeapons) if(!g_Config.m_SvOldTeleportHook && !g_Config.m_SvOldTeleportWeapons)
{ {
m_Core.m_Vel = vec2(0, 0); m_Core.m_Vel = vec2(0, 0);
@ -1878,10 +1871,10 @@ void CCharacter::HandleTiles(int Index)
// first check if there is a TeleCheckOut for the current recorded checkpoint, if not check previous checkpoints // first check if there is a TeleCheckOut for the current recorded checkpoint, if not check previous checkpoints
for(int k = m_TeleCheckpoint - 1; k >= 0; k--) for(int k = m_TeleCheckpoint - 1; k >= 0; k--)
{ {
if(!(*m_pTeleCheckOuts)[k].empty()) if(!Collision()->TeleCheckOuts(k).empty())
{ {
int TeleOut = GameWorld()->m_Core.RandomOr0((*m_pTeleCheckOuts)[k].size()); int TeleOut = GameWorld()->m_Core.RandomOr0(Collision()->TeleCheckOuts(k).size());
m_Core.m_Pos = (*m_pTeleCheckOuts)[k][TeleOut]; m_Core.m_Pos = Collision()->TeleCheckOuts(k)[TeleOut];
m_Core.m_Vel = vec2(0, 0); m_Core.m_Vel = vec2(0, 0);
if(!g_Config.m_SvTeleportHoldHook) if(!g_Config.m_SvTeleportHoldHook)
@ -1915,10 +1908,10 @@ void CCharacter::HandleTiles(int Index)
// first check if there is a TeleCheckOut for the current recorded checkpoint, if not check previous checkpoints // first check if there is a TeleCheckOut for the current recorded checkpoint, if not check previous checkpoints
for(int k = m_TeleCheckpoint - 1; k >= 0; k--) for(int k = m_TeleCheckpoint - 1; k >= 0; k--)
{ {
if(!(*m_pTeleCheckOuts)[k].empty()) if(!Collision()->TeleCheckOuts(k).empty())
{ {
int TeleOut = GameWorld()->m_Core.RandomOr0((*m_pTeleCheckOuts)[k].size()); int TeleOut = GameWorld()->m_Core.RandomOr0(Collision()->TeleCheckOuts(k).size());
m_Core.m_Pos = (*m_pTeleCheckOuts)[k][TeleOut]; m_Core.m_Pos = Collision()->TeleCheckOuts(k)[TeleOut];
if(!g_Config.m_SvTeleportHoldHook) if(!g_Config.m_SvTeleportHoldHook)
{ {

View file

@ -147,9 +147,6 @@ private:
CCharacterCore m_Core; CCharacterCore m_Core;
CGameTeams *m_pTeams = nullptr; CGameTeams *m_pTeams = nullptr;
std::map<int, std::vector<vec2>> *m_pTeleOuts = nullptr;
std::map<int, std::vector<vec2>> *m_pTeleCheckOuts = nullptr;
// info for dead reckoning // info for dead reckoning
int m_ReckoningTick; // tick that we are performing dead reckoning From int m_ReckoningTick; // tick that we are performing dead reckoning From
CCharacterCore m_SendCore; // core that we should send CCharacterCore m_SendCore; // core that we should send
@ -179,7 +176,6 @@ private:
public: public:
CGameTeams *Teams() { return m_pTeams; } CGameTeams *Teams() { return m_pTeams; }
void SetTeams(CGameTeams *pTeams); void SetTeams(CGameTeams *pTeams);
void SetTeleports(std::map<int, std::vector<vec2>> *pTeleOuts, std::map<int, std::vector<vec2>> *pTeleCheckOuts);
void FillAntibot(CAntibotCharacterData *pData); void FillAntibot(CAntibotCharacterData *pData);
void Pause(bool Pause); void Pause(bool Pause);

View file

@ -164,10 +164,10 @@ void CLaser::DoBounce()
} }
m_ZeroEnergyBounceInLastTick = Distance == 0.0f; m_ZeroEnergyBounceInLastTick = Distance == 0.0f;
if(Res == TILE_TELEINWEAPON && !GameServer()->m_pController->m_TeleOuts[z - 1].empty()) if(Res == TILE_TELEINWEAPON && !GameServer()->Collision()->TeleOuts(z - 1).empty())
{ {
int TeleOut = GameServer()->m_World.m_Core.RandomOr0(GameServer()->m_pController->m_TeleOuts[z - 1].size()); int TeleOut = GameServer()->m_World.m_Core.RandomOr0(GameServer()->Collision()->TeleOuts(z - 1).size());
m_TelePos = GameServer()->m_pController->m_TeleOuts[z - 1][TeleOut]; m_TelePos = GameServer()->Collision()->TeleOuts(z - 1)[TeleOut];
m_WasTele = true; m_WasTele = true;
} }
else else

View file

@ -276,10 +276,10 @@ void CProjectile::Tick()
z = GameServer()->Collision()->IsTeleport(x); z = GameServer()->Collision()->IsTeleport(x);
else else
z = GameServer()->Collision()->IsTeleportWeapon(x); z = GameServer()->Collision()->IsTeleportWeapon(x);
if(z && !GameServer()->m_pController->m_TeleOuts[z - 1].empty()) if(z && !GameServer()->Collision()->TeleOuts(z - 1).empty())
{ {
int TeleOut = GameServer()->m_World.m_Core.RandomOr0(GameServer()->m_pController->m_TeleOuts[z - 1].size()); int TeleOut = GameServer()->m_World.m_Core.RandomOr0(GameServer()->Collision()->TeleOuts(z - 1).size());
m_Pos = GameServer()->m_pController->m_TeleOuts[z - 1][TeleOut]; m_Pos = GameServer()->Collision()->TeleOuts(z - 1)[TeleOut];
m_StartTick = Server()->Tick(); m_StartTick = Server()->Tick();
} }
} }

View file

@ -40,8 +40,6 @@ IGameController::IGameController(class CGameContext *pGameServer) :
m_ForceBalanced = false; m_ForceBalanced = false;
m_CurrentRecord = 0; m_CurrentRecord = 0;
InitTeleporter();
} }
IGameController::~IGameController() = default; IGameController::~IGameController() = default;
@ -484,8 +482,6 @@ void IGameController::OnCharacterSpawn(class CCharacter *pChr)
// give default weapons // give default weapons
pChr->GiveWeapon(WEAPON_HAMMER); pChr->GiveWeapon(WEAPON_HAMMER);
pChr->GiveWeapon(WEAPON_GUN); pChr->GiveWeapon(WEAPON_GUN);
pChr->SetTeleports(&m_TeleOuts, &m_TeleCheckOuts);
} }
void IGameController::HandleCharacterTiles(CCharacter *pChr, int MapIndex) void IGameController::HandleCharacterTiles(CCharacter *pChr, int MapIndex)
@ -712,31 +708,6 @@ CClientMask IGameController::GetMaskForPlayerWorldEvent(int Asker, int ExceptId)
return Teams().TeamMask(GameServer()->GetDDRaceTeam(Asker), ExceptId, Asker); return Teams().TeamMask(GameServer()->GetDDRaceTeam(Asker), ExceptId, Asker);
} }
void IGameController::InitTeleporter()
{
if(!GameServer()->Collision()->Layers()->TeleLayer())
return;
int Width = GameServer()->Collision()->Layers()->TeleLayer()->m_Width;
int Height = GameServer()->Collision()->Layers()->TeleLayer()->m_Height;
for(int i = 0; i < Width * Height; i++)
{
int Number = GameServer()->Collision()->TeleLayer()[i].m_Number;
int Type = GameServer()->Collision()->TeleLayer()[i].m_Type;
if(Number > 0)
{
if(Type == TILE_TELEOUT)
{
m_TeleOuts[Number - 1].emplace_back(i % Width * 32.0f + 16.0f, i / Width * 32.0f + 16.0f);
}
else if(Type == TILE_TELECHECKOUT)
{
m_TeleCheckOuts[Number - 1].emplace_back(i % Width * 32.0f + 16.0f, i / Width * 32.0f + 16.0f);
}
}
}
}
void IGameController::DoTeamChange(CPlayer *pPlayer, int Team, bool DoChatMsg) void IGameController::DoTeamChange(CPlayer *pPlayer, int Team, bool DoChatMsg)
{ {
Team = ClampTeam(Team); Team = ClampTeam(Team);

View file

@ -8,9 +8,6 @@
#include <engine/shared/protocol.h> #include <engine/shared/protocol.h>
#include <game/server/teams.h> #include <game/server/teams.h>
#include <map>
#include <vector>
struct CScoreLoadBestTimeResult; struct CScoreLoadBestTimeResult;
/* /*
@ -148,15 +145,12 @@ public:
virtual bool CanJoinTeam(int Team, int NotThisId, char *pErrorReason, int ErrorReasonSize); virtual bool CanJoinTeam(int Team, int NotThisId, char *pErrorReason, int ErrorReasonSize);
int ClampTeam(int Team); int ClampTeam(int Team);
CClientMask GetMaskForPlayerWorldEvent(int Asker, int ExceptId = -1); CClientMask GetMaskForPlayerWorldEvent(int Asker, int ExceptID = -1);
virtual void InitTeleporter();
bool IsTeamPlay() { return m_GameFlags & GAMEFLAG_TEAMS; } bool IsTeamPlay() { return m_GameFlags & GAMEFLAG_TEAMS; }
// DDRace // DDRace
float m_CurrentRecord; float m_CurrentRecord;
std::map<int, std::vector<vec2>> m_TeleOuts;
std::map<int, std::vector<vec2>> m_TeleCheckOuts;
CGameTeams &Teams() { return m_Teams; } CGameTeams &Teams() { return m_Teams; }
std::shared_ptr<CScoreLoadBestTimeResult> m_pLoadBestTimeResult; std::shared_ptr<CScoreLoadBestTimeResult> m_pLoadBestTimeResult;
}; };

View file

@ -3,11 +3,11 @@
#ifndef GAME_VERSION_H #ifndef GAME_VERSION_H
#define GAME_VERSION_H #define GAME_VERSION_H
#ifndef GAME_RELEASE_VERSION #ifndef GAME_RELEASE_VERSION
#define GAME_RELEASE_VERSION "18.0.3" #define GAME_RELEASE_VERSION "18.1"
#endif #endif
#define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION #define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION
#define GAME_NETVERSION "0.6 626fce9a778df4d4" #define GAME_NETVERSION "0.6 626fce9a778df4d4"
#define DDNET_VERSION_NUMBER 18003 #define DDNET_VERSION_NUMBER 18010
extern const char *GIT_SHORTREV_HASH; extern const char *GIT_SHORTREV_HASH;
#define GAME_NAME "DDNet" #define GAME_NAME "DDNet"
#endif #endif