Files
Main/Assets/Code/Logic/_Required/TimerEvent/TimerEventSystem.cs
2025-01-25 04:38:09 +08:00

363 lines
16 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}