每日清除数据
目录
每天一次的一些数据和调用
例如:侠客宴会每天只能参加3次,一天结束后,次数需要重新置0。
那么会有这么一个问题,玩家如果下线了,那么当前场景tick不到玩家,玩家次数如何重新置0?
时钟设计
一些类的继承关系
1.场景需要时钟,看下AbstractBPScene类的继承关系:
public abstract class AbstractBPScene extends AbstractSceneService implements IntervalTimberable
场景基类实现了,间隔时间定时Tick的接口IntervalTimberable
/**
* 间隔时间定时Tick的接口
*/
public interface IntervalTimberable extends Timerable
IntervalTimberable 实现 Timerable接口,接口可以实现接口,并且接口可以实现多个接口。
/**
* 可以定时tick的
* Created by Wang Lizhi on 2017/8/29.
*/
public interface Timerable
{
}
2.我们再看下定时Tick时钟,类的继承关系:
/**
* Tick时钟基类
*/
public abstract class BaseTimerTicker<T extends Timerable>
{
/**
* 定时执行体接口
*/
private T timerable;
public BaseTimerTicker(T timerable)
{
this.timerable = timerable;
}
}
基类是一个泛型类,指定传入的泛型必须实现Timerable接口。
泛型类这么做有好处,成员属性T timerable,在构造方法传参时,T必须实现Timerable接口,否则编译不通过。我们可以利用这些性质,对传入的类做一些限制。
/**
* 间隔一定时间 Tick的时钟
*/
public class IntervalTimerTicker extends BaseTimerTicker<IntervalTimberable>
IntervalTimerTicker继承Tick时钟基类
/**
* 自然时间定时Tick的时钟
*/
public class NartualTimerTicker extends BaseTimerTicker<NaturalTimberable>
NartualTimerTicker继承Tick时钟基类
3.泛型类测试
/**
* 鸟会飞 -- 实现飞接口
* @author lizhibiao
* @date 2020/7/10 17:48
*/
public class Bird implements FlyInterface
{
}
/**
* 鸟会飞 -- 实现飞接口
* @author lizhibiao
* @date 2020/7/10 17:48
*/
public class Bird implements FlyInterface
{
}
/**
* 鸭子
* @author lizhibiao
* @date 2020/7/10 17:49
*/
public class Duck
{
}
/**
* 构造类类
* @author lizhibiao
* @date 2020/7/10 17:44
*/
public class TBuild<T extends FlyInterface>
{
private T obj;
public TBuild(T obj)
{
this.obj = obj;
}
}
/**
* 测试类
* @author lizhibiao
* @date 2020/7/10 17:50
*/
public class Test
{
public static void main(String[] args)
{
//鸟会飞,实现了FlyInterface,编译通过
Bird bird = new Bird();
TBuild<Bird> birdTBuild = new TBuild<>(bird);
//传入的对象duck不会飞,没有实现FlyInterface,所以编译不通过,直接报红色警告
Duck duck = new Duck();
TBuild<? extends FlyInterface> tBuild = new TBuild<>(duck);
}
}
鸟会飞,实现了FlyInterface,编译通过。
传入的对象duck不会飞,没有实现FlyInterface,所以编译不通过,直接报红。
场景初始化和激活重置时钟
AbstractBPScene下:
init()和activeCallback,调用重置场景时钟
/**
* 重置场景时钟
*/
private void resetTimeClock()
{
}
重置时钟resetTimeClock()方法
/**
* 重置场景时钟
*/
private void resetTimeClock()
{
long nowMillis = System.currentTimeMillis();
//场景主时钟
if (this.sysClock == null)
{
sysClock = new TimeClock();
}
sysClock.init(nowMillis);
//场景定时时钟
if (this.timerClock == null)
{
//this是AbstractBPScene,AbstractBPScene实现了IntervalTimberable接口
timerClock = new TimerTickerHolder(this, null);
}
timerClock.init(nowMillis);
}
场景主时钟:存的都是一些当前的格林治时间,定时时钟需要拿当前时间和之前的时间作比较。
场景定时时钟:例如每秒进行一次tick,没分钟进行一次tick,需要从场景主时钟拿当前时间
和缓存的时间作比较。
this是AbstractBPScene,AbstractBPScene实现了IntervalTimberable接口
1.场景主时钟初始化主要操作
private void updateTime(long nowMillis)
{
//DEFAULT_RAW_OFFSET = TimeZone.getDefault().getRawOffset(); // 默认时 区时间偏移
//当前时间戳会加上默认时区的偏移
long rawOffset = nowMillis + TimeUtils.DEFAULT_RAW_OFFSET;
//格林威治时间,距离现在的总天数,总小时,总分钟数
curMils = nowMillis;
curDay = rawOffset / TimeUtils.MILLIS_PER_DAY;
curHour = rawOffset / TimeUtils.MILLIS_PER_HOUR;
curMinute = rawOffset / TimeUtils.MILLIS_PER_MINUTE;
curSecond = rawOffset / TimeUtils.MILLIS_PER_SECOND;
}
当前时间戳会加上默认时区的偏移,距离现在的总天数,总小时,总分钟数。
2.场景定时钟初始化主要操作
/**
* 每毫秒tick上一次被Tick的时间戳
*/
protected long lastMills;
/**
* 每分钟tick上一次被Tick的时间戳
*/
protected long lastMinute;
/**
* 每小时tick上一次被Tick的时间戳
*/
protected long lastHour;
初始化缓存上一次tick时间戳,就是初始化传入的时间戳。
3.world上时钟初始化
WorldMessageHandler.java:
public void handleCommonDataLoadResponse(BPWorldService service, BPLoadCommonDataRsp message)
{
...
service.copyFrom(commonDataCache);
...
}
收到load服务器公共数据回复消息
BPWorldService.java:
/**
* 从db读出来的数据赋值到world上的对应模块里
* @param dataCache
*/
public void copyFrom(CommonDataCache dataCache)
{
serverInfoModule.copyFrom(dataCache.getServerBaseInfoModel());
}
调用serverInfoModule.copyFrom()
public void copyFrom(ServerBaseInfoModel model)
{
TimeClock sysClock = new TimeClock();
sysClock.init(getNowTickTime());
TimerTickerHolder timerClock = new TimerTickerHolder(worldService, worldService);
timerClock.init(timestamp);
}
其实每次都是重新new对象,重新初始化,并不是去数据库加载的,只是放在了加载数据库方法里。
4.每个生物对象挂的时钟
/**
* 初始化时调用
*/
public void init()
{
// init clock
tickTimer = new TimerTickerHolder(this,this);
long nowMillis = MainClock.getInstance().currentTimeMillis();
tickTimer.init(nowMillis);
}
生物对象基类BPObject.java下,初始化时钟。
public Actor createActor(BPLoginObject loginObject, String accountID, long actorID,
ActorDataCache actorDataCache, int birthX, int birthY, int birthZ,
int rotation, int defaultKnightID, int fashionDressID)
{
...
actor.init();
...
创角的时候会初始化一次。
@MessageHandler(MessageIDConst.LOAD_ACTOR_DATA_RESPONSE)
public void handleLoadActorDataResponse(AbstractBPLoginService loginService, BPLoadActorDataRsp message)
{
...
//非常注意这里是新new了一个actor对象
Actor actor = new Actor();
loginObject.setActor(actor);
actor.setCoreEntity(loginObject.getCoreEntity());
actor.init();
...
}
登录的时候也会调用一次init()
5.场景下非自然日的更新机制
AbstractBPScene下:
protected void tickCallback(int interval)
{
// 场景主时钟
sysClock.tick(nowTickTime);
// 场景定时时钟
timerClock.tick(sysClock);
}
定时时钟会去从主时钟拿当前时间,所以主时钟顺序在前。
场景主时钟更新:
private void updateTime(long nowMillis)
{
//DEFAULT_RAW_OFFSET = TimeZone.getDefault().getRawOffset(); // 默认时区时间偏移
//当前时间戳会加上默认时区的偏移
long rawOffset = nowMillis + TimeUtils.DEFAULT_RAW_OFFSET;
//格林威治时间,距离现在的总天数,总小时,总分钟数
curMils = nowMillis;
curDay = rawOffset / TimeUtils.MILLIS_PER_DAY;
curHour = rawOffset / TimeUtils.MILLIS_PER_HOUR;
curMinute = rawOffset / TimeUtils.MILLIS_PER_MINUTE;
curSecond = rawOffset / TimeUtils.MILLIS_PER_SECOND;
}
没啥看的,传入当前时间戳,每次重新更新时间。
场景定时时钟更新:
@Override
public void tickImpl(TimeClock timeClock)
{
long clockMillis = timeClock.getCurMils();
// 秒
if (clockMillis - lastMills < TimeUtils.MILLIS_PER_SECOND)
{
return;
}
IntervalTimberable timerable = getTimerable();
lastMills = clockMillis;
timerable.secondIntervalTick();
// 分
if (clockMillis - lastMinute < TimeUtils.MILLIS_PER_MINUTE)
{
return;
}
lastMinute = clockMillis;
timerable.minuteIntervalTick();
// 小时
if (clockMillis - lastHour < TimeUtils.MILLIS_PER_HOUR)
{
return;
}
lastHour = clockMillis;
timerable.hourIntervalTick();
}
需要拿当前时间和上一次时间做比较,传入主时钟timeClock。
接下来是满足秒、分、小时,那么就调用对应的秒tick、分tick、小时tick。
同理,其它生物对象、世界的时钟更新类似。
自然日时钟,更新机制类似。
dailyTick玩家不在线,跨天处理机制
项目目前每天00:00调用一次dailyTick,玩家如果一直在线,那自然会调用没有什么问题。
但是,玩家如果下线了,那么当前场景tick不到玩家,玩家次数如何重新置0?
/**
* 自然日Tick : 每天的00:00调用一次;如果跨天0点时玩家不在线,那么玩家登录立即调用
*/
void dailyNaturalTick();
如果跨天0点时玩家不在线,那么玩家登录立即调用,如何做到?
1.玩家afterLoad传入上一次在线时间
@Override public void afterLoad() { … // 初始化时钟,用玩家上次下线时间来初始化时钟,可以保证时钟不间断的tick long lastOffLineTime = getActorCommonModule().getLastOffLineTime(); long nowMillis = getNowTickTime(); if(lastOffLineTime <= 0 || lastOffLineTime > nowMillis) { lastOffLineTime = nowMillis; getActorCommonModule().setLastOffLineTime(lastOffLineTime); getActorCommonModule().setModify(true); } //传入上一次在线时间 this.getTickTimer().init(lastOffLineTime); … }
玩家afterLoad传入上一次在线时间
2.对象上的时钟更新
AbstractBPScene下:
tickObject()
{
// 对象的时钟更新,并且调用时间间隔的tick
// 传入场景主时钟
object.tickUpdateClcok(sysClock);
}
场景基类下,tick对象时进行对象时钟更新,所以判定出跨天了,执行一次dailyTick。
到此,我们搞明白原理。
查看方法和类的调用栈,快捷键
crtl + alt + h