前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >事件系统的设计方法

事件系统的设计方法

原创
作者头像
深雾
修改2020-07-06 10:28:52
7900
修改2020-07-06 10:28:52
举报
文章被收录于专栏:工具类

前言

事件功能用过段时间,像用户登入登出,充值,完成任务等非常的方便,有时间看看源码学习,设计的时候比较复杂,但完成后使用很简单,选了创建角色事件走遍流程,下面是JAVA代码

事件实体GameEvent

代码语言:java
复制
public abstract class GameEvent {
    /** 派发类型 */
    private int dispatchType;
    /** 事件的key {@code EventKey} */
    public String name;
    /**
     * @param name        事件名称
     * @param actorId    当前角色
     */
    public GameEvent(String name) {
        this.name = name;
    }
    public GameEvent(String name, int dispatchType) {
        this.name = name;
        this.dispatchType = dispatchType;
    }

角色事件

代码语言:java
复制
public abstract class ActorEvent extends GameEvent {
    /** * 角色id */
    private long actorId;
    public ActorEvent(String name, long actorId) {
        super(name, DispatchType.ACTOR);
        this.actorId = actorId;
    }
    @Override
    public long getUniqueId() {
        return actorId;
    }
    public long getActorId() {
        return actorId;
    }
}

创建角色事件

代码语言:java
复制
/*** 创建角色事件*/
public class ActorCreateEvent extends ActorEvent {
    public ActorCreateEvent(long actorId) {
        super(EventKey.ACTOR_CREATE_EVENT, actorId);
    }
}

EventKey 事件类型

代码语言:java
复制
public interface EventKey {
    /** System 系统底层事件:用于在线角色定时触发的事件 @EventOnline */
    public final static String ONLINE_EVENT = "ONLINE_EVENT_%s_%s";
    /** User角色登陆事件*/
    public final static String ACTOR_LOGIN = "ACTOR_LOGIN";
    /** 创建角色事件*/
    public final static String ACTOR_CREATE_EVENT = "ACTOR_CREATE_EVENT";

消息派发类型线程池

代码语言:shell
复制
/*** 消息派发类型线程池*/
public interface DispatchType {
    /** 运维专用线程 */
    public static int MAINTAIN = 1;
    /** 角色线程池(大部份模块用该线程),角色之间的数据修改需要通过事件方式 */
    public static int ACTOR = 2;
    /** 用户登陆  */
    public static int USER_LOGIN = 3;
    /** 帐号相关的线程 */
    public static int SETTING = 4;
    /** 节点相关的线程 */
    public static int NODE = 5;
    /** 
     * 注意!!!!!!!!!!!
     * 以下为每个进程的线程配置。线程数为1的,一定要谨慎修改!!切记!切记!
     */
    public static List<ThreadInfo> getGameThreadInfo() {
        List<ThreadInfo> list = new ArrayList<>();
        list.add(ThreadInfo.valueOf("maintain", MAINTAIN, 1));
        list.add(ThreadInfo.valueOf("actor", ACTOR, Runtime.getRuntime().availableProcessors() * 2 + 1));
        list.add(ThreadInfo.valueOf("setting", SETTING, 1));
        list.add(ThreadInfo.valueOf("user_login", USER_LOGIN, 1));
        list.add(ThreadInfo.valueOf("node", NODE, 1));
        return list;
    }
}

事件派发管理

创建角色方法

创建的ActorCreateEvent继承GameEvent类,dispatchType = ACTOR,DispatchHelper派发事件

代码语言:java
复制
    @Override
    public TResult<Long> createActor(long uid, int platformId, int channelId, int serverId, String actorName, boolean sex, int avatarId,
            String createIP) {
        Result checkResult = this.checkActorName(actorName);
        if (checkResult.isFail()) {
            return TResult.valueOf(checkResult);
        }
        if (actorDao.getActorId(uid, serverId) > 0) {
            return TResult.valueOf(CREATE_ACTOR_FAIL);
        }
        this.checkActorName(actorName);
        int level = globalConfigService.findGlobalConfig(GlobalConfigKey.ACTOR_INIT_LEVEL).findInt();
        int initSaveDiamond = globalConfigService.findGlobalConfig(GlobalConfigKey.SAVE_DIAMOND_INIT).findInt();
        Actor actor = actorDao.createActor(uid, platformId, channelId, serverId, actorName, sex, avatarId, level, initSaveDiamond);
        int passStoryRewardTime = globalConfigService.findGlobalConfig(GlobalConfigKey.PASS_STORY_TIME).findInt();
        long expireTime = actor.getCreateTime() + passStoryRewardTime * TimeUtils.ONE_HOUR_MILLISECOND;
        actor.setPassStoryRewardExpireTime(expireTime);
        dbQueue.updateQueue(actor);
        List<RewardObject> rewardList = globalConfigService.findGlobalObject(GlobalConfigKey.ACTOR_INIT_REWARD, RewardObjectListConfig.class).getVs();
        RewardHelper.sendRewardList(actor.getActorId(), rewardList, OperationType.ACTOR_CREATE);
        VipConfig vipConfig = VipConfigService.getVipConfig(actor.getVipLevel());
        prerogativeFacade.addPrerogative(actor.getActorId(), vipConfig.getPrerogativeIdList(), OperationType.ACTOR_CREATE);
        DispatchHelper.postEvent(new ActorCreateEvent(actor.getActorId()));
        if (!ActorHelper.isRobot(actor.getActorId())) {
            GameOssLogger.newUser(uid, platformId, channelId, serverId, actor.getActorId());
            GameOssLogger.actorInfo(actor, createIP);
        }
        return TResult.sucess(actor.getActorId());
    }

触发事件方法

触发@Event(name = EventKey.ACTOR_CREATE_EVENT)的创角事件方法

代码语言:java
复制
@Event(name = EventKey.ACTOR_CREATE_EVENT)
    public void onActorCreateEvent(ActorEvent event) {
        long actorId = event.getActorId();
        TResult<Actor> result = this.getActor(actorId);
        if (result.isFail()) {
            return;
        }
        Actor actor = result.item;
        if (actor.getPlatformId() == PlatformId.WAN_BA || actor.getPlatformId() == PlatformId.WAN_BA_IOS) {
            ActorHelper.reportRegchar(actor.getUid());
        }
    }

还有活动多次事件触发等

代码语言:java
复制
@Override
    public void onEvent(GameEvent e) {
        List<ActivityOpenConfig> activityOpenConfigList = ActivityOpenConfigService.getActivityOpenConfigList(getType());
        switch (e.name) {
        case EventKey.GOD_EQUIPMENT_EVENT:
            // 神装召唤事件
            GodEquipmentEvent godEquipmentEvent = e.convert();
        case EventKey.GACHA_ACTIVITY_EVENT:
            // 抽奖事件

线程调度DispatchHelpe

调度EventContext

代码语言:java
复制
@Component
public class DispatchHelper {
    @Autowired
    private DispatchContext dispatchContext;
    @Autowired
    private EventContext eventContext;
    @Autowired
    private RouterContext routerContext;
    private static ObjectReference<DispatchHelper> ref = new ObjectReference<DispatchHelper>();
    @PostConstruct
    protected void init() {
        ref.set(this);
    }
    public static void postEvent(GameEvent e) {
        ref.get().eventContext.post(e);
    }
}

事件派发环境

dispatchContext排序事件类型从小到大执行

代码语言:java
复制
public class EventContext {
    @Autowired
    DispatchContext dispatchContext;
    private static Logger LOGGER = LoggerFactory.getLogger(EventContext.class);
    /** OnEventListener接口事件注册列表 */
    private Map<String, List<OnEventListener>> onEventMaps = new HashMap<>();
    /** Event注解事件注册列表  */
    private Map<String, List<ASMMethod>> asmEventMaps = new HashMap<>();
    /** 事件队列 */
    private static ConcurrentLinkedQueue<GameEvent> EVENT_QUEUE = new ConcurrentLinkedQueue<>();
    /** 事件队列线程执行器 */
    private NamedScheduleExecutor eventExecutor;

    @PostConstruct
    public void initEventQueue() {
        eventExecutor = new NamedScheduleExecutor(1, "eventQueueThread");
        eventExecutor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    for (;;) {
                        GameEvent e = EVENT_QUEUE.poll();
                        if (e == null) {
                            return;
                        }
                        EventDispatch dispatch = createEventDispatch(e);
                        dispatchContext.post(dispatch);
                    }
                } catch (Exception e) {
                    LOGGER.error("scene queue thread error:{}", e);
                }
            }
        }, 10, 10, TimeUnit.MILLISECONDS);
    }

    /**
     * 创建事件派发包
     * @param event
     * @return
     */
    private EventDispatch createEventDispatch(GameEvent event) {
        List<OnEventListener> eventListenerList = this.onEventMaps.get(event.getName());
        List<ASMMethod> asmEventList = this.asmEventMaps.get(event.getName());
        EventDispatch dispatch = new EventDispatch(event, eventListenerList, asmEventList);
        return dispatch;
    }

    public boolean register(Object obj, Class<?> clazz, Method method, String... eventNames) {
        int eventOrderId = 0;
        EventOrder eventOrder = clazz.getAnnotation(EventOrder.class);
        if (eventOrder != null) {
            eventOrderId = eventOrder.orderId();
        }
        MethodAccess methodAccess = MethodAccess.get(clazz);
        ASMMethod asmMethod = ASMMethod.valueOf(method, methodAccess, obj, eventOrderId);
        boolean register = false;
        for (String eventName : eventNames) {
            if (eventName == null || eventName.equals("")) {
                LOGGER.error("event name is null! method={}", method.getName());
                continue;
            }
            List<ASMMethod> valueList = asmEventMaps.get(eventName);
            if (valueList == null) {
                valueList = new ArrayList<>();
                asmEventMaps.put(eventName, valueList);
            }
            valueList.add(asmMethod);
            Collections.sort(valueList);
            register = true;
        }

        return register;
    }

    public boolean register(Object obj, Class<?> clazz, Method method) {
        method.setAccessible(true);
        // 普通的事件注解
        Event event = method.getAnnotation(Event.class);
        if (event != null) {
            if (event.name() == null) {
                LOGGER.error("event name is null! method={}", method.getName());
                return false;
            }
            return register(obj, clazz, method, event.name());
        }
        return false;
    }

    public void registeEventListener(OnEventListener listener) {
        Set<String> eventSet = new HashSet<>();
        listener.registerEvent(eventSet);
        if (eventSet.isEmpty()) {
            return;
        }
        for (String name : eventSet) {
            if (name == null || name.equals("")) {
                continue;
            }
            List<OnEventListener> valueList = onEventMaps.get(name);
            if (valueList == null) {
                valueList = new ArrayList<>();
                onEventMaps.put(name, valueList);
            }
            valueList.add(listener);
            Collections.sort(valueList);
        }
    }
    public void post(GameEvent event) {
        if (event != null) {
            // 事件先进队列,以防ringbuffer满环的时候被自己的消费者线程投递消息,造成死环.
            EVENT_QUEUE.add(event);
        }
    }
    public static boolean isEmpty() {
        return EVENT_QUEUE.isEmpty();
    }
    public static int getQueueSize() {
        return EVENT_QUEUE.size();
    }
}

事件监听器

添加和接收事件

代码语言:java
复制
public interface OnEventListener extends Comparable<OnEventListener> {
    @Override
    default int compareTo(OnEventListener o) {
        int orderId = 0;
        EventOrder eventOrder = this.getClass().getAnnotation(EventOrder.class);
        if (eventOrder != null) {
            orderId = eventOrder.orderId();
        }
        int targetOrderId = 0;
        EventOrder targetEventOrder = o.getClass().getAnnotation(EventOrder.class);
        if (targetEventOrder != null) {
            targetOrderId = targetEventOrder.orderId();
        }
        if (orderId > targetOrderId) {
            return 1;
        } else if (orderId < targetOrderId) {
            return -1;
        } else {
            return this.getClass().getName().compareTo(o.getClass().getName());
        }
    }
    /**
     * 事件名集合
     * @param eventSet    添加事件名 {@code EventKey}
     */
    public void registerEvent(Set<String> eventSet);
    /**
     * 接收事件的方法
     * @param event
     */
    public void onEvent(GameEvent event);
}

ASM字节码方法

代码语言:java
复制
public class ASMMethod implements Comparable<ASMMethod> {
    /**
     * ASM方法访问对象
     */
    private MethodAccess access;
    /**
     * 方法索引
     */
    private int index;
    /**
     * 方法所属对象实体
     */
    private Object instance;
    /**
     * 执行顺序ID
     */
    private int orderId;
    /**
     * valueOf.
     * @param method        方法对象
     * @param methodAccess    asm的方法访问对象
     * @param instance        当前方法所属类的实例
     * @return
     */
    public static ASMMethod valueOf(Method method, MethodAccess methodAccess, Object instance) {
        ASMMethod asmMethod = new ASMMethod();
        asmMethod.access = methodAccess;
        asmMethod.index = asmMethod.access.getIndex(method.getName(), method.getParameterTypes());
        asmMethod.instance = instance;
        return asmMethod;
    }
    public static ASMMethod valueOf(Method method, MethodAccess methodAccess, Object instance, int orderId) {
        ASMMethod asmMethod = new ASMMethod();
        asmMethod.access = methodAccess;
        asmMethod.index = asmMethod.access.getIndex(method.getName(), method.getParameterTypes());
        asmMethod.instance = instance;
        asmMethod.orderId = orderId;
        return asmMethod;
    }
    public Object invoke(Object... args) {
        return access.invoke(instance, index, args);
    }
    public int getOrderId() {
        return orderId;
    }
    @Override
    public String toString() {
        return instance.getClass().getName();
    }
    @Override
    public int compareTo(ASMMethod o) {
        if (this.orderId > o.orderId) {
            return 1;
        } else if (this.orderId < o.orderId) {
            return -1;
        } else {
            return this.instance.getClass().getName().compareTo(o.instance.getClass().getName());
        }
    }
}

事件执行线程

线程命名的执行器封装类

代码语言:java
复制
public class NamedScheduleExecutor {
    private ScheduledExecutorService executor;
    public NamedScheduleExecutor(int poolSize, final String name) {
        executor = Executors.newScheduledThreadPool(poolSize, new NamedThreadFactory(name));
    }
    public ScheduledExecutorService getExecutor() {
        return executor;
    }
    public Future<?> submit(Runnable run) {
        return executor.submit(run);
    }
    public ScheduledFuture<?> schedule(Runnable run, int delay, TimeUnit timeUnit) {
        return executor.schedule(run, delay, timeUnit);
    }
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable run, int initialDelay, int period, TimeUnit unit) {
        return executor.scheduleAtFixedRate(run, initialDelay, period, unit);
    }
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable run, int initialDelay, int delay, TimeUnit unit) {
        return executor.scheduleWithFixedDelay(run, initialDelay, delay, unit);
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 事件实体GameEvent
    • 消息派发类型线程池
    • 事件派发管理
      • 创建角色方法
        • 触发事件方法
          • 线程调度DispatchHelpe
          • 事件派发环境
            • 事件监听器
              • ASM字节码方法
              • 事件执行线程
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档