Commit a8293102 authored by Melledy's avatar Melledy Committed by GitHub
Browse files

Merge branch 'development' into stable

parents 304b9cb8 ecf7a81a
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.PacketChangeMpTeamAvatarRsp;
import java.util.List;
import java.util.ArrayList;
import java.util.HashSet;
import static emu.grasscutter.Configuration.*;
@Command(label = "team", usage = "team <add|remove|set> [avatarId,...] [index|first|last|index-index,...]",
permission = "player.team", permissionTargeted = "player.team.others", description = "commands.team.description")
public final class TeamCommand implements CommandHandler {
private static final int BASE_AVATARID = 10000000;
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
return;
}
switch (args.get(0)) {
case "add":
if (!addCommand(sender, targetPlayer, args)) return;
break;
case "remove":
if (!removeCommand(sender, targetPlayer, args)) return;
break;
case "set":
if (!setCommand(sender, targetPlayer, args)) return;
break;
default:
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
return;
}
targetPlayer.getTeamManager().updateTeamEntities(
new PacketChangeMpTeamAvatarRsp(targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo()));
}
private boolean addCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_usage");
return false;
}
int index = -1;
if (args.size() > 2) {
try {
index = Integer.parseInt(args.get(2)) - 1;
if (index < 0) index = 0;
} catch (Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index");
return false;
}
}
var avatarIds = args.get(1).split(",");
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam);
return false;
}
for (var avatarId: avatarIds) {
int id = Integer.parseInt(avatarId);
var success = addAvatar(sender, targetPlayer, id, index);
if (index > 0) ++index;
}
return true;
}
private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_usage");
return false;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
var avatarCount = currentTeamAvatars.size();
var metaIndexList = args.get(1).split(",");
var indexes = new HashSet<Integer>();
var ignoreList = new ArrayList<Integer>();
for (var metaIndex: metaIndexList) {
// step 1: parse metaIndex to indexes
var subIndexes = transformToIndexes(metaIndex, avatarCount);
if (subIndexes == null) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", metaIndex);
continue;
}
// step 2: get all of the avatar id through indexes
for (var avatarIndex: subIndexes) {
try {
indexes.add(currentTeamAvatars.get(avatarIndex - 1));
} catch (Exception e) {
ignoreList.add(avatarIndex);
continue;
}
}
}
// step 3: check if user remove all of the avatar
if (indexes.size() >= avatarCount) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much");
return false;
}
// step 4: hint user for ignore index
if (!ignoreList.isEmpty()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList);
}
// step 5: remove
currentTeamAvatars.removeAll(indexes);
return true;
}
private boolean setCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 3) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
CommandHandler.sendTranslatedMessage(sender, "commands.team.set_usage");
return false;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
int index;
try {
index = Integer.parseInt(args.get(1)) - 1;
if (index < 0) index = 0;
} catch(Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", args.get(1));
return false;
}
if (index + 1 > currentTeamAvatars.size()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range");
return false;
}
int avatarId;
try {
avatarId = Integer.parseInt(args.get(2));
} catch(Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_parse_avatar_id", args.get(2));
return false;
}
if (avatarId < BASE_AVATARID) {
avatarId += BASE_AVATARID;
}
if (currentTeamAvatars.contains(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
return false;
}
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
return false;
}
currentTeamAvatars.set(index, avatarId);
return true;
}
private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) {
if (avatarId < BASE_AVATARID) {
avatarId += BASE_AVATARID;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
if (currentTeamAvatars.contains(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
return false;
}
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
return false;
}
if (index < 0) {
currentTeamAvatars.add(avatarId);
} else {
currentTeamAvatars.add(index, avatarId);
}
return true;
}
private List<Integer> transformToIndexes(String metaIndexes, int listLength) {
// step 1: check if metaIndexes is a special constants
if (metaIndexes.equals("first")) {
return List.of(1);
} else if (metaIndexes.equals("last")) {
return List.of(listLength);
}
// step 2: check if metaIndexes is a range
if (metaIndexes.contains("-")) {
var range = metaIndexes.split("-");
if (range.length < 2) {
return null;
}
int min, max;
try {
min = switch (range[0]) {
case "first" -> 1;
case "last" -> listLength;
default -> Integer.parseInt(range[0]);
};
max = switch (range[1]) {
case "first" -> 1;
case "last" -> listLength;
default -> Integer.parseInt(range[1]);
};
} catch (Exception e) {
return null;
}
if (min > max) {
min ^= max;
max ^= min;
min ^= max;
}
var indexes = new ArrayList<Integer>();
for (int i = min; i <= max; ++i) {
indexes.add(i);
}
return indexes;
}
// step 3: index is a value, simply return
try {
int index = Integer.parseInt(metaIndexes);
return List.of(index);
} catch (Exception e) {
return null;
}
}
}
......@@ -10,18 +10,13 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "tpall", usage = "tpall",
description = "Teleports all players in your world to your position", permission = "player.tpall")
@Command(label = "tpall", usage = "tpall", permission = "player.tpall", permissionTargeted = "player.tpall.others", description = "commands.teleportAll.description")
public final class TeleportAllCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (targetPlayer == null) {
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
return;
}
if (!targetPlayer.getWorld().isMultiplayer()) {
CommandHandler.sendMessage(sender, translate("commands.teleportAll.error"));
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.error"));
return;
}
......@@ -33,6 +28,6 @@ public final class TeleportAllCommand implements CommandHandler {
player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), pos);
}
CommandHandler.sendMessage(sender, translate("commands.teleportAll.success"));
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success"));
}
}
......@@ -10,8 +10,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "teleport", usage = "teleport <x> <y> <z> [scene id]", aliases = {"tp"},
description = "Change the player's position.", permission = "player.teleport")
@Command(label = "teleport", usage = "teleport <x> <y> <z> [sceneId]", aliases = {"tp"}, permission = "player.teleport", permissionTargeted = "player.teleport.others", description = "commands.teleport.description")
public final class TeleportCommand implements CommandHandler {
private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later
......@@ -27,11 +26,6 @@ public final class TeleportCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (targetPlayer == null) {
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
return;
}
Position pos = targetPlayer.getPos();
float x = pos.getX();
float y = pos.getY();
......@@ -43,7 +37,7 @@ public final class TeleportCommand implements CommandHandler {
try {
sceneId = Integer.parseInt(args.get(3));
}catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate("commands.execution.argument_error"));
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error"));
} // Fallthrough
case 3:
try {
......@@ -51,20 +45,20 @@ public final class TeleportCommand implements CommandHandler {
y = parseRelative(args.get(1), y);
z = parseRelative(args.get(2), z);
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate("commands.teleport.invalid_position"));
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.invalid_position"));
}
break;
default:
CommandHandler.sendMessage(sender, translate("commands.teleport.usage"));
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.usage"));
return;
}
Position target_pos = new Position(x, y, z);
boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, target_pos);
if (!result) {
CommandHandler.sendMessage(sender, translate("commands.teleport.invalid_position"));
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.invalid_position"));
} else {
CommandHandler.sendMessage(sender, translate("commands.teleport.success",
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.success",
targetPlayer.getNickname(), Float.toString(x), Float.toString(y),
Float.toString(z), Integer.toString(sceneId))
);
......
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.managers.EnergyManager.EnergyManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.TeamManager;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass;
import emu.grasscutter.utils.Position;
import java.util.List;
import static emu.grasscutter.Configuration.GAME_OPTIONS;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "unlimitenergy", usage = "unlimitenergy [on|off|toggle]", aliases = {"ule"}, permission = "player.unlimitenergy", permissionTargeted = "player.unlimitenergy.others", description = "commands.unlimitenergy.description")
public final class UnlimitEnergyCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if(!GAME_OPTIONS.energyUsage){
CommandHandler.sendMessage(sender, translate(sender, "commands.unlimitenergy.config_error"));
return;
}
Boolean status = targetPlayer.getEnergyManager().getEnergyUsage();
if (args.size() == 1) {
switch (args.get(0).toLowerCase()) {
case "on":
status = true;
break;
case "off":
status = false;
break;
default:
status = !status;
break;
}
}
EnergyManager energyManager=targetPlayer.getEnergyManager();
energyManager.setEnergyUsage(!status);
// if unlimitEnergy is enable , make currentActiveTeam's Avatar full-energy
if (status) {
for (EntityAvatar entityAvatar : targetPlayer.getTeamManager().getActiveTeam()) {
entityAvatar.addEnergy(1000,
PropChangeReasonOuterClass.PropChangeReason.PROP_CHANGE_REASON_GM,true);
}
}
CommandHandler.sendMessage(sender, translate(sender, "commands.unlimitenergy.success", (status ? translate(sender, "commands.status.enabled") : translate(sender, "commands.status.disabled")), targetPlayer.getNickname()));
}
}
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.tower.TowerLevelRecord;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "unlocktower", usage = "unlocktower", aliases = {"ut"},
description = "commands.unlocktower.description", permission = "player.tower")
public class UnlockTowerCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
unlockFloor(targetPlayer, targetPlayer.getServer().getTowerScheduleManager()
.getCurrentTowerScheduleData().getEntranceFloorId());
unlockFloor(targetPlayer, targetPlayer.getServer().getTowerScheduleManager()
.getScheduleFloors());
CommandHandler.sendMessage(sender, translate(sender, "commands.unlocktower.success"));
}
public void unlockFloor(Player player, List<Integer> floors){
floors.stream()
.filter(id -> !player.getTowerManager().getRecordMap().containsKey(id))
.forEach(id -> player.getTowerManager().getRecordMap().put(id, new TowerLevelRecord(id)));
}
}
......@@ -11,17 +11,11 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "weather", usage = "weather <weatherId> [climateId]",
description = "Changes the weather.", aliases = {"w"}, permission = "player.weather")
@Command(label = "weather", usage = "weather <climate type(weatherId)> <weather type(climateId)>", aliases = {"w"}, permission = "player.weather", permissionTargeted = "player.weather.others", description = "commands.weather.description")
public final class WeatherCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (targetPlayer == null) {
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
return;
}
int weatherId = 0;
int climateId = 1;
switch (args.size()) {
......@@ -29,17 +23,17 @@ public final class WeatherCommand implements CommandHandler {
try {
climateId = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate("commands.weather.invalid_id"));
CommandHandler.sendMessage(sender, translate(sender, "commands.weather.invalid_id"));
}
case 1:
try {
weatherId = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate("commands.weather.invalid_id"));
CommandHandler.sendMessage(sender, translate(sender, "commands.weather.invalid_id"));
}
break;
default:
CommandHandler.sendMessage(sender, translate("commands.weather.usage"));
CommandHandler.sendMessage(sender, translate(sender, "commands.weather.usage"));
return;
}
......@@ -48,6 +42,6 @@ public final class WeatherCommand implements CommandHandler {
targetPlayer.getScene().setWeather(weatherId);
targetPlayer.getScene().setClimate(climate);
targetPlayer.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(targetPlayer));
CommandHandler.sendMessage(sender, translate("commands.weather.success", Integer.toString(weatherId), Integer.toString(climateId)));
CommandHandler.sendMessage(sender, translate(sender, "commands.weather.success", Integer.toString(weatherId), Integer.toString(climateId)));
}
}
package emu.grasscutter.data;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils;
import java.io.*;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.List;
import java.util.regex.Pattern;
import static emu.grasscutter.Configuration.DATA;
public class DataLoader {
/**
* Load a data file by its name. If the file isn't found within the /data directory then it will fallback to the default within the jar resources
* @see #load(String, boolean)
* @param resourcePath The path to the data file to be loaded.
* @return InputStream of the data file.
* @throws FileNotFoundException
*/
public static InputStream load(String resourcePath) throws FileNotFoundException {
return load(resourcePath, true);
}
/**
* Load a data file by its name.
* @param resourcePath The path to the data file to be loaded.
* @param useFallback If the file does not exist in the /data directory, should it use the default file in the jar?
* @return InputStream of the data file.
* @throws FileNotFoundException
*/
public static InputStream load(String resourcePath, boolean useFallback) throws FileNotFoundException {
if(Utils.fileExists(DATA(resourcePath))) {
// Data is in the resource directory
return new FileInputStream(DATA(resourcePath));
} else {
if(useFallback) {
return FileUtils.readResourceAsStream("/defaults/data/" + resourcePath);
}
}
return null;
}
public static void CheckAllFiles() {
try {
List<Path> filenames = FileUtils.getPathsFromResource("/defaults/data/");
if (filenames == null) {
Grasscutter.getLogger().error("We were unable to locate your default data files.");
}
for (Path file : filenames) {
String relativePath = String.valueOf(file).split("defaults[\\\\\\/]data[\\\\\\/]")[1];
CheckAndCopyData(relativePath);
}
} catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while trying to check the data folder.", e);
}
GenerateGachaMappings();
}
private static void CheckAndCopyData(String name) {
String filePath = Utils.toFilePath(DATA(name));
if (!Utils.fileExists(filePath)) {
// Check if file is in subdirectory
if (name.indexOf("/") != -1) {
String[] path = name.split("/");
String folder = "";
for(int i = 0; i < (path.length - 1); i++) {
folder += path[i] + "/";
// Make sure the current folder exists
String folderToCreate = Utils.toFilePath(DATA(folder));
if(!Utils.fileExists(folderToCreate)) {
Grasscutter.getLogger().info("Creating data folder '" + folder + "'");
Utils.createFolder(folderToCreate);
}
}
}
Grasscutter.getLogger().info("Creating default '" + name + "' data");
FileUtils.copyResource("/defaults/data/" + name, filePath);
}
}
private static void GenerateGachaMappings() {
if (!Utils.fileExists(GachaHandler.gachaMappings)) {
try {
Grasscutter.getLogger().info("Creating default '" + GachaHandler.gachaMappings + "' data");
Tools.createGachaMapping(GachaHandler.gachaMappings);
} catch (Exception exception) {
Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception);
}
}
}
}
......@@ -8,10 +8,12 @@ import java.util.Map;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.Utils;
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
import emu.grasscutter.data.custom.OpenConfigEntry;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.data.def.*;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.data.binout.MainQuestData;
import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.excels.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
......@@ -22,8 +24,10 @@ public class GameData {
// BinOutputs
private static final Int2ObjectMap<String> abilityHashes = new Int2ObjectOpenHashMap<>();
private static final Map<String, AbilityEmbryoEntry> abilityEmbryos = new HashMap<>();
private static final Map<String, AbilityModifierEntry> abilityModifiers = new HashMap<>();
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
// ExcelConfigs
private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
......@@ -48,6 +52,7 @@ public class GameData {
private static final Int2ObjectMap<WeaponCurveData> weaponCurveDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<EquipAffixData> equipAffixDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<EnvAnimalGatherConfigData> envAnimalGatherConfigDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<MonsterData> monsterDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<NpcData> npcDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<GadgetData> gadgetDataMap = new Int2ObjectOpenHashMap<>();
......@@ -60,16 +65,28 @@ public class GameData {
private static final Int2ObjectMap<SceneData> sceneDataMap = new Int2ObjectLinkedOpenHashMap<>();
private static final Int2ObjectMap<FetterData> fetterDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexQuestData> codexQuestDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexQuestData> codexQuestDataIdMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexAnimalData> codexAnimalDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexWeaponData> codexWeaponDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexWeaponData> codexWeaponDataIdMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexMaterialData> codexMaterialDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexMaterialData> codexMaterialDataIdMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexReliquaryData> codexReliquaryDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexReliquaryData> codexReliquaryDataIdMap = new Int2ObjectOpenHashMap<>();
private static final ArrayList<CodexReliquaryData> codexReliquaryArrayList = new ArrayList<>();
private static final Int2ObjectMap<FetterCharacterCardData> fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<DailyDungeonData> dailyDungeonDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<DungeonData> dungeonDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<QuestData> questDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
// Cache
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
......@@ -101,6 +118,10 @@ public class GameData {
return abilityEmbryos;
}
public static Map<String, AbilityModifierEntry> getAbilityModifiers() {
return abilityModifiers;
}
public static Map<String, OpenConfigEntry> getOpenConfigEntries() {
return openConfigEntries;
}
......@@ -114,6 +135,10 @@ public class GameData {
return getScenePointEntries().get(sceneId + "_" + pointId);
}
public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() {
return mainQuestData;
}
public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
return avatarDataMap;
}
......@@ -216,6 +241,9 @@ public class GameData {
public static Int2ObjectMap<MonsterData> getMonsterDataMap() {
return monsterDataMap;
}
public static Int2ObjectMap<EnvAnimalGatherConfigData> getEnvAnimalGatherConfigDataMap() {
return envAnimalGatherConfigDataMap;
}
public static Int2ObjectMap<NpcData> getNpcDataMap() {
return npcDataMap;
......@@ -278,6 +306,18 @@ public class GameData {
return fetters;
}
public static Int2ObjectMap<CodexQuestData> getCodexQuestDataIdMap(){return codexQuestDataIdMap;}
public static Int2ObjectMap<CodexAnimalData> getCodexAnimalDataMap(){return codexAnimalDataMap;}
public static Int2ObjectMap<CodexWeaponData> getCodexWeaponDataIdMap(){return codexWeaponDataIdMap;}
public static Int2ObjectMap<CodexMaterialData> getCodexMaterialDataIdMap(){return codexMaterialDataIdMap;}
public static Int2ObjectMap<CodexReliquaryData> getcodexReliquaryIdMap(){return codexReliquaryDataIdMap;}
public static ArrayList<CodexReliquaryData> getcodexReliquaryArrayList(){return codexReliquaryArrayList;}
public static Int2ObjectMap<WorldLevelData> getWorldLevelDataMap() {
return worldLevelDataMap;
}
......@@ -320,4 +360,11 @@ public class GameData {
public static Int2ObjectMap<TowerLevelData> getTowerLevelDataMap(){
return towerLevelDataMap;
}
public static Int2ObjectMap<TowerScheduleData> getTowerScheduleDataMap(){
return towerScheduleDataMap;
}
public static Int2ObjectMap<QuestData> getQuestDataMap() {
return questDataMap;
}
}
......@@ -7,8 +7,8 @@ import org.danilopianini.util.FlexibleQuadTree;
import org.danilopianini.util.SpatialIndex;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.def.ReliquaryAffixData;
import emu.grasscutter.data.def.ReliquaryMainPropData;
import emu.grasscutter.data.excels.ReliquaryAffixData;
import emu.grasscutter.data.excels.ReliquaryMainPropData;
import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
import emu.grasscutter.utils.WeightedList;
......
package emu.grasscutter.data;
import java.io.File;
import java.io.FileReader;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
......@@ -12,20 +11,30 @@ import emu.grasscutter.utils.Utils;
import org.reflections.Reflections;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.data.binout.AbilityModifier;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.data.binout.MainQuestData;
import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType;
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.common.ScenePointConfig;
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
import emu.grasscutter.data.custom.OpenConfigEntry;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
import emu.grasscutter.game.world.SpawnDataEntry.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import static emu.grasscutter.Configuration.*;
public class ResourceLoader {
private static List<String> loadedResources = new ArrayList<String>();
public static List<Class<?>> getResourceDefClasses() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
......@@ -47,12 +56,14 @@ public class ResourceLoader {
// Load ability lists
loadAbilityEmbryos();
loadOpenConfig();
loadAbilityModifiers();
// Load resources
loadResources();
// Process into depots
GameDepot.load();
// Load spawn data
// Load spawn data and quests
loadSpawnData();
loadQuests();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints();
// Custom - TODO move this somewhere else
......@@ -89,6 +100,10 @@ public class ResourceLoader {
}
public static void loadResources() {
loadResources(false);
}
public static void loadResources(boolean doReload) {
for (Class<?> resourceDefinition : getResourceDefClasses()) {
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
......@@ -104,7 +119,7 @@ public class ResourceLoader {
}
try {
loadFromResource(resourceDefinition, type, map);
loadFromResource(resourceDefinition, type, map, doReload);
} catch (Exception e) {
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e);
}
......@@ -112,30 +127,32 @@ public class ResourceLoader {
}
@SuppressWarnings("rawtypes")
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map) throws Exception {
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
if(!loadedResources.contains(c.getSimpleName()) || doReload) {
for (String name : type.name()) {
loadFromResource(c, name, map);
}
Grasscutter.getLogger().info("Loaded " + map.size() + " " + c.getSimpleName() + "s.");
loadedResources.add(c.getSimpleName());
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
FileReader fileReader = new FileReader(Grasscutter.getConfig().RESOURCE_FOLDER + "ExcelBinOutput/" + fileName);
Gson gson = Grasscutter.getGsonFactory();
List list = gson.fromJson(fileReader, List.class);
try (FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName))) {
List list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, c).getType());
for (Object o : list) {
Map<String, Object> tempMap = Utils.switchPropertiesUpperLowerCase((Map<String, Object>) o, c);
GameResource res = gson.fromJson(gson.toJson(tempMap), TypeToken.get(c).getType());
GameResource res = (GameResource) o;
res.onLoad();
map.put(res.getId(), res);
}
}
}
private static void loadScenePoints() {
Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)");
File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Scene/Point");
File folder = new File(RESOURCE("BinOutput/Scene/Point"));
if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) {
Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!");
......@@ -144,8 +161,7 @@ public class ResourceLoader {
List<ScenePointEntry> scenePointList = new ArrayList<>();
for (File file : Objects.requireNonNull(folder.listFiles())) {
ScenePointConfig config = null;
Integer sceneId = null;
ScenePointConfig config; Integer sceneId;
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
......@@ -183,23 +199,19 @@ public class ResourceLoader {
}
private static void loadAbilityEmbryos() {
// Read from cached file if exists
File embryoCache = new File(Grasscutter.getConfig().DATA_FOLDER + "AbilityEmbryos.json");
List<AbilityEmbryoEntry> embryoList = null;
if (embryoCache.exists()) {
// Load from cache
try (FileReader fileReader = new FileReader(embryoCache)) {
embryoList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType());
} catch (Exception e) {
e.printStackTrace();
}
} else {
// Read from cached file if exists
try(InputStream embryoCache = DataLoader.load("AbilityEmbryos.json", false)) {
embryoList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(embryoCache), TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType());
} catch(Exception ignored) {}
if(embryoList == null) {
// Load from BinOutput
Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)");
embryoList = new LinkedList<>();
File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Avatar/"));
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Avatar/")));
File[] files = folder.listFiles();
if(files == null) {
Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath());
......@@ -244,51 +256,103 @@ public class ResourceLoader {
}
}
private static void loadSpawnData() {
// Read from cached file if exists
File spawnDataEntries = new File(Grasscutter.getConfig().DATA_FOLDER + "Spawns.json");
List<SpawnGroupEntry> spawnEntryList = null;
private static void loadAbilityModifiers() {
// Load from BinOutput
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Ability/Temp/AvatarAbilities/")));
File[] files = folder.listFiles();
if (files == null) {
Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath());
return;
}
for (File file : files) {
List<AbilityConfigData> abilityConfigList;
if (spawnDataEntries.exists()) {
// Load from cache
try (FileReader fileReader = new FileReader(spawnDataEntries)) {
spawnEntryList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
try (FileReader fileReader = new FileReader(file)) {
abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType());
} catch (Exception e) {
e.printStackTrace();
continue;
}
for (AbilityConfigData data : abilityConfigList) {
if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) {
continue;
}
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName);
for (Entry<String, AbilityModifier> entry : data.Default.modifiers.entrySet()) {
AbilityModifier modifier = entry.getValue();
// Stare.
if (modifier.onAdded != null) {
for (AbilityModifierAction action : modifier.onAdded) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnAdded().add(action);
}
}
}
if (modifier.onThinkInterval != null) {
for (AbilityModifierAction action : modifier.onThinkInterval) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnThinkInterval().add(action);
}
}
}
if (modifier.onRemoved != null) {
for (AbilityModifierAction action : modifier.onRemoved) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnRemoved().add(action);
}
}
}
}
GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry);
}
}
}
private static void loadSpawnData() {
List<SpawnGroupEntry> spawnEntryList = null;
// Read from cached file if exists
try(InputStream spawnDataEntries = DataLoader.load("Spawns.json")) {
spawnEntryList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(spawnDataEntries), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
} catch (Exception ignored) {}
if (spawnEntryList == null || spawnEntryList.isEmpty()) {
Grasscutter.getLogger().error("No spawn data loaded!");
return;
}
for (SpawnGroupEntry entry : spawnEntryList) {
entry.getSpawns().stream().forEach(s -> {
s.setGroup(entry);
});
entry.getSpawns().forEach(s -> s.setGroup(entry));
GameDepot.getSpawnListById(entry.getSceneId()).insert(entry, entry.getPos().getX(), entry.getPos().getZ());
}
}
private static void loadOpenConfig() {
// Read from cached file if exists
File openConfigCache = new File(Grasscutter.getConfig().DATA_FOLDER + "OpenConfig.json");
List<OpenConfigEntry> list = null;
if (openConfigCache.exists()) {
try (FileReader fileReader = new FileReader(openConfigCache)) {
list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, OpenConfigEntry.class).getType());
} catch (Exception e) {
e.printStackTrace();
}
} else {
try(InputStream openConfigCache = DataLoader.load("OpenConfig.json", false)) {
list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(openConfigCache), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
} catch (Exception ignored) {}
if (list == null) {
Map<String, OpenConfigEntry> map = new TreeMap<>();
java.lang.reflect.Type type = new TypeToken<Map<String, OpenConfigData[]>>() {}.getType();
String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"};
for (String name : folderNames) {
File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + name));
File folder = new File(Utils.toFilePath(RESOURCE(name)));
File[] files = folder.listFiles();
if(files == null) {
Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return;
......@@ -328,6 +392,29 @@ public class ResourceLoader {
}
}
private static void loadQuests() {
File folder = new File(RESOURCE("BinOutput/Quest/"));
if (!folder.exists()) {
return;
}
for (File file : folder.listFiles()) {
MainQuestData mainQuest = null;
try (FileReader fileReader = new FileReader(file)) {
mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, MainQuestData.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest);
}
Grasscutter.getLogger().info("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
}
// BinOutput configs
private static class AvatarConfig {
......@@ -348,8 +435,14 @@ public class ResourceLoader {
public static class OpenConfigData {
public String $type;
public String abilityName;
@SerializedName(value="talentIndex", alternate={"OJOFFKLNAHN"})
public int talentIndex;
@SerializedName(value="skillID", alternate={"overtime"})
public int skillID;
@SerializedName(value="pointDelta", alternate={"IGEBKIHPOIF"})
public int pointDelta;
}
}
package emu.grasscutter.data.custom;
package emu.grasscutter.data.binout;
public class AbilityEmbryoEntry {
private String name;
......
package emu.grasscutter.data.binout;
import java.util.Map;
public class AbilityModifier {
public AbilityModifierAction[] onAdded;
public AbilityModifierAction[] onThinkInterval;
public AbilityModifierAction[] onRemoved;
public static class AbilityConfigData {
public AbilityData Default;
}
public static class AbilityData {
public String abilityName;
public Map<String, AbilityModifier> modifiers;
}
public static class AbilityModifierAction {
public String $type;
public AbilityModifierActionType type;
public String target;
public AbilityModifierValue amount;
public AbilityModifierValue amountByTargetCurrentHPRatio;
}
public static class AbilityModifierValue {
public boolean isFormula;
public boolean isDynamic;
public String dynamicKey;
}
public enum AbilityModifierActionType {
HealHP, ApplyModifier, LoseHP;
}
}
package emu.grasscutter.data.binout;
import java.util.ArrayList;
import java.util.List;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
public class AbilityModifierEntry {
private String name; // Custom value
public List<AbilityModifierAction> onModifierAdded;
public List<AbilityModifierAction> onThinkInterval;
public List<AbilityModifierAction> onRemoved;
public AbilityModifierEntry(String name) {
this.name = name;
this.onModifierAdded = new ArrayList<>();
this.onThinkInterval = new ArrayList<>();
this.onRemoved = new ArrayList<>();
}
public String getName() {
return name;
}
public List<AbilityModifierAction> getOnAdded() {
return onModifierAdded;
}
public List<AbilityModifierAction> getOnThinkInterval() {
return onThinkInterval;
}
public List<AbilityModifierAction> getOnRemoved() {
return onRemoved;
}
}
package emu.grasscutter.data.binout;
import emu.grasscutter.game.quest.enums.LogicType;
import emu.grasscutter.game.quest.enums.QuestTrigger;
import emu.grasscutter.game.quest.enums.QuestType;
public class MainQuestData {
private int id;
private int series;
private QuestType type;
private long titleTextMapHash;
private int[] suggestTrackMainQuestList;
private int[] rewardIdList;
private SubQuestData[] subQuests;
public int getId() {
return id;
}
public int getSeries() {
return series;
}
public QuestType getType() {
return type;
}
public long getTitleTextMapHash() {
return titleTextMapHash;
}
public int[] getSuggestTrackMainQuestList() {
return suggestTrackMainQuestList;
}
public int[] getRewardIdList() {
return rewardIdList;
}
public SubQuestData[] getSubQuests() {
return subQuests;
}
public static class SubQuestData {
private int subId;
public int getSubId() {
return subId;
}
}
}
package emu.grasscutter.data.custom;
package emu.grasscutter.data.binout;
import java.util.ArrayList;
import java.util.List;
......
package emu.grasscutter.data.custom;
package emu.grasscutter.data.binout;
import emu.grasscutter.data.common.PointData;
......
package emu.grasscutter.data.common;
public class CurveInfo {
private String Type;
private String Arith;
private float Value;
private String type;
private String arith;
private float value;
public String getType() {
return Type;
return type;
}
public String getArith() {
return Arith;
return arith;
}
public float getValue() {
return Value;
return value;
}
}
......@@ -3,16 +3,16 @@ package emu.grasscutter.data.common;
import emu.grasscutter.game.props.FightProperty;
public class FightPropData {
private String PropType;
private String propType;
private FightProperty prop;
private float Value;
private float value;
public String getPropType() {
return PropType;
return propType;
}
public float getValue() {
return Value;
return value;
}
public FightProperty getProp() {
......@@ -20,6 +20,6 @@ public class FightPropData {
}
public void onLoad() {
this.prop = FightProperty.getPropByName(PropType);
this.prop = FightProperty.getPropByName(propType);
}
}
\ No newline at end of file
package emu.grasscutter.data.common;
import com.google.gson.annotations.SerializedName;
public class ItemParamData {
private int Id;
private int Count;
@SerializedName(value="id", alternate={"itemId"})
private int id;
@SerializedName(value="count", alternate={"itemCount"})
private int count;
public ItemParamData() {}
public ItemParamData(int id, int count) {
this.Id = id;
this.Count = count;
this.id = id;
this.count = count;
}
public int getId() {
return Id;
return id;
}
public int getItemId() {
return id;
}
public int getCount() {
return Count;
return count;
}
public int getItemCount() {
return count;
}
}
package emu.grasscutter.data.common;
public class ItemParamStringData {
private int Id;
private String Count;
private int id;
private String count;
public ItemParamStringData() {}
public int getId() {
return Id;
return id;
}
public String getCount() {
return Count;
return count;
}
public ItemParamData toItemParamData() {
if (Count.contains(";")) {
String[] split = Count.split(";");
Count = Count.split(";")[split.length - 1];
} else if (Count.contains(".")) {
return new ItemParamData(Id, (int) Math.ceil(Double.parseDouble(Count)));
if (count.contains(";")) {
String[] split = count.split(";");
count = count.split(";")[split.length - 1];
} else if (count.contains(".")) {
return new ItemParamData(id, (int) Math.ceil(Double.parseDouble(count)));
}
return new ItemParamData(Id, Integer.parseInt(Count));
return new ItemParamData(id, Integer.parseInt(count));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment