Change way that finishProgressList is handled at login (#135)
Some checks failed
Build / Build-Server-Jar (push) Has been cancelled

Quests that have isRewind=False cannot be rewindTargets
checkAndUpdateContent saves the quest if Content changed
Don't modify finishProgressList if it's from a login
This commit is contained in:
Nazrin 2024-10-21 19:07:03 -07:00 committed by GitHub
parent 9138b96a94
commit 3932bdead9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 39 additions and 24 deletions

View File

@ -4,6 +4,7 @@ import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed; import dev.morphia.annotations.Indexed;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Loggers; import emu.grasscutter.Loggers;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ScriptSceneData; import emu.grasscutter.data.binout.ScriptSceneData;
@ -254,7 +255,7 @@ public class GameMainQuest {
return null; return null;
} }
public GameQuest getRewindTarget(){ public GameQuest getHighestActiveQuest() {
var activeQuests = getChildQuests().values().stream() var activeQuests = getChildQuests().values().stream()
.filter(p -> (p.getState() == QuestState.QUEST_STATE_UNFINISHED || p.getState() == QuestState.QUEST_STATE_FINISHED)).toList(); .filter(p -> (p.getState() == QuestState.QUEST_STATE_UNFINISHED || p.getState() == QuestState.QUEST_STATE_FINISHED)).toList();
var highestActiveQuest = activeQuests.stream() var highestActiveQuest = activeQuests.stream()
@ -266,7 +267,7 @@ public class GameMainQuest {
var firstUnstarted = getChildQuests().values().stream() var firstUnstarted = getChildQuests().values().stream()
.filter(q -> q.getQuestData() != null && q.getState().getValue() != QuestState.QUEST_STATE_FINISHED.getValue()) .filter(q -> q.getQuestData() != null && q.getState().getValue() != QuestState.QUEST_STATE_FINISHED.getValue())
.min(Comparator.comparingInt(a -> a.getQuestData().getOrder())); .min(Comparator.comparingInt(a -> a.getQuestData().getOrder()));
if(firstUnstarted.isEmpty()){ if (firstUnstarted.isEmpty()) {
// all quests are probably finished, do don't rewind and maybe also set the mainquest to finished? // all quests are probably finished, do don't rewind and maybe also set the mainquest to finished?
return null; return null;
} }
@ -274,14 +275,20 @@ public class GameMainQuest {
//todo maybe try to accept quests if there is no active quest and no rewind target? //todo maybe try to accept quests if there is no active quest and no rewind target?
//tryAcceptSubQuests(QuestTrigger.QUEST_COND_NONE, "", 0); //tryAcceptSubQuests(QuestTrigger.QUEST_COND_NONE, "", 0);
} }
return highestActiveQuest;
}
public GameQuest getRewindTarget(){
var highestActiveQuest = getHighestActiveQuest();
var highestOrder = highestActiveQuest.getQuestData().getOrder(); var highestOrder = highestActiveQuest.getQuestData().getOrder();
var rewindTarget = getChildQuests().values().stream() var rewindTarget = getChildQuests().values().stream()
.filter(q -> q.getQuestData() != null) .filter(q -> q.getQuestData() != null)
.filter(q -> q.getQuestData().isRewind() && q.getQuestData().getOrder() <= highestOrder) .filter(q -> q.getQuestData().isRewind() && q.getQuestData().getOrder() <= highestOrder)
.max(Comparator.comparingInt(a -> a.getQuestData().getOrder())) .max(Comparator.comparingInt(a -> a.getQuestData().getOrder()))
.orElse(highestActiveQuest); .orElse(null);
Grasscutter.getLogger().trace("For quest {}, the rewindTarget is {}", this.getParentQuestId(), rewindTarget);
return rewindTarget; return rewindTarget;
} }
@ -354,10 +361,10 @@ public class GameMainQuest {
return true; return true;
} }
public void checkProgress(){ public void checkProgress(boolean shouldReset) {
for (var quest : getChildQuests().values()){ for (var quest : getChildQuests().values()){
if(quest.getState() == QuestState.QUEST_STATE_UNFINISHED) { if(quest.getState() == QuestState.QUEST_STATE_UNFINISHED) {
questManager.checkQuestAlreadyFullfilled(quest); questManager.checkQuestAlreadyFulfilled(quest, shouldReset);
} }
} }
} }

View File

@ -101,7 +101,7 @@ public class GameQuest {
triggerStateEvents(); triggerStateEvents();
getQuestData().getBeginExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam())); getQuestData().getBeginExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
getOwner().getQuestManager().checkQuestAlreadyFullfilled(this); getOwner().getQuestManager().checkQuestAlreadyFulfilled(this, true);
getOwner().getDungeonEntryManager().checkQuestForDungeonEntryUpdate(this); getOwner().getDungeonEntryManager().checkQuestForDungeonEntryUpdate(this);
Grasscutter.getLogger().debug("Quest {} is started", subQuestId); Grasscutter.getLogger().debug("Quest {} is started", subQuestId);

View File

@ -1,20 +1,18 @@
package emu.grasscutter.game.quest; package emu.grasscutter.game.quest;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.quest.SubQuestData; import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.BasePlayerManager; import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.enums.*; import emu.grasscutter.game.quest.enums.LogicType;
import emu.grasscutter.game.quest.enums.ParentQuestState;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.World; import emu.grasscutter.game.world.World;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
import emu.grasscutter.server.packet.send.PacketQuestGlobalVarNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import io.netty.util.concurrent.FastThreadLocalThread; import io.netty.util.concurrent.FastThreadLocalThread;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -24,6 +22,12 @@ import lombok.val;
import org.anime_game_servers.core.gi.enums.QuestState; import org.anime_game_servers.core.gi.enums.QuestState;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class QuestManager extends BasePlayerManager { public class QuestManager extends BasePlayerManager {
@ -106,18 +110,20 @@ public class QuestManager extends BasePlayerManager {
public void onLogin() { public void onLogin() {
List<GameMainQuest> activeQuests = getActiveMainQuests(); List<GameMainQuest> activeQuests = getActiveMainQuests();
for (GameMainQuest quest : activeQuests) { for (GameMainQuest quest : activeQuests) {
val rewindTarget = quest.getRewindTarget(); var rewindTarget = quest.getRewindTarget();
if (rewindTarget == null) rewindTarget = quest.getHighestActiveQuest();
val finalRewindTarget = rewindTarget;
List<Position> rewindPos = quest.rewind(); // <pos, rotation> List<Position> rewindPos = quest.rewind(); // <pos, rotation>
if (rewindPos != null) { if (rewindPos != null) {
getPlayer().getPosition().set(rewindPos.get(0)); getPlayer().getPosition().set(rewindPos.get(0));
getPlayer().getRotation().set(rewindPos.get(1)); getPlayer().getRotation().set(rewindPos.get(1));
} }
//execute all the beginExec before the rewind target on UNFINISHED quests on login only //execute all the beginExec before the rewind target on UNFINISHED quests on login only
quest.getChildQuests().values().stream().filter(p -> p.getQuestData().getOrder() < rewindTarget.getQuestData().getOrder() quest.getChildQuests().values().stream().filter(p -> p.getQuestData().getOrder() < finalRewindTarget.getQuestData().getOrder()
&& p.getState().getValue() == QuestState.QUEST_STATE_UNFINISHED.getValue()).forEach(q -> { && p.getState().getValue() == QuestState.QUEST_STATE_UNFINISHED.getValue()).forEach(q -> {
q.getQuestData().getBeginExec().forEach(e -> getPlayer().getServer().getQuestSystem().triggerExec(q, e, e.getParam())); q.getQuestData().getBeginExec().forEach(e -> getPlayer().getServer().getQuestSystem().triggerExec(q, e, e.getParam()));
}); });
quest.checkProgress(); quest.checkProgress(false);
} }
player.getActivityManager().triggerActivityConditions(); player.getActivityManager().triggerActivityConditions();
} }
@ -301,7 +307,7 @@ public class QuestManager extends BasePlayerManager {
// Forcefully start // Forcefully start
quest.start(); quest.start();
checkQuestAlreadyFullfilled(quest); checkQuestAlreadyFulfilled(quest, true);
return quest; return quest;
} }
@ -406,7 +412,7 @@ public class QuestManager extends BasePlayerManager {
* TODO move content checks to use static informations where possible to allow direct already fulfilled checking * TODO move content checks to use static informations where possible to allow direct already fulfilled checking
* @param quest * @param quest
*/ */
public void checkQuestAlreadyFullfilled(GameQuest quest){ public void checkQuestAlreadyFulfilled(GameQuest quest, boolean shouldReset) {
Grasscutter.getGameServer().getScheduler().scheduleDelayedTask(() -> { Grasscutter.getGameServer().getScheduler().scheduleDelayedTask(() -> {
val questSystem = getPlayer().getServer().getQuestSystem(); val questSystem = getPlayer().getServer().getQuestSystem();
val questData = quest.getQuestData(); val questData = quest.getQuestData();
@ -415,13 +421,13 @@ public class QuestManager extends BasePlayerManager {
return; return;
} }
val shouldFinish = questSystem.initialCheckContent(quest, quest.getFinishProgressList(), questData.getFinishCond(), questData.getFinishCondComb()); val shouldFinish = questSystem.initialCheckContent(quest, quest.getFinishProgressList(), questData.getFinishCond(), questData.getFinishCondComb(), shouldReset);
if (shouldFinish) { if (shouldFinish) {
quest.finish(false); quest.finish(false);
return; return;
} }
if(!questData.getFailCond().isEmpty()) { if(!questData.getFailCond().isEmpty()) {
val shouldFail = questSystem.initialCheckContent(quest, quest.getFailProgressList(), questData.getFailCond(), questData.getFailCondComb()); val shouldFail = questSystem.initialCheckContent(quest, quest.getFailProgressList(), questData.getFailCond(), questData.getFailCondComb(), shouldReset);
if (shouldFail) { if (shouldFail) {
quest.fail(); quest.fail();
} }

View File

@ -101,7 +101,7 @@ public class QuestSystem extends BaseGameSystem {
} }
public boolean initialCheckContent(GameQuest quest, int[] curProgress, public boolean initialCheckContent(GameQuest quest, int[] curProgress,
List<QuestContentCondition> conditions, LogicType logicType){ List<QuestContentCondition> conditions, LogicType logicType, boolean shouldReset) {
val owner = quest.getOwner(); val owner = quest.getOwner();
var changed = false; var changed = false;
int[] finished = new int[conditions.size()]; int[] finished = new int[conditions.size()];
@ -112,17 +112,18 @@ public class QuestSystem extends BaseGameSystem {
val startingProgress = curProgress[i]; val startingProgress = curProgress[i];
int result = handler.initialCheck(quest, quest.getQuestData(), condition); int result = handler.initialCheck(quest, quest.getQuestData(), condition);
curProgress[i] = result; if (shouldReset) curProgress[i] = result;
if (startingProgress != result) { if (startingProgress != result) {
changed = true; changed = true;
} }
finished[i] = handler.checkProgress(quest, condition, result) ? 1 : 0; finished[i] = handler.checkProgress(quest, condition, result) ? 1 : 0;
} }
if(changed) { if (changed) {
owner.getSession().send(new PacketQuestProgressUpdateNotify(quest)); owner.getSession().send(new PacketQuestProgressUpdateNotify(quest));
} }
return LogicType.calculate(logicType, finished); return LogicType.calculate(logicType, finished);
} }
public boolean checkAndUpdateContent(GameQuest quest, int[] curProgress, public boolean checkAndUpdateContent(GameQuest quest, int[] curProgress,
List<QuestContentCondition> conditions, LogicType logicType, List<QuestContentCondition> conditions, LogicType logicType,
QuestContent condType, String paramStr, int... params){ QuestContent condType, String paramStr, int... params){
@ -146,6 +147,7 @@ public class QuestSystem extends BaseGameSystem {
} }
if(changed) { if(changed) {
owner.getSession().send(new PacketQuestProgressUpdateNotify(quest)); owner.getSession().send(new PacketQuestProgressUpdateNotify(quest));
quest.save();
} }
return LogicType.calculate(logicType, finished); return LogicType.calculate(logicType, finished);
} }