Files
MinFt/Client/Assets/Scripts/Services/HeartBeat.cs
Liubing\LB 3d8d4d18f3 first commit
先修复一下,错误的场景

删除不必要的钓场资产

修复into场景

update:更新meta文件,修复报错

update:修复资源

修复第一章节建造

修复建造第三章

update:删除多余内容

update:更新README.md

update:还原图标

update:更新README

update:更新配置
2026-04-28 02:17:22 +08:00

227 lines
5.1 KiB
C#

using System;
using System.Threading;
using System.Threading.Tasks;
using asap.core;
using UniRx;
using UnityEngine;
public class HeartBeat
{
[Inject]
public IStageService stageService {get; set;}
[Inject]
public IConfig config {get; set;}
private CompositeDisposable disposables;
private CancellationTokenSource tokenSource;
private bool initialized;
private bool isPaused;
private bool hasPauseSnapshot;
private DateTime pauseTimeUtc;
private TimeSpan remainingDelayWhenPaused;
private DateTime nextHeartbeatTimeUtc;
public void Init()
{
if (initialized)
{
return;
}
initialized = true;
isPaused = true;
hasPauseSnapshot = false;
remainingDelayWhenPaused = TimeSpan.Zero;
disposables = new CompositeDisposable();
var stageLoadDisposable = GContext.OnEvent<StageLoadEvent>()
.Where(e => e.state == EStageLoadState.Loaded && e.stageId == "FishingStage")
.Take(1)
.Subscribe(OnFishingStageLoad);
disposables.Add(stageLoadDisposable);
var pauseDisposable = Observable.EveryApplicationPause()
.Subscribe(OnApplicationPauseChanged);
disposables.Add(pauseDisposable);
}
public void Dispose()
{
if (!initialized)
{
return;
}
initialized = false;
isPaused = true;
hasPauseSnapshot = false;
remainingDelayWhenPaused = TimeSpan.Zero;
disposables.Dispose();
disposables = null;
StopHeartBeatLoop();
}
private void OnApplicationPauseChanged(bool paused)
{
if (paused)
{
PauseHeartBeat();
}
else
{
ResumeHeartBeat();
}
}
private void OnFishingStageLoad(StageLoadEvent @event)
{
if (tokenSource != null || stageService.GetStage("FishingStage") == null)
{
return;
}
StartHeartBeatLoop(GetInterval());
}
private void PauseHeartBeat()
{
if (isPaused)
{
return;
}
isPaused = true;
pauseTimeUtc = DateTime.UtcNow;
hasPauseSnapshot = true;
if (nextHeartbeatTimeUtc > pauseTimeUtc)
{
remainingDelayWhenPaused = nextHeartbeatTimeUtc - pauseTimeUtc;
}
else
{
remainingDelayWhenPaused = TimeSpan.Zero;
}
StopHeartBeatLoop();
}
private void ResumeHeartBeat()
{
TimeSpan delay;
bool sendImmediately = false;
if (!isPaused)
{
return;
}
var interval = GetInterval();
if (!hasPauseSnapshot)
{
delay = interval;
}
else
{
var bgElapsed = DateTime.UtcNow - pauseTimeUtc;
var remainingDelay = remainingDelayWhenPaused;
if (remainingDelay <= TimeSpan.Zero)
{
remainingDelay = interval;
}
if (bgElapsed >= remainingDelay)
{
sendImmediately = true;
delay = interval;
}
else
{
delay = remainingDelay - bgElapsed;
}
}
if (stageService.GetStage("FishingStage") == null)
{
return;
}
if (sendImmediately)
{
SendHeartBeat();
}
StartHeartBeatLoop(delay);
}
private void StartHeartBeatLoop(TimeSpan firstDelay)
{
if (tokenSource != null)
{
return;
}
tokenSource = new CancellationTokenSource();
isPaused = false;
nextHeartbeatTimeUtc = DateTime.UtcNow + firstDelay;
StartHeartBeat(tokenSource.Token, firstDelay);
}
private void StopHeartBeatLoop()
{
if (tokenSource != null)
{
tokenSource.Cancel();
tokenSource.Dispose();
tokenSource = null;
}
}
private async void StartHeartBeat(CancellationToken token, TimeSpan firstDelay)
{
try
{
var delay = firstDelay;
while (!token.IsCancellationRequested)
{
if (delay > TimeSpan.Zero)
{
await Task.Delay(delay, token);
}
if (token.IsCancellationRequested || isPaused)
{
return;
}
SendHeartBeat();
delay = GetInterval();
}
}
catch (OperationCanceledException)
{
}
catch (Exception)
{
}
}
private void SendHeartBeat()
{
using (var e = GEvent.TackEvent("HeartBeat", gaSend: false))
{
e.AddContent("FPS", game.RootCtx.fps);
}
nextHeartbeatTimeUtc = DateTime.UtcNow + GetInterval();
}
private TimeSpan GetInterval()
{
var intervalSeconds = Mathf.Max(1, (int)config.GetFloat("HeartBeat", 300));
return TimeSpan.FromSeconds(intervalSeconds);
}
}