如何在循环中为每个对象创建独立实例以区分同类型Challenge的完成状态
问题核心原因
你遇到的同类型Challenge状态混淆、进度不生效的核心问题出在状态存储的位置和逻辑错误,具体可以拆解为3个错误点:
- 你把
challengeHolderMap定义在for循环内部,每次遍历到新的Challenge时,上一轮循环创建的Map和Holder实例会直接被销毁,所有进度、完成状态根本没有持久保存,每次循环都是新建初始状态(进度1、未完成)的Holder,自然无法区分不同Challenge的状态。 - 创建ChallengeHolder时进度写死为1,没有读取玩家的历史进度,也没有在触发收集行为时累加进度,逻辑上永远只能判断进度为1的情况。
- 没有基于Challenge的唯一标识做区分,仅靠类型判断的话,同类型的不同Challenge会被判定为同一个,自然会出现完成一个就全部标记完成的问题。
修复方案
1. 先给Challenge类增加唯一标识
在你的Challenge类中新增唯一ID字段(比如自增ID或者唯一字符串ID),用来区分同类型的不同Challenge,示例:
public class Challenge { private String id; // 新增唯一标识,同类型的不同Challenge id不同 private ChallengeType type; private int challengeCondition; // 其余原有字段和get/set方法 public String getId() { return id; } }
2. 调整ChallengeHolder缓存结构
把存储Holder的Map从循环内移到全局作用域(可以放在你的Plugin类或者ChallengeManager中作为成员变量),用双层Map结构保证每个玩家的每个Challenge对应唯一的Holder实例:
// 全局缓存定义:Key1=玩家UUID,Key2=Challenge唯一ID,Value=对应的Holder实例 private Map<UUID, Map<String, ChallengeHolder>> playerChallengeCache = new HashMap<>();
3. 重写循环逻辑
修正进度读取、更新、状态判断的逻辑:
// 先获取当前玩家的所有Challenge缓存,没有的话初始化 Map<String, ChallengeHolder> userChallengeMap = playerChallengeCache.computeIfAbsent(playerUUID, k -> new HashMap<>()); for (Challenge challenge : challengeList) { // 原有过滤逻辑保留 if (plugin.getChallengeManager().canCompleteChallenge(playerUUID, challenge) || !plugin.getChallengeManager().isSameChallengeType(challenge, ChallengeType.COLLECT)) { continue; } String challengeId = challenge.getId(); // 从缓存取当前Challenge对应的Holder,不存在则新建 ChallengeHolder holder = userChallengeMap.computeIfAbsent(challengeId, k -> new ChallengeHolder(challenge, 0, false)); // 已经完成的直接跳过 if (holder.isCompleted()) { continue; } // 物品匹配判断保留 ItemStack itemStack = new ItemStack(new ChallengeReplacer(plugin).replaceItemStack(holder.getChallenge())); if (!generatorItem.isSimilar(itemStack)) { continue; } // 匹配到对应物品,进度+1 holder.setProgress(holder.getProgress() + 1); int currentProgress = holder.getProgress(); int challengeCondition = challenge.getChallengeCondition(); // 进度满足条件时标记完成 if (currentProgress >= challengeCondition) { int challengeRemaining = plugin.getDatabaseManager().getChallengeRemaining(playerUUID, challenge); plugin.getChallengeManager().updateChallenge(playerUUID, challenge, (challengeRemaining - 1)); holder.setCompleted(true); } }
额外注意事项
- 如果服务端重启后需要保留进度,需要把ChallengeHolder的进度和状态定期持久化到数据库,启动时再加载到缓存中即可。
- 如果玩家完成Challenge后要重置进度,直接从
userChallengeMap中移除对应Challenge的Key即可。
内容的提问来源于stack exchange,提问作者Ricardo
相关产品推荐
相关产品推荐

