新的事件处理机制

功能

监视生物的行为状态(例如:生物开始移动(onCreatureStartMove),生物死亡(ICreatureDead),在不同的状态下产生相应的事件并通知感兴趣的对象做出相应的处理。

使用

1. 在/object/observer下定义事件接口(如果没有合适的事件)

public interface ICreatureXxx {
    // 这个事件机制基于apache.commons.lang3.evnet.EventListenerSupport,EventListenerSupport基于接口的动态代理实现,因此一个接口可以有多个方法
    void onCreatureXxx(...);
}

2. 事件触发

abstractCreature.getObserveController().fire(ICreatureUseXxx.class).onCreatureXxx(...);

3. 在对事件感兴趣的类上实现相应的接口

public class Xxx implements ICreatureXxx {
    @Override
    void onCreatureXxx(...) {

    }
}

4.把观察者添加到对应abstractCreature上

// xxx:观察者对象
absrtactCreature.getObserveController().attach(ICreatureXxx,xxx)

##不使用EventBus事件系统的原因

  1. EventBus是全局性质的事件机制,这里的事件机制是全生物状态的观察监听机制,也就是说,如果使用EventBus应当用submit(或者同步的synSubmit)发布事件时,所有的@ReceiverAnno注解的方法都会被触发,但是这里的事件机制只有attach到对应相应生物上的接口实现类的方法被触发。
  2. EventBus不具备移除观测者的功能。这里可以使用abstractCreature.getObserveController().removeObserver(ICreatureXxx,Xxx)移除迷人abstractCreature身上的特定观察者。

排名系统学习

排名榜用于游戏中玩家间的排名和对比,从战力、等级、装备评分、公会、副本通关层数等很多其他模块的数据可以来用进行排行。为了减少代码重复和工作量,所以一个通用排行系统是必须。

排行的抽象

Rank是对所有排名名词的抽象,每个进行排名的角色都拥有Rank

public class Rank {
    // 注意,第一名为零
    public static final NO1 = 0;
    public static final Rank NOT_IN_RANK = new Rank(false, -1);
    // 是否上榜
    private final boolean inRank;

    private final int rank;

    private Rank(boolean inRank, int rank) {
        this.inRank = inRank;
        this.rank = rank;
    }

    public Rank(int rank) {
        this(true, rank);
    }

    // 在榜上玩家处理
    public void ifInRank(Consumer<Integer> consumer) {
        if(this.isInRank()) {
            consumer.accept(rank);
        }
    }

    // 判断排名是否在排行榜上
    public boolean isInRank() {
        return inRank;
    }

    // 获取排名值
    public int getRank() {
        return rank;
    }
}

单个排行榜的信息,多个Rank可以组成RankList,而RankList包含各种类型的排名,并且会持久化的

@Entity
@Cached(type=CacheType.MANUAL)
@InitailConfig(type=InitialType.ALL)
public class RankList extends AbstractEntity<RankPk> {
    // 排名前n名变化时,才通知,包括调出前3名的
    private transient static final int NOTIFY_RANK = 3;
    @Id
    private RankPk key;
    @Lob
    private String rankJson;
    private transient List<RankEntity> rankList;
    private transient Map<Long, RankEntity> rankMap;

    private transient ReentrantWriteLock lock = new ReentrantReadWriteLock();

    // 跨服结算
    private long lastSettleTime;
    // 跨服同步缓存
    private transient AtomicBoolean sync = new AtomiBoolean(false);

    // 记录有改变的排名实体,用于跨服增量同步
    private transient RankUpdateSet changeSet;
    // true为内存排行,不用入库
    private transient boolean memory;

    public static RankList valueOf(RankPk rankPk, boolean memory) {
        RankList vo = new RankList();
        vo.kwy = rankPk;
        //+1是为了排行榜满时,新增上榜数据,在逻辑运算时不用扩容
        vo.rankList = new ArrayList<>(rankPk.getRankType().getMaxRankSize()+1);
        vo.rankMap. = new ConcurrentHashMap<>(rankPk.getRankType().getMAxRankSize()+1);
        if(rankPk.getRankType().isCross()) {
            vo.changeSet = new RankUpdateSet(vo.key);
        }
        vo.memory = memory;
        return vo;
    }

    public static RankList valueOf(RankPk rankPk) {
        return valueOf(rankPk, false);
    }

    public void directInsertRank(RankEntity rankEntity) {
        if(rankMap.put(rankEntity.getUuid(), rankEntity) == null) {
            this.rankList.add(rankEntity.getRank(), rankEntiy);
        }
    }

    public RankEntity removeFromRank(int rank) {
        RankEntity remove = this.rankList.remove(rank);
        this.rankMap.remove(remove.getUuid());
        return remove;
    }

    private void updateRecord(RankEntity rankEntity) {
        if(getId().getRankType().isCross() && ServerType.isCenterServer()) {
            this.changeSet.updateRank(rankEntity.getUuid());
        }
    }

    @Ovveride
    public RankPk getId() {
        return key;
    }    

    @Override
    protected void serialize() {
        rankList.forEach(RankEntity::serialize);
        rankJson = rankJson.object2String(rankList);
    }

    @Override

    public void unSerialize() {

    }

    public boolean isNo1(long uuid) {
        RankEntity rankEntity = rankMap.get(uuid);
        if(rankEntity == null) {
            return false;
        }
        return rankEntity.isNo1();
    }

    public Rank getRank(long uuid) {
        RankList rankEntity = rankMap.get(uuid);
        if(rankEntity == null) {
            return Rank.NOT_IN_RANK;
        } else {
            return new Rank(rankEntity.getRank());
        }
    }

    public RankEntity getRankEntity(long uuid) {
        return rankMap.get(uuid);
    }

    // 清空排行榜
    public void clear() {
        lock.writeLock.lock();
        try {
            rankMap.clear();
            rankList.clear();
        } finally {
            lock.writeLock().unlock();
        }
        RankManager.self().update(this);
    }

    // 清空排行榜,但不更新
    public void clear() {
        lock.writeLock.lock();
        try {
            rankMap.clear();
            rankList.clear();
        } finally {
            lock.writeLock().unlock();
        }
    }

    // 添加数据,不做排序处理
    public void addRankEntity(RankEntity rankEntyry) {
        if(rankMap.put(rankEntity.getUuid(), rankEntity) == null) {
            rankList.add(rankEntity);
        }        
    }

    // 清除过多的数据
    public void removevOverfull(Predicate<RankEntity> predicate) {
        // 可移除玩家数 = 榜单玩家数量-榜单可显示最大值
        int count = rankList.size() - key.getRankType().getLimit();
        if(count <= 0) {
            return;
        }
        lock.writeLock().lock();
        try {
            for(int i=0; i < rankList.size() && c<count; i++) {
                rankEntity = rankList.get(i);
                if(predicae.test(rankEntity)) {
                    removeWithIndex(i);
                    c++;
                }
            }
            // 重新设置排名
            for(int i=0; i < rankList.size(); i++) {
                setNewRank(rankList.get(i),i);
            }
            RankManager.self().update(this);
        } finally {
            lock.writeLock().unlock();
        }
    }

    private void setNewRank(RankEntity rankEntity, int newRank) {
        int oldRank = rankEntity.getRank();
        if(newRank == oldRank) {
            return;
        }
        rankEntity.getRank(newRank);
        rankEntity.setUpdateTime(System.currentTimeMills());
        if(newRank < NOTIFY_RANK || oldRank < NOTIFY_RANK>) {
            try {
                this.key.getRankType().getHandler().onChange(rankEntity.getUuid(), oldRank, rankEntity.getRank());
            } catch(Exception e) {
                logger.error("rank change", e);
            }
        }
    }

    // 移出数据,不做排序处理
    public void removeWithIndex(int index) {
        RankEntity rankEntity = rankList.remove(index);
        if(rankEntity != null) {
            rankMap.remove(rankEntity.getUuid());
        }
        RankManger.self().update(this);
    }

    // 重置排行顺序
    public void sortWithRank() {
        lock.writeLock().lock();
        try {
            Collections.sort(rankList);
            for(int i=0; i< rankList.size(); i++) {
                rankList.get(i).sentRank(i);
            }
        } finally {
            lock.writeLock().unlock();
    }

    // 更新排行榜数据
    public RankEntity update(long uuid, long newValue, Consumer<RankEntity> extraUpdater) {
        lock.writeLock().lock();
        try {
            RankEntity rankEntity = rankMap.get(uuid);
            if(rankEntity == null) {
                // 未上榜的单位
                if(!canIntoRank(newValue)) {
                    return null;
                }
                rankEntity = new RankEntity(key.getRankType(), uuid, rnkList.size(), newValue);
                // 上榜更新
                addRankEntity(rankEntity);
                update(rankEntity, RankWave.UP, extraUpdater, true);
                // 记录新增的排名
                updateRecord(rankEntity);
            } else {
                // 已经在榜上的单位
                long oldValue = rankEntity.getValue();
                if(!key.getRankType().needUpdateWhenChange(oldValue,newValue)) {
                    // 排行值不需要进行变化
                    return null;
                }
                rankEntity.setValue(newValue);
                update(rankEntity, key.getRankType().comprator(oldValue, newValue), extraUpdater, false);
                // 记录新增的排名
                updateRecord(rankEntity);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    // 判断是否满足进入排行榜条件
    public boolean canIntoRank(long newValue) {
        return !(isFull() && key.getRankType().comparator(getLastValue(), newVlaue) != RankWave.UP);
    }

    // 更新排行榜
    private void update(RankEntuty updateEntity, RankWave wave, Consumer<RankEntity> extraUpdater, boolean first) {
        if(wave == RankWave.NONE) {
            return;
        }
        int mark = wave.getMark();
        int myOldRank = updateEntity.getRank();
        for(int i=updateEntiy.getRank()+ mark; i<rankList.size() && i>=0; i +=mark) {
            RankEntity exist = rankList.get(i);
            if(key.getRankType().comparator(exist.getValue(), updateEntity.getValue()) != wave) {
                break; // 找到合适的位置了
            }
            // 交换位置
            rankList.set(i - mark, exist);
            setNewRank(exist, i-mark);
            rankList.set(i, updateEntiy);
            updateEntity.setRank(i);
            updateEntiy.setRank(i);
        }
        // 更新额外信息
        if(extraUpdater != null) {
            extraUpdater.accept(updateEntiy);
        }
        if(rankList.size() > key.getRankType().getMaxRankSize) {
            removeWithIndex(rankList.size() -1);
        }
        boolean isRankChange = myOldRank != updateEntity.getRank();
        if(isRankChange) {
            // 名次有变更在记录更新时间
            updateEntity.setUpdateTime(System.currentTimeMills());
        }
        boolean isChange = first || (isRankChange && (myOldRank < NOTIFY_RANK || updateEntity.getRank() < NOTIFY_RANK ));
        if(isChange) {
            this.key.getRankType().getHandler.onChange(updateEntity.getUuid(), myOldRank, updateEntity.getRank());
        }
    }

    // 排行榜最低
    public long getLastValue() {
        lock.readLock.lock();
        try {
            if(rankList.isEmpty()) {
                return 0;
            }
            return rankList.get(rankList.size()-1).getValue();
        } finally {
            lock.readLock().unlock();
        }
    }

    // 排行榜是否已满
    public boolean isFull() {
        lock.readLock().lock();
        try {
            return rankList.size() >= key.getRankType().getMaxRankSize();
        } finally {
            lock.readLock().unlock();
        }
    }

    // 获取分页数据,例如获取前20名(0,20)
    public List<RankEntity> page(int start, int end) {
        // ...
    }

    public List<RankEntity> getAll() {
        // ...
    }

    // 获取榜单第一
    public RankEntity getFirstRankEntity() {
        if(rankList.isEmpty()) {
            return null;
        }
        return rankList.get(0);
    }
    // 移出数据,更新排名
    public void removeWithUuidAndSort(long uuid) {

    }

    // 重置排名
    public void resetRank(int rank) {
        for(int i=rank; i< this.rankList.size(); i++) {
            this.rankList.get(i).setRank(i);
        }
    }
    public void replaceRankList(List<RankEntity> rankList) {
        lock.writeLock().lock();
        try {
            this.rankList = rankList;
            this.rankMap.clear();
            for(RankEntity rankEntiy : this.rankList)) {
                this.ranjMap.puifAbsent(rankEntity.getUuid(), rankEntity);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    public boolean isMemory() {
        return memory;
    }

}

排行榜数据波动方式

public enum RankWave {
    // 排名上升
    UP(-1),
    // 排名下降
    DOWN(1),
    // 不变
    NONE(0),

    private int mark;

    RankWave(int mark) {
        this.mark = mark;
    }
    public int getMark() {
        return mark;
    }
}

排行榜数据库联合主键

@Embeddable
public class RankPk implements Serializable, Comparable<RankPk> {

    @Enumerated(EnumType.STRING)
    @Column(columnDefinition = "varchar(32) NOT NULL")
    private RankType rankType;

    private long subKey;

    public RankPk() {

    }

    public static RankPk valueOf(RankType rankType, long subKey) {
        RankRk vo = new RankPk();
        vo.subKey = subkey;
        vo.rankType = rankType;
        return vo;
    }

   // 各种get/set ......
    @Override
    public int compareTo(RankPk p) {
        if(rankType != o.rankType()) {
            return Integer.compare(rankType.oridianl(), o.rankType.ordinal());
        }
        return Long.compare(subKey,o.subKey);
    }

    @Override
    public boolean equals() {
        if(this== o) {
            return true;
        }
        if(o ==null || getClass() != o.getClass()) {
            return false;
        }
        RankRk ranRk = (RankPk) o;
        return rankType == rankPk.rankType && 
        Objects.equals(subKey, rankPk.subKey);
    }

    // hashCode() 与 toString() 方法 .......


排行类型

排行榜类型在枚举里定义,然后也要在RankResource.xlsx里配置相同的排行类型。

@Desc("排行榜类型")
public enum RankType implements EnumReadable,ValueConverter, EnumValue {

    @Desc("战力排行")
    FIGHT_FORCE(1) {
        @Override
        public int getMaxRankSize() {
            // 比排行榜可现实的最大数大0.2,为了在移除流失玩家时做候补
            return (int) (this.getLimit() * 1.2);
        }
    }
    @Desc("等级排行")
    LEVEL(2) {
        @Override
        public int getMaxRankSize() {
            // 比排行榜可显示的最大数大0.2,为了在移除流失玩家时做候补
            return (int) (this.getLimit() * 1.2);
        }
    }

    // ......

    @Desc("跨服争霸全服模式个人英雄塔排行")
    TRANSFER_HERO_TOWER_SER_PER(34,ServerGroupType.godKillTowerServer),
    @Desc("飞升等级榜")
    FLY_LEVEL(38),

    RankType(int value) {
        this(value,null);
    }

    RankType(int value, ServerGroupType serverGroupType) {
        this.value = value;
        this.serverGroupType = serverGroupType;
    }
    // 排行榜可显示的最大数
    public int getLimit() {
        return RankManger.self().getRankResource(this).getRankAmount();
    }
    // 服务器排行的最大数
    public int getMaxRankSize() {
        return this.getLimit();
    }

    // 比较排行榜值的大小,如果有不同的排序方式,可以覆盖该方法
    public RankWave comparator(long oldValue, long newValue) {
        return (oldValue < newValue) ? RankWave.UP : ((oldValue == newValue) ? rankWave.NONE:RankWave.DOWN);
    }

    // 当排行榜内的单位,在数据变化后,检测是否需要更新值,更新后会导致排行变动
    public boolean needUpdateWhenChange(long oldValue, long newValue) {
        return oldValue != newValue;
    }

    @Override
    public int value() {
        return value;
    }

    // 获取排行榜处理器
    public BaseRankHandler getHandler() {
        return BaseRankHandler.get(this);
    }

    public boolean isCross() {
        return serverGroupType != null;
    }

    public ServerGroupType getServerGroupType() {
        return serverGroupType;
    }
}

排行配置

@Resouce
public class RankResource {
    @ResourceId
    public RankType rankType;
    // 排行榜上榜最大人数
    private ubr rankAmount;
    // 客户端显示最大数量。因为发奖原因,可能服务端需要保存的排行榜比客户端需要显示的数量大
    private int showAmount;
    // 达到排行榜第一的通告
    private MessageCode rankUpNumberOne;
    // 排行榜第一登陆的通告
    private MessageCode numberOneLoginOn;


}

排行处理器

角色的游戏数据变化会影响排行榜的变化,所以需要对各种排行类型定义对应的处理器

排行榜处理接口

public abstract class BaseRankHandler {
    // 所有排行榜类型对应处理器接口
    private static EnumMap<RankType, BaseRankHandler> handlerMap = new EnumMap<>(RankType.class);

    public static BaseRankHandler get(RankType type) {
        return handlerMap.get(type);
    }

    public static Collection<BaseRankHandler> getHandlers() {
        return handlerMap.values();
    }

    @PostConstruct
    public void init() {
        RankTpye type = type();
        if(handlerMap.constaisKey(type)) {
            throw new RuntimeException(String.format("RankHandler type[%s]重复!",type));
        }
        handlerMap.put(type, this);
    }

    public RankList getRanKList() {
        return RankManager.self().getRankList(type());
    }

    public RankList getRankList(long subKey) {
        return RankManager.self().getRankList(type(), subKey);
    }

    public Rank getRank(long uuid) {
        return getRankList().getRank(uuid);
    }

    // 更新排行信息
    protected void updateRank(long subKey, long uuid, long newValue, Consumer<RankEntiy> extraUpdater)   {
        getRankList(subKey).update(uuid,newValue, extraUdater);
    }

    // 每一个排行榜类型,都应该有一个`BaseRankHandler`
    public abstract RankType type() ;

    // 获取该排行榜中,与玩家关联的排名实体。以玩家为主体的排行就是玩家本身的排行实体
    // 不以玩家为主体的排行,例如公会排名名,就是玩家所属工会排名
    public abstract RankEntity getRankEntity(long uuid);     

    // 排行主键
    public abstract RankEntity getRankEntity(long uuid);

    // 游戏服发送排行榜信息
    public void sendRankList(long playerId, int start, int terminal) {

    }

    public <T> T getRankListPacket(long playerId, int start, int terminal) {
        return null;
    }

    // 该排名奖励发给哪些玩家
    public List<Long> getRewardPlayerIds(long uuid) {
        return Collection.emptyList()
    }

    // 清空排行榜
    public void clear() {
        RankManager.self().walkRankList(type(), RankList::clear());
    }

    // 名次变化,只有前几名排行变化才触发
    public void onChange(long uuid, int oldRank, int newRank) {

    }

    // 给排行榜内发奖
    public void sendRankReward(long uuid, int rank) {}

}

实现了BaseRankHandler接口的处理器

玩家等级排行榜处理器

@Component
public class PlayerLevelRankHandler extends BasePlayerRankHandler {
    @Overide
    public RankType type() {
        retrun RankType.LEVEL;
    }

    @Override
    public long getRankValue(Player player) {
        return palyer.getLevel();
    }

    // 战力排行
    @ReceiverAnno
    public void updateRank(Player player) {
        // 玩家等级不会降低,不做降低移出排行榜操作,如果前提不成立,需要支持降级导致的出榜逻辑
        if(getRankValue(player) >= RankContants.getInstance().getEnterLevelRankListValue()) {
            super.updateRank(player);
        }
    }
}

总结

添加一个新的排行榜,需要在RankType添加一个新的枚举,在RankResource.xlsx添加对应的排行类型。实现BasePlayerRankHandler为排行榜指定处理器,描述如果获取排行值、如果处理排行的更新。

掉落系统学习

概况

掉落系统是RPG类游戏的重要组成部分,直接影响玩家的核心游戏体验。掉落配置表繁多,配置参数复杂。如果策划配错数值,服务端必须对整个系统极为了解才能快速找到出错的地方。

配置

掉落系统拥有基础的五张表,这五张表之间层层相关,一步步决定了最终掉落的物品。

掉落表关系图:

ObjectResource: 怪物表dropResourceIds字段配置下面的掉落ID,同一个怪可以有多种掉落方式

DropResource: gropIds索引到 DropItemGroupResource的Id,确定

DropGropResource: itemGroupId索引到DropItemGroupResource的Id,确定掉落的掉落组

DropItemGroupResource: dropItemId索引到DropItemResource的id,确定掉落里面有哪些掉落项 PS:这张表不是配物品ID!是下面表的ID!!!!

DropItemResource: code索引到物品ID,确定掉落项的各种属性

当需要给一个怪物配置掉落时,我们需要在怪物ObjectResource的dropResourceIds添加一个掉落id,然后在DropResource决定掉落策略(掉地上或者飞包)、掉落触发条件、掉落归属策略、玩家和怪物的掉落条件,之后在DropGropResource决定掉落次数限制、掉落随机范围、随机方式(是否放回)、各个掉落物组的权重,之后在DropItemGroupResource填写各掉落物的权重,最后在DropItemResourc配置掉落物的物品id、数量、类型、是否绑定、是否专用、记录描述等。

对象表 -> 掉落方式表 -> 掉落组表 -> 掉落物组表 -> 掉落物表

额外的相关联表:

DropGroupLimitResource: 掉落限制表,可以配置个人每人每次限制、全服每日次数限制、个人永久次数限制

掉落的业务方法

掉落逻辑处理

@Component
public class DropService {

    // 幸运大爆
    public boolean luckyValueDrop(Player player, int bossId, int x, int y) {
        // ...
    }

    // 幸运大爆是否触发
    public boolean isLuckyDropHit(Player player, PlayerDropEntity playerDropEntity, int bossId) {
        // ...
    }

    // 计算出超级幸运大爆物品奖励
    private Reward getSuperLuckDropReward(Player player) {
        // ...
    }

    // 创建掉落物的配置信息
    public void createDropToGroundItems(Player player, PlayerKillBossDropEvent envet) {
        // ...
    }

    // 战斗服游戏服统一执行飞包掉落物的逻辑
    public void dropToBag(Player player, PlayerDropToBagEvent event) {
        List<DropItemResource> result = calcuteDropItems(player, event.getBossDropInfo(), event.getDropId());
        if(result == null || result.isEmpty()) {
            return;
        }
        DropStrategy.dropToBag(event.getBossDropInfo(), player, result);
    }



    // 根据掉落ID计算出掉落物的逻辑
    private List<DropItemResource> calcutrDropItems(Player player, BossDropInfo bossDropInfo, int dropId) {
        DropResource dropResource = DropManager.getInstance().getDropResource(dropId);
        MapResource mapResource = MapManger.self().getMapResource(bossDropInfo.getMapId());
        Map<Object, Object> params = null;
        if(mapResource.isTransferMap()) {
            // 如果是跨服地图,则对去开服天数取战区平均天数
            params = new HashMap<>();
            params.put("openDay", CenterManager.self().getServerGroupOpenDay());
        }
        // 检验玩家是否符合掉落条件(DropResource中配置的playerConditionsDefs和开服天数)
        Result result = dropResource.getPlayerConditions().verify(player, 1, params);
        if(result.isFali()) {
            return null;
        }
        // 判断是否触发击杀幸运次数掉落
        boolean isPlayerLuckyHit = dropResource.isPlayerLuckyNumHit(player);
        if(!isPlayerLuckyHit) {
            return null;
        }
        boolean isServerLuckyHit = dropResource.isServeLuckyNumHit();
        if(!isServerLuckyHit) {
            return null;
        }
        return DropStrategy.getDropItemAndAddRecord(player, dropResource.getGrouoIds(), params, bossDropInfo);
    }



}

掉落归属策略


public enum DropBelongStrategy {

    // 无归属
    EVERYONE(true),
    // 击杀者归属
    KILLER(false) {
        @Override
        public BelongContext createContext(Npc death, AbstractCreature killer) {
            FightPlayer fightPlayer = killer.getPlayerMasterOrNull();
            if(fightPlayer == null) {
                return BelongContext.valueOf(DropBelongStrategy.EVERYONE);
            }  
            BelongContext belongContext = BelongContext.valueOf(this);
            belongContext.setKillerId(fightPlayer.getObjectId());
            belongContext.setDropBelongStrategy(this);
            return belongContext;
        }

        @Override
        public IBelongStrategy getBelongStrategy(BelongContext belongContext) {
            // 若击杀者不是玩家或者玩家的召唤物,则默认所有人拾取
            return new KillerBelongStrategy(belongContext.getKillerId());
        }

        @Override
        public List<FightPlayer> getBelongFightPlayers(Npc death, AbstractCreature killer) {
            ArrayList<FightPlayer> players = new ArrayList<>(1);
            if(killer != null) {
                AbstractCreature master  = killer.getMasterOrSelf();
                if(master.isPlayer()) {
                    players.add((FightPlayer) master);
                }
            }
            return players;
        }
    }

    // 仇恨最高者归属
    HATRED(true) {
        @Override
        public BelongContext createContext(Npc death,AbstractCreature killer) {
            AbstractCreature abstractCreature = death.getAggroBelong();
            FightPlayer fightPlayer = abstractCreature.getPlayerMasterOrNull();
            if(fightPlayer == null) {
                return BelongContext.valueOf(DropBelongStrategy.EVERYONE);
            }
            BelongContext belongContext = BelongContext.valueOf(this);
            belongContext.setBelongId(death.getAggroBelong.getOjectId());
            return belongContext;
        }
        @Override
        public IBelongStrategy(BelongContext belongContext) {
            return new AggroBelongStrategy(belongContext.getBelongId());
        }
        @Override
        public List<FightPlayer> getBelongFightPlayers(Npc death, AbstractCreature killer){
            AbstractCreature aggroBeong = death.getAggroBelong();
            ArrayList<FightPlayer> players = new ArrayList<>(1);
            if(aggroBelong != null) {
                FightPlayer master = aggroBelong.getPlayerMasterOrNull();
                if(master != null) {
                    players.add(master);
                }
                return palyers;
            }
        }
    }

    // 击杀者公会归属
    KILLER_GUILD(false),

    // 仇恨最高者归者公会归属
     HATRED_GUILD(true) {

     }

     // 击杀者队伍归属
     KILLER_TEAM(ture) {

     }

     // 仇恨最高者公会归属,且需工会玩家在仇恨列表
     HATRED_GUILD_IN_AGGER(ture) {

     }

     // 是否属于玩家
     private boolean isToBelongPlayer;

     DropBelongStrategy(boolean isToBelongPlayer) {
         this.isToBelongPlayer = isToBelongPlayer;
     }

    // 返回掉落拾取策略
    public IBelongStrategy getBelongStrategy() {
        retuen new EveryoneBelongStatergy();
    }

    // 返回掉落归属玩家Id
    public List<FightPlayer> getBelongFightPlayers(Npc death, AbstractCreautre killer) {
        return new ArrayList<FightPlayer>;
    }

    // 创建掉落内容
    public BelongContext createContext() {
        return BrlongContext.valueOf(DropBelongStrategy.EVERYONE);
    }

    public boolean isToBelongPlayer() {
        return isTobelongPlayer;
    }
}

掉落策略


public enum DropStrategy {

    // 掉落到地上
    DropToGroupStrategy {

        @Override
        public void doDrop(Npc death, AbstractCreature killer, DropRsource dropResource) {
            // 确定归属策略影响到的主体,用于决定向谁抛事件
            FightPlayer fightPlayer = null;
            if(dropRsource.getBelongStrategy().isToBelongPlayer()) {
                AbstractCreature aggroBelong = death.getAggroBelong();
                if(aggroBelong == null) {
                    return null;
                }
                fightPlayer = aggroBelong.getPlayerMasterOrNull();
            } else {
                fightPlayer = killer.getPlayerMasterOrNull();
            }
            if(fightPlayer == null) {
                return;
            }
            // 向掉落主体抛掉落事件,掉落主体接受事件之后,进行各种玩家掉落条件以及掉落次数的验证
            fightPlayer.getPlayerBridge().submitEvent(
                PlayerKillBossDropEvent.valueOf(dropResource.getId(), dropResource.getBelongStrategy().createContext(death,killer),
                BossDropInfo.valueOf(death));
            );
        }
    },

    // 掉落到玩家背包
    DropToPlayerBagStrategy {
        @Override
        public void doDrop(Npc death, AbstractCreature killer, DropRsource dropResource) {
            // 确定归属策略
            List<FightPlayer> players = dropResource.getBelongFightPlayers(death,killer);
            for(FightPlayer player : players) {
                player.getPlayerBridge().submitEvent(PlayerDropToBagEvent.valueOf(dropResource.getId(), BossDropInfo.valueOf(death)));
            }
        }
    }

    // ================掉落到仓库,等可后续加类型==============



    public void doDrop(Npc death, AbstractCreature killer, DropResource dropResource) {
        // ...
    }

    // 根据掉落组随机出掉落物信息,根据掉落信息可以转化成掉落到地上的对象,或者掉落到背包的物品
    public static List<DropItemResource> rollGoods(Player player, List<Integer> groupIds) {
        return rollGoods(player, groupIds, null);
    }

    // 随机出掉落物
    public static List<DropItemResource> rollGoods(Player player, List<Integer> groupIds, Map<Object, Object    >) { 
        List<DropItemResource> result = new ArrayList<>();
        for(Integer groupId : groupIds) {
            // 获取掉落组配置
            DropGroupResource groupResource = DropManager.getInstance().getDropGroupResource(groupId);
            if(groupResource == null) {
                // 配了不存在的掉落组说明随机不到东西
                if(!groupResource.checkLimit(player)) {
                    continue;
                }
                CHooser chooser = groupResource.getChooser(plyaer,params);
                List<ChooserItem> itemGroups = ChooserManager.getInstance().chooser(chooser);
                if(itemGroups.isEmpty()) {
                    continue;
                }
                itemGroups.forEach(itemGroup -> {
                    int itemGroupId = Integer.valueOf(itemGroup.getResult());
                    if(itemGroupId != 0) {
                        // 获取掉落物组配置
                        DropItemGroupResource itemGroupResource = DropManager.getInstance().getDropItemGroupResource(itemGroupId);
                        ChooserItem dropItem = ChooaweManager.getInstance().chooseOne(itemGroupResource.getChooser());
                        if(dropItem != null) {
                            int dropItemId = Integer.valueOf(dropItem.getResult());
                            if(dropItemId > 0) {
                                result.add(DropManager.getInstance().getDropItemResource(dropItemId));
                            }
                        }
                    }
                });

            }
        }
        return result;
    }

    // 将物品掉落到地图上
    public static dropToGround0(Grid grid, int dropItemId, IBelongStrategy, WorldMapInstance mapInstance) {
        if(dropItemId == 0) {
            return;
        }
        Map<String, Object> args = new HashMap<>();
        args.put("x",grid.getX());
        args.put("y", grid.getY());
        args.put("dropItemId", dropItemId);
        args.put("belongStrategy", belongStrategy;
        args.put("strartClearTask", true);
        DropObject dropObject = (DropObject) DropObjectCreator.getCreator(ObjectType.DROPOBJECT).create(null, null, mapInstance,args);
        World.getInstance().spawn(dropObject);
    }

    // 掉落到地方的方法
    public static void dropToGround(int centerX, int centerY, List<DropItemResource> dropItemResources,
    IBelongStrategy belongStrategy, WorldMapInstance mapInstance) {
        // ...
    }

    // 掉落到背包
    public static List<DropItemReosource> dropToBag(BossDropInfo bossDropInfo, Player player, List<DropItemResource> rsult) {
        // ...
    }

    //  生成掉落记录
    public static BossDropRecord createDropRecord(int mapId, int objectId, long mapInstanceId, String name, int dropItemId) {
        // ...
    }

    // 获取掉落物
    public static List<DropItemResource> getDropItemAndAddRecord(Player player, List<Integer> groupIds, ) {
        List<DropItemResource> result = new ArrayList<>();
        if(groupIds.isEmpty()) {
            return result;
        }
        List<DropItemResource> rollGoods = rolGoods(player, groupIds,params);
        // 检测广播
        broadcastDropItems(plyaer, rollGoods,bossDropInfo);\
        // 检测掉落记录
        checkDropRecord(player, rollGoods, bossDropInfo);
        return rollGoods;
    }

    // 检测珍惜掉落广播
    public static void broadcastItems(Player player, List<DropItemReosurce> rollGoods, BossDropINfo bossDropInfo) {

    }

    // 检测掉落到背包的嗲罗记录是否需要广播到其他游戏服
    // 掉落到背包是在游戏服进行掉落,获取groupIndex的方式是在游戏服的门票信息里面去获取
    public static void checkDropRecord(Player palyer, List<DropItemReosurce> rollGoods, BossDropINfo bossDropInfo) {
        // ...
    }

}

门面类和掉落的触发

掉落外观类

@Component
@SocketClass
public class DropFacade {

    // ...

    @ReceiverAnno
    public void onKillBoss(KillBossEvent event) {
        onKillBoss(event.getPlayer(), event.getMapId(), event.getBossId(), event.getX(), event.getY());
    }

    private void onKillBoss(Player player, int mapId, int bossId, int x, int y) {
        // Vip大爆
        MapResource mapRsource = MapManager.self().getMapResourceStorage().get(mapId, true);
        if(mapResource.getType() == MapType.FIELD || mapResource.getType() == MapType.TRANSFER_FIELD) {
            dropService.vipDrop(player, bossId);
        }
        // 幸运大爆
        if(ModuleOpenManger.getInstance().isOpen(player, ModuleOpenType.LUCKY_DROP)) {
            dropService.luckyValueDrop(player, bossId, x, y);
        }
    }


    // 游戏服,玩家接受掉落事件,进行玩家条件检测以及次数判断
    @ReceiverAnno
    public void onPlayerKillBossDrop(PlayerKillBossDropEvent event) {
        dropSerive.createDropToGroundItems(event.getPlayer(), event);
    }

    // 游戏服,处理掉落物到场景事件
    @ReceiverAnno
    public void onDropToGround(DropToGroundEvent event) {
        dropService.doDropToGround(event.getPlayer().getFightPlayer(), event.getDropItemIds(),
        event.getBelongContext(), event.getBossDropInfo());
    }

    // 战斗服,处理掉落物掉到场景事件
    @ReceiverAnno
    public void onTransferDropToGround(onTransferDropToGroundEvent event) {
        dropService.doDropToGround(event.getPlayer().getFightPlayer(), event.getDropItemIds(),
        event.getBelongContext(), event.getBossDropInfo());
    }

    // 战斗服,统一处理飞背包掉落物事件
    @ReceiverAnno
    public void onDropToBag(PlayerDropToBagEvent event) {
        dropService.dropToBa(event.getPlayer(), event);
    }

}

掉落数据的持久化

玩家掉落数据

@Entity
@Cached
public class PlayerDropEntity extends AbstractEntity<Long> {

    @Id
    private long playerId;

    // ======= 击杀怪物记录 ========
    @Lob
    private String dailyKillMonsterTimesStr;
    // 与dropId关联的怪物的击杀次数<dropId, times>
    private transinet ConcurrentHashMap<Integer, Integer> dailyKillMonsterTimes;

    @Lob
    private String dailyKillMonsterLuckyNumStr;
    // 当日击杀幸运数<掉落ID, 幸运击杀数组>
    private transient ConcurrentHashMap<Integer, List<Integer>> dailyKillMonsterLuckyNums;

    // ========= 掉落组掉落记录 ==========

    // 当日掉落物品组次数 <dropGroupId, times>,key为DropGroupLimit的id
    @Lob
    private String dailyDropGroupTimesStr;
    private transient ConcurrentHashMap<Integer, Integer> dailyDropGroupTimes;

    // 永久掉落物品次数 <dropGroupId, times>,key为DropGroupLimit的id
    @Lob
    private String dropGroupTimesStr;
    private transient ConcurrentHashMap<Integer, Integer> dropGroupTimes;

    // ======== 幸运值相关 ============

    // 当前幸运值
    private int luckyValue;
    // 今日回收装备数量
    private transient Map<Intger, Integer> dailyRecycleEquipNum;
    @Lob
    private String dailyRecycleEquipNumData;
    // 今日幸运大爆奖励记录
    private transient List<LuckyDropRecord> luckyDropRecords;
    @Lob
    private String luckyDropRecordsData;
    // 今日获得幸运值
    private int dailyAddLuckyValue;
    // 历史获得幸运值
    private int totalAddLuckyValue;
    // 今日幸运大爆次数
    private int dailyLuckDropTimes;
    // 历史幸运大爆次数
    private int totalLuckyDropTimes;

    private transient Map<Integer, QuestLuckyDropData> questLuckyDropDataMap;
    @Lob
    private String questLuckyDropDataStr;

    // 每日生成的超级幸运掉落次数列表
    private transient List<Integer> dailySuperLuckyDropValueList;
    @Lob
    private String dailySuperLuckyDropValueDataStr;

    private transient Map<ObjectSubType, BossLuckyTimeData> bossLuckyTimesMap;
    @Lob
    private String bossLuckyTimesData;

    // 各种处理属性的方法 。。。


}