시간과 타이머¶
아이펀 엔진은 내부적으로 monotonic clock 을 사용하며, 개발자의 편의를 위해 참고용으로 wall clock 역시 제공합니다. 그리고 주기적으로 event 를 발생시킬 수 있는 timer 역시 제공합니다. 각각에 대한 설명은 다음과 같습니다.
Monotonic clock¶
Wall clock 은 현실 세계와 시간 단위가 똑같다는 장점이 있지만, 서버의 시간을 동기화할 때 시간 값이 튈 수 있다는 단점이 있습니다. 이때 서버 내부에서 특정 시간에 처리되어야 하는 일이 실행되지 않거나, 두 번 실행되는 일이 발생할 수 있습니다. 이 때문에 아이펀 엔진은 모든 시간 단위를 Monotonic clock 을 이용하며 게임 서버 개발자 역시 monotonic clock 을 사용할 것을 권장합니다.
Monotonic clock 은 이름에서 알 수 있듯이 시간 값이 단조 증가하는 시간입니다. 즉 서버의 시간을 동기화 해서 1초가 뒤로 돌아가더라도 안정적으로 증가하는 시간 값을 반환합니다. monotonic clock 은 최신 OS 들에서는 모두 제공하는 기능이며, 아이펀 엔진의 monotomic clock 은 system 에서 제공하는 monotonic clock 을 이용합니다. 아이펀 엔진의 Monotonic clock 는 마이크로세컨드까지의 정밀도를 제공합니다.
Monotonic clock 활용법¶
#include <funapi.h>
void test_function(MonotonicClock::Value old) {
// 현재 시간은 다음처럼 얻습니다.
MonotonicClock::Value t = MonotonicClock::Now();
// 시간 비교는 일반 integer 와 같습니다.
if (old < t) {
// old 는 t 보다는 작네요.
}
// struct timespec 으로의 변경은 다음처럼 합니다.
struc timesepc ts;
MonotonicClock::ToTimespec(t, &ts);
}
using funapi;
void TestFunction(Int64 old)
{
// 현재 시간은 다음처럼 얻습니다.
Int64 t = MonotonicClock.Now;
// 시간 비교는 일반 integer 와 같습니다.
if (old < t) {
// old 는 t 보다는 작네요.
}
}
Wall clock¶
Monotonic clock 은 서버 시간을 보정하는 상황에서도 안정적으로 쓸 수 있다는 장점이 있지만, 사람이 읽을 수 있는 형태가 아니기 때문에 디버깅할 때 곤란합니다. 이 때문에 아이펀 엔진은 Wall clock 도 제공합니다.
아이펀 엔진에서의 Wall clock 은 게임 서버가 뜨는 순간의 현실 시계와 Monotonic clock 값의 차이를 기억했다가 이후에 Wall clock 이 필요한 경우 해당 차이를 반영한 값을 반영하는 것입니다. 이 때문에 아이펀 엔진의 Wall clock 역시 단조 증가합니다.
그 때문에 OS 시간이 보정될 때 아이펀 엔진의 Wall clock 값과 OS의 시간 값이 다를 수 있습니다. 그 때문에 아이펀 엔진에서는 Wall clock 을 참고용으로 쓰시는 것을 권장합니다.
Wall clock 활용법¶
#include <funapi.h>
void test_function(WallClock::Value old) {
// 현재 시간은 다음처럼 얻습니다.
WallClock::Value t = WallClock::Now();
// 시간 비교는 일반 integer 와 같습니다.
if (old < t) {
// old 는 t 보다는 작네요.
}
// 로그로 쉽게 출력할 수 있습니다.
LOG(INFO) << t;
// Monotonic clock 값으로 변환할 수도 있습니다.
MonotonicClock::Value m = WallClock::ToMonotonicClock(t);
}
using funapi;
public void TestFunction(System.DateTime old)
{
// 현재 시간은 다음처럼 얻습니다.
System.DateTime dt = WallClock.Now;
// 시간 비교는 일반 DateTime 과 같습니다.
if (old < t) {
// old 는 t 보다는 작네요.
}
// Monotonic clock 값으로 변환할 수도 있습니다.
Int64 m = WallClock.ToMonotonicClock (dt);
}
Note
WallClock 의 Now 는 UTC 값을 리턴합니다.
Timestamp 로의 전환¶
아래처럼 Wall clock 을 정수값의 timestamp 로 변환할 수 있습니다.
// 현재 시간에 대한 timestamp 를 얻거나
int64_t ts = WallClock::GetTimestampInSec();
// WallClock::Value 값을 timestamp 로 변환할 수 있습니다.
WallClock::Value t = WallClock::Now();
ts = WallClock::GetTimestampInSec(t);
// 반대로 timestamp 값을 WallClock::Value 로도 변환할 수 있습니다.
t = WallClock::FromTimestampInSec(ts);
// 밀리초 단위로도 가능합니다.
ts = WallClock::GetTimestampInMsec();
ts = WallClock::GetTimestampInMsec(t);
t = WallClock::FromTimestampInMsec(ts);
// 현재 시간에 대한 timestamp 를 얻거나
Int64 ts = WallClock.GetTimestampInSec ();
// System.DateTime 값을 timestamp 로 변환할 수 있습니다.
System.DateTime dateTime = WallClock.Now;
ts = WallClock.GetTimestampInSec (dateTime);
// 반대로 timestamp 값을 System.DateTime 로도 변환할 수 있습니다.
dateTime = WallClock.FromTimestampInSec (ts);
// 밀리초 단위로도 가능합니다.
ts = WallClock.GetTimestampInMsec ();
ts = WallClock.GetTimestampInMsec (dateTime);
dateTime = WallClock.FromTimestampInMsec (ts);
시간의 문자열 표현¶
아래처럼 Wall clock 을 문자열 형태로 변환할 수 있습니다.
Note
시간에 대한 문자열 표현은 ISO-extended 형식으로 표현됩니다. 예) 2016-09-21T03:30:16.787663
// 현재 시간에 대한 문자열을 얻거나
string ts = WallClock::GetTimestring();
// WallClock::Value 값을 문자열로 변환할 수 있습니다.
WallClock::Value t = WallClock::Now();
ts = WallClock::GetTimestring(t);
// 반대로 문자열을 WallClock::Value 로도 변환할 수 있습니다.
if (not WallClock::FromTimestring(ts, &t)) {
// ts 가 올바른 iso-extended time string 이 아니면 실패합니다.
}
// 현재 시간에 대한 문자열을 얻거나
string timestr = WallClock.GetTimestring ();
// System.DateTime 값을 문자열로 변환할 수 있습니다.
System.DateTime t = WallClock.Now;
timestr = t.ToTimestring ();
Log.Info (timestr);
// 반대로 문자열을 System.DateTime 로도 변환할 수 있습니다.
if (!WallClock.FromTimestring (timestr, out t)) {
// timestr 가 올바른 iso-extended time string 이 아니면 실패합니다.
}
시스템 설정의 지역을 반영해서 시간 문자열을 얻을 수도 있습니다.
// 현재 시간에 대한 로컬 시간 문자열을 얻거나
string ts = WallClock::GetLocalTimestring();
// WallClock::Value 값을 로컬 시간 문자열로 변환할 수 있습니다.
WallClock::Value t = WallClock::Now();
ts = WallClock::GetLocalTimestring(t);
// 현재 시간에 대한 로컬 시간 문자열을 얻거나
string timestr = WallClock.GetLocalTimestring ();
// System.DateTime 값을 로컬 시간 문자열로 변환할 수 있습니다.
System.DateTime t = WallClock.Now;
timestr = t.ToLocalTimestring ();
타이머¶
게임 서버들은 대개 주기적으로 처리하는 일들이 있고, 이를 위해 ‘Tick’ 이라고 불리는 반복되는 timer 가 필요합니다. 아이펀 엔진은 반복되는 타이머와 일회성 타이머를 지원합니다. 아이펀 엔진 타이머는 타이머가 expire 될 때 호출될 핸들러를 등록하는 방식으로 사용합니다. 아래는 그 예제입니다.
타이머 활용법¶
#include <funapi.h>
extern void handler1(Timer::Id tid, const WallClock::Value &at);
extern void handler2(Timer::Id tid, const WallClock::Value &at);
extern void handler3(Timer::Id tid, const WallClock::Value &at);
void test_function() {
// 특정 시간에 expire 되게 하려면 ExpireAt 을 이용합니다.
// 아래 예는 지금으로부터 500msec 뒤에 expire 하도록 설정합니다
Timer::ExpireAt(WallClock::Now() + boost::posix_time::millisec(500), handler1);
// 상대 시간 뒤에 expire 되게 하려면 ExpireAfter 를 이용합니다.
Timer::ExpireAfter(boost::posix_time::millisec(500), handler2);
// 반복해서 expire 되는 timer 는 interval 과 핸들러를 지정합니다.
// 아래는 500msec 단위로 expire 되는 timer 입니다.
Timer::ExpireRepeatedly(boost::posix_time::millisec(500), handler3);
}
Important
Timer::ExpireAt()
, Timer::ExpireAfter()
, Timer::ExpireRepeatedly()
, Timer::Cancel()
함수들은 의도하지 않은 중복 호출을 방지하기 위하여 오브젝트 롤백을 감지하는 기능이 내장되어 있습니다. 오브젝트 서브시스템의 기능을 이용하신다면 트랜잭션 을 참고하세요.
using funapi;
public void TestFunction()
{
// 특정 시간에 expire 되게 하려면 ExpireAt 을 이용합니다.
// 아래 예는 지금으로부터 500msec 뒤에 expire 하도록 설정합니다
Timer.ExpireAt (
WallClock.Now + WallClock.FromMsec(1500), TimerHandler1);
// delegate를 이용해 타이머를 호출 할수도 있습니다.
Timer.Handler timer_handler2 = (
/*UInt64 tid*/ tid,
/*System.DateTime*/ value) => {
Log.Info ("timer_handler_2");
};
// 반복해서 expire 되는 timer 는 interval 과 핸들러를 지정합니다.
// 아래는 500msec 단위로 expire 되는 timer 입니다.
// delegate를 이용해 호출하겠습니다.
Timer.ExpireRepeatedly (WallClock.FromSec (1), timer_handler2);
// 반복해서 expire 되는 timer 는 interval 과 핸들러를 지정합니다.
// 아래는 500msec 단위로 expire 되는 timer 입니다.
Timer.ExpireAfter (WallClock.FromMsec(500), TimerHandler3);
}
public void TimerHandler1(UInt64 tid, DateTime value)
{
Log.Info ("TimerHandler_1");
}
public void TimerHandler3(UInt64 tid, DateTime value)
{
Log.Info ("TimerHandler_3");
}
Important
Timer.ExpireAt()
, Timer.ExpireAfter()
, Timer.ExpireRepeatedly()
, Timer::Cancel()
함수들은 의도하지 않은 중복 호출을 방지하기 위하여 오브젝트 롤백을 감지하는 기능이 내장되어 있습니다. 오브젝트 서브시스템의 기능을 이용하신다면 트랜잭션 을 참고하세요.