如何在玩家心跳机制中每日仅触发一次5点整事件?
解决方案:添加布尔字段标记是可靠的实现方式
应该在Reset类中添加resetAt5布尔字段来标记当日5点事件是否已触发,这是确保EventOnDailyAtHour5每日仅执行一次的合理方案,具体原因和修改如下:
现有代码的逻辑漏洞
当前代码仅在跨日重置分支中判断是否触发5点事件,存在两种触发失效或重复的场景:
- 玩家在凌晨5点前上线:跨日重置时因当前时间未到5点,不会触发事件;等时间到5点后,每分钟的心跳会持续执行,但没有任何标记控制,会导致事件被重复触发。
- 玩家当日5点后多次上下线:虽然跨日逻辑不会重复执行,但如果没有持久化的标记,可能在边缘场景下误触发二次事件。
修改步骤
1. 扩展Reset类,添加触发标记
在Reset类中新增resetAt5字段,默认值设为false,并添加对应的序列化标签保证持久化:
@Data public class Reset { @Tag(1) private long resetTime; @Tag(2) private boolean resetAt5 = false; // 标记当日5点事件是否已触发 }
2. 更新心跳逻辑,完善触发控制
修改playerHeart方法,在跨日时重置标记,在日常心跳中判断触发条件并控制重复:
@Override public void playerHeart(Player player, long time) { Reset reset = player.getBase().getReset(); long fiveOClock = TimeUtil.getClockTime(time, 3600 * 5); if (!TimeUtil.isSameDay(reset.getResetTime(), time)) { // 触发每日重置事件 EventManager.fire(EventOnPlayerDailyReset.class, e -> e.onPlayerDailyReset(player)); // 新的一天重置5点事件标记 reset.setResetAt5(false); // 如果跨日时已经过5点,直接触发事件并标记 if (time >= fiveOClock) { EventManager.fire(EventOnDailyAtHour5.class, e -> e.onDailyAtHour5(player)); reset.setResetAt5(true); } reset.setResetTime(time); player.saveBase(); } else { // 日常心跳中:判断是否在5点前后1分钟内,且未触发过事件 if (time >= fiveOClock && time <= fiveOClock + 60 * 1000 && !reset.isResetAt5()) { EventManager.fire(EventOnDailyAtHour5.class, e -> e.onDailyAtHour5(player)); reset.setResetAt5(true); player.saveBase(); } } EventManager.fire(EventOnPlayerHeart.class, e -> e.onPlayerHeart(player, time)); }
关键细节说明
- 误差范围:添加
±1分钟的判断范围,避免因心跳触发时机(每分钟一次)刚好错过5点整,导致事件漏触发。 - 持久化标记:触发事件后必须调用
player.saveBase(),确保标记被持久化,防止玩家下线再上线后重复触发。 - 跨日重置标记:每次跨日时将
resetAt5设为false,是新一天能正常触发事件的核心保证。
内容的提问来源于stack exchange,提问作者Criwran
相关产品推荐
相关产品推荐

