Newer
Older
Import / projects / Gameloft / core / Time / ServerTime.h
#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__