Initial commit

This commit is contained in:
Melledy
2023-09-25 06:03:09 -07:00
commit 48b267cecd
214 changed files with 11265 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
package emu.lunarcore;
import lombok.Getter;
@Getter
public class Config {
public DatabaseInfo accountDatabase = new DatabaseInfo();
public DatabaseInfo gameDatabase = new DatabaseInfo();
public InternalMongoInfo internalMongoServer = new InternalMongoInfo();
public boolean useSameDatabase = true;
public KeystoreInfo keystore = new KeystoreInfo();
public ServerConfig httpServer = new ServerConfig("127.0.0.1", 443);
public GameServerConfig gameServer = new GameServerConfig("127.0.0.1", 23301);
public DownloadData downloadData = new DownloadData();
public String resourceDir = "./resources";
public String dataDir = "./data";
@Getter
public static class DatabaseInfo {
public String uri = "mongodb://localhost:27017";
public String collection = "lunarrail";
public boolean useInternal = true;
}
@Getter
public static class InternalMongoInfo {
public String address = "localhost";
public int port = 27017;
public String filePath = "database.mv";
}
@Getter
public static class KeystoreInfo {
public String path = "./keystore.p12";
public String password = "lunar";
}
@Getter
public static class ServerConfig {
public String bindAddress = "0.0.0.0";
public String publicAddress = "127.0.0.1";
public int port;
public boolean useSSL = true;
public ServerConfig(String address, int port) {
this.publicAddress = address;
this.port = port;
}
public String getDisplayAddress() {
return (useSSL ? "https" : "http") + "://" + publicAddress + ":" + port;
}
}
@Getter
public static class GameServerConfig extends ServerConfig {
public String id = "lunar_rail_test";
public String name = "Test";
public String description = "Test Server";
public GameServerConfig(String address, int port) {
super(address, port);
}
}
@Getter
public static class DownloadData {
public String assetBundleUrl = null;
public String exResourceUrl = null;
public String luaUrl = null;
public String ifixUrl = null;
}
}

View File

@@ -0,0 +1,22 @@
package emu.lunarcore;
import java.time.Instant;
import java.time.ZoneOffset;
public class GameConstants {
public static String VERSION = "1.3.0";
public static String MDK_VERSION = "5377911";
public static final ZoneOffset CURRENT_OFFSET = ZoneOffset.systemDefault().getRules().getOffset(Instant.now());
// Game
public static final String DEFAULT_NAME = "Trailblazer";
public static final int MAX_TRAILBLAZER_LEVEL = 70;
public static final int MAX_STAMINA = 240;
public static final int MAX_AVATARS_IN_TEAM = 4;
public static final int DEFAULT_TEAMS = 6;
// Custom
public static final int SERVER_CONSOLE_UID = 99;
public static final int EQUIPMENT_SLOT_ID = 100;
}

View File

@@ -0,0 +1,160 @@
package emu.lunarcore;
import java.io.*;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import ch.qos.logback.classic.Logger;
import emu.lunarcore.commands.ServerCommands;
import emu.lunarcore.data.ResourceLoader;
import emu.lunarcore.database.DatabaseManager;
import emu.lunarcore.server.game.GameServer;
import emu.lunarcore.server.http.HttpServer;
import emu.lunarcore.util.Handbook;
import emu.lunarcore.util.JsonUtils;
import lombok.Getter;
public class LunarRail {
private static Logger log = (Logger) LoggerFactory.getLogger(LunarRail.class);
private static File configFile = new File("./config.json");
private static Config config;
@Getter private static DatabaseManager accountDatabase;
@Getter private static DatabaseManager gameDatabase;
@Getter private static HttpServer httpServer;
@Getter private static GameServer gameServer;
private static ServerType serverType = ServerType.BOTH;
// Load config first before doing anything
static {
LunarRail.loadConfig();
}
public static void main(String[] args) {
// Start Server
LunarRail.getLogger().info("Starting Lunar Rail...");
// Parse arguments
for (String arg : args) {
switch (arg) {
case "-dispatch":
serverType = ServerType.DISPATCH;
break;
case "-game":
serverType = ServerType.GAME;
break;
case "-database":
// Database only
DatabaseManager databaseManager = new DatabaseManager();
databaseManager.startInternalMongoServer(LunarRail.getConfig().getInternalMongoServer());
LunarRail.getLogger().info("Running local mongo server at " + databaseManager.getServer().getConnectionString());
// Console
LunarRail.startConsole();
return;
}
}
// Load resources
ResourceLoader.loadAll();
// Build handbook TODO
Handbook.generate();
// Start Database(s)
LunarRail.initDatabases();
// Start Servers TODO
httpServer = new HttpServer(serverType);
httpServer.start();
if (serverType.runGame()) {
gameServer = new GameServer(getConfig().getGameServer());
gameServer.start();
}
// Start console
LunarRail.startConsole();
}
public static Config getConfig() {
return config;
}
public static Logger getLogger() {
return log;
}
// Database
private static void initDatabases() {
accountDatabase = new DatabaseManager(LunarRail.getConfig().getAccountDatabase());
if (LunarRail.getConfig().useSameDatabase) {
gameDatabase = accountDatabase;
} else {
gameDatabase = new DatabaseManager(LunarRail.getConfig().getGameDatabase());
}
}
// Config
public static void loadConfig() {
try (FileReader file = new FileReader(configFile)) {
config = JsonUtils.loadToClass(file, Config.class);
} catch (Exception e) {
LunarRail.config = new Config();
}
saveConfig();
}
public static void saveConfig() {
try (FileWriter file = new FileWriter(configFile)) {
Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
file.write(gson.toJson(config));
} catch (Exception e) {
getLogger().error("Config save error");
}
}
// Server console
private static void startConsole() {
String input;
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
while ((input = br.readLine()) != null) {
ServerCommands.handle(input);
}
} catch (Exception e) {
LunarRail.getLogger().error("Console error:", e);
}
}
// Server enums
public enum ServerType {
BOTH (true, true),
DISPATCH (true, false),
GAME (false, true);
private final boolean runDispatch;
private final boolean runGame;
private ServerType(boolean runDispatch, boolean runGame) {
this.runDispatch = runDispatch;
this.runGame = runGame;
}
public boolean runDispatch() {
return runDispatch;
}
public boolean runGame() {
return runGame;
}
}
}

View File

@@ -0,0 +1,13 @@
package emu.lunarcore.commands;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Command {
public String[] aliases() default "";
public int gmLevel() default 1;
public String desc() default "";
}

View File

@@ -0,0 +1,177 @@
package emu.lunarcore.commands;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.data.excel.NpcMonsterExcel;
import emu.lunarcore.data.excel.StageExcel;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.scene.EntityMonster;
import emu.lunarcore.util.Position;
@SuppressWarnings("unused")
public class PlayerCommands {
private static HashMap<String, PlayerCommand> list = new HashMap<>();
static {
try {
// Look for classes
for (Class<?> cls : PlayerCommands.class.getDeclaredClasses()) {
// Get non abstract classes
if (!Modifier.isAbstract(cls.getModifiers())) {
Command commandAnnotation = cls.getAnnotation(Command.class);
PlayerCommand command = (PlayerCommand) cls.getConstructor().newInstance();
if (commandAnnotation != null) {
command.setLevel(commandAnnotation.gmLevel());
for (String alias : commandAnnotation.aliases()) {
if (alias.length() == 0) {
continue;
}
String commandName = "!" + alias;
list.put(commandName, command);
commandName = "/" + alias;
list.put(commandName, command);
}
}
String commandName = "!" + cls.getSimpleName().toLowerCase();
list.put(commandName, command);
commandName = "/" + cls.getSimpleName().toLowerCase();
list.put(commandName, command);
}
}
} catch (Exception e) {
}
}
public static void handle(Player player, String msg) {
String[] split = msg.split(" ");
// End if invalid
if (split.length == 0) {
return;
}
//
String first = split[0].toLowerCase();
PlayerCommand c = PlayerCommands.list.get(first);
if (c != null) {
// Execute
int len = Math.min(first.length() + 1, msg.length());
c.execute(player, msg.substring(len));
} else {
player.dropMessage("Error: Invalid command!");
}
}
public static abstract class PlayerCommand {
// GM level required to use this command
private int level;
protected int getLevel() { return this.level; }
protected void setLevel(int minLevel) { this.level = minLevel; }
// Main
public abstract void execute(Player player, String raw);
}
// ================ Commands ================
@Command(aliases = {"g", "item"}, desc = "/give [item id] [count] - Gives {count} amount of {item id}")
public static class Give extends PlayerCommand {
@Override
public void execute(Player player, String raw) {
String[] split = raw.split(" ");
int itemId = 0, count = 1;
try {
itemId = Integer.parseInt(split[0]);
} catch (Exception e) {
itemId = 0;
}
try {
count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1);
} catch (Exception e) {
count = 1;
}
// Give
ItemExcel itemData = GameData.getItemExcelMap().get(itemId);
GameItem item;
if (itemData == null) {
player.dropMessage("Error: Item data not found");
return;
}
if (itemData.isEquippable()) {
List<GameItem> items = new LinkedList<>();
for (int i = 0; i < count; i++) {
item = new GameItem(itemData);
//items.add(item);
player.getInventory().addItem(item);
}
// TODO add item hint packet
} else {
item = new GameItem(itemData, count);
player.getInventory().addItem(item);
// TODO add item hint packet
}
player.dropMessage("Giving you " + count + " of " + itemId);
}
}
/* Temporarily disabled as spawned monsters need
@Command(desc = "/spawn [monster id] [count] - Creates {count} amount of {item id}")
public static class Spawn extends PlayerCommand {
@Override
public void execute(Player player, String raw) {
String[] split = raw.split(" ");
int monsterId = 0, stageId = 2;
try {
monsterId = Integer.parseInt(split[0]);
} catch (Exception e) {
monsterId = 0;
}
try {
stageId = Integer.parseInt(split[1]);
} catch (Exception e) {
stageId = 2;
}
// TODO
NpcMonsterExcel excel = GameData.getNpcMonsterExcelMap().get(monsterId);
if (excel == null) {
player.dropMessage("Npc monster id not found!");
return;
}
StageExcel stage = GameData.getStageExcelMap().get(stageId);
if (stage == null) {
player.dropMessage("Stage id not found!");
return;
}
Position pos = player.getPos().clone();
pos.setX(pos.getX() + 50);
// Add to scene
EntityMonster monster = new EntityMonster(excel, stage, pos);
player.getScene().addMonster(monster);
}
}
*/
}

View File

@@ -0,0 +1,119 @@
package emu.lunarcore.commands;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import emu.lunarcore.LunarRail;
import emu.lunarcore.util.Utils;
@SuppressWarnings("unused")
public class ServerCommands {
private static HashMap<String, ServerCommand> list = new HashMap<>();
static {
try {
// Look for classes
for (Class<?> cls : ServerCommands.class.getDeclaredClasses()) {
// Get non abstract classes
if (!Modifier.isAbstract(cls.getModifiers())) {
String commandName = cls.getSimpleName().toLowerCase();
list.put(commandName, (ServerCommand) cls.newInstance());
}
}
} catch (Exception e) {
}
}
public static void handle(String msg) {
String[] split = msg.split(" ");
// End if invalid
if (split.length == 0) {
return;
}
//
String first = split[0].toLowerCase();
ServerCommand c = ServerCommands.list.get(first);
if (c != null) {
// Execute
int len = Math.min(first.length() + 1, msg.length());
c.execute(msg.substring(len));
} else {
LunarRail.getLogger().info("Invalid command!");
}
}
public static abstract class ServerCommand {
public abstract void execute(String raw);
}
// ================ Commands ================
private static class Account extends ServerCommand {
@Override
public void execute(String raw) {
String[] split = raw.split(" ");
if (split.length < 2) {
LunarRail.getLogger().error("Invalid amount of args");
return;
}
emu.lunarcore.game.account.Account account = null;
String command = split[0].toLowerCase();
String username = split[1];
switch (command) {
case "create":
if (split.length < 2) { // Should be 3 if passwords were enabled
LunarRail.getLogger().error("Invalid amount of args");
return;
}
// Get password
//String password = split[2];
// Reserved player uid
int reservedUid = Utils.parseSafeInt(split[2]);
// Get acocunt from database
account = LunarRail.getAccountDatabase().getObjectByField(emu.lunarcore.game.account.Account.class, "username", username);
if (account == null) {
// Create account
//String hash = BCrypt.withDefaults().hashToString(12, password.toCharArray());
account = new emu.lunarcore.game.account.Account(username);
account.setReservedPlayerUid(reservedUid);
account.save();
LunarRail.getLogger().info("Account created");
} else {
LunarRail.getLogger().error("Account already exists");
}
break;
case "delete":
account = LunarRail.getAccountDatabase().getObjectByField(emu.lunarcore.game.account.Account.class, "name", username);
if (account == null) {
LunarRail.getLogger().info("Account doesnt exist");
return;
}
boolean success = LunarRail.getAccountDatabase().delete(account);
if (success) {
LunarRail.getLogger().info("Account deleted");
}
break;
}
}
}
}

View File

@@ -0,0 +1,103 @@
package emu.lunarcore.data;
import java.lang.reflect.Field;
import emu.lunarcore.data.config.FloorInfo;
import emu.lunarcore.data.excel.*;
import emu.lunarcore.util.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
@SuppressWarnings("unused")
public class GameData {
// Excels
@Getter private static Int2ObjectMap<AvatarExcel> avatarExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ItemExcel> itemExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<EquipmentExcel> equipExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RelicExcel> relicExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<MonsterExcel> monsterExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<NpcMonsterExcel> npcMonsterExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<StageExcel> stageExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<MapEntranceExcel> mapEntranceExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<AvatarPromotionExcel> avatarPromotionExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<AvatarSkillTreeExcel> avatarSkillTreeExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<AvatarRankExcel> avatarRankExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<EquipmentPromotionExcel> equipmentPromotionExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<PlayerLevelExcel> playerLevelExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<ExpTypeExcel> expTypeExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<EquipmentExpTypeExcel> equipmentExpTypeExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<RelicExpTypeExcel> relicExpTypeExcelMap = new Int2ObjectOpenHashMap<>();
@Getter
private static Int2ObjectMap<RelicMainAffixExcel> relicMainAffixExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<RelicSubAffixExcel> relicSubAffixExcelMap = new Int2ObjectOpenHashMap<>();
// Configs (Bin)
@Getter private static Object2ObjectMap<String, FloorInfo> floorInfos = new Object2ObjectOpenHashMap<>();
public static Int2ObjectMap<?> getMapForExcel(Class<?> resourceDefinition) {
Int2ObjectMap<?> map = null;
try {
Field field = GameData.class.getDeclaredField(Utils.lowerCaseFirstChar(resourceDefinition.getSimpleName()) + "Map");
field.setAccessible(true);
map = (Int2ObjectMap<?>) field.get(null);
field.setAccessible(false);
} catch (Exception e) {
}
return map;
}
public static AvatarPromotionExcel getAvatarPromotionExcel(int id, int promotion) {
return avatarPromotionExcelMap.get((id << 8) + promotion);
}
public static AvatarSkillTreeExcel getAvatarSkillTreeExcel(int skill, int level) {
return avatarSkillTreeExcelMap.get((skill << 4) + level);
}
public static AvatarRankExcel getAvatarRankExcel(int rankId) {
return avatarRankExcelMap.get(rankId);
}
public static EquipmentPromotionExcel getEquipmentPromotionExcel(int id, int promotion) {
return equipmentPromotionExcelMap.get((id << 8) + promotion);
}
public static int getPlayerExpRequired(int level) {
var excel = playerLevelExcelMap.get(level);
return excel != null ? excel.getPlayerExp() : 0;
}
public static int getAvatarExpRequired(int expGroup, int level) {
var excel = expTypeExcelMap.get((expGroup << 16) + level);
return excel != null ? excel.getExp() : 0;
}
public static int getEquipmentExpRequired(int expGroup, int level) {
var excel = equipmentExpTypeExcelMap.get((expGroup << 16) + level);
return excel != null ? excel.getExp() : 0;
}
public static int getRelicExpRequired(int expGroup, int level) {
var excel = relicExpTypeExcelMap.get((expGroup << 16) + level);
return excel != null ? excel.getExp() : 0;
}
public static RelicSubAffixExcel getRelicSubAffixExcel(int groupId, int affixId) {
return relicSubAffixExcelMap.get((groupId << 8) + affixId);
}
public static FloorInfo getFloorInfo(int planeId, int floorId) {
return floorInfos.get("P" + planeId + "_F" + floorId);
}
}

View File

@@ -0,0 +1,37 @@
package emu.lunarcore.data;
import java.util.ArrayList;
import java.util.List;
import emu.lunarcore.data.excel.RelicMainAffixExcel;
import emu.lunarcore.data.excel.RelicSubAffixExcel;
import emu.lunarcore.util.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
// Game data that is parsed by the server goes here
public class GameDepot {
private static Int2ObjectMap<List<RelicMainAffixExcel>> relicMainAffixDepot = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<List<RelicSubAffixExcel>> relicSubAffixDepot = new Int2ObjectOpenHashMap<>();
public static void addRelicMainAffix(RelicMainAffixExcel affix) {
List<RelicMainAffixExcel> list = relicMainAffixDepot.computeIfAbsent(affix.getGroupID(), k -> new ArrayList<>());
list.add(affix);
}
public static void addRelicSubAffix(RelicSubAffixExcel affix) {
List<RelicSubAffixExcel> list = relicSubAffixDepot.computeIfAbsent(affix.getGroupID(), k -> new ArrayList<>());
list.add(affix);
}
public static RelicMainAffixExcel getRandomRelicMainAffix(int groupId) {
var list = relicMainAffixDepot.get(groupId);
if (list == null) return null;
return list.get(Utils.randomRange(0, list.size() - 1));
}
public static List<RelicSubAffixExcel> getRelicSubAffixList(int groupId) {
return relicSubAffixDepot.get(groupId);
}
}

View File

@@ -0,0 +1,15 @@
package emu.lunarcore.data;
public abstract class GameResource implements Comparable<GameResource> {
public abstract int getId();
public void onLoad() {
}
@Override
public int compareTo(GameResource o) {
return this.getId() - o.getId();
}
}

View File

@@ -0,0 +1,37 @@
package emu.lunarcore.data;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
public class ResourceDeserializers {
protected static class LunarRailDoubleDeserializer implements JsonDeserializer<Double> {
@Override
public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive()) {
return json.getAsDouble();
} else {
// FixPoint
var obj = json.getAsJsonObject();
return obj.get("Value").getAsDouble();
}
}
}
protected static class LunarRailHashDeserializer implements JsonDeserializer<Long> {
@Override
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive()) {
return json.getAsLong();
} else {
// TextID
var obj = json.getAsJsonObject();
return obj.get("Hash").getAsLong();
}
}
}
}

View File

@@ -0,0 +1,211 @@
package emu.lunarcore.data;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.reflections.Reflections;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import emu.lunarcore.LunarRail;
import emu.lunarcore.data.ResourceDeserializers.LunarRailDoubleDeserializer;
import emu.lunarcore.data.ResourceDeserializers.LunarRailHashDeserializer;
import emu.lunarcore.data.config.FloorInfo;
import emu.lunarcore.data.config.FloorInfo.FloorGroupSimpleInfo;
import emu.lunarcore.data.config.GroupInfo;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
public class ResourceLoader {
private static boolean loaded = false;
// Special gson factory we create for loading resources
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(double.class, new LunarRailDoubleDeserializer())
.registerTypeAdapter(long.class, new LunarRailHashDeserializer())
.create();
// Load all resources
public static void loadAll() {
// Make sure we don't load more than once
if (loaded) return;
// Start loading resources
loadResources();
// Load floor infos after resources
loadFloorInfos();
// Done
loaded = true;
}
private static List<Class<?>> getResourceDefClasses() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
List<Class<?>> classList = new ArrayList<>(classes.size());
classes.forEach(o -> {
Class<?> c = (Class<?>) o;
if (c.getAnnotation(ResourceType.class) != null) {
classList.add(c);
}
});
classList.sort((a, b) -> b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value());
return classList;
}
private static void loadResources() {
for (Class<?> resourceDefinition : getResourceDefClasses()) {
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
if (type == null) {
continue;
}
@SuppressWarnings("rawtypes")
Int2ObjectMap map = GameData.getMapForExcel(resourceDefinition);
try {
loadFromResource(resourceDefinition, type, map);
} catch (Exception e) {
LunarRail.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e);
}
}
}
@SuppressWarnings("rawtypes")
private static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map) throws Exception {
int count = 0;
for (String name : type.name()) {
count += loadFromResource(c, type, name, map);
}
LunarRail.getLogger().info("Loaded " + count + " " + c.getSimpleName() + "s.");
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static <T> int loadFromResource(Class<T> c, ResourceType type, String fileName, Int2ObjectMap map) throws Exception {
String file = LunarRail.getConfig().getResourceDir() + "/ExcelOutput/" + fileName;
// Load reader from file
try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
// Setup variables
Stream<T> stream = null;
// Determine format of json
JsonElement json = JsonParser.parseReader(fileReader);
if (json.isJsonArray()) {
// Parse list
List<T> excels = gson.fromJson(json, TypeToken.getParameterized(List.class, c).getType());
stream = excels.stream();
} else if (json.isJsonObject()) {
// Check if object is map or a nested map
boolean isMap = true;
var it = json.getAsJsonObject().asMap().entrySet().iterator();
if (it.hasNext()) {
var it2 = it.next().getValue().getAsJsonObject().asMap().entrySet().iterator();
String key = it2.next().getKey();
try {
Integer.parseInt(key);
isMap = false;
} catch (Exception ex) {
}
}
// Parse json
if (isMap) {
// Map
Map<Integer, T> excels = gson.fromJson(json, TypeToken.getParameterized(Map.class, Integer.class, c).getType());
stream = excels.values().stream();
} else {
// Nested Map
Map<Integer, Map<Integer, T>> excels = gson.fromJson(json, TypeToken.getParameterized(Map.class, Integer.class, TypeToken.getParameterized(Map.class, Integer.class, c).getType()).getType());
stream = excels.values().stream().flatMap(m -> m.values().stream());
}
} else {
throw new Exception("Invalid excel file: " + fileName);
}
// Sanity check
if (stream == null) return 0;
// Mutable integer
AtomicInteger count = new AtomicInteger();
stream.forEach(o -> {
GameResource res = (GameResource) o;
res.onLoad();
count.getAndIncrement();
if (map != null) {
map.put(res.getId(), res);
}
});
return count.get();
}
}
// Might be better to cache
private static void loadFloorInfos() {
// Load floor infos
File floorDir = new File(LunarRail.getConfig().getResourceDir() + "/Config/LevelOutput/Floor/");
if (!floorDir.exists()) {
LunarRail.getLogger().warn("Floor infos are missing, please check your resources.");
return;
}
// Dump
for (File file : floorDir.listFiles()) {
try (FileReader reader = new FileReader(file)) {
FloorInfo floor = gson.fromJson(reader, FloorInfo.class);
String name = file.getName().substring(0, file.getName().indexOf('.'));
GameData.getFloorInfos().put(name, floor);
} catch (Exception e) {
e.printStackTrace();
}
}
// Load group infos
for (FloorInfo floor : GameData.getFloorInfos().values()) {
for (FloorGroupSimpleInfo simpleGroup : floor.getSimpleGroupList()) {
File file = new File(LunarRail.getConfig().getResourceDir() + "/" + simpleGroup.getGroupPath());
if (!file.exists()) {
continue;
}
// TODO optimize
try (FileReader reader = new FileReader(file)) {
GroupInfo group = gson.fromJson(reader, GroupInfo.class);
group.setId(simpleGroup.getID());
floor.getGroups().put(simpleGroup.getID(), group);
} catch (Exception e) {
e.printStackTrace();
}
}
// Post load callback to cache floor info
floor.onLoad();
}
// Done
LunarRail.getLogger().info("Loaded " + GameData.getFloorInfos().size() + " FloorInfos.");
}
}

View File

@@ -0,0 +1,32 @@
package emu.lunarcore.data;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourceType {
/** Names of the file that this Resource loads from */
String[] name();
/** Load priority - dictates which order to load this resource, with "highest" being loaded first */
LoadPriority loadPriority() default LoadPriority.NORMAL;
public enum LoadPriority {
HIGHEST (4),
HIGH (3),
NORMAL (2),
LOW (1),
LOWEST (0);
private final int value;
LoadPriority(int value) {
this.value = value;
}
public int value() {
return value;
}
}
}

View File

@@ -0,0 +1,46 @@
package emu.lunarcore.data.common;
import com.google.gson.annotations.SerializedName;
import emu.lunarcore.proto.ItemCostOuterClass.ItemCost;
import lombok.Getter;
@Getter
public class ItemParam {
@SerializedName(value = "id", alternate = {"ItemId", "ItemID"})
private int id;
@SerializedName(value = "count", alternate = {"ItemCount", "ItemNum"})
private int count;
private ItemParamType type = ItemParamType.PILE;
public ItemParam() {
// Gson
}
public ItemParam(ItemParamType type, int id, int count) {
this.type = type;
this.id = id;
this.count = count;
}
public ItemParam(ItemCost itemCost) {
if (itemCost.hasPileItem()) {
this.id = itemCost.getPileItem().getItemId();
this.count = itemCost.getPileItem().getItemNum();
} else if (itemCost.hasEquipmentUniqueId()) {
this.type = ItemParamType.UNIQUE;
this.id = itemCost.getEquipmentUniqueId();
this.count = 1;
} else if (itemCost.hasRelicUniqueId()) {
this.type = ItemParamType.UNIQUE;
this.id = itemCost.getRelicUniqueId();
this.count = 1;
}
}
public static enum ItemParamType {
UNKNOWN, PILE, UNIQUE;
}
}

View File

@@ -0,0 +1,5 @@
package emu.lunarcore.data.config;
public class AnchorInfo extends ObjectInfo {
}

View File

@@ -0,0 +1,59 @@
package emu.lunarcore.data.config;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
@Getter
public class FloorInfo {
private int FloorID;
@SerializedName(value = "GroupList")
private List<FloorGroupSimpleInfo> SimpleGroupList;
// Cached data
private transient boolean loaded;
private transient Int2ObjectMap<GroupInfo> groups;
private transient Int2ObjectMap<PropInfo> cachedTeleports;
public FloorInfo() {
this.groups = new Int2ObjectOpenHashMap<>();
this.cachedTeleports = new Int2ObjectOpenHashMap<>();
}
public AnchorInfo getAnchorInfo(int groupId, int anchorId) {
GroupInfo group = this.getGroups().get(groupId);
if (group == null) return null;
return group.getAnchorList().stream().filter(a -> a.getID() == anchorId).findFirst().orElse(null);
}
public void onLoad() {
if (this.loaded) return;
// Cache anchors
for (GroupInfo group : groups.values()) {
if (group.getPropList() == null) {
continue;
}
for (PropInfo prop : group.getPropList()) {
if (prop.getAnchorID() > 0) {
this.cachedTeleports.put(prop.getMappingInfoID(), prop);
}
}
}
this.loaded = true;
}
@Getter
public static class FloorGroupSimpleInfo {
private String GroupPath;
private int ID;
}
}

View File

@@ -0,0 +1,24 @@
package emu.lunarcore.data.config;
import java.util.List;
import lombok.Getter;
@Getter
public class GroupInfo {
private transient int id;
private GroupLoadSide LoadSide;
private boolean LoadOnInitial;
private List<AnchorInfo> AnchorList;
private List<MonsterInfo> MonsterList;
private List<PropInfo> PropList;
public void setId(int id) {
if (this.id == 0) this.id = id;
}
public static enum GroupLoadSide {
Client, Server;
}
}

View File

@@ -0,0 +1,9 @@
package emu.lunarcore.data.config;
import lombok.Getter;
@Getter
public class MonsterInfo extends ObjectInfo {
private int NPCMonsterID;
private int EventID;
}

View File

@@ -0,0 +1,21 @@
package emu.lunarcore.data.config;
import emu.lunarcore.util.Position;
import lombok.Getter;
@Getter
public class ObjectInfo {
public int ID;
public float PosX;
public float PosY;
public float PosZ;
public String Name;
public float RotY;
/*
* Returns a new Position object
*/
public Position clonePos() {
return new Position((int) (this.PosX * 1000f), (int) (this.PosY * 1000f), (int) (this.PosZ * 1000f));
}
}

View File

@@ -0,0 +1,17 @@
package emu.lunarcore.data.config;
import emu.lunarcore.game.scene.PropState;
import lombok.Getter;
@Getter
public class PropInfo extends ObjectInfo {
public float RotX;
public float RotZ;
private int MappingInfoID;
private int AnchorGroupID;
private int AnchorID;
private int PropID;
private int EventID;
private PropState State;
private boolean IsDelete;
}

View File

@@ -0,0 +1,64 @@
package emu.lunarcore.data.excel;
import java.util.ArrayList;
import java.util.List;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.game.avatar.AvatarBaseType;
import emu.lunarcore.game.avatar.DamageType;
import lombok.AccessLevel;
import lombok.Getter;
@Getter
@ResourceType(name = {"AvatarConfig.json"})
public class AvatarExcel extends GameResource {
private int AvatarID;
private long AvatarName;
private DamageType DamageType;
private AvatarBaseType AvatarBaseType;
private double SPNeed;
private int ExpGroup;
private int MaxPromotion;
private int MaxRank;
private int[] RankIDList;
private int[] SkillList;
@Getter(AccessLevel.NONE)
private transient AvatarPromotionExcel[] promotionData;
private transient List<AvatarSkillTreeExcel> defaultSkillTrees;
private transient int maxSp;
public AvatarExcel() {
this.defaultSkillTrees = new ArrayList<>();
}
@Override
public int getId() {
return AvatarID;
}
public AvatarPromotionExcel getPromotionData(int i) {
return this.promotionData[i];
}
public int getRankId(int rank) {
return RankIDList[Math.min(rank, RankIDList.length - 1)];
}
@Override
public void onLoad() {
// Load promotion data
this.promotionData = new AvatarPromotionExcel[MaxPromotion + 1];
for (int i = 0; i <= MaxPromotion; i++) {
this.promotionData[i] = GameData.getAvatarPromotionExcel(getId(), i);
}
// Cache max sp
this.maxSp = (int) this.SPNeed * 100;
}
}

View File

@@ -0,0 +1,27 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"AvatarExpItemConfig.json"}, loadPriority = LoadPriority.LOW)
public class AvatarExpItemExcel extends GameResource {
private int ItemID;
private int Exp;
@Override
public int getId() {
return ItemID;
}
@Override
public void onLoad() {
ItemExcel excel = GameData.getItemExcelMap().get(ItemID);
if (excel == null) return;
excel.setAvatarExp(Exp);
}
}

View File

@@ -0,0 +1,56 @@
package emu.lunarcore.data.excel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.data.common.ItemParam;
import lombok.Getter;
@Getter
@ResourceType(name = {"AvatarPromotionConfig.json"}, loadPriority = LoadPriority.HIGHEST)
public class AvatarPromotionExcel extends GameResource {
private int AvatarID;
private int Promotion;
private int MaxLevel;
private int PlayerLevelRequire;
private int WorldLevelRequire;
private List<ItemParam> PromotionCostList;
private transient int PromotionCostCoin;
private double AttackBase;
private double AttackAdd;
private double DefenceBase;
private double DefenceAdd;
private double HPBase;
private double HPAdd;
private double SpeedBase;
private double CriticalChance;
private double CriticalDamage;
private double BaseAggro;
@Override
public int getId() {
return (AvatarID << 8) + Promotion;
}
@Override
public void onLoad() {
if (this.PromotionCostList == null) {
this.PromotionCostList = new ArrayList<>();
} else {
Iterator<ItemParam> it = this.PromotionCostList.iterator();
while (it.hasNext()) {
ItemParam param = it.next();
if (param.getId() == 2) {
this.PromotionCostCoin = param.getCount();
it.remove();
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
package emu.lunarcore.data.excel;
import java.util.List;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.data.common.ItemParam;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import lombok.Getter;
@Getter
@ResourceType(name = {"AvatarRankConfig.json"}, loadPriority = LoadPriority.HIGHEST)
public class AvatarRankExcel extends GameResource {
private int RankID;
private int Rank;
private Int2IntOpenHashMap SkillAddLevelList;
private List<ItemParam> UnlockCost;
@Override
public int getId() {
return RankID;
}
@Override
public void onLoad() {
}
}

View File

@@ -0,0 +1,62 @@
package emu.lunarcore.data.excel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.data.common.ItemParam;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import lombok.Getter;
@Getter
@ResourceType(name = {"AvatarSkillTreeConfig.json"}, loadPriority = LoadPriority.LOW)
public class AvatarSkillTreeExcel extends GameResource {
private int PointID;
private int Level;
private int MaxLevel;
private boolean DefaultUnlock;
private int AvatarID;
private int AvatarPromotionLimit;
private int AvatarLevelLimit;
private List<ItemParam> MaterialList;
private IntArrayList PrePoint;
private IntArrayList LevelUpSkillID;
private transient int MaterialCostCoin;
@Override
public int getId() {
return (PointID << 4) + Level;
}
@Override
public void onLoad() {
// Parse material list
if (this.MaterialList == null) {
this.MaterialList = new ArrayList<>();
} else {
Iterator<ItemParam> it = this.MaterialList.iterator();
while (it.hasNext()) {
ItemParam param = it.next();
if (param.getId() == 2) {
this.MaterialCostCoin = param.getCount();
it.remove();
}
}
}
// Load to excel
AvatarExcel excel = GameData.getAvatarExcelMap().get(AvatarID);
if (excel == null) return;
if (this.isDefaultUnlock()) {
excel.getDefaultSkillTrees().add(this);
}
}
}

View File

@@ -0,0 +1,41 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.game.inventory.GameItem;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import lombok.Getter;
@Getter
@ResourceType(name = {"EquipmentConfig.json"}, loadPriority = LoadPriority.LOW)
public class EquipmentExcel extends GameResource {
private int EquipmentID;
private int MaxPromotion;
private int MaxRank;
private int ExpType;
private int ExpProvide;
private int CoinCost;
private IntOpenHashSet RankUpCostList;
@Override
public int getId() {
return EquipmentID;
}
public boolean isRankUpItem(GameItem item) {
return item.getItemId() == this.EquipmentID || RankUpCostList.contains(item.getItemId());
}
@Override
public void onLoad() {
ItemExcel excel = GameData.getItemExcelMap().get(this.getId());
if (excel != null) {
excel.setEquipmentExcel(this);
}
}
}

View File

@@ -0,0 +1,29 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"EquipmentExpItemConfig.json"}, loadPriority = LoadPriority.LOW)
public class EquipmentExpItemExcel extends GameResource {
private int ItemID;
private int ExpProvide;
private int CoinCost;
@Override
public int getId() {
return ItemID;
}
@Override
public void onLoad() {
ItemExcel excel = GameData.getItemExcelMap().get(ItemID);
if (excel == null) return;
excel.setEquipmentExp(ExpProvide);
excel.setExpCost(CoinCost);
}
}

View File

@@ -0,0 +1,27 @@
package emu.lunarcore.data.excel;
import com.google.gson.annotations.SerializedName;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"EquipmentExpType.json"}, loadPriority = LoadPriority.NORMAL)
public class EquipmentExpTypeExcel extends GameResource {
@SerializedName(value = "id", alternate = {"ExpType"})
private int TypeID;
private int Level;
private int Exp;
@Override
public int getId() {
return (TypeID << 16) + Level;
}
@Override
public void onLoad() {
}
}

View File

@@ -0,0 +1,52 @@
package emu.lunarcore.data.excel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.data.common.ItemParam;
import lombok.Getter;
@Getter
@ResourceType(name = {"EquipmentPromotionConfig.json"}, loadPriority = LoadPriority.HIGHEST)
public class EquipmentPromotionExcel extends GameResource {
private int EquipmentID;
private int Promotion;
private int MaxLevel;
private int PlayerLevelRequire;
private int WorldLevelRequire;
private List<ItemParam> PromotionCostList;
private transient int PromotionCostCoin;
private double AttackBase;
private double AttackAdd;
private double DefenceBase;
private double DefenceAdd;
private double HPBase;
private double HPAdd;
@Override
public int getId() {
return (EquipmentID << 8) + Promotion;
}
@Override
public void onLoad() {
if (this.PromotionCostList == null) {
this.PromotionCostList = new ArrayList<>();
} else {
Iterator<ItemParam> it = this.PromotionCostList.iterator();
while (it.hasNext()) {
ItemParam param = it.next();
if (param.getId() == 2) {
this.PromotionCostCoin = param.getCount();
it.remove();
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"ExpType.json"}, loadPriority = LoadPriority.NORMAL)
public class ExpTypeExcel extends GameResource {
private int TypeID;
private int Level;
private int Exp;
@Override
public int getId() {
return (TypeID << 16) + Level;
}
@Override
public void onLoad() {
}
}

View File

@@ -0,0 +1,90 @@
package emu.lunarcore.data.excel;
import java.util.List;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.common.ItemParam;
import emu.lunarcore.game.inventory.ItemMainType;
import emu.lunarcore.game.inventory.ItemRarity;
import emu.lunarcore.game.inventory.ItemSubType;
import lombok.Getter;
import lombok.Setter;
@Getter
@ResourceType(name = {"ItemConfig.json", "ItemConfigAvatar.json", "ItemConfigAvatarPlayerIcon.json", "ItemConfigAvatarRank.json",
"ItemConfigBook.json", "ItemConfigDisk.json", "ItemConfigEquipment.json", "ItemConfigRelic.json", "ItemPlayerCard.json"})
public class ItemExcel extends GameResource {
// General item data
private int ID;
private long ItemName;
private ItemMainType ItemMainType;
private ItemSubType ItemSubType;
private ItemRarity Rarity;
private int PileLimit;
private List<ItemParam> ReturnItemIDList;
// Transient cache
@Setter private transient EquipmentExcel equipmentExcel;
@Setter private transient RelicExcel relicExcel;
@Setter private transient int avatarExp;
@Setter private transient int relicExp;
@Setter private transient int equipmentExp;
@Setter private transient int expCost;
@Override
public int getId() {
return ID;
}
public boolean isEquipment() {
return ItemMainType == emu.lunarcore.game.inventory.ItemMainType.Equipment && this.getEquipmentExcel() != null;
}
public boolean isRelic() {
return ItemMainType == emu.lunarcore.game.inventory.ItemMainType.Relic && this.getRelicExcel() != null;
}
public boolean isEquippable() {
return ItemMainType == emu.lunarcore.game.inventory.ItemMainType.Relic || ItemMainType == emu.lunarcore.game.inventory.ItemMainType.Equipment;
}
public int getRelicExp() {
if (this.relicExcel != null) {
return this.relicExcel.getExpProvide();
}
return this.relicExp;
}
public int getRelicExpCost() {
if (this.relicExcel != null) {
return this.relicExcel.getCoinCost();
}
return this.expCost;
}
public int getEquipmentExp() {
if (this.equipmentExcel != null) {
return this.equipmentExcel.getExpProvide();
}
return this.equipmentExp;
}
public int getEquipmentExpCost() {
if (this.equipmentExcel != null) {
return this.equipmentExcel.getCoinCost();
}
return this.expCost;
}
public int getEquipSlot() {
if (this.getRelicExcel() != null) {
return this.getRelicExcel().getType().getVal();
} else if (this.getEquipmentExcel() != null) {
return 100;
}
return 0;
}
}

View File

@@ -0,0 +1,20 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = {"MapEntrance.json"})
public class MapEntranceExcel extends GameResource {
private int ID;
private int PlaneID;
private int FloorID;
private int StartGroupID;
private int StartAnchorID;
@Override
public int getId() {
return ID;
}
}

View File

@@ -0,0 +1,18 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = {"MonsterConfig.json"})
public class MonsterExcel extends GameResource {
private int MonsterID;
private int MonsterTemplateID;
private long MonsterName;
@Override
public int getId() {
return MonsterID;
}
}

View File

@@ -0,0 +1,22 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = {"NPCMonsterData.json"})
public class NpcMonsterExcel extends GameResource {
private int ID;
private long NPCName;
@Override
public int getId() {
return ID;
}
@Override
public void onLoad() {
}
}

View File

@@ -0,0 +1,18 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"PlayerLevelConfig.json"}, loadPriority = LoadPriority.NORMAL)
public class PlayerLevelExcel extends GameResource {
private int Level;
private int PlayerExp;
@Override
public int getId() {
return Level;
}
}

View File

@@ -0,0 +1,37 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.game.inventory.RelicType;
import lombok.Getter;
@Getter
@ResourceType(name = {"RelicConfig.json"}, loadPriority = LoadPriority.LOW)
public class RelicExcel extends GameResource {
private int ID;
private int SetID;
private RelicType Type;
private int MainAffixGroup;
private int SubAffixGroup;
private int MaxLevel;
private int ExpType;
private int ExpProvide;
private int CoinCost;
@Override
public int getId() {
return ID;
}
@Override
public void onLoad() {
ItemExcel excel = GameData.getItemExcelMap().get(this.getId());
if (excel != null) {
excel.setRelicExcel(this);
}
}
}

View File

@@ -0,0 +1,29 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"RelicExpItem.json"}, loadPriority = LoadPriority.LOW)
public class RelicExpItemExcel extends GameResource {
private int ItemID;
private int ExpProvide;
private int CoinCost;
@Override
public int getId() {
return ItemID;
}
@Override
public void onLoad() {
ItemExcel excel = GameData.getItemExcelMap().get(ItemID);
if (excel == null) return;
excel.setRelicExp(ExpProvide);
excel.setExpCost(CoinCost);
}
}

View File

@@ -0,0 +1,24 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"RelicExpType.json"}, loadPriority = LoadPriority.NORMAL)
public class RelicExpTypeExcel extends GameResource {
private int TypeID;
private int Level;
private int Exp;
@Override
public int getId() {
return (TypeID << 16) + Level;
}
@Override
public void onLoad() {
}
}

View File

@@ -0,0 +1,31 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameDepot;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.game.avatar.AvatarPropertyType;
import lombok.Getter;
@Getter
@ResourceType(name = {"RelicMainAffixConfig.json"}, loadPriority = LoadPriority.NORMAL)
public class RelicMainAffixExcel extends GameResource {
private int GroupID;
private int AffixID;
private AvatarPropertyType Property;
private double BaseValue;
private double LevelAdd;
private boolean IsAvailable;
@Override
public int getId() {
return (GroupID << 16) + AffixID;
}
@Override
public void onLoad() {
GameDepot.addRelicMainAffix(this);
}
}

View File

@@ -0,0 +1,31 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameDepot;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.game.avatar.AvatarPropertyType;
import lombok.Getter;
@Getter
@ResourceType(name = {"RelicSubAffixConfig.json"}, loadPriority = LoadPriority.NORMAL)
public class RelicSubAffixExcel extends GameResource {
private int GroupID;
private int AffixID;
private AvatarPropertyType Property;
private double BaseValue;
private double StepValue;
private int StepNum;
@Override
public int getId() {
return (GroupID << 16) + AffixID;
}
@Override
public void onLoad() {
GameDepot.addRelicSubAffix(this);
}
}

View File

@@ -0,0 +1,23 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = {"StageConfig.json"})
public class StageExcel extends GameResource {
private int StageID;
private long StageName;
private int Level;
@Override
public int getId() {
return StageID;
}
@Override
public void onLoad() {
}
}

View File

@@ -0,0 +1,23 @@
package emu.lunarcore.database;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
@Entity(value = "counters", useDiscriminator = false)
public class DatabaseCounter {
@Id
private String id;
private int count;
public DatabaseCounter() {}
public DatabaseCounter(String id) {
this.id = id;
this.count = 10000;
}
public int getNextId() {
int id = ++count;
return id;
}
}

View File

@@ -0,0 +1,182 @@
package emu.lunarcore.database;
import java.util.stream.Stream;
import org.reflections.Reflections;
import com.mongodb.MongoCommandException;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import com.mongodb.client.result.DeleteResult;
import de.bwaldvogel.mongo.MongoBackend;
import de.bwaldvogel.mongo.MongoServer;
import de.bwaldvogel.mongo.backend.h2.H2Backend;
import de.bwaldvogel.mongo.backend.memory.MemoryBackend;
import dev.morphia.Datastore;
import dev.morphia.DeleteOptions;
import dev.morphia.Morphia;
import dev.morphia.annotations.Entity;
import dev.morphia.mapping.Mapper;
import dev.morphia.mapping.MapperOptions;
import dev.morphia.query.filters.Filters;
import emu.lunarcore.Config.DatabaseInfo;
import emu.lunarcore.Config.InternalMongoInfo;
import emu.lunarcore.LunarRail;
public final class DatabaseManager {
private MongoServer server;
private Datastore datastore;
private DeleteOptions DELETE_MANY = new DeleteOptions().multi(true);
public DatabaseManager() {
}
public DatabaseManager(DatabaseInfo info) {
// Variables
String connectionString = info.getUri();
// Local mongo server
if (info.isUseInternal()) {
connectionString = startInternalMongoServer(LunarRail.getConfig().getInternalMongoServer());
LunarRail.getLogger().info("Using local mongo server at " + server.getConnectionString());
}
// Initialize
MongoClient gameMongoClient = MongoClients.create(connectionString);
// Set mapper options.
MapperOptions mapperOptions = MapperOptions.builder()
.storeEmpties(true)
.storeNulls(false)
.build();
// Create data store.
datastore = Morphia.createDatastore(gameMongoClient, info.getCollection(), mapperOptions);
// Map classes
Class<?>[] entities = new Reflections(LunarRail.class.getPackageName())
.getTypesAnnotatedWith(Entity.class)
.stream()
.filter(cls -> {
Entity e = cls.getAnnotation(Entity.class);
return e != null && !e.value().equals(Mapper.IGNORED_FIELDNAME);
})
.toArray(Class<?>[]::new);
datastore.getMapper().map(entities);
// Ensure indexes
ensureIndexes();
}
public MongoServer getServer() {
return server;
}
public MongoDatabase getDatabase() {
return getDatastore().getDatabase();
}
public Datastore getDatastore() {
return datastore;
}
private void ensureIndexes() {
try {
datastore.ensureIndexes();
} catch (MongoCommandException exception) {
LunarRail.getLogger().warn("Mongo index error: ", exception);
// Duplicate index error
if (exception.getCode() == 85) {
// Drop all indexes and re add them
MongoIterable<String> collections = datastore.getDatabase().listCollectionNames();
for (String name : collections) {
datastore.getDatabase().getCollection(name).dropIndexes();
}
// Add back indexes
datastore.ensureIndexes();
}
}
}
//
public String startInternalMongoServer(InternalMongoInfo internalMongo) {
// Get backend
MongoBackend backend = null;
if (internalMongo.filePath != null && internalMongo.filePath.length() > 0) {
backend = new H2Backend(internalMongo.filePath);
} else {
backend = new MemoryBackend();
}
// Create the local mongo server and replace the connection string
server = new MongoServer(backend);
// Bind to address of it exists
if (internalMongo.getAddress() != null && internalMongo.getPort() != 0) {
server.bind(internalMongo.getAddress(), internalMongo.getPort());
} else {
server.bind(); // Binds to random port
}
return server.getConnectionString();
}
// Database Functions
public boolean checkIfObjectExists(Class<?> cls, long uid) {
return getDatastore().find(cls).filter(Filters.eq("_id", uid)).count() > 0;
}
public <T> T getObjectByUid(Class<T> cls, long uid) {
return getDatastore().find(cls).filter(Filters.eq("_id", uid)).first();
}
public <T> T getObjectByField(Class<T> cls, String filter, String value) {
return getDatastore().find(cls).filter(Filters.eq(filter, value)).first();
}
public <T> T getObjectByField(Class<T> cls, String filter, int value) {
return getDatastore().find(cls).filter(Filters.eq(filter, value)).first();
}
public <T> Stream<T> getObjects(Class<T> cls, String filter, long uid) {
return getDatastore().find(cls).filter(Filters.eq(filter, uid)).stream();
}
public <T> Stream<T> getObjects(Class<T> cls) {
return getDatastore().find(cls).stream();
}
public <T> void save(T obj) {
getDatastore().save(obj);
}
public <T> boolean delete(T obj) {
DeleteResult result = getDatastore().delete(obj);
return result.getDeletedCount() > 0;
}
public boolean delete(Class<?> cls, String filter, long uid) {
DeleteResult result = getDatastore().find(cls).filter(Filters.eq(filter, uid)).delete(DELETE_MANY);
return result.getDeletedCount() > 0;
}
public synchronized int getNextObjectId(Class<?> c) {
DatabaseCounter counter = getDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first();
if (counter == null) {
counter = new DatabaseCounter(c.getSimpleName());
}
try {
return counter.getNextId();
} finally {
getDatastore().save(counter);
}
}
}

View File

@@ -0,0 +1,60 @@
package emu.lunarcore.game.account;
import dev.morphia.annotations.*;
import emu.lunarcore.LunarRail;
import emu.lunarcore.util.Crypto;
import emu.lunarcore.util.Snowflake32;
import emu.lunarcore.util.Utils;
import lombok.Getter;
@Getter
@Entity(value = "accounts", useDiscriminator = false)
public class Account {
@Id private String uid;
@Indexed(options = @IndexOptions(unique = true))
@Collation(locale = "simple", caseLevel = true)
private String username;
private String password; // Unused for now
private int reservedPlayerUid;
private String comboToken; // Combo token
private String dispatchToken; // Session token for dispatch server
@Deprecated
public Account() {
}
public Account(String username) {
this.uid = Long.toString(Snowflake32.newUid());
this.username = username;
}
public String getEmail() {
return username;
}
public void setReservedPlayerUid(int uid) {
this.reservedPlayerUid = uid;
}
// TODO make unique
public String generateComboToken() {
this.comboToken = Utils.bytesToHex(Crypto.createSessionKey(32));
this.save();
return this.comboToken;
}
// TODO make unique
public String generateDispatchToken() {
this.dispatchToken = Utils.bytesToHex(Crypto.createSessionKey(32));
this.save();
return this.dispatchToken;
}
public void save() {
LunarRail.getAccountDatabase().save(this);
}
}

View File

@@ -0,0 +1,21 @@
package emu.lunarcore.game.avatar;
import lombok.Getter;
@Getter
public enum AvatarBaseType {
Unknown (0),
Warrior (1),
Rogue (2),
Mage (3),
Shaman (4),
Warlock (5),
Knight (6),
Priest (7);
private final int val;
private AvatarBaseType(int value) {
this.val = value;
}
}

View File

@@ -0,0 +1,74 @@
package emu.lunarcore.game.avatar;
import lombok.Getter;
public enum AvatarPropertyType {
Unknown (0),
MaxHP (1),
Attack (2),
Defence (3),
Speed (4),
CriticalChance (5),
CriticalDamage (6),
HealRatio (7),
StanceBreakAddedRatio (8),
SPRatio (9),
StatusProbability (10),
StatusResistance (11),
PhysicalAddedRatio (12),
PhysicalResistance (13),
FireAddedRatio (14),
FireResistance (15),
IceAddedRatio (16),
IceResistance (17),
ThunderAddedRatio (18),
ThunderResistance (19),
WindAddedRatio (20),
WindResistance (21),
QuantumAddedRatio (22),
QuantumResistance (23),
ImaginaryAddedRatio (24),
ImaginaryResistance (25),
BaseHP (26),
HPDelta (27),
BaseAttack (28),
AttackDelta (29),
BaseDefence (30),
DefenceDelta (31),
HPAddedRatio (32),
AttackAddedRatio (33),
DefenceAddedRatio (34),
BaseSpeed (35),
HealTakenRatio (36),
PhysicalResistanceDelta (37),
FireResistanceDelta (38),
IceResistanceDelta (39),
ThunderResistanceDelta (40),
WindResistanceDelta (41),
QuantumResistanceDelta (42),
ImaginaryResistanceDelta (43),
AllDamageReduce (44),
RelicValueExtraAdditionRatio (45),
EquipValueExtraAdditionRatio (46),
EquipExtraRank (47),
AvatarExtraRank (48),
AllDamageTypeAddedRatio (49),
SpeedAddedRatio (50),
SpeedDelta (51),
CriticalChanceBase (52),
CriticalDamageBase (53),
SPRatioBase (54),
HealRatioBase (55),
StatusProbabilityBase (56),
StatusResistanceBase (57),
BreakDamageAddedRatio (58),
BreakDamageAddedRatioBase (59),
MaxSP (60);
@Getter
private int val;
private AvatarPropertyType(int value) {
this.val = value;
}
}

View File

@@ -0,0 +1,94 @@
package emu.lunarcore.game.avatar;
import java.util.Iterator;
import java.util.stream.Stream;
import emu.lunarcore.LunarRail;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.AvatarExcel;
import emu.lunarcore.game.player.BasePlayerManager;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class AvatarStorage extends BasePlayerManager implements Iterable<GameAvatar> {
private final Int2ObjectMap<GameAvatar> avatars;
public AvatarStorage(Player player) {
super(player);
this.avatars = new Int2ObjectOpenHashMap<>();
}
public Int2ObjectMap<GameAvatar> getAvatars() {
return avatars;
}
public int getAvatarCount() {
return this.avatars.size();
}
public GameAvatar getAvatarById(int id) {
return getAvatars().get(id);
}
public boolean hasAvatar(int id) {
return getAvatars().containsKey(id);
}
public boolean addAvatar(GameAvatar avatar) {
// Sanity
if (avatar.getExcel() == null || this.hasAvatar(avatar.getAvatarId())) {
return false;
}
// Set owner first
avatar.setOwner(getPlayer());
// Put into maps
this.avatars.put(avatar.getAvatarId(), avatar);
// Save to database
avatar.save();
// Send packet
getPlayer().sendPacket(new PacketPlayerSyncScNotify(avatar));
return true;
}
public void recalcAvatarStats() {
//this.getAvatars().values().stream().forEach(GameAvatar::recalcStats);
}
@Override
public Iterator<GameAvatar> iterator() {
return getAvatars().values().iterator();
}
// Database
public void loadFromDatabase() {
Stream<GameAvatar> stream = LunarRail.getGameDatabase().getObjects(GameAvatar.class, "ownerUid", this.getPlayer().getUid());
stream.forEach(avatar -> {
// Should never happen
if (avatar.getId() == null) {
return;
}
// Load avatar excel data
AvatarExcel excel = GameData.getAvatarExcelMap().get(avatar.getAvatarId());
if (excel == null) {
return;
}
// Set ownerships
avatar.setExcel(excel);
avatar.setOwner(getPlayer());
// Add to avatar storage
this.avatars.put(avatar.getAvatarId(), avatar);
});
}
}

View File

@@ -0,0 +1,6 @@
package emu.lunarcore.game.avatar;
// These are in excels but i prefer them as enums
public enum DamageType {
Physical, Ice, Fire, Thunder, Wind, Quantum, Imaginary;
}

View File

@@ -0,0 +1,276 @@
package emu.lunarcore.game.avatar;
import java.util.HashMap;
import java.util.Map;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.lunarcore.GameConstants;
import emu.lunarcore.LunarRail;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.AvatarExcel;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.inventory.ItemMainType;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.scene.GameEntity;
import emu.lunarcore.proto.AvatarOuterClass.Avatar;
import emu.lunarcore.proto.AvatarSkillTreeOuterClass.AvatarSkillTree;
import emu.lunarcore.proto.AvatarTypeOuterClass.AvatarType;
import emu.lunarcore.proto.BattleAvatarOuterClass.BattleAvatar;
import emu.lunarcore.proto.BattleEquipmentOuterClass.BattleEquipment;
import emu.lunarcore.proto.BattleRelicOuterClass.BattleRelic;
import emu.lunarcore.proto.EquipRelicOuterClass.EquipRelic;
import emu.lunarcore.proto.LineupAvatarOuterClass.LineupAvatar;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
import emu.lunarcore.proto.SceneActorInfoOuterClass.SceneActorInfo;
import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.lunarcore.proto.SpBarInfoOuterClass.SpBarInfo;
import emu.lunarcore.proto.VectorOuterClass.Vector;
import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
@Getter
@Entity(value = "avatars", useDiscriminator = false)
public class GameAvatar implements GameEntity {
@Id private ObjectId id;
@Indexed @Getter private int ownerUid; // Uid of player that this avatar belongs to
private transient Player owner;
private transient AvatarExcel excel;
private int avatarId; // Id of avatar
@Setter private int level;
@Setter private int exp;
@Setter private int promotion;
@Setter private int rank; // Eidolons
private int currentHp;
private int currentSp;
private Map<Integer, Integer> skills;
private transient int entityId;
private transient Int2ObjectMap<GameItem> equips;
@Deprecated // Morphia only
public GameAvatar() {
this.equips = new Int2ObjectOpenHashMap<>();
this.currentHp = 10000;
this.currentSp = 0;
}
// On creation
public GameAvatar(int avatarId) {
this(GameData.getAvatarExcelMap().get(avatarId));
}
public GameAvatar(AvatarExcel excel) {
this();
this.excel = excel;
this.avatarId = excel.getId();
this.level = 1;
// Set default skills
this.skills = new HashMap<>();
for (var skillTree : excel.getDefaultSkillTrees()) {
this.skills.put(skillTree.getPointID(), skillTree.getLevel());
}
// Set stats
this.currentHp = 10000;
}
public void setOwner(Player player) {
this.owner = player;
this.ownerUid = player.getUid();
}
public void setExcel(AvatarExcel excel) {
this.excel = excel;
}
@Override
public void setEntityId(int entityId) {
this.entityId = entityId;
}
public int getMaxSp() {
return this.getExcel().getMaxSp();
}
public void setCurrentHp(int amount) {
this.currentHp = Math.max(Math.min(amount, 10000), 0);
}
public void setCurrentSp(int amount) {
this.currentSp = Math.max(Math.min(amount, getMaxSp()), 0);
}
// Equips
public GameItem getEquipBySlot(int slot) {
return this.getEquips().get(slot);
}
public GameItem getEquipment() {
return this.getEquips().get(GameConstants.EQUIPMENT_SLOT_ID);
}
public boolean equipItem(GameItem item) {
// Sanity check
int slot = item.getEquipSlot();
if (slot == 0) return false;
// Check if other avatars have this item equipped
GameAvatar otherAvatar = getOwner().getAvatarById(item.getEquipAvatar());
if (otherAvatar != null) {
// Unequip this item from the other avatar
if (otherAvatar.unequipItem(slot) != null) {
getOwner().sendPacket(new PacketPlayerSyncScNotify(otherAvatar));
}
// Swap with other avatar
if (getEquips().containsKey(slot)) {
GameItem toSwap = this.getEquipBySlot(slot);
otherAvatar.equipItem(toSwap);
}
} else if (getEquips().containsKey(slot)) {
// Unequip item in current slot if it exists
GameItem unequipped = unequipItem(slot);
if (unequipped != null) {
getOwner().sendPacket(new PacketPlayerSyncScNotify(unequipped));
}
}
// Set equip
getEquips().put(slot, item);
// Save equip if equipped avatar was changed
if (item.setEquipAvatar(this.getAvatarId())) {
item.save();
}
// Send packet
getOwner().sendPacket(new PacketPlayerSyncScNotify(this, item));
return true;
}
public GameItem unequipItem(int slot) {
GameItem item = getEquips().remove(slot);
if (item != null) {
item.setEquipAvatar(0);
item.save();
return item;
}
return null;
}
// Proto
public Avatar toProto() {
var proto = Avatar.newInstance()
.setBaseAvatarId(this.getAvatarId())
.setLevel(this.getLevel())
.setExp(this.getExp())
.setPromotion(this.getPromotion())
.setRank(this.getRank());
for (var equip : this.getEquips().values()) {
if (equip.getItemMainType() == ItemMainType.Relic) {
proto.addEquipRelicList(EquipRelic.newInstance().setSlot(equip.getEquipSlot()).setRelicUniqueId(equip.getInternalUid()));
} else if (equip.getItemMainType() == ItemMainType.Equipment) {
proto.setEquipmentUniqueId(equip.getInternalUid());
}
}
for (var skill : getSkills().entrySet()) {
proto.addSkilltreeList(AvatarSkillTree.newInstance().setPointId(skill.getKey()).setLevel(skill.getValue()));
}
return proto;
}
public LineupAvatar toLineupAvatarProto(int slot) {
var proto = LineupAvatar.newInstance()
.setAvatarType(AvatarType.AVATAR_FORMAL_TYPE)
.setId(this.getAvatarId())
.setSpBar(SpBarInfo.newInstance().setCurSp(this.getCurrentSp()).setMaxSp(this.getMaxSp()))
.setHp(this.getCurrentHp())
.setSlot(slot);
return proto;
}
@Override
public SceneEntityInfo toSceneEntityProto() {
var proto = SceneEntityInfo.newInstance()
.setEntityId(this.getEntityId())
.setMotion(MotionInfo.newInstance().setPos(getOwner().getPos().toProto()).setRot(Vector.newInstance().setY(0)))
.setActor(SceneActorInfo.newInstance().setBaseAvatarId(this.getAvatarId()).setAvatarType(AvatarType.AVATAR_FORMAL_TYPE));
return proto;
}
public BattleAvatar toBattleProto(int index) {
var proto = BattleAvatar.newInstance()
.setAvatarType(AvatarType.AVATAR_FORMAL_TYPE)
.setId(this.getAvatarId())
.setLevel(this.getLevel())
.setPromotion(this.getPromotion())
.setRank(this.getRank())
.setIndex(index)
.setHp(this.getCurrentHp())
.setSpBar(SpBarInfo.newInstance().setCurSp(this.getCurrentSp()).setMaxSp(this.getMaxSp()))
.setWorldLevel(this.getOwner().getWorldLevel());
// Skill tree
for (var skill : getSkills().entrySet()) {
proto.addSkilltreeList(AvatarSkillTree.newInstance().setPointId(skill.getKey()).setLevel(skill.getValue()));
}
// Build equips
for (var equip : this.getEquips().values()) {
if (equip.getItemMainType() == ItemMainType.Relic) {
// Build battle relic proto
var relic = BattleRelic.newInstance()
.setId(equip.getItemId())
.setLevel(equip.getLevel())
.setUniqueId(equip.getInternalUid())
.setMainAffixId(equip.getMainAffix());
if (equip.getSubAffixes() != null) {
for (var subAffix : equip.getSubAffixes()) {
relic.addSubAffixList(subAffix.toProto());
}
}
proto.addRelicList(relic);
} else if (equip.getItemMainType() == ItemMainType.Equipment) {
// Build battle equipment proto
var equipment = BattleEquipment.newInstance()
.setId(equip.getItemId())
.setLevel(equip.getLevel())
.setPromotion(equip.getPromotion())
.setRank(equip.getRank());
proto.addEquipmentList(equipment);
}
}
return proto;
}
// Database
public void save() {
LunarRail.getGameDatabase().save(this);
}
}

View File

@@ -0,0 +1,15 @@
package emu.lunarcore.game.battle;
import emu.lunarcore.game.player.Player;
public class Battle {
private final Player player;
public Battle(Player player) {
this.player = player;
}
public Player getPlayer() {
return player;
}
}

View File

@@ -0,0 +1,72 @@
package emu.lunarcore.game.battle;
import java.util.Collection;
import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.scene.EntityMonster;
import emu.lunarcore.game.scene.GameEntity;
import emu.lunarcore.proto.AvatarBattleInfoOuterClass.AvatarBattleInfo;
import emu.lunarcore.proto.AvatarPropertyOuterClass.AvatarProperty;
import emu.lunarcore.proto.BattleEndStatusOuterClass.BattleEndStatus;
import emu.lunarcore.server.game.BaseGameService;
import emu.lunarcore.server.game.GameServer;
import emu.lunarcore.server.packet.send.PacketSceneCastSkillScRsp;
import emu.lunarcore.server.packet.send.PacketSyncLineupNotify;
import us.hebi.quickbuf.RepeatedInt;
import us.hebi.quickbuf.RepeatedMessage;
public class BattleService extends BaseGameService {
public BattleService(GameServer server) {
super(server);
}
public void onBattleStart(Player player, int attackerId, RepeatedInt attackedList) {
// Setup variables
int entityId = attackedList.get(0);
GameEntity entity = null;
// Check if attacker is the player or not
if (player.getScene().getAvatarEntityIds().contains(attackerId)) {
entity = player.getScene().getEntities().get(entityId);
} else if (player.getScene().getAvatarEntityIds().contains(entityId)) {
entity = player.getScene().getEntities().get(attackerId);
}
if (entity != null) {
if (entity instanceof EntityMonster) {
player.sendPacket(new PacketSceneCastSkillScRsp(player, (EntityMonster) entity));
return;
}
}
player.sendPacket(new PacketSceneCastSkillScRsp(1));
}
public void onBattleResult(Player player, BattleEndStatus result, RepeatedMessage<AvatarBattleInfo> battleAvatars) {
// Lose
if (result == BattleEndStatus.BATTLE_END_LOSE) {
}
// Set health/energy
for (var battleAvatar : battleAvatars) {
GameAvatar avatar = player.getAvatarById(battleAvatar.getId());
if (avatar == null) continue;
AvatarProperty prop = battleAvatar.getAvatarStatus();
int currentHp = (int) Math.round((prop.getLeftHp() / prop.getMaxHp()) * 100);
int currentSp = (int) prop.getLeftSp() * 100;
//avatar.setCurrentHp(currentHp);
avatar.setCurrentSp(currentSp);
avatar.save();
}
// Sync with player
player.sendPacket(new PacketSyncLineupNotify(player.getLineupManager().getCurrentLineup()));
}
}

View File

@@ -0,0 +1,46 @@
package emu.lunarcore.game.gacha;
import emu.lunarcore.proto.GachaCeilingOuterClass.GachaCeiling;
import emu.lunarcore.proto.GachaInfoOuterClass.GachaInfo;
import lombok.Getter;
@Getter
public class GachaBanner {
private int id; // Id should match one of the ids in GachaBasicInfo.json
private GachaType gachaType;
private int beginTime;
private int endTime;
private int[] rateUpItems5;
private int[] rateUpItems4;
private int eventChance = 50;
public GachaInfo toProto() {
var info = GachaInfo.newInstance()
.setGachaId(this.getId())
.setDetailUrl("")
.setHistoryUrl("");
if (this.gachaType == GachaType.Normal) {
// Gacha ceiling
info.setGachaCeiling(GachaCeiling.newInstance());
} else {
info.setBeginTime(this.getBeginTime());
info.setEndTime(this.getEndTime());
}
if (this.getRateUpItems4().length > 0) {
for (int id : getRateUpItems4()) {
info.addUpInfo(id);
}
}
if (this.getRateUpItems5().length > 0) {
for (int id : getRateUpItems5()) {
info.addUpInfo(id);
info.addFeatured(id);
}
}
return info;
}
}

View File

@@ -0,0 +1,283 @@
package emu.lunarcore.game.gacha;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import emu.lunarcore.LunarRail;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.inventory.ItemMainType;
import emu.lunarcore.game.inventory.ItemRarity;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.proto.GachaItemOuterClass.GachaItem;
import emu.lunarcore.proto.GetGachaInfoScRspOuterClass.GetGachaInfoScRsp;
import emu.lunarcore.proto.ItemListOuterClass.ItemList;
import emu.lunarcore.server.game.BaseGameService;
import emu.lunarcore.server.game.GameServer;
import emu.lunarcore.server.packet.send.PacketDoGachaScRsp;
import emu.lunarcore.util.JsonUtils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
public class GachaService extends BaseGameService {
private final Int2ObjectMap<GachaBanner> gachaBanners;
private GetGachaInfoScRsp cachedProto;
private int[] yellowAvatars = new int[] {1003, 1004, 1101, 1107, 1104, 1209, 1211};
private int[] yellowWeapons = new int[] {23000, 23002, 23003, 23004, 23005, 23012, 23013};
private int[] purpleAvatars = new int[] {1001, 1002, 1008, 1009, 1013, 1103, 1105, 1106, 1108, 1109, 1111, 1201, 1202, 1206, 1207};
private int[] purpleWeapons = new int[] {21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21009, 21010, 21011, 21012, 21013, 21014, 21015, 21016, 21017, 21018, 21019, 21020};
private int[] blueWeapons = new int[] {20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20009, 20010, 20011, 20012, 20013, 20014, 20015, 20016, 20017, 20018, 20019, 20020};
private static int starglitterId = 251;
private static int stardustId = 252;
public GachaService(GameServer server) {
super(server);
this.gachaBanners = new Int2ObjectOpenHashMap<>();
this.load();
}
public Int2ObjectMap<GachaBanner> getGachaBanners() {
return gachaBanners;
}
public int randomRange(int min, int max) {
return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
}
public int getRandom(int[] array) {
return array[randomRange(0, array.length - 1)];
}
public synchronized void load() {
try (FileReader fileReader = new FileReader(LunarRail.getConfig().getDataDir() + "/Banners.json")) {
List<GachaBanner> banners = JsonUtils.loadToList(fileReader, GachaBanner.class);
for (GachaBanner banner : banners) {
getGachaBanners().put(banner.getId(), banner);
}
} catch (Exception e) {
// TODO Auto-generated catch block
LunarRail.getLogger().warn("No gacha banners loaded!");
}
}
public synchronized void doPulls(Player player, int gachaId, int times) {
// Sanity check
if (times != 10 && times != 1) {
return;
}
if (player.getInventory().getInventoryTab(ItemMainType.Equipment).getSize() + times > player.getInventory().getInventoryTab(ItemMainType.Equipment).getMaxCapacity()) {
player.sendPacket(new PacketDoGachaScRsp());
return;
}
// Get banner
GachaBanner banner = this.getGachaBanners().get(gachaId);
if (banner == null) {
player.sendPacket(new PacketDoGachaScRsp());
return;
}
// Spend currency
if (banner.getGachaType().getCostItem() > 0) {
GameItem costItem = player.getInventory().getInventoryTab(ItemMainType.Material).getItemById(banner.getGachaType().getCostItem());
if (costItem == null || costItem.getCount() < times) {
return;
}
player.getInventory().removeItem(costItem, times);
}
// Roll
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner.getGachaType());
IntList wonItems = new IntArrayList(times);
for (int i = 0; i < times; i++) {
int random = this.randomRange(1, 10000);
int itemId = 0;
int bonusYellowChance = gachaInfo.getPity5() >= 74 ? 100 * (gachaInfo.getPity5() - 73): 0;
int yellowChance = 60 + (int) Math.floor(100f * (gachaInfo.getPity5() / 73f)) + bonusYellowChance;
int purpleChance = 10000 - (510 + (int) Math.floor(790f * (gachaInfo.getPity4() / 8f)));
if (random <= yellowChance || gachaInfo.getPity5() >= 89) {
if (banner.getRateUpItems5().length > 0) {
int eventChance = this.randomRange(1, 100);
if (eventChance <= banner.getEventChance() || gachaInfo.getFailedFeaturedItemPulls() >= 1) {
itemId = getRandom(banner.getRateUpItems5());
gachaInfo.setFailedFeaturedItemPulls(0);
} else {
// Lost the 50/50... rip
gachaInfo.addFailedFeaturedItemPulls(1);
}
}
if (itemId == 0) {
int typeChance = this.randomRange(banner.getGachaType().getMinItemType(), banner.getGachaType().getMaxItemType());
if (typeChance == 1) {
itemId = getRandom(this.yellowAvatars);
} else {
itemId = getRandom(this.yellowWeapons);
}
}
// Pity
gachaInfo.addPity4(1);
gachaInfo.setPity5(0);
} else if (random >= purpleChance || gachaInfo.getPity4() >= 9) {
if (banner.getRateUpItems4().length > 0) {
int eventChance = this.randomRange(1, 100);
if (eventChance >= 50) {
itemId = getRandom(banner.getRateUpItems4());
}
}
if (itemId == 0) {
int typeChance = this.randomRange(banner.getGachaType().getMinItemType(), banner.getGachaType().getMaxItemType());
if (typeChance == 1) {
itemId = getRandom(this.purpleAvatars);
} else {
itemId = getRandom(this.purpleWeapons);
}
}
// Pity
gachaInfo.addPity5(1);
gachaInfo.setPity4(0);
} else {
itemId = getRandom(this.blueWeapons);
// Pity
gachaInfo.addPity4(1);
gachaInfo.addPity5(1);
}
// Add winning item
wonItems.add(itemId);
}
// Add to character
List<GachaItem> list = new ArrayList<>();
int stardust = 0, starglitter = 0;
for (int itemId : wonItems) {
ItemExcel itemData = GameData.getItemExcelMap().get(itemId);
if (itemData == null) {
continue;
}
// Create gacha item
GachaItem gachaItem = GachaItem.newInstance();
int addStardust = 0, addStarglitter = 0;
// Dupe check
if (itemData.getItemMainType() == ItemMainType.AvatarCard) {
int avatarId = itemData.getId();
GameAvatar avatar = player.getAvatars().getAvatarById(avatarId);
if (avatar != null) {
int constLevel = avatar.getRank();
int constItemId = avatarId + 10000; // Hacky. TODO optimize by using AvatarRankExcel
GameItem constItem = player.getInventory().getInventoryTab(ItemMainType.Material).getItemById(constItemId);
if (constItem != null) {
constLevel += constItem.getCount();
}
if (constLevel < 6) {
// Not max const
addStarglitter = 2;
// Add 1 const
//gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null));
//gachaItem.addTokenItemList(ItemParam.newBuilder().setItemId(constItemId).setCount(1));
player.getInventory().addItem(constItemId, 1);
} else {
// Is max const
addStarglitter = 5;
}
if (itemData.getRarity() == ItemRarity.SuperRare) {
addStarglitter *= 5;
}
} else {
// New
gachaItem.setIsNew(true);
}
} else {
// Is weapon
switch (itemData.getRarity()) {
case SuperRare:
addStarglitter = 10;
break;
case VeryRare:
addStarglitter = 2;
break;
case Rare:
addStardust = 15;
break;
default:
break;
}
}
// Create item
GameItem item = new GameItem(itemData);
gachaItem.setGachaItem(item.toProto());
gachaItem.setUnk1(ItemList.newInstance());
gachaItem.setUnk2(ItemList.newInstance());
player.getInventory().addItem(item);
stardust += addStardust;
starglitter += addStarglitter;
/*
if (addStardust > 0) {
gachaItem.addTokenItemList(ItemParam.newBuilder().setItemId(stardustId).setCount(addStardust));
} if (addStarglitter > 0) {
ItemParam starglitterParam = ItemParam.newBuilder().setItemId(starglitterId).setCount(addStarglitter).build();
if (isTransferItem) {
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(starglitterParam));
}
gachaItem.addTokenItemList(starglitterParam);
}
*/
list.add(gachaItem.newInstance());
}
// Add stardust/starglitter
if (stardust > 0) {
player.getInventory().addItem(stardustId, stardust);
} if (starglitter > 0) {
player.getInventory().addItem(starglitterId, starglitter);
}
// Packets
player.sendPacket(new PacketDoGachaScRsp(banner, times, list));
}
private synchronized GetGachaInfoScRsp createProto() {
var proto = GetGachaInfoScRsp.newInstance();
for (GachaBanner banner : getGachaBanners().values()) {
proto.addGachaInfoList(banner.toProto());
}
return proto;
}
public GetGachaInfoScRsp toProto() {
if (this.cachedProto == null) {
this.cachedProto = createProto();
}
return this.cachedProto;
}
}

View File

@@ -0,0 +1,21 @@
package emu.lunarcore.game.gacha;
import lombok.Getter;
@Getter
public enum GachaType {
Newbie (101, 1, 2),
Normal (101, 1, 2),
AvatarUp (102, 1, 1),
WeaponUp (102, 2, 2);
private int costItem;
private int minItemType;
private int maxItemType;
private GachaType(int costItem, int min, int max) {
this.costItem = costItem;
this.minItemType = min;
this.maxItemType = max;
}
}

View File

@@ -0,0 +1,46 @@
package emu.lunarcore.game.gacha;
import dev.morphia.annotations.Entity;
@Entity(useDiscriminator = false)
public class PlayerGachaBannerInfo {
private int pity5 = 0;
private int pity4 = 0;
private int failedFeaturedItemPulls = 0;
public int getPity5() {
return pity5;
}
public void setPity5(int pity5) {
this.pity5 = pity5;
}
public void addPity5(int amount) {
this.pity5 += amount;
}
public int getPity4() {
return pity4;
}
public void setPity4(int pity4) {
this.pity4 = pity4;
}
public void addPity4(int amount) {
this.pity4 += amount;
}
public int getFailedFeaturedItemPulls() {
return failedFeaturedItemPulls;
}
public void setFailedFeaturedItemPulls(int failedEventCharacterPulls) {
this.failedFeaturedItemPulls = failedEventCharacterPulls;
}
public void addFailedFeaturedItemPulls(int amount) {
failedFeaturedItemPulls += amount;
}
}

View File

@@ -0,0 +1,38 @@
package emu.lunarcore.game.gacha;
import dev.morphia.annotations.Entity;
@Entity(useDiscriminator = false)
public class PlayerGachaInfo {
private PlayerGachaBannerInfo standardBanner;
private PlayerGachaBannerInfo eventCharacterBanner;
private PlayerGachaBannerInfo eventWeaponBanner;
public PlayerGachaInfo() {
this.standardBanner = new PlayerGachaBannerInfo();
this.eventCharacterBanner = new PlayerGachaBannerInfo();
this.eventWeaponBanner = new PlayerGachaBannerInfo();
}
public PlayerGachaBannerInfo getStandardBanner() {
return standardBanner;
}
public PlayerGachaBannerInfo getEventCharacterBanner() {
return eventCharacterBanner;
}
public PlayerGachaBannerInfo getEventWeaponBanner() {
return eventWeaponBanner;
}
public PlayerGachaBannerInfo getBannerInfo(GachaType type) {
if (type == GachaType.AvatarUp) {
return this.eventCharacterBanner;
} else if (type == GachaType.WeaponUp) {
return this.eventWeaponBanner;
}
return this.standardBanner;
}
}

View File

@@ -0,0 +1,45 @@
package emu.lunarcore.game.inventory;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class EquipInventoryTab extends InventoryTab {
private final Set<GameItem> items;
private final int maxCapacity;
public EquipInventoryTab(int maxCapacity) {
this.items = new HashSet<>();
this.maxCapacity = maxCapacity;
}
@Override
public GameItem getItemById(int id) {
return null;
}
@Override
public void onAddItem(GameItem item) {
this.items.add(item);
}
@Override
public void onRemoveItem(GameItem item) {
this.items.remove(item);
}
@Override
public int getSize() {
return this.items.size();
}
@Override
public int getMaxCapacity() {
return this.maxCapacity;
}
@Override
public Iterator<GameItem> iterator() {
return items.iterator();
}
}

View File

@@ -0,0 +1,266 @@
package emu.lunarcore.game.inventory;
import java.util.ArrayList;
import java.util.List;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.lunarcore.LunarRail;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.GameDepot;
import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.data.excel.RelicMainAffixExcel;
import emu.lunarcore.data.excel.RelicSubAffixExcel;
import emu.lunarcore.game.avatar.AvatarPropertyType;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.proto.EquipmentOuterClass.Equipment;
import emu.lunarcore.proto.ItemOuterClass.Item;
import emu.lunarcore.proto.MaterialOuterClass.Material;
import emu.lunarcore.proto.PileItemOuterClass.PileItem;
import emu.lunarcore.proto.RelicOuterClass.Relic;
import emu.lunarcore.util.Utils;
import emu.lunarcore.util.WeightedList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import lombok.Getter;
import lombok.Setter;
@Getter
@Entity(value = "items", useDiscriminator = false)
public class GameItem {
@Id private ObjectId id;
@Indexed private int ownerUid; // Uid of player that this avatar belongs to
private transient int internalUid; // Internal unique id of item
private transient ItemExcel excel;
private int itemId;
private int count;
@Setter private int level;
@Setter private int exp;
@Setter private int totalExp;
@Setter private int promotion;
@Setter private int rank; // Superimpose
@Setter private boolean locked;
private int mainAffix;
private List<ItemSubAffix> subAffixes;
private int equipAvatar;
@Deprecated
public GameItem() {
// Morphia only
}
public GameItem(int itemId) {
this(GameData.getItemExcelMap().get(itemId));
}
public GameItem(int itemId, int count) {
this(GameData.getItemExcelMap().get(itemId), count);
}
public GameItem(ItemExcel data) {
this(data, 1);
}
public GameItem(ItemExcel excel, int count) {
this.itemId = excel.getId();
this.excel = excel;
switch (excel.getItemMainType()) {
case Virtual:
this.count = count;
break;
case Equipment:
this.count = 1;
this.level = 1;
this.rank = 1;
break;
case Relic:
this.count = 1;
// Init affixes
if (getExcel().getRelicExcel() != null) {
// Main affix
var affix = GameDepot.getRandomRelicMainAffix(getExcel().getRelicExcel().getMainAffixGroup());
if (affix != null) {
this.mainAffix = affix.getAffixID();
}
// Sub affixes
int baseSubAffixes = Math.min(Math.max(getExcel().getRarity().getVal() - 2, 0), 3);
this.addSubAffixes(Utils.randomRange(baseSubAffixes, baseSubAffixes + 1));
}
break;
default:
this.count = Math.min(count, excel.getPileLimit());
}
}
public void setOwner(Player player) {
this.ownerUid = player.getUid();
this.internalUid = player.getInventory().getNextItemInternalUid();
}
public void setExcel(ItemExcel excel) {
this.excel = excel;
}
public ItemMainType getItemMainType() {
return excel.getItemMainType();
}
public int getEquipSlot() {
return excel.getEquipSlot();
}
public boolean isEquipped() {
return this.getEquipAvatar() > 0;
}
public boolean isDestroyable() {
return !this.isLocked() && !this.isEquipped();
}
public void setCount(int count) {
this.count = count;
}
public boolean setEquipAvatar(int newEquipAvatar) {
if (this.equipAvatar != newEquipAvatar) {
this.equipAvatar = newEquipAvatar;
return true;
}
return false;
}
// Sub affixes
public void addSubAffixes(int quantity) {
for (int i = 0; i < quantity; i++) {
this.addSubAffix();
}
}
public void addSubAffix() {
if (this.subAffixes == null) {
this.subAffixes = new ArrayList<>();
}
if (this.subAffixes.size() < 4) {
this.addNewSubAffix();
} else {
this.upgradeRandomSubAffix();
}
}
private void addNewSubAffix() {
// Get list of affixes to add
List<RelicSubAffixExcel> affixList = GameDepot.getRelicSubAffixList(getExcel().getRelicExcel().getSubAffixGroup());
if (affixList == null) return;
// Blacklist main affix and any sub affixes
AvatarPropertyType mainAffixProperty = AvatarPropertyType.Unknown;
RelicMainAffixExcel mainAffix = GameData.getRelicMainAffixExcelMap().get(this.mainAffix);
if (mainAffix != null) {
mainAffixProperty = mainAffix.getProperty();
}
IntSet blacklist = new IntOpenHashSet();
for (ItemSubAffix subAffix : this.getSubAffixes()) {
blacklist.add(subAffix.getId());
}
// Build random list
WeightedList<RelicSubAffixExcel> randomList = new WeightedList<>();
for (RelicSubAffixExcel affix : affixList) {
if (affix.getProperty() != mainAffixProperty && !blacklist.contains(affix.getAffixID())) {
randomList.add(10, affix);
}
}
// Sanity check
if (randomList.size() == 0) {
return;
}
// Add random stat
RelicSubAffixExcel subAffix = randomList.next();
this.subAffixes.add(new ItemSubAffix(subAffix));
}
private void upgradeRandomSubAffix() {
ItemSubAffix subAffix = Utils.randomElement(this.subAffixes);
subAffix.incrementCount();
}
// Database
public void save() {
if (this.count > 0 && this.ownerUid > 0) {
LunarRail.getGameDatabase().save(this);
} else if (this.getId() != null) {
LunarRail.getGameDatabase().delete(this);
}
}
// Proto
public Material toMaterialProto() {
var proto = Material.newInstance()
.setTid(this.getItemId())
.setNum(this.getCount());
return proto;
}
public Relic toRelicProto() {
var proto = Relic.newInstance()
.setTid(this.getItemId())
.setUniqueId(this.getInternalUid())
.setLevel(this.getLevel())
.setExp(this.getExp())
.setIsProtected(this.isLocked())
.setBaseAvatarId(this.getEquipAvatar())
.setMainAffixId(this.mainAffix);
if (this.subAffixes != null) {
for (var subAffix : this.subAffixes) {
proto.addSubAffixList(subAffix.toProto());
}
}
return proto;
}
public Equipment toEquipmentProto() {
var proto = Equipment.newInstance()
.setTid(this.getItemId())
.setUniqueId(this.getInternalUid())
.setLevel(this.getLevel())
.setExp(this.getExp())
.setIsProtected(this.isLocked())
.setPromotion(this.getPromotion())
.setRank(this.getRank())
.setBaseAvatarId(this.getEquipAvatar());
return proto;
}
public PileItem toPileProto() {
return PileItem.newInstance()
.setItemId(this.getItemId())
.setItemNum(this.getCount());
}
public Item toProto() {
return Item.newInstance()
.setItemId(this.getItemId())
.setNum(this.getCount());
}
}

View File

@@ -0,0 +1,348 @@
package emu.lunarcore.game.inventory;
import java.util.Collection;
import java.util.stream.Stream;
import emu.lunarcore.LunarRail;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.common.ItemParam;
import emu.lunarcore.data.common.ItemParam.ItemParamType;
import emu.lunarcore.data.excel.AvatarExcel;
import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.game.avatar.AvatarStorage;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.player.BasePlayerManager;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
public class Inventory extends BasePlayerManager {
private final Long2ObjectMap<GameItem> store;
private final Int2ObjectMap<InventoryTab> inventoryTypes;
private int nextInternalUid;
public Inventory(Player player) {
super(player);
this.store = new Long2ObjectOpenHashMap<>();
this.inventoryTypes = new Int2ObjectOpenHashMap<>();
this.createInventoryTab(ItemMainType.Equipment, new EquipInventoryTab(1500));
this.createInventoryTab(ItemMainType.Relic, new EquipInventoryTab(1500));
this.createInventoryTab(ItemMainType.Material, new MaterialInventoryTab(2000));
}
public AvatarStorage getAvatarStorage() {
return this.getPlayer().getAvatars();
}
public Long2ObjectMap<GameItem> getItems() {
return store;
}
public Int2ObjectMap<InventoryTab> getInventoryTypes() {
return inventoryTypes;
}
public InventoryTab getInventoryTab(ItemMainType type) {
return getInventoryTypes().get(type.getVal());
}
public void createInventoryTab(ItemMainType type, InventoryTab tab) {
this.getInventoryTypes().put(type.getVal(), tab);
}
public int getNextItemInternalUid() {
return ++nextInternalUid;
}
/* Returns an item using its internal uid
* */
public GameItem getItemByUid(int uid) {
return this.getItems().get(uid);
}
public GameItem getMaterialByItemId(int id) {
return this.getInventoryTab(ItemMainType.Material).getItemById(id);
}
public GameItem getItemByParam(ItemParam param) {
if (param.getType() == ItemParamType.PILE) {
return this.getMaterialByItemId(param.getId());
} else if (param.getType() == ItemParamType.UNIQUE) {
return this.getItemByUid(param.getId());
}
return null;
}
public boolean addItem(int itemId) {
return addItem(itemId, 1);
}
public boolean addItem(int itemId, int count) {
ItemExcel excel = GameData.getItemExcelMap().get(itemId);
if (excel == null) {
return false;
}
GameItem item = new GameItem(excel, count);
return addItem(item);
}
public boolean addItem(GameItem item) {
GameItem result = putItem(item);
if (result != null) {
// TODO Send packet (update)
getPlayer().sendPacket(new PacketPlayerSyncScNotify(item));
return true;
}
return false;
}
private synchronized GameItem putItem(GameItem item) {
// Dont add items that dont have a valid item definition.
if (item.getExcel() == null) {
return null;
}
// Add item to inventory store
ItemMainType type = item.getExcel().getItemMainType();
InventoryTab tab = getInventoryTab(type);
// Add
switch (type) {
case Equipment:
case Relic:
if (tab.getSize() >= tab.getMaxCapacity()) {
return null;
}
// Duplicates cause problems
item.setCount(Math.max(item.getCount(), 1));
// Adds to inventory
this.putItem(item, tab);
// Set ownership and save to database
item.save();
return item;
case Virtual:
// Handle
this.addVirtualItem(item.getItemId(), item.getCount());
return item;
case AvatarCard:
// Add avatar
AvatarExcel avatarExcel = GameData.getAvatarExcelMap().get(item.getItemId());
if (avatarExcel != null && !getPlayer().getAvatars().hasAvatar(avatarExcel.getId())) {
getPlayer().addAvatar(new GameAvatar(avatarExcel));
}
return null;
case Material:
switch (item.getExcel().getItemSubType()) {
default:
if (tab == null) {
return null;
}
GameItem existingItem = tab.getItemById(item.getItemId());
if (existingItem == null) {
// Item type didnt exist before, we will add it to main inventory map if there is enough space
if (tab.getSize() >= tab.getMaxCapacity()) {
return null;
}
this.putItem(item, tab);
// Set ownership and save to db
item.save();
return item;
} else {
// Add count
existingItem.setCount(Math.min(existingItem.getCount() + item.getCount(), item.getExcel().getPileLimit()));
existingItem.save();
return existingItem;
}
}
default:
return null;
}
}
private synchronized void putItem(GameItem item, InventoryTab tab) {
// Set owner and internal uid first
item.setOwner(this.getPlayer());
// Add if tab exists
if (tab != null) {
// Put in item store
getItems().put(item.getInternalUid(), item);
// Add to tab
tab.onAddItem(item);
}
}
private void addVirtualItem(int itemId, int count) {
switch (itemId) {
case 1: // Stellar Jade
getPlayer().addHCoin(count);
break;
case 2: // Credit
getPlayer().addSCoin(count);
break;
case 3: // Oneiric Shard
getPlayer().addMCoin(count);
break;
case 11: // Trailblaze Power
getPlayer().addStamina(count);
break;
case 22: // Trailblaze EXP
getPlayer().addExp(count);
break;
}
}
public synchronized void removeItems(Collection<ItemParam> items) {
for (ItemParam param : items) {
GameItem item = this.getItemByParam(param);
if (item != null) {
this.removeItem(item, param.getCount());
}
}
}
public synchronized boolean removePileItem(int uid, int count) {
GameItem item = this.getMaterialByItemId(uid);
if (item == null) {
return false;
}
return removeItem(item, count);
}
public synchronized boolean removeUniqueItem(int uid, int count) {
GameItem item = this.getItemByUid(uid);
if (item == null) {
return false;
}
return removeItem(item, count);
}
public synchronized boolean removeItem(GameItem item, int count) {
// Sanity check
if (count <= 0 || item == null || item.getOwnerUid() != getPlayer().getUid()) {
return false;
}
if (item.getExcel() == null || item.getExcel().isEquippable()) {
item.setCount(0);
} else {
item.setCount(item.getCount() - count);
}
if (item.getCount() <= 0) {
// Remove from inventory tab too
InventoryTab tab = null;
if (item.getExcel() != null) {
tab = getInventoryTab(item.getExcel().getItemMainType());
}
// Remove from inventory if less than 0
deleteItem(item, tab);
// TODO Send packet (delete)
getPlayer().sendPacket(new PacketPlayerSyncScNotify(item));
} else {
// TODO Send packet (update)
getPlayer().sendPacket(new PacketPlayerSyncScNotify(item));
}
// Update in db
item.save();
// Returns true on success
return true;
}
private void deleteItem(GameItem item, InventoryTab tab) {
getItems().remove(item.getInternalUid());
if (tab != null) {
tab.onRemoveItem(item);
}
}
// Equips
public boolean equipItem(int avatarId, int equipId) {
GameAvatar avatar = getPlayer().getAvatarById(avatarId);
GameItem item = this.getItemByUid(equipId);
if (avatar != null && item != null) {
return avatar.equipItem(item);
}
return false;
}
public boolean unequipItem(int avatarId, int slot) {
GameAvatar avatar = getPlayer().getAvatars().getAvatarById(avatarId);
if (avatar != null) {
GameItem unequipped = avatar.unequipItem(slot);
if (unequipped != null) {
getPlayer().sendPacket(new PacketPlayerSyncScNotify(avatar, unequipped));
return true;
}
}
return false;
}
// Database
public void loadFromDatabase() {
Stream<GameItem> stream = LunarRail.getGameDatabase().getObjects(GameItem.class, "ownerUid", this.getPlayer().getUid());
stream.forEach(item -> {
// Should never happen
if (item.getId() == null) {
return;
}
// Load item excel data
ItemExcel excel = GameData.getItemExcelMap().get(item.getItemId());
if (excel == null) {
// Delete item if it has no excel data
item.setCount(0);
item.save();
return;
}
// Set ownerships
item.setExcel(excel);
// Put in inventory
InventoryTab tab = getInventoryTab(item.getExcel().getItemMainType());
putItem(item, tab);
// Equip to a character if possible
if (item.isEquipped()) {
GameAvatar avatar = getPlayer().getAvatarById(item.getEquipAvatar());
boolean hasEquipped = false;
if (avatar != null) {
hasEquipped = avatar.equipItem(item);
}
if (!hasEquipped) {
// Unset equipped flag on item since we couldnt find an avatar to equip it to
item.setEquipAvatar(0);
item.save();
}
}
});
}
}

View File

@@ -0,0 +1,13 @@
package emu.lunarcore.game.inventory;
public abstract class InventoryTab implements Iterable<GameItem> {
public abstract GameItem getItemById(int id);
public abstract void onAddItem(GameItem item);
public abstract void onRemoveItem(GameItem item);
public abstract int getSize();
public abstract int getMaxCapacity();
}

View File

@@ -0,0 +1,22 @@
package emu.lunarcore.game.inventory;
import lombok.Getter;
@Getter
public enum ItemMainType {
Unknown (0),
Virtual (1),
AvatarCard (2),
Equipment (3),
Relic (4),
Usable (5),
Material (6),
Mission (7),
Display (8);
private int val;
private ItemMainType(int value) {
this.val = value;
}
}

View File

@@ -0,0 +1,19 @@
package emu.lunarcore.game.inventory;
import lombok.Getter;
@Getter
public enum ItemRarity {
Unknown (0),
Normal (1),
NotNormal (2),
Rare (3),
VeryRare (4),
SuperRare (5);
private int val;
private ItemRarity(int value) {
this.val = value;
}
}

View File

@@ -0,0 +1,39 @@
package emu.lunarcore.game.inventory;
import dev.morphia.annotations.Entity;
import emu.lunarcore.data.excel.RelicSubAffixExcel;
import emu.lunarcore.proto.RelicAffixOuterClass.RelicAffix;
import emu.lunarcore.util.Utils;
import lombok.Getter;
@Getter
@Entity(useDiscriminator = false)
public class ItemSubAffix {
private int id; // Affix id
private int count;
private int step;
@Deprecated
public ItemSubAffix() {
// Morphia only!
}
public ItemSubAffix(RelicSubAffixExcel subAffix) {
this.id = subAffix.getAffixID();
this.count = 1;
this.step = Utils.randomRange(0, subAffix.getStepNum());
}
public void incrementCount() {
this.count += 1;
}
public RelicAffix toProto() {
var proto = RelicAffix.newInstance()
.setAffixId(this.id)
.setCnt(this.count)
.setStep(this.step);
return proto;
}
}

View File

@@ -0,0 +1,35 @@
package emu.lunarcore.game.inventory;
import lombok.Getter;
@Getter
public enum ItemSubType {
Unknown (0),
Virtual (101),
GameplayCounter (102),
AvatarCard (201),
Equipment (301),
Relic (401),
Gift (501),
Food (502),
ForceOpitonalGift (503),
Book (504),
HeadIcon (505),
MusicAlbum (506),
Formula (507),
ChatBubble (508),
PhoneTheme (510),
Material (601),
Eidolon (602),
MuseumExhibit (603),
MuseumStuff (604),
Mission (701),
RelicSetShowOnly (801),
RelicRarityShowOnly (802);
private int val;
private ItemSubType(int value) {
this.val = value;
}
}

View File

@@ -0,0 +1,46 @@
package emu.lunarcore.game.inventory;
import java.util.Iterator;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class MaterialInventoryTab extends InventoryTab {
private final Int2ObjectMap<GameItem> items;
private final int maxCapacity;
public MaterialInventoryTab(int maxCapacity) {
this.items = new Int2ObjectOpenHashMap<>();
this.maxCapacity = maxCapacity;
}
@Override
public GameItem getItemById(int id) {
return this.items.get(id);
}
@Override
public void onAddItem(GameItem item) {
this.items.put(item.getItemId(), item);
}
@Override
public void onRemoveItem(GameItem item) {
this.items.remove(item.getItemId());
}
@Override
public int getSize() {
return this.items.size();
}
@Override
public int getMaxCapacity() {
return this.maxCapacity;
}
@Override
public Iterator<GameItem> iterator() {
return items.values().iterator();
}
}

View File

@@ -0,0 +1,20 @@
package emu.lunarcore.game.inventory;
import lombok.Getter;
@Getter
public enum RelicType {
Unknow (0),
HEAD (1),
HAND (2),
BODY (3),
FOOT (4),
NECK (5),
OBJECT (6);
private int val;
private RelicType(int value) {
this.val = value;
}
}

View File

@@ -0,0 +1,13 @@
package emu.lunarcore.game.player;
public abstract class BasePlayerManager {
private transient Player player;
public BasePlayerManager(Player player) {
this.player = player;
}
public Player getPlayer() {
return player;
}
}

View File

@@ -0,0 +1,266 @@
package emu.lunarcore.game.player;
import java.util.List;
import dev.morphia.annotations.Entity;
import emu.lunarcore.GameConstants;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.server.packet.send.PacketSyncLineupNotify;
import lombok.Getter;
@Entity(useDiscriminator = false) @Getter
public class LineupManager {
private transient Player player;
private PlayerLineup[] lineups;
private int currentIndex;
private int currentLeader;
@Deprecated
public LineupManager() {
// Morphia only!
}
public LineupManager(Player player) {
this();
this.validate(player);
}
public PlayerLineup getLineup(int index) {
// Sanity
if (index < 0 || index >= this.getLineups().length) {
return null;
}
return this.lineups[index];
}
public PlayerLineup getCurrentLineup() {
return getLineup(this.currentIndex);
}
// Lineup functions
public boolean changeLeader(int slot) {
if (slot >= 0 && slot < this.getCurrentLineup().size()) {
this.currentLeader = slot;
return true;
}
return false;
}
public boolean joinLineup(int index, int slot, int avatarId) {
// Get lineup
PlayerLineup lineup = this.getLineup(index);
if (lineup == null) return false;
boolean isCurrentLineup = lineup == getCurrentLineup();
// Get avatar
GameAvatar avatar = getPlayer().getAvatarById(avatarId);
if (avatar == null) return false;
// Join lineup
if (slot >= 0 && slot < lineup.size()) {
// Replace avatar
lineup.getAvatars().set(slot, avatarId);
} else if (lineup.size() < GameConstants.MAX_AVATARS_IN_TEAM) {
// Add avatar
lineup.getAvatars().add(avatarId);
} else {
// No changes were made
return false;
}
// Save
this.getPlayer().save();
// Sync lineup with scene
if (isCurrentLineup) {
player.getScene().syncLineup();
}
// Sync lineup data
player.sendPacket(new PacketSyncLineupNotify(lineup));
return true;
}
public boolean quitLineup(int index, int avatarId) {
// Get lineup
PlayerLineup lineup = this.getLineup(index);
if (lineup == null) return false;
boolean isCurrentLineup = lineup == getCurrentLineup();
// Sanity check to make sure were not removing the last member of our lineup
if (isCurrentLineup && lineup.size() <= 1) {
return false;
}
//
int i = lineup.getAvatars().indexOf(avatarId);
if (i != -1) {
lineup.getAvatars().remove(i);
} else {
return false;
}
// Validate leader index
if (this.getCurrentLeader() >= lineup.size()) {
this.currentLeader = 0;
}
// Save
this.getPlayer().save();
// Sync lineup with scene
if (isCurrentLineup) {
player.getScene().syncLineup();
}
// Sync lineup data
player.sendPacket(new PacketSyncLineupNotify(lineup));
return true;
}
public boolean switchLineup(int index) {
// Sanity
if (index == this.getCurrentIndex()) {
return false;
}
// Get lineup
PlayerLineup lineup = this.getLineup(index);
// Make sure lineup exists and has size
if (lineup == null || lineup.size() == 0) {
return false;
}
// Set index
this.currentIndex = index;
this.currentLeader = 0;
// Save
this.getPlayer().save();
// Sync lineup data
player.getScene().syncLineup();
player.sendPacket(new PacketSyncLineupNotify(lineup));
return true;
}
public boolean replaceLineup(int index, List<Integer> lineupList) {
// Sanity - Make sure player cant remove all avatars from the current lineup
if (index == this.currentIndex && lineupList.size() == 0) {
return false;
}
// Get lineup
PlayerLineup lineup = this.getLineup(index);
if (lineup == null) return false;
// Clear
lineup.getAvatars().clear();
// Add
for (int avatarId : lineupList) {
GameAvatar avatar = getPlayer().getAvatarById(avatarId);
if (avatar != null) {
lineup.getAvatars().add(avatarId);
}
}
// Validate leader index
if (this.getCurrentLeader() >= lineup.size()) {
this.currentLeader = 0;
}
// Save
this.getPlayer().save();
// Sync lineup with scene
if (lineup == getCurrentLineup()) {
player.getScene().syncLineup();
}
// Sync lineup data
player.sendPacket(new PacketSyncLineupNotify(lineup));
return true;
}
public boolean swapLineup(int index, int src, int dest) {
// Sanity
if (src == dest) return false;
// Get lineup
PlayerLineup lineup = this.getLineup(index);
// Validate slots
if ((lineup == null) || (src < 0 && src >= lineup.size())) {
return false;
}
if (dest < 0 && dest >= lineup.size()) {
return false;
}
// Swap
int srcId = lineup.getAvatars().get(src);
int destId = lineup.getAvatars().get(dest);
lineup.getAvatars().set(src, destId);
lineup.getAvatars().set(dest, srcId);
// Save
this.getPlayer().save();
// Sync lineup data
player.sendPacket(new PacketSyncLineupNotify(lineup));
return true;
}
public boolean changeLineupName(int index, String name) {
// Get lineup
PlayerLineup lineup = this.getLineup(index);
if (lineup == null) return false;
// Change name
lineup.setName(name);
return true;
}
// Max sure all lineups exist in the array
public void validate(Player player) {
// Set player
this.player = player;
// Make sure lineups exist
if (this.getLineups() == null) {
this.lineups = new PlayerLineup[GameConstants.DEFAULT_TEAMS];
} else if (this.getLineups().length != GameConstants.DEFAULT_TEAMS) {
// TODO move lineups from old array to this new one
this.lineups = new PlayerLineup[GameConstants.DEFAULT_TEAMS];
}
// Create new lineups for any missing ones
for (int i = 0; i < this.lineups.length; i++) {
if (this.lineups[i] == null) {
this.lineups[i] = new PlayerLineup(i);
}
this.lineups[i].setOwnerAndIndex(getPlayer(), i);
}
// Set current index if out of bounds
if (this.currentIndex < 0) {
this.currentIndex = 0;
} else if (this.currentIndex >= this.lineups.length) {
this.currentIndex = this.lineups.length - 1;
}
}
}

View File

@@ -0,0 +1,310 @@
package emu.lunarcore.game.player;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.lunarcore.GameConstants;
import emu.lunarcore.LunarRail;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.config.AnchorInfo;
import emu.lunarcore.data.config.FloorInfo;
import emu.lunarcore.data.config.PropInfo;
import emu.lunarcore.data.excel.MapEntranceExcel;
import emu.lunarcore.game.account.Account;
import emu.lunarcore.game.avatar.AvatarStorage;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.gacha.PlayerGachaInfo;
import emu.lunarcore.game.inventory.Inventory;
import emu.lunarcore.game.scene.Scene;
import emu.lunarcore.proto.PlayerBasicInfoOuterClass.PlayerBasicInfo;
import emu.lunarcore.server.game.GameServer;
import emu.lunarcore.server.game.GameSession;
import emu.lunarcore.server.packet.BasePacket;
import emu.lunarcore.server.packet.SessionState;
import emu.lunarcore.server.packet.send.PacketEnterSceneByServerScNotify;
import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify;
import emu.lunarcore.server.packet.send.PacketRevcMsgScNotify;
import emu.lunarcore.util.Position;
import lombok.Getter;
@Entity(value = "players", useDiscriminator = false)
@Getter
public class Player {
private transient GameSession session;
@Id private int uid;
@Indexed private String accountUid;
private String name;
private String signature;
private int birthday;
private int level;
private int exp;
private int worldLevel;
private int stamina;
private int scoin; // Credits
private int hcoin; // Jade
private int mcoin; // Crystals
private transient Scene scene;
private Position pos;
private int planeId;
private int floorId;
private int entryId;
// Player managers
private transient final AvatarStorage avatars;
private transient final Inventory inventory;
// Database persistent data
private LineupManager lineupManager;
private PlayerGachaInfo gachaInfo;
@Deprecated // Morphia only
public Player() {
this.avatars = new AvatarStorage(this);
this.inventory = new Inventory(this);
}
// Called when player is created
public Player(GameSession session) {
this();
this.session = session;
this.accountUid = getAccount().getUid();
this.name = GameConstants.DEFAULT_NAME;
this.level = 1;
this.stamina = GameConstants.MAX_STAMINA;
this.pos = new Position(-36589, -5400, 70019);
this.planeId = 10101;
this.floorId = 10101001;
this.entryId = 1010101;
this.lineupManager = new LineupManager(this);
this.gachaInfo = new PlayerGachaInfo();
// Setup uid
this.initUid();
// Give us a starter character.
// TODO script tutorial
GameAvatar avatar = new GameAvatar(8001);
this.getAvatars().addAvatar(avatar);
this.getLineupManager().getCurrentLineup().getAvatars().add(8001);
}
public GameServer getServer() {
return session.getServer();
}
public Account getAccount() {
return session.getAccount();
}
public void setSession(GameSession session) {
if (this.session == null) {
this.session = session;
}
}
public boolean setNickname(String nickname) {
if (nickname != this.name && nickname.length() <= 32) {
this.name = nickname;
this.sendPacket(new PacketPlayerSyncScNotify(this));
this.save();
return true;
}
return false;
}
public int setBirthday(int birthday) {
if (this.birthday == 0) {
int month = birthday / 100;
int day = birthday % 100;
if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
this.birthday = birthday;
this.save();
return this.birthday;
}
}
return 0;
}
public boolean hasLoggedIn() {
return this.getSession() != null && this.getSession().getState() != SessionState.WAITING_FOR_TOKEN;
}
public boolean addAvatar(GameAvatar avatar) {
return getAvatars().addAvatar(avatar);
}
public GameAvatar getAvatarById(int avatarId) {
return getAvatars().getAvatarById(avatarId);
}
private void initUid() {
if (this.uid > 0) return;
int nextUid = session.getAccount().getReservedPlayerUid();
if (nextUid == GameConstants.SERVER_CONSOLE_UID) {
nextUid = 0;
}
while (nextUid == 0 || LunarRail.getGameDatabase().checkIfObjectExists(Player.class, nextUid)) {
nextUid = LunarRail.getGameDatabase().getNextObjectId(Player.class);
}
this.uid = nextUid;
}
public void addSCoin(int amount) {
this.scoin += amount;
this.sendPacket(new PacketPlayerSyncScNotify(this));
}
public void addHCoin(int amount) {
this.hcoin += amount;
this.sendPacket(new PacketPlayerSyncScNotify(this));
}
public void addMCoin(int amount) {
this.mcoin += amount;
this.sendPacket(new PacketPlayerSyncScNotify(this));
}
public void addStamina(int amount) {
this.stamina = Math.min(this.stamina + amount, GameConstants.MAX_STAMINA);
this.sendPacket(new PacketPlayerSyncScNotify(this));
}
public void addExp(int amount) {
// Required exp
int reqExp = GameData.getPlayerExpRequired(level + 1);
// Add exp
this.exp += amount;
while (this.exp >= reqExp && reqExp > 0) {
this.level += 1;
reqExp = GameData.getPlayerExpRequired(this.level + 1);
}
// Save
this.save();
// Send packet
this.sendPacket(new PacketPlayerSyncScNotify(this));
}
public int getDisplayExp() {
return this.exp - GameData.getPlayerExpRequired(this.level);
}
public void enterScene(int entryId, int teleportId) {
// Get map entrance excel
MapEntranceExcel entry = GameData.getMapEntranceExcelMap().get(entryId);
if (entry == null) return;
// Get floor info
FloorInfo floor = GameData.getFloorInfo(entry.getPlaneID(), entry.getFloorID());
if (floor == null) return;
// Get teleport anchor
int startGroup = entry.getStartGroupID();
int anchorId = entry.getStartAnchorID();
if (teleportId != 0 || anchorId == 0) {
PropInfo teleport = floor.getCachedTeleports().get(teleportId);
if (teleport != null) {
startGroup = teleport.getAnchorGroupID();
anchorId = teleport.getAnchorID();
}
}
AnchorInfo anchor = floor.getAnchorInfo(startGroup, anchorId);
if (anchor == null) return;
// Set position
this.getPos().set(
(int) (anchor.getPosX() * 1000f),
(int) (anchor.getPosY() * 1000f),
(int) (anchor.getPosZ() * 1000f)
);
this.planeId = entry.getPlaneID();
this.floorId = entry.getFloorID();
this.entryId = entry.getId();
// Save player
this.save();
// Move to scene
loadScene(entry.getPlaneID(), entry.getFloorID(), entry.getId());
}
private void loadScene(int planeId, int floorId, int entryId) {
// Sanity check
if (this.scene != null && this.scene.getPlaneId() == planeId) {
// Don't create a new scene if were already in the one we want to teleport to
} else {
this.scene = new Scene(this, planeId, floorId, entryId);
}
// TODO send packet
if (this.getSession().getState() != SessionState.WAITING_FOR_TOKEN) {
this.sendPacket(new PacketEnterSceneByServerScNotify(this));
}
}
public void dropMessage(String message) {
this.sendPacket(new PacketRevcMsgScNotify(this, message));
}
public void sendPacket(BasePacket packet) {
if (this.hasLoggedIn()) {
this.getSession().send(packet);
}
}
public void save() {
if (this.uid <= 0) {
LunarRail.getLogger().error("Tried to save a player object without a uid!");
return;
}
LunarRail.getGameDatabase().save(this);
}
public void onLogin() {
// Load avatars and inventory first
this.getAvatars().loadFromDatabase();
this.getInventory().loadFromDatabase();
this.getAvatars().recalcAvatarStats(); // Recalc stats after items have loaded for the avatars
// Load Etc
this.getLineupManager().validate(this);
// Enter scene (should happen after everything else loads)
this.loadScene(planeId, floorId, entryId);
}
public PlayerBasicInfo toProto() {
var proto = PlayerBasicInfo.newInstance()
.setNickname(this.getName())
.setLevel(this.getLevel())
.setExp(this.getDisplayExp())
.setWorldLevel(this.getWorldLevel())
.setScoin(this.getScoin())
.setHcoin(this.getHcoin())
.setMcoin(this.getMcoin())
.setStamina(this.getStamina());
return proto;
}
}

View File

@@ -0,0 +1,85 @@
package emu.lunarcore.game.player;
import java.util.ArrayList;
import java.util.List;
import dev.morphia.annotations.Entity;
import emu.lunarcore.GameConstants;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.proto.ExtraLineupTypeOuterClass.ExtraLineupType;
import emu.lunarcore.proto.LineupInfoOuterClass.LineupInfo;
import lombok.Getter;
@Entity(useDiscriminator = false) @Getter
public class PlayerLineup {
private transient Player owner;
private transient int index;
private String name;
private List<Integer> avatars;
@Deprecated // Morphia only!
public PlayerLineup() {
}
public PlayerLineup(int index) {
this.name = "Squad " + (index + 1);
this.avatars = new ArrayList<>(GameConstants.MAX_AVATARS_IN_TEAM);
}
protected void setOwnerAndIndex(Player player, int index) {
this.owner = player;
this.index = index;
}
public void setName(String name) {
this.name = name;
}
public int size() {
return avatars.size();
}
public boolean contains(GameAvatar avatar) {
return getAvatars().contains(avatar.getAvatarId());
}
public boolean addAvatar(GameAvatar avatar) {
if (contains(avatar)) {
return false;
}
getAvatars().add(avatar.getAvatarId());
return true;
}
public boolean removeAvatar(int slot) {
if (size() <= 1) {
return false;
}
getAvatars().remove(slot);
return true;
}
public LineupInfo toProto() {
var proto = LineupInfo.newInstance()
.setIndex(index)
.setName(this.getName())
.setLeaderSlot(this.getOwner().getLineupManager().getCurrentLeader())
.setTechniquePoints(5)
.setExtraLineupType(ExtraLineupType.LINEUP_NONE);
for (int slot = 0; slot < this.getAvatars().size(); slot++) {
GameAvatar avatar = owner.getAvatars().getAvatarById(getAvatars().get(slot));
if (avatar == null) continue;
proto.addAvatarList(avatar.toLineupAvatarProto(slot));
}
return proto;
}
}

View File

@@ -0,0 +1,50 @@
package emu.lunarcore.game.scene;
import emu.lunarcore.data.excel.NpcMonsterExcel;
import emu.lunarcore.data.excel.StageExcel;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.lunarcore.proto.SceneNpcMonsterInfoOuterClass.SceneNpcMonsterInfo;
import emu.lunarcore.proto.VectorOuterClass.Vector;
import emu.lunarcore.util.Position;
import lombok.Getter;
import lombok.Setter;
@Getter
public class EntityMonster implements GameEntity {
@Setter private int entityId;
@Setter private int worldLevel;
@Setter private int groupId;
@Setter private int instId;
@Setter private int eventId;
private NpcMonsterExcel excel;
private StageExcel stage;
private Position pos;
private Position rot;
public EntityMonster(NpcMonsterExcel excel, StageExcel stage, Position pos) {
this.excel = excel;
this.stage = stage;
this.pos = pos;
this.rot = new Position();
}
@Override
public SceneEntityInfo toSceneEntityProto() {
var monster = SceneNpcMonsterInfo.newInstance()
.setWorldLevel(this.getWorldLevel())
.setMonsterId(excel.getId())
.setEventId(this.getEventId());
var proto = SceneEntityInfo.newInstance()
.setEntityId(this.getEntityId())
.setGroupId(this.getGroupId())
.setInstId(this.getInstId())
.setMotion(MotionInfo.newInstance().setPos(getPos().toProto()).setRot(getRot().toProto()))
.setNpcMonster(monster);
return proto;
}
}

View File

@@ -0,0 +1,48 @@
package emu.lunarcore.game.scene;
import emu.lunarcore.data.excel.NpcMonsterExcel;
import emu.lunarcore.data.excel.StageExcel;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.lunarcore.proto.SceneNpcMonsterInfoOuterClass.SceneNpcMonsterInfo;
import emu.lunarcore.proto.ScenePropInfoOuterClass.ScenePropInfo;
import emu.lunarcore.proto.VectorOuterClass.Vector;
import emu.lunarcore.util.Position;
import lombok.Getter;
import lombok.Setter;
@Getter
public class EntityProp implements GameEntity {
@Setter private int entityId;
@Setter private int groupId;
@Setter private int instId;
@Setter private int propId;
@Setter private PropState state;
private Position pos;
private Position rot;
public EntityProp(int propId, Position pos) {
this.propId = propId;
this.pos = pos;
this.rot = new Position();
this.state = PropState.Closed;
}
@Override
public SceneEntityInfo toSceneEntityProto() {
var prop = ScenePropInfo.newInstance()
.setPropId(this.getPropId())
.setPropState(this.getState().getVal());
var proto = SceneEntityInfo.newInstance()
.setEntityId(this.getEntityId())
.setGroupId(this.getGroupId())
.setInstId(this.getInstId())
.setMotion(MotionInfo.newInstance().setPos(getPos().toProto()).setRot(getRot().toProto()))
.setProp(prop);
return proto;
}
}

View File

@@ -0,0 +1,15 @@
package emu.lunarcore.game.scene;
import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
public interface GameEntity {
public int getEntityId();
public void setEntityId(int id);
public default int getGroupId() {
return 0;
}
public SceneEntityInfo toSceneEntityProto();
}

View File

@@ -0,0 +1,48 @@
package emu.lunarcore.game.scene;
import lombok.Getter;
public enum PropState {
Closed (0),
Open (1),
Locked (2),
BridgeState1 (3),
BridgeState2 (4),
BridgeState3 (5),
BridgeState4 (6),
CheckPointDisable (7),
CheckPointEnable (8),
TriggerDisable (9),
TriggerEnable (10),
ChestLocked (11),
ChestClosed (12),
ChestUsed (13),
Elevator1 (14),
Elevator2 (15),
Elevator3 (16),
WaitActive (17),
EventClose (18),
EventOpen (19),
Hidden (20),
TeleportGate0 (21),
TeleportGate1 (22),
TeleportGate2 (23),
TeleportGate3 (24),
Destructed (25),
CustomState01 (101),
CustomState02 (102),
CustomState03 (103),
CustomState04 (104),
CustomState05 (105),
CustomState06 (106),
CustomState07 (107),
CustomState08 (108),
CustomState09 (109);
@Getter
private final int val;
private PropState(int val) {
this.val = val;
}
}

View File

@@ -0,0 +1,224 @@
package emu.lunarcore.game.scene;
import java.util.ArrayList;
import java.util.List;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.config.FloorInfo;
import emu.lunarcore.data.config.GroupInfo;
import emu.lunarcore.data.config.GroupInfo.GroupLoadSide;
import emu.lunarcore.data.config.MonsterInfo;
import emu.lunarcore.data.config.PropInfo;
import emu.lunarcore.data.excel.NpcMonsterExcel;
import emu.lunarcore.data.excel.StageExcel;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.player.PlayerLineup;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.proto.SceneEntityGroupInfoOuterClass.SceneEntityGroupInfo;
import emu.lunarcore.proto.SceneInfoOuterClass.SceneInfo;
import emu.lunarcore.server.packet.send.PacketSceneEntityUpdateScNotify;
import emu.lunarcore.server.packet.send.PacketSceneGroupRefreshScNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import lombok.Getter;
@Getter
public class Scene {
private final Player player;
private final int planeId;
private final int floorId;
private final int entryId;
private int lastEntityId = 0;
// Avatar entites
private IntSet avatarEntityIds;
private Int2ObjectMap<GameAvatar> avatars;
// Other entities TODO
private Int2ObjectMap<GameEntity> entities;
public Scene(Player player, int planeId, int floorId, int entryId) {
this.player = player;
this.planeId = planeId;
this.floorId = floorId;
this.entryId = entryId;
// Setup avatars
this.avatarEntityIds = new IntOpenHashSet();
this.avatars = new Int2ObjectOpenHashMap<>();
this.entities = new Int2ObjectOpenHashMap<>();
PlayerLineup lineup = getPlayer().getLineupManager().getCurrentLineup();
for (int avatarId : lineup.getAvatars()) {
GameAvatar avatar = getPlayer().getAvatarById(avatarId);
if (avatar == null) continue;
this.avatars.put(avatarId, avatar);
// Add entity id
avatar.setEntityId(this.getNextEntityId());
this.avatarEntityIds.add(avatar.getEntityId());
}
// Spawn monsters
FloorInfo floorInfo = GameData.getFloorInfo(this.planeId, this.floorId);
if (floorInfo == null) return;
for (GroupInfo group : floorInfo.getGroups().values()) {
// Skip non-server groups
if (group.getLoadSide() != GroupLoadSide.Server) {
continue;
}
// Add monsters
if (group.getMonsterList() != null || group.getMonsterList().size() > 0) {
for (MonsterInfo monsterInfo : group.getMonsterList()) {
// Get excels from game data
NpcMonsterExcel excel = GameData.getNpcMonsterExcelMap().get(monsterInfo.getNPCMonsterID());
StageExcel stage = GameData.getStageExcelMap().get(1);
if (excel == null || stage == null) continue;
// Create monster with excels
EntityMonster monster = new EntityMonster(excel, stage, monsterInfo.clonePos());
monster.getRot().setY((int) (monsterInfo.getRotY() * 1000f));
monster.setInstId(monsterInfo.getID());
monster.setEventId(monsterInfo.getEventID());
monster.setGroupId(group.getId());
// Add to monsters
this.addEntity(monster);
}
}
// Add props
if (group.getPropList() != null || group.getPropList().size() > 0) {
for (PropInfo propInfo : group.getPropList()) {
// Dont add deleted props?
/*
if (propInfo.isIsDelete()) {
continue;
}
*/
// Create prop from prop info
EntityProp prop = new EntityProp(propInfo.getPropID(), propInfo.clonePos());
//prop.setState(propInfo.getState());
prop.getRot().set(
(int) (propInfo.getRotX() * 1000f),
(int) (propInfo.getRotY() * 1000f),
(int) (propInfo.getRotZ() * 1000f)
);
prop.setInstId(propInfo.getID());
prop.setGroupId(group.getId());
// Add to monsters
this.addEntity(prop);
}
}
}
}
private int getNextEntityId() {
return ++lastEntityId;
}
public void syncLineup() {
// Get current lineup
PlayerLineup lineup = getPlayer().getLineupManager().getCurrentLineup();
// Setup new avatars list
var newAvatars = new Int2ObjectOpenHashMap<GameAvatar>();
for (int avatarId : lineup.getAvatars()) {
GameAvatar avatar = getPlayer().getAvatarById(avatarId);
if (avatar == null) continue;
newAvatars.put(avatarId, avatar);
}
// Clear entity id cache
this.avatarEntityIds.clear();
// Add/Remove
List<GameAvatar> toAdd = new ArrayList<>();
List<GameAvatar> toRemove = new ArrayList<>();
for (var avatar : newAvatars.values()) {
if (!this.avatars.containsKey(avatar.getAvatarId())) {
toAdd.add(avatar);
avatar.setEntityId(getNextEntityId());
}
// Add to entity id cache
this.avatarEntityIds.add(avatar.getEntityId());
}
for (var avatar : this.avatars.values()) {
if (!newAvatars.containsKey(avatar.getAvatarId())) {
toRemove.add(avatar);
avatar.setEntityId(0);
}
}
// Sync packet
getPlayer().sendPacket(new PacketSceneGroupRefreshScNotify(toAdd, toRemove));
}
public synchronized void addEntity(GameEntity entity) {
// Dont add if monster id already exists
if (entity.getEntityId() != 0) return;
// Set entity id and add monster to entity map
entity.setEntityId(this.getNextEntityId());
this.entities.put(entity.getEntityId(), entity);
}
public synchronized void removeEntity(int entityId) {
GameEntity entity = this.entities.remove(entityId);
// TODO send packet
}
public SceneInfo toProto() {
// Proto
var proto = SceneInfo.newInstance()
.setWorldId(301)
.setLCMMECNPOBA(2)
.setPlaneId(this.getPlaneId())
.setFloorId(this.getFloorId())
.setEntryId(this.getEntryId());
// Get current lineup
PlayerLineup lineup = getPlayer().getLineupManager().getCurrentLineup();
int leaderAvatarId = lineup.getAvatars().get(getPlayer().getLineupManager().getCurrentLeader());
// Scene group
var playerGroup = SceneEntityGroupInfo.newInstance();
for (var avatar : avatars.values()) {
playerGroup.addEntityList(avatar.toSceneEntityProto());
if (leaderAvatarId == avatar.getAvatarId()) {
proto.setLeaderEntityId(avatar.getEntityId());
}
}
proto.addEntityGroupList(playerGroup);
// Sort entities into groups
var groups = new Int2ObjectOpenHashMap<SceneEntityGroupInfo>();
for (var monster : entities.values()) {
var group = groups.computeIfAbsent(monster.getGroupId(), i -> SceneEntityGroupInfo.newInstance().setGroupId(i));
group.addEntityList(monster.toSceneEntityProto());
}
for (var group : groups.values()) {
proto.addEntityGroupList(group);
}
// Done
return proto;
}
}

View File

@@ -0,0 +1,46 @@
package emu.lunarcore.game.service;
import emu.lunarcore.commands.PlayerCommands;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.server.game.BaseGameService;
import emu.lunarcore.server.game.GameServer;
public class ChatService extends BaseGameService {
public ChatService(GameServer server) {
super(server);
}
public void sendPrivChat(Player player, int targetUid, String message) {
// Sanity checks
if (message == null || message.length() == 0) {
return;
}
// Check if command
if (message.charAt(0) == '!') {
PlayerCommands.handle(player, message);
return;
}
// Get target
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
return;
}
// Create chat packet TODO
}
public void sendPrivChat(Player player, int targetUid, int emote) {
// Get target
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
return;
}
// Create chat packet TODO
}
}

View File

@@ -0,0 +1,496 @@
package emu.lunarcore.game.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.common.ItemParam;
import emu.lunarcore.data.excel.AvatarPromotionExcel;
import emu.lunarcore.data.excel.AvatarRankExcel;
import emu.lunarcore.data.excel.AvatarSkillTreeExcel;
import emu.lunarcore.data.excel.EquipmentPromotionExcel;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.server.game.BaseGameService;
import emu.lunarcore.server.game.GameServer;
import emu.lunarcore.server.packet.BasePacket;
import emu.lunarcore.server.packet.CmdId;
import emu.lunarcore.server.packet.send.*;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
public class InventoryService extends BaseGameService {
public InventoryService(GameServer server) {
super(server);
}
// === Avatars ===
public void levelUpAvatar(Player player, int avatarId, Collection<ItemParam> items) {
// Get avatar
GameAvatar avatar = player.getAvatarById(avatarId);
if (avatar == null) return;
AvatarPromotionExcel promoteData = GameData.getAvatarPromotionExcel(avatarId, avatar.getPromotion());
if (promoteData == null) return;
// Exp gain
int expGain = 0;
// Verify items
for (ItemParam param : items) {
GameItem item = player.getInventory().getItemByParam(param);
if (item == null || item.getExcel().getAvatarExp() == 0 || item.getCount() < param.getCount()) return;
expGain += item.getExcel().getAvatarExp() * param.getCount();
}
// Verify credits
int cost = expGain / 10;
if (player.getScoin() < cost) {
player.sendPacket(new PacketAvatarExpUpScRsp());
return;
}
// Pay items
player.getInventory().removeItems(items);
player.addSCoin(-cost);
// Level up
int maxLevel = promoteData.getMaxLevel();
int level = avatar.getLevel();
int exp = avatar.getExp();
int reqExp = GameData.getAvatarExpRequired(avatar.getExcel().getExpGroup(), level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Set req exp
reqExp = GameData.getAvatarExpRequired(avatar.getExcel().getExpGroup(), level);
}
}
// Done
avatar.setLevel(level);
avatar.setExp(exp);
avatar.save();
player.save();
// TODO add back leftover exp
List<GameItem> returnItems = new ArrayList<>();
// Send packets
player.sendPacket(new PacketPlayerSyncScNotify(avatar));
player.sendPacket(new PacketAvatarExpUpScRsp(returnItems));
}
public void promoteAvatar(Player player, int avatarId) {
// Get avatar
GameAvatar avatar = player.getAvatarById(avatarId);
if (avatar == null || avatar.getPromotion() >= avatar.getExcel().getMaxPromotion()) return;
AvatarPromotionExcel promotion = GameData.getAvatarPromotionExcel(avatarId, avatar.getPromotion());
// Sanity check
if ((promotion == null) || avatar.getLevel() < promotion.getMaxLevel() || player.getLevel() < promotion.getPlayerLevelRequire() || player.getWorldLevel() < promotion.getWorldLevelRequire()) {
return;
}
// Verify items
for (ItemParam param : promotion.getPromotionCostList()) {
GameItem item = player.getInventory().getItemByParam(param);
if (item == null || item.getCount() < param.getCount()) return;
}
// Verify credits
if (player.getScoin() < promotion.getPromotionCostCoin()) {
player.sendPacket(new BasePacket(CmdId.PromoteAvatarScRsp));
return;
}
// Pay items
player.getInventory().removeItems(promotion.getPromotionCostList());
player.addSCoin(-promotion.getPromotionCostCoin());
// Promote
avatar.setPromotion(avatar.getPromotion() + 1);
avatar.save();
player.save();
// Send packets
player.sendPacket(new PacketPlayerSyncScNotify(avatar));
player.sendPacket(new BasePacket(CmdId.PromoteAvatarScRsp));
}
public void unlockSkillTreeAvatar(Player player, int pointId) {
// Hacky way to get avatar id
int avatarId = pointId / 1000;
// Get avatar + Skill Tree data
GameAvatar avatar = player.getAvatarById(avatarId);
if (avatar == null) return;
int nextLevel = avatar.getSkills().getOrDefault(pointId, 0) + 1;
AvatarSkillTreeExcel skillTree = GameData.getAvatarSkillTreeExcel(pointId, nextLevel);
if (skillTree == null || skillTree.getAvatarID() != avatarId) return;
// Verify items
for (ItemParam param : skillTree.getMaterialList()) {
GameItem item = player.getInventory().getItemByParam(param);
if (item == null || item.getCount() < param.getCount()) {
player.sendPacket(new PacketUnlockSkilltreeScRsp());
return;
}
}
// Verify credits
if (player.getScoin() < skillTree.getMaterialCostCoin()) {
player.sendPacket(new PacketUnlockSkilltreeScRsp());
return;
}
// Pay items
player.getInventory().removeItems(skillTree.getMaterialList());
player.addSCoin(-skillTree.getMaterialCostCoin());
// Add skill
avatar.getSkills().put(pointId, nextLevel);
avatar.save();
player.save();
// Send packets
player.sendPacket(new PacketPlayerSyncScNotify(avatar));
player.sendPacket(new PacketUnlockSkilltreeScRsp(avatarId, pointId, nextLevel));
}
public void rankUpAvatar(Player player, int avatarId) {
// Get avatar
GameAvatar avatar = player.getAvatarById(avatarId);
if (avatar == null || avatar.getRank() >= avatar.getExcel().getMaxRank()) return;
AvatarRankExcel rankData = GameData.getAvatarRankExcel(avatar.getExcel().getRankId(avatar.getRank()));
if (rankData == null) return;
// Verify items
for (ItemParam param : rankData.getUnlockCost()) {
GameItem item = player.getInventory().getItemByParam(param);
if (item == null || item.getCount() < param.getCount()) {
player.sendPacket(new BasePacket(CmdId.RankUpAvatarScRsp));
return;
}
}
// Pay items
player.getInventory().removeItems(rankData.getUnlockCost());
// Add rank
avatar.setRank(avatar.getRank() + 1);
avatar.save();
// Send packets
player.sendPacket(new PacketPlayerSyncScNotify(avatar));
player.sendPacket(new BasePacket(CmdId.RankUpAvatarScRsp));
}
// === Equipment ===
public void levelUpEquipment(Player player, int equipId, Collection<ItemParam> items) {
// Get equipment
GameItem equip = player.getInventory().getItemByUid(equipId);
if (equip == null || !equip.getExcel().isEquipment()) {
player.sendPacket(new PacketExpUpEquipmentScRsp());
return;
}
EquipmentPromotionExcel promoteData = GameData.getEquipmentPromotionExcel(equip.getItemId(), equip.getPromotion());
if (promoteData == null) return;
// Exp gain
int cost = 0;
int expGain = 0;
// Verify items
for (ItemParam param : items) {
GameItem item = player.getInventory().getItemByParam(param);
System.out.println(param.getId());
if (item == null || item.isLocked() || item.getCount() < param.getCount()) {
player.sendPacket(new PacketExpUpEquipmentScRsp());
return;
}
if (item.getExcel().getEquipmentExp() > 0) {
expGain += item.getExcel().getEquipmentExp() * param.getCount();
cost += item.getExcel().getEquipmentExpCost() * param.getCount();
}
}
// Verify credits
if (player.getScoin() < cost) {
player.sendPacket(new PacketExpUpEquipmentScRsp());
return;
}
// Pay items
player.getInventory().removeItems(items);
player.addSCoin(-cost);
// Level up
int maxLevel = promoteData.getMaxLevel();
int level = equip.getLevel();
int exp = equip.getExp();
int reqExp = GameData.getEquipmentExpRequired(equip.getExcel().getEquipmentExcel().getExpType(), level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Set req exp
reqExp = GameData.getEquipmentExpRequired(equip.getExcel().getEquipmentExcel().getExpType(), level);
}
}
// Done
equip.setLevel(level);
equip.setExp(exp);
equip.save();
player.save();
// TODO add back leftover exp
List<GameItem> returnItems = new ArrayList<>();
// Send packets
player.sendPacket(new PacketPlayerSyncScNotify(equip));
player.sendPacket(new PacketExpUpEquipmentScRsp(returnItems));
}
public void promoteEquipment(Player player, int equipId) {
// Get equipment
GameItem equip = player.getInventory().getItemByUid(equipId);
if (equip == null || !equip.getExcel().isEquipment() || equip.getPromotion() >= equip.getExcel().getEquipmentExcel().getMaxPromotion()) {
player.sendPacket(new BasePacket(CmdId.PromoteEquipmentScRsp));
return;
}
EquipmentPromotionExcel promotion = GameData.getEquipmentPromotionExcel(equip.getItemId(), equip.getPromotion());
// Sanity check
if ((promotion == null) || equip.getLevel() < promotion.getMaxLevel() || player.getLevel() < promotion.getPlayerLevelRequire() || player.getWorldLevel() < promotion.getWorldLevelRequire()) {
player.sendPacket(new BasePacket(CmdId.PromoteEquipmentScRsp));
return;
}
// Verify items
for (ItemParam param : promotion.getPromotionCostList()) {
GameItem item = player.getInventory().getItemByParam(param);
if (item == null || item.getCount() < param.getCount()) {
player.sendPacket(new BasePacket(CmdId.PromoteEquipmentScRsp));
return;
}
}
// Verify credits
if (player.getScoin() < promotion.getPromotionCostCoin()) {
player.sendPacket(new BasePacket(CmdId.PromoteEquipmentScRsp));
return;
}
// Pay items
player.getInventory().removeItems(promotion.getPromotionCostList());
player.addSCoin(-promotion.getPromotionCostCoin());
// Promote
equip.setPromotion(equip.getPromotion() + 1);
equip.save();
player.save();
// Send packets
player.sendPacket(new PacketPlayerSyncScNotify(equip));
player.sendPacket(new BasePacket(CmdId.PromoteEquipmentScRsp));
}
public void rankUpEquipment(Player player, int equipId, List<ItemParam> items) {
// Get avatar
GameItem equip = player.getInventory().getItemByUid(equipId);
if (equip == null || !equip.getExcel().isEquipment() || equip.getRank() >= equip.getExcel().getEquipmentExcel().getMaxRank()) {
player.sendPacket(new BasePacket(CmdId.RankUpEquipmentScRsp));
return;
}
// Verify items
for (ItemParam param : items) {
GameItem item = player.getInventory().getItemByParam(param);
if (item == null || !equip.getExcel().getEquipmentExcel().isRankUpItem(item) || item.getCount() < param.getCount()) {
player.sendPacket(new BasePacket(CmdId.RankUpEquipmentScRsp));
return;
}
}
// Pay items
player.getInventory().removeItems(items);
// Add rank
equip.setRank(Math.min(equip.getRank() + items.size(), equip.getExcel().getEquipmentExcel().getMaxRank()));
equip.save();
// Send packets
player.sendPacket(new PacketPlayerSyncScNotify(equip));
player.sendPacket(new BasePacket(CmdId.RankUpEquipmentScRsp));
}
// === Relic ===
public void levelUpRelic(Player player, int equipId, Collection<ItemParam> items) {
// Get relic
GameItem equip = player.getInventory().getItemByUid(equipId);
if (equip == null || !equip.getExcel().isRelic()) {
player.sendPacket(new PacketExpUpRelicScRsp());
return;
}
// Exp gain
int cost = 0;
int expGain = 0;
// Verify items
for (ItemParam param : items) {
GameItem item = player.getInventory().getItemByParam(param);
if (item == null || item.isLocked() || item.getCount() < param.getCount()) {
player.sendPacket(new PacketExpUpRelicScRsp());
return;
}
if (item.getExcel().getRelicExp() > 0) {
expGain += item.getExcel().getRelicExp() * param.getCount();
cost += item.getExcel().getRelicExpCost() * param.getCount();
}
if (item.getTotalExp() > 0) {
expGain += (int) Math.floor(item.getTotalExp() * 0.80D);
}
}
// Verify credits
if (player.getScoin() < cost) {
player.sendPacket(new PacketExpUpRelicScRsp());
return;
}
// Pay items
player.getInventory().removeItems(items);
player.addSCoin(-cost);
// Level up
int maxLevel = equip.getExcel().getRelicExcel().getMaxLevel();
int level = equip.getLevel();
int exp = equip.getExp();
int upgrades = 0;
int reqExp = GameData.getRelicExpRequired(equip.getExcel().getRelicExcel().getExpType(), level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Check upgrades
if (level % 3 == 0) {
upgrades++;
}
// Set req exp
reqExp = GameData.getRelicExpRequired(equip.getExcel().getRelicExcel().getExpType(), level);
}
}
// Add affixes
if (upgrades > 0) {
equip.addSubAffixes(upgrades);
}
// Done
equip.setLevel(level);
equip.setExp(exp);
equip.save();
player.save();
// TODO add back leftover exp
List<GameItem> returnItems = new ArrayList<>();
// Send packets
player.sendPacket(new PacketPlayerSyncScNotify(equip));
player.sendPacket(new PacketExpUpRelicScRsp(returnItems));
}
// === Etc ===
public void lockEquip(Player player, int equipId, boolean locked) {
GameItem equip = player.getInventory().getItemByUid(equipId);
if (equip == null || !equip.getExcel().isEquippable()) {
return;
}
equip.setLocked(locked);
equip.save();
// Send packet
player.sendPacket(new PacketPlayerSyncScNotify(equip));
}
public void sellItems(Player player, List<ItemParam> items) {
// Verify items
var returnItems = new Int2IntOpenHashMap();
for (ItemParam param : items) {
GameItem item = player.getInventory().getItemByParam(param);
if (item == null || item.isLocked() || item.getCount() < param.getCount()) {
player.sendPacket(new PacketSellItemScRsp(null));
return;
}
// Add return items
for (ItemParam ret : item.getExcel().getReturnItemIDList()) {
// Add to return items
returnItems.put(ret.getId(), returnItems.getOrDefault(ret.getId(), 0) + ret.getCount());
}
}
// Delete items
player.getInventory().removeItems(items);
// Add return items
for (var returnItem : returnItems.int2IntEntrySet()) {
player.getInventory().addItem(returnItem.getIntKey(), returnItem.getIntValue());
}
// Send packet
player.sendPacket(new PacketSellItemScRsp(returnItems));
}
}

View File

@@ -0,0 +1,13 @@
package emu.lunarcore.server.game;
public abstract class BaseGameService {
private final GameServer server;
public BaseGameService(GameServer server) {
this.server = server;
}
public GameServer getServer() {
return server;
}
}

View File

@@ -0,0 +1,98 @@
package emu.lunarcore.server.game;
import java.net.InetSocketAddress;
import emu.lunarcore.Config.GameServerConfig;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.battle.BattleService;
import emu.lunarcore.game.gacha.GachaService;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.service.ChatService;
import emu.lunarcore.game.service.InventoryService;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import kcp.highway.ChannelConfig;
import kcp.highway.KcpServer;
import lombok.Getter;
public class GameServer extends KcpServer {
private final GameServerConfig serverConfig;
private final RegionInfo info;
private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler;
private final Int2ObjectMap<Player> players;
// Managers
@Getter private final BattleService battleService;
@Getter private final InventoryService inventoryService;
@Getter private final GachaService gachaService;
@Getter private final ChatService chatService;
public GameServer(GameServerConfig serverConfig) {
// Game Server base
this.serverConfig = serverConfig;
this.info = new RegionInfo(this);
this.address = new InetSocketAddress(serverConfig.bindAddress, serverConfig.getPort());
this.packetHandler = new GameServerPacketHandler();
this.players = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
// Setup managers
this.battleService = new BattleService(this);
this.inventoryService = new InventoryService(this);
this.gachaService = new GachaService(this);
this.chatService = new ChatService(this);
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onShutdown));
}
public GameServerConfig getServerConfig() {
return this.serverConfig;
}
public GameServerPacketHandler getPacketHandler() {
return this.packetHandler;
}
public void registerPlayer(Player player) {
players.put(player.getUid(), player);
}
public void deregisterPlayer(int uid) {
players.remove(uid);
}
public Player getPlayerByUid(int uid) {
return players.get(uid);
}
public void start() {
// Setup config and init server
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true, 50, 2, true);
channelConfig.setMtu(1400);
channelConfig.setSndwnd(256);
channelConfig.setRcvwnd(256);
channelConfig.setTimeoutMillis(30 * 1000);//30s
channelConfig.setUseConvChannel(true);
channelConfig.setAckNoDelay(true);
this.init(new GameServerKcpListener(this), channelConfig, address);
// Setup region info
this.info.setUp(true);
this.info.save();
// Done
LunarRail.getLogger().info("Game Server started on " + address.getPort());
}
private void onShutdown() {
// Set region info
this.info.setUp(false);
this.info.save();
}
}

View File

@@ -0,0 +1,52 @@
package emu.lunarcore.server.game;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import kcp.highway.KcpListener;
import kcp.highway.Ukcp;
public class GameServerKcpListener implements KcpListener {
private final GameServer server;
private final Object2ObjectMap<Ukcp, GameSession> sessions;
public GameServerKcpListener(GameServer server) {
this.server = server;
this.sessions = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>());
}
public GameServer getServer() {
return this.server;
}
@Override
public void onConnected(Ukcp ukcp) {
GameSession session = new GameSession(server, ukcp);
sessions.put(ukcp, session);
session.onConnect();
}
@Override
public void handleClose(Ukcp ukcp) {
GameSession session = sessions.remove(ukcp);
if (session != null) {
session.onDisconnect();
}
}
@Override
public void handleReceive(ByteBuf packet, Ukcp ukcp) {
GameSession session = sessions.get(ukcp);
if (session != null) {
session.onMessage(packet);
}
}
@Override
public void handleException(Throwable err, Ukcp ukcp) {
// TODO
}
}

View File

@@ -0,0 +1,97 @@
package emu.lunarcore.server.game;
import java.util.Set;
import org.reflections.Reflections;
import emu.lunarcore.LunarRail;
import emu.lunarcore.server.packet.CmdId;
import emu.lunarcore.server.packet.Opcodes;
import emu.lunarcore.server.packet.PacketHandler;
import emu.lunarcore.server.packet.SessionState;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@SuppressWarnings("unchecked")
public class GameServerPacketHandler {
private final Int2ObjectMap<PacketHandler> handlers;
public GameServerPacketHandler() {
this.handlers = new Int2ObjectOpenHashMap<>();
this.registerHandlers();
}
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
public void registerHandlers() {
Reflections reflections = new Reflections(LunarRail.class.getPackageName());
Set<?> handlerClasses = reflections.getSubTypesOf(PacketHandler.class);
for (Object obj : handlerClasses) {
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
}
// Debug
LunarRail.getLogger().info("Game Server registered " + this.handlers.size() + " packet handlers");
}
public void handle(GameSession session, int opcode, byte[] header, byte[] data) {
PacketHandler handler = this.handlers.get(opcode);
if (handler != null) {
try {
// Make sure session is ready for packets
SessionState state = session.getState();
if (opcode == CmdId.PlayerHeartBeatCsReq) {
// Always continue if packet is ping request
} else if (opcode == CmdId.PlayerGetTokenCsReq) {
if (state != SessionState.WAITING_FOR_TOKEN) {
return;
}
} else if (opcode == CmdId.PlayerLoginCsReq) {
if (state != SessionState.WAITING_FOR_LOGIN) {
return;
}
}
/*
else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
if (state != SessionState.PICKING_CHARACTER) {
return;
}
}
*/
else {
if (state != SessionState.ACTIVE) {
return;
}
}
// Handle packet
handler.handle(session, header, data);
} catch (Exception ex) {
// TODO Remove this when no more needed
ex.printStackTrace();
}
return; // Packet successfully handled
}
// Log unhandled packets
//LunarRail.getLogger().info("Unhandled packet (" + opcode + "): " + CmdIdUtils.getOpcodeName(opcode));
}
}

View File

@@ -0,0 +1,170 @@
package emu.lunarcore.server.game;
import java.net.InetSocketAddress;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.account.Account;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.server.packet.BasePacket;
import emu.lunarcore.server.packet.CmdIdUtils;
import emu.lunarcore.server.packet.SessionState;
import emu.lunarcore.util.Utils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import kcp.highway.Ukcp;
import lombok.Getter;
public class GameSession {
@Getter private final GameServer server;
@Getter private InetSocketAddress address;
@Getter private Account account;
@Getter private Player player;
// Network
private Ukcp ukcp;
// Flags
@Getter private SessionState state = SessionState.WAITING_FOR_TOKEN;
private boolean useSecretKey;
private GameSession(GameServer server) {
this.server = server;
}
public GameSession(GameServer server, Ukcp ukcp) {
this(server);
this.ukcp = ukcp;
this.address = this.ukcp.user().getRemoteAddress();
}
public int getUid() {
return this.player.getUid();
}
public boolean useSecretKey() {
return useSecretKey;
}
public void setAccount(Account account) {
this.account = account;
}
public void setPlayer(Player player) {
this.player = player;
this.player.setSession(this);
this.getServer().registerPlayer(player);
}
public void setUseSecretKey(boolean key) {
this.useSecretKey = key;
}
public void setState(SessionState state) {
this.state = state;
}
public void onConnect() {
LunarRail.getLogger().info("Client connected from " + address.getHostString());
}
public void onDisconnect() {
LunarRail.getLogger().info("Client disconnected from " + address.getHostString());
this.state = SessionState.INACTIVE;
if (player != null) {
// Save first
player.save();
// Deregister
this.getServer().deregisterPlayer(player.getUid());
}
}
public void onMessage(ByteBuf packet) {
try {
// Decrypt and turn back into a packet
// Crypto.xor(packet.array(), useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
// Decode
while (packet.readableBytes() > 0) {
// Length
if (packet.readableBytes() < 16) {
return;
}
// Packet header sanity check
int constHeader = packet.readInt();
if (constHeader != BasePacket.HEADER_CONST) {
return; // Bad packet
}
// Data
int opcode = packet.readShort();
int headerLength = packet.readShort();
int dataLength = packet.readInt();
byte[] header = new byte[headerLength];
byte[] data = new byte[dataLength];
packet.readBytes(header);
packet.readBytes(data);
// Packet tail sanity check
int constTail = packet.readInt();
if (constTail != BasePacket.TAIL_CONST) {
return; // Bad packet
}
// Log packet
logPacket("RECV", opcode, data);
// Handle
getServer().getPacketHandler().handle(this, opcode, header, data);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// packet.release();
}
}
public void send(BasePacket packet) {
// Test
if (packet.getOpcode() <= 0) {
LunarRail.getLogger().warn("Tried to send packet with missing cmd id!");
return;
}
// Send
this.send(packet.build());
// Log
logPacket("SEND", packet.getOpcode(), packet.getData());
}
/**
* Sends a empty packet with the specified cmd id.
* @param cmdId
*/
public void send(int cmdId) {
// TODO optimize to send bytes with cmdId instead of creating a new base packet object
this.send(new BasePacket(cmdId));
}
private void send(byte[] bytes) {
if (this.ukcp != null) {
ByteBuf buf = Unpooled.wrappedBuffer(bytes);
this.ukcp.write(buf);
buf.release();
}
}
public void logPacket(String sendOrRecv, int opcode, byte[] payload) {
LunarRail.getLogger().info(sendOrRecv + ": " + CmdIdUtils.getOpcodeName(opcode) + " (" + opcode + ")");
System.out.println(Utils.bytesToHex(payload));
}
public void close() {
this.ukcp.close();
}
}

View File

@@ -0,0 +1,38 @@
package emu.lunarcore.server.game;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.lunarcore.LunarRail;
import lombok.Getter;
import lombok.Setter;
@Getter
@Entity(value = "regions", useDiscriminator = false)
public class RegionInfo {
@Id private String id;
private String name;
private String desc;
private String gateAddress;
private String gameAddress;
@Setter private boolean up;
@Deprecated
public RegionInfo() {
// Morphia only
}
public RegionInfo(GameServer server) {
this.id = server.getServerConfig().getId();
this.name = server.getServerConfig().getName();
this.desc = server.getServerConfig().getDescription();
this.gateAddress = LunarRail.getHttpServer().getServerConfig().getDisplayAddress();
this.gameAddress = server.getServerConfig().getDisplayAddress();
this.up = true;
}
public void save() {
LunarRail.getAccountDatabase().save(this);
}
}

View File

@@ -0,0 +1,153 @@
package emu.lunarcore.server.http;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import emu.lunarcore.Config.ServerConfig;
import emu.lunarcore.LunarRail;
import emu.lunarcore.LunarRail.ServerType;
import emu.lunarcore.server.http.handlers.*;
import io.javalin.Javalin;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
public class HttpServer {
private final Javalin app;
private final ServerType type;
private boolean started;
public HttpServer(ServerType type) {
this.type = type;
this.app = Javalin.create();
this.addRoutes();
}
public Javalin getApp() {
return this.app;
}
public ServerType getType() {
return type;
}
public ServerConfig getServerConfig() {
return LunarRail.getConfig().getHttpServer();
}
private HttpConnectionFactory getHttpFactory() {
HttpConfiguration httpsConfig = new HttpConfiguration();
SecureRequestCustomizer src = new SecureRequestCustomizer();
src.setSniHostCheck(false);
httpsConfig.addCustomizer(src);
return new HttpConnectionFactory(httpsConfig);
}
private SslContextFactory.Server getSSLContextFactory() {
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(LunarRail.getConfig().getKeystore().getPath());
sslContextFactory.setKeyStorePassword(LunarRail.getConfig().getKeystore().getPassword());
sslContextFactory.setSniRequired(false);
sslContextFactory.setRenegotiationAllowed(false);
return sslContextFactory;
}
public void start() {
if (this.started) return;
this.started = true;
// Http server
if (getServerConfig().isUseSSL()) {
ServerConnector sslConnector = new ServerConnector(getApp().jettyServer().server(), getSSLContextFactory(), getHttpFactory());
sslConnector.setHost(getServerConfig().getBindAddress());
sslConnector.setPort(getServerConfig().getPort());
getApp().jettyServer().server().addConnector(sslConnector);
getApp().start();
} else {
getApp().start(getServerConfig().getBindAddress(), getServerConfig().getPort());
}
// Done
LunarRail.getLogger().info("Http Server started on " + getServerConfig().getPort());
}
private void addRoutes() {
// Add routes based on what type of server this is
if (this.getType().runDispatch()) {
this.addDispatchRoutes();
this.addLogServerRoutes();
}
if (this.getType().runGame()) {
this.addGateServerRoutes();
}
// Fallback handler
getApp().error(404, this::notFoundHandler);
}
private void addDispatchRoutes() {
// Get region info
getApp().get("/query_dispatch", new QueryDispatchHandler());
// Captcha -> api-account-os.hoyoverse.com
getApp().post("/account/risky/api/check", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"none\",\"action\":\"ACTION_NONE\",\"geetest\":null}}"));
// === AUTHENTICATION === hkrpg-sdk-os-static.hoyoverse.com
// Username & Password login (from client). Returns a session key to the client.
getApp().post("/hkrpg_global/mdk/shield/api/login", new UsernameLoginHandler());
// Cached session key verify (from registry). Returns a session key to the client.
getApp().post("/hkrpg_global/mdk/shield/api/verify", new TokenLoginHandler());
// Exchange session key for login token (combo token)
getApp().post("/hkrpg_global/combo/granter/login/v2/login", new ComboTokenGranterHandler());
// Config
getApp().get("/hkrpg_global/combo/granter/api/getConfig", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"\",\"push_alias_type\":0,\"disable_ysdk_guard\":true,\"enable_announce_pic_popup\":false,\"app_name\":\"<EFBFBD>??RPG\",\"qr_enabled_apps\":{\"bbs\":false,\"cloud\":false},\"qr_app_icons\":{\"app\":\"\",\"bbs\":\"\",\"cloud\":\"\"},\"qr_cloud_display_name\":\"\",\"enable_user_center\":true,\"functional_switch_configs\":{}}}"));
getApp().get("/hkrpg_global/mdk/shield/api/loadConfig", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":24,\"game_key\":\"hkrpg_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"<EFBFBD>??RPG\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\",\"gl\",\"ap\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":2592000},\"ap\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":2592000},\"gl\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}},\"initialize_firebase\":false,\"bbs_auth_login\":false,\"bbs_auth_login_ignore\":[],\"fetch_instance_id\":false,\"enable_flash_login\":false}}"));
// === EXTRA ===
// hkrpg-sdk-os.hoyoverse.com
getApp().post("/hkrpg_global/combo/granter/api/compareProtocolVersion", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":false,\"protocol\":null}}"));
getApp().get("/hkrpg_global/mdk/agreement/api/getAgreementInfos", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}"));
// sdk-os-static.hoyoverse.com
getApp().get("/combo/box/api/config/sdk/combo", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"kibana_pc_config\":\"{ \\\"enable\\\": 0, \\\"level\\\": \\\"Info\\\",\\\"modules\\\": [\\\"download\\\"] }\\n\",\"network_report_config\":\"{ \\\"enable\\\": 0, \\\"status_codes\\\": [206], \\\"url_paths\\\": [\\\"dataUpload\\\", \\\"red_dot\\\"] }\\n\",\"list_price_tierv2_enable\":\"false\\n\",\"pay_payco_centered_host\":\"bill.payco.com\",\"telemetry_config\":\"{\\n \\\"dataupload_enable\\\": 0,\\n}\",\"enable_web_dpi\":\"true\"}}}"));
getApp().get("/combo/box/api/config/sw/precache", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"url\":\"\",\"enable\":\"false\"}}}"));
// sg-public-data-api.hoyoverse.com
getApp().get("/device-fp/api/getFp", new FingerprintHandler());
getApp().get("/device-fp/api/getExtList", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"code\":200,\"msg\":\"ok\",\"ext_list\":[],\"pkg_list\":[],\"pkg_str\":\"/vK5WTh5SS3SAj8Zm0qPWg==\"}}"));
// abtest-api-data-sg.hoyoverse.com
getApp().post("/data_abtest_api/config/experiment/list", new HttpJsonResponse("{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6125_197\",\"version\":\"1\",\"configs\":{\"cardType\":\"direct\"}}]}"));
}
private void addLogServerRoutes() {
// hkrpg-log-upload-os.hoyoverse.com
getApp().post("/sdk/dataUpload", new HttpJsonResponse("{\"code\":0}"));
// log-upload-os.hoyoverse.com
getApp().post("/crashdump/dataUpload", new HttpJsonResponse("{\"code\":0}"));
getApp().post("/apm/dataUpload", new HttpJsonResponse("{\"code\":0}"));
// minor-api-os.hoyoverse.com
getApp().post("/common/h5log/log/batch", new HttpJsonResponse("{\"retcode\":0,\"message\":\"success\",\"data\":null}"));
}
private void addGateServerRoutes() {
// Gateway info
getApp().get("/query_gateway", new QueryGatewayHandler());
}
private void notFoundHandler(Context ctx) {
ctx.status(404);
ctx.contentType(ContentType.TEXT_PLAIN);
ctx.result("not found");
}
}

View File

@@ -0,0 +1,63 @@
package emu.lunarcore.server.http.handlers;
import org.jetbrains.annotations.NotNull;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.account.Account;
import emu.lunarcore.server.http.objects.ComboTokenReqJson;
import emu.lunarcore.server.http.objects.ComboTokenReqJson.LoginTokenData;
import emu.lunarcore.server.http.objects.ComboTokenResJson;
import emu.lunarcore.server.http.objects.ComboTokenResJson.LoginData;
import emu.lunarcore.util.JsonUtils;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import io.javalin.http.Handler;
public class ComboTokenGranterHandler implements Handler {
public ComboTokenGranterHandler() {
// TODO Auto-generated constructor stub
}
@Override
public void handle(@NotNull Context ctx) throws Exception {
// Setup response
ComboTokenResJson res = new ComboTokenResJson();
// Parse request
ComboTokenReqJson req = JsonUtils.decode(ctx.body(), ComboTokenReqJson.class);
// Validate
if (req == null || req.data == null) {
res.retcode = -202;
res.message = "Error logging in";
return;
}
// Get login data
LoginTokenData data = JsonUtils.decode(req.data, LoginTokenData.class);
// Validate 2
if (data == null) {
res.retcode = -202;
res.message = "Invalid login data";
return;
}
// Login
Account account = LunarRail.getAccountDatabase().getObjectByField(Account.class, "_id", data.uid);
if (account == null || !account.getDispatchToken().equals(data.token)) {
res.retcode = -201;
res.message = "Game account cache information error";
} else {
res.message = "OK";
res.data = new LoginData(account.getUid(), account.generateComboToken());
}
// Result
ctx.contentType(ContentType.APPLICATION_JSON);
ctx.result(JsonUtils.encode(res));
}
}

View File

@@ -0,0 +1,34 @@
package emu.lunarcore.server.http.handlers;
import org.jetbrains.annotations.NotNull;
import emu.lunarcore.server.http.objects.FingerprintReqJson;
import emu.lunarcore.server.http.objects.FingerprintResJson;
import emu.lunarcore.server.http.objects.FingerprintResJson.FingerprintDataJson;
import emu.lunarcore.util.JsonUtils;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import io.javalin.http.Handler;
public class FingerprintHandler implements Handler {
@Override
public void handle(@NotNull Context ctx) throws Exception {
FingerprintResJson res = new FingerprintResJson();
FingerprintReqJson req = JsonUtils.decode(ctx.body(), FingerprintReqJson.class);
if (req == null) {
res.retcode = -202;
res.message = "Error";
}
res.message = "OK";
res.data = new FingerprintDataJson(req.device_fp);
// Result
ctx.contentType(ContentType.APPLICATION_JSON);
ctx.result(JsonUtils.encode(res));
}
}

View File

@@ -0,0 +1,22 @@
package emu.lunarcore.server.http.handlers;
import org.jetbrains.annotations.NotNull;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import io.javalin.http.Handler;
public class HttpJsonResponse implements Handler {
private final String json;
public HttpJsonResponse(String jsonString) {
this.json = jsonString;
}
@Override
public void handle(@NotNull Context ctx) throws Exception {
ctx.status(200);
ctx.contentType(ContentType.APPLICATION_JSON);
ctx.result(json);
}
}

View File

@@ -0,0 +1,38 @@
package emu.lunarcore.server.http.handlers;
import org.jetbrains.annotations.NotNull;
import emu.lunarcore.LunarRail;
import emu.lunarcore.proto.DispatchRegionDataOuterClass.DispatchRegionData;
import emu.lunarcore.proto.RegionEntryOuterClass.RegionEntry;
import emu.lunarcore.util.Utils;
import io.javalin.http.Context;
import io.javalin.http.Handler;
public class QueryDispatchHandler implements Handler {
public QueryDispatchHandler() {
}
@Override
public void handle(@NotNull Context ctx) throws Exception {
// Get regions TODO get regions from database
RegionEntry region = RegionEntry.newInstance()
.setName(LunarRail.getConfig().getGameServer().getId())
.setDispatchUrl(LunarRail.getConfig().getHttpServer().getDisplayAddress() + "/query_gateway")
.setEnvType("2")
.setDisplayName(LunarRail.getConfig().getGameServer().getName());
// Build region list
DispatchRegionData regions = DispatchRegionData.newInstance();
regions.addRegionList(region);
// Log
LunarRail.getLogger().info("Client request: query_dispatch");
// Encode to base64 and send to client
ctx.result(Utils.base64Encode(regions.toByteArray()));
}
}

View File

@@ -0,0 +1,53 @@
package emu.lunarcore.server.http.handlers;
import org.jetbrains.annotations.NotNull;
import emu.lunarcore.GameConstants;
import emu.lunarcore.LunarRail;
import emu.lunarcore.proto.GateserverOuterClass.Gateserver;
import emu.lunarcore.util.Utils;
import io.javalin.http.Context;
import io.javalin.http.Handler;
public class QueryGatewayHandler implements Handler {
public QueryGatewayHandler() {
}
@Override
public void handle(@NotNull Context ctx) throws Exception {
// Build gateserver proto
Gateserver gateserver = Gateserver.newInstance()
.setRegionName(LunarRail.getConfig().getGameServer().getId())
.setIp(LunarRail.getConfig().getGameServer().getPublicAddress())
.setPort(LunarRail.getConfig().getGameServer().getPort())
.setUnk1(true)
.setUnk2(true)
.setUnk3(true)
.setMdkResVersion(GameConstants.MDK_VERSION);
// Set streaming data urls
var data = LunarRail.getConfig().getDownloadData();
if (data.assetBundleUrl != null) {
gateserver.setAssetBundleUrl(data.assetBundleUrl);
}
if (data.exResourceUrl != null) {
gateserver.setAssetBundleUrl(data.exResourceUrl);
}
if (data.luaUrl != null) {
gateserver.setAssetBundleUrl(data.luaUrl);
}
if (data.ifixUrl != null) {
gateserver.setAssetBundleUrl(data.ifixUrl);
}
// Log
LunarRail.getLogger().info("Client request: query_gateway");
// Encode to base64 and send to client
ctx.result(Utils.base64Encode(gateserver.toByteArray()));
}
}

View File

@@ -0,0 +1,52 @@
package emu.lunarcore.server.http.handlers;
import org.jetbrains.annotations.NotNull;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.account.Account;
import emu.lunarcore.server.http.objects.LoginResJson;
import emu.lunarcore.server.http.objects.LoginResJson.VerifyData;
import emu.lunarcore.server.http.objects.LoginTokenReqJson;
import emu.lunarcore.util.JsonUtils;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import io.javalin.http.Handler;
public class TokenLoginHandler implements Handler {
public TokenLoginHandler() {
}
@Override
public void handle(@NotNull Context ctx) throws Exception {
// Setup response
LoginResJson res = new LoginResJson();
// Parse request
LoginTokenReqJson req = JsonUtils.decode(ctx.body(), LoginTokenReqJson.class);
// Validate
if (req == null) {
res.retcode = -202;
res.message = "Error logging in";
return;
}
// Login
Account account = LunarRail.getAccountDatabase().getObjectByField(Account.class, "_id", req.uid);
if (account == null || !account.getDispatchToken().equals(req.token)) {
res.retcode = -201;
res.message = "Game account cache information error";
} else {
res.message = "OK";
res.data = new VerifyData(account.getUid(), account.getEmail(), req.token);
}
// Result
ctx.contentType(ContentType.APPLICATION_JSON);
ctx.result(JsonUtils.encode(res));
}
}

View File

@@ -0,0 +1,52 @@
package emu.lunarcore.server.http.handlers;
import org.jetbrains.annotations.NotNull;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.account.Account;
import emu.lunarcore.server.http.objects.LoginAccountReqJson;
import emu.lunarcore.server.http.objects.LoginResJson;
import emu.lunarcore.server.http.objects.LoginResJson.VerifyData;
import emu.lunarcore.util.JsonUtils;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import io.javalin.http.Handler;
public class UsernameLoginHandler implements Handler {
public UsernameLoginHandler() {
}
@Override
public void handle(@NotNull Context ctx) throws Exception {
// Setup response
LoginResJson res = new LoginResJson();
// Parse request
LoginAccountReqJson req = JsonUtils.decode(ctx.body(), LoginAccountReqJson.class);
// Validate
if (req == null) {
res.retcode = -202;
res.message = "Error logging in";
return;
}
// Login
Account account = LunarRail.getAccountDatabase().getObjectByField(Account.class, "username", req.account);
if (account == null) {
res.retcode = -201;
res.message = "Username not found.";
} else {
res.message = "OK";
res.data = new VerifyData(account.getUid(), account.getEmail(), account.generateDispatchToken());
}
// Send result
ctx.contentType(ContentType.APPLICATION_JSON);
ctx.result(JsonUtils.encode(res));
}
}

View File

@@ -0,0 +1,15 @@
package emu.lunarcore.server.http.objects;
public class ComboTokenReqJson {
public int app_id;
public int channel_id;
public String data;
public String device;
public String sign;
public static class LoginTokenData {
public String uid;
public String token;
public boolean guest;
}
}

View File

@@ -0,0 +1,22 @@
package emu.lunarcore.server.http.objects;
public class ComboTokenResJson {
public String message;
public int retcode;
public LoginData data = null;
public static class LoginData {
public int account_type = 1;
public boolean heartbeat;
public String combo_id;
public String combo_token;
public String open_id;
public String data = "{\"guest\":false}";
public String fatigue_remind = null; // ?
public LoginData(String openId, String comboToken) {
this.open_id = openId;
this.combo_token = comboToken;
}
}
}

View File

@@ -0,0 +1,5 @@
package emu.lunarcore.server.http.objects;
public class FingerprintReqJson {
public String device_fp;
}

View File

@@ -0,0 +1,19 @@
package emu.lunarcore.server.http.objects;
public class FingerprintResJson {
public String message;
public int retcode;
public FingerprintDataJson data;
public static class FingerprintDataJson {
public String device_fp;
public String message;
public int code;
public FingerprintDataJson(String fp) {
this.code = 200;
this.message = "OK";
this.device_fp = fp;
}
}
}

View File

@@ -0,0 +1,7 @@
package emu.lunarcore.server.http.objects;
public class LoginAccountReqJson {
public String account;
public String password;
public boolean is_crypto;
}

View File

@@ -0,0 +1,44 @@
package emu.lunarcore.server.http.objects;
public class LoginResJson {
public String message;
public int retcode;
public VerifyData data;
public static class VerifyData {
public VerifyAccountData account = new VerifyAccountData();
public boolean device_grant_required = false;
public String realname_operation = "NONE";
public boolean realperson_required = false;
public boolean safe_mobile_required = false;
public VerifyData(String accountUid, String email, String token) {
this.account.uid = accountUid;
this.account.email = email;
this.account.token = token;
}
}
public static class VerifyAccountData {
public String uid;
public String name = "";
public String email = "";
public String mobile = "";
public String is_email_verify = "0";
public String realname = "";
public String identity_card = "";
public String token;
public String safe_mobile = "";
public String facebook_name = "";
public String twitter_name = "";
public String game_center_name = "";
public String google_name = "";
public String apple_name = "";
public String sony_name = "";
public String tap_name = "";
public String country = "US";
public String reactivate_ticket = "";
public String area_code = "**";
public String device_grant_ticket = "";
}
}

View File

@@ -0,0 +1,6 @@
package emu.lunarcore.server.http.objects;
public class LoginTokenReqJson {
public String uid;
public String token;
}

View File

@@ -0,0 +1,91 @@
package emu.lunarcore.server.packet;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import us.hebi.quickbuf.ProtoMessage;
public class BasePacket {
public static final int HEADER_CONST = 0x9d74c714;
public static final int TAIL_CONST = 0xd7a152c8;
private int opcode;
private byte[] data;
// Encryption
private boolean useDispatchKey;
public boolean shouldEncrypt = true;
public BasePacket(int opcode) {
this.opcode = opcode;
}
public int getOpcode() {
return opcode;
}
public void setOpcode(int opcode) {
this.opcode = opcode;
}
public boolean useDispatchKey() {
return useDispatchKey;
}
public void setUseDispatchKey(boolean useDispatchKey) {
this.useDispatchKey = useDispatchKey;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public void setData(ProtoMessage<?> proto) {
this.data = proto.toByteArray();
}
public byte[] build() {
if (getData() == null) {
this.data = new byte[0];
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(4 + 2 + 4 + getData().length + 4);
this.writeUint32(baos, HEADER_CONST);
this.writeUint16(baos, opcode);
this.writeUint16(baos, 0); // Empty header
this.writeUint32(baos, data.length);
this.writeBytes(baos, data);
this.writeUint32(baos, TAIL_CONST);
byte[] packet = baos.toByteArray();
return packet;
}
public void writeUint16(ByteArrayOutputStream baos, int i) {
// Unsigned short
baos.write((byte) ((i >>> 8) & 0xFF));
baos.write((byte) (i & 0xFF));
}
public void writeUint32(ByteArrayOutputStream baos, int i) {
// Unsigned int (long)
baos.write((byte) ((i >>> 24) & 0xFF));
baos.write((byte) ((i >>> 16) & 0xFF));
baos.write((byte) ((i >>> 8) & 0xFF));
baos.write((byte) (i & 0xFF));
}
public void writeBytes(ByteArrayOutputStream baos, byte[] bytes) {
try {
baos.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,961 @@
package emu.lunarcore.server.packet;
public class CmdId {
// Empty
public static final int NONE = 0;
// Opcodes
public static final int BuyBpLevelScRsp = 3014;
public static final int TakeBpRewardCsReq = 3029;
public static final int BuyBpLevelCsReq = 3047;
public static final int BattlePassInfoNotify = 3007;
public static final int TakeAllRewardCsReq = 3092;
public static final int TakeAllRewardScRsp = 3087;
public static final int TakeBpRewardScRsp = 3083;
public static final int ReplaceLineupCsReq = 716;
public static final int QuitLineupCsReq = 714;
public static final int GetCurLineupDataScRsp = 729;
public static final int JoinLineupCsReq = 783;
public static final int SwapLineupScRsp = 790;
public static final int GetAllLineupDataCsReq = 793;
public static final int GetStageLineupCsReq = 707;
public static final int QuitLineupScRsp = 792;
public static final int SwapLineupCsReq = 787;
public static final int SyncLineupNotify = 732;
public static final int SetLineupNameCsReq = 756;
public static final int GetAllLineupDataScRsp = 702;
public static final int SetLineupNameScRsp = 740;
public static final int GetLineupAvatarDataCsReq = 706;
public static final int GetStageLineupScRsp = 725;
public static final int VirtualLineupDestroyNotify = 796;
public static final int SwitchLineupIndexCsReq = 705;
public static final int ReplaceLineupScRsp = 718;
public static final int ChangeLineupLeaderCsReq = 736;
public static final int GetLineupAvatarDataScRsp = 708;
public static final int JoinLineupScRsp = 747;
public static final int SwitchLineupIndexScRsp = 780;
public static final int ChangeLineupLeaderScRsp = 742;
public static final int GetCurLineupDataCsReq = 774;
public static final int EnterTrialActivityStageScRsp = 2668;
public static final int GetLoginActivityCsReq = 2607;
public static final int TakeTrialActivityRewardScRsp = 2663;
public static final int TakeMonsterResearchActivityRewardScRsp = 2696;
public static final int TakeLoginActivityRewardScRsp = 2629;
public static final int RogueChallengeActivityBuffChooseScRsp = 2670;
public static final int CurTrialActivityScNotify = 2609;
public static final int RogueChallengeRefreshAssistListScRsp = 2662;
public static final int GetMonsterResearchActivityDataScRsp = 2656;
public static final int EnterActivityBattleStageCsReq = 2684;
public static final int GetActivityScheduleConfigScRsp = 2647;
public static final int RogueChallengeBattleResultScNotify = 2698;
public static final int EnterTrialActivityStageCsReq = 2617;
public static final int RogueChallengeActivityDataScRsp = 2618;
public static final int StartTrialActivityScRsp = 2694;
public static final int GetMonsterResearchActivityDataCsReq = 2680;
public static final int LeaveTrialActivityScRsp = 2697;
public static final int RogueChallengeActivityBuffChooseScNotify = 2634;
public static final int GetTrialActivityDataScRsp = 2615;
public static final int RogueChallengeRefreshAssistListCsReq = 2678;
public static final int GetLoginActivityScRsp = 2625;
public static final int SubmitMonsterResearchActivityMaterialCsReq = 2640;
public static final int RogueChallengeActivityBuffChooseCsReq = 2685;
public static final int EnterActivityBattleStageScRsp = 2612;
public static final int TakeLoginActivityRewardCsReq = 2674;
public static final int RogueChallengeActivityDataCsReq = 2616;
public static final int GetActivityScheduleConfigCsReq = 2683;
public static final int LeaveTrialActivityCsReq = 2667;
public static final int GetTrialActivityDataCsReq = 2650;
public static final int TrialActivityDataChangeScNotify = 2645;
public static final int TakeTrialActivityRewardCsReq = 2630;
public static final int TakeMonsterResearchActivityRewardCsReq = 2602;
public static final int StartTrialActivityCsReq = 2665;
public static final int SubmitMonsterResearchActivityMaterialScRsp = 2693;
public static final int GetQuestDataCsReq = 907;
public static final int TakeQuestRewardCsReq = 974;
public static final int QuestRecordScNotify = 987;
public static final int TakeQuestRewardScRsp = 929;
public static final int GetQuestDataScRsp = 925;
public static final int GetQuestRecordCsReq = 914;
public static final int GetQuestRecordScRsp = 992;
public static final int FinishQuestCsReq = 990;
public static final int FinishQuestScRsp = 932;
public static final int GetRndOptionCsReq = 3407;
public static final int GetRndOptionScRsp = 3425;
public static final int DailyFirstMeetPamCsReq = 3474;
public static final int DailyFirstMeetPamScRsp = 3429;
public static final int TrainVisitorBehaviorFinishCsReq = 3707;
public static final int TrainVisitorBehaviorFinishScRsp = 3725;
public static final int GetTrainVisitorBehaviorScRsp = 3729;
public static final int TrainRefreshTimeNotify = 3783;
public static final int GetTrainVisitorBehaviorCsReq = 3774;
public static final int BattleLogReportScRsp = 132;
public static final int PVEBattleResultScRsp = 125;
public static final int QuitBattleCsReq = 174;
public static final int QuitBattleScNotify = 187;
public static final int GetCurBattleInfoCsReq = 183;
public static final int BattleLogReportCsReq = 190;
public static final int SyncClientResVersionCsReq = 114;
public static final int QuitBattleScRsp = 129;
public static final int PVEBattleResultCsReq = 107;
public static final int GetCurBattleInfoScRsp = 147;
public static final int SyncClientResVersionScRsp = 192;
public static final int UnlockChatBubbleScNotify = 5183;
public static final int SelectChatBubbleCsReq = 5174;
public static final int UnlockPhoneThemeScNotify = 5192;
public static final int GetPhoneDataCsReq = 5107;
public static final int SelectPhoneThemeCsReq = 5147;
public static final int SelectChatBubbleScRsp = 5129;
public static final int SelectPhoneThemeScRsp = 5114;
public static final int GetPhoneDataScRsp = 5125;
public static final int PlayerReturnForceFinishScNotify = 4532;
public static final int PlayerReturnStartScNotify = 4507;
public static final int PlayerReturnSignCsReq = 4525;
public static final int PlayerReturnTakePointRewardCsReq = 4583;
public static final int PlayerReturnTakePointRewardScRsp = 4547;
public static final int PlayerReturnInfoQueryScRsp = 4590;
public static final int PlayerReturnPointChangeScNotify = 4529;
public static final int PlayerReturnInfoQueryCsReq = 4587;
public static final int PlayerReturnTakeRewardCsReq = 4514;
public static final int PlayerReturnTakeRewardScRsp = 4592;
public static final int PlayerReturnSignScRsp = 4574;
public static final int TakeBattleCollegeGroupRewardCsReq = 5747;
public static final int TakeBattleCollegeGroupRewardScRsp = 5714;
public static final int BattleCollegeDataChangeScNotify = 5774;
public static final int GetBattleCollegeDataCsReq = 5707;
public static final int StartBattleCollegeCsReq = 5729;
public static final int StartBattleCollegeScRsp = 5783;
public static final int GetBattleCollegeDataScRsp = 5725;
public static final int GetFirstTalkNpcCsReq = 2183;
public static final int GetFirstTalkNpcScRsp = 2147;
public static final int FinishFirstTalkNpcScRsp = 2192;
public static final int FinishFirstTalkNpcCsReq = 2114;
public static final int GetNpcTakenRewardScRsp = 2125;
public static final int GetFirstTalkByPerformanceNpcCsReq = 2132;
public static final int FinishFirstTalkByPerformanceNpcCsReq = 2108;
public static final int TakeTalkRewardScRsp = 2129;
public static final int GetNpcTakenRewardCsReq = 2107;
public static final int SelectInclinationTextCsReq = 2187;
public static final int GetFirstTalkByPerformanceNpcScRsp = 2106;
public static final int TakeTalkRewardCsReq = 2174;
public static final int FinishFirstTalkByPerformanceNpcScRsp = 2136;
public static final int SelectInclinationTextScRsp = 2190;
public static final int SetHeadIconScRsp = 2829;
public static final int GetPlayerBoardDataCsReq = 2807;
public static final int SetHeadIconCsReq = 2874;
public static final int UnlockHeadIconScNotify = 2887;
public static final int SetSignatureScRsp = 2832;
public static final int SetIsDisplayAvatarInfoScRsp = 2892;
public static final int SetSignatureCsReq = 2890;
public static final int SetAssistAvatarCsReq = 2806;
public static final int SetDisplayAvatarCsReq = 2883;
public static final int SetIsDisplayAvatarInfoCsReq = 2814;
public static final int SetDisplayAvatarScRsp = 2847;
public static final int GetPlayerBoardDataScRsp = 2825;
public static final int SetAssistAvatarScRsp = 2808;
public static final int StartAlleyEventCsReq = 4714;
public static final int GetAlleyInfoCsReq = 4707;
public static final int LogisticsGameScRsp = 4729;
public static final int AlleyGuaranteedFundsScRsp = 4727;
public static final int AlleyPlacingGameCsReq = 4708;
public static final int SaveLogisticsScRsp = 4735;
public static final int AlleyEventEffectNotify = 4790;
public static final int AlleyShipUnlockScNotify = 4785;
public static final int GetSaveLogisticsMapCsReq = 4719;
public static final int AlleyShipUsedCountScNotify = 4777;
public static final int AlleyPlacingGameScRsp = 4736;
public static final int LogisticsScoreRewardSyncInfoScNotify = 4712;
public static final int RefreshAlleyOrderScRsp = 4756;
public static final int GetAlleyInfoScRsp = 4725;
public static final int GetSaveLogisticsMapScRsp = 4799;
public static final int AlleyTakeEventRewardCsReq = 4776;
public static final int AlleyGuaranteedFundsCsReq = 4722;
public static final int RefreshAlleyOrderCsReq = 4780;
public static final int SaveLogisticsCsReq = 4770;
public static final int LogisticsDetonateStarSkiffCsReq = 4734;
public static final int PrestigeLevelUpCsReq = 4702;
public static final int LogisticsInfoScNotify = 4713;
public static final int AlleyTakeEventRewardScRsp = 4733;
public static final int LogisticsDetonateStarSkiffScRsp = 4784;
public static final int AlleyFundsScNotify = 4716;
public static final int AlleyShipmentEventEffectsScNotify = 4771;
public static final int StartAlleyEventScRsp = 4792;
public static final int AlleyShopLevelScNotify = 4718;
public static final int AlleyOrderChangedScNotify = 4740;
public static final int LogisticsGameCsReq = 4774;
public static final int TakePrestigeRewardCsReq = 4732;
public static final int PrestigeLevelUpScRsp = 4796;
public static final int TakePrestigeRewardScRsp = 4706;
public static final int AlleyEventChangeNotify = 4787;
public static final int GetDailyActiveInfoCsReq = 3374;
public static final int TakeAllApRewardCsReq = 3347;
public static final int TakeAllApRewardScRsp = 3314;
public static final int TakeApRewardCsReq = 3307;
public static final int DailyActiveInfoNotify = 3383;
public static final int GetDailyActiveInfoScRsp = 3329;
public static final int TakeApRewardScRsp = 3325;
public static final int GetFantasticStoryActivityDataScRsp = 4925;
public static final int EnterFantasticStoryActivityStageScRsp = 4983;
public static final int EnterFantasticStoryActivityStageCsReq = 4929;
public static final int FinishChapterScNotify = 4974;
public static final int FantasticStoryActivityBattleEndScNotify = 4947;
public static final int GetFantasticStoryActivityDataCsReq = 4907;
public static final int TrialBackGroundMusicScRsp = 3192;
public static final int UnlockBackGroundMusicScRsp = 3147;
public static final int UnlockBackGroundMusicCsReq = 3183;
public static final int PlayBackGroundMusicScRsp = 3129;
public static final int GetJukeboxDataScRsp = 3125;
public static final int TrialBackGroundMusicCsReq = 3114;
public static final int PlayBackGroundMusicCsReq = 3174;
public static final int GetJukeboxDataCsReq = 3107;
public static final int FinishSectionIdCsReq = 2714;
public static final int GetNpcStatusCsReq = 2774;
public static final int FinishSectionIdScRsp = 2792;
public static final int GetNpcMessageGroupScRsp = 2725;
public static final int GetNpcMessageGroupCsReq = 2707;
public static final int FinishPerformSectionIdCsReq = 2787;
public static final int FinishPerformSectionIdScRsp = 2790;
public static final int GetNpcStatusScRsp = 2729;
public static final int FinishItemIdCsReq = 2783;
public static final int FinishItemIdScRsp = 2747;
public static final int GetGachaInfoCsReq = 1907;
public static final int DoGachaCsReq = 1974;
public static final int GetGachaCeilingCsReq = 1983;
public static final int DoGachaScRsp = 1929;
public static final int ExchangeGachaCeilingCsReq = 1914;
public static final int GetGachaInfoScRsp = 1925;
public static final int ExchangeGachaCeilingScRsp = 1992;
public static final int GetGachaCeilingScRsp = 1947;
public static final int AcceptActivityExpeditionScRsp = 2532;
public static final int AcceptActivityExpeditionCsReq = 2590;
public static final int TakeExpeditionRewardScRsp = 2592;
public static final int AcceptExpeditionScRsp = 2529;
public static final int TakeExpeditionRewardCsReq = 2514;
public static final int GetExpeditionDataCsReq = 2507;
public static final int ExpeditionDataChangeScNotify = 2587;
public static final int CancelActivityExpeditionScRsp = 2508;
public static final int CancelActivityExpeditionCsReq = 2506;
public static final int GetExpeditionDataScRsp = 2525;
public static final int CancelExpeditionScRsp = 2547;
public static final int CancelExpeditionCsReq = 2583;
public static final int TakeActivityExpeditionRewardScRsp = 2542;
public static final int AcceptExpeditionCsReq = 2574;
public static final int TakeActivityExpeditionRewardCsReq = 2536;
public static final int SyncAcceptedPamMissionNotify = 4074;
public static final int AcceptedPamMissionExpireCsReq = 4007;
public static final int AcceptedPamMissionExpireScRsp = 4025;
public static final int FinishPlotScRsp = 1125;
public static final int FinishPlotCsReq = 1107;
public static final int SpringTransferCsReq = 1436;
public static final int LastSpringRefreshTimeNotify = 1493;
public static final int EnterSectionScRsp = 1413;
public static final int GetCurSceneInfoCsReq = 1414;
public static final int InteractPropCsReq = 1474;
public static final int ActivateFarmElementCsReq = 1464;
public static final int GetSceneMapInfoCsReq = 1439;
public static final int EnterSectionCsReq = 1435;
public static final int StartCocoonStageCsReq = 1433;
public static final int SpringTransferScRsp = 1442;
public static final int SetClientPausedCsReq = 1478;
public static final int GetCurSceneInfoScRsp = 1492;
public static final int ReturnLastTownScRsp = 1496;
public static final int GetSpringRecoverDataScRsp = 1472;
public static final int GetEnteredSceneCsReq = 1431;
public static final int SavePointsInfoNotify = 1476;
public static final int HealPoolInfoNotify = 1417;
public static final int GameplayCounterRecoverScRsp = 1444;
public static final int GameplayCounterCountDownCsReq = 1454;
public static final int SceneEntityDisappearScNotify = 1490;
public static final int SyncServerSceneChangeNotify = 1443;
public static final int GetSceneMapInfoScRsp = 1424;
public static final int SceneEnterStageCsReq = 1416;
public static final int SetCurInteractEntityCsReq = 1499;
public static final int SpringRefreshCsReq = 1456;
public static final int ActivateFarmElementScRsp = 1453;
public static final int SpringRecoverCsReq = 1415;
public static final int SceneCastSkillMpUpdateScNotify = 1405;
public static final int SetGroupCustomSaveDataScRsp = 1494;
public static final int SpringRefreshScRsp = 1440;
public static final int SetSpringRecoverConfigScRsp = 1450;
public static final int SceneEntityTeleportCsReq = 1482;
public static final int GetUnlockTeleportCsReq = 1455;
public static final int SceneUpdatePositionVersionNotify = 1406;
public static final int UpdateFloorSavedValueNotify = 1457;
public static final int ReturnLastTownCsReq = 1402;
public static final int SceneEntityDieScNotify = 1498;
public static final int GetSpringRecoverDataCsReq = 1500;
public static final int GameplayCounterUpdateScNotify = 1469;
public static final int InteractPropScRsp = 1429;
public static final int ScenePlaneEventScNotify = 1460;
public static final int EnterSceneCsReq = 1410;
public static final int SyncEntityBuffChangeListScNotify = 1408;
public static final int RefreshTriggerPropScNotify = 1423;
public static final int GameplayCounterCountDownScRsp = 1449;
public static final int RefreshTriggerPropCsReq = 1451;
public static final int EnterSceneByServerScNotify = 1421;
public static final int SpringRecoverSingleAvatarCsReq = 1468;
public static final int EntityBindPropScRsp = 1412;
public static final int StartCocoonStageScRsp = 1434;
public static final int SetClientPausedScRsp = 1462;
public static final int GroupStateChangeScRsp = 1452;
public static final int SetGroupCustomSaveDataCsReq = 1465;
public static final int SceneCastSkillCsReq = 1483;
public static final int SceneEntityMoveCsReq = 1407;
public static final int GroupStateChangeCsReq = 1420;
public static final int SceneCastSkillScRsp = 1447;
public static final int EntityBindPropCsReq = 1484;
public static final int EnterSceneScRsp = 1401;
public static final int SceneEntityMoveScRsp = 1425;
public static final int EnteredSceneChangeScNotify = 1404;
public static final int ReEnterLastElementStageCsReq = 1409;
public static final int SetCurInteractEntityScRsp = 1477;
public static final int UpdateMechanismBarScNotify = 1463;
public static final int SceneEntityUpdateScNotify = 1487;
public static final int SetSpringRecoverConfigCsReq = 1475;
public static final int GroupStateChangeScNotify = 1437;
public static final int RecoverAllLineupScRsp = 1427;
public static final int GetEnteredSceneScRsp = 1426;
public static final int GameplayCounterRecoverCsReq = 1486;
public static final int SceneEntityMoveScNotify = 1432;
public static final int SpringRecoverScRsp = 1445;
public static final int GetUnlockTeleportScRsp = 1446;
public static final int SpringRecoverSingleAvatarScRsp = 1430;
public static final int ReEnterLastElementStageScRsp = 1441;
public static final int RecoverAllLineupCsReq = 1422;
public static final int RefreshTriggerPropScRsp = 1491;
public static final int SceneEnterStageScRsp = 1418;
public static final int SceneEntityTeleportScRsp = 1481;
public static final int SceneGroupRefreshScNotify = 1438;
public static final int GetChallengeRaidInfoCsReq = 2247;
public static final int StartRaidCsReq = 2207;
public static final int ChallengeRaidNotify = 2290;
public static final int GetRaidInfoCsReq = 2232;
public static final int GetAllSaveRaidScRsp = 2256;
public static final int TakeChallengeRaidRewardScRsp = 2287;
public static final int RaidKickByServerScNotify = 2293;
public static final int GetSaveRaidScRsp = 2205;
public static final int SetClientRaidTargetCountScRsp = 2236;
public static final int SetClientRaidTargetCountCsReq = 2208;
public static final int GetSaveRaidCsReq = 2242;
public static final int GetRaidInfoScRsp = 2206;
public static final int RaidInfoNotify = 2283;
public static final int GetAllSaveRaidCsReq = 2280;
public static final int LeaveRaidCsReq = 2274;
public static final int TakeChallengeRaidRewardCsReq = 2292;
public static final int StartRaidScRsp = 2225;
public static final int LeaveRaidScRsp = 2229;
public static final int GetChallengeRaidInfoScRsp = 2214;
public static final int DelSaveRaidScNotify = 2240;
public static final int PrepareRogueAdventureRoomCsReq = 5625;
public static final int GetRogueShopBuffInfoScRsp = 5614;
public static final int BuyRogueShopMiracleCsReq = 5692;
public static final int GetRogueAdventureRoomInfoCsReq = 5636;
public static final int BuyRogueShopBuffCsReq = 5690;
public static final int GetRogueAdventureRoomInfoScRsp = 5642;
public static final int SyncRogueCommonBuffDisplayScNotify = 5605;
public static final int PrepareRogueAdventureRoomScRsp = 5674;
public static final int GetRogueShopMiracleInfoScRsp = 5683;
public static final int RogueNpcDisappearCsReq = 5606;
public static final int SyncRogueAdventureRoomInfoScNotify = 5607;
public static final int SyncRogueCommonMiracleDisplayScNotify = 5680;
public static final int BuyRogueShopMiracleScRsp = 5687;
public static final int BuyRogueShopBuffScRsp = 5632;
public static final int GetRogueShopBuffInfoCsReq = 5647;
public static final int GetRogueShopMiracleInfoCsReq = 5629;
public static final int RogueNpcDisappearScRsp = 5608;
public static final int SyncRogueCommonItemDisplayScNotify = 5656;
public static final int MuseumDispatchFinishedScNotify = 4318;
public static final int BuyNpcStuffScRsp = 4329;
public static final int MuseumTargetMissionFinishNotify = 4370;
public static final int MuseumRandomEventSelectCsReq = 4396;
public static final int MuseumRandomEventStartScNotify = 4340;
public static final int GetExhibitScNotify = 4390;
public static final int FinishCurTurnCsReq = 4332;
public static final int GetMuseumInfoCsReq = 4307;
public static final int MuseumTargetRewardNotify = 4335;
public static final int MuseumRandomEventSelectScRsp = 4316;
public static final int MuseumTakeCollectRewardScRsp = 4371;
public static final int MuseumTakeCollectRewardCsReq = 4313;
public static final int FinishCurTurnScRsp = 4306;
public static final int SetStuffToAreaScRsp = 4347;
public static final int UpgradeAreaScRsp = 4336;
public static final int BuyNpcStuffCsReq = 4374;
public static final int UpgradeAreaCsReq = 4308;
public static final int UpgradeAreaStatScRsp = 4305;
public static final int MuseumFundsChangedScNotify = 4356;
public static final int MuseumRandomEventQueryCsReq = 4393;
public static final int MuseumRandomEventQueryScRsp = 4302;
public static final int RemoveStuffFromAreaScRsp = 4392;
public static final int SetStuffToAreaCsReq = 4383;
public static final int GetMuseumInfoScRsp = 4325;
public static final int MuseumTargetStartNotify = 4385;
public static final int MuseumInfoChangedScNotify = 4380;
public static final int UpgradeAreaStatCsReq = 4342;
public static final int RemoveStuffFromAreaCsReq = 4314;
public static final int GetStuffScNotify = 4387;
public static final int GetShareDataCsReq = 4174;
public static final int CancelCacheNotifyScRsp = 4190;
public static final int ShareCsReq = 4107;
public static final int TriggerHealVoiceScRsp = 4192;
public static final int ShareScRsp = 4125;
public static final int CancelCacheNotifyCsReq = 4187;
public static final int TriggerHealVoiceCsReq = 4114;
public static final int TakePictureScRsp = 4147;
public static final int TakePictureCsReq = 4183;
public static final int GetShareDataScRsp = 4129;
public static final int TakeMailAttachmentCsReq = 814;
public static final int GetMailCsReq = 807;
public static final int NewMailScNotify = 887;
public static final int DelMailScRsp = 847;
public static final int GetMailScRsp = 825;
public static final int MarkReadMailScRsp = 829;
public static final int DelMailCsReq = 883;
public static final int MarkReadMailCsReq = 874;
public static final int TakeMailAttachmentScRsp = 892;
public static final int GetBasicInfoScRsp = 72;
public static final int ExchangeStaminaCsReq = 36;
public static final int SetGenderCsReq = 84;
public static final int ExchangeStaminaScRsp = 42;
public static final int SetPlayerInfoScRsp = 62;
public static final int PlayerLoginFinishScRsp = 10;
public static final int GetVideoVersionKeyScRsp = 21;
public static final int PlayerLoginScRsp = 25;
public static final int GateServerScNotify = 94;
public static final int GmTalkScRsp = 32;
public static final int ServerAnnounceNotify = 19;
public static final int GetHeroBasicTypeInfoScRsp = 27;
public static final int GetLevelRewardScRsp = 85;
public static final int SetHeroBasicTypeScRsp = 77;
public static final int GetSecretKeyInfoScRsp = 82;
public static final int SetPlayerInfoCsReq = 78;
public static final int FeatureSwitchClosedScNotify = 67;
public static final int PlayerHeartBeatScRsp = 65;
public static final int ReserveStaminaExchangeCsReq = 43;
public static final int GetVideoVersionKeyCsReq = 1;
public static final int GmTalkScNotify = 92;
public static final int SetGenderScRsp = 12;
public static final int AceAntiCheaterScRsp = 17;
public static final int RegionStopScNotify = 56;
public static final int QueryProductInfoScRsp = 79;
public static final int RetcodeNotify = 30;
public static final int SetLanguageCsReq = 13;
public static final int PlayerGetTokenCsReq = 83;
public static final int PlayerLoginFinishCsReq = 81;
public static final int SetGameplayBirthdayScRsp = 15;
public static final int GetLevelRewardTakenListScRsp = 16;
public static final int GetLevelRewardTakenListCsReq = 96;
public static final int UpdatePlayerSettingScRsp = 57;
public static final int UpdatePlayerSettingCsReq = 59;
public static final int SetNicknameScRsp = 2;
public static final int GetAuthkeyCsReq = 5;
public static final int ReserveStaminaExchangeScRsp = 55;
public static final int SetHeroBasicTypeCsReq = 99;
public static final int SetLanguageScRsp = 71;
public static final int AceAntiCheaterCsReq = 45;
public static final int UpdateFeatureSwitchScNotify = 53;
public static final int DailyRefreshNotify = 75;
public static final int PlayerGetTokenScRsp = 47;
public static final int PlayerLoginCsReq = 7;
public static final int MonthCardRewardNotify = 68;
public static final int SetGameplayBirthdayCsReq = 50;
public static final int SetRedPointStatusScNotify = 60;
public static final int GetAuthkeyScRsp = 80;
public static final int HeroBasicTypeChangedNotify = 98;
public static final int GetSecretKeyInfoCsReq = 41;
public static final int StaminaInfoScNotify = 46;
public static final int PlayerHeartBeatCsReq = 63;
public static final int GetBasicInfoCsReq = 100;
public static final int AntiAddictScNotify = 40;
public static final int GetLevelRewardCsReq = 18;
public static final int PlayerLogoutCsReq = 74;
public static final int ClientDownloadDataScNotify = 64;
public static final int PlayerKickOutScNotify = 87;
public static final int PlayerLogoutScRsp = 29;
public static final int GetHeroBasicTypeInfoCsReq = 22;
public static final int GmTalkCsReq = 90;
public static final int QueryProductInfoCsReq = 73;
public static final int SetNicknameCsReq = 93;
public static final int GetSingleRedDotParamGroupScRsp = 5947;
public static final int UpdateRedDotDataScRsp = 5929;
public static final int UpdateRedDotDataCsReq = 5974;
public static final int GetAllRedDotDataScRsp = 5925;
public static final int GetSingleRedDotParamGroupCsReq = 5983;
public static final int GetAllRedDotDataCsReq = 5907;
public static final int EnterAdventureScRsp = 1325;
public static final int EnterAdventureCsReq = 1307;
public static final int GetCurChallengeCsReq = 1790;
public static final int GetChallengeCsReq = 1707;
public static final int TakeChallengeRewardCsReq = 1742;
public static final int LeaveChallengeCsReq = 1783;
public static final int TakeChallengeRewardScRsp = 1705;
public static final int StartChallengeScRsp = 1729;
public static final int LeaveChallengeScRsp = 1747;
public static final int TakeChallengeTargetRewardCsReq = 1708;
public static final int GetCurChallengeScRsp = 1732;
public static final int GetChallengeScRsp = 1725;
public static final int FinishChallengeScRsp = 1787;
public static final int StartChallengeCsReq = 1774;
public static final int FinishChallengeCsReq = 1792;
public static final int ChallengeSettleNotify = 1714;
public static final int ChallengeLineupNotify = 1706;
public static final int TakeChallengeTargetRewardScRsp = 1736;
public static final int GetAvatarDataCsReq = 307;
public static final int DressRelicAvatarScRsp = 380;
public static final int TakeOffEquipmentCsReq = 332;
public static final int DressAvatarScRsp = 390;
public static final int DressRelicAvatarCsReq = 305;
public static final int GetAvatarDataScRsp = 325;
public static final int RankUpAvatarCsReq = 336;
public static final int UnlockSkilltreeScRsp = 347;
public static final int TakeOffRelicScRsp = 340;
public static final int DressAvatarCsReq = 387;
public static final int TakeOffRelicCsReq = 356;
public static final int AvatarExpUpScRsp = 329;
public static final int TakePromotionRewardCsReq = 393;
public static final int PromoteAvatarCsReq = 314;
public static final int TakePromotionRewardScRsp = 302;
public static final int AvatarExpUpCsReq = 374;
public static final int UnlockSkilltreeCsReq = 383;
public static final int PromoteAvatarScRsp = 392;
public static final int AddAvatarScNotify = 308;
public static final int RankUpAvatarScRsp = 342;
public static final int TakeOffEquipmentScRsp = 306;
public static final int GetChatFriendHistoryScRsp = 3992;
public static final int MarkChatEmojiCsReq = 3932;
public static final int GetChatFriendHistoryCsReq = 3914;
public static final int GetPrivateChatHistoryCsReq = 3983;
public static final int SendMsgScRsp = 3925;
public static final int GetChatEmojiListScRsp = 3990;
public static final int PrivateMsgOfflineUsersScNotify = 3929;
public static final int GetPrivateChatHistoryScRsp = 3947;
public static final int BatchMarkChatEmojiScRsp = 3936;
public static final int BatchMarkChatEmojiCsReq = 3908;
public static final int RevcMsgScNotify = 3974;
public static final int SendMsgCsReq = 3907;
public static final int GetChatEmojiListCsReq = 3987;
public static final int MarkChatEmojiScRsp = 3906;
public static final int UnlockTutorialScRsp = 1647;
public static final int UnlockTutorialGuideScRsp = 1692;
public static final int UnlockTutorialGuideCsReq = 1614;
public static final int FinishTutorialGuideCsReq = 1632;
public static final int FinishTutorialGuideScRsp = 1606;
public static final int GetTutorialCsReq = 1607;
public static final int GetTutorialScRsp = 1625;
public static final int GetTutorialGuideCsReq = 1674;
public static final int FinishTutorialScRsp = 1690;
public static final int FinishTutorialCsReq = 1687;
public static final int UnlockTutorialCsReq = 1683;
public static final int GetTutorialGuideScRsp = 1629;
public static final int TakeCityShopRewardScRsp = 1547;
public static final int CityShopInfoScNotify = 1514;
public static final int BuyGoodsCsReq = 1574;
public static final int BuyGoodsScRsp = 1529;
public static final int GetShopListScRsp = 1525;
public static final int GetShopListCsReq = 1507;
public static final int TakeCityShopRewardCsReq = 1583;
public static final int EnterFightActivityStageCsReq = 3629;
public static final int FightActivityDataChangeScNotify = 3674;
public static final int TakeFightActivityRewardCsReq = 3647;
public static final int GetFightActivityDataScRsp = 3625;
public static final int GetFightActivityDataCsReq = 3607;
public static final int TakeFightActivityRewardScRsp = 3614;
public static final int EnterFightActivityStageScRsp = 3683;
public static final int SharePunkLordMonsterScRsp = 3247;
public static final int TakePunkLordPointRewardScRsp = 3236;
public static final int PunkLordMonsterInfoScNotify = 3242;
public static final int PunkLordDataChangeNotify = 3299;
public static final int GetPunkLordBattleRecordScRsp = 3222;
public static final int SummonPunkLordMonsterCsReq = 3214;
public static final int GetKilledPunkLordMonsterDataScRsp = 3285;
public static final int PunkLordRaidTimeOutScNotify = 3240;
public static final int GetKilledPunkLordMonsterDataCsReq = 3218;
public static final int GetPunkLordMonsterDataScRsp = 3225;
public static final int SharePunkLordMonsterCsReq = 3283;
public static final int StartPunkLordRaidCsReq = 3274;
public static final int TakeKilledPunkLordMonsterScoreScRsp = 3219;
public static final int TakePunkLordPointRewardCsReq = 3208;
public static final int TakeKilledPunkLordMonsterScoreCsReq = 3271;
public static final int GetPunkLordDataScRsp = 3280;
public static final int PunkLordMonsterKilledNotify = 3213;
public static final int StartPunkLordRaidScRsp = 3229;
public static final int GetPunkLordBattleRecordCsReq = 3277;
public static final int SummonPunkLordMonsterScRsp = 3292;
public static final int GetPunkLordDataCsReq = 3205;
public static final int PunkLordBattleResultScNotify = 3216;
public static final int GetPunkLordMonsterDataCsReq = 3207;
public static final int AcceptMissionEventScRsp = 1240;
public static final int GetMissionDataScRsp = 1225;
public static final int StartFinishSubMissionScNotify = 1271;
public static final int InterruptMissionEventScRsp = 1216;
public static final int GetMissionEventDataScRsp = 1205;
public static final int FinishTalkMissionCsReq = 1274;
public static final int GetMissionEventDataCsReq = 1242;
public static final int FinishCosumeItemMissionScRsp = 1236;
public static final int GetMainMissionCustomValueCsReq = 1222;
public static final int AcceptMainMissionCsReq = 1299;
public static final int DailyTaskDataScNotify = 1292;
public static final int StartFinishMainMissionScNotify = 1219;
public static final int GetMissionStatusCsReq = 1293;
public static final int GetMissionDataCsReq = 1207;
public static final int MissionEventRewardScNotify = 1280;
public static final int GetMissionStatusScRsp = 1202;
public static final int TeleportToMissionResetPointCsReq = 1235;
public static final int AcceptMissionEventCsReq = 1256;
public static final int TeleportToMissionResetPointScRsp = 1213;
public static final int SetMissionEventProgressScRsp = 1285;
public static final int SubMissionRewardScNotify = 1270;
public static final int FinishTalkMissionScRsp = 1229;
public static final int GetMainMissionCustomValueScRsp = 1227;
public static final int MissionAcceptScNotify = 1276;
public static final int MissionRewardScNotify = 1283;
public static final int SetMissionEventProgressCsReq = 1218;
public static final int MissionGroupWarnScNotify = 1206;
public static final int SyncTaskScRsp = 1214;
public static final int AcceptMainMissionScRsp = 1277;
public static final int InterruptMissionEventCsReq = 1296;
public static final int FinishCosumeItemMissionCsReq = 1208;
public static final int SyncTaskCsReq = 1247;
public static final int PlayerSyncScNotify = 607;
public static final int ExpUpRelicCsReq = 542;
public static final int SellItemScRsp = 593;
public static final int ExpUpEquipmentCsReq = 532;
public static final int AutoUseTurnFoodNotify = 598;
public static final int GetRecyleTimeScRsp = 513;
public static final int ComposeItemScRsp = 536;
public static final int CancelTurnFoodCsReq = 578;
public static final int AddEquipmentScNotify = 570;
public static final int ExpUpEquipmentScRsp = 506;
public static final int GetMarkItemListScRsp = 527;
public static final int SetTurnFoodScRsp = 512;
public static final int ComposeItemCsReq = 508;
public static final int LockRelicCsReq = 580;
public static final int RechargeSuccNotify = 502;
public static final int ExpUpRelicScRsp = 505;
public static final int MarkItemScRsp = 533;
public static final int RankUpEquipmentCsReq = 587;
public static final int GetRecyleTimeCsReq = 535;
public static final int DestroyItemScRsp = 577;
public static final int GetBagScRsp = 525;
public static final int ComposeLimitNumUpdateNotify = 519;
public static final int LockEquipmentCsReq = 583;
public static final int PromoteEquipmentScRsp = 529;
public static final int ComposeSelectedRelicScRsp = 585;
public static final int SetTurnFoodCsReq = 584;
public static final int RankUpEquipmentScRsp = 590;
public static final int LockRelicScRsp = 556;
public static final int UseItemScRsp = 592;
public static final int ExchangeHcoinScRsp = 516;
public static final int PromoteEquipmentCsReq = 574;
public static final int DestroyItemCsReq = 599;
public static final int SellItemCsReq = 540;
public static final int ExchangeHcoinCsReq = 596;
public static final int GetBagCsReq = 507;
public static final int GetMarkItemListCsReq = 522;
public static final int ComposeLimitNumCompleteNotify = 571;
public static final int ComposeSelectedRelicCsReq = 518;
public static final int CancelMarkItemNotify = 534;
public static final int MarkItemCsReq = 576;
public static final int LockEquipmentScRsp = 547;
public static final int CancelTurnFoodScRsp = 562;
public static final int UseItemCsReq = 514;
public static final int TextJoinQueryCsReq = 3874;
public static final int TextJoinSaveScRsp = 3825;
public static final int TextJoinQueryScRsp = 3829;
public static final int TextJoinBatchSaveScRsp = 3847;
public static final int TextJoinBatchSaveCsReq = 3883;
public static final int TextJoinSaveCsReq = 3807;
public static final int UseTreasureDungeonItemScRsp = 4496;
public static final int FightTreasureDungeonMonsterCsReq = 4480;
public static final int InteractTreasureDungeonGridScRsp = 4493;
public static final int OpenTreasureDungeonGridCsReq = 4442;
public static final int QuitTreasureDungeonCsReq = 4416;
public static final int EnterTreasureDungeonScRsp = 4436;
public static final int UseTreasureDungeonItemCsReq = 4402;
public static final int OpenTreasureDungeonGridScRsp = 4405;
public static final int GetTreasureDungeonActivityDataScRsp = 4406;
public static final int QuitTreasureDungeonScRsp = 4418;
public static final int GetTreasureDungeonActivityDataCsReq = 4432;
public static final int EnterTreasureDungeonCsReq = 4408;
public static final int FightTreasureDungeonMonsterScRsp = 4456;
public static final int InteractTreasureDungeonGridCsReq = 4440;
public static final int TreasureDungeonDataScNotify = 4407;
public static final int TreasureDungeonFinishScNotify = 4425;
public static final int GetAssistListScRsp = 2919;
public static final int SetFriendRemarkNameScRsp = 2996;
public static final int GetPlatformPlayerInfoCsReq = 2962;
public static final int DeleteFriendCsReq = 2908;
public static final int SetAssistScRsp = 2977;
public static final int GetFriendLoginInfoScRsp = 2979;
public static final int GetFriendApplyListInfoCsReq = 2983;
public static final int SyncAddBlacklistScNotify = 2956;
public static final int DeleteFriendScRsp = 2936;
public static final int DeleteBlacklistScRsp = 2970;
public static final int AddBlacklistScRsp = 2980;
public static final int SearchPlayerScRsp = 2913;
public static final int GetFriendRecommendListInfoCsReq = 2940;
public static final int GetAssistHistoryCsReq = 2976;
public static final int ReportPlayerScRsp = 2918;
public static final int SyncApplyFriendScNotify = 2987;
public static final int TakeAssistRewardScRsp = 2912;
public static final int GetAssistListCsReq = 2971;
public static final int ReportPlayerCsReq = 2916;
public static final int CurAssistChangedNotify = 2978;
public static final int NewAssistHistoryNotify = 2934;
public static final int GetFriendListInfoScRsp = 2925;
public static final int GetAssistHistoryScRsp = 2933;
public static final int GetPlayerDetailInfoScRsp = 2929;
public static final int HandleFriendScRsp = 2932;
public static final int GetPlayerDetailInfoCsReq = 2974;
public static final int SearchPlayerCsReq = 2935;
public static final int GetFriendLoginInfoCsReq = 2973;
public static final int ApplyFriendScRsp = 2992;
public static final int AddBlacklistCsReq = 2905;
public static final int GetPlatformPlayerInfoScRsp = 2998;
public static final int SetForbidOtherApplyFriendCsReq = 2964;
public static final int SetAssistCsReq = 2999;
public static final int SyncHandleFriendScNotify = 2906;
public static final int GetFriendRecommendListInfoScRsp = 2993;
public static final int ApplyFriendCsReq = 2914;
public static final int SyncDeleteFriendScNotify = 2942;
public static final int SetForbidOtherApplyFriendScRsp = 2953;
public static final int GetCurAssistCsReq = 2922;
public static final int HandleFriendCsReq = 2990;
public static final int GetFriendListInfoCsReq = 2907;
public static final int SetFriendRemarkNameCsReq = 2902;
public static final int TakeAssistRewardCsReq = 2984;
public static final int GetCurAssistScRsp = 2927;
public static final int GetFriendApplyListInfoScRsp = 2947;
public static final int DeleteBlacklistCsReq = 2985;
public static final int GetBoxingClubInfoCsReq = 4207;
public static final int GetBoxingClubInfoScRsp = 4225;
public static final int GiveUpBoxingClubChallengeScRsp = 4292;
public static final int BoxingClubRewardScNotify = 4287;
public static final int MatchBoxingClubOpponentCsReq = 4274;
public static final int MatchBoxingClubOpponentScRsp = 4229;
public static final int BoxingClubChallengeUpdateScNotify = 4290;
public static final int GiveUpBoxingClubChallengeCsReq = 4214;
public static final int StartBoxingClubBattleCsReq = 4283;
public static final int StartBoxingClubBattleScRsp = 4247;
public static final int GetPlayerReturnMultiDropInfoCsReq = 4629;
public static final int GetMultipleDropInfoCsReq = 4607;
public static final int MultipleDropInfoScNotify = 4674;
public static final int GetMultipleDropInfoScRsp = 4625;
public static final int GetPlayerReturnMultiDropInfoScRsp = 4683;
public static final int MultipleDropInfoNotify = 4647;
public static final int GetReplayTokenCsReq = 3507;
public static final int GetPlayerReplayInfoScRsp = 3529;
public static final int GetPlayerReplayInfoCsReq = 3574;
public static final int GetReplayTokenScRsp = 3525;
public static final int RollRogueBuffScRsp = 1808;
public static final int FinishAeonDialogueGroupCsReq = 1826;
public static final int EnterRogueMapRoomScRsp = 1878;
public static final int SelectRogueMiracleCsReq = 1834;
public static final int SyncRogueBuffSwapInfoScNotify = 1813;
public static final int SelectRogueBuffCsReq = 1890;
public static final int StartRogueScRsp = 1829;
public static final int GetRogueInfoCsReq = 1807;
public static final int GetRogueBuffEnhanceInfoScRsp = 1818;
public static final int GetRogueScoreRewardInfoScRsp = 1849;
public static final int SelectRogueBonusCsReq = 1864;
public static final int EnterRogueCsReq = 1883;
public static final int RemoveRogueMiracleScNotify = 1841;
public static final int SyncRogueAreaUnlockScNotify = 1860;
public static final int FinishRogueDialogueGroupScRsp = 1817;
public static final int TakeRogueEventHandbookRewardCsReq = 1886;
public static final int GetRogueBuffEnhanceInfoCsReq = 1816;
public static final int SyncRoguePickAvatarInfoScNotify = 1803;
public static final int TakeRogueScoreRewardCsReq = 1802;
public static final int ReplaceRogueMiracleDisplayScNotify = 1852;
public static final int SyncRogueGetItemScNotify = 1839;
public static final int UpdateRogueMiracleScNotify = 1882;
public static final int SyncRogueEventHandbookScNotify = 1820;
public static final int FinishRogueDialogueGroupCsReq = 1845;
public static final int SelectRogueMiracleScRsp = 1884;
public static final int SyncRogueMiracleScNotify = 1888;
public static final int SyncRogueAeonScNotify = 1821;
public static final int SyncRogueFinishScNotify = 1842;
public static final int RollRogueBuffCsReq = 1806;
public static final int SyncRogueBonusSelectInfoScNotify = 1879;
public static final int EnterRogueScRsp = 1847;
public static final int PickRogueAvatarCsReq = 1805;
public static final int GetRogueScoreRewardInfoCsReq = 1854;
public static final int ExchangeRogueRewardKeyCsReq = 1863;
public static final int GetRogueInitialScoreScRsp = 1898;
public static final int LeaveRogueScRsp = 1892;
public static final int TakeRogueMiracleHandbookRewardCsReq = 1869;
public static final int EnhanceRogueBuffCsReq = 1885;
public static final int GetRogueInitialScoreCsReq = 1862;
public static final int SelectRogueDialogueEventCsReq = 1881;
public static final int SyncRogueMiracleHandbookScNotify = 1858;
public static final int GetRogueAeonInfoCsReq = 1837;
public static final int EnhanceRogueBuffScRsp = 1870;
public static final int AddRogueBuffScNotify = 1856;
public static final int SyncRogueStatusScNotify = 1848;
public static final int GetRogueInfoScRsp = 1825;
public static final int ReforgeRogueBuffScRsp = 1875;
public static final int AddRogueMiracleScNotify = 1809;
public static final int GetRogueAeonInfoScRsp = 1831;
public static final int SwapRogueBuffScRsp = 1819;
public static final int ExchangeRogueRewardKeyScRsp = 1865;
public static final int TradeRogueMiracleScRsp = 1897;
public static final int SyncRogueAeonLevelUpRewardScNotify = 1857;
public static final int GetRogueDialogueEventDataScRsp = 1815;
public static final int QuitRogueCsReq = 1877;
public static final int SyncRogueBuffSelectInfoScNotify = 1887;
public static final int SelectRogueDialogueEventScRsp = 1810;
public static final int TakeRogueAeonLevelRewardScRsp = 1843;
public static final int GetRogueDialogueEventDataCsReq = 1850;
public static final int ReviveRogueAvatarCsReq = 1840;
public static final int ReforgeRogueBuffCsReq = 1872;
public static final int ReviveRogueAvatarScRsp = 1893;
public static final int TakeRogueEventHandbookRewardScRsp = 1844;
public static final int GetRogueTalentInfoScRsp = 1891;
public static final int SyncRogueVirtualItemInfoScNotify = 1866;
public static final int SyncRogueMapRoomScNotify = 1873;
public static final int StartRogueCsReq = 1874;
public static final int SyncRogueMiracleTradeInfoScNotify = 1894;
public static final int OpenRogueChestCsReq = 1868;
public static final int FinishAeonDialogueGroupScRsp = 1804;
public static final int EnableRogueTalentCsReq = 1823;
public static final int SyncRogueMiracleInfoScNotify = 1855;
public static final int SyncRogueDialogueEventDataScNotify = 1801;
public static final int SyncRogueBuffReforgeInfoScNotify = 1900;
public static final int SelectRogueBonusScRsp = 1853;
public static final int EnableRogueTalentScRsp = 1895;
public static final int OpenRogueChestScRsp = 1830;
public static final int TradeRogueMiracleCsReq = 1867;
public static final int RemoveRogueBuffScNotify = 1835;
public static final int SelectRogueBuffScRsp = 1832;
public static final int TakeRogueMiracleHandbookRewardScRsp = 1838;
public static final int GetRogueTalentInfoCsReq = 1851;
public static final int SyncRogueSeasonFinishScNotify = 1833;
public static final int LeaveRogueCsReq = 1814;
public static final int GetRogueHandbookDataCsReq = 1846;
public static final int QuitRogueScRsp = 1822;
public static final int SyncRogueRewardInfoScNotify = 1861;
public static final int PickRogueAvatarScRsp = 1880;
public static final int GetRogueHandbookDataScRsp = 1859;
public static final int EnterRogueMapRoomCsReq = 1812;
public static final int SyncRogueMiracleSelectInfoScNotify = 1836;
public static final int TakeRogueAeonLevelRewardCsReq = 1824;
public static final int SwapRogueBuffCsReq = 1871;
public static final int TakeRogueScoreRewardScRsp = 1896;
public static final int SyncRogueReviveInfoScNotify = 1899;
public static final int ChessRogueConfirmRollCsReq = 5514;
public static final int ChessRogueSelectBpCsReq = 5454;
public static final int ChessRogueUpdateLevelBaseInfoScNotify = 5563;
public static final int ChessRogueCellUpdateNotify = 5582;
public static final int ChessRogueUpdateActionPointScNotify = 5553;
public static final int SyncChessRogueMainStoryFinishScNotify = 5535;
public static final int ReforgeChessRogueBuffScRsp = 5461;
public static final int ReforgeChessRogueBuffCsReq = 5523;
public static final int EnhanceChessRogueBuffScRsp = 5539;
public static final int ChessRogueQueryAeonDimensionsCsReq = 5434;
public static final int EnhanceChessRogueBuffCsReq = 5572;
public static final int ChessRogueQueryScRsp = 5524;
public static final int ChessRogueStartScRsp = 5599;
public static final int ChessRogueUpdateAeonModifierValueScNotify = 5499;
public static final int ReplaceChessRogueMiracleDisplayScNotify = 5585;
public static final int GetChessRogueStoryAeonTalkInfoScRsp = 5531;
public static final int AddChessRogueBuffScNotify = 5546;
public static final int TradeChessRogueMiracleCsReq = 5573;
public static final int RepairChessRogueMiracleScRsp = 5438;
public static final int ChessRogueSelectBpScRsp = 5417;
public static final int ChessRogueEnterScRsp = 5594;
public static final int ChessRogueQuitScRsp = 5449;
public static final int ChessRogueChangeyAeonDimensionNotify = 5480;
public static final int AddChessRogueMiracleScNotify = 5476;
public static final int SyncChessRogueBuffSelectInfoScNotify = 5418;
public static final int SelectChessRogueMiracleCsReq = 5404;
public static final int SyncChessRogueMiracleRepairInfoScNotify = 5544;
public static final int ChessRogueUpdateAllowedSelectCellScNotify = 5568;
public static final int ChessRogueQuitCsReq = 5580;
public static final int ChessRogueGoAheadScRsp = 5567;
public static final int ChessRogueReRollDiceCsReq = 5528;
public static final int DropChessRogueBuffScRsp = 5508;
public static final int SelectChessRogueBuffScRsp = 5547;
public static final int SyncChessRogueMiracleInfoScNotify = 5463;
public static final int FinishChessRogueSubStoryCsReq = 5405;
public static final int ChessRogueGiveUpRollScRsp = 5490;
public static final int ChessRogueLayerAccountInfoNotify = 5478;
public static final int ChessRogueEnterCsReq = 5525;
public static final int ChessRogueEnterCellScRsp = 5526;
public static final int SelectChessRogueMiracleScRsp = 5460;
public static final int ChessRogueUpdateDiceInfoScNotify = 5552;
public static final int ChessRogueReRollDiceScRsp = 5527;
public static final int RemoveChessRogueBuffScNotify = 5458;
public static final int ChessRogueStartCsReq = 5550;
public static final int ChessRoguePickAvatarCsReq = 5481;
public static final int ChessRogueUpdateReviveInfoScNotify = 5414;
public static final int SyncChessRogueMiracleTradeInfoScNotify = 5428;
public static final int SelectChessRogueSubStoryScRsp = 5409;
public static final int ChessRogueGiveUpCsReq = 5432;
public static final int ChessRogueRollDiceScRsp = 5447;
public static final int DropChessRogueBuffCsReq = 5541;
public static final int FinishChessRogueSubStoryScRsp = 5598;
public static final int SelectChessRogueBonusCsReq = 5538;
public static final int ChessRogueQueryBpScRsp = 5484;
public static final int ChessRogueUpdateBoardScNotify = 5453;
public static final int GetChessRogueStoryInfoCsReq = 5593;
public static final int SelectChessRogueSubStoryCsReq = 5543;
public static final int ChessRogueGiveUpScRsp = 5542;
public static final int SyncChessRogueBuffDropInfoScNotify = 5486;
public static final int ChessRogueUpdateUnlockLevelScNotify = 5574;
public static final int ChessRogueQuestFinishNotify = 5492;
public static final int TradeChessRogueMiracleScRsp = 5557;
public static final int ChessRogueReviveAvatarScRsp = 5401;
public static final int ChessRogueEnterNextLayerScRsp = 5427;
public static final int ChessRogueEnterNextLayerCsReq = 5596;
public static final int ChessRogueQueryCsReq = 5416;
public static final int ChessRogueMoveCellNotify = 5590;
public static final int ChessRogueUpdateMoneyInfoScNotify = 5556;
public static final int RollChessRogueBuffCsReq = 5587;
public static final int ChessRogueQueryBpCsReq = 5471;
public static final int ChessRogueSelectCellScRsp = 5530;
public static final int EnterChessRogueAeonRoomScRsp = 5500;
public static final int SyncChessRogueBuffReforgeInfoScNotify = 5560;
public static final int ChessRogueFinishCurRoomNotify = 5511;
public static final int ChessRogueRollDiceCsReq = 5591;
public static final int SyncChessRogueBonusSelectInfoScNotify = 5467;
public static final int ChessRogueCheatRollScRsp = 5600;
public static final int GetChessRogueStoryAeonTalkInfoCsReq = 5518;
public static final int ChessRogueGiveUpRollCsReq = 5408;
public static final int GetChessRogueBuffEnhanceInfoCsReq = 5512;
public static final int RemoveChessRogueMiracleScNotify = 5555;
public static final int ChessRogueLeaveScRsp = 5450;
public static final int ChessRogueCheatRollCsReq = 5569;
public static final int SelectChessRogueBonusScRsp = 5588;
public static final int RollChessRogueBuffScRsp = 5436;
public static final int ChessRogueLeaveCsReq = 5554;
public static final int ChessRogueReviveAvatarCsReq = 5558;
public static final int ChessRogueConfirmRollScRsp = 5495;
public static final int EnterChessRogueAeonRoomCsReq = 5422;
public static final int GetChessRogueBuffEnhanceInfoScRsp = 5425;
public static final int ChessRoguePickAvatarScRsp = 5482;
public static final int RepairChessRogueMiracleCsReq = 5420;
public static final int UpdateChessRogueMiracleScNotify = 5444;
public static final int GetChessRogueStoryInfoScRsp = 5597;
public static final int ChessRogueGoAheadCsReq = 5505;
public static final int SelectChessRogueBuffCsReq = 5464;
public static final int SyncChessRogueMiracleSelectInfoScNotify = 5472;
public static final int ChessRogueQueryAeonDimensionsScRsp = 5494;
public static final int ChessRogueEnterCellCsReq = 5426;
public static final int ChessRogueSelectCellCsReq = 5579;
public static final int RogueModifierDelNotify = 5387;
public static final int RogueModifierUpdateNotify = 5392;
public static final int RogueModifierSelectCellScRsp = 5383;
public static final int RogueModifierAddNotify = 5374;
public static final int RogueModifierSelectCellCsReq = 5329;
public static final int GetUpdatedArchiveDataCsReq = 2374;
public static final int GetUpdatedArchiveDataScRsp = 2329;
public static final int GetArchiveDataScRsp = 2325;
public static final int GetArchiveDataCsReq = 2307;
public static final int GetChapterScRsp = 447;
public static final int WaypointShowNewCsNotify = 414;
public static final int TakeChapterRewardCsReq = 492;
public static final int GetWaypointScRsp = 425;
public static final int TakeChapterRewardScRsp = 487;
public static final int SetCurWaypointCsReq = 474;
public static final int GetWaypointCsReq = 407;
public static final int SetCurWaypointScRsp = 429;
public static final int GetChapterCsReq = 483;
}

View File

@@ -0,0 +1,53 @@
package emu.lunarcore.server.packet;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import emu.lunarcore.GameConstants;
import emu.lunarcore.LunarRail;
import emu.lunarcore.util.JsonUtils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class CmdIdUtils {
private static Int2ObjectMap<String> opcodeMap;
static {
opcodeMap = new Int2ObjectOpenHashMap<>();
Field[] fields = CmdId.class.getFields();
for (Field f : fields) {
if (f.getType().equals(int.class)) {
try {
opcodeMap.put(f.getInt(null), f.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static String getOpcodeName(int opcode) {
if (opcode <= 0) return "UNKNOWN";
return opcodeMap.getOrDefault(opcode, "UNKNOWN");
}
public static void dumpPacketIds() {
try (FileWriter writer = new FileWriter("./PacketIds_" + GameConstants.VERSION + ".json")) {
// Create sorted tree map
Map<Integer, String> packetIds = opcodeMap.int2ObjectEntrySet().stream()
.filter(e -> e.getIntKey() > 0)
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey, Int2ObjectMap.Entry::getValue, (k, v) -> v, TreeMap::new));
// Write to file
writer.write(JsonUtils.encode(packetIds));
LunarRail.getLogger().info("Dumped packet ids.");
} catch (IOException e) {
e.printStackTrace();
}
}
}

Some files were not shown because too many files have changed in this diff Show More