|
|
本帖最后由 pkoukk 于 2026-1-8 11:40 编辑
自己重写的初衷,主要玩的整合包里的宠吸方案突出一个能用就行
右下角会有大量的“无法拾取”提示,宠物过滤器也不起作用,强迫症有点难受
OK,现在问题分为两个
第一个,宠物过滤不起作用
那个帖子要50币,家境贫寒买不起
但是根据现象,我猜测现有的应该是把LootCommand里的代码直接搬过来了,没有过宠物筛选,也就是
-
- List<MapObject> items = c.getPlayer().getMap().getMapObjectsInRange(c.getPlayer().getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapObjectType.ITEM));
- for (MapObject item : items) {
- MapItem mapItem = (MapItem) item;
- if (mapItem.getOwnerId() == c.getPlayer().getId() || mapItem.getOwnerId() == c.getPlayer().getPartyId()) {
- c.getPlayer().pickupItem(mapItem);
- }
- }
复制代码
那么,我们只要把原本的宠物装备的筛选逻辑加上就可以了,为了后面使用方便,我们把原本的筛选逻辑重构成一个函数
-
- private final boolean shouldPickupItem(Character chr, MapItem mapitem) {
- if (mapitem == null || mapitem.isPickedUp()) {
- return false;
- }
- if (mapitem.getMeso() > 0) {
- if (!chr.isEquippedMesoMagnet()) {
- return false;
- }
- if (chr.isEquippedPetItemIgnore()) {
- final Set<Integer> petIgnore = chr.getExcludedItems();
- if (!petIgnore.isEmpty() && petIgnore.contains(Integer.MAX_VALUE)) {
- return false;
- }
- }
- } else {
- if (!chr.isEquippedItemPouch()) {
- return false;
- }
- if (chr.isEquippedPetItemIgnore()) {
- final Set<Integer> petIgnore = chr.getExcludedItems();
- if (!petIgnore.isEmpty() && petIgnore.contains(mapitem.getItem().getItemId())) {
- return false;
- }
- }
- }
- return true;
- }
复制代码
那么这个时候,我们只要加上这条判断,宠物过滤也就重新起作用了
-
- List<MapObject> items = chr.getMap().getMapObjectsInRange(pet.getPos(),
- Double.POSITIVE_INFINITY, Arrays.asList(MapObjectType.ITEM));
- for (MapObject item : items) {
- MapItem mapItem = (MapItem) item;
- if (shouldPickupItem(chr, mapItem) && (mapItem.getOwnerId() == chr.getId()
- || mapItem.getOwnerId() == chr.getPartyId())) {
- chr.pickupItem(mapItem, petIndex);
- }
- }
复制代码
第二个问题,烦人的无法拾取物品的提示
来看服务端源码,不难发现,提示来自于sendPacket(PacketCreator.showItemUnavailable());
最简单的办法,注释掉它,客户端就不会收到提示了
但是我闲的蛋疼,看看根因是什么,能不能解决一下
那么看一眼代码,触发这种提示的条件有几种:
1. 物品已被拾取
2. 处在一些组队地图里,不能给队友扔东西
3. 没有接到任务,但尝试拾取任务掉落
1 可能的触发原因:没有并发控制,上一次全屏吸还没结束,下一次又开始了,第二次查询到的物品被第一次拾取,故而报错
2和3的原因一样:在客户端里,这些物品会被客户端逻辑隐藏,宠物看不见这些物品,不会去拾取它,但是当改用范围查询之后,就触发了这些本不该被拾取的物品的拾取,故而报错
简单分析下,
在单机或者少量亲友游玩的场景下,这些都不是问题,直接注释sendPacket(PacketCreator.showItemUnavailable()); 屏蔽提示就完事了
大部分人看到这里就可以了,下面真的没必要
当然,我闲的蛋疼,而且有代码洁癖,还是去解决一下吧。
首先是1
加个并发控制很简单,我们在character 中新增一个属性 private AtomicBoolean petLootInProgress = new AtomicBoolean(false);
然后我们不在PetLootHandler中直接调用 chr.pickupItem,而是新增一个函数
-
- public final void pickupItems(List<MapObject> obs, int petIndex) {
- // Use compareAndSet for atomic check-and-set (single flight pattern)
- if (!petLootInProgress.compareAndSet(false, true)) {
- enableActions();
- return;
- }
- try {
- for (MapObject ob : obs) {
- var item = (MapItem) ob;
- pickupItem(ob, petIndex);
- }
- } catch (Exception e) {
- log.error(I18nUtil.getLogMessage("Character.pickupItems.error", getName()), e);
- } finally {
- petLootInProgress.set(false);
- enableActions();
- }
- }
复制代码
在PetLootHandler里,我们对原始识别到的物品进行完初次宠物过滤之后,调用pickupItems,这样多次请求就会被合并成一次,实现类似single flight的效果
- List<MapObject> filteredItems = items.stream()
- .filter(item -> {
- MapItem mapItem = (MapItem) item;
- return shouldPickupItem(chr, mapItem) && (mapItem.getOwnerId() == chr.getId()
- || mapItem.getOwnerId() == chr.getPartyId());
- })
- .toList();
- chr.pickupItems(filteredItems, petIndex);
复制代码
然后是2和3,
2很好办,charcter的类中已经包好了这个检测函数public boolean needQuestItem(int questid, int itemid) ,只要在拾取前判断一下,跳过即可
3就有有点长了,因为我是玩单机,不会有这个情况,所以就没改,但逻辑也很简单。大概是if ((MapId.isSelfLootableOnly(mapId)) && (mapitem.isPlayerDrop()&& mapitem.getDropper().getObjectId() != client.getPlayer().getObjectId()))
最后,闲的蛋疼的我发现原本每拾取一次物品,就会给客户端发包enableActions解卡
但我们现在批量拾取,可以视做异步过程,没必要每捡一个东西就发一个包,在拾取结束的finally里发一次就够了,然后把pickupItem中的enableActions替换成一个新的enableActions,当petIndex>=0&&petLootInProgress的时候直接return,没必要发那么多包回去。
这段就不放代码了,位点太杂,需要降低回包的腐竹应该看得懂思路
|
评分
-
查看全部评分
|