Files
Main/Assets/Code/Logic/_Required/TimerEvent/TimerEventSystem.cs

363 lines
16 KiB
C#
Raw Normal View History

2025-01-25 04:38:09 +08:00
using Thousandto.Code.Center;
using Thousandto.Core.Base;
using System;
using System.Collections.Generic;
namespace Thousandto.Code.Logic
{
//计时器事件系统
public class TimerEventSystem : BaseSystem
{
#region//常量
//每小时的秒数
public const float HOUR_SEC_COUNT = 60 * 60;
//每天的秒数
public const float DAY_SEC_COUNT = HOUR_SEC_COUNT * 24;
//每周的秒数
public const float WEEK_SEC_COUNT = DAY_SEC_COUNT * 7;
#endregion
#region//私有变量
//id计数器
private int _idCounter = 1;
//计时器列表
private List<TimerEventInfo> _timeEventList = new List<TimerEventInfo>();
//记录心跳系统是否已经接收到心跳信息
private bool _receiveHeartMsg = false;
//下一次跨天时间戳
private int _crossDayTime = -1;
#endregion
#region//初始化,反初始化
public void Initialize()
{
_receiveHeartMsg = false;
_idCounter = 1;
_crossDayTime = -1;
}
public void Uninitialize()
{
_receiveHeartMsg = false;
_idCounter = 1;
_timeEventList.Clear();
_crossDayTime = -1;
}
#endregion
#region//公有函数
/// <summary>
/// 增加倒计时事件
/// </summary>
/// <param name="countDownTime 倒计时时间,单位秒"></param>
/// <param loop="是否循环不循环就只会执行1次循环就会循环倒计时"></param>
/// <param callBack="时间到时的回调函数,参数为计时器id"></param>
/// <returns>返回计时器ID如果小于0表示添加失败</returns>
public int AddCountDownEvent(float countDownTime, bool loop, object param, Action<int, double, object> callBack)
{
if (countDownTime <= 0f)
return -1;
if (callBack == null)
return -1;
return AddTimerEvent(countDownTime, 0f, TimerEventType.CountDown, loop, param, callBack);
}
/// <summary>
/// 增加小时时间戳计时器,每个小时到达指定时间时通知
/// </summary>
/// <param time="时间值单位秒每小时达到这个时间的时候通知本时间必须小于1小时"></param>
/// <param loop="是否循环不循环就只会执行1次循环就会循环计时"></param>
/// <param callBack="时间到时的回调函数,参数为计时器id"></param>
/// <returns>返回计时器ID如果小于0表示添加失败</returns>
public int AddTimeStampHourEvent(float time, float validTime, bool loop, object param, Action<int, double, object> callBack)
{
if (time <= 0f || time >= HOUR_SEC_COUNT)
return -1;
if (callBack == null)
return -1;
return AddTimerEvent(time, validTime, TimerEventType.TimeStampHour, loop, param, callBack);
}
/// <summary>
/// 增加天时间戳计时器,每天到达指定时间时通知
/// </summary>
/// <param time="时间值单位秒每天达到这个时间的时候通知本时间必须小于1天"></param>
/// <param loop="是否循环不循环就只会执行1次循环就会循环计时"></param>
/// <param callBack="时间到时的回调函数,参数为计时器id"></param>
/// <returns>返回计时器ID如果小于0表示添加失败</returns>
public int AddTimeStampDayEvent(float time, float validTime, bool loop, object param, Action<int, double, object> callBack)
{
if (time <= 0f || time >= DAY_SEC_COUNT)
return -1;
if (callBack == null)
return -1;
return AddTimerEvent(time, validTime, TimerEventType.TimeStampDay, loop, param, callBack);
}
/// <summary>
/// 增加周时间戳计时器,每周到达指定时间时通知
/// </summary>
/// <param time="时间值单位秒每周达到这个时间的时候通知本时间必须小于1周"></param>
/// <param loop="是否循环不循环就只会执行1次循环就会循环计时"></param>
/// <param callBack="时间到时的回调函数,参数为计时器id"></param>
/// <returns>返回计时器ID如果小于0表示添加失败</returns>
public int AddTimeStampWeekEvent(float time, float validTime, bool loop, object param, Action<int, double, object> callBack)
{
if (time <= 0f || time >= WEEK_SEC_COUNT)
{
UnityEngine.Debug.LogError("周活动时间有误:" + time + (time <= 0) + (time >= WEEK_SEC_COUNT));
return -1;
}
if (callBack == null)
{
UnityEngine.Debug.LogError("周活动callBack为空");
return -1;
}
return AddTimerEvent(time, validTime, TimerEventType.TimeStampWeek, loop, param, callBack);
}
/// <summary>
/// 删除计时器
/// </summary>
/// <param name="id 计时器id"></param>
public void RemoveTimerEvent(int id)
{
for (int i = _timeEventList.Count - 1; i >= 0; --i)
{
if (_timeEventList[i].ID == id)
{
_timeEventList.RemoveAt(i);
break;
}
}
}
#endregion
#region//私有函数
protected override bool OnUpdate(float deltaTime)
{
var curHeart = GameCenter.HeartSystem.IsReceiveHeart;
if (curHeart && !_receiveHeartMsg)
{
//判断如果第一次收到心跳消息,重新计算计时器
_receiveHeartMsg = true;
for (int i = _timeEventList.Count - 1; i >= 0; --i)
{
CalculateExecuteTime(_timeEventList[i]);
}
_timeEventList.Sort(TimerSort);
_crossDayTime = GetCrossDayTime((int)GameCenter.HeartSystem.ServerTime);
}
if (curHeart)
{
var serverTime = GameCenter.HeartSystem.ServerTime + GameCenter.HeartSystem.ServerZoneOffset;
bool reSort = false;
for (int i = _timeEventList.Count - 1; i >= 0; --i)
{
if(i >= _timeEventList.Count)
{
continue;
}
var timer = _timeEventList[i];
if (CheckTimer(timer, serverTime))
{
if (timer.Loop)
{
//循环的计时器,需要重新排序
reSort = true;
}
else
{
_timeEventList.Remove(timer);
}
}
else
{
break;
}
}
if (reSort)
{
_timeEventList.Sort(TimerSort);
}
if (GameCenter.HeartSystem.ServerTime >= _crossDayTime)
{
_crossDayTime = GetCrossDayTime((int)GameCenter.HeartSystem.ServerTime);
GameCenter.PushFixEvent(Global.LogicEventDefine.EID_EVENT_CROSSDAY);
}
}
return base.OnUpdate(deltaTime);
}
private bool CheckTimer(TimerEventInfo timer, double serverTime)
{
if (serverTime >= timer.NextExecuteTime)
{
//已经超过了执行的时间,判断是否还在有效期内
var outTime = serverTime - timer.NextExecuteTime;
if (outTime < timer.ValidTimeValue || timer.Type == TimerEventType.CountDown)
{
//还在有效期,执行事件
if (!timer.IsExecute)
{
timer.CallBack(timer.ID, timer.ValidTimeValue - outTime, timer.Param);
}
if (!timer.Loop)
{
timer.IsExecute = true;
}
}
if (timer.Loop)
{
timer.IsExecute = false;
switch (timer.Type)
{
case TimerEventType.CountDown:
//下一次处理时间
timer.NextExecuteTime = serverTime + timer.TimeValue;
break;
case TimerEventType.TimeStampHour:
{
//下一次处理时间
DateTime curDateTime = new DateTime(1970, 1, 1, 0, 0, 0);
long lTime = long.Parse(((ulong)serverTime).ToString() + "0000000");
TimeSpan toNow = new TimeSpan(lTime);
curDateTime = curDateTime.Add(toNow);
var hourStartTime = serverTime - curDateTime.Minute * 60 - curDateTime.Second;
timer.NextExecuteTime = hourStartTime + HOUR_SEC_COUNT + timer.TimeValue;
}
break;
case TimerEventType.TimeStampDay:
{
//下一次处理时间
DateTime curDateTime = new DateTime(1970, 1, 1, 0, 0, 0);
long lTime = long.Parse(((ulong)serverTime).ToString() + "0000000");
TimeSpan toNow = new TimeSpan(lTime);
curDateTime = curDateTime.Add(toNow);
var dayStartSec = serverTime - curDateTime.Hour * HOUR_SEC_COUNT - curDateTime.Minute * 60 - curDateTime.Second;
timer.NextExecuteTime = dayStartSec + DAY_SEC_COUNT + timer.TimeValue;
}
break;
case TimerEventType.TimeStampWeek:
{
//下一次处理时间
DateTime curDateTime = new DateTime(1970, 1, 1, 0, 0, 0);
long lTime = long.Parse(((ulong)serverTime).ToString() + "0000000");
TimeSpan toNow = new TimeSpan(lTime);
curDateTime = curDateTime.Add(toNow);
int dayOfWeek = (int)curDateTime.DayOfWeek;
if (dayOfWeek == 0)
{
dayOfWeek = 6;
}
else
{
dayOfWeek -= 1;
}
var weekStartSec = serverTime - dayOfWeek * DAY_SEC_COUNT - curDateTime.Hour * HOUR_SEC_COUNT - curDateTime.Minute * 60 - curDateTime.Second;
timer.NextExecuteTime = weekStartSec + WEEK_SEC_COUNT + timer.TimeValue;
}
break;
}
}
return true;
}
return false;
}
//增加计时器统一函数
private int AddTimerEvent(float time, float validTime, TimerEventType type, bool loop, object param, Action<int, double, object> callBack)
{
var timerInfo = new TimerEventInfo();
timerInfo.ID = ++_idCounter;
timerInfo.Type = type;
timerInfo.Loop = loop;
timerInfo.TimeValue = time;
timerInfo.ValidTimeValue = validTime;
timerInfo.Param = param;
timerInfo.AddedTime = GameCenter.HeartSystem.ServerTime + GameCenter.HeartSystem.ServerZoneOffset;
timerInfo.CallBack = callBack;
timerInfo.IsExecute = false;
_timeEventList.Add(timerInfo);
if (GameCenter.HeartSystem.IsReceiveHeart)
{
CalculateExecuteTime(timerInfo);
_timeEventList.Sort(TimerSort);
}
return timerInfo.ID;
}
//计算下一次执行的时间
private void CalculateExecuteTime(TimerEventInfo timerInfo)
{
var serverTime = GameCenter.HeartSystem.ServerTime + GameCenter.HeartSystem.ServerZoneOffset;
DateTime curDateTime = new DateTime(1970, 1, 1, 0, 0, 0);
long lTime = long.Parse(((ulong)serverTime).ToString() + "0000000");
TimeSpan toNow = new TimeSpan(lTime);
curDateTime = curDateTime.Add(toNow);
switch (timerInfo.Type)
{
case TimerEventType.CountDown:
timerInfo.NextExecuteTime = serverTime + timerInfo.TimeValue;
break;
case TimerEventType.TimeStampHour:
{
var hourStartTime = serverTime - curDateTime.Minute * 60 - curDateTime.Second;
timerInfo.NextExecuteTime = hourStartTime + timerInfo.TimeValue;
while ((timerInfo.NextExecuteTime + timerInfo.ValidTimeValue) < serverTime)
{
hourStartTime += HOUR_SEC_COUNT;
timerInfo.NextExecuteTime = hourStartTime + timerInfo.TimeValue;
}
}
break;
case TimerEventType.TimeStampDay:
{
var dayStartSec = serverTime - curDateTime.Hour * HOUR_SEC_COUNT - curDateTime.Minute * 60 - curDateTime.Second;
timerInfo.NextExecuteTime = dayStartSec + timerInfo.TimeValue;
while ((timerInfo.NextExecuteTime + timerInfo.ValidTimeValue) < serverTime)
{
dayStartSec += DAY_SEC_COUNT;
timerInfo.NextExecuteTime = dayStartSec + timerInfo.TimeValue;
}
}
break;
case TimerEventType.TimeStampWeek:
{
int dayOfWeek = (int)curDateTime.DayOfWeek;
if (dayOfWeek == 0)
{
dayOfWeek = 6;
}
else
{
dayOfWeek -= 1;
}
var weekStartSec = serverTime - dayOfWeek * DAY_SEC_COUNT - curDateTime.Hour * HOUR_SEC_COUNT - curDateTime.Minute * 60 - curDateTime.Second;
timerInfo.NextExecuteTime = weekStartSec + timerInfo.TimeValue;
while ((timerInfo.NextExecuteTime + timerInfo.ValidTimeValue) < serverTime)
{
//已超时,换到下一周
weekStartSec += WEEK_SEC_COUNT;
timerInfo.NextExecuteTime = weekStartSec + timerInfo.TimeValue;
}
}
break;
}
}
//排序函数
private int TimerSort(TimerEventInfo left, TimerEventInfo right)
{
//从后往前排
return right.NextExecuteTime.CompareTo(left.NextExecuteTime);
}
//获取跨天时间戳
private int GetCrossDayTime(int time)
{
var dataTime = new DateTime((long)time * 10000000L + 621356256000000000L);
return (int)((new DateTime(dataTime.Year, dataTime.Month, dataTime.Day).Ticks - 621356256000000000L) / 10000000L) + 86400;
}
#endregion
}
}