Environment.TickCount64,效率高o(1),比较精确。单位是毫秒。DateTime接口,但是频繁调会比较费。考虑每隔一段时间拉取后缓存起来。同样,如果以计时为需求,用TickCount就能用。但是客户端的C#版本只支持TickCount,不支持TickCount64。
int.MaxValue ≈ 2,147,483,647 ms ≈ 24.9 天
这里我们采用”进位”的思想,记录上一次TickCount的符号和这次TickCount的符号。如果上一次为负号,这一次为正号,那么说明发生了一次完整溢出,高32位进一位。如果上一次为正号,这一次为负号,那么发生了一次正数溢出。
static sbyte tickCount = 0;
static bool negative = tickCount < 0;
static short trueTickCount = 0;
static short bits = 0;
static short step = 1 << 8;
static void F() {
//byte是8位 short是16位
tickCount++;
//如果上次是负数,这次正数,说明有溢出
if (negative)
{
if (tickCount >= 0)
{
negative= false;
bits+=step;
}
}
//如果上次是正数,这次负数,说明超过sbyte的最大值了
else
{
if (tickCount < 0)
{
negative = true;
}
}
trueTickCount = (short)((bits) | (byte)tickCount);
}
int.MaxValue * 2 ≈ 2,147,483,647 * 2 ms ≈ 24.9 * 2天也就是差不多49天没有Tick一次,那么检测溢出会失效一次。由于客户端时区和本地时间和服务器的不一定一致,所以需要同步。
在客户端玩家登录后,服务器同步一下时间和时区。然后按照间隔同步时间。或者只同步一次时间,然后客户端本地按照Tick加上原始时间戳。但是这样有一个问题,客户端和服务器的Tick依赖于硬件,会有累计误差。所以还是服务器定时对一下表。
很多游戏都有过了凌晨12点/凌晨4点/凌晨5点后重置某些模块的需求。
一种思路是,每次判断的时候,判断上一次时间和这次时间中间,是否有过某点的次数,然后发送事件通知各个模块去处理。
如何判断呢?可以想象有两根线,分别代表上次时间和这次时间,然后按照比如凌晨5点画很多根线,这些线如果在二者中间就表示跨了一次。
int Check(DateTime d0,DateTime d1)
{
int count = 0;
var ts = d1 - d0;
for(int i=0;i<=ts.Days; i++)
{
var checkDate = new DateTime(d0.Year, d0.Month, d0.Day+i, 5, 0, 0);
var t0 = checkDate.Ticks - d0.Ticks;
var t1 = checkDate.Ticks - d1.Ticks;
if(t0>0 && t1 < 0)
{
count++;
}
}
return count;
}