#ifndef __UTIL_SERVER_TIME_H_INCLUDED__
#define __UTIL_SERVER_TIME_H_INCLUDED__
#include <RKTypes.h>
#include <string>
#include <time.h>
#include "RKTimer.h"
#if defined(OS_ANDROID)
#include <cstdlib>
#endif
/// brief Wrapper class representing a timestamp.
/// details This provides some compile-time protection from accidentally passing incorrect values
/// into the ServerTimeManager helper functions.
/// note 'strong typedef' pattern borrowed from BOOST_STRONG_TYPEDEF.
/// note Even though the timestamps are calculated using functions from time.h, we don't store
/// this as a time_t due to serialisation concerns: instead an int32 is used.
class Timestamp
{
public:
static const Timestamp INVALID_TIMESTAMP;
static const Timestamp BEGINNING_OF_TIME;
static const Timestamp MAX_TIMESTAMP;
static const Timestamp ONE_DAY;
static const int SECONDS_IN_DAY;
typedef int32 StorageType;
Timestamp() {}
explicit Timestamp(const StorageType rhs) : m_data(rhs) {}
#if defined(OS_ANDROID)
explicit Timestamp(const std::string& strTime) { m_data = std::atoi(strTime.c_str()); }
#else
explicit Timestamp(const std::string& strTime) { m_data = std::stoi(strTime); }
#endif // OS_ANDROID
Timestamp(const Timestamp & rhs) : m_data(rhs.m_data){}
Timestamp & operator=(const Timestamp & rhs) { m_data = rhs.m_data; return *this; }
Timestamp & operator=(const StorageType & rhs) { m_data = rhs; return *this; }
operator const StorageType & () const { return m_data; }
operator StorageType & () { return m_data; }
const StorageType* operator&() const { return &m_data; } // NOLINT(runtime/operator)
StorageType* operator&() { return &m_data; } // NOLINT(runtime/operator)
static Timestamp Now() { return Timestamp(time(nullptr)); }
Timestamp & operator+=(const Timestamp & other) { m_data += other.m_data; return *this; }
Timestamp operator+(const Timestamp & other) { return Timestamp(m_data + other.m_data); }
Timestamp operator-(const Timestamp & other) { return Timestamp(m_data - other.m_data); }
Timestamp GetDayStart() const { return Timestamp((m_data / SECONDS_IN_DAY) * SECONDS_IN_DAY); }
Timestamp GetPeriodStart(const Timestamp& base, uint32_t durationSecs) const
{
Timestamp offset(((m_data - base.m_data) / durationSecs) * durationSecs);
Timestamp ret = offset + base;
return ret;
}
Timestamp GetPeriodEnd(const Timestamp& base, uint32_t durationSecs) const
{
Timestamp start = GetPeriodStart(base, durationSecs);
Timestamp ret = Timestamp(durationSecs) + start;
return ret;
}
// determine if another time stamp is within the same period as this one
bool IsWithinSamePeriod(const Timestamp& base, uint32_t durationSecs, const Timestamp& other) const
{
Timestamp start = other.GetPeriodStart(base, durationSecs);
Timestamp end = other.GetPeriodEnd(base, durationSecs);
return *this > start &&
*this <= end;
}
private:
StorageType m_data = 0;
};
/// brief Tracks the time difference between server and client clocks and communication latency
/// in order to provide accurate comparisons between timestamps.
class ServerTimeManager
{
public:
static inline ServerTimeManager* Get() { return m_instance; }
/// brief Gives length of time encapsulated by two different timestamps from the same clock.
/// return Number of seconds that occur between the two timestamps.
/// note Calling this function with timestamps from other system's clocks may yield unexpected results.
static inline int64_t GetTimeDifference(Timestamp endTime, Timestamp beginTime)
{
return static_cast<int64_t>(endTime) - static_cast<int64_t>(beginTime);
}
/// brief Gives the approximate server time.
/// return An approximate server timestamp.
static Timestamp GetApproximateServerTime();
/// brief Gives the time until this system's clock reaches a timestamp from the server's clock.
/// return The approximate number of seconds until this clock is equal to the timestamp indicated by the passed parameter.
static inline int64_t GetApproximateTimeRemaining(Timestamp timeUntil)
{
return GetTimeDifference(timeUntil, GetApproximateServerTime());
}
/// brief Set the server clock difference from the received server time
void OnServerTimestampReceived(Timestamp nServerTimestamp);
static int64_t ConvertUTCTimeStringToSeconds(const std::string & dateAsString);
private:
static void RegisterService();
static void UnregisterService();
static ServerTimeManager* m_instance;
RKTimer m_timer;
int64_t m_lastServerClock = 0;
float m_leftoverTime = 0.0f;
};
namespace bne
{
///*******************************************************************
/// \brief Duration of an action (and associated helper functions)
///*******************************************************************
class Duration
{
public:
bool operator==(const Duration& other) const;
bool operator!=(const Duration& other) const;
int64_t GetTotalDuration() const;
int64_t GetRemainingDuration(Timestamp now = ServerTimeManager::GetApproximateServerTime()) const;
int64_t GetElapsedDuration(Timestamp now = ServerTimeManager::GetApproximateServerTime()) const;
bool IsComplete(Timestamp now = ServerTimeManager::GetApproximateServerTime()) const;
bool IsActive() const;
void Start(int32 a_duration);
void Stop();
void ShiftTime(int32 a_offset);
void SkipTime(int32 a_offset) { ShiftTime(-a_offset); }
const Timestamp& GetStartTime() const { return m_start; }
const Timestamp& GetEndTime() const { return m_end; }
void SetStartTime(const Timestamp& a_start) { m_start = a_start; }
void SetEndTime(const Timestamp& a_end) { m_end = a_end; }
private:
Timestamp m_start = Timestamp::INVALID_TIMESTAMP; //< time at which we started
Timestamp m_end = Timestamp::INVALID_TIMESTAMP; //< time at which we will end
};
///*******************************************************************
} // namespace bne
#endif // __UTIL_SERVER_TIME_H_INCLUDED__