Skip to content

BqsAdvListener clinit error #151

@Invadermonky

Description

@Invadermonky

Description

Attempting to interact with choice quest rewards too fast can result in a client-side crash. This issue appears more prevalent with large quest books that take a moment to update after a quest is complete.

Problematic Code

It appears that there are two different event subscribers to the same event with the same priority within the EventHandler, likely resulting in the events firing in the incorrect order.

public void onLivingUpdate(LivingUpdateEvent event) {
if (event.getEntityLiving().world.isRemote) return;
if (!(event.getEntityLiving() instanceof EntityPlayerMP)) return;
if (event.getEntityLiving().ticksExisted % 20 != 0) return; // Only triggers once per second
EntityPlayerMP player = (EntityPlayerMP) event.getEntityLiving();
betterquesting.api2.cache.QuestCache qc = player.getCapability(CapabilityProviderQuestCache.CAP_QUEST_CACHE, null);
boolean editMode = QuestSettings.INSTANCE.getProperty(NativeProps.EDIT_MODE);
if (qc == null) return;
List<DBEntry<IQuest>> activeQuests = QuestDatabase.INSTANCE.bulkLookup(qc.getActiveQuests());
List<DBEntry<IQuest>> pendingAutoClaims = QuestDatabase.INSTANCE.bulkLookup(qc.getPendingAutoClaims());
QResetTime[] pendingResets = qc.getScheduledResets();
UUID uuid = QuestingAPI.getQuestingUUID(player);
boolean refreshCache = false;
if (!editMode && player.ticksExisted % 60 == 0) // Passive quest state check every 3 seconds
{
List<Integer> com = new ArrayList<>();
for (DBEntry<IQuest> quest : activeQuests) {
if (!quest.getValue().isUnlocked(uuid)) continue; // Although it IS active, it cannot be completed yet
if (quest.getValue().canSubmit(player)) quest.getValue().update(player);
if (quest.getValue().isComplete(uuid) && !quest.getValue().canSubmit(player)) {
refreshCache = true;
qc.markQuestDirty(quest.getID());
com.add(quest.getID());
if (!quest.getValue().getProperty(NativeProps.SILENT))
postPresetNotice(quest.getValue(), player, 2);
DBEntry<IParty> partyEntry = PartyManager.INSTANCE.getParty(uuid);
if (partyEntry != null && player.getServer() != null) {
for (UUID memID : partyEntry.getValue().getMembers()) {
EntityPlayerMP memPlayer = player.getServer().getPlayerList().getPlayerByUsername(NameCache.INSTANCE.getName(memID));
if (memPlayer != null) {
quest.getValue().detect(memPlayer);
}
}
}
}
}
MinecraftForge.EVENT_BUS.post(new QuestEvent(Type.COMPLETED, uuid, com));
}
if (!editMode && player.getServer() != null) // Repeatable quest resets
{
List<Integer> res = new ArrayList<>();
long totalTime = System.currentTimeMillis();
for (QResetTime rTime : pendingResets) {
IQuest entry = QuestDatabase.INSTANCE.getValue(rTime.questID);
if (totalTime >= rTime.time && !entry.canSubmit(player)) // REEEEEEEEEset
{
if (entry.getProperty(NativeProps.GLOBAL)) {
entry.resetUser(null, false);
} else {
entry.resetUser(uuid, false);
}
refreshCache = true;
qc.markQuestDirty(rTime.questID);
res.add(rTime.questID);
if (!entry.getProperty(NativeProps.SILENT)) postPresetNotice(entry, player, 1);
} else break; // Entries are sorted by time so we fail fast and skip checking the others
}
MinecraftForge.EVENT_BUS.post(new QuestEvent(Type.RESET, uuid, res));
}
if (!editMode) {
for (DBEntry<IQuest> entry : pendingAutoClaims) // Auto claims
{
if (entry.getValue().canClaim(player)) {
entry.getValue().claimReward(player);
refreshCache = true;
qc.markQuestDirty(entry.getID());
// Not going to notify of auto-claims anymore. Kinda pointless if they're already being pinged for completion
}
}
}
if (refreshCache || player.ticksExisted % 200 == 0) // Refresh the cache if something changed or every 10 seconds
{
qc.updateCache(player);
}
if (qc.getDirtyQuests().length > 0) NetQuestSync.sendSync(player, qc.getDirtyQuests(), false, true, true);
qc.cleanAllQuests();
}

public void onEntityLiving(LivingUpdateEvent event) {
if (!(event.getEntityLiving() instanceof EntityPlayer) || event.getEntityLiving().world.isRemote || event.getEntityLiving().ticksExisted % 20 != 0 || QuestingAPI.getAPI(ApiReference.SETTINGS).getProperty(NativeProps.EDIT_MODE))
return;
EntityPlayer player = (EntityPlayer) event.getEntityLiving();
ParticipantInfo pInfo = new ParticipantInfo(player);
List<DBEntry<IQuest>> actQuest = QuestingAPI.getAPI(ApiReference.QUEST_DB).bulkLookupShared(pInfo);
for (DBEntry<IQuest> entry : actQuest) {
for (DBEntry<ITask> task : entry.getValue().getTasks().getEntries()) {
if (task.getValue() instanceof ITaskTickable) {
((ITaskTickable) task.getValue()).tickTask(pInfo, entry);
} else if (task.getValue() instanceof TaskTrigger) {
((TaskTrigger) task.getValue()).checkSetup(player, entry);
}
}
}
}

Crashlog

crash-2026-05-09_09.02.03-server.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions