mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-25 20:03:01 +08:00
Continue merging quests
(pt. 2)
This commit is contained in:
parent
97ee71bcf4
commit
644f1b3ab9
@ -1,57 +1,59 @@
|
|||||||
package emu.grasscutter.command;
|
package emu.grasscutter.command;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
public class CommandHelpers {
|
public class CommandHelpers {
|
||||||
public static final Pattern lvlRegex =
|
public static final Pattern lvlRegex =
|
||||||
Pattern.compile("(?<!\\w)l(?:vl?)?(\\d+)"); // Java doesn't have raw string literals :(
|
Pattern.compile("(?<!\\w)l(?:vl?)?(\\d+)"); // Java doesn't have raw string literals :(
|
||||||
public static final Pattern amountRegex =
|
public static final Pattern amountRegex =
|
||||||
Pattern.compile("((?<=(?<!\\w)x)\\d+|\\d+(?=x)(?!x\\d))");
|
Pattern.compile("((?<=(?<!\\w)x)\\d+|\\d+(?=x)(?!x\\d))");
|
||||||
public static final Pattern refineRegex = Pattern.compile("(?<!\\w)r(\\d+)");
|
public static final Pattern refineRegex = Pattern.compile("(?<!\\w)r(\\d+)");
|
||||||
public static final Pattern rankRegex = Pattern.compile("(\\d+)\\*");
|
public static final Pattern rankRegex = Pattern.compile("(\\d+)\\*");
|
||||||
public static final Pattern constellationRegex = Pattern.compile("(?<!\\w)c(\\d+)");
|
public static final Pattern constellationRegex = Pattern.compile("(?<!\\w)c(\\d+)");
|
||||||
public static final Pattern skillLevelRegex = Pattern.compile("sl(\\d+)");
|
public static final Pattern skillLevelRegex = Pattern.compile("sl(\\d+)");
|
||||||
public static final Pattern stateRegex = Pattern.compile("state(\\d+)");
|
public static final Pattern stateRegex = Pattern.compile("state(\\d+)");
|
||||||
public static final Pattern blockRegex = Pattern.compile("blk(\\d+)");
|
public static final Pattern blockRegex = Pattern.compile("blk(\\d+)");
|
||||||
public static final Pattern groupRegex = Pattern.compile("grp(\\d+)");
|
public static final Pattern groupRegex = Pattern.compile("grp(\\d+)");
|
||||||
public static final Pattern configRegex = Pattern.compile("cfg(\\d+)");
|
public static final Pattern configRegex = Pattern.compile("cfg(\\d+)");
|
||||||
public static final Pattern hpRegex = Pattern.compile("(?<!\\w)hp(\\d+)");
|
public static final Pattern hpRegex = Pattern.compile("(?<!\\w)hp(\\d+)");
|
||||||
public static final Pattern maxHPRegex = Pattern.compile("maxhp(\\d+)");
|
public static final Pattern maxHPRegex = Pattern.compile("maxhp(\\d+)");
|
||||||
public static final Pattern atkRegex = Pattern.compile("atk(\\d+)");
|
public static final Pattern atkRegex = Pattern.compile("atk(\\d+)");
|
||||||
public static final Pattern defRegex = Pattern.compile("def(\\d+)");
|
public static final Pattern defRegex = Pattern.compile("def(\\d+)");
|
||||||
public static final Pattern aiRegex = Pattern.compile("ai(\\d+)");
|
public static final Pattern aiRegex = Pattern.compile("ai(\\d+)");
|
||||||
|
public static final Pattern sceneRegex = Pattern.compile("scene(\\d+)");
|
||||||
public static int matchIntOrNeg(Pattern pattern, String arg) {
|
public static final Pattern suiteRegex = Pattern.compile("suite(\\d+)");
|
||||||
Matcher match = pattern.matcher(arg);
|
|
||||||
if (match.find()) {
|
public static int matchIntOrNeg(Pattern pattern, String arg) {
|
||||||
return Integer.parseInt(
|
Matcher match = pattern.matcher(arg);
|
||||||
match.group(
|
if (match.find()) {
|
||||||
1)); // This should be exception-safe as only \d+ can be passed to it (i.e. non-empty
|
return Integer.parseInt(
|
||||||
// string of pure digits)
|
match.group(
|
||||||
}
|
1)); // This should be exception-safe as only \d+ can be passed to it (i.e. non-empty
|
||||||
return -1;
|
// string of pure digits)
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
public static <T> List<String> parseIntParameters(
|
}
|
||||||
List<String> args, @Nonnull T params, Map<Pattern, BiConsumer<T, Integer>> map) {
|
|
||||||
args.removeIf(
|
public static <T> List<String> parseIntParameters(
|
||||||
arg -> {
|
List<String> args, @Nonnull T params, Map<Pattern, BiConsumer<T, Integer>> map) {
|
||||||
var argL = arg.toLowerCase();
|
args.removeIf(
|
||||||
boolean deleteArg = false;
|
arg -> {
|
||||||
for (var entry : map.entrySet()) {
|
var argL = arg.toLowerCase();
|
||||||
int argNum = matchIntOrNeg(entry.getKey(), argL);
|
boolean deleteArg = false;
|
||||||
if (argNum != -1) {
|
for (var entry : map.entrySet()) {
|
||||||
entry.getValue().accept(params, argNum);
|
int argNum = matchIntOrNeg(entry.getKey(), argL);
|
||||||
deleteArg = true;
|
if (argNum != -1) {
|
||||||
}
|
entry.getValue().accept(params, argNum);
|
||||||
}
|
deleteArg = true;
|
||||||
return deleteArg;
|
}
|
||||||
});
|
}
|
||||||
return args;
|
return deleteArg;
|
||||||
}
|
});
|
||||||
}
|
return args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
import emu.grasscutter.command.Command;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketCutsceneBeginNotify;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
label = "cutscene",
|
||||||
|
aliases = {"c"},
|
||||||
|
usage = {"[<cutsceneId>]"},
|
||||||
|
permission = "player.group",
|
||||||
|
permissionTargeted = "player.group.others")
|
||||||
|
public final class CutsceneCommand implements CommandHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
sendUsageMessage(sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
val cutSceneId = Integer.parseInt(args.get(0));
|
||||||
|
targetPlayer.sendPacket(new PacketCutsceneBeginNotify(cutSceneId));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
|
import emu.grasscutter.command.Command;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketScenePlayerSoundNotify;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
label = "sound",
|
||||||
|
aliases = {"s", "audio"},
|
||||||
|
usage = {"[<audioname>] [<x><y><z>]"},
|
||||||
|
permission = "player.group",
|
||||||
|
permissionTargeted = "player.group.others")
|
||||||
|
public final class SoundCommand implements CommandHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
sendUsageMessage(sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
val soundName = args.get(0);
|
||||||
|
var playPosition = targetPlayer.getPosition();
|
||||||
|
if (args.size() == 4) {
|
||||||
|
try {
|
||||||
|
float x, y, z;
|
||||||
|
x = Float.parseFloat(args.get(1));
|
||||||
|
y = Float.parseFloat(args.get(2));
|
||||||
|
z = Float.parseFloat(args.get(3));
|
||||||
|
playPosition = new Position(x, y, z);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (args.size() > 1) {
|
||||||
|
sendUsageMessage(sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
targetPlayer
|
||||||
|
.getScene()
|
||||||
|
.broadcastPacket(new PacketScenePlayerSoundNotify(playPosition, soundName, 1));
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,13 @@
|
|||||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
|
||||||
public class InTimeTrigger extends ChallengeTrigger {
|
public class InTimeTrigger extends ChallengeTrigger {
|
||||||
@Override
|
@Override
|
||||||
public void onCheckTimeout(WorldChallenge challenge) {
|
public void onCheckTimeout(WorldChallenge challenge) {
|
||||||
var current = System.currentTimeMillis();
|
var current = challenge.getScene().getSceneTimeSeconds();
|
||||||
if (current - challenge.getStartedAt() > challenge.getTimeLimit() * 1000L) {
|
if (current - challenge.getStartedAt() > challenge.getTimeLimit()) {
|
||||||
challenge.fail();
|
challenge.fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import emu.grasscutter.game.activity.ActivityManager;
|
|||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||||
import emu.grasscutter.game.battlepass.BattlePassManager;
|
import emu.grasscutter.game.battlepass.BattlePassManager;
|
||||||
|
import emu.grasscutter.game.entity.EntityAvatar;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
import emu.grasscutter.game.expedition.ExpeditionInfo;
|
import emu.grasscutter.game.expedition.ExpeditionInfo;
|
||||||
import emu.grasscutter.game.friends.FriendsList;
|
import emu.grasscutter.game.friends.FriendsList;
|
||||||
@ -44,6 +45,7 @@ import emu.grasscutter.game.props.ClimateType;
|
|||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
import emu.grasscutter.game.quest.QuestManager;
|
import emu.grasscutter.game.quest.QuestManager;
|
||||||
|
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.game.shop.ShopLimit;
|
import emu.grasscutter.game.shop.ShopLimit;
|
||||||
import emu.grasscutter.game.tower.TowerData;
|
import emu.grasscutter.game.tower.TowerData;
|
||||||
@ -51,20 +53,18 @@ import emu.grasscutter.game.tower.TowerManager;
|
|||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.game.world.World;
|
import emu.grasscutter.game.world.World;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.proto.*;
|
||||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||||
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
||||||
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
|
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
|
||||||
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass;
|
|
||||||
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
|
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
|
||||||
import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass;
|
|
||||||
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
|
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
|
||||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||||
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
|
|
||||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||||
import emu.grasscutter.net.proto.SocialShowAvatarInfoOuterClass;
|
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
|
||||||
import emu.grasscutter.scripts.data.SceneRegion;
|
import emu.grasscutter.scripts.data.SceneRegion;
|
||||||
import emu.grasscutter.server.event.player.PlayerJoinEvent;
|
import emu.grasscutter.server.event.player.PlayerJoinEvent;
|
||||||
import emu.grasscutter.server.event.player.PlayerQuitEvent;
|
import emu.grasscutter.server.event.player.PlayerQuitEvent;
|
||||||
@ -88,6 +88,7 @@ import java.time.ZoneId;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||||
|
|
||||||
@ -472,6 +473,8 @@ public class Player {
|
|||||||
|
|
||||||
// Handle open state unlocks from level-up.
|
// Handle open state unlocks from level-up.
|
||||||
this.getProgressManager().tryUnlockOpenStates();
|
this.getProgressManager().tryUnlockOpenStates();
|
||||||
|
this.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_PLAYER_LEVEL_UP, level);
|
||||||
|
this.getQuestManager().queueEvent(QuestCond.QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER, level);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -613,7 +616,7 @@ public class Player {
|
|||||||
|
|
||||||
public void onEnterRegion(SceneRegion region) {
|
public void onEnterRegion(SceneRegion region) {
|
||||||
getQuestManager().forEachActiveQuest(quest -> {
|
getQuestManager().forEachActiveQuest(quest -> {
|
||||||
if (quest.getTriggers().containsKey("ENTER_REGION_" + region.config_id)) {
|
if (quest.getTriggerData() != null && quest.getTriggers().containsKey("ENTER_REGION_"+ region.config_id)) {
|
||||||
// If trigger hasn't been fired yet
|
// If trigger hasn't been fired yet
|
||||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("ENTER_REGION_" + region.config_id, true))) {
|
if (!Boolean.TRUE.equals(quest.getTriggers().put("ENTER_REGION_" + region.config_id, true))) {
|
||||||
//getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
//getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||||
@ -824,6 +827,85 @@ public class Player {
|
|||||||
addAvatar(avatar, true);
|
addAvatar(avatar, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addAvatar(int avatarId) {
|
||||||
|
// I dont see why we cant do this lolz
|
||||||
|
addAvatar(new Avatar(avatarId), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getTrialAvatarParam (int trialAvatarId) {
|
||||||
|
if (GameData.getTrialAvatarCustomData().isEmpty()) { // use default data if custom data not available
|
||||||
|
if (GameData.getTrialAvatarDataMap().get(trialAvatarId) == null) return List.of();
|
||||||
|
|
||||||
|
return GameData.getTrialAvatarDataMap().get(trialAvatarId)
|
||||||
|
.getTrialAvatarParamList();
|
||||||
|
}
|
||||||
|
// use custom data
|
||||||
|
if (GameData.getTrialAvatarCustomData().get(trialAvatarId) == null) return List.of();
|
||||||
|
|
||||||
|
var trialCustomParams = GameData.getTrialAvatarCustomData().get(trialAvatarId).getTrialAvatarParamList();
|
||||||
|
return trialCustomParams.isEmpty() ? List.of() : Stream.of(trialCustomParams.get(0).split(";")).map(Integer::parseInt).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addTrialAvatar(int trialAvatarId, GrantReason reason, int questMainId){
|
||||||
|
List<Integer> trialAvatarBasicParam = getTrialAvatarParam(trialAvatarId);
|
||||||
|
if (trialAvatarBasicParam.isEmpty()) return false;
|
||||||
|
|
||||||
|
Avatar avatar = new Avatar(trialAvatarBasicParam.get(0));
|
||||||
|
if (avatar.getAvatarData() == null || !hasSentLoginPackets()) return false;
|
||||||
|
|
||||||
|
avatar.setOwner(this);
|
||||||
|
// Add trial weapons and relics
|
||||||
|
avatar.setTrialAvatarInfo(trialAvatarBasicParam.get(1), trialAvatarId, reason, questMainId);
|
||||||
|
avatar.equipTrialItems();
|
||||||
|
// Recalc stats
|
||||||
|
avatar.recalcStats();
|
||||||
|
|
||||||
|
// Packet, mimic official server behaviour, add to player's bag but not saving to db
|
||||||
|
sendPacket(new PacketAvatarAddNotify(avatar, false));
|
||||||
|
// add to avatar to temporary trial team
|
||||||
|
getTeamManager().addAvatarToTrialTeam(avatar);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addTrialAvatarForQuest(int trialAvatarId, int questMainId) {
|
||||||
|
// TODO: Find method for 'setupTrialAvatarTeamForQuest'.
|
||||||
|
getTeamManager().setupTrialAvatars(true);
|
||||||
|
if (!addTrialAvatar(
|
||||||
|
trialAvatarId,
|
||||||
|
GrantReason.GRANT_REASON_BY_QUEST,
|
||||||
|
questMainId)) return false;
|
||||||
|
getTeamManager().trialAvatarTeamPostUpdate();
|
||||||
|
// Packet, mimic official server behaviour, neccessary to stop player from modifying team
|
||||||
|
sendPacket(new PacketAvatarTeamUpdateNotify(this));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTrialAvatarsForActivity(List<Integer> trialAvatarIds) {
|
||||||
|
getTeamManager().setupTrialAvatars(false);
|
||||||
|
trialAvatarIds.forEach(trialAvatarId -> addTrialAvatar(
|
||||||
|
trialAvatarId,
|
||||||
|
GrantReason.GRANT_REASON_BY_TRIAL_AVATAR_ACTIVITY,
|
||||||
|
0));
|
||||||
|
getTeamManager().trialAvatarTeamPostUpdate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeTrialAvatarForQuest(int trialAvatarId) {
|
||||||
|
if (!getTeamManager().isUsingTrialTeam()) return false;
|
||||||
|
|
||||||
|
sendPacket(new PacketAvatarDelNotify(List.of(getTeamManager().getTrialAvatarGuid(trialAvatarId))));
|
||||||
|
getTeamManager().removeTrialAvatarTeam(trialAvatarId);
|
||||||
|
sendPacket(new PacketAvatarTeamUpdateNotify());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTrialAvatarForActivity() {
|
||||||
|
if (!getTeamManager().isUsingTrialTeam()) return;
|
||||||
|
|
||||||
|
sendPacket(new PacketAvatarDelNotify(getTeamManager().getActiveTeam().stream()
|
||||||
|
.map(x -> x.getAvatar().getGuid()).toList()));
|
||||||
|
getTeamManager().removeTrialAvatarTeam();
|
||||||
|
}
|
||||||
|
|
||||||
public void addFlycloak(int flycloakId) {
|
public void addFlycloak(int flycloakId) {
|
||||||
this.getFlyCloakList().add(flycloakId);
|
this.getFlyCloakList().add(flycloakId);
|
||||||
this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId));
|
this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId));
|
||||||
@ -834,6 +916,11 @@ public class Player {
|
|||||||
this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId));
|
this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addPersonalLine(int personalLineId) {
|
||||||
|
this.getPersonalLineList().add(personalLineId);
|
||||||
|
session.getPlayer().getQuestManager().queueEvent(QuestCond.QUEST_COND_PERSONAL_LINE_UNLOCK, personalLineId);
|
||||||
|
}
|
||||||
|
|
||||||
public void addNameCard(int nameCardId) {
|
public void addNameCard(int nameCardId) {
|
||||||
this.getNameCardList().add(nameCardId);
|
this.getNameCardList().add(nameCardId);
|
||||||
this.sendPacket(new PacketUnlockNameCardNotify(nameCardId));
|
this.sendPacket(new PacketUnlockNameCardNotify(nameCardId));
|
||||||
@ -863,6 +950,46 @@ public class Player {
|
|||||||
this.getServer().getChatSystem().sendPrivateMessageFromServer(getUid(), message.toString());
|
this.getServer().getChatSystem().sendPrivateMessageFromServer(getUid(), message.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAvatarsAbilityForScene(Scene scene) {
|
||||||
|
try {
|
||||||
|
var levelEntityConfig = scene.getSceneData().getLevelEntityConfig();
|
||||||
|
var config = GameData.getConfigLevelEntityDataMap().get(levelEntityConfig);
|
||||||
|
if (config == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> avatarIds = scene.getSceneData().getSpecifiedAvatarList();
|
||||||
|
List<EntityAvatar> specifiedAvatarList = getTeamManager().getActiveTeam();
|
||||||
|
|
||||||
|
if (avatarIds != null && avatarIds.size() > 0){
|
||||||
|
// certain scene could limit specifc avatars' entry
|
||||||
|
specifiedAvatarList.clear();
|
||||||
|
for (int id : avatarIds){
|
||||||
|
var avatar = getAvatars().getAvatarById(id);
|
||||||
|
if (avatar == null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
specifiedAvatarList.add(new EntityAvatar(scene, avatar));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (EntityAvatar entityAvatar : specifiedAvatarList){
|
||||||
|
var avatarData = entityAvatar.getAvatar().getAvatarData();
|
||||||
|
if (avatarData == null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
avatarData.buildEmbryo();
|
||||||
|
if (config.getAvatarAbilities() == null){
|
||||||
|
continue; // continue and not break because has to rebuild ability for the next avatar if any
|
||||||
|
}
|
||||||
|
for (var abilities : config.getAvatarAbilities()){
|
||||||
|
avatarData.getAbilities().add(Utils.abilityHash(abilities.getAbilityName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e){
|
||||||
|
Grasscutter.getLogger().error("Error applying level entity config for scene {}", scene.getSceneData().getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Sends a message to another player.
|
* Sends a message to another player.
|
||||||
*
|
*
|
||||||
@ -982,7 +1109,7 @@ public class Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SocialDetail.Builder social = SocialDetail.newBuilder()
|
return SocialDetail.newBuilder()
|
||||||
.setUid(this.getUid())
|
.setUid(this.getUid())
|
||||||
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()))
|
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()))
|
||||||
.setNickname(this.getNickname())
|
.setNickname(this.getNickname())
|
||||||
@ -996,7 +1123,6 @@ public class Player {
|
|||||||
.addAllShowNameCardIdList(this.getShowNameCardInfoList())
|
.addAllShowNameCardIdList(this.getShowNameCardInfoList())
|
||||||
.setFinishAchievementNum(this.getFinishedAchievementNum())
|
.setFinishAchievementNum(this.getFinishedAchievementNum())
|
||||||
.setFriendEnterHomeOptionValue(this.getHome() == null ? 0 : this.getHome().getEnterHomeOption());
|
.setFriendEnterHomeOptionValue(this.getHome() == null ? 0 : this.getHome().getEnterHomeOption());
|
||||||
return social;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFinishedAchievementNum() {
|
public int getFinishedAchievementNum() {
|
||||||
@ -1135,6 +1261,8 @@ public class Player {
|
|||||||
|
|
||||||
// Home resources
|
// Home resources
|
||||||
this.getHome().updateHourlyResources(this);
|
this.getHome().updateHourlyResources(this);
|
||||||
|
|
||||||
|
this.getQuestManager().onTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void doDailyReset() {
|
private synchronized void doDailyReset() {
|
||||||
@ -1200,12 +1328,17 @@ public class Player {
|
|||||||
this.achievements = Achievements.getByPlayer(this);
|
this.achievements = Achievements.getByPlayer(this);
|
||||||
this.getAvatars().loadFromDatabase();
|
this.getAvatars().loadFromDatabase();
|
||||||
this.getInventory().loadFromDatabase();
|
this.getInventory().loadFromDatabase();
|
||||||
this.loadBattlePassManager(); // Call before avatar postLoad to avoid null pointer
|
|
||||||
this.getAvatars().postLoad(); // Needs to be called after inventory is handled
|
|
||||||
|
|
||||||
this.getFriendsList().loadFromDatabase();
|
this.getFriendsList().loadFromDatabase();
|
||||||
this.getMailHandler().loadFromDatabase();
|
this.getMailHandler().loadFromDatabase();
|
||||||
this.getQuestManager().loadFromDatabase();
|
this.getQuestManager().loadFromDatabase();
|
||||||
|
|
||||||
|
this.loadBattlePassManager();
|
||||||
|
this.getAvatars().postLoad(); // Needs to be called after inventory is handled
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPlayerBorn() {
|
||||||
|
getQuestManager().onPlayerBorn();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLogin() {
|
public void onLogin() {
|
||||||
|
@ -1,108 +1,109 @@
|
|||||||
package emu.grasscutter.game.props;
|
package emu.grasscutter.game.props;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import emu.grasscutter.scripts.constants.IntValueEnum;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import java.util.HashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import java.util.Map;
|
import java.util.HashMap;
|
||||||
import java.util.stream.Stream;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Stream;
|
||||||
public enum EntityType {
|
|
||||||
None(0),
|
public enum EntityType implements IntValueEnum {
|
||||||
Avatar(1),
|
None(0),
|
||||||
Monster(2),
|
Avatar(1),
|
||||||
Bullet(3),
|
Monster(2),
|
||||||
AttackPhyisicalUnit(4),
|
Bullet(3),
|
||||||
AOE(5),
|
AttackPhyisicalUnit(4),
|
||||||
Camera(6),
|
AOE(5),
|
||||||
EnviroArea(7),
|
Camera(6),
|
||||||
Equip(8),
|
EnviroArea(7),
|
||||||
MonsterEquip(9),
|
Equip(8),
|
||||||
Grass(10),
|
MonsterEquip(9),
|
||||||
Level(11),
|
Grass(10),
|
||||||
NPC(12),
|
Level(11),
|
||||||
TransPointFirst(13),
|
NPC(12),
|
||||||
TransPointFirstGadget(14),
|
TransPointFirst(13),
|
||||||
TransPointSecond(15),
|
TransPointFirstGadget(14),
|
||||||
TransPointSecondGadget(16),
|
TransPointSecond(15),
|
||||||
DropItem(17),
|
TransPointSecondGadget(16),
|
||||||
Field(18),
|
DropItem(17),
|
||||||
Gadget(19),
|
Field(18),
|
||||||
Water(20),
|
Gadget(19),
|
||||||
GatherPoint(21),
|
Water(20),
|
||||||
GatherObject(22),
|
GatherPoint(21),
|
||||||
AirflowField(23),
|
GatherObject(22),
|
||||||
SpeedupField(24),
|
AirflowField(23),
|
||||||
Gear(25),
|
SpeedupField(24),
|
||||||
Chest(26),
|
Gear(25),
|
||||||
EnergyBall(27),
|
Chest(26),
|
||||||
ElemCrystal(28),
|
EnergyBall(27),
|
||||||
Timeline(29),
|
ElemCrystal(28),
|
||||||
Worktop(30),
|
Timeline(29),
|
||||||
Team(31),
|
Worktop(30),
|
||||||
Platform(32),
|
Team(31),
|
||||||
AmberWind(33),
|
Platform(32),
|
||||||
EnvAnimal(34),
|
AmberWind(33),
|
||||||
SealGadget(35),
|
EnvAnimal(34),
|
||||||
Tree(36),
|
SealGadget(35),
|
||||||
Bush(37),
|
Tree(36),
|
||||||
QuestGadget(38),
|
Bush(37),
|
||||||
Lightning(39),
|
QuestGadget(38),
|
||||||
RewardPoint(40),
|
Lightning(39),
|
||||||
RewardStatue(41),
|
RewardPoint(40),
|
||||||
MPLevel(42),
|
RewardStatue(41),
|
||||||
WindSeed(43),
|
MPLevel(42),
|
||||||
MpPlayRewardPoint(44),
|
WindSeed(43),
|
||||||
ViewPoint(45),
|
MpPlayRewardPoint(44),
|
||||||
RemoteAvatar(46),
|
ViewPoint(45),
|
||||||
GeneralRewardPoint(47),
|
RemoteAvatar(46),
|
||||||
PlayTeam(48),
|
GeneralRewardPoint(47),
|
||||||
OfferingGadget(49),
|
PlayTeam(48),
|
||||||
EyePoint(50),
|
OfferingGadget(49),
|
||||||
MiracleRing(51),
|
EyePoint(50),
|
||||||
Foundation(52),
|
MiracleRing(51),
|
||||||
WidgetGadget(53),
|
Foundation(52),
|
||||||
Vehicle(54),
|
WidgetGadget(53),
|
||||||
SubEquip(55),
|
Vehicle(54),
|
||||||
FishRod(56),
|
SubEquip(55),
|
||||||
CustomTile(57),
|
FishRod(56),
|
||||||
FishPool(58),
|
CustomTile(57),
|
||||||
CustomGadget(59),
|
FishPool(58),
|
||||||
BlackMud(60),
|
CustomGadget(59),
|
||||||
RoguelikeOperatorGadget(61),
|
BlackMud(60),
|
||||||
NightCrowGadget(62),
|
RoguelikeOperatorGadget(61),
|
||||||
Projector(63),
|
NightCrowGadget(62),
|
||||||
Screen(64),
|
Projector(63),
|
||||||
EchoShell(65),
|
Screen(64),
|
||||||
UIInteractGadget(66),
|
EchoShell(65),
|
||||||
PlaceHolder(99);
|
UIInteractGadget(66),
|
||||||
|
PlaceHolder(99);
|
||||||
private static final Int2ObjectMap<EntityType> map = new Int2ObjectOpenHashMap<>();
|
|
||||||
private static final Map<String, EntityType> stringMap = new HashMap<>();
|
private static final Int2ObjectMap<EntityType> map = new Int2ObjectOpenHashMap<>();
|
||||||
|
private static final Map<String, EntityType> stringMap = new HashMap<>();
|
||||||
static {
|
|
||||||
Stream.of(values())
|
static {
|
||||||
.forEach(
|
Stream.of(values())
|
||||||
e -> {
|
.forEach(
|
||||||
map.put(e.getValue(), e);
|
e -> {
|
||||||
stringMap.put(e.name(), e);
|
map.put(e.getValue(), e);
|
||||||
});
|
stringMap.put(e.name(), e);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
private final int value;
|
|
||||||
|
private final int value;
|
||||||
EntityType(int value) {
|
|
||||||
this.value = value;
|
EntityType(int value) {
|
||||||
}
|
this.value = value;
|
||||||
|
}
|
||||||
public static EntityType getTypeByValue(int value) {
|
|
||||||
return map.getOrDefault(value, None);
|
public static EntityType getTypeByValue(int value) {
|
||||||
}
|
return map.getOrDefault(value, None);
|
||||||
|
}
|
||||||
public static EntityType getTypeByName(String name) {
|
|
||||||
return stringMap.getOrDefault(name, None);
|
public static EntityType getTypeByName(String name) {
|
||||||
}
|
return stringMap.getOrDefault(name, None);
|
||||||
|
}
|
||||||
public int getValue() {
|
|
||||||
return value;
|
public int getValue() {
|
||||||
}
|
return value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -16,7 +16,6 @@ import emu.grasscutter.database.DatabaseHelper;
|
|||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.quest.enums.*;
|
import emu.grasscutter.game.quest.enums.*;
|
||||||
import emu.grasscutter.game.world.World;
|
|
||||||
import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
|
import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
|
||||||
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
|
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
|
||||||
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
|
||||||
@ -41,7 +40,7 @@ public class GameMainQuest {
|
|||||||
@Getter private int[] questVars;
|
@Getter private int[] questVars;
|
||||||
@Getter private long[] timeVar;
|
@Getter private long[] timeVar;
|
||||||
//QuestUpdateQuestVarReq is sent in two stages...
|
//QuestUpdateQuestVarReq is sent in two stages...
|
||||||
@Getter private List<Integer> questVarsUpdate;
|
private List<Integer> questVarsUpdate;
|
||||||
@Getter private ParentQuestState state;
|
@Getter private ParentQuestState state;
|
||||||
@Getter private boolean isFinished;
|
@Getter private boolean isFinished;
|
||||||
@Getter List<QuestGroupSuite> questGroupSuites;
|
@Getter List<QuestGroupSuite> questGroupSuites;
|
||||||
@ -67,6 +66,13 @@ public class GameMainQuest {
|
|||||||
addAllChildQuests();
|
addAllChildQuests();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Integer> getQuestVarsUpdate() {
|
||||||
|
if(questVarsUpdate == null){
|
||||||
|
questVarsUpdate = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return questVarsUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
private void addAllChildQuests() {
|
private void addAllChildQuests() {
|
||||||
List<Integer> subQuestIds = Arrays.stream(GameData.getMainQuestDataMap().get(this.parentQuestId).getSubQuests()).map(SubQuestData::getSubId).toList();
|
List<Integer> subQuestIds = Arrays.stream(GameData.getMainQuestDataMap().get(this.parentQuestId).getSubQuests()).map(SubQuestData::getSubId).toList();
|
||||||
for (Integer subQuestId : subQuestIds) {
|
for (Integer subQuestId : subQuestIds) {
|
||||||
@ -166,10 +172,10 @@ public class GameMainQuest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handoff main quest
|
// handoff main quest
|
||||||
if (mainQuestData.getSuggestTrackMainQuestList() != null) {
|
// if (mainQuestData.getSuggestTrackMainQuestList() != null) {
|
||||||
Arrays.stream(mainQuestData.getSuggestTrackMainQuestList())
|
// Arrays.stream(mainQuestData.getSuggestTrackMainQuestList())
|
||||||
.forEach(getQuestManager()::startMainQuest);
|
// .forEach(getQuestManager()::startMainQuest);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
//TODO
|
//TODO
|
||||||
public void fail() {}
|
public void fail() {}
|
||||||
@ -181,9 +187,9 @@ public class GameMainQuest {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if(rewindPositions.isEmpty()){
|
// if(rewindPositions.isEmpty()){
|
||||||
addRewindPoints();
|
// this.addRewindPoints();
|
||||||
}*/
|
// }
|
||||||
|
|
||||||
List<Position> posAndRot = new ArrayList<>();
|
List<Position> posAndRot = new ArrayList<>();
|
||||||
if(hasRewindPosition(targetQuest.getSubQuestId(), posAndRot)){
|
if(hasRewindPosition(targetQuest.getSubQuestId(), posAndRot)){
|
||||||
@ -198,8 +204,8 @@ public class GameMainQuest {
|
|||||||
if (hasRewindPosition(quest.getSubQuestId(), posAndRot)) {
|
if (hasRewindPosition(quest.getSubQuestId(), posAndRot)) {
|
||||||
return posAndRot;
|
return posAndRot;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,308 +1,312 @@
|
|||||||
package emu.grasscutter.game.quest;
|
package emu.grasscutter.game.quest;
|
||||||
|
|
||||||
import dev.morphia.annotations.Entity;
|
import dev.morphia.annotations.Entity;
|
||||||
import dev.morphia.annotations.Transient;
|
import dev.morphia.annotations.Transient;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.ChapterData;
|
import emu.grasscutter.data.excels.ChapterData;
|
||||||
import emu.grasscutter.data.excels.QuestData;
|
import emu.grasscutter.data.excels.QuestData;
|
||||||
import emu.grasscutter.data.excels.TriggerExcelConfigData;
|
import emu.grasscutter.data.excels.TriggerExcelConfigData;
|
||||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.game.quest.enums.QuestState;
|
import emu.grasscutter.game.quest.enums.QuestState;
|
||||||
import emu.grasscutter.net.proto.ChapterStateOuterClass;
|
import emu.grasscutter.net.proto.ChapterStateOuterClass;
|
||||||
import emu.grasscutter.net.proto.QuestOuterClass.Quest;
|
import emu.grasscutter.net.proto.QuestOuterClass.Quest;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.server.packet.send.PacketChapterStateNotify;
|
import emu.grasscutter.server.packet.send.PacketChapterStateNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketDelQuestNotify;
|
import emu.grasscutter.server.packet.send.PacketDelQuestNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.script.Bindings;
|
import javax.script.Bindings;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
public class GameQuest {
|
public class GameQuest {
|
||||||
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
||||||
@Transient @Getter private QuestData questData;
|
@Transient @Getter private QuestData questData;
|
||||||
|
|
||||||
@Getter private int subQuestId;
|
@Getter private int subQuestId;
|
||||||
@Getter private int mainQuestId;
|
@Getter private int mainQuestId;
|
||||||
@Getter @Setter public QuestState state;
|
@Getter @Setter public QuestState state;
|
||||||
|
|
||||||
@Getter @Setter private int startTime;
|
@Getter @Setter private int startTime;
|
||||||
@Getter @Setter private int acceptTime;
|
@Getter @Setter private int acceptTime;
|
||||||
@Getter @Setter private int finishTime;
|
@Getter @Setter private int finishTime;
|
||||||
|
|
||||||
@Getter @Setter private long startGameDay;
|
@Getter @Setter private long startGameDay;
|
||||||
|
|
||||||
@Getter private int[] finishProgressList;
|
@Getter private int[] finishProgressList;
|
||||||
@Getter private int[] failProgressList;
|
@Getter private int[] failProgressList;
|
||||||
@Transient @Getter private Map<String, TriggerExcelConfigData> triggerData;
|
@Transient @Getter private Map<String, TriggerExcelConfigData> triggerData;
|
||||||
@Getter private Map<String, Boolean> triggers;
|
@Getter private Map<String, Boolean> triggers;
|
||||||
private transient Bindings bindings;
|
private transient Bindings bindings;
|
||||||
|
|
||||||
@Deprecated // Morphia only. Do not use.
|
@Deprecated // Morphia only. Do not use.
|
||||||
public GameQuest() {}
|
public GameQuest() {}
|
||||||
|
|
||||||
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
|
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
|
||||||
this.mainQuest = mainQuest;
|
this.mainQuest = mainQuest;
|
||||||
this.subQuestId = questData.getId();
|
this.subQuestId = questData.getId();
|
||||||
this.mainQuestId = questData.getMainId();
|
this.mainQuestId = questData.getMainId();
|
||||||
this.questData = questData;
|
this.questData = questData;
|
||||||
this.state = QuestState.QUEST_STATE_UNSTARTED;
|
this.state = QuestState.QUEST_STATE_UNSTARTED;
|
||||||
this.triggerData = new HashMap<>();
|
this.triggerData = new HashMap<>();
|
||||||
this.triggers = new HashMap<>();
|
this.triggers = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
clearProgress(false);
|
clearProgress(false);
|
||||||
this.acceptTime = Utils.getCurrentSeconds();
|
this.acceptTime = Utils.getCurrentSeconds();
|
||||||
this.startTime = this.acceptTime;
|
this.startTime = this.acceptTime;
|
||||||
this.startGameDay = getOwner().getWorld().getTotalGameTimeDays();
|
this.startGameDay = getOwner().getWorld().getTotalGameTimeDays();
|
||||||
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
||||||
val triggerCond =
|
val triggerCond =
|
||||||
questData.getFinishCond().stream()
|
questData.getFinishCond().stream()
|
||||||
.filter(p -> p.getType() == QuestContent.QUEST_CONTENT_TRIGGER_FIRE)
|
.filter(p -> p.getType() == QuestContent.QUEST_CONTENT_TRIGGER_FIRE)
|
||||||
.toList();
|
.toList();
|
||||||
if (triggerCond.size() > 0) {
|
if (triggerCond.size() > 0) {
|
||||||
for (val cond : triggerCond) {
|
for (val cond : triggerCond) {
|
||||||
TriggerExcelConfigData newTrigger =
|
TriggerExcelConfigData newTrigger =
|
||||||
GameData.getTriggerExcelConfigDataMap().get(cond.getParam()[0]);
|
GameData.getTriggerExcelConfigDataMap().get(cond.getParam()[0]);
|
||||||
if (newTrigger != null) {
|
if (newTrigger != null) {
|
||||||
if (this.triggerData == null) {
|
if (this.triggerData == null) {
|
||||||
this.triggerData = new HashMap<>();
|
this.triggerData = new HashMap<>();
|
||||||
}
|
}
|
||||||
triggerData.put(newTrigger.getTriggerName(), newTrigger);
|
triggerData.put(newTrigger.getTriggerName(), newTrigger);
|
||||||
triggers.put(newTrigger.getTriggerName(), false);
|
triggers.put(newTrigger.getTriggerName(), false);
|
||||||
SceneGroup group = SceneGroup.of(newTrigger.getGroupId()).load(newTrigger.getSceneId());
|
SceneGroup group = SceneGroup.of(newTrigger.getGroupId()).load(newTrigger.getSceneId());
|
||||||
getOwner()
|
getOwner()
|
||||||
.getWorld()
|
.getWorld()
|
||||||
.getSceneById(newTrigger.getSceneId())
|
.getSceneById(newTrigger.getSceneId())
|
||||||
.loadTriggerFromGroup(group, newTrigger.getTriggerName());
|
.loadTriggerFromGroup(group, newTrigger.getTriggerName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||||
|
|
||||||
if (ChapterData.beginQuestChapterMap.containsKey(subQuestId)) {
|
if (ChapterData.beginQuestChapterMap.containsKey(subQuestId)) {
|
||||||
getOwner()
|
getOwner()
|
||||||
.sendPacket(
|
.sendPacket(
|
||||||
new PacketChapterStateNotify(
|
new PacketChapterStateNotify(
|
||||||
ChapterData.beginQuestChapterMap.get(subQuestId).getId(),
|
ChapterData.beginQuestChapterMap.get(subQuestId).getId(),
|
||||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_BEGIN));
|
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_BEGIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some subQuests and talks become active when some other subQuests are unfinished (even from
|
// Some subQuests and talks become active when some other subQuests are unfinished (even from
|
||||||
// different MainQuests)
|
// different MainQuests)
|
||||||
getOwner()
|
getOwner()
|
||||||
.getQuestManager()
|
.getQuestManager()
|
||||||
.queueEvent(
|
.queueEvent(
|
||||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||||
getSubQuestId(),
|
getSubQuestId(),
|
||||||
getState().getValue(),
|
getState().getValue(),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
getOwner()
|
getOwner()
|
||||||
.getQuestManager()
|
.getQuestManager()
|
||||||
.queueEvent(
|
.queueEvent(
|
||||||
QuestCond.QUEST_COND_STATE_EQUAL, getSubQuestId(), getState().getValue(), 0, 0, 0);
|
QuestCond.QUEST_COND_STATE_EQUAL, getSubQuestId(), getState().getValue(), 0, 0, 0);
|
||||||
|
|
||||||
getQuestData()
|
getQuestData()
|
||||||
.getBeginExec()
|
.getBeginExec()
|
||||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||||
getOwner().getQuestManager().checkQuestAlreadyFullfilled(this);
|
getOwner().getQuestManager().checkQuestAlreadyFullfilled(this);
|
||||||
|
|
||||||
Grasscutter.getLogger().debug("Quest {} is started", subQuestId);
|
Grasscutter.getLogger().debug("Quest {} is started", subQuestId);
|
||||||
}
|
this.save();
|
||||||
|
}
|
||||||
public String getTriggerNameById(int id) {
|
|
||||||
TriggerExcelConfigData trigger = GameData.getTriggerExcelConfigDataMap().get(id);
|
public String getTriggerNameById(int id) {
|
||||||
if (trigger != null) {
|
TriggerExcelConfigData trigger = GameData.getTriggerExcelConfigDataMap().get(id);
|
||||||
String triggerName = trigger.getTriggerName();
|
if (trigger != null) {
|
||||||
return triggerName;
|
String triggerName = trigger.getTriggerName();
|
||||||
}
|
return triggerName;
|
||||||
// return empty string if can't find trigger
|
}
|
||||||
return "";
|
// return empty string if can't find trigger
|
||||||
}
|
return "";
|
||||||
|
}
|
||||||
public Player getOwner() {
|
|
||||||
return this.getMainQuest().getOwner();
|
public Player getOwner() {
|
||||||
}
|
return this.getMainQuest().getOwner();
|
||||||
|
}
|
||||||
public void setConfig(QuestData config) {
|
|
||||||
if (config == null || getSubQuestId() != config.getId()) return;
|
public void setConfig(QuestData config) {
|
||||||
this.questData = config;
|
if (config == null || getSubQuestId() != config.getId()) return;
|
||||||
}
|
this.questData = config;
|
||||||
|
}
|
||||||
public void setFinishProgress(int index, int value) {
|
|
||||||
finishProgressList[index] = value;
|
public void setFinishProgress(int index, int value) {
|
||||||
}
|
finishProgressList[index] = value;
|
||||||
|
}
|
||||||
public void setFailProgress(int index, int value) {
|
|
||||||
failProgressList[index] = value;
|
public void setFailProgress(int index, int value) {
|
||||||
}
|
failProgressList[index] = value;
|
||||||
|
}
|
||||||
public boolean clearProgress(boolean notifyDelete) {
|
|
||||||
// TODO improve
|
public boolean clearProgress(boolean notifyDelete) {
|
||||||
var oldState = state;
|
// TODO improve
|
||||||
if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) {
|
var oldState = state;
|
||||||
this.finishProgressList = new int[questData.getFinishCond().size()];
|
if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) {
|
||||||
}
|
this.finishProgressList = new int[questData.getFinishCond().size()];
|
||||||
|
}
|
||||||
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
|
|
||||||
this.failProgressList = new int[questData.getFailCond().size()];
|
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
|
||||||
}
|
this.failProgressList = new int[questData.getFailCond().size()];
|
||||||
setState(QuestState.QUEST_STATE_UNSTARTED);
|
}
|
||||||
finishTime = 0;
|
setState(QuestState.QUEST_STATE_UNSTARTED);
|
||||||
acceptTime = 0;
|
finishTime = 0;
|
||||||
startTime = 0;
|
acceptTime = 0;
|
||||||
if (oldState == QuestState.QUEST_STATE_UNSTARTED) {
|
startTime = 0;
|
||||||
return false;
|
if (oldState == QuestState.QUEST_STATE_UNSTARTED) {
|
||||||
}
|
return false;
|
||||||
if (notifyDelete) {
|
}
|
||||||
getOwner().sendPacket(new PacketDelQuestNotify(getSubQuestId()));
|
if (notifyDelete) {
|
||||||
}
|
getOwner().sendPacket(new PacketDelQuestNotify(getSubQuestId()));
|
||||||
save();
|
}
|
||||||
return true;
|
save();
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
public void finish() {
|
|
||||||
this.state = QuestState.QUEST_STATE_FINISHED;
|
public void finish() {
|
||||||
this.finishTime = Utils.getCurrentSeconds();
|
this.state = QuestState.QUEST_STATE_FINISHED;
|
||||||
|
this.finishTime = Utils.getCurrentSeconds();
|
||||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
|
||||||
|
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||||
if (getQuestData().isFinishParent()) {
|
|
||||||
// This quest finishes the questline - the main quest will also save the quest to db, so we
|
if (getQuestData().isFinishParent()) {
|
||||||
// don't have to call save() here
|
// This quest finishes the questline - the main quest will also save the quest to db, so we
|
||||||
getMainQuest().finish();
|
// don't have to call save() here
|
||||||
}
|
getMainQuest().finish();
|
||||||
|
}
|
||||||
getQuestData()
|
|
||||||
.getFinishExec()
|
getQuestData()
|
||||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
.getFinishExec()
|
||||||
// Some subQuests have conditions that subQuests are finished (even from different MainQuests)
|
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||||
getOwner()
|
// Some subQuests have conditions that subQuests are finished (even from different MainQuests)
|
||||||
.getQuestManager()
|
getOwner()
|
||||||
.queueEvent(
|
.getQuestManager()
|
||||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
.queueEvent(
|
||||||
this.subQuestId,
|
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||||
this.state.getValue(),
|
this.subQuestId,
|
||||||
0,
|
this.state.getValue(),
|
||||||
0,
|
0,
|
||||||
0);
|
0,
|
||||||
getOwner()
|
0);
|
||||||
.getQuestManager()
|
getOwner()
|
||||||
.queueEvent(QuestContent.QUEST_CONTENT_FINISH_PLOT, this.subQuestId, 0);
|
.getQuestManager()
|
||||||
getOwner()
|
.queueEvent(QuestContent.QUEST_CONTENT_FINISH_PLOT, this.subQuestId, 0);
|
||||||
.getQuestManager()
|
getOwner()
|
||||||
.queueEvent(
|
.getQuestManager()
|
||||||
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
.queueEvent(
|
||||||
getOwner()
|
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||||
.getScene()
|
getOwner()
|
||||||
.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST, getSubQuestId());
|
.getScene()
|
||||||
|
.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST, getSubQuestId());
|
||||||
getOwner().getProgressManager().tryUnlockOpenStates();
|
|
||||||
|
getOwner().getProgressManager().tryUnlockOpenStates();
|
||||||
if (ChapterData.endQuestChapterMap.containsKey(subQuestId)) {
|
|
||||||
mainQuest
|
if (ChapterData.endQuestChapterMap.containsKey(subQuestId)) {
|
||||||
.getOwner()
|
mainQuest
|
||||||
.sendPacket(
|
.getOwner()
|
||||||
new PacketChapterStateNotify(
|
.sendPacket(
|
||||||
ChapterData.endQuestChapterMap.get(subQuestId).getId(),
|
new PacketChapterStateNotify(
|
||||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_END));
|
ChapterData.endQuestChapterMap.get(subQuestId).getId(),
|
||||||
}
|
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_END));
|
||||||
|
}
|
||||||
// hard coding to give amber
|
|
||||||
if (getQuestData().getSubId() == 35402) {
|
// hard coding to give amber
|
||||||
getOwner().getInventory().addItem(1021, 1, ActionReason.QuestItem); // amber item id
|
if (getQuestData().getSubId() == 35402) {
|
||||||
}
|
getOwner().getInventory().addItem(1021, 1, ActionReason.QuestItem); // amber item id
|
||||||
Grasscutter.getLogger().debug("Quest {} is finished", subQuestId);
|
}
|
||||||
}
|
|
||||||
|
this.save();
|
||||||
// TODO
|
|
||||||
public void fail() {
|
Grasscutter.getLogger().debug("Quest {} is finished", subQuestId);
|
||||||
this.state = QuestState.QUEST_STATE_FAILED;
|
}
|
||||||
this.finishTime = Utils.getCurrentSeconds();
|
|
||||||
|
// TODO
|
||||||
this.getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
public void fail() {
|
||||||
|
this.state = QuestState.QUEST_STATE_FAILED;
|
||||||
// Some subQuests have conditions that subQuests fail (even from different MainQuests)
|
this.finishTime = Utils.getCurrentSeconds();
|
||||||
this.getOwner()
|
|
||||||
.getQuestManager()
|
this.getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||||
.queueEvent(
|
|
||||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
// Some subQuests have conditions that subQuests fail (even from different MainQuests)
|
||||||
this.subQuestId,
|
this.getOwner()
|
||||||
this.state.getValue(),
|
.getQuestManager()
|
||||||
0,
|
.queueEvent(
|
||||||
0,
|
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||||
0);
|
this.subQuestId,
|
||||||
this.getOwner()
|
this.state.getValue(),
|
||||||
.getQuestManager()
|
0,
|
||||||
.queueEvent(
|
0,
|
||||||
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
0);
|
||||||
|
this.getOwner()
|
||||||
this.getQuestData()
|
.getQuestManager()
|
||||||
.getFailExec()
|
.queueEvent(
|
||||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||||
|
|
||||||
if (this.getQuestData().getTrialAvatarList() != null) {
|
this.getQuestData()
|
||||||
this.getQuestData()
|
.getFailExec()
|
||||||
.getTrialAvatarList()
|
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||||
.forEach(t -> this.getOwner().getTeamManager().removeTrialAvatar(t));
|
|
||||||
}
|
if (this.getQuestData().getTrialAvatarList() != null) {
|
||||||
|
this.getQuestData()
|
||||||
Grasscutter.getLogger().debug("Quest {} is failed", subQuestId);
|
.getTrialAvatarList()
|
||||||
}
|
.forEach(t -> this.getOwner().getTeamManager().removeTrialAvatar(t));
|
||||||
|
}
|
||||||
// Return true if it did the rewind
|
|
||||||
public boolean rewind(boolean notifyDelete) {
|
Grasscutter.getLogger().debug("Quest {} is failed", subQuestId);
|
||||||
getMainQuest().getChildQuests().values().stream()
|
}
|
||||||
.filter(p -> p.getQuestData().getOrder() > this.getQuestData().getOrder())
|
|
||||||
.forEach(
|
// Return true if it did the rewind
|
||||||
q -> {
|
public boolean rewind(boolean notifyDelete) {
|
||||||
q.clearProgress(notifyDelete);
|
getMainQuest().getChildQuests().values().stream()
|
||||||
});
|
.filter(p -> p.getQuestData().getOrder() > this.getQuestData().getOrder())
|
||||||
clearProgress(notifyDelete);
|
.forEach(
|
||||||
this.start();
|
q -> {
|
||||||
return true;
|
q.clearProgress(notifyDelete);
|
||||||
}
|
});
|
||||||
|
clearProgress(notifyDelete);
|
||||||
public void save() {
|
this.start();
|
||||||
getMainQuest().save();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Quest toProto() {
|
public void save() {
|
||||||
Quest.Builder proto =
|
getMainQuest().save();
|
||||||
Quest.newBuilder()
|
}
|
||||||
.setQuestId(getSubQuestId())
|
|
||||||
.setState(getState().getValue())
|
public Quest toProto() {
|
||||||
.setParentQuestId(getMainQuestId())
|
Quest.Builder proto =
|
||||||
.setStartTime(getStartTime())
|
Quest.newBuilder()
|
||||||
.setStartGameTime(438)
|
.setQuestId(getSubQuestId())
|
||||||
.setAcceptTime(getAcceptTime());
|
.setState(getState().getValue())
|
||||||
|
.setParentQuestId(getMainQuestId())
|
||||||
if (getFinishProgressList() != null) {
|
.setStartTime(getStartTime())
|
||||||
for (int i : getFinishProgressList()) {
|
.setStartGameTime(438)
|
||||||
proto.addFinishProgressList(i);
|
.setAcceptTime(getAcceptTime());
|
||||||
}
|
|
||||||
}
|
if (getFinishProgressList() != null) {
|
||||||
|
for (int i : getFinishProgressList()) {
|
||||||
if (getFailProgressList() != null) {
|
proto.addFinishProgressList(i);
|
||||||
for (int i : getFailProgressList()) {
|
}
|
||||||
proto.addFailProgressList(i);
|
}
|
||||||
}
|
|
||||||
}
|
if (getFailProgressList() != null) {
|
||||||
|
for (int i : getFailProgressList()) {
|
||||||
return proto.build();
|
proto.addFailProgressList(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return proto.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,10 +7,7 @@ import emu.grasscutter.data.excels.QuestData;
|
|||||||
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.ParentQuestState;
|
import emu.grasscutter.game.quest.enums.*;
|
||||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
|
||||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
|
||||||
import emu.grasscutter.game.quest.enums.QuestState;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||||
@ -19,6 +16,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.LinkedBlockingDeque;
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
@ -86,13 +84,17 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
this.mainQuests = new Int2ObjectOpenHashMap<>();
|
this.mainQuests = new Int2ObjectOpenHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO store user value set on enable
|
||||||
|
public boolean isQuestingEnabled() {
|
||||||
|
return Grasscutter.getConfig().server.game.gameOptions.questing;
|
||||||
|
}
|
||||||
|
|
||||||
public void onPlayerBorn() {
|
public void onPlayerBorn() {
|
||||||
// TODO scan the quest and start the quest with acceptCond fulfilled
|
// TODO scan the quest and start the quest with acceptCond fulfilled
|
||||||
// The off send 3 request in that order: 1. FinishedParentQuestNotify, 2. QuestListNotify, 3. ServerCondMeetQuestListUpdateNotify
|
// The off send 3 request in that order: 1. FinishedParentQuestNotify, 2. QuestListNotify, 3. ServerCondMeetQuestListUpdateNotify
|
||||||
|
|
||||||
List<GameMainQuest> newQuests = this.addMultMainQuests(newPlayerMainQuests);
|
if(this.isQuestingEnabled()) {
|
||||||
for (GameMainQuest mainQuest : newQuests) {
|
this.enableQuests();
|
||||||
startMainQuest(mainQuest.getParentQuestId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(newQuests));
|
//getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(newQuests));
|
||||||
@ -117,6 +119,8 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
}
|
}
|
||||||
quest.checkProgress();
|
quest.checkProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player.getActivityManager().triggerActivityConditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTick(){
|
public void onTick(){
|
||||||
@ -163,7 +167,8 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void enableQuests() {
|
public void enableQuests() {
|
||||||
onPlayerBorn();
|
this.triggerEvent(QuestCond.QUEST_COND_NONE, null, 0);
|
||||||
|
this.triggerEvent(QuestCond.QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER, null, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -256,6 +261,11 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return addQuest(questConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameQuest addQuest(@Nonnull QuestData questConfig) {
|
||||||
|
|
||||||
// Main quest
|
// Main quest
|
||||||
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());
|
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());
|
||||||
|
|
||||||
@ -265,7 +275,7 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sub quest
|
// Sub quest
|
||||||
GameQuest quest = mainQuest.getChildQuestById(questId);
|
GameQuest quest = mainQuest.getChildQuestById(questConfig.getSubId());
|
||||||
|
|
||||||
// Forcefully start
|
// Forcefully start
|
||||||
quest.start();
|
quest.start();
|
||||||
@ -286,13 +296,12 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
.map(MainQuestData.SubQuestData::getSubId)
|
.map(MainQuestData.SubQuestData::getSubId)
|
||||||
.ifPresent(this::addQuest);
|
.ifPresent(this::addQuest);
|
||||||
//TODO find a better way then hardcoding to detect needed required quests
|
//TODO find a better way then hardcoding to detect needed required quests
|
||||||
if(mainQuestId == 355){
|
// if (mainQuestId == 355){
|
||||||
startMainQuest(361);
|
// startMainQuest(361);
|
||||||
startMainQuest(418);
|
// startMainQuest(418);
|
||||||
startMainQuest(423);
|
// startMainQuest(423);
|
||||||
startMainQuest(20509);
|
// startMainQuest(20509);
|
||||||
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public void queueEvent(QuestCond condType, int... params) {
|
public void queueEvent(QuestCond condType, int... params) {
|
||||||
queueEvent(condType, "", params);
|
queueEvent(condType, "", params);
|
||||||
@ -312,13 +321,42 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
|
|
||||||
public void triggerEvent(QuestCond condType, String paramStr, int... params) {
|
public void triggerEvent(QuestCond condType, String paramStr, int... params) {
|
||||||
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
||||||
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
|
var potentialQuests = GameData.getQuestDataByConditions(condType, params[0], paramStr);
|
||||||
.filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
|
if(potentialQuests == null){
|
||||||
.toList();
|
return;
|
||||||
for (GameMainQuest mainquest : checkMainQuests) {
|
|
||||||
mainquest.tryAcceptSubQuests(condType, paramStr, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var questSystem = getPlayer().getServer().getQuestSystem();
|
||||||
|
var owner = getPlayer();
|
||||||
|
|
||||||
|
potentialQuests.forEach(questData -> {
|
||||||
|
if(this.wasSubQuestStarted(questData)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
val acceptCond = questData.getAcceptCond();
|
||||||
|
int[] accept = new int[acceptCond.size()];
|
||||||
|
for (int i = 0; i < acceptCond.size(); i++) {
|
||||||
|
val condition = acceptCond.get(i);
|
||||||
|
boolean result = questSystem.triggerCondition(owner, questData, condition, paramStr, params);
|
||||||
|
accept[i] = result ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept);
|
||||||
|
|
||||||
|
if (shouldAccept){
|
||||||
|
GameQuest quest = owner.getQuestManager().addQuest(questData);
|
||||||
|
Grasscutter.getLogger().debug("Added quest {} result {}", questData.getSubId(), quest !=null);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean wasSubQuestStarted(QuestData questData) {
|
||||||
|
var quest = getQuestById(questData.getId());
|
||||||
|
if(quest == null) return false;
|
||||||
|
|
||||||
|
return quest.state != QuestState.QUEST_STATE_UNSTARTED;
|
||||||
|
}
|
||||||
|
|
||||||
public void triggerEvent(QuestContent condType, String paramStr, int... params) {
|
public void triggerEvent(QuestContent condType, String paramStr, int... params) {
|
||||||
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
||||||
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
|
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
|
||||||
@ -332,6 +370,7 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO maybe trigger them delayed to allow basic communication finish first
|
* TODO maybe trigger them delayed to allow basic communication finish first
|
||||||
|
* 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 checkQuestAlreadyFullfilled(GameQuest quest){
|
||||||
@ -355,6 +394,7 @@ public class QuestManager extends BasePlayerManager {
|
|||||||
queueEvent(condition.getType(), condition.getParam()[0], condition.getParam()[1]);
|
queueEvent(condition.getType(), condition.getParam()[0], condition.getParam()[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case QUEST_CONTENT_PLAYER_LEVEL_UP -> queueEvent(condition.getType(), player.getLevel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 1);
|
}, 1);
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
package emu.grasscutter.game.quest.content;
|
package emu.grasscutter.game.quest.content;
|
||||||
|
|
||||||
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT;
|
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT;
|
||||||
|
|
||||||
import emu.grasscutter.data.excels.QuestData;
|
import emu.grasscutter.data.excels.QuestData;
|
||||||
import emu.grasscutter.game.quest.GameQuest;
|
import emu.grasscutter.game.quest.GameQuest;
|
||||||
import emu.grasscutter.game.quest.QuestValueContent;
|
import emu.grasscutter.game.quest.QuestValueContent;
|
||||||
|
|
||||||
@QuestValueContent(QUEST_CONTENT_UNLOCK_TRANS_POINT)
|
@QuestValueContent(QUEST_CONTENT_UNLOCK_TRANS_POINT)
|
||||||
public class ContentUnlockTransPoint extends BaseContent {
|
public class ContentUnlockTransPoint extends BaseContent {
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(
|
public boolean execute(
|
||||||
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||||
return condition.getParam()[0] == params[0] && condition.getParam()[1] == params[1];
|
var sceneId = condition.getParam()[0];
|
||||||
}
|
var scenePointId = condition.getParam()[1];
|
||||||
}
|
var scenePoints = quest.getOwner().getUnlockedScenePoints().get(sceneId);
|
||||||
|
return scenePoints != null && scenePoints.contains(scenePointId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
package emu.grasscutter.game.quest.exec;
|
package emu.grasscutter.game.quest.exec;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.excels.QuestData;
|
import emu.grasscutter.data.excels.QuestData;
|
||||||
import emu.grasscutter.game.quest.GameQuest;
|
import emu.grasscutter.game.quest.GameQuest;
|
||||||
import emu.grasscutter.game.quest.QuestValueExec;
|
import emu.grasscutter.game.quest.QuestValueExec;
|
||||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||||
|
|
||||||
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY)
|
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY)
|
||||||
public class ExecAddCurAvatarEnergy extends QuestExecHandler {
|
public class ExecAddCurAvatarEnergy extends QuestExecHandler {
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||||
Grasscutter.getLogger().info("Energy refilled");
|
Grasscutter.getLogger().debug("Energy refilled");
|
||||||
return quest.getOwner().getEnergyManager().refillActiveEnergy();
|
return quest.getOwner().getEnergyManager().refillActiveEnergy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,12 +319,14 @@ public class World implements Iterable<Player> {
|
|||||||
var newScene = this.getSceneById(teleportProperties.getSceneId());
|
var newScene = this.getSceneById(teleportProperties.getSceneId());
|
||||||
newScene.addPlayer(player);
|
newScene.addPlayer(player);
|
||||||
player.setAvatarsAbilityForScene(newScene);
|
player.setAvatarsAbilityForScene(newScene);
|
||||||
|
|
||||||
// Dungeon
|
// Dungeon
|
||||||
// Dungeon system is handling this already
|
// Dungeon system is handling this already
|
||||||
// if(dungeonData!=null){
|
// if(dungeonData!=null){
|
||||||
// var dungeonManager = new DungeonManager(newScene, dungeonData);
|
// var dungeonManager = new DungeonManager(newScene, dungeonData);
|
||||||
// dungeonManager.startDungeon();
|
// dungeonManager.startDungeon();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
SceneConfig config = newScene.getScriptManager().getConfig();
|
SceneConfig config = newScene.getScriptManager().getConfig();
|
||||||
if (teleportProperties.getTeleportTo() == null && config != null) {
|
if (teleportProperties.getTeleportTo() == null && config != null) {
|
||||||
if (config.born_pos != null) {
|
if (config.born_pos != null) {
|
||||||
|
@ -1,153 +1,160 @@
|
|||||||
package emu.grasscutter.game.world;
|
package emu.grasscutter.game.world;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.DataLoader;
|
import emu.grasscutter.data.DataLoader;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.InvestigationMonsterData;
|
import emu.grasscutter.data.excels.InvestigationMonsterData;
|
||||||
import emu.grasscutter.data.excels.RewardPreviewData;
|
import emu.grasscutter.data.excels.RewardPreviewData;
|
||||||
import emu.grasscutter.data.excels.world.WorldLevelData;
|
import emu.grasscutter.data.excels.world.WorldLevelData;
|
||||||
import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler;
|
import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler;
|
||||||
import emu.grasscutter.game.entity.gadget.chest.ChestInteractHandler;
|
import emu.grasscutter.game.entity.gadget.chest.ChestInteractHandler;
|
||||||
import emu.grasscutter.game.entity.gadget.chest.NormalChestInteractHandler;
|
import emu.grasscutter.game.entity.gadget.chest.NormalChestInteractHandler;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.InvestigationMonsterOuterClass;
|
import emu.grasscutter.net.proto.InvestigationMonsterOuterClass;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.scripts.data.SceneMonster;
|
import emu.grasscutter.scripts.data.SceneMonster;
|
||||||
import emu.grasscutter.server.game.BaseGameSystem;
|
import emu.grasscutter.server.game.BaseGameSystem;
|
||||||
import emu.grasscutter.server.game.GameServer;
|
import emu.grasscutter.server.game.GameServer;
|
||||||
import java.util.HashMap;
|
import org.luaj.vm2.LuaError;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.HashMap;
|
||||||
import java.util.Objects;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
public class WorldDataSystem extends BaseGameSystem {
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
|
|
||||||
private final Map<String, SceneGroup> sceneInvestigationGroupMap; // <sceneId_groupId, Group>
|
public class WorldDataSystem extends BaseGameSystem {
|
||||||
|
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
|
||||||
public WorldDataSystem(GameServer server) {
|
private final Map<String, SceneGroup> sceneInvestigationGroupMap; // <sceneId_groupId, Group>
|
||||||
super(server);
|
|
||||||
this.chestInteractHandlerMap = new HashMap<>();
|
public WorldDataSystem(GameServer server) {
|
||||||
this.sceneInvestigationGroupMap = new ConcurrentHashMap<>();
|
super(server);
|
||||||
|
this.chestInteractHandlerMap = new HashMap<>();
|
||||||
loadChestConfig();
|
this.sceneInvestigationGroupMap = new ConcurrentHashMap<>();
|
||||||
}
|
|
||||||
|
loadChestConfig();
|
||||||
public synchronized void loadChestConfig() {
|
}
|
||||||
// set the special chest first
|
|
||||||
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
|
public synchronized void loadChestConfig() {
|
||||||
|
// set the special chest first
|
||||||
try {
|
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
|
||||||
DataLoader.loadList("ChestReward.json", ChestReward.class)
|
|
||||||
.forEach(
|
try {
|
||||||
reward ->
|
DataLoader.loadList("ChestReward.json", ChestReward.class)
|
||||||
reward
|
.forEach(
|
||||||
.getObjNames()
|
reward ->
|
||||||
.forEach(
|
reward
|
||||||
name ->
|
.getObjNames()
|
||||||
chestInteractHandlerMap.computeIfAbsent(
|
.forEach(
|
||||||
name, x -> new NormalChestInteractHandler(reward))));
|
name ->
|
||||||
} catch (Exception e) {
|
chestInteractHandlerMap.computeIfAbsent(
|
||||||
Grasscutter.getLogger().error("Unable to load chest reward config.", e);
|
name, x -> new NormalChestInteractHandler(reward))));
|
||||||
}
|
} catch (Exception e) {
|
||||||
}
|
Grasscutter.getLogger().error("Unable to load chest reward config.", e);
|
||||||
|
}
|
||||||
public Map<String, ChestInteractHandler> getChestInteractHandlerMap() {
|
}
|
||||||
return chestInteractHandlerMap;
|
|
||||||
}
|
public Map<String, ChestInteractHandler> getChestInteractHandlerMap() {
|
||||||
|
return chestInteractHandlerMap;
|
||||||
public RewardPreviewData getRewardByBossId(int monsterId) {
|
}
|
||||||
var investigationMonsterData =
|
|
||||||
GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
public RewardPreviewData getRewardByBossId(int monsterId) {
|
||||||
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
|
var investigationMonsterData =
|
||||||
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
|
GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||||
.findFirst();
|
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
|
||||||
|
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
|
||||||
if (investigationMonsterData.isEmpty()) {
|
.findFirst();
|
||||||
return null;
|
|
||||||
}
|
if (investigationMonsterData.isEmpty()) {
|
||||||
return GameData.getRewardPreviewDataMap()
|
return null;
|
||||||
.get(investigationMonsterData.get().getRewardPreviewId());
|
}
|
||||||
}
|
return GameData.getRewardPreviewDataMap()
|
||||||
|
.get(investigationMonsterData.get().getRewardPreviewId());
|
||||||
private SceneGroup getInvestigationGroup(int sceneId, int groupId) {
|
}
|
||||||
var key = sceneId + "_" + groupId;
|
|
||||||
if (!sceneInvestigationGroupMap.containsKey(key)) {
|
private SceneGroup getInvestigationGroup(int sceneId, int groupId) {
|
||||||
var group = SceneGroup.of(groupId).load(sceneId);
|
var key = sceneId + "_" + groupId;
|
||||||
sceneInvestigationGroupMap.putIfAbsent(key, group);
|
if (!sceneInvestigationGroupMap.containsKey(key)) {
|
||||||
return group;
|
try {
|
||||||
}
|
var group = SceneGroup.of(groupId).load(sceneId);
|
||||||
return sceneInvestigationGroupMap.get(key);
|
sceneInvestigationGroupMap.putIfAbsent(key, group);
|
||||||
}
|
return group;
|
||||||
|
} catch (LuaError luaError) {
|
||||||
public int getMonsterLevel(SceneMonster monster, World world) {
|
Grasscutter.getLogger()
|
||||||
// Calculate level
|
.error("failed to get investigationGroup {} in scene{}:", groupId, sceneId, luaError);
|
||||||
int level = monster.level;
|
}
|
||||||
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel());
|
}
|
||||||
|
return sceneInvestigationGroupMap.get(key);
|
||||||
if (worldLevelData != null) {
|
}
|
||||||
level = worldLevelData.getMonsterLevel();
|
|
||||||
}
|
public int getMonsterLevel(SceneMonster monster, World world) {
|
||||||
return level;
|
// Calculate level
|
||||||
}
|
int level = monster.level;
|
||||||
|
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel());
|
||||||
private InvestigationMonsterOuterClass.InvestigationMonster getInvestigationMonster(
|
|
||||||
Player player, InvestigationMonsterData imd) {
|
if (worldLevelData != null) {
|
||||||
if (imd.getGroupIdList().isEmpty() || imd.getMonsterIdList().isEmpty()) {
|
level = worldLevelData.getMonsterLevel();
|
||||||
return null;
|
}
|
||||||
}
|
return level;
|
||||||
|
}
|
||||||
var groupId = imd.getGroupIdList().get(0);
|
|
||||||
var monsterId = imd.getMonsterIdList().get(0);
|
private InvestigationMonsterOuterClass.InvestigationMonster getInvestigationMonster(
|
||||||
var sceneId = imd.getCityData().getSceneId();
|
Player player, InvestigationMonsterData imd) {
|
||||||
var group = getInvestigationGroup(sceneId, groupId);
|
if (imd.getGroupIdList().isEmpty() || imd.getMonsterIdList().isEmpty()) {
|
||||||
|
return null;
|
||||||
if (group == null || group.monsters == null) {
|
}
|
||||||
return null;
|
|
||||||
}
|
var groupId = imd.getGroupIdList().get(0);
|
||||||
|
var monsterId = imd.getMonsterIdList().get(0);
|
||||||
var monster =
|
var sceneId = imd.getCityData().getSceneId();
|
||||||
group.monsters.values().stream().filter(x -> x.monster_id == monsterId).findFirst();
|
var group = getInvestigationGroup(sceneId, groupId);
|
||||||
if (monster.isEmpty()) {
|
|
||||||
return null;
|
if (group == null || group.monsters == null) {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
var builder = InvestigationMonsterOuterClass.InvestigationMonster.newBuilder();
|
|
||||||
|
var monster =
|
||||||
builder
|
group.monsters.values().stream().filter(x -> x.monster_id == monsterId).findFirst();
|
||||||
.setId(imd.getId())
|
if (monster.isEmpty()) {
|
||||||
.setCityId(imd.getCityId())
|
return null;
|
||||||
.setSceneId(imd.getCityData().getSceneId())
|
}
|
||||||
.setGroupId(groupId)
|
|
||||||
.setMonsterId(monsterId)
|
var builder = InvestigationMonsterOuterClass.InvestigationMonster.newBuilder();
|
||||||
.setLevel(getMonsterLevel(monster.get(), player.getWorld()))
|
|
||||||
.setIsAlive(true)
|
builder
|
||||||
.setNextRefreshTime(Integer.MAX_VALUE)
|
.setId(imd.getId())
|
||||||
.setRefreshInterval(Integer.MAX_VALUE)
|
.setCityId(imd.getCityId())
|
||||||
.setPos(monster.get().pos.toProto());
|
.setSceneId(imd.getCityData().getSceneId())
|
||||||
|
.setGroupId(groupId)
|
||||||
if ("Boss".equals(imd.getMonsterCategory())) {
|
.setMonsterId(monsterId)
|
||||||
var bossChest = group.searchBossChestInGroup();
|
.setLevel(getMonsterLevel(monster.get(), player.getWorld()))
|
||||||
if (bossChest.isPresent()) {
|
.setIsAlive(true)
|
||||||
builder.setResin(bossChest.get().resin);
|
.setNextRefreshTime(Integer.MAX_VALUE)
|
||||||
builder.setMaxBossChestNum(bossChest.get().take_num);
|
.setRefreshInterval(Integer.MAX_VALUE)
|
||||||
}
|
.setPos(monster.get().pos.toProto());
|
||||||
}
|
|
||||||
return builder.build();
|
if ("Boss".equals(imd.getMonsterCategory())) {
|
||||||
}
|
var bossChest = group.searchBossChestInGroup();
|
||||||
|
if (bossChest.isPresent()) {
|
||||||
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(
|
builder.setResin(bossChest.get().resin);
|
||||||
Player player, int cityId) {
|
builder.setMaxBossChestNum(bossChest.get().take_num);
|
||||||
var cityData = GameData.getCityDataMap().get(cityId);
|
}
|
||||||
if (cityData == null) {
|
}
|
||||||
Grasscutter.getLogger().warn("City not exist {}", cityId);
|
return builder.build();
|
||||||
return List.of();
|
}
|
||||||
}
|
|
||||||
|
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(
|
||||||
return GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
Player player, int cityId) {
|
||||||
.filter(imd -> imd.getCityId() == cityId)
|
var cityData = GameData.getCityDataMap().get(cityId);
|
||||||
.map(imd -> this.getInvestigationMonster(player, imd))
|
if (cityData == null) {
|
||||||
.filter(Objects::nonNull)
|
Grasscutter.getLogger().warn("City not exist {}", cityId);
|
||||||
.toList();
|
return List.of();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||||
|
.filter(imd -> imd.getCityId() == cityId)
|
||||||
|
.map(imd -> this.getInvestigationMonster(player, imd))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,152 +1,176 @@
|
|||||||
package emu.grasscutter.scripts;
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.props.EntityType;
|
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeEventMarkType;
|
||||||
import emu.grasscutter.game.quest.enums.QuestState;
|
import emu.grasscutter.game.dungeons.challenge.enums.FatherChallengeProperty;
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
import emu.grasscutter.game.props.ElementType;
|
||||||
import emu.grasscutter.scripts.constants.ScriptGadgetState;
|
import emu.grasscutter.game.props.EntityType;
|
||||||
import emu.grasscutter.scripts.constants.ScriptRegionShape;
|
import emu.grasscutter.game.quest.enums.QuestState;
|
||||||
import emu.grasscutter.scripts.data.SceneMeta;
|
import emu.grasscutter.scripts.constants.*;
|
||||||
import emu.grasscutter.scripts.serializer.LuaSerializer;
|
import emu.grasscutter.scripts.data.SceneMeta;
|
||||||
import emu.grasscutter.scripts.serializer.Serializer;
|
import emu.grasscutter.scripts.serializer.LuaSerializer;
|
||||||
import emu.grasscutter.utils.FileUtils;
|
import emu.grasscutter.scripts.serializer.Serializer;
|
||||||
import java.io.File;
|
import emu.grasscutter.utils.FileUtils;
|
||||||
import java.io.FileReader;
|
import java.io.File;
|
||||||
import java.lang.ref.SoftReference;
|
import java.io.FileReader;
|
||||||
import java.nio.file.Files;
|
import java.lang.ref.SoftReference;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Files;
|
||||||
import java.util.Arrays;
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.Optional;
|
||||||
import javax.script.*;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import lombok.Getter;
|
import javax.script.*;
|
||||||
import org.luaj.vm2.LuaTable;
|
import lombok.Getter;
|
||||||
import org.luaj.vm2.LuaValue;
|
import org.luaj.vm2.LuaTable;
|
||||||
import org.luaj.vm2.lib.OneArgFunction;
|
import org.luaj.vm2.LuaValue;
|
||||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
import org.luaj.vm2.lib.OneArgFunction;
|
||||||
import org.luaj.vm2.script.LuajContext;
|
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||||
|
import org.luaj.vm2.script.LuajContext;
|
||||||
public class ScriptLoader {
|
|
||||||
private static ScriptEngineManager sm;
|
public class ScriptLoader {
|
||||||
@Getter private static ScriptEngine engine;
|
private static ScriptEngineManager sm;
|
||||||
private static ScriptEngineFactory factory;
|
@Getter private static ScriptEngine engine;
|
||||||
@Getter private static Serializer serializer;
|
private static ScriptEngineFactory factory;
|
||||||
@Getter private static ScriptLib scriptLib;
|
@Getter private static Serializer serializer;
|
||||||
@Getter private static LuaValue scriptLibLua;
|
@Getter private static ScriptLib scriptLib;
|
||||||
/** suggest GC to remove it if the memory is less */
|
@Getter private static LuaValue scriptLibLua;
|
||||||
private static final Map<String, SoftReference<CompiledScript>> scriptsCache =
|
/** suggest GC to remove it if the memory is less */
|
||||||
new ConcurrentHashMap<>();
|
private static final Map<String, SoftReference<CompiledScript>> scriptsCache =
|
||||||
/** sceneId - SceneMeta */
|
new ConcurrentHashMap<>();
|
||||||
private static final Map<Integer, SoftReference<SceneMeta>> sceneMetaCache =
|
/** sceneId - SceneMeta */
|
||||||
new ConcurrentHashMap<>();
|
private static final Map<Integer, SoftReference<SceneMeta>> sceneMetaCache =
|
||||||
|
new ConcurrentHashMap<>();
|
||||||
public static synchronized void init() throws Exception {
|
|
||||||
if (sm != null) {
|
public static synchronized void init() throws Exception {
|
||||||
throw new Exception("Script loader already initialized");
|
if (sm != null) {
|
||||||
}
|
throw new Exception("Script loader already initialized");
|
||||||
|
}
|
||||||
// Create script engine
|
|
||||||
sm = new ScriptEngineManager();
|
// Create script engine
|
||||||
engine = sm.getEngineByName("luaj");
|
sm = new ScriptEngineManager();
|
||||||
factory = getEngine().getFactory();
|
engine = sm.getEngineByName("luaj");
|
||||||
|
factory = getEngine().getFactory();
|
||||||
// Lua stuff
|
|
||||||
serializer = new LuaSerializer();
|
// Lua stuff
|
||||||
|
serializer = new LuaSerializer();
|
||||||
// Set engine to replace require as a temporary fix to missing scripts
|
|
||||||
LuajContext ctx = (LuajContext) engine.getContext();
|
// Set engine to replace require as a temporary fix to missing scripts
|
||||||
ctx.globals.set(
|
LuajContext ctx = (LuajContext) engine.getContext();
|
||||||
"require",
|
ctx.globals.set(
|
||||||
new OneArgFunction() {
|
"require",
|
||||||
@Override
|
new OneArgFunction() {
|
||||||
public LuaValue call(LuaValue arg0) {
|
@Override
|
||||||
return LuaValue.ZERO;
|
public LuaValue call(LuaValue arg0) {
|
||||||
}
|
return LuaValue.ZERO;
|
||||||
});
|
}
|
||||||
|
});
|
||||||
LuaTable table = new LuaTable();
|
|
||||||
Arrays.stream(EntityType.values())
|
addEnumByIntValue(ctx, EntityType.values(), "EntityType");
|
||||||
.forEach(e -> table.set(e.name().toUpperCase(), e.getValue()));
|
addEnumByIntValue(ctx, QuestState.values(), "QuestState");
|
||||||
ctx.globals.set("EntityType", table);
|
addEnumByIntValue(ctx, ElementType.values(), "ElementType");
|
||||||
|
|
||||||
LuaTable table1 = new LuaTable();
|
addEnumByOrdinal(ctx, GroupKillPolicy.values(), "GroupKillPolicy");
|
||||||
Arrays.stream(QuestState.values())
|
addEnumByOrdinal(ctx, SealBattleType.values(), "SealBattleType");
|
||||||
.forEach(e -> table1.set(e.name().toUpperCase(), e.getValue()));
|
addEnumByOrdinal(ctx, FatherChallengeProperty.values(), "FatherChallengeProperty");
|
||||||
ctx.globals.set("QuestState", table1);
|
addEnumByOrdinal(ctx, ChallengeEventMarkType.values(), "ChallengeEventMarkType");
|
||||||
|
|
||||||
ctx.globals.set(
|
ctx.globals.set(
|
||||||
"EventType",
|
"EventType",
|
||||||
CoerceJavaToLua.coerce(
|
CoerceJavaToLua.coerce(
|
||||||
new EventType())); // TODO - make static class to avoid instantiating a new class every
|
new EventType())); // TODO - make static class to avoid instantiating a new class every
|
||||||
// scene
|
// scene
|
||||||
ctx.globals.set("GadgetState", CoerceJavaToLua.coerce(new ScriptGadgetState()));
|
ctx.globals.set("GadgetState", CoerceJavaToLua.coerce(new ScriptGadgetState()));
|
||||||
ctx.globals.set("RegionShape", CoerceJavaToLua.coerce(new ScriptRegionShape()));
|
ctx.globals.set("RegionShape", CoerceJavaToLua.coerce(new ScriptRegionShape()));
|
||||||
|
|
||||||
scriptLib = new ScriptLib();
|
scriptLib = new ScriptLib();
|
||||||
scriptLibLua = CoerceJavaToLua.coerce(scriptLib);
|
scriptLibLua = CoerceJavaToLua.coerce(scriptLib);
|
||||||
ctx.globals.set("ScriptLib", scriptLibLua);
|
ctx.globals.set("ScriptLib", scriptLibLua);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Optional<T> tryGet(SoftReference<T> softReference) {
|
private static <T extends Enum<T>> void addEnumByOrdinal(
|
||||||
try {
|
LuajContext ctx, T[] enumArray, String name) {
|
||||||
return Optional.ofNullable(softReference.get());
|
LuaTable table = new LuaTable();
|
||||||
} catch (NullPointerException npe) {
|
Arrays.stream(enumArray)
|
||||||
return Optional.empty();
|
.forEach(
|
||||||
}
|
e -> {
|
||||||
}
|
table.set(e.name(), e.ordinal());
|
||||||
|
table.set(e.name().toUpperCase(), e.ordinal());
|
||||||
@Deprecated(forRemoval = true)
|
});
|
||||||
public static CompiledScript getScriptByPath(String path) {
|
ctx.globals.set(name, table);
|
||||||
var sc = tryGet(scriptsCache.get(path));
|
}
|
||||||
if (sc.isPresent()) {
|
|
||||||
return sc.get();
|
private static <T extends Enum<T> & IntValueEnum> void addEnumByIntValue(
|
||||||
}
|
LuajContext ctx, T[] enumArray, String name) {
|
||||||
|
LuaTable table = new LuaTable();
|
||||||
Grasscutter.getLogger().debug("Loading script " + path);
|
Arrays.stream(enumArray)
|
||||||
|
.forEach(
|
||||||
File file = new File(path);
|
e -> {
|
||||||
|
table.set(e.name(), e.getValue());
|
||||||
if (!file.exists()) return null;
|
table.set(e.name().toUpperCase(), e.getValue());
|
||||||
|
});
|
||||||
try (FileReader fr = new FileReader(file)) {
|
ctx.globals.set(name, table);
|
||||||
var script = ((Compilable) getEngine()).compile(fr);
|
}
|
||||||
scriptsCache.put(path, new SoftReference<>(script));
|
|
||||||
return script;
|
public static <T> Optional<T> tryGet(SoftReference<T> softReference) {
|
||||||
} catch (Exception e) {
|
try {
|
||||||
Grasscutter.getLogger().error("Loading script {} failed!", path, e);
|
return Optional.ofNullable(softReference.get());
|
||||||
return null;
|
} catch (NullPointerException npe) {
|
||||||
}
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public static CompiledScript getScript(String path) {
|
|
||||||
var sc = tryGet(scriptsCache.get(path));
|
@Deprecated(forRemoval = true)
|
||||||
if (sc.isPresent()) {
|
public static CompiledScript getScriptByPath(String path) {
|
||||||
return sc.get();
|
var sc = tryGet(scriptsCache.get(path));
|
||||||
}
|
if (sc.isPresent()) {
|
||||||
|
return sc.get();
|
||||||
Grasscutter.getLogger().debug("Loading script " + path);
|
}
|
||||||
final Path scriptPath = FileUtils.getScriptPath(path);
|
|
||||||
if (!Files.exists(scriptPath)) return null;
|
// Grasscutter.getLogger().debug("Loading script " + path);
|
||||||
|
|
||||||
try {
|
File file = new File(path);
|
||||||
var script = ((Compilable) getEngine()).compile(Files.newBufferedReader(scriptPath));
|
|
||||||
scriptsCache.put(path, new SoftReference<>(script));
|
if (!file.exists()) return null;
|
||||||
return script;
|
|
||||||
} catch (Exception e) {
|
try (FileReader fr = new FileReader(file)) {
|
||||||
Grasscutter.getLogger()
|
var script = ((Compilable) getEngine()).compile(fr);
|
||||||
.error("Loading script {} failed! - {}", path, e.getLocalizedMessage());
|
scriptsCache.put(path, new SoftReference<>(script));
|
||||||
return null;
|
return script;
|
||||||
}
|
} catch (Exception e) {
|
||||||
}
|
Grasscutter.getLogger().error("Loading script {} failed!", path, e);
|
||||||
|
return null;
|
||||||
public static SceneMeta getSceneMeta(int sceneId) {
|
}
|
||||||
return tryGet(sceneMetaCache.get(sceneId))
|
}
|
||||||
.orElseGet(
|
|
||||||
() -> {
|
public static CompiledScript getScript(String path) {
|
||||||
var instance = SceneMeta.of(sceneId);
|
var sc = tryGet(scriptsCache.get(path));
|
||||||
sceneMetaCache.put(sceneId, new SoftReference<>(instance));
|
if (sc.isPresent()) {
|
||||||
return instance;
|
return sc.get();
|
||||||
});
|
}
|
||||||
}
|
|
||||||
}
|
// Grasscutter.getLogger().debug("Loading script " + path);
|
||||||
|
final Path scriptPath = FileUtils.getScriptPath(path);
|
||||||
|
if (!Files.exists(scriptPath)) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var script = ((Compilable) getEngine()).compile(Files.newBufferedReader(scriptPath));
|
||||||
|
scriptsCache.put(path, new SoftReference<>(script));
|
||||||
|
return script;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger()
|
||||||
|
.error("Loading script {} failed! - {}", path, e.getLocalizedMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SceneMeta getSceneMeta(int sceneId) {
|
||||||
|
return tryGet(sceneMetaCache.get(sceneId))
|
||||||
|
.orElseGet(
|
||||||
|
() -> {
|
||||||
|
var instance = SceneMeta.of(sceneId);
|
||||||
|
sceneMetaCache.put(sceneId, new SoftReference<>(instance));
|
||||||
|
return instance;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,86 +1,87 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
import com.github.davidmoten.rtreemulti.RTree;
|
import com.github.davidmoten.rtreemulti.RTree;
|
||||||
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
||||||
import com.github.davidmoten.rtreemulti.geometry.Rectangle;
|
import com.github.davidmoten.rtreemulti.geometry.Rectangle;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.scripts.SceneIndexManager;
|
import emu.grasscutter.scripts.SceneIndexManager;
|
||||||
import emu.grasscutter.scripts.ScriptLoader;
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.script.Bindings;
|
import javax.script.Bindings;
|
||||||
import javax.script.CompiledScript;
|
import javax.script.CompiledScript;
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
@ToString
|
@ToString
|
||||||
@Setter
|
@Setter
|
||||||
public class SceneBlock {
|
public class SceneBlock {
|
||||||
public int id;
|
public int id;
|
||||||
public Position max;
|
public Position max;
|
||||||
public Position min;
|
public Position min;
|
||||||
|
|
||||||
public int sceneId;
|
public int sceneId;
|
||||||
public Map<Integer, SceneGroup> groups;
|
public Map<Integer, SceneGroup> groups;
|
||||||
public RTree<SceneGroup, Geometry> sceneGroupIndex;
|
public RTree<SceneGroup, Geometry> sceneGroupIndex;
|
||||||
|
|
||||||
private transient boolean loaded; // Not an actual variable in the scripts either
|
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||||
|
|
||||||
public boolean isLoaded() {
|
public boolean isLoaded() {
|
||||||
return this.loaded;
|
return this.loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoaded(boolean loaded) {
|
public void setLoaded(boolean loaded) {
|
||||||
this.loaded = loaded;
|
this.loaded = loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(Position pos) {
|
public boolean contains(Position pos) {
|
||||||
return pos.getX() <= this.max.getX()
|
int range = Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange;
|
||||||
&& pos.getX() >= this.min.getX()
|
return pos.getX() <= (this.max.getX() + range)
|
||||||
&& pos.getZ() <= this.max.getZ()
|
&& pos.getX() >= (this.min.getX() - range)
|
||||||
&& pos.getZ() >= this.min.getZ();
|
&& pos.getZ() <= (this.max.getZ() + range)
|
||||||
}
|
&& pos.getZ() >= (this.min.getZ() - range);
|
||||||
|
}
|
||||||
public SceneBlock load(int sceneId, Bindings bindings) {
|
|
||||||
if (this.loaded) {
|
public SceneBlock load(int sceneId, Bindings bindings) {
|
||||||
return this;
|
if (this.loaded) {
|
||||||
}
|
return this;
|
||||||
this.sceneId = sceneId;
|
}
|
||||||
this.setLoaded(true);
|
this.sceneId = sceneId;
|
||||||
|
this.setLoaded(true);
|
||||||
CompiledScript cs =
|
|
||||||
ScriptLoader.getScript(
|
CompiledScript cs =
|
||||||
"Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + ".lua");
|
ScriptLoader.getScript(
|
||||||
|
"Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + ".lua");
|
||||||
if (cs == null) {
|
|
||||||
return null;
|
if (cs == null) {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
// Eval script
|
|
||||||
try {
|
// Eval script
|
||||||
cs.eval(bindings);
|
try {
|
||||||
|
cs.eval(bindings);
|
||||||
// Set groups
|
|
||||||
this.groups =
|
// Set groups
|
||||||
ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")).stream()
|
this.groups =
|
||||||
.collect(Collectors.toMap(x -> x.id, y -> y, (a, b) -> a));
|
ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")).stream()
|
||||||
|
.collect(Collectors.toMap(x -> x.id, y -> y, (a, b) -> a));
|
||||||
this.groups.values().forEach(g -> g.block_id = this.id);
|
|
||||||
this.sceneGroupIndex =
|
this.groups.values().forEach(g -> g.block_id = this.id);
|
||||||
SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
|
this.sceneGroupIndex =
|
||||||
} catch (ScriptException exception) {
|
SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
|
||||||
Grasscutter.getLogger()
|
} catch (ScriptException exception) {
|
||||||
.error(
|
Grasscutter.getLogger()
|
||||||
"An error occurred while loading block " + this.id + " in scene " + sceneId,
|
.error(
|
||||||
exception);
|
"An error occurred while loading block " + this.id + " in scene " + sceneId,
|
||||||
}
|
exception);
|
||||||
Grasscutter.getLogger().debug("Successfully loaded block {} in scene {}.", this.id, sceneId);
|
}
|
||||||
return this;
|
Grasscutter.getLogger().debug("Successfully loaded block {} in scene {}.", this.id, sceneId);
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
public Rectangle toRectangle() {
|
|
||||||
return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray());
|
public Rectangle toRectangle() {
|
||||||
}
|
return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,246 +1,268 @@
|
|||||||
package emu.grasscutter.scripts.serializer;
|
package emu.grasscutter.scripts.serializer;
|
||||||
|
|
||||||
import com.esotericsoftware.reflectasm.ConstructorAccess;
|
import com.esotericsoftware.reflectasm.ConstructorAccess;
|
||||||
import com.esotericsoftware.reflectasm.MethodAccess;
|
import com.esotericsoftware.reflectasm.MethodAccess;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.scripts.ScriptUtils;
|
import emu.grasscutter.scripts.ScriptUtils;
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.lang.reflect.Field;
|
||||||
import lombok.AccessLevel;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import lombok.AllArgsConstructor;
|
import java.lang.reflect.Type;
|
||||||
import lombok.Data;
|
import java.util.*;
|
||||||
import lombok.experimental.FieldDefaults;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import org.luaj.vm2.LuaTable;
|
import lombok.AccessLevel;
|
||||||
import org.luaj.vm2.LuaValue;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
public class LuaSerializer implements Serializer {
|
import lombok.experimental.FieldDefaults;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
private static final Map<Class<?>, MethodAccess> methodAccessCache = new ConcurrentHashMap<>();
|
import org.luaj.vm2.LuaTable;
|
||||||
private static final Map<Class<?>, ConstructorAccess<?>> constructorCache =
|
import org.luaj.vm2.LuaValue;
|
||||||
new ConcurrentHashMap<>();
|
|
||||||
private static final Map<Class<?>, Map<String, FieldMeta>> fieldMetaCache =
|
public class LuaSerializer implements Serializer {
|
||||||
new ConcurrentHashMap<>();
|
|
||||||
|
private static final Map<Class<?>, MethodAccess> methodAccessCache = new ConcurrentHashMap<>();
|
||||||
@Override
|
private static final Map<Class<?>, ConstructorAccess<?>> constructorCache =
|
||||||
public <T> List<T> toList(Class<T> type, Object obj) {
|
new ConcurrentHashMap<>();
|
||||||
return serializeList(type, (LuaTable) obj);
|
private static final Map<Class<?>, Map<String, FieldMeta>> fieldMetaCache =
|
||||||
}
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T toObject(Class<T> type, Object obj) {
|
public <T> List<T> toList(Class<T> type, Object obj) {
|
||||||
return serialize(type, (LuaTable) obj);
|
return serializeList(type, (LuaTable) obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Map<String, T> toMap(Class<T> type, Object obj) {
|
public <T> T toObject(Class<T> type, Object obj) {
|
||||||
return serializeMap(type, (LuaTable) obj);
|
return serialize(type, null, (LuaTable) obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> Map<String, T> serializeMap(Class<T> type, LuaTable table) {
|
@Override
|
||||||
Map<String, T> map = new HashMap<>();
|
public <T> Map<String, T> toMap(Class<T> type, Object obj) {
|
||||||
|
return serializeMap(type, (LuaTable) obj);
|
||||||
if (table == null) {
|
}
|
||||||
return map;
|
|
||||||
}
|
private <T> Map<String, T> serializeMap(Class<T> type, LuaTable table) {
|
||||||
|
Map<String, T> map = new HashMap<>();
|
||||||
try {
|
|
||||||
LuaValue[] keys = table.keys();
|
if (table == null) {
|
||||||
for (LuaValue k : keys) {
|
return map;
|
||||||
try {
|
}
|
||||||
LuaValue keyValue = table.get(k);
|
|
||||||
|
try {
|
||||||
T object = null;
|
LuaValue[] keys = table.keys();
|
||||||
|
for (LuaValue k : keys) {
|
||||||
if (keyValue.istable()) {
|
try {
|
||||||
object = serialize(type, keyValue.checktable());
|
LuaValue keyValue = table.get(k);
|
||||||
} else if (keyValue.isint()) {
|
|
||||||
object = (T) (Integer) keyValue.toint();
|
T object = null;
|
||||||
} else if (keyValue.isnumber()) {
|
|
||||||
object = (T) (Float) keyValue.tofloat(); // terrible...
|
if (keyValue.istable()) {
|
||||||
} else if (keyValue.isstring()) {
|
object = serialize(type, null, keyValue.checktable());
|
||||||
object = (T) keyValue.tojstring();
|
} else if (keyValue.isint()) {
|
||||||
} else if (keyValue.isboolean()) {
|
object = (T) (Integer) keyValue.toint();
|
||||||
object = (T) (Boolean) keyValue.toboolean();
|
} else if (keyValue.isnumber()) {
|
||||||
} else {
|
object = (T) (Float) keyValue.tofloat(); // terrible...
|
||||||
object = (T) keyValue;
|
} else if (keyValue.isstring()) {
|
||||||
}
|
object = (T) keyValue.tojstring();
|
||||||
|
} else if (keyValue.isboolean()) {
|
||||||
if (object != null) {
|
object = (T) (Boolean) keyValue.toboolean();
|
||||||
map.put(String.valueOf(k), object);
|
} else {
|
||||||
}
|
object = (T) keyValue;
|
||||||
} catch (Exception ex) {
|
}
|
||||||
|
|
||||||
}
|
if (object != null) {
|
||||||
}
|
map.put(String.valueOf(k), object);
|
||||||
} catch (Exception e) {
|
}
|
||||||
e.printStackTrace();
|
} catch (Exception ex) {
|
||||||
}
|
|
||||||
|
}
|
||||||
return map;
|
}
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
public <T> List<T> serializeList(Class<T> type, LuaTable table) {
|
}
|
||||||
List<T> list = new ArrayList<>();
|
|
||||||
|
return map;
|
||||||
if (table == null) {
|
}
|
||||||
return list;
|
|
||||||
}
|
public <T> List<T> serializeList(Class<T> type, LuaTable table) {
|
||||||
|
List<T> list = new ArrayList<>();
|
||||||
try {
|
|
||||||
LuaValue[] keys = table.keys();
|
if (table == null) {
|
||||||
for (LuaValue k : keys) {
|
return list;
|
||||||
try {
|
}
|
||||||
LuaValue keyValue = table.get(k);
|
|
||||||
|
try {
|
||||||
T object = null;
|
LuaValue[] keys = table.keys();
|
||||||
|
for (LuaValue k : keys) {
|
||||||
if (keyValue.istable()) {
|
try {
|
||||||
object = serialize(type, keyValue.checktable());
|
LuaValue keyValue = table.get(k);
|
||||||
} else if (keyValue.isint()) {
|
|
||||||
object = (T) (Integer) keyValue.toint();
|
T object = null;
|
||||||
} else if (keyValue.isnumber()) {
|
|
||||||
object = (T) (Float) keyValue.tofloat(); // terrible...
|
if (keyValue.istable()) {
|
||||||
} else if (keyValue.isstring()) {
|
object = serialize(type, null, keyValue.checktable());
|
||||||
object = (T) keyValue.tojstring();
|
} else if (keyValue.isint()) {
|
||||||
} else if (keyValue.isboolean()) {
|
object = (T) (Integer) keyValue.toint();
|
||||||
object = (T) (Boolean) keyValue.toboolean();
|
} else if (keyValue.isnumber()) {
|
||||||
} else {
|
object = (T) (Float) keyValue.tofloat(); // terrible...
|
||||||
object = (T) keyValue;
|
} else if (keyValue.isstring()) {
|
||||||
}
|
object = (T) keyValue.tojstring();
|
||||||
|
} else if (keyValue.isboolean()) {
|
||||||
if (object != null) {
|
object = (T) (Boolean) keyValue.toboolean();
|
||||||
list.add(object);
|
} else {
|
||||||
}
|
object = (T) keyValue;
|
||||||
} catch (Exception ex) {
|
}
|
||||||
|
|
||||||
}
|
if (object != null) {
|
||||||
}
|
list.add(object);
|
||||||
} catch (Exception e) {
|
}
|
||||||
e.printStackTrace();
|
} catch (Exception ex) {
|
||||||
}
|
|
||||||
|
}
|
||||||
return list;
|
}
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
public <T> T serialize(Class<T> type, LuaTable table) {
|
}
|
||||||
T object = null;
|
|
||||||
|
return list;
|
||||||
if (type == List.class) {
|
}
|
||||||
try {
|
|
||||||
Class<T> listType = (Class<T>) type.getTypeParameters()[0].getClass();
|
private Class<?> getListType(Class<?> type, @Nullable Field field) {
|
||||||
return (T) serializeList(listType, table);
|
if (field == null) {
|
||||||
} catch (Exception e) {
|
return type.getTypeParameters()[0].getClass();
|
||||||
e.printStackTrace();
|
}
|
||||||
return null;
|
Type fieldType = field.getGenericType();
|
||||||
}
|
if (fieldType instanceof ParameterizedType) {
|
||||||
}
|
return (Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0];
|
||||||
|
}
|
||||||
try {
|
|
||||||
if (!methodAccessCache.containsKey(type)) {
|
return null;
|
||||||
cacheType(type);
|
}
|
||||||
}
|
|
||||||
var methodAccess = methodAccessCache.get(type);
|
public <T> T serialize(Class<T> type, @Nullable Field field, LuaTable table) {
|
||||||
var fieldMetaMap = fieldMetaCache.get(type);
|
T object = null;
|
||||||
|
|
||||||
object = (T) constructorCache.get(type).newInstance();
|
if (type == List.class) {
|
||||||
|
try {
|
||||||
if (table == null) {
|
Class<?> listType = getListType(type, field);
|
||||||
return object;
|
return (T) serializeList(listType, table);
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
LuaValue[] keys = table.keys();
|
return null;
|
||||||
for (LuaValue k : keys) {
|
}
|
||||||
try {
|
}
|
||||||
var keyName = k.checkjstring();
|
|
||||||
if (!fieldMetaMap.containsKey(keyName)) {
|
try {
|
||||||
continue;
|
if (!methodAccessCache.containsKey(type)) {
|
||||||
}
|
cacheType(type);
|
||||||
var fieldMeta = fieldMetaMap.get(keyName);
|
}
|
||||||
LuaValue keyValue = table.get(k);
|
var methodAccess = methodAccessCache.get(type);
|
||||||
|
var fieldMetaMap = fieldMetaCache.get(type);
|
||||||
if (keyValue.istable()) {
|
|
||||||
methodAccess.invoke(
|
object = (T) constructorCache.get(type).newInstance();
|
||||||
object, fieldMeta.index, serialize(fieldMeta.getType(), keyValue.checktable()));
|
|
||||||
} else if (fieldMeta.getType().equals(float.class)) {
|
if (table == null) {
|
||||||
methodAccess.invoke(object, fieldMeta.index, keyValue.tofloat());
|
return object;
|
||||||
} else if (fieldMeta.getType().equals(int.class)) {
|
}
|
||||||
methodAccess.invoke(object, fieldMeta.index, keyValue.toint());
|
|
||||||
} else if (fieldMeta.getType().equals(String.class)) {
|
LuaValue[] keys = table.keys();
|
||||||
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
for (LuaValue k : keys) {
|
||||||
} else if (fieldMeta.getType().equals(boolean.class)) {
|
try {
|
||||||
methodAccess.invoke(object, fieldMeta.index, keyValue.toboolean());
|
var keyName = k.checkjstring();
|
||||||
} else {
|
if (!fieldMetaMap.containsKey(keyName)) {
|
||||||
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
continue;
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
var fieldMeta = fieldMetaMap.get(keyName);
|
||||||
// ex.printStackTrace();
|
LuaValue keyValue = table.get(k);
|
||||||
continue;
|
|
||||||
}
|
if (keyValue.istable()) {
|
||||||
}
|
methodAccess.invoke(
|
||||||
} catch (Exception e) {
|
object,
|
||||||
Grasscutter.getLogger().debug(ScriptUtils.toMap(table).toString());
|
fieldMeta.index,
|
||||||
e.printStackTrace();
|
serialize(fieldMeta.getType(), fieldMeta.getField(), keyValue.checktable()));
|
||||||
}
|
} else if (fieldMeta.getType().equals(float.class)) {
|
||||||
|
methodAccess.invoke(object, fieldMeta.index, keyValue.tofloat());
|
||||||
return object;
|
} else if (fieldMeta.getType().equals(int.class)) {
|
||||||
}
|
methodAccess.invoke(object, fieldMeta.index, keyValue.toint());
|
||||||
|
} else if (fieldMeta.getType().equals(String.class)) {
|
||||||
public <T> Map<String, FieldMeta> cacheType(Class<T> type) {
|
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
||||||
if (fieldMetaCache.containsKey(type)) {
|
} else if (fieldMeta.getType().equals(boolean.class)) {
|
||||||
return fieldMetaCache.get(type);
|
methodAccess.invoke(object, fieldMeta.index, keyValue.toboolean());
|
||||||
}
|
} else {
|
||||||
if (!constructorCache.containsKey(type)) {
|
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
||||||
constructorCache.putIfAbsent(type, ConstructorAccess.get(type));
|
}
|
||||||
}
|
} catch (Exception ex) {
|
||||||
var methodAccess =
|
// ex.printStackTrace();
|
||||||
Optional.ofNullable(methodAccessCache.get(type)).orElse(MethodAccess.get(type));
|
continue;
|
||||||
methodAccessCache.putIfAbsent(type, methodAccess);
|
}
|
||||||
|
}
|
||||||
var fieldMetaMap = new HashMap<String, FieldMeta>();
|
} catch (Exception e) {
|
||||||
var methodNameSet = new HashSet<>(Arrays.stream(methodAccess.getMethodNames()).toList());
|
Grasscutter.getLogger().debug(ScriptUtils.toMap(table).toString());
|
||||||
|
e.printStackTrace();
|
||||||
Arrays.stream(type.getDeclaredFields())
|
}
|
||||||
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
|
|
||||||
.forEach(
|
return object;
|
||||||
field -> {
|
}
|
||||||
var setter = getSetterName(field.getName());
|
|
||||||
var index = methodAccess.getIndex(setter);
|
public <T> Map<String, FieldMeta> cacheType(Class<T> type) {
|
||||||
fieldMetaMap.put(
|
if (fieldMetaCache.containsKey(type)) {
|
||||||
field.getName(), new FieldMeta(field.getName(), setter, index, field.getType()));
|
return fieldMetaCache.get(type);
|
||||||
});
|
}
|
||||||
|
if (!constructorCache.containsKey(type)) {
|
||||||
Arrays.stream(type.getFields())
|
constructorCache.putIfAbsent(type, ConstructorAccess.get(type));
|
||||||
.filter(field -> !fieldMetaMap.containsKey(field.getName()))
|
}
|
||||||
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
|
var methodAccess =
|
||||||
.forEach(
|
Optional.ofNullable(methodAccessCache.get(type)).orElse(MethodAccess.get(type));
|
||||||
field -> {
|
methodAccessCache.putIfAbsent(type, methodAccess);
|
||||||
var setter = getSetterName(field.getName());
|
|
||||||
var index = methodAccess.getIndex(setter);
|
var fieldMetaMap = new HashMap<String, FieldMeta>();
|
||||||
fieldMetaMap.put(
|
var methodNameSet = new HashSet<>(Arrays.stream(methodAccess.getMethodNames()).toList());
|
||||||
field.getName(), new FieldMeta(field.getName(), setter, index, field.getType()));
|
|
||||||
});
|
Arrays.stream(type.getDeclaredFields())
|
||||||
|
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
|
||||||
fieldMetaCache.put(type, fieldMetaMap);
|
.forEach(
|
||||||
return fieldMetaMap;
|
field -> {
|
||||||
}
|
var setter = getSetterName(field.getName());
|
||||||
|
var index = methodAccess.getIndex(setter);
|
||||||
public String getSetterName(String fieldName) {
|
fieldMetaMap.put(
|
||||||
if (fieldName == null || fieldName.length() == 0) {
|
field.getName(),
|
||||||
return null;
|
new FieldMeta(field.getName(), setter, index, field.getType(), field));
|
||||||
}
|
});
|
||||||
if (fieldName.length() == 1) {
|
|
||||||
return "set" + fieldName.toUpperCase();
|
Arrays.stream(type.getFields())
|
||||||
}
|
.filter(field -> !fieldMetaMap.containsKey(field.getName()))
|
||||||
return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
|
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
|
||||||
}
|
.forEach(
|
||||||
|
field -> {
|
||||||
@Data
|
var setter = getSetterName(field.getName());
|
||||||
@AllArgsConstructor
|
var index = methodAccess.getIndex(setter);
|
||||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
fieldMetaMap.put(
|
||||||
static class FieldMeta {
|
field.getName(),
|
||||||
String name;
|
new FieldMeta(field.getName(), setter, index, field.getType(), field));
|
||||||
String setter;
|
});
|
||||||
int index;
|
|
||||||
Class<?> type;
|
fieldMetaCache.put(type, fieldMetaMap);
|
||||||
}
|
return fieldMetaMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSetterName(String fieldName) {
|
||||||
|
if (fieldName == null || fieldName.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (fieldName.length() == 1) {
|
||||||
|
return "set" + fieldName.toUpperCase();
|
||||||
|
}
|
||||||
|
return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
static class FieldMeta {
|
||||||
|
String name;
|
||||||
|
String setter;
|
||||||
|
int index;
|
||||||
|
Class<?> type;
|
||||||
|
@Nullable Field field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,279 +1,281 @@
|
|||||||
package emu.grasscutter.server.game;
|
package emu.grasscutter.server.game;
|
||||||
|
|
||||||
import static emu.grasscutter.config.Configuration.GAME_INFO;
|
import static emu.grasscutter.config.Configuration.GAME_INFO;
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
import emu.grasscutter.GameConstants;
|
import emu.grasscutter.GameConstants;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.database.DatabaseHelper;
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
import emu.grasscutter.game.battlepass.BattlePassSystem;
|
import emu.grasscutter.game.battlepass.BattlePassSystem;
|
||||||
import emu.grasscutter.game.chat.ChatSystem;
|
import emu.grasscutter.game.chat.ChatSystem;
|
||||||
import emu.grasscutter.game.chat.ChatSystemHandler;
|
import emu.grasscutter.game.chat.ChatSystemHandler;
|
||||||
import emu.grasscutter.game.combine.CombineManger;
|
import emu.grasscutter.game.combine.CombineManger;
|
||||||
import emu.grasscutter.game.drop.DropSystem;
|
import emu.grasscutter.game.drop.DropSystem;
|
||||||
import emu.grasscutter.game.dungeons.DungeonSystem;
|
import emu.grasscutter.game.dungeons.DungeonSystem;
|
||||||
import emu.grasscutter.game.expedition.ExpeditionSystem;
|
import emu.grasscutter.game.expedition.ExpeditionSystem;
|
||||||
import emu.grasscutter.game.gacha.GachaSystem;
|
import emu.grasscutter.game.gacha.GachaSystem;
|
||||||
import emu.grasscutter.game.managers.cooking.CookingCompoundManager;
|
import emu.grasscutter.game.managers.cooking.CookingCompoundManager;
|
||||||
import emu.grasscutter.game.managers.cooking.CookingManager;
|
import emu.grasscutter.game.managers.cooking.CookingManager;
|
||||||
import emu.grasscutter.game.managers.energy.EnergyManager;
|
import emu.grasscutter.game.managers.energy.EnergyManager;
|
||||||
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.quest.QuestSystem;
|
import emu.grasscutter.game.quest.QuestSystem;
|
||||||
import emu.grasscutter.game.shop.ShopSystem;
|
import emu.grasscutter.game.shop.ShopSystem;
|
||||||
import emu.grasscutter.game.systems.AnnouncementSystem;
|
import emu.grasscutter.game.systems.AnnouncementSystem;
|
||||||
import emu.grasscutter.game.systems.InventorySystem;
|
import emu.grasscutter.game.systems.InventorySystem;
|
||||||
import emu.grasscutter.game.systems.MultiplayerSystem;
|
import emu.grasscutter.game.systems.MultiplayerSystem;
|
||||||
import emu.grasscutter.game.tower.TowerSystem;
|
import emu.grasscutter.game.tower.TowerSystem;
|
||||||
import emu.grasscutter.game.world.World;
|
import emu.grasscutter.game.world.World;
|
||||||
import emu.grasscutter.game.world.WorldDataSystem;
|
import emu.grasscutter.game.world.WorldDataSystem;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||||
import emu.grasscutter.server.event.game.ServerTickEvent;
|
import emu.grasscutter.server.event.game.ServerTickEvent;
|
||||||
import emu.grasscutter.server.event.internal.ServerStartEvent;
|
import emu.grasscutter.server.event.internal.ServerStartEvent;
|
||||||
import emu.grasscutter.server.event.internal.ServerStopEvent;
|
import emu.grasscutter.server.event.internal.ServerStopEvent;
|
||||||
import emu.grasscutter.server.event.types.ServerEvent;
|
import emu.grasscutter.server.event.types.ServerEvent;
|
||||||
import emu.grasscutter.server.scheduler.ServerTaskScheduler;
|
import emu.grasscutter.server.scheduler.ServerTaskScheduler;
|
||||||
import emu.grasscutter.task.TaskMap;
|
import emu.grasscutter.task.TaskMap;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import kcp.highway.ChannelConfig;
|
import kcp.highway.ChannelConfig;
|
||||||
import kcp.highway.KcpServer;
|
import kcp.highway.KcpServer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public final class GameServer extends KcpServer {
|
public final class GameServer extends KcpServer {
|
||||||
// Game server base
|
// Game server base
|
||||||
private final InetSocketAddress address;
|
private final InetSocketAddress address;
|
||||||
private final GameServerPacketHandler packetHandler;
|
private final GameServerPacketHandler packetHandler;
|
||||||
private final Map<Integer, Player> players;
|
private final Map<Integer, Player> players;
|
||||||
private final Set<World> worlds;
|
private final Set<World> worlds;
|
||||||
|
|
||||||
// Server systems
|
// Server systems
|
||||||
private final InventorySystem inventorySystem;
|
private final InventorySystem inventorySystem;
|
||||||
private final GachaSystem gachaSystem;
|
private final GachaSystem gachaSystem;
|
||||||
private final ShopSystem shopSystem;
|
private final ShopSystem shopSystem;
|
||||||
private final MultiplayerSystem multiplayerSystem;
|
private final MultiplayerSystem multiplayerSystem;
|
||||||
private final DungeonSystem dungeonSystem;
|
private final DungeonSystem dungeonSystem;
|
||||||
private final ExpeditionSystem expeditionSystem;
|
private final ExpeditionSystem expeditionSystem;
|
||||||
private final DropSystem dropSystem;
|
private final DropSystem dropSystem;
|
||||||
private final WorldDataSystem worldDataSystem;
|
private final WorldDataSystem worldDataSystem;
|
||||||
private final BattlePassSystem battlePassSystem;
|
private final BattlePassSystem battlePassSystem;
|
||||||
private final CombineManger combineSystem;
|
private final CombineManger combineSystem;
|
||||||
private final TowerSystem towerSystem;
|
private final TowerSystem towerSystem;
|
||||||
private final AnnouncementSystem announcementSystem;
|
private final AnnouncementSystem announcementSystem;
|
||||||
private final QuestSystem questSystem;
|
private final QuestSystem questSystem;
|
||||||
|
|
||||||
// Extra
|
// Extra
|
||||||
private final ServerTaskScheduler scheduler;
|
private final ServerTaskScheduler scheduler;
|
||||||
private final TaskMap taskMap;
|
private final TaskMap taskMap;
|
||||||
|
|
||||||
private ChatSystemHandler chatManager;
|
private ChatSystemHandler chatManager;
|
||||||
|
|
||||||
public GameServer() {
|
public GameServer() {
|
||||||
this(getAdapterInetSocketAddress());
|
this(getAdapterInetSocketAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameServer(InetSocketAddress address) {
|
public GameServer(InetSocketAddress address) {
|
||||||
ChannelConfig channelConfig = new ChannelConfig();
|
ChannelConfig channelConfig = new ChannelConfig();
|
||||||
channelConfig.nodelay(true, GAME_INFO.kcpInterval, 2, true);
|
channelConfig.nodelay(true, GAME_INFO.kcpInterval, 2, true);
|
||||||
channelConfig.setMtu(1400);
|
channelConfig.setMtu(1400);
|
||||||
channelConfig.setSndwnd(256);
|
channelConfig.setSndwnd(256);
|
||||||
channelConfig.setRcvwnd(256);
|
channelConfig.setRcvwnd(256);
|
||||||
channelConfig.setTimeoutMillis(30 * 1000); // 30s
|
channelConfig.setTimeoutMillis(30 * 1000); // 30s
|
||||||
channelConfig.setUseConvChannel(true);
|
channelConfig.setUseConvChannel(true);
|
||||||
channelConfig.setAckNoDelay(false);
|
channelConfig.setAckNoDelay(false);
|
||||||
|
|
||||||
this.init(GameSessionManager.getListener(), channelConfig, address);
|
this.init(GameSessionManager.getListener(), channelConfig, address);
|
||||||
|
|
||||||
EnergyManager.initialize();
|
EnergyManager.initialize();
|
||||||
StaminaManager.initialize();
|
StaminaManager.initialize();
|
||||||
CookingManager.initialize();
|
CookingManager.initialize();
|
||||||
CookingCompoundManager.initialize();
|
CookingCompoundManager.initialize();
|
||||||
CombineManger.initialize();
|
CombineManger.initialize();
|
||||||
|
|
||||||
// Game Server base
|
// Game Server base
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
||||||
this.players = new ConcurrentHashMap<>();
|
this.players = new ConcurrentHashMap<>();
|
||||||
this.worlds = Collections.synchronizedSet(new HashSet<>());
|
this.worlds = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
// Extra
|
// Extra
|
||||||
this.scheduler = new ServerTaskScheduler();
|
this.scheduler = new ServerTaskScheduler();
|
||||||
this.taskMap = new TaskMap(true);
|
this.taskMap = new TaskMap(true);
|
||||||
|
|
||||||
// Create game systems
|
// Create game systems
|
||||||
this.inventorySystem = new InventorySystem(this);
|
this.inventorySystem = new InventorySystem(this);
|
||||||
this.gachaSystem = new GachaSystem(this);
|
this.gachaSystem = new GachaSystem(this);
|
||||||
this.shopSystem = new ShopSystem(this);
|
this.shopSystem = new ShopSystem(this);
|
||||||
this.multiplayerSystem = new MultiplayerSystem(this);
|
this.multiplayerSystem = new MultiplayerSystem(this);
|
||||||
this.dungeonSystem = new DungeonSystem(this);
|
this.dungeonSystem = new DungeonSystem(this);
|
||||||
this.dropSystem = new DropSystem(this);
|
this.dropSystem = new DropSystem(this);
|
||||||
this.expeditionSystem = new ExpeditionSystem(this);
|
this.expeditionSystem = new ExpeditionSystem(this);
|
||||||
this.combineSystem = new CombineManger(this);
|
this.combineSystem = new CombineManger(this);
|
||||||
this.towerSystem = new TowerSystem(this);
|
this.towerSystem = new TowerSystem(this);
|
||||||
this.worldDataSystem = new WorldDataSystem(this);
|
this.worldDataSystem = new WorldDataSystem(this);
|
||||||
this.battlePassSystem = new BattlePassSystem(this);
|
this.battlePassSystem = new BattlePassSystem(this);
|
||||||
this.announcementSystem = new AnnouncementSystem(this);
|
this.announcementSystem = new AnnouncementSystem(this);
|
||||||
this.questSystem = new QuestSystem(this);
|
this.questSystem = new QuestSystem(this);
|
||||||
|
|
||||||
// Chata manager
|
// Chata manager
|
||||||
this.chatManager = new ChatSystem(this);
|
this.chatManager = new ChatSystem(this);
|
||||||
|
|
||||||
// Hook into shutdown event.
|
// Hook into shutdown event.
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InetSocketAddress getAdapterInetSocketAddress() {
|
private static InetSocketAddress getAdapterInetSocketAddress() {
|
||||||
InetSocketAddress inetSocketAddress;
|
InetSocketAddress inetSocketAddress;
|
||||||
if (GAME_INFO.bindAddress.equals("")) {
|
if (GAME_INFO.bindAddress.equals("")) {
|
||||||
inetSocketAddress = new InetSocketAddress(GAME_INFO.bindPort);
|
inetSocketAddress = new InetSocketAddress(GAME_INFO.bindPort);
|
||||||
} else {
|
} else {
|
||||||
inetSocketAddress = new InetSocketAddress(GAME_INFO.bindAddress, GAME_INFO.bindPort);
|
inetSocketAddress = new InetSocketAddress(GAME_INFO.bindAddress, GAME_INFO.bindPort);
|
||||||
}
|
}
|
||||||
return inetSocketAddress;
|
return inetSocketAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public ChatSystemHandler getChatManager() {
|
public ChatSystemHandler getChatManager() {
|
||||||
return chatManager;
|
return chatManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setChatManager(ChatSystemHandler chatManager) {
|
public void setChatManager(ChatSystemHandler chatManager) {
|
||||||
this.chatManager = chatManager;
|
this.chatManager = chatManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChatSystemHandler getChatSystem() {
|
public ChatSystemHandler getChatSystem() {
|
||||||
return chatManager;
|
return chatManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setChatSystem(ChatSystemHandler chatManager) {
|
public void setChatSystem(ChatSystemHandler chatManager) {
|
||||||
this.chatManager = chatManager;
|
this.chatManager = chatManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerPlayer(Player player) {
|
public void registerPlayer(Player player) {
|
||||||
getPlayers().put(player.getUid(), player);
|
getPlayers().put(player.getUid(), player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayerByUid(int id) {
|
public Player getPlayerByUid(int id) {
|
||||||
return this.getPlayerByUid(id, false);
|
return this.getPlayerByUid(id, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
|
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
|
||||||
// Console check
|
// Console check
|
||||||
if (id == GameConstants.SERVER_CONSOLE_UID) {
|
if (id == GameConstants.SERVER_CONSOLE_UID) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get from online players
|
// Get from online players
|
||||||
Player player = this.getPlayers().get(id);
|
Player player = this.getPlayers().get(id);
|
||||||
|
|
||||||
if (!allowOfflinePlayers) {
|
if (!allowOfflinePlayers) {
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check database if character isnt here
|
// Check database if character isnt here
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
player = DatabaseHelper.getPlayerByUid(id);
|
player = DatabaseHelper.getPlayerByUid(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayerByAccountId(String accountId) {
|
public Player getPlayerByAccountId(String accountId) {
|
||||||
Optional<Player> playerOpt =
|
Optional<Player> playerOpt =
|
||||||
getPlayers().values().stream()
|
getPlayers().values().stream()
|
||||||
.filter(player -> player.getAccount().getId().equals(accountId))
|
.filter(player -> player.getAccount().getId().equals(accountId))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
return playerOpt.orElse(null);
|
return playerOpt.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocialDetail.Builder getSocialDetailByUid(int id) {
|
public SocialDetail.Builder getSocialDetailByUid(int id) {
|
||||||
// Get from online players
|
// Get from online players
|
||||||
Player player = this.getPlayerByUid(id, true);
|
Player player = this.getPlayerByUid(id, true);
|
||||||
|
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return player.getSocialDetail();
|
return player.getSocialDetail();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getAccountByName(String username) {
|
public Account getAccountByName(String username) {
|
||||||
Optional<Player> playerOpt =
|
Optional<Player> playerOpt =
|
||||||
getPlayers().values().stream()
|
getPlayers().values().stream()
|
||||||
.filter(player -> player.getAccount().getUsername().equals(username))
|
.filter(player -> player.getAccount().getUsername().equals(username))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
if (playerOpt.isPresent()) {
|
if (playerOpt.isPresent()) {
|
||||||
return playerOpt.get().getAccount();
|
return playerOpt.get().getAccount();
|
||||||
}
|
}
|
||||||
return DatabaseHelper.getAccountByName(username);
|
return DatabaseHelper.getAccountByName(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void onTick() {
|
public synchronized void onTick() {
|
||||||
var tickStart = Instant.now();
|
var tickStart = Instant.now();
|
||||||
|
|
||||||
// Tick worlds.
|
// Tick worlds.
|
||||||
this.worlds.removeIf(World::onTick);
|
this.worlds.removeIf(World::onTick);
|
||||||
|
|
||||||
// Tick players.
|
// Tick players.
|
||||||
this.players.values().forEach(Player::onTick);
|
this.players.values().forEach(Player::onTick);
|
||||||
|
|
||||||
// Tick scheduler.
|
// Tick scheduler.
|
||||||
this.getScheduler().runTasks();
|
this.getScheduler().runTasks();
|
||||||
|
|
||||||
// Call server tick event.
|
// Call server tick event.
|
||||||
ServerTickEvent event = new ServerTickEvent(tickStart, Instant.now());
|
ServerTickEvent event = new ServerTickEvent(tickStart, Instant.now());
|
||||||
event.call();
|
event.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerWorld(World world) {
|
public void registerWorld(World world) {
|
||||||
this.getWorlds().add(world);
|
this.getWorlds().add(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deregisterWorld(World world) {
|
public void deregisterWorld(World world) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
world.save(); //Save the player's world
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
// Schedule game loop.
|
// Schedule game loop.
|
||||||
Timer gameLoop = new Timer();
|
Timer gameLoop = new Timer();
|
||||||
gameLoop.scheduleAtFixedRate(
|
gameLoop.scheduleAtFixedRate(
|
||||||
new TimerTask() {
|
new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
onTick();
|
onTick();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
|
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Date(),
|
new Date(),
|
||||||
1000L);
|
1000L);
|
||||||
Grasscutter.getLogger().info(translate("messages.status.free_software"));
|
Grasscutter.getLogger().info(translate("messages.status.free_software"));
|
||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
.info(translate("messages.game.address_bind", GAME_INFO.accessAddress, address.getPort()));
|
.info(translate("messages.game.address_bind", GAME_INFO.accessAddress, address.getPort()));
|
||||||
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
|
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
|
||||||
event.call();
|
event.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServerShutdown() {
|
public void onServerShutdown() {
|
||||||
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
|
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
|
||||||
event.call();
|
event.call();
|
||||||
|
|
||||||
// Kick and save all players
|
// Kick and save all players
|
||||||
List<Player> list = new ArrayList<>(this.getPlayers().size());
|
List<Player> list = new ArrayList<>(this.getPlayers().size());
|
||||||
list.addAll(this.getPlayers().values());
|
list.addAll(this.getPlayers().values());
|
||||||
|
|
||||||
for (Player player : list) {
|
for (Player player : list) {
|
||||||
player.getSession().close();
|
player.getSession().close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
getWorlds().forEach(World::save);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,45 +1,61 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
import emu.grasscutter.game.quest.GameMainQuest;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.QuestUpdateQuestVarReqOuterClass;
|
import emu.grasscutter.net.proto.QuestUpdateQuestVarReqOuterClass.QuestUpdateQuestVarReq;
|
||||||
import emu.grasscutter.net.proto.QuestVarOpOuterClass;
|
import emu.grasscutter.net.proto.QuestVarOpOuterClass.QuestVarOp;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
import emu.grasscutter.server.packet.send.PacketQuestUpdateQuestVarRsp;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
import java.util.List;
|
import emu.grasscutter.server.packet.send.PacketQuestUpdateQuestVarRsp;
|
||||||
|
import java.util.List;
|
||||||
@Opcodes(PacketOpcodes.QuestUpdateQuestVarReq)
|
|
||||||
public class HandlerQuestUpdateQuestVarReq extends PacketHandler {
|
@Opcodes(PacketOpcodes.QuestUpdateQuestVarReq)
|
||||||
|
public class HandlerQuestUpdateQuestVarReq extends PacketHandler {
|
||||||
@Override
|
|
||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
@Override
|
||||||
// Client sends packets. One with the value, and one with the index and the new value to
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
// set/inc/dec
|
// Client sends packets. One with the value, and one with the index and the new value to
|
||||||
var req = QuestUpdateQuestVarReqOuterClass.QuestUpdateQuestVarReq.parseFrom(payload);
|
// set/inc/dec
|
||||||
GameMainQuest mainQuest =
|
var req = QuestUpdateQuestVarReq.parseFrom(payload);
|
||||||
session.getPlayer().getQuestManager().getMainQuestById(req.getQuestId() / 100);
|
var questManager = session.getPlayer().getQuestManager();
|
||||||
List<QuestVarOpOuterClass.QuestVarOp> questVars = req.getQuestVarOpListList();
|
var subQuest = questManager.getQuestById(req.getQuestId());
|
||||||
if (mainQuest.getQuestVarsUpdate().size() == 0) {
|
var mainQuest = questManager.getMainQuestById(req.getParentQuestId());
|
||||||
for (QuestVarOpOuterClass.QuestVarOp questVar : questVars) {
|
if (mainQuest == null && subQuest != null) {
|
||||||
mainQuest.getQuestVarsUpdate().add(questVar.getValue());
|
mainQuest = subQuest.getMainQuest();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
for (QuestVarOpOuterClass.QuestVarOp questVar : questVars) {
|
if (mainQuest == null) {
|
||||||
if (questVar.getIsAdd()) {
|
session.send(new PacketQuestUpdateQuestVarRsp(req, Retcode.RET_QUEST_NOT_EXIST));
|
||||||
if (questVar.getValue() >= 0) {
|
Grasscutter.getLogger()
|
||||||
mainQuest.incQuestVar(questVar.getIndex(), questVar.getValue());
|
.debug(
|
||||||
} else {
|
"trying to update QuestVar for non existing quest s{} m{}",
|
||||||
mainQuest.decQuestVar(questVar.getIndex(), questVar.getValue());
|
req.getQuestId(),
|
||||||
}
|
req.getParentQuestId());
|
||||||
} else {
|
return;
|
||||||
mainQuest.setQuestVar(questVar.getIndex(), mainQuest.getQuestVarsUpdate().get(0));
|
}
|
||||||
}
|
List<QuestVarOp> questVars = req.getQuestVarOpListList();
|
||||||
// remove the first element from the update list
|
var questVarUpdate = mainQuest.getQuestVarsUpdate();
|
||||||
mainQuest.getQuestVarsUpdate().remove(0);
|
if (questVarUpdate.size() == 0) {
|
||||||
}
|
for (var questVar : questVars) {
|
||||||
}
|
questVarUpdate.add(questVar.getValue());
|
||||||
session.send(new PacketQuestUpdateQuestVarRsp(req.getQuestId()));
|
}
|
||||||
}
|
} else {
|
||||||
}
|
for (QuestVarOp questVar : questVars) {
|
||||||
|
if (questVar.getIsAdd()) {
|
||||||
|
if (questVar.getValue() >= 0) {
|
||||||
|
mainQuest.incQuestVar(questVar.getIndex(), questVar.getValue());
|
||||||
|
} else {
|
||||||
|
mainQuest.decQuestVar(questVar.getIndex(), questVar.getValue());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mainQuest.setQuestVar(questVar.getIndex(), questVarUpdate.get(0));
|
||||||
|
}
|
||||||
|
// remove the first element from the update list
|
||||||
|
questVarUpdate.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.send(new PacketQuestUpdateQuestVarRsp(req));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,17 +1,25 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.PlayerEnterDungeonRspOuterClass.PlayerEnterDungeonRsp;
|
import emu.grasscutter.net.proto.PlayerEnterDungeonRspOuterClass.PlayerEnterDungeonRsp;
|
||||||
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
public class PacketPlayerEnterDungeonRsp extends BasePacket {
|
|
||||||
|
public class PacketPlayerEnterDungeonRsp extends BasePacket {
|
||||||
public PacketPlayerEnterDungeonRsp(int pointId, int dungeonId) {
|
|
||||||
super(PacketOpcodes.PlayerEnterDungeonRsp);
|
public PacketPlayerEnterDungeonRsp(int pointId, int dungeonId, boolean success) {
|
||||||
|
super(PacketOpcodes.PlayerEnterDungeonRsp);
|
||||||
PlayerEnterDungeonRsp proto =
|
|
||||||
PlayerEnterDungeonRsp.newBuilder().setPointId(pointId).setDungeonId(dungeonId).build();
|
PlayerEnterDungeonRsp proto =
|
||||||
|
PlayerEnterDungeonRsp.newBuilder()
|
||||||
this.setData(proto);
|
.setPointId(pointId)
|
||||||
}
|
.setDungeonId(dungeonId)
|
||||||
}
|
.setRetcode(
|
||||||
|
success
|
||||||
|
? Retcode.RET_SUCC_VALUE
|
||||||
|
: Retcode.RET_FAIL_VALUE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,32 +1,33 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.PlayerHomeCompInfoNotifyOuterClass;
|
import emu.grasscutter.net.proto.PlayerHomeCompInfoNotifyOuterClass;
|
||||||
import emu.grasscutter.net.proto.PlayerHomeCompInfoOuterClass;
|
import emu.grasscutter.net.proto.PlayerHomeCompInfoOuterClass;
|
||||||
|
|
||||||
public class PacketPlayerHomeCompInfoNotify extends BasePacket {
|
import java.util.List;
|
||||||
|
|
||||||
public PacketPlayerHomeCompInfoNotify(Player player) {
|
public class PacketPlayerHomeCompInfoNotify extends BasePacket {
|
||||||
super(PacketOpcodes.PlayerHomeCompInfoNotify);
|
|
||||||
|
public PacketPlayerHomeCompInfoNotify(Player player) {
|
||||||
if (player.getRealmList() == null) {
|
super(PacketOpcodes.PlayerHomeCompInfoNotify);
|
||||||
// Do not send
|
|
||||||
return;
|
if (player.getRealmList() == null) {
|
||||||
}
|
// Do not send
|
||||||
|
return;
|
||||||
PlayerHomeCompInfoNotifyOuterClass.PlayerHomeCompInfoNotify proto =
|
}
|
||||||
PlayerHomeCompInfoNotifyOuterClass.PlayerHomeCompInfoNotify.newBuilder()
|
|
||||||
.setCompInfo(
|
PlayerHomeCompInfoNotifyOuterClass.PlayerHomeCompInfoNotify proto =
|
||||||
PlayerHomeCompInfoOuterClass.PlayerHomeCompInfo.newBuilder()
|
PlayerHomeCompInfoNotifyOuterClass.PlayerHomeCompInfoNotify.newBuilder()
|
||||||
.addAllUnlockedModuleIdList(player.getRealmList())
|
.setCompInfo(
|
||||||
.addAllSeenModuleIdList(player.getSeenRealmList())
|
PlayerHomeCompInfoOuterClass.PlayerHomeCompInfo.newBuilder()
|
||||||
.addAllLevelupRewardGotLevelList(player.getHomeRewardedLevels())
|
.addAllUnlockedModuleIdList(player.getRealmList())
|
||||||
.setFriendEnterHomeOptionValue(player.getHome().getEnterHomeOption())
|
.addAllLevelupRewardGotLevelList(List.of(1)) // Hardcoded
|
||||||
.build())
|
.setFriendEnterHomeOptionValue(player.getHome().getEnterHomeOption())
|
||||||
.build();
|
.build())
|
||||||
|
.build();
|
||||||
this.setData(proto);
|
|
||||||
}
|
this.setData(proto);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.PlayerSetPauseRspOuterClass.PlayerSetPauseRsp;
|
||||||
public class PacketPlayerSetPauseRsp extends BasePacket {
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
|
|
||||||
public PacketPlayerSetPauseRsp(int clientSequence) {
|
public class PacketPlayerSetPauseRsp extends BasePacket {
|
||||||
super(PacketOpcodes.PlayerSetPauseRsp);
|
|
||||||
|
public PacketPlayerSetPauseRsp() {
|
||||||
this.buildHeader(clientSequence);
|
super(PacketOpcodes.PlayerSetPauseRsp);
|
||||||
}
|
|
||||||
}
|
this.setData(PlayerSetPauseRsp.newBuilder()
|
||||||
|
.setRetcode(Retcode.RET_SUCC_VALUE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,19 +1,27 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.QuestUpdateQuestVarRspOuterClass;
|
import emu.grasscutter.net.proto.QuestUpdateQuestVarReqOuterClass.QuestUpdateQuestVarReq;
|
||||||
|
import emu.grasscutter.net.proto.QuestUpdateQuestVarRspOuterClass;
|
||||||
@Opcodes(PacketOpcodes.QuestUpdateQuestVarReq)
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
public class PacketQuestUpdateQuestVarRsp extends BasePacket {
|
|
||||||
|
@Opcodes(PacketOpcodes.QuestUpdateQuestVarReq)
|
||||||
public PacketQuestUpdateQuestVarRsp(int questId) {
|
public class PacketQuestUpdateQuestVarRsp extends BasePacket {
|
||||||
super(PacketOpcodes.QuestUpdateQuestVarRsp);
|
|
||||||
var rsp =
|
public PacketQuestUpdateQuestVarRsp(QuestUpdateQuestVarReq req) {
|
||||||
QuestUpdateQuestVarRspOuterClass.QuestUpdateQuestVarRsp.newBuilder()
|
this(req, Retcode.RET_SUCC);
|
||||||
.setQuestId(questId)
|
}
|
||||||
.build();
|
|
||||||
this.setData(rsp);
|
public PacketQuestUpdateQuestVarRsp(QuestUpdateQuestVarReq req, Retcode retcode) {
|
||||||
}
|
super(PacketOpcodes.QuestUpdateQuestVarRsp);
|
||||||
}
|
var rsp =
|
||||||
|
QuestUpdateQuestVarRspOuterClass.QuestUpdateQuestVarRsp.newBuilder()
|
||||||
|
.setQuestId(req.getQuestId())
|
||||||
|
.setParentQuestId(req.getParentQuestId())
|
||||||
|
.setRetcode(retcode.getNumber())
|
||||||
|
.build();
|
||||||
|
this.setData(rsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,31 +1,29 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.ScenePointUnlockNotifyOuterClass.ScenePointUnlockNotify;
|
import emu.grasscutter.net.proto.ScenePointUnlockNotifyOuterClass.ScenePointUnlockNotify;
|
||||||
|
|
||||||
public class PacketScenePointUnlockNotify extends BasePacket {
|
public class PacketScenePointUnlockNotify extends BasePacket {
|
||||||
public PacketScenePointUnlockNotify(int sceneId, int pointId) {
|
public PacketScenePointUnlockNotify(int sceneId, int pointId) {
|
||||||
super(PacketOpcodes.ScenePointUnlockNotify);
|
super(PacketOpcodes.ScenePointUnlockNotify);
|
||||||
|
|
||||||
ScenePointUnlockNotify.Builder p =
|
ScenePointUnlockNotify.Builder p =
|
||||||
ScenePointUnlockNotify.newBuilder()
|
ScenePointUnlockNotify.newBuilder()
|
||||||
.setSceneId(sceneId)
|
.setSceneId(sceneId)
|
||||||
.addPointList(pointId)
|
.addPointList(pointId);
|
||||||
.addUnhidePointList(pointId);
|
|
||||||
|
this.setData(p);
|
||||||
this.setData(p);
|
}
|
||||||
}
|
|
||||||
|
public PacketScenePointUnlockNotify(int sceneId, Iterable<Integer> pointIds) {
|
||||||
public PacketScenePointUnlockNotify(int sceneId, Iterable<Integer> pointIds) {
|
super(PacketOpcodes.ScenePointUnlockNotify);
|
||||||
super(PacketOpcodes.ScenePointUnlockNotify);
|
|
||||||
|
ScenePointUnlockNotify.Builder p =
|
||||||
ScenePointUnlockNotify.Builder p =
|
ScenePointUnlockNotify.newBuilder()
|
||||||
ScenePointUnlockNotify.newBuilder()
|
.setSceneId(sceneId)
|
||||||
.setSceneId(sceneId)
|
.addAllPointList(pointIds);
|
||||||
.addAllPointList(pointIds)
|
|
||||||
.addAllUnhidePointList(pointIds);
|
this.setData(p);
|
||||||
|
}
|
||||||
this.setData(p);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,36 +1,27 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.SceneTimeNotifyOuterClass.SceneTimeNotify;
|
import emu.grasscutter.net.proto.SceneTimeNotifyOuterClass.SceneTimeNotify;
|
||||||
|
|
||||||
public class PacketSceneTimeNotify extends BasePacket {
|
public class PacketSceneTimeNotify extends BasePacket {
|
||||||
|
|
||||||
public PacketSceneTimeNotify(Player player) {
|
public PacketSceneTimeNotify(Player player) {
|
||||||
super(PacketOpcodes.SceneTimeNotify);
|
this(player.getScene());
|
||||||
|
}
|
||||||
var proto =
|
|
||||||
SceneTimeNotify.newBuilder()
|
public PacketSceneTimeNotify(Scene scene) {
|
||||||
.setIsPaused(player.isPaused())
|
super(PacketOpcodes.SceneTimeNotify);
|
||||||
.setSceneId(player.getSceneId())
|
|
||||||
.setSceneTime(player.getScene().getSceneTime())
|
var proto =
|
||||||
.build();
|
SceneTimeNotify.newBuilder()
|
||||||
|
.setSceneId(scene.getId())
|
||||||
this.setData(proto);
|
.setSceneTime(scene.getSceneTime())
|
||||||
}
|
.setIsPaused(scene.isPaused())
|
||||||
|
.build();
|
||||||
public PacketSceneTimeNotify(Scene scene) {
|
|
||||||
super(PacketOpcodes.SceneTimeNotify);
|
this.setData(proto);
|
||||||
|
}
|
||||||
var proto =
|
}
|
||||||
SceneTimeNotify.newBuilder()
|
|
||||||
.setSceneId(scene.getId())
|
|
||||||
.setSceneTime(scene.getSceneTime())
|
|
||||||
.setIsPaused(scene.isPaused())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
this.setData(proto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.UnlockPersonalLineRspOuterClass;
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
|
import emu.grasscutter.net.proto.UnlockPersonalLineRspOuterClass;
|
||||||
public class PacketUnlockPersonalLineRsp extends BasePacket {
|
|
||||||
|
public class PacketUnlockPersonalLineRsp extends BasePacket {
|
||||||
public PacketUnlockPersonalLineRsp(int id, int level, int chapterId) {
|
|
||||||
super(PacketOpcodes.UnlockPersonalLineRsp);
|
public PacketUnlockPersonalLineRsp(int id, int level, int chapterId) {
|
||||||
|
super(PacketOpcodes.UnlockPersonalLineRsp);
|
||||||
var proto = UnlockPersonalLineRspOuterClass.UnlockPersonalLineRsp.newBuilder();
|
|
||||||
|
var proto = UnlockPersonalLineRspOuterClass.UnlockPersonalLineRsp.newBuilder();
|
||||||
proto.setPersonalLineId(id).setLevel(level).setChapterId(chapterId);
|
|
||||||
|
proto.setPersonalLineId(id).setLevel(level).setChapterId(chapterId);
|
||||||
this.setData(proto);
|
|
||||||
}
|
this.setData(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PacketUnlockPersonalLineRsp(int id, Retcode retCode) {
|
||||||
|
super(PacketOpcodes.UnlockPersonalLineRsp);
|
||||||
|
|
||||||
|
var proto = UnlockPersonalLineRspOuterClass.UnlockPersonalLineRsp.newBuilder();
|
||||||
|
|
||||||
|
proto.setPersonalLineId(id).setRetcode(retCode.getNumber());
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,67 +1,73 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.entity.EntityVehicle;
|
import emu.grasscutter.game.entity.EntityVehicle;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.proto.VehicleInteractRspOuterClass.VehicleInteractRsp;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType;
|
import emu.grasscutter.net.proto.VehicleInteractRspOuterClass.VehicleInteractRsp;
|
||||||
import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember;
|
import emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType;
|
||||||
|
import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember;
|
||||||
public class PacketVehicleInteractRsp extends BasePacket {
|
|
||||||
|
public class PacketVehicleInteractRsp extends BasePacket {
|
||||||
public PacketVehicleInteractRsp(Player player, int entityId, VehicleInteractType interactType) {
|
|
||||||
super(PacketOpcodes.VehicleInteractRsp);
|
public PacketVehicleInteractRsp(Player player, int entityId, VehicleInteractType interactType) {
|
||||||
VehicleInteractRsp.Builder proto = VehicleInteractRsp.newBuilder();
|
super(PacketOpcodes.VehicleInteractRsp);
|
||||||
|
VehicleInteractRsp.Builder proto = VehicleInteractRsp.newBuilder();
|
||||||
GameEntity vehicle = player.getScene().getEntityById(entityId);
|
|
||||||
|
GameEntity vehicle = player.getScene().getEntityById(entityId);
|
||||||
if (vehicle instanceof EntityVehicle) {
|
|
||||||
proto.setEntityId(vehicle.getId());
|
if (vehicle instanceof EntityVehicle) {
|
||||||
|
proto.setEntityId(vehicle.getId());
|
||||||
VehicleMember vehicleMember =
|
|
||||||
VehicleMember.newBuilder()
|
VehicleMember vehicleMember =
|
||||||
.setUid(player.getUid())
|
VehicleMember.newBuilder()
|
||||||
.setAvatarGuid(player.getTeamManager().getCurrentCharacterGuid())
|
.setUid(player.getUid())
|
||||||
.build();
|
.setAvatarGuid(player.getTeamManager().getCurrentCharacterGuid())
|
||||||
|
.build();
|
||||||
proto.setInteractType(interactType);
|
|
||||||
proto.setMember(vehicleMember);
|
proto.setInteractType(interactType);
|
||||||
|
proto.setMember(vehicleMember);
|
||||||
switch (interactType) {
|
|
||||||
case VEHICLE_INTERACT_TYPE_IN -> {
|
switch (interactType) {
|
||||||
((EntityVehicle) vehicle).getVehicleMembers().add(vehicleMember);
|
case VEHICLE_INTERACT_TYPE_IN -> {
|
||||||
}
|
((EntityVehicle) vehicle).getVehicleMembers().add(vehicleMember);
|
||||||
case VEHICLE_INTERACT_TYPE_OUT -> {
|
player
|
||||||
((EntityVehicle) vehicle).getVehicleMembers().remove(vehicleMember);
|
.getQuestManager()
|
||||||
}
|
.queueEvent(
|
||||||
default -> {}
|
QuestContent.QUEST_CONTENT_ENTER_VEHICLE,
|
||||||
}
|
((EntityVehicle) vehicle).getGadgetId());
|
||||||
}
|
}
|
||||||
this.setData(proto.build());
|
case VEHICLE_INTERACT_TYPE_OUT -> {
|
||||||
}
|
((EntityVehicle) vehicle).getVehicleMembers().remove(vehicleMember);
|
||||||
|
}
|
||||||
public PacketVehicleInteractRsp(
|
default -> {}
|
||||||
EntityVehicle vehicle, VehicleMember vehicleMember, VehicleInteractType interactType) {
|
}
|
||||||
super(PacketOpcodes.VehicleInteractRsp);
|
}
|
||||||
VehicleInteractRsp.Builder proto = VehicleInteractRsp.newBuilder();
|
this.setData(proto.build());
|
||||||
|
}
|
||||||
if (vehicle != null) {
|
|
||||||
proto.setEntityId(vehicle.getId());
|
public PacketVehicleInteractRsp(
|
||||||
proto.setInteractType(interactType);
|
EntityVehicle vehicle, VehicleMember vehicleMember, VehicleInteractType interactType) {
|
||||||
proto.setMember(vehicleMember);
|
super(PacketOpcodes.VehicleInteractRsp);
|
||||||
|
VehicleInteractRsp.Builder proto = VehicleInteractRsp.newBuilder();
|
||||||
switch (interactType) {
|
|
||||||
case VEHICLE_INTERACT_TYPE_IN -> {
|
if (vehicle != null) {
|
||||||
vehicle.getVehicleMembers().add(vehicleMember);
|
proto.setEntityId(vehicle.getId());
|
||||||
}
|
proto.setInteractType(interactType);
|
||||||
case VEHICLE_INTERACT_TYPE_OUT -> {
|
proto.setMember(vehicleMember);
|
||||||
vehicle.getVehicleMembers().remove(vehicleMember);
|
|
||||||
}
|
switch (interactType) {
|
||||||
default -> {}
|
case VEHICLE_INTERACT_TYPE_IN -> {
|
||||||
}
|
vehicle.getVehicleMembers().add(vehicleMember);
|
||||||
}
|
}
|
||||||
this.setData(proto.build());
|
case VEHICLE_INTERACT_TYPE_OUT -> {
|
||||||
}
|
vehicle.getVehicleMembers().remove(vehicleMember);
|
||||||
}
|
}
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setData(proto.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,62 +1,66 @@
|
|||||||
package emu.grasscutter.server.scheduler;
|
package emu.grasscutter.server.scheduler;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
/** This class works the same as a runnable, except with more information. */
|
/** This class works the same as a runnable, except with more information. */
|
||||||
public final class ServerTask implements Runnable {
|
public final class ServerTask implements Runnable {
|
||||||
/* The runnable to run. */
|
/* The runnable to run. */
|
||||||
private final Runnable runnable;
|
private final Runnable runnable;
|
||||||
/* This ID is assigned by the scheduler. */
|
/* This ID is assigned by the scheduler. */
|
||||||
@Getter private final int taskId;
|
@Getter private final int taskId;
|
||||||
/* The period at which the task should be run. */
|
/* The period at which the task should be run. */
|
||||||
/* The delay between the first execute. */
|
/* The delay between the first execute. */
|
||||||
private final int period, delay;
|
private final int period, delay;
|
||||||
/* The amount of times the task has been run. */
|
/* The amount of times the task has been run. */
|
||||||
@Getter private int ticks = 0;
|
@Getter private int ticks = 0;
|
||||||
/* Should the check consider delay? */
|
/* Should the check consider delay? */
|
||||||
private boolean considerDelay = true;
|
private boolean considerDelay = true;
|
||||||
|
|
||||||
public ServerTask(Runnable runnable, int taskId, int period, int delay) {
|
public ServerTask(Runnable runnable, int taskId, int period, int delay) {
|
||||||
this.runnable = runnable;
|
this.runnable = runnable;
|
||||||
this.taskId = taskId;
|
this.taskId = taskId;
|
||||||
this.period = period;
|
this.period = period;
|
||||||
this.delay = delay;
|
this.delay = delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cancels the task from running the next time. */
|
/** Cancels the task from running the next time. */
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
Grasscutter.getGameServer().getScheduler().cancelTask(this.taskId);
|
Grasscutter.getGameServer().getScheduler().cancelTask(this.taskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the task should run at the current tick.
|
* Checks if the task should run at the current tick.
|
||||||
*
|
*
|
||||||
* @return True if the task should run, false otherwise.
|
* @return True if the task should run, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean shouldRun() {
|
public boolean shouldRun() {
|
||||||
if (this.delay != -1 && this.considerDelay) {
|
// Increase tick count.
|
||||||
this.considerDelay = false;
|
var ticks = this.ticks++;
|
||||||
return this.ticks == this.delay;
|
if (this.delay != -1 && this.considerDelay) {
|
||||||
} else if (this.period != -1) return this.ticks % this.period == 0;
|
this.considerDelay = false;
|
||||||
else return true;
|
return ticks == this.delay;
|
||||||
}
|
} else if (this.period != -1) return ticks % this.period == 0;
|
||||||
|
else return true;
|
||||||
/**
|
}
|
||||||
* Checks if the task should be canceled.
|
|
||||||
*
|
/**
|
||||||
* @return True if the task should be canceled, false otherwise.
|
* Checks if the task should be canceled.
|
||||||
*/
|
*
|
||||||
public boolean shouldCancel() {
|
* @return True if the task should be canceled, false otherwise.
|
||||||
return this.period == -1;
|
*/
|
||||||
}
|
public boolean shouldCancel() {
|
||||||
|
return this.period == -1 && ticks >= delay;
|
||||||
/** Runs the task. */
|
}
|
||||||
@Override
|
|
||||||
public void run() {
|
/** Runs the task. */
|
||||||
// Run the runnable.
|
@Override
|
||||||
this.runnable.run();
|
public void run() {
|
||||||
// Increase tick count.
|
// Run the runnable.
|
||||||
this.ticks++;
|
try {
|
||||||
}
|
this.runnable.run();
|
||||||
}
|
} catch (Exception ex) {
|
||||||
|
Grasscutter.getLogger().error("Exception during task: ", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
package emu.grasscutter.task;
|
package emu.grasscutter.task;
|
||||||
|
|
||||||
import org.quartz.Job;
|
import org.quartz.*;
|
||||||
import org.quartz.JobExecutionException;
|
|
||||||
import org.quartz.PersistJobDataAfterExecution;
|
@PersistJobDataAfterExecution
|
||||||
|
public abstract class TaskHandler implements Job {
|
||||||
@PersistJobDataAfterExecution
|
public void restartExecute() throws JobExecutionException {
|
||||||
public abstract class TaskHandler implements Job {
|
execute(null);
|
||||||
public void restartExecute() throws JobExecutionException {
|
}
|
||||||
execute(null);
|
|
||||||
}
|
public abstract void onEnable();
|
||||||
|
|
||||||
public abstract void onEnable();
|
public abstract void onDisable();
|
||||||
|
}
|
||||||
public abstract void onDisable();
|
|
||||||
}
|
|
||||||
|
@ -1,391 +1,394 @@
|
|||||||
package emu.grasscutter.tools;
|
package emu.grasscutter.tools;
|
||||||
|
|
||||||
import static emu.grasscutter.utils.FileUtils.getResourcePath;
|
import static emu.grasscutter.utils.FileUtils.getResourcePath;
|
||||||
import static emu.grasscutter.utils.Language.getTextMapKey;
|
import static emu.grasscutter.utils.Language.getTextMapKey;
|
||||||
|
|
||||||
import emu.grasscutter.GameConstants;
|
import emu.grasscutter.GameConstants;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.command.CommandHandler;
|
import emu.grasscutter.command.CommandHandler;
|
||||||
import emu.grasscutter.command.CommandMap;
|
import emu.grasscutter.command.CommandMap;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.ResourceLoader;
|
import emu.grasscutter.data.ResourceLoader;
|
||||||
import emu.grasscutter.data.excels.ItemData;
|
import emu.grasscutter.data.excels.ItemData;
|
||||||
import emu.grasscutter.data.excels.achievement.AchievementData;
|
import emu.grasscutter.data.excels.achievement.AchievementData;
|
||||||
import emu.grasscutter.data.excels.avatar.AvatarData;
|
import emu.grasscutter.data.excels.avatar.AvatarData;
|
||||||
import emu.grasscutter.game.inventory.MaterialType;
|
import emu.grasscutter.game.inventory.MaterialType;
|
||||||
import emu.grasscutter.utils.Language;
|
import emu.grasscutter.utils.Language;
|
||||||
import emu.grasscutter.utils.Language.TextStrings;
|
import emu.grasscutter.utils.Language.TextStrings;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import java.util.stream.LongStream;
|
import java.util.stream.LongStream;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
|
||||||
public final class Tools {
|
public final class Tools {
|
||||||
/**
|
/**
|
||||||
* This generates the GM handbooks with a message by default.
|
* This generates the GM handbooks with a message by default.
|
||||||
*
|
*
|
||||||
* @throws Exception If an error occurs while generating the handbooks.
|
* @throws Exception If an error occurs while generating the handbooks.
|
||||||
*/
|
*/
|
||||||
public static void createGmHandbooks() throws Exception {
|
public static void createGmHandbooks() throws Exception {
|
||||||
Tools.createGmHandbooks(true);
|
Tools.createGmHandbooks(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a GM handbook for each language.
|
* Generates a GM handbook for each language.
|
||||||
*
|
*
|
||||||
* @param message Should a message be printed to the console?
|
* @param message Should a message be printed to the console?
|
||||||
* @throws Exception If an error occurs while generating the handbooks.
|
* @throws Exception If an error occurs while generating the handbooks.
|
||||||
*/
|
*/
|
||||||
public static void createGmHandbooks(boolean message) throws Exception {
|
public static void createGmHandbooks(boolean message) throws Exception {
|
||||||
val languages = Language.TextStrings.getLanguages();
|
val languages = Language.TextStrings.getLanguages();
|
||||||
|
|
||||||
ResourceLoader.loadAll();
|
ResourceLoader.loadAll();
|
||||||
val mainQuestTitles =
|
val mainQuestTitles =
|
||||||
new Int2IntRBTreeMap(
|
new Int2IntRBTreeMap(
|
||||||
GameData.getMainQuestDataMap().int2ObjectEntrySet().stream()
|
GameData.getMainQuestDataMap().int2ObjectEntrySet().stream()
|
||||||
.collect(
|
.collect(
|
||||||
Collectors.toMap(
|
Collectors.toMap(
|
||||||
e -> e.getIntKey(), e -> (int) e.getValue().getTitleTextMapHash())));
|
e -> e.getIntKey(), e -> (int) e.getValue().getTitleTextMapHash())));
|
||||||
// val questDescs = new
|
// val questDescs = new
|
||||||
// Int2IntRBTreeMap(GameData.getQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getDescTextMapHash())));
|
// Int2IntRBTreeMap(GameData.getQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getDescTextMapHash())));
|
||||||
|
|
||||||
val avatarDataMap = new Int2ObjectRBTreeMap<>(GameData.getAvatarDataMap());
|
val avatarDataMap = new Int2ObjectRBTreeMap<>(GameData.getAvatarDataMap());
|
||||||
val itemDataMap = new Int2ObjectRBTreeMap<>(GameData.getItemDataMap());
|
val itemDataMap = new Int2ObjectRBTreeMap<>(GameData.getItemDataMap());
|
||||||
val monsterDataMap = new Int2ObjectRBTreeMap<>(GameData.getMonsterDataMap());
|
val monsterDataMap = new Int2ObjectRBTreeMap<>(GameData.getMonsterDataMap());
|
||||||
val sceneDataMap = new Int2ObjectRBTreeMap<>(GameData.getSceneDataMap());
|
val sceneDataMap = new Int2ObjectRBTreeMap<>(GameData.getSceneDataMap());
|
||||||
val questDataMap = new Int2ObjectRBTreeMap<>(GameData.getQuestDataMap());
|
val questDataMap = new Int2ObjectRBTreeMap<>(GameData.getQuestDataMap());
|
||||||
val achievementDataMap = new Int2ObjectRBTreeMap<>(GameData.getAchievementDataMap());
|
val achievementDataMap = new Int2ObjectRBTreeMap<>(GameData.getAchievementDataMap());
|
||||||
|
|
||||||
Function<SortedMap<?, ?>, String> getPad = m -> "%" + m.lastKey().toString().length() + "s : ";
|
Function<SortedMap<?, ?>, String> getPad = m -> "%" + m.lastKey().toString().length() + "s : ";
|
||||||
|
|
||||||
// Create builders and helper functions
|
// Create builders and helper functions
|
||||||
val handbookBuilders =
|
val handbookBuilders =
|
||||||
IntStream.range(0, TextStrings.NUM_LANGUAGES).mapToObj(i -> new StringBuilder()).toList();
|
IntStream.range(0, TextStrings.NUM_LANGUAGES).mapToObj(i -> new StringBuilder()).toList();
|
||||||
var h =
|
var h =
|
||||||
new Object() {
|
new Object() {
|
||||||
void newLine(String line) {
|
void newLine(String line) {
|
||||||
handbookBuilders.forEach(b -> b.append(line + "\n"));
|
handbookBuilders.forEach(b -> b.append(line + "\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void newSection(String title) {
|
void newSection(String title) {
|
||||||
newLine("\n\n// " + title);
|
newLine("\n\n// " + title);
|
||||||
}
|
}
|
||||||
|
|
||||||
void newTranslatedLine(String template, TextStrings... textstrings) {
|
void newTranslatedLine(String template, TextStrings... textstrings) {
|
||||||
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
||||||
String s = template;
|
String s = template;
|
||||||
for (int j = 0; j < textstrings.length; j++)
|
for (int j = 0; j < textstrings.length; j++)
|
||||||
s = s.replace("{" + j + "}", textstrings[j].strings[i]);
|
s = s.replace("{" + j + "}", textstrings[j].strings[i]);
|
||||||
handbookBuilders.get(i).append(s + "\n");
|
handbookBuilders.get(i).append(s + "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void newTranslatedLine(String template, long... hashes) {
|
void newTranslatedLine(String template, long... hashes) {
|
||||||
newTranslatedLine(
|
newTranslatedLine(
|
||||||
template,
|
template,
|
||||||
LongStream.of(hashes)
|
LongStream.of(hashes)
|
||||||
.mapToObj(hash -> getTextMapKey(hash))
|
.mapToObj(hash -> getTextMapKey(hash))
|
||||||
.toArray(TextStrings[]::new));
|
.toArray(TextStrings[]::new));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Preamble
|
// Preamble
|
||||||
h.newLine("// Grasscutter " + GameConstants.VERSION + " GM Handbook");
|
h.newLine("// Grasscutter " + GameConstants.VERSION + " GM Handbook");
|
||||||
h.newLine(
|
h.newLine(
|
||||||
"// Created "
|
"// Created "
|
||||||
+ DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()));
|
+ DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()));
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
h.newSection("Commands");
|
h.newSection("Commands");
|
||||||
final List<CommandHandler> cmdList = CommandMap.getInstance().getHandlersAsList();
|
final List<CommandHandler> cmdList = CommandMap.getInstance().getHandlersAsList();
|
||||||
final String padCmdLabel =
|
final String padCmdLabel =
|
||||||
"%"
|
"%"
|
||||||
+ cmdList.stream()
|
+ cmdList.stream()
|
||||||
.map(CommandHandler::getLabel)
|
.map(CommandHandler::getLabel)
|
||||||
.map(String::length)
|
.map(String::length)
|
||||||
.max(Integer::compare)
|
.max(Integer::compare)
|
||||||
.get()
|
.get()
|
||||||
+ "s : ";
|
+ "s : ";
|
||||||
for (CommandHandler cmd : cmdList) {
|
for (CommandHandler cmd : cmdList) {
|
||||||
final String label = padCmdLabel.formatted(cmd.getLabel());
|
final String label = padCmdLabel.formatted(cmd.getLabel());
|
||||||
final String descKey = cmd.getDescriptionKey();
|
final String descKey = cmd.getDescriptionKey();
|
||||||
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
||||||
String desc =
|
String desc =
|
||||||
languages.get(i).get(descKey).replace("\n", "\n\t\t\t\t").replace("\t", " ");
|
languages.get(i).get(descKey).replace("\n", "\n\t\t\t\t").replace("\t", " ");
|
||||||
handbookBuilders.get(i).append(label + desc + "\n");
|
handbookBuilders.get(i).append(label + desc + "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Avatars
|
// Avatars
|
||||||
h.newSection("Avatars");
|
h.newSection("Avatars");
|
||||||
val avatarPre = getPad.apply(avatarDataMap);
|
val avatarPre = getPad.apply(avatarDataMap);
|
||||||
avatarDataMap.forEach(
|
avatarDataMap.forEach(
|
||||||
(id, data) ->
|
(id, data) ->
|
||||||
h.newTranslatedLine(avatarPre.formatted(id) + "{0}", data.getNameTextMapHash()));
|
h.newTranslatedLine(avatarPre.formatted(id) + "{0}", data.getNameTextMapHash()));
|
||||||
// Items
|
// Items
|
||||||
h.newSection("Items");
|
h.newSection("Items");
|
||||||
val itemPre = getPad.apply(itemDataMap);
|
val itemPre = getPad.apply(itemDataMap);
|
||||||
itemDataMap.forEach(
|
itemDataMap.forEach(
|
||||||
(id, data) -> {
|
(id, data) -> {
|
||||||
val name = getTextMapKey(data.getNameTextMapHash());
|
val name = getTextMapKey(data.getNameTextMapHash());
|
||||||
if (Objects.requireNonNull(data.getMaterialType()) == MaterialType.MATERIAL_BGM) {
|
switch (data.getMaterialType()) {
|
||||||
val bgmName =
|
case MATERIAL_BGM:
|
||||||
Optional.ofNullable(data.getItemUse())
|
val bgmName =
|
||||||
.map(u -> u.get(0))
|
Optional.ofNullable(data.getItemUse())
|
||||||
.map(u -> u.getUseParam())
|
.map(u -> u.get(0))
|
||||||
.filter(u -> u.length > 0)
|
.map(u -> u.getUseParam())
|
||||||
.map(u -> Integer.parseInt(u[0]))
|
.filter(u -> u.length > 0)
|
||||||
.map(bgmId -> GameData.getHomeWorldBgmDataMap().get(bgmId))
|
.map(u -> Integer.parseInt(u[0]))
|
||||||
.map(bgm -> bgm.getBgmNameTextMapHash())
|
.map(bgmId -> GameData.getHomeWorldBgmDataMap().get(bgmId))
|
||||||
.map(hash -> getTextMapKey(hash));
|
.map(bgm -> bgm.getBgmNameTextMapHash())
|
||||||
if (bgmName.isPresent()) {
|
.map(hash -> getTextMapKey(hash));
|
||||||
h.newTranslatedLine(itemPre.formatted(id) + "{0} - {1}", name, bgmName.get());
|
if (bgmName.isPresent()) {
|
||||||
return;
|
h.newTranslatedLine(itemPre.formatted(id) + "{0} - {1}", name, bgmName.get());
|
||||||
} // Fall-through
|
return;
|
||||||
}
|
} // Fall-through
|
||||||
h.newTranslatedLine(itemPre.formatted(id) + "{0}", name);
|
default:
|
||||||
});
|
h.newTranslatedLine(itemPre.formatted(id) + "{0}", name);
|
||||||
// Monsters
|
return;
|
||||||
h.newSection("Monsters");
|
}
|
||||||
val monsterPre = getPad.apply(monsterDataMap);
|
});
|
||||||
monsterDataMap.forEach(
|
// Monsters
|
||||||
(id, data) ->
|
h.newSection("Monsters");
|
||||||
h.newTranslatedLine(
|
val monsterPre = getPad.apply(monsterDataMap);
|
||||||
monsterPre.formatted(id) + data.getMonsterName() + " - {0}",
|
monsterDataMap.forEach(
|
||||||
data.getNameTextMapHash()));
|
(id, data) ->
|
||||||
// Scenes - no translations
|
h.newTranslatedLine(
|
||||||
h.newSection("Scenes");
|
monsterPre.formatted(id) + data.getMonsterName() + " - {0}",
|
||||||
val padSceneId = getPad.apply(sceneDataMap);
|
data.getNameTextMapHash()));
|
||||||
sceneDataMap.forEach((id, data) -> h.newLine(padSceneId.formatted(id) + data.getScriptData()));
|
// Scenes - no translations
|
||||||
// Quests
|
h.newSection("Scenes");
|
||||||
h.newSection("Quests");
|
val padSceneId = getPad.apply(sceneDataMap);
|
||||||
val padQuestId = getPad.apply(questDataMap);
|
sceneDataMap.forEach((id, data) -> h.newLine(padSceneId.formatted(id) + data.getScriptData()));
|
||||||
questDataMap.forEach(
|
// Quests
|
||||||
(id, data) ->
|
h.newSection("Quests");
|
||||||
h.newTranslatedLine(
|
val padQuestId = getPad.apply(questDataMap);
|
||||||
padQuestId.formatted(id) + "{0} - {1}",
|
questDataMap.forEach(
|
||||||
mainQuestTitles.get(data.getMainId()),
|
(id, data) ->
|
||||||
data.getDescTextMapHash()));
|
h.newTranslatedLine(
|
||||||
// Achievements
|
padQuestId.formatted(id) + "{0} - {1}",
|
||||||
h.newSection("Achievements");
|
mainQuestTitles.get(data.getMainId()),
|
||||||
val padAchievementId = getPad.apply(achievementDataMap);
|
data.getDescTextMapHash()));
|
||||||
achievementDataMap.values().stream()
|
// Achievements
|
||||||
.filter(AchievementData::isUsed)
|
h.newSection("Achievements");
|
||||||
.forEach(
|
val padAchievementId = getPad.apply(achievementDataMap);
|
||||||
data -> {
|
achievementDataMap.values().stream()
|
||||||
h.newTranslatedLine(
|
.filter(AchievementData::isUsed)
|
||||||
padAchievementId.formatted(data.getId()) + "{0} - {1}",
|
.forEach(
|
||||||
data.getTitleTextMapHash(),
|
data -> {
|
||||||
data.getDescTextMapHash());
|
h.newTranslatedLine(
|
||||||
});
|
padAchievementId.formatted(data.getId()) + "{0} - {1}",
|
||||||
|
data.getTitleTextMapHash(),
|
||||||
// Write txt files
|
data.getDescTextMapHash());
|
||||||
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
});
|
||||||
File GMHandbookOutputpath = new File("./GM Handbook");
|
|
||||||
GMHandbookOutputpath.mkdir();
|
// Write txt files
|
||||||
final String fileName =
|
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
||||||
"./GM Handbook/GM Handbook - %s.txt".formatted(TextStrings.ARR_LANGUAGES[i]);
|
File GMHandbookOutputpath = new File("./GM Handbook");
|
||||||
try (PrintWriter writer =
|
GMHandbookOutputpath.mkdir();
|
||||||
new PrintWriter(
|
final String fileName =
|
||||||
new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8),
|
"./GM Handbook/GM Handbook - %s.txt".formatted(TextStrings.ARR_LANGUAGES[i]);
|
||||||
false)) {
|
try (PrintWriter writer =
|
||||||
writer.write(handbookBuilders.get(i).toString());
|
new PrintWriter(
|
||||||
}
|
new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8),
|
||||||
}
|
false)) {
|
||||||
|
writer.write(handbookBuilders.get(i).toString());
|
||||||
if (message) Grasscutter.getLogger().info("GM Handbooks generated!");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> createGachaMappingJsons() {
|
if (message) Grasscutter.getLogger().info("GM Handbooks generated!");
|
||||||
final int NUM_LANGUAGES = Language.TextStrings.NUM_LANGUAGES;
|
}
|
||||||
final Language.TextStrings CHARACTER = Language.getTextMapKey(4233146695L); // "Character" in EN
|
|
||||||
final Language.TextStrings WEAPON = Language.getTextMapKey(4231343903L); // "Weapon" in EN
|
public static List<String> createGachaMappingJsons() {
|
||||||
final Language.TextStrings STANDARD_WISH =
|
final int NUM_LANGUAGES = Language.TextStrings.NUM_LANGUAGES;
|
||||||
Language.getTextMapKey(332935371L); // "Standard Wish" in EN
|
final Language.TextStrings CHARACTER = Language.getTextMapKey(4233146695L); // "Character" in EN
|
||||||
final Language.TextStrings CHARACTER_EVENT_WISH =
|
final Language.TextStrings WEAPON = Language.getTextMapKey(4231343903L); // "Weapon" in EN
|
||||||
Language.getTextMapKey(2272170627L); // "Character Event Wish" in EN
|
final Language.TextStrings STANDARD_WISH =
|
||||||
final Language.TextStrings CHARACTER_EVENT_WISH_2 =
|
Language.getTextMapKey(332935371L); // "Standard Wish" in EN
|
||||||
Language.getTextMapKey(3352513147L); // "Character Event Wish-2" in EN
|
final Language.TextStrings CHARACTER_EVENT_WISH =
|
||||||
final Language.TextStrings WEAPON_EVENT_WISH =
|
Language.getTextMapKey(2272170627L); // "Character Event Wish" in EN
|
||||||
Language.getTextMapKey(2864268523L); // "Weapon Event Wish" in EN
|
final Language.TextStrings CHARACTER_EVENT_WISH_2 =
|
||||||
final List<StringBuilder> sbs = new ArrayList<>(NUM_LANGUAGES);
|
Language.getTextMapKey(3352513147L); // "Character Event Wish-2" in EN
|
||||||
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
|
final Language.TextStrings WEAPON_EVENT_WISH =
|
||||||
sbs.add(new StringBuilder("{\n")); // Web requests should never need Windows line endings
|
Language.getTextMapKey(2864268523L); // "Weapon Event Wish" in EN
|
||||||
|
final List<StringBuilder> sbs = new ArrayList<>(NUM_LANGUAGES);
|
||||||
// Avatars
|
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
|
||||||
GameData.getAvatarDataMap()
|
sbs.add(new StringBuilder("{\n")); // Web requests should never need Windows line endings
|
||||||
.keySet()
|
|
||||||
.intStream()
|
// Avatars
|
||||||
.sorted()
|
GameData.getAvatarDataMap()
|
||||||
.forEach(
|
.keySet()
|
||||||
id -> {
|
.intStream()
|
||||||
AvatarData data = GameData.getAvatarDataMap().get(id);
|
.sorted()
|
||||||
int avatarID = data.getId();
|
.forEach(
|
||||||
if (avatarID >= 11000000) { // skip test avatar
|
id -> {
|
||||||
return;
|
AvatarData data = GameData.getAvatarDataMap().get(id);
|
||||||
}
|
int avatarID = data.getId();
|
||||||
String color =
|
if (avatarID >= 11000000) { // skip test avatar
|
||||||
switch (data.getQualityType()) {
|
return;
|
||||||
case "QUALITY_PURPLE" -> "purple";
|
}
|
||||||
case "QUALITY_ORANGE" -> "yellow";
|
String color =
|
||||||
case "QUALITY_BLUE" -> "blue";
|
switch (data.getQualityType()) {
|
||||||
default -> "";
|
case "QUALITY_PURPLE" -> "purple";
|
||||||
};
|
case "QUALITY_ORANGE" -> "yellow";
|
||||||
Language.TextStrings avatarName = Language.getTextMapKey(data.getNameTextMapHash());
|
case "QUALITY_BLUE" -> "blue";
|
||||||
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
default -> "";
|
||||||
sbs.get(langIdx)
|
};
|
||||||
.append("\t\"")
|
Language.TextStrings avatarName = Language.getTextMapKey(data.getNameTextMapHash());
|
||||||
.append(avatarID % 1000 + 1000)
|
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
||||||
.append("\": [\"")
|
sbs.get(langIdx)
|
||||||
.append(avatarName.get(langIdx))
|
.append("\t\"")
|
||||||
.append(" (")
|
.append(avatarID % 1000 + 1000)
|
||||||
.append(CHARACTER.get(langIdx))
|
.append("\": [\"")
|
||||||
.append(")\", \"")
|
.append(avatarName.get(langIdx))
|
||||||
.append(color)
|
.append(" (")
|
||||||
.append("\"],\n");
|
.append(CHARACTER.get(langIdx))
|
||||||
}
|
.append(")\", \"")
|
||||||
});
|
.append(color)
|
||||||
|
.append("\"],\n");
|
||||||
// Weapons
|
}
|
||||||
GameData.getItemDataMap()
|
});
|
||||||
.keySet()
|
|
||||||
.intStream()
|
// Weapons
|
||||||
.sorted()
|
GameData.getItemDataMap()
|
||||||
.forEach(
|
.keySet()
|
||||||
id -> {
|
.intStream()
|
||||||
ItemData data = GameData.getItemDataMap().get(id);
|
.sorted()
|
||||||
if (data.getId() <= 11101 || data.getId() >= 20000) {
|
.forEach(
|
||||||
return; // skip non weapon items
|
id -> {
|
||||||
}
|
ItemData data = GameData.getItemDataMap().get(id);
|
||||||
String color =
|
if (data.getId() <= 11101 || data.getId() >= 20000) {
|
||||||
switch (data.getRankLevel()) {
|
return; // skip non weapon items
|
||||||
case 3 -> "blue";
|
}
|
||||||
case 4 -> "purple";
|
String color =
|
||||||
case 5 -> "yellow";
|
switch (data.getRankLevel()) {
|
||||||
default -> null;
|
case 3 -> "blue";
|
||||||
};
|
case 4 -> "purple";
|
||||||
if (color == null) return; // skip unnecessary entries
|
case 5 -> "yellow";
|
||||||
Language.TextStrings weaponName = Language.getTextMapKey(data.getNameTextMapHash());
|
default -> null;
|
||||||
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
};
|
||||||
sbs.get(langIdx)
|
if (color == null) return; // skip unnecessary entries
|
||||||
.append("\t\"")
|
Language.TextStrings weaponName = Language.getTextMapKey(data.getNameTextMapHash());
|
||||||
.append(data.getId())
|
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
||||||
.append("\": [\"")
|
sbs.get(langIdx)
|
||||||
.append(weaponName.get(langIdx).replaceAll("\"", "\\\\\""))
|
.append("\t\"")
|
||||||
.append(" (")
|
.append(data.getId())
|
||||||
.append(WEAPON.get(langIdx))
|
.append("\": [\"")
|
||||||
.append(")\", \"")
|
.append(weaponName.get(langIdx).replaceAll("\"", "\\\\\""))
|
||||||
.append(color)
|
.append(" (")
|
||||||
.append("\"],\n");
|
.append(WEAPON.get(langIdx))
|
||||||
}
|
.append(")\", \"")
|
||||||
});
|
.append(color)
|
||||||
|
.append("\"],\n");
|
||||||
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
}
|
||||||
sbs.get(langIdx)
|
});
|
||||||
.append("\t\"200\": \"")
|
|
||||||
.append(STANDARD_WISH.get(langIdx))
|
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
||||||
.append("\",\n\t\"301\": \"")
|
sbs.get(langIdx)
|
||||||
.append(CHARACTER_EVENT_WISH.get(langIdx))
|
.append("\t\"200\": \"")
|
||||||
.append("\",\n\t\"400\": \"")
|
.append(STANDARD_WISH.get(langIdx))
|
||||||
.append(CHARACTER_EVENT_WISH_2.get(langIdx))
|
.append("\",\n\t\"301\": \"")
|
||||||
.append("\",\n\t\"302\": \"")
|
.append(CHARACTER_EVENT_WISH.get(langIdx))
|
||||||
.append(WEAPON_EVENT_WISH.get(langIdx))
|
.append("\",\n\t\"400\": \"")
|
||||||
.append("\"\n}");
|
.append(CHARACTER_EVENT_WISH_2.get(langIdx))
|
||||||
}
|
.append("\",\n\t\"302\": \"")
|
||||||
return sbs.stream().map(StringBuilder::toString).toList();
|
.append(WEAPON_EVENT_WISH.get(langIdx))
|
||||||
}
|
.append("\"\n}");
|
||||||
|
}
|
||||||
public static void createGachaMappings(Path location) throws IOException {
|
return sbs.stream().map(StringBuilder::toString).toList();
|
||||||
ResourceLoader.loadResources();
|
}
|
||||||
List<String> jsons = createGachaMappingJsons();
|
|
||||||
var usedLocales = new HashSet<String>();
|
public static void createGachaMappings(Path location) throws IOException {
|
||||||
StringBuilder sb = new StringBuilder("mappings = {\n");
|
ResourceLoader.loadResources();
|
||||||
for (int i = 0; i < Language.TextStrings.NUM_LANGUAGES; i++) {
|
List<String> jsons = createGachaMappingJsons();
|
||||||
String locale =
|
var usedLocales = new HashSet<String>();
|
||||||
Language.TextStrings.ARR_GC_LANGUAGES[i]
|
StringBuilder sb = new StringBuilder("mappings = {\n");
|
||||||
.toLowerCase(); // TODO: change the templates to not use lowercased locale codes
|
for (int i = 0; i < Language.TextStrings.NUM_LANGUAGES; i++) {
|
||||||
if (usedLocales.add(
|
String locale =
|
||||||
locale)) { // Some locales fallback to en-us, we don't want to redefine en-us with
|
Language.TextStrings.ARR_GC_LANGUAGES[i]
|
||||||
// vietnamese strings
|
.toLowerCase(); // TODO: change the templates to not use lowercased locale codes
|
||||||
sb.append("\t\"%s\": ".formatted(locale));
|
if (usedLocales.add(
|
||||||
sb.append(jsons.get(i).replace("\n", "\n\t") + ",\n");
|
locale)) { // Some locales fallback to en-us, we don't want to redefine en-us with
|
||||||
}
|
// vietnamese strings
|
||||||
}
|
sb.append("\t\"%s\": ".formatted(locale));
|
||||||
sb.setLength(sb.length() - 2); // Delete trailing ",\n"
|
sb.append(jsons.get(i).replace("\n", "\n\t") + ",\n");
|
||||||
sb.append("\n}");
|
}
|
||||||
|
}
|
||||||
Files.createDirectories(location.getParent());
|
sb.setLength(sb.length() - 2); // Delete trailing ",\n"
|
||||||
Files.writeString(location, sb);
|
sb.append("\n}");
|
||||||
Grasscutter.getLogger().debug("Mappings generated to " + location);
|
|
||||||
}
|
Files.createDirectories(location.getParent());
|
||||||
|
Files.writeString(location, sb);
|
||||||
public static List<String> getAvailableLanguage() {
|
Grasscutter.getLogger().debug("Mappings generated to " + location);
|
||||||
List<String> availableLangList = new ArrayList<>();
|
}
|
||||||
try {
|
|
||||||
Files.newDirectoryStream(getResourcePath("TextMap"), "TextMap*.json")
|
public static List<String> getAvailableLanguage() {
|
||||||
.forEach(
|
List<String> availableLangList = new ArrayList<>();
|
||||||
path -> {
|
try {
|
||||||
availableLangList.add(
|
Files.newDirectoryStream(getResourcePath("TextMap"), "TextMap*.json")
|
||||||
path.getFileName()
|
.forEach(
|
||||||
.toString()
|
path -> {
|
||||||
.replace("TextMap", "")
|
availableLangList.add(
|
||||||
.replace(".json", "")
|
path.getFileName()
|
||||||
.toLowerCase());
|
.toString()
|
||||||
});
|
.replace("TextMap", "")
|
||||||
} catch (IOException e) {
|
.replace(".json", "")
|
||||||
Grasscutter.getLogger().error("Failed to get available languages:", e);
|
.toLowerCase());
|
||||||
}
|
});
|
||||||
return availableLangList;
|
} catch (IOException e) {
|
||||||
}
|
Grasscutter.getLogger().error("Failed to get available languages:", e);
|
||||||
|
}
|
||||||
@Deprecated(forRemoval = true, since = "1.2.3")
|
return availableLangList;
|
||||||
public static String getLanguageOption() {
|
}
|
||||||
List<String> availableLangList = getAvailableLanguage();
|
|
||||||
|
@Deprecated(forRemoval = true, since = "1.2.3")
|
||||||
// Use system out for better format
|
public static String getLanguageOption() {
|
||||||
if (availableLangList.size() == 1) {
|
List<String> availableLangList = getAvailableLanguage();
|
||||||
return availableLangList.get(0).toUpperCase();
|
|
||||||
}
|
// Use system out for better format
|
||||||
StringBuilder stagedMessage = new StringBuilder();
|
if (availableLangList.size() == 1) {
|
||||||
stagedMessage.append(
|
return availableLangList.get(0).toUpperCase();
|
||||||
"The following languages mappings are available, please select one: [default: EN] \n");
|
}
|
||||||
|
StringBuilder stagedMessage = new StringBuilder();
|
||||||
StringBuilder groupedLangList = new StringBuilder(">\t");
|
stagedMessage.append(
|
||||||
String input;
|
"The following languages mappings are available, please select one: [default: EN] \n");
|
||||||
int groupedLangCount = 0;
|
|
||||||
|
StringBuilder groupedLangList = new StringBuilder(">\t");
|
||||||
for (String availableLanguage : availableLangList) {
|
String input;
|
||||||
groupedLangCount++;
|
int groupedLangCount = 0;
|
||||||
groupedLangList.append(availableLanguage).append("\t");
|
|
||||||
|
for (String availableLanguage : availableLangList) {
|
||||||
if (groupedLangCount == 6) {
|
groupedLangCount++;
|
||||||
stagedMessage.append(groupedLangList).append("\n");
|
groupedLangList.append(availableLanguage).append("\t");
|
||||||
groupedLangCount = 0;
|
|
||||||
groupedLangList = new StringBuilder(">\t");
|
if (groupedLangCount == 6) {
|
||||||
}
|
stagedMessage.append(groupedLangList).append("\n");
|
||||||
}
|
groupedLangCount = 0;
|
||||||
|
groupedLangList = new StringBuilder(">\t");
|
||||||
if (groupedLangCount > 0) {
|
}
|
||||||
stagedMessage.append(groupedLangList).append("\n");
|
}
|
||||||
}
|
|
||||||
|
if (groupedLangCount > 0) {
|
||||||
stagedMessage.append("\nYour choice: [EN] ");
|
stagedMessage.append(groupedLangList).append("\n");
|
||||||
|
}
|
||||||
input = Grasscutter.getConsole().readLine(stagedMessage.toString());
|
|
||||||
if (availableLangList.contains(input.toLowerCase())) {
|
stagedMessage.append("\nYour choice: [EN] ");
|
||||||
return input.toUpperCase();
|
|
||||||
}
|
input = Grasscutter.getConsole().readLine(stagedMessage.toString());
|
||||||
|
if (availableLangList.contains(input.toLowerCase())) {
|
||||||
Grasscutter.getLogger().info("Invalid option. Will use EN (English) as fallback.");
|
return input.toUpperCase();
|
||||||
return "EN";
|
}
|
||||||
}
|
|
||||||
}
|
Grasscutter.getLogger().info("Invalid option. Will use EN (English) as fallback.");
|
||||||
|
return "EN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user