#include <Time/ServerTime.h>
#include <RKHeap.h>
#include <time.h>
#include <limits.h>
#include <stdlib.h>
#include <algorithm>
#include <cmath>
#include <sstream>
#include <iomanip>
#include <cstring>
#if defined(GAME_WIN32)
#if !defined(snprintf)
#define snprintf _snprintf
#endif
#endif
#if defined(GAME_WIN32)
#define gmtime_safe(out, in) gmtime_s(out, in)
#define localtime_safe(out, in) localtime_s(out, in)
#else
#define gmtime_safe(out, in) gmtime_r(in, out)
#define localtime_safe(out, in) localtime_r(in, out)
#endif
const Timestamp Timestamp::INVALID_TIMESTAMP = Timestamp(0);
const Timestamp Timestamp::BEGINNING_OF_TIME = Timestamp(1);
const Timestamp Timestamp::MAX_TIMESTAMP = Timestamp(INT_MAX);
const int32 Timestamp::SECONDS_IN_DAY = 24 * 60 * 60;
const Timestamp Timestamp::ONE_DAY = Timestamp(SECONDS_IN_DAY);
ServerTimeManager* ServerTimeManager::m_instance = nullptr;
Timestamp ServerTimeManager::GetApproximateServerTime()
{
ServerTimeManager* thisPtr = Get();
if (thisPtr)
{
return Timestamp(thisPtr->m_lastServerClock + Timestamp(thisPtr->m_leftoverTime + thisPtr->m_timer.GetUpTime()));
}
else
{
return Timestamp::Now();
}
}
void ServerTimeManager::OnServerTimestampReceived(Timestamp nServerTimestamp)
{
m_lastServerClock = nServerTimestamp;
// Store the remaining sub-second times from last server timestamp for less jerky countdowns.
double discardedPart;
m_leftoverTime = modf(m_leftoverTime + m_timer.GetUpTime(), &discardedPart);
// Stay in the past so that events are not sent ahead of time
if (m_leftoverTime > 0.0f)
{
m_leftoverTime -= 1.0f;
}
// Reset the timer.
m_timer.Init();
}
void ServerTimeManager::RegisterService()
{
if (!m_instance)
{
m_instance = new ServerTimeManager();
}
}
void ServerTimeManager::UnregisterService()
{
delete m_instance;
m_instance = nullptr;
}
int64_t ServerTimeManager::ConvertUTCTimeStringToSeconds(const std::string & serverTime)
{
if (serverTime.empty())
{
return 0;
}
time_t timeAsSecs = 0;
tm stagingTm = {};
std::memset(&stagingTm, 0, sizeof(struct tm));
// Convert to tm time
#if defined(GAME_WIN32)
std::stringstream serverTimeAsStream(serverTime);
serverTimeAsStream >> std::get_time(&stagingTm, "%Y-%m-%d%t%H:%M:%S");
#else
// Our linux build isn't fully C++11 compliant!!!
std::string strGMTDate = serverTime;
strGMTDate += " GMT";
strptime(strGMTDate.c_str(), "%Y-%m-%d %H:%M:%SZ %Z", &stagingTm);
#endif
timeAsSecs = mktime(&stagingTm);
#if defined(OS_ANDROID) || defined(WIN32) || defined(OS_LINUX)
// Calculate the time offset for our time zone
time_t rawtime = 0;
tm timeinfo;
::time(&rawtime);
localtime_safe(&timeinfo, &rawtime);
#ifndef OS_ANDROID //tam.dnn fix 8916228
timeinfo.tm_isdst = 0;
#endif
time_t addSec = mktime(&timeinfo);
gmtime_safe(&timeinfo, &rawtime);
addSec -= mktime(&timeinfo);
timeAsSecs += addSec;
#endif
return timeAsSecs;
}
bool bne::Duration::operator==(const bne::Duration& other) const
{
return GetStartTime() == other.GetStartTime() && GetEndTime() == other.GetEndTime();
}
bool bne::Duration::operator!=(const bne::Duration& other) const
{
return !(*this == other);
}
int64_t bne::Duration::GetTotalDuration() const
{
return ServerTimeManager::GetTimeDifference(m_end, m_start);
}
int64_t bne::Duration::GetRemainingDuration(Timestamp now) const
{
return std::max(ServerTimeManager::GetTimeDifference(m_end, now), int64_t(0));
}
int64_t bne::Duration::GetElapsedDuration(Timestamp now) const
{
return ServerTimeManager::GetTimeDifference(now, m_start);
}
bool bne::Duration::IsComplete(Timestamp now) const
{
return ServerTimeManager::GetTimeDifference(now, GetEndTime()) >= 0;
}
bool bne::Duration::IsActive() const
{
return GetStartTime() != Timestamp::INVALID_TIMESTAMP && GetEndTime() != Timestamp::INVALID_TIMESTAMP;
}
void bne::Duration::Start(int32 a_duration)
{
SetStartTime(ServerTimeManager::GetApproximateServerTime());
SetEndTime(Timestamp(a_duration) + GetStartTime());
}
void bne::Duration::Stop()
{
SetStartTime(Timestamp::INVALID_TIMESTAMP);
SetEndTime(Timestamp::INVALID_TIMESTAMP);
}
void bne::Duration::ShiftTime(int32 a_secOffset)
{
Timestamp offset(a_secOffset);
SetStartTime(offset + GetStartTime());
SetEndTime(offset + GetEndTime());
if (GetEndTime() < GetStartTime()) { SetEndTime(GetStartTime()); }
}