commit 48b267cecd87ceb80a773e59fa623206fdc16265 Author: Melledy <121644117+Melledy@users.noreply.github.com> Date: Mon Sep 25 06:03:09 2023 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe94dd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,75 @@ +# Compiled class file +*.class + +#idea +*.idea +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.war +*.nar +*.ear +*.zip +*.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build/ +out/ + +# Ignore Gradle properties +gradle.properties + +# Eclipse +.project +.classpath +.settings +.metadata +.properties +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +.loadpath +.recommenders + +# VSCode +.vscode + +# lombok +/.apt_generated/ + +# macOS +.DS_Store +.directory + +# Lunar Rail generated/resource/log folders +src/generated +/resources +/logs + +# Lunar Rail compiled +/*.jar +/*.sh + +# Lunar Rail extra +Star Rail Handbook.txt +config.json +*.mv +*.exe +Test.java \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..50e7505 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Lunar Rail +A WIP server emulator for version 1.3.0 of a certain turn based anime game. + +# Running the server and client + +### Prerequisites +* Java 17 JDK + +### Recommended +* Mongodb (4.0+) + +### Starting up the server +1. Compile the server with `./gradlew jar` +2. Create a folder named `resources` in your server directory, you will need to downlaod `TextMap` and `ExcelBin` folders which you can get from a repo like [https://github.com/Dimbreath/StarRailData](https://github.com/Dimbreath/StarRailData) into your resources folder. +3. Run the server with `java -jar LunarRail.jar`. Lunar Rail comes with a built in internal mongo server for its database, so no Mongodb installation is required. However, it is highly recomended to install Mongodb anyways. + +### Connecting with the client +1. Login with the client to an official server at least once to download game data. +2. If you are using the provided keystore, you will need to install and have [Fiddler](https://www.telerik.com/fiddler) running. Make sure fiddler is set to decrypt https traffic. +3. Set your hosts file to redirect at least `hkrpg-sdk-os-static.hoyoverse.com` and `globaldp-prod-os01.starrails.com` to your http (dispatch) server ip. + +### Server console commands + +`/account create [username] {playerid}` - Creates an account with the specified username and the in-game uid for that account. The playerid parameter is optional and will be auto generated if not set. + +### In-Game commands +There is a dummy user named "Server" in every player's friends list that you can message to use commands. Commands also work in other chat rooms, such as private/team chats. + +`!spawn [monster id] [stage id]` + +`!give [item id] [amount]` \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..8a2597a --- /dev/null +++ b/build.gradle @@ -0,0 +1,148 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java project to get you started. + * For more details take a look at the Java Quickstart chapter in the Gradle + * User Manual available at https://docs.gradle.org/5.6.3/userguide/tutorial_java_projects.html + */ + +plugins { + // Apply the application plugin to add support for building a CLI application + id 'application' + + // Apply the java plugin to add support for Java + id 'java' + + // Protoc plugin + id 'com.google.protobuf' version '0.8.19' + + id 'eclipse' +} + +compileJava.options.encoding = "UTF-8" +compileTestJava.options.encoding = "UTF-8" + +sourceCompatibility = JavaVersion.VERSION_17 +targetCompatibility = JavaVersion.VERSION_17 + +repositories { + mavenCentral() + jcenter() +} + +protobuf { + protoc { + artifact = 'com.google.protobuf:protoc:3.24.3' + } + plugins { + quickbuf { + artifact = 'us.hebi.quickbuf:protoc-gen-quickbuf:1.3.1' + } + } + generateProtoTasks { + all().each { task -> + task.builtins { + remove java + } + task.plugins { + quickbuf { + option 'store_unknown_fields=true' + + outputSubDir = '' + } + } + } + } + generatedFilesBaseDir = "$projectDir/src/generated/" +} + +dependencies { + implementation fileTree(dir: 'lib', include: ['*.jar']) + + implementation group: 'org.slf4j', name: 'slf4j-api', version: '2.0.9' + implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.4.11' + implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.4.11' + + implementation group: 'it.unimi.dsi', name: 'fastutil-core', version: '8.5.12' + implementation group: 'org.reflections', name: 'reflections', version: '0.10.2' + + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + implementation group: 'us.hebi.quickbuf', name: 'quickbuf-runtime', version: '1.3.1' + + implementation group: 'io.javalin', name: 'javalin', version: '5.6.2' + + implementation group: 'io.netty', name: 'netty-common', version: '4.1.97.Final' + implementation group: 'io.netty', name: 'netty-handler', version: '4.1.97.Final' + implementation group: 'io.netty', name: 'netty-transport-native-epoll', version: '4.1.97.Final' + implementation group: 'io.netty', name: 'netty-transport-native-kqueue', version: '4.1.97.Final' + + implementation group: 'dev.morphia.morphia', name: 'morphia-core', version: '2.3.8' + implementation group: 'de.bwaldvogel', name: 'mongo-java-server', version: '1.44.0' + implementation group: 'de.bwaldvogel', name: 'mongo-java-server-h2-backend', version: '1.44.0' + + protobuf files('proto/') + + compileOnly 'org.projectlombok:lombok:1.18.30' + annotationProcessor 'org.projectlombok:lombok:1.18.30' +} + +configurations.all { + exclude group: 'org.slf4j', module: 'slf4j' +} + +clean { + delete protobuf.generatedFilesBaseDir +} + +application { + // Define the main class for the application + mainClassName = 'emu.lunarcore.LunarRail' +} + +jar { + exclude '*.proto' + + manifest { + attributes 'Main-Class': 'emu.lunarcore.LunarRail' + } + + jar { + archiveBaseName = 'LunarRail' + archiveVersion = '' + } + + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } + + duplicatesStrategy = DuplicatesStrategy.INCLUDE + + from('src/main/java') { + include '*.xml' + } + + getDestinationDirectory().set(file(".")) +} + +sourceSets { + main { + proto { + srcDir 'src/generated' + } + java { + srcDir 'src/main/java' + } + } +} + +eclipse { + classpath { + file.whenMerged { cp -> + cp.entries.add( new org.gradle.plugins.ide.eclipse.model.SourceFolder('src/generated/main/', null) ) + } + } +} + +processResources { + dependsOn "generateProto" +} \ No newline at end of file diff --git a/data/Banners.json b/data/Banners.json new file mode 100644 index 0000000..11d980b --- /dev/null +++ b/data/Banners.json @@ -0,0 +1,27 @@ +[ + { + "id": 1001, + "gachaType": "Normal", + "beginTime": 0, + "endTime": 0, + "rateUpItems5": [], + "rateUpItems4": [] + }, + { + "id": 2009, + "gachaType": "AvatarUp", + "beginTime": 0, + "endTime": 1924992000, + "rateUpItems5": [1213], + "rateUpItems4": [1207, 1001, 1009] + }, + { + "id": 3009, + "gachaType": "WeaponUp", + "beginTime": 0, + "endTime": 1924992000, + "eventChance": 75, + "rateUpItems5": [1208], + "rateUpItems4": [1106, 1109, 1110] + } +] \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..41d9927 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..fae0804 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/keystore.p12 b/keystore.p12 new file mode 100644 index 0000000..0637663 Binary files /dev/null and b/keystore.p12 differ diff --git a/lib/kcp.jar b/lib/kcp.jar new file mode 100644 index 0000000..9808b0a Binary files /dev/null and b/lib/kcp.jar differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f692034 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/5.6.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'Lunar Rail' diff --git a/src/main/java/emu/lunarcore/Config.java b/src/main/java/emu/lunarcore/Config.java new file mode 100644 index 0000000..0c5704d --- /dev/null +++ b/src/main/java/emu/lunarcore/Config.java @@ -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; + } + +} diff --git a/src/main/java/emu/lunarcore/GameConstants.java b/src/main/java/emu/lunarcore/GameConstants.java new file mode 100644 index 0000000..e7e1b38 --- /dev/null +++ b/src/main/java/emu/lunarcore/GameConstants.java @@ -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; +} diff --git a/src/main/java/emu/lunarcore/LunarRail.java b/src/main/java/emu/lunarcore/LunarRail.java new file mode 100644 index 0000000..8b13095 --- /dev/null +++ b/src/main/java/emu/lunarcore/LunarRail.java @@ -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; + } + } +} diff --git a/src/main/java/emu/lunarcore/commands/Command.java b/src/main/java/emu/lunarcore/commands/Command.java new file mode 100644 index 0000000..73302b5 --- /dev/null +++ b/src/main/java/emu/lunarcore/commands/Command.java @@ -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 ""; +} diff --git a/src/main/java/emu/lunarcore/commands/PlayerCommands.java b/src/main/java/emu/lunarcore/commands/PlayerCommands.java new file mode 100644 index 0000000..4da136d --- /dev/null +++ b/src/main/java/emu/lunarcore/commands/PlayerCommands.java @@ -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 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 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); + } + } + */ +} diff --git a/src/main/java/emu/lunarcore/commands/ServerCommands.java b/src/main/java/emu/lunarcore/commands/ServerCommands.java new file mode 100644 index 0000000..2c68578 --- /dev/null +++ b/src/main/java/emu/lunarcore/commands/ServerCommands.java @@ -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 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; + } + } + } + +} diff --git a/src/main/java/emu/lunarcore/data/GameData.java b/src/main/java/emu/lunarcore/data/GameData.java new file mode 100644 index 0000000..b0192a8 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/GameData.java @@ -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 avatarExcelMap = new Int2ObjectOpenHashMap<>(); + @Getter private static Int2ObjectMap itemExcelMap = new Int2ObjectOpenHashMap<>(); + @Getter private static Int2ObjectMap equipExcelMap = new Int2ObjectOpenHashMap<>(); + @Getter private static Int2ObjectMap relicExcelMap = new Int2ObjectOpenHashMap<>(); + @Getter private static Int2ObjectMap monsterExcelMap = new Int2ObjectOpenHashMap<>(); + @Getter private static Int2ObjectMap npcMonsterExcelMap = new Int2ObjectOpenHashMap<>(); + @Getter private static Int2ObjectMap stageExcelMap = new Int2ObjectOpenHashMap<>(); + @Getter private static Int2ObjectMap mapEntranceExcelMap = new Int2ObjectOpenHashMap<>(); + + private static Int2ObjectMap avatarPromotionExcelMap = new Int2ObjectOpenHashMap<>(); + private static Int2ObjectMap avatarSkillTreeExcelMap = new Int2ObjectOpenHashMap<>(); + private static Int2ObjectMap avatarRankExcelMap = new Int2ObjectOpenHashMap<>(); + private static Int2ObjectMap equipmentPromotionExcelMap = new Int2ObjectOpenHashMap<>(); + + private static Int2ObjectMap playerLevelExcelMap = new Int2ObjectOpenHashMap<>(); + private static Int2ObjectMap expTypeExcelMap = new Int2ObjectOpenHashMap<>(); + private static Int2ObjectMap equipmentExpTypeExcelMap = new Int2ObjectOpenHashMap<>(); + private static Int2ObjectMap relicExpTypeExcelMap = new Int2ObjectOpenHashMap<>(); + + @Getter + private static Int2ObjectMap relicMainAffixExcelMap = new Int2ObjectOpenHashMap<>(); + private static Int2ObjectMap relicSubAffixExcelMap = new Int2ObjectOpenHashMap<>(); + + // Configs (Bin) + @Getter private static Object2ObjectMap 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); + } +} diff --git a/src/main/java/emu/lunarcore/data/GameDepot.java b/src/main/java/emu/lunarcore/data/GameDepot.java new file mode 100644 index 0000000..44d3666 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/GameDepot.java @@ -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> relicMainAffixDepot = new Int2ObjectOpenHashMap<>(); + private static Int2ObjectMap> relicSubAffixDepot = new Int2ObjectOpenHashMap<>(); + + public static void addRelicMainAffix(RelicMainAffixExcel affix) { + List list = relicMainAffixDepot.computeIfAbsent(affix.getGroupID(), k -> new ArrayList<>()); + list.add(affix); + } + + public static void addRelicSubAffix(RelicSubAffixExcel affix) { + List 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 getRelicSubAffixList(int groupId) { + return relicSubAffixDepot.get(groupId); + } +} diff --git a/src/main/java/emu/lunarcore/data/GameResource.java b/src/main/java/emu/lunarcore/data/GameResource.java new file mode 100644 index 0000000..40fb2ab --- /dev/null +++ b/src/main/java/emu/lunarcore/data/GameResource.java @@ -0,0 +1,15 @@ +package emu.lunarcore.data; + +public abstract class GameResource implements Comparable { + + public abstract int getId(); + + public void onLoad() { + + } + + @Override + public int compareTo(GameResource o) { + return this.getId() - o.getId(); + } +} diff --git a/src/main/java/emu/lunarcore/data/ResourceDeserializers.java b/src/main/java/emu/lunarcore/data/ResourceDeserializers.java new file mode 100644 index 0000000..862db16 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/ResourceDeserializers.java @@ -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 { + @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 { + @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(); + } + } + } +} diff --git a/src/main/java/emu/lunarcore/data/ResourceLoader.java b/src/main/java/emu/lunarcore/data/ResourceLoader.java new file mode 100644 index 0000000..d22a570 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/ResourceLoader.java @@ -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> getResourceDefClasses() { + Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName()); + Set classes = reflections.getSubTypesOf(GameResource.class); + + List> 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 int loadFromResource(Class 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 stream = null; + + // Determine format of json + JsonElement json = JsonParser.parseReader(fileReader); + + if (json.isJsonArray()) { + // Parse list + List 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 excels = gson.fromJson(json, TypeToken.getParameterized(Map.class, Integer.class, c).getType()); + stream = excels.values().stream(); + } else { + // Nested Map + Map> 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."); + } +} diff --git a/src/main/java/emu/lunarcore/data/ResourceType.java b/src/main/java/emu/lunarcore/data/ResourceType.java new file mode 100644 index 0000000..92c8426 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/ResourceType.java @@ -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; + } + } +} diff --git a/src/main/java/emu/lunarcore/data/common/ItemParam.java b/src/main/java/emu/lunarcore/data/common/ItemParam.java new file mode 100644 index 0000000..069840f --- /dev/null +++ b/src/main/java/emu/lunarcore/data/common/ItemParam.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/data/config/AnchorInfo.java b/src/main/java/emu/lunarcore/data/config/AnchorInfo.java new file mode 100644 index 0000000..6122549 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/AnchorInfo.java @@ -0,0 +1,5 @@ +package emu.lunarcore.data.config; + +public class AnchorInfo extends ObjectInfo { + +} diff --git a/src/main/java/emu/lunarcore/data/config/FloorInfo.java b/src/main/java/emu/lunarcore/data/config/FloorInfo.java new file mode 100644 index 0000000..a282acb --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/FloorInfo.java @@ -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 SimpleGroupList; + + // Cached data + private transient boolean loaded; + private transient Int2ObjectMap groups; + private transient Int2ObjectMap 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; + } +} diff --git a/src/main/java/emu/lunarcore/data/config/GroupInfo.java b/src/main/java/emu/lunarcore/data/config/GroupInfo.java new file mode 100644 index 0000000..59f56be --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/GroupInfo.java @@ -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 AnchorList; + private List MonsterList; + private List PropList; + + public void setId(int id) { + if (this.id == 0) this.id = id; + } + + public static enum GroupLoadSide { + Client, Server; + } +} diff --git a/src/main/java/emu/lunarcore/data/config/MonsterInfo.java b/src/main/java/emu/lunarcore/data/config/MonsterInfo.java new file mode 100644 index 0000000..db3bb7e --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/MonsterInfo.java @@ -0,0 +1,9 @@ +package emu.lunarcore.data.config; + +import lombok.Getter; + +@Getter +public class MonsterInfo extends ObjectInfo { + private int NPCMonsterID; + private int EventID; +} diff --git a/src/main/java/emu/lunarcore/data/config/ObjectInfo.java b/src/main/java/emu/lunarcore/data/config/ObjectInfo.java new file mode 100644 index 0000000..bf50c13 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/ObjectInfo.java @@ -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)); + } +} diff --git a/src/main/java/emu/lunarcore/data/config/PropInfo.java b/src/main/java/emu/lunarcore/data/config/PropInfo.java new file mode 100644 index 0000000..1c20614 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/PropInfo.java @@ -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; +} diff --git a/src/main/java/emu/lunarcore/data/excel/AvatarExcel.java b/src/main/java/emu/lunarcore/data/excel/AvatarExcel.java new file mode 100644 index 0000000..7d45cd9 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/AvatarExcel.java @@ -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 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; + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/AvatarExpItemExcel.java b/src/main/java/emu/lunarcore/data/excel/AvatarExpItemExcel.java new file mode 100644 index 0000000..0a5653e --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/AvatarExpItemExcel.java @@ -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); + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/AvatarPromotionExcel.java b/src/main/java/emu/lunarcore/data/excel/AvatarPromotionExcel.java new file mode 100644 index 0000000..b0b6268 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/AvatarPromotionExcel.java @@ -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 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 it = this.PromotionCostList.iterator(); + while (it.hasNext()) { + ItemParam param = it.next(); + if (param.getId() == 2) { + this.PromotionCostCoin = param.getCount(); + it.remove(); + } + } + } + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/AvatarRankExcel.java b/src/main/java/emu/lunarcore/data/excel/AvatarRankExcel.java new file mode 100644 index 0000000..bba4254 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/AvatarRankExcel.java @@ -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 UnlockCost; + + @Override + public int getId() { + return RankID; + } + + @Override + public void onLoad() { + + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/AvatarSkillTreeExcel.java b/src/main/java/emu/lunarcore/data/excel/AvatarSkillTreeExcel.java new file mode 100644 index 0000000..e26567a --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/AvatarSkillTreeExcel.java @@ -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 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 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); + } + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/EquipmentExcel.java b/src/main/java/emu/lunarcore/data/excel/EquipmentExcel.java new file mode 100644 index 0000000..b59a99d --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/EquipmentExcel.java @@ -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); + } + } + +} diff --git a/src/main/java/emu/lunarcore/data/excel/EquipmentExpItemExcel.java b/src/main/java/emu/lunarcore/data/excel/EquipmentExpItemExcel.java new file mode 100644 index 0000000..1066f94 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/EquipmentExpItemExcel.java @@ -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); + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/EquipmentExpTypeExcel.java b/src/main/java/emu/lunarcore/data/excel/EquipmentExpTypeExcel.java new file mode 100644 index 0000000..8a3af7b --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/EquipmentExpTypeExcel.java @@ -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() { + + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/EquipmentPromotionExcel.java b/src/main/java/emu/lunarcore/data/excel/EquipmentPromotionExcel.java new file mode 100644 index 0000000..a47f008 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/EquipmentPromotionExcel.java @@ -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 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 it = this.PromotionCostList.iterator(); + while (it.hasNext()) { + ItemParam param = it.next(); + if (param.getId() == 2) { + this.PromotionCostCoin = param.getCount(); + it.remove(); + } + } + } + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/ExpTypeExcel.java b/src/main/java/emu/lunarcore/data/excel/ExpTypeExcel.java new file mode 100644 index 0000000..e15591e --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/ExpTypeExcel.java @@ -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() { + + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/ItemExcel.java b/src/main/java/emu/lunarcore/data/excel/ItemExcel.java new file mode 100644 index 0000000..c2efa7a --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/ItemExcel.java @@ -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 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; + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/MapEntranceExcel.java b/src/main/java/emu/lunarcore/data/excel/MapEntranceExcel.java new file mode 100644 index 0000000..aa50fc4 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/MapEntranceExcel.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/MonsterExcel.java b/src/main/java/emu/lunarcore/data/excel/MonsterExcel.java new file mode 100644 index 0000000..af25751 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/MonsterExcel.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/NpcMonsterExcel.java b/src/main/java/emu/lunarcore/data/excel/NpcMonsterExcel.java new file mode 100644 index 0000000..ec5805c --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/NpcMonsterExcel.java @@ -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() { + + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/PlayerLevelExcel.java b/src/main/java/emu/lunarcore/data/excel/PlayerLevelExcel.java new file mode 100644 index 0000000..056523c --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/PlayerLevelExcel.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/RelicExcel.java b/src/main/java/emu/lunarcore/data/excel/RelicExcel.java new file mode 100644 index 0000000..cbf17ba --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/RelicExcel.java @@ -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); + } + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/RelicExpItemExcel.java b/src/main/java/emu/lunarcore/data/excel/RelicExpItemExcel.java new file mode 100644 index 0000000..ba3d297 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/RelicExpItemExcel.java @@ -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); + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/RelicExpTypeExcel.java b/src/main/java/emu/lunarcore/data/excel/RelicExpTypeExcel.java new file mode 100644 index 0000000..03b6322 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/RelicExpTypeExcel.java @@ -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() { + + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/RelicMainAffixExcel.java b/src/main/java/emu/lunarcore/data/excel/RelicMainAffixExcel.java new file mode 100644 index 0000000..58c5617 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/RelicMainAffixExcel.java @@ -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); + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/RelicSubAffixExcel.java b/src/main/java/emu/lunarcore/data/excel/RelicSubAffixExcel.java new file mode 100644 index 0000000..367e469 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/RelicSubAffixExcel.java @@ -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); + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/StageExcel.java b/src/main/java/emu/lunarcore/data/excel/StageExcel.java new file mode 100644 index 0000000..d29acd2 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/StageExcel.java @@ -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() { + + } +} diff --git a/src/main/java/emu/lunarcore/database/DatabaseCounter.java b/src/main/java/emu/lunarcore/database/DatabaseCounter.java new file mode 100644 index 0000000..6a962f9 --- /dev/null +++ b/src/main/java/emu/lunarcore/database/DatabaseCounter.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/database/DatabaseManager.java b/src/main/java/emu/lunarcore/database/DatabaseManager.java new file mode 100644 index 0000000..ceeaad3 --- /dev/null +++ b/src/main/java/emu/lunarcore/database/DatabaseManager.java @@ -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 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 getObjectByUid(Class cls, long uid) { + return getDatastore().find(cls).filter(Filters.eq("_id", uid)).first(); + } + + public T getObjectByField(Class cls, String filter, String value) { + return getDatastore().find(cls).filter(Filters.eq(filter, value)).first(); + } + + public T getObjectByField(Class cls, String filter, int value) { + return getDatastore().find(cls).filter(Filters.eq(filter, value)).first(); + } + + public Stream getObjects(Class cls, String filter, long uid) { + return getDatastore().find(cls).filter(Filters.eq(filter, uid)).stream(); + } + + public Stream getObjects(Class cls) { + return getDatastore().find(cls).stream(); + } + + public void save(T obj) { + getDatastore().save(obj); + } + + public 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); + } + } +} diff --git a/src/main/java/emu/lunarcore/game/account/Account.java b/src/main/java/emu/lunarcore/game/account/Account.java new file mode 100644 index 0000000..b81f77f --- /dev/null +++ b/src/main/java/emu/lunarcore/game/account/Account.java @@ -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); + } +} diff --git a/src/main/java/emu/lunarcore/game/avatar/AvatarBaseType.java b/src/main/java/emu/lunarcore/game/avatar/AvatarBaseType.java new file mode 100644 index 0000000..47e3ccc --- /dev/null +++ b/src/main/java/emu/lunarcore/game/avatar/AvatarBaseType.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/avatar/AvatarPropertyType.java b/src/main/java/emu/lunarcore/game/avatar/AvatarPropertyType.java new file mode 100644 index 0000000..0840474 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/avatar/AvatarPropertyType.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/avatar/AvatarStorage.java b/src/main/java/emu/lunarcore/game/avatar/AvatarStorage.java new file mode 100644 index 0000000..e336fb4 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/avatar/AvatarStorage.java @@ -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 { + private final Int2ObjectMap avatars; + + public AvatarStorage(Player player) { + super(player); + this.avatars = new Int2ObjectOpenHashMap<>(); + } + + public Int2ObjectMap 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 iterator() { + return getAvatars().values().iterator(); + } + + // Database + + public void loadFromDatabase() { + Stream 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); + }); + } +} diff --git a/src/main/java/emu/lunarcore/game/avatar/DamageType.java b/src/main/java/emu/lunarcore/game/avatar/DamageType.java new file mode 100644 index 0000000..13a30f8 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/avatar/DamageType.java @@ -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; +} diff --git a/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java b/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java new file mode 100644 index 0000000..ae63731 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java @@ -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 skills; + + private transient int entityId; + private transient Int2ObjectMap 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); + } +} diff --git a/src/main/java/emu/lunarcore/game/battle/Battle.java b/src/main/java/emu/lunarcore/game/battle/Battle.java new file mode 100644 index 0000000..9a53dfb --- /dev/null +++ b/src/main/java/emu/lunarcore/game/battle/Battle.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/battle/BattleService.java b/src/main/java/emu/lunarcore/game/battle/BattleService.java new file mode 100644 index 0000000..80f01bf --- /dev/null +++ b/src/main/java/emu/lunarcore/game/battle/BattleService.java @@ -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 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())); + } + +} diff --git a/src/main/java/emu/lunarcore/game/gacha/GachaBanner.java b/src/main/java/emu/lunarcore/game/gacha/GachaBanner.java new file mode 100644 index 0000000..078dd52 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/gacha/GachaBanner.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/gacha/GachaService.java b/src/main/java/emu/lunarcore/game/gacha/GachaService.java new file mode 100644 index 0000000..a7c6259 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/gacha/GachaService.java @@ -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 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 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 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 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; + } +} diff --git a/src/main/java/emu/lunarcore/game/gacha/GachaType.java b/src/main/java/emu/lunarcore/game/gacha/GachaType.java new file mode 100644 index 0000000..16c45d6 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/gacha/GachaType.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/gacha/PlayerGachaBannerInfo.java b/src/main/java/emu/lunarcore/game/gacha/PlayerGachaBannerInfo.java new file mode 100644 index 0000000..bc63bcb --- /dev/null +++ b/src/main/java/emu/lunarcore/game/gacha/PlayerGachaBannerInfo.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/gacha/PlayerGachaInfo.java b/src/main/java/emu/lunarcore/game/gacha/PlayerGachaInfo.java new file mode 100644 index 0000000..35daddd --- /dev/null +++ b/src/main/java/emu/lunarcore/game/gacha/PlayerGachaInfo.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/EquipInventoryTab.java b/src/main/java/emu/lunarcore/game/inventory/EquipInventoryTab.java new file mode 100644 index 0000000..08f8671 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/EquipInventoryTab.java @@ -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 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 iterator() { + return items.iterator(); + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/GameItem.java b/src/main/java/emu/lunarcore/game/inventory/GameItem.java new file mode 100644 index 0000000..9a1037b --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/GameItem.java @@ -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 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 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 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()); + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/Inventory.java b/src/main/java/emu/lunarcore/game/inventory/Inventory.java new file mode 100644 index 0000000..67a5099 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/Inventory.java @@ -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 store; + private final Int2ObjectMap 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 getItems() { + return store; + } + + public Int2ObjectMap 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 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 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(); + } + } + }); + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/InventoryTab.java b/src/main/java/emu/lunarcore/game/inventory/InventoryTab.java new file mode 100644 index 0000000..8e3fc25 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/InventoryTab.java @@ -0,0 +1,13 @@ +package emu.lunarcore.game.inventory; + +public abstract class InventoryTab implements Iterable { + 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(); +} diff --git a/src/main/java/emu/lunarcore/game/inventory/ItemMainType.java b/src/main/java/emu/lunarcore/game/inventory/ItemMainType.java new file mode 100644 index 0000000..4ece63c --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/ItemMainType.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/ItemRarity.java b/src/main/java/emu/lunarcore/game/inventory/ItemRarity.java new file mode 100644 index 0000000..0b94169 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/ItemRarity.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/ItemSubAffix.java b/src/main/java/emu/lunarcore/game/inventory/ItemSubAffix.java new file mode 100644 index 0000000..85e8f38 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/ItemSubAffix.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/ItemSubType.java b/src/main/java/emu/lunarcore/game/inventory/ItemSubType.java new file mode 100644 index 0000000..b0c5e21 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/ItemSubType.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/MaterialInventoryTab.java b/src/main/java/emu/lunarcore/game/inventory/MaterialInventoryTab.java new file mode 100644 index 0000000..d2a07fc --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/MaterialInventoryTab.java @@ -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 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 iterator() { + return items.values().iterator(); + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/RelicType.java b/src/main/java/emu/lunarcore/game/inventory/RelicType.java new file mode 100644 index 0000000..7bc2c76 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/inventory/RelicType.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/player/BasePlayerManager.java b/src/main/java/emu/lunarcore/game/player/BasePlayerManager.java new file mode 100644 index 0000000..ed2b893 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/player/BasePlayerManager.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/player/LineupManager.java b/src/main/java/emu/lunarcore/game/player/LineupManager.java new file mode 100644 index 0000000..6cc4885 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/player/LineupManager.java @@ -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 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; + } + } +} diff --git a/src/main/java/emu/lunarcore/game/player/Player.java b/src/main/java/emu/lunarcore/game/player/Player.java new file mode 100644 index 0000000..53e07ce --- /dev/null +++ b/src/main/java/emu/lunarcore/game/player/Player.java @@ -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; + } + +} diff --git a/src/main/java/emu/lunarcore/game/player/PlayerLineup.java b/src/main/java/emu/lunarcore/game/player/PlayerLineup.java new file mode 100644 index 0000000..94306ba --- /dev/null +++ b/src/main/java/emu/lunarcore/game/player/PlayerLineup.java @@ -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 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; + } +} diff --git a/src/main/java/emu/lunarcore/game/scene/EntityMonster.java b/src/main/java/emu/lunarcore/game/scene/EntityMonster.java new file mode 100644 index 0000000..866394f --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/EntityMonster.java @@ -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; + } + +} diff --git a/src/main/java/emu/lunarcore/game/scene/EntityProp.java b/src/main/java/emu/lunarcore/game/scene/EntityProp.java new file mode 100644 index 0000000..4fee023 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/EntityProp.java @@ -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; + } + +} diff --git a/src/main/java/emu/lunarcore/game/scene/GameEntity.java b/src/main/java/emu/lunarcore/game/scene/GameEntity.java new file mode 100644 index 0000000..3770279 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/GameEntity.java @@ -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(); +} diff --git a/src/main/java/emu/lunarcore/game/scene/PropState.java b/src/main/java/emu/lunarcore/game/scene/PropState.java new file mode 100644 index 0000000..57b5917 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/PropState.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/game/scene/Scene.java b/src/main/java/emu/lunarcore/game/scene/Scene.java new file mode 100644 index 0000000..144a63b --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/Scene.java @@ -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 avatars; + + // Other entities TODO + private Int2ObjectMap 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(); + 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 toAdd = new ArrayList<>(); + List 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(); + + 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; + } +} diff --git a/src/main/java/emu/lunarcore/game/service/ChatService.java b/src/main/java/emu/lunarcore/game/service/ChatService.java new file mode 100644 index 0000000..4521ac5 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/service/ChatService.java @@ -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 + } +} diff --git a/src/main/java/emu/lunarcore/game/service/InventoryService.java b/src/main/java/emu/lunarcore/game/service/InventoryService.java new file mode 100644 index 0000000..dcce46c --- /dev/null +++ b/src/main/java/emu/lunarcore/game/service/InventoryService.java @@ -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 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 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 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 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 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 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 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 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)); + } +} diff --git a/src/main/java/emu/lunarcore/server/game/BaseGameService.java b/src/main/java/emu/lunarcore/server/game/BaseGameService.java new file mode 100644 index 0000000..7c83488 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/game/BaseGameService.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/server/game/GameServer.java b/src/main/java/emu/lunarcore/server/game/GameServer.java new file mode 100644 index 0000000..f5f9ea7 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/game/GameServer.java @@ -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 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(); + } +} diff --git a/src/main/java/emu/lunarcore/server/game/GameServerKcpListener.java b/src/main/java/emu/lunarcore/server/game/GameServerKcpListener.java new file mode 100644 index 0000000..8863d47 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/game/GameServerKcpListener.java @@ -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 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 + } +} diff --git a/src/main/java/emu/lunarcore/server/game/GameServerPacketHandler.java b/src/main/java/emu/lunarcore/server/game/GameServerPacketHandler.java new file mode 100644 index 0000000..7712bc8 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/game/GameServerPacketHandler.java @@ -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 handlers; + + public GameServerPacketHandler() { + this.handlers = new Int2ObjectOpenHashMap<>(); + + this.registerHandlers(); + } + + public void registerPacketHandler(Class 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) 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)); + } +} diff --git a/src/main/java/emu/lunarcore/server/game/GameSession.java b/src/main/java/emu/lunarcore/server/game/GameSession.java new file mode 100644 index 0000000..e140464 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/game/GameSession.java @@ -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(); + } +} diff --git a/src/main/java/emu/lunarcore/server/game/RegionInfo.java b/src/main/java/emu/lunarcore/server/game/RegionInfo.java new file mode 100644 index 0000000..5e7c9a8 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/game/RegionInfo.java @@ -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); + } +} diff --git a/src/main/java/emu/lunarcore/server/http/HttpServer.java b/src/main/java/emu/lunarcore/server/http/HttpServer.java new file mode 100644 index 0000000..ba28e40 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/HttpServer.java @@ -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\":\"崩�??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\":\"崩�??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"); + } +} diff --git a/src/main/java/emu/lunarcore/server/http/handlers/ComboTokenGranterHandler.java b/src/main/java/emu/lunarcore/server/http/handlers/ComboTokenGranterHandler.java new file mode 100644 index 0000000..8b99204 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/handlers/ComboTokenGranterHandler.java @@ -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)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/http/handlers/FingerprintHandler.java b/src/main/java/emu/lunarcore/server/http/handlers/FingerprintHandler.java new file mode 100644 index 0000000..78fcbfb --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/handlers/FingerprintHandler.java @@ -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)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/http/handlers/HttpJsonResponse.java b/src/main/java/emu/lunarcore/server/http/handlers/HttpJsonResponse.java new file mode 100644 index 0000000..ba2bf97 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/handlers/HttpJsonResponse.java @@ -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); + } +} diff --git a/src/main/java/emu/lunarcore/server/http/handlers/QueryDispatchHandler.java b/src/main/java/emu/lunarcore/server/http/handlers/QueryDispatchHandler.java new file mode 100644 index 0000000..27b3772 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/handlers/QueryDispatchHandler.java @@ -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())); + } + +} diff --git a/src/main/java/emu/lunarcore/server/http/handlers/QueryGatewayHandler.java b/src/main/java/emu/lunarcore/server/http/handlers/QueryGatewayHandler.java new file mode 100644 index 0000000..906e6d5 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/handlers/QueryGatewayHandler.java @@ -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())); + } + +} diff --git a/src/main/java/emu/lunarcore/server/http/handlers/TokenLoginHandler.java b/src/main/java/emu/lunarcore/server/http/handlers/TokenLoginHandler.java new file mode 100644 index 0000000..e429734 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/handlers/TokenLoginHandler.java @@ -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)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/http/handlers/UsernameLoginHandler.java b/src/main/java/emu/lunarcore/server/http/handlers/UsernameLoginHandler.java new file mode 100644 index 0000000..48535b7 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/handlers/UsernameLoginHandler.java @@ -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)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/http/objects/ComboTokenReqJson.java b/src/main/java/emu/lunarcore/server/http/objects/ComboTokenReqJson.java new file mode 100644 index 0000000..9265a3b --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/objects/ComboTokenReqJson.java @@ -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; + } +} diff --git a/src/main/java/emu/lunarcore/server/http/objects/ComboTokenResJson.java b/src/main/java/emu/lunarcore/server/http/objects/ComboTokenResJson.java new file mode 100644 index 0000000..29694ee --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/objects/ComboTokenResJson.java @@ -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; + } + } +} diff --git a/src/main/java/emu/lunarcore/server/http/objects/FingerprintReqJson.java b/src/main/java/emu/lunarcore/server/http/objects/FingerprintReqJson.java new file mode 100644 index 0000000..95d7338 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/objects/FingerprintReqJson.java @@ -0,0 +1,5 @@ +package emu.lunarcore.server.http.objects; + +public class FingerprintReqJson { + public String device_fp; +} diff --git a/src/main/java/emu/lunarcore/server/http/objects/FingerprintResJson.java b/src/main/java/emu/lunarcore/server/http/objects/FingerprintResJson.java new file mode 100644 index 0000000..5b99847 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/objects/FingerprintResJson.java @@ -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; + } + } +} diff --git a/src/main/java/emu/lunarcore/server/http/objects/LoginAccountReqJson.java b/src/main/java/emu/lunarcore/server/http/objects/LoginAccountReqJson.java new file mode 100644 index 0000000..ebdb860 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/objects/LoginAccountReqJson.java @@ -0,0 +1,7 @@ +package emu.lunarcore.server.http.objects; + +public class LoginAccountReqJson { + public String account; + public String password; + public boolean is_crypto; +} diff --git a/src/main/java/emu/lunarcore/server/http/objects/LoginResJson.java b/src/main/java/emu/lunarcore/server/http/objects/LoginResJson.java new file mode 100644 index 0000000..e0223ea --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/objects/LoginResJson.java @@ -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 = ""; + } +} diff --git a/src/main/java/emu/lunarcore/server/http/objects/LoginTokenReqJson.java b/src/main/java/emu/lunarcore/server/http/objects/LoginTokenReqJson.java new file mode 100644 index 0000000..48bf274 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/http/objects/LoginTokenReqJson.java @@ -0,0 +1,6 @@ +package emu.lunarcore.server.http.objects; + +public class LoginTokenReqJson { + public String uid; + public String token; +} diff --git a/src/main/java/emu/lunarcore/server/packet/BasePacket.java b/src/main/java/emu/lunarcore/server/packet/BasePacket.java new file mode 100644 index 0000000..4554937 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/BasePacket.java @@ -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(); + } + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/CmdId.java b/src/main/java/emu/lunarcore/server/packet/CmdId.java new file mode 100644 index 0000000..c9645f4 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/CmdId.java @@ -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; +} \ No newline at end of file diff --git a/src/main/java/emu/lunarcore/server/packet/CmdIdUtils.java b/src/main/java/emu/lunarcore/server/packet/CmdIdUtils.java new file mode 100644 index 0000000..0d4d9ae --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/CmdIdUtils.java @@ -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 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 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(); + } + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/Opcodes.java b/src/main/java/emu/lunarcore/server/packet/Opcodes.java new file mode 100644 index 0000000..7d38113 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/Opcodes.java @@ -0,0 +1,13 @@ +package emu.lunarcore.server.packet; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Opcodes { + /** Opcode for the packet/handler */ + int value(); + + /** HANDLER ONLY - will disable this handler from being registered */ + boolean disabled() default false; +} diff --git a/src/main/java/emu/lunarcore/server/packet/PacketHandler.java b/src/main/java/emu/lunarcore/server/packet/PacketHandler.java new file mode 100644 index 0000000..5b871d4 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/PacketHandler.java @@ -0,0 +1,9 @@ +package emu.lunarcore.server.packet; + +import emu.lunarcore.server.game.GameSession; + +public abstract class PacketHandler { + protected static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + public abstract void handle(GameSession session, byte[] header, byte[] data) throws Exception; +} diff --git a/src/main/java/emu/lunarcore/server/packet/SessionState.java b/src/main/java/emu/lunarcore/server/packet/SessionState.java new file mode 100644 index 0000000..9d6e2c2 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/SessionState.java @@ -0,0 +1,9 @@ +package emu.lunarcore.server.packet; + +public enum SessionState { + INACTIVE, + WAITING_FOR_TOKEN, + WAITING_FOR_LOGIN, + PICKING_CHARACTER, + ACTIVE +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/Handler.java b/src/main/java/emu/lunarcore/server/packet/recv/Handler.java new file mode 100644 index 0000000..2853453 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/Handler.java @@ -0,0 +1,16 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.NONE) +public class Handler extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + // Template - Do not delete! + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerAvatarExpUpCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerAvatarExpUpCsReq.java new file mode 100644 index 0000000..76cad4f --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerAvatarExpUpCsReq.java @@ -0,0 +1,29 @@ +package emu.lunarcore.server.packet.recv; + +import java.util.ArrayList; +import java.util.List; + +import emu.lunarcore.data.common.ItemParam; +import emu.lunarcore.proto.AvatarExpUpCsReqOuterClass.AvatarExpUpCsReq; +import emu.lunarcore.proto.ItemCostOuterClass.ItemCost; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.AvatarExpUpCsReq) +public class HandlerAvatarExpUpCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = AvatarExpUpCsReq.parseFrom(data); + + List items = new ArrayList<>(req.getItemCostList().getItemList().length()); + for (ItemCost cost : req.getItemCostList().getItemList()) { + items.add(new ItemParam(cost)); + } + + session.getServer().getInventoryService().levelUpAvatar(session.getPlayer(), req.getBaseAvatarId(), items); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerChangeLineupLeaderCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerChangeLineupLeaderCsReq.java new file mode 100644 index 0000000..3438b0a --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerChangeLineupLeaderCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.ChangeLineupLeaderCsReqOuterClass.ChangeLineupLeaderCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketChangeLineupLeaderScRsp; + +@Opcodes(CmdId.ChangeLineupLeaderCsReq) +public class HandlerChangeLineupLeaderCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = ChangeLineupLeaderCsReq.parseFrom(data); + + session.getPlayer().getLineupManager().changeLeader(req.getSlot()); + session.send(new PacketChangeLineupLeaderScRsp(req.getSlot())); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerDoGachaCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerDoGachaCsReq.java new file mode 100644 index 0000000..016c0ab --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerDoGachaCsReq.java @@ -0,0 +1,19 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.DoGachaCsReqOuterClass.DoGachaCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.DoGachaCsReq) +public class HandlerDoGachaCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = DoGachaCsReq.parseFrom(data); + + session.getServer().getGachaService().doPulls(session.getPlayer(), req.getGachaId(), req.getGachaNum()); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerDressAvatarCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerDressAvatarCsReq.java new file mode 100644 index 0000000..413c35e --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerDressAvatarCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.DressAvatarCsReqOuterClass.DressAvatarCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.DressAvatarCsReq) +public class HandlerDressAvatarCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = DressAvatarCsReq.parseFrom(data); + + session.getPlayer().getInventory().equipItem(req.getBaseAvatarId(), req.getEquipmentUniqueId()); + session.send(new BasePacket(CmdId.DressAvatarScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerDressRelicAvatarCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerDressRelicAvatarCsReq.java new file mode 100644 index 0000000..2fdb116 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerDressRelicAvatarCsReq.java @@ -0,0 +1,24 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.DressRelicAvatarCsReqOuterClass.DressRelicAvatarCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.DressRelicAvatarCsReq) +public class HandlerDressRelicAvatarCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = DressRelicAvatarCsReq.parseFrom(data); + + for (var param : req.getParamList()) { + session.getPlayer().getInventory().equipItem(req.getBaseAvatarId(), param.getRelicUniqueId()); + } + + session.send(new BasePacket(CmdId.DressRelicAvatarScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerEnterSceneCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerEnterSceneCsReq.java new file mode 100644 index 0000000..c2a71af --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerEnterSceneCsReq.java @@ -0,0 +1,20 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.EnterSceneCsReqOuterClass.EnterSceneCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.EnterSceneCsReq) +public class HandlerEnterSceneCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = EnterSceneCsReq.parseFrom(data); + + session.getPlayer().enterScene(req.getEntryId(), req.getTeleportId()); + session.send(CmdId.EnterSceneScRsp); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerEntityBindPropCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerEntityBindPropCsReq.java new file mode 100644 index 0000000..3af1152 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerEntityBindPropCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.EntityBindPropCsReq) +public class HandlerEntityBindPropCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new BasePacket(CmdId.EntityBindPropScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerExpUpEquipmentCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerExpUpEquipmentCsReq.java new file mode 100644 index 0000000..d040df3 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerExpUpEquipmentCsReq.java @@ -0,0 +1,29 @@ +package emu.lunarcore.server.packet.recv; + +import java.util.ArrayList; +import java.util.List; + +import emu.lunarcore.data.common.ItemParam; +import emu.lunarcore.proto.ExpUpEquipmentCsReqOuterClass.ExpUpEquipmentCsReq; +import emu.lunarcore.proto.ItemCostOuterClass.ItemCost; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.ExpUpEquipmentCsReq) +public class HandlerExpUpEquipmentCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = ExpUpEquipmentCsReq.parseFrom(data); + + List items = new ArrayList<>(req.getItemCostList().getItemList().length()); + for (ItemCost cost : req.getItemCostList().getItemList()) { + items.add(new ItemParam(cost)); + } + + session.getServer().getInventoryService().levelUpEquipment(session.getPlayer(), req.getEquipmentUniqueId(), items); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerExpUpRelicCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerExpUpRelicCsReq.java new file mode 100644 index 0000000..7516b38 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerExpUpRelicCsReq.java @@ -0,0 +1,29 @@ +package emu.lunarcore.server.packet.recv; + +import java.util.ArrayList; +import java.util.List; + +import emu.lunarcore.data.common.ItemParam; +import emu.lunarcore.proto.ExpUpRelicCsReqOuterClass.ExpUpRelicCsReq; +import emu.lunarcore.proto.ItemCostOuterClass.ItemCost; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.ExpUpRelicCsReq) +public class HandlerExpUpRelicCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = ExpUpRelicCsReq.parseFrom(data); + + List items = new ArrayList<>(req.getItemCostList().getItemList().length()); + for (ItemCost cost : req.getItemCostList().getItemList()) { + items.add(new ItemParam(cost)); + } + + session.getServer().getInventoryService().levelUpRelic(session.getPlayer(), req.getRelicUniqueId(), items); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAllLineupDataCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAllLineupDataCsReq.java new file mode 100644 index 0000000..5297425 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAllLineupDataCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetAllLineupDataScRsp; + +@Opcodes(CmdId.GetAllLineupDataCsReq) +public class HandlerGetAllLineupDataCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetAllLineupDataScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAssistHistoryCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAssistHistoryCsReq.java new file mode 100644 index 0000000..98e88d9 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAssistHistoryCsReq.java @@ -0,0 +1,16 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.GetAssistHistoryCsReq) +public class HandlerGetAssistHistoryCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(CmdId.GetAssistHistoryScRsp); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAvatarDataCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAvatarDataCsReq.java new file mode 100644 index 0000000..3eb4b56 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetAvatarDataCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetAvatarDataScRsp; + +@Opcodes(CmdId.GetAvatarDataCsReq) +public class HandlerGetAvatarDataCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetAvatarDataScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetBagCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetBagCsReq.java new file mode 100644 index 0000000..79d8a42 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetBagCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetBagScRsp; + +@Opcodes(CmdId.GetBagCsReq) +public class HandlerGetBagCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetBagScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetBasicInfoCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetBasicInfoCsReq.java new file mode 100644 index 0000000..5891dfc --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetBasicInfoCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetBasicInfoScRsp; + +@Opcodes(CmdId.GetBasicInfoCsReq) +public class HandlerGetBasicInfoCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetBasicInfoScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetChatEmojiListCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetChatEmojiListCsReq.java new file mode 100644 index 0000000..4220b20 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetChatEmojiListCsReq.java @@ -0,0 +1,16 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.GetChatEmojiListCsReq) +public class HandlerGetChatEmojiListCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(CmdId.GetChatEmojiListScRsp); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetCurLineupDataCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetCurLineupDataCsReq.java new file mode 100644 index 0000000..c1d7c8a --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetCurLineupDataCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetCurLineupDataScRsp; + +@Opcodes(CmdId.GetCurLineupDataCsReq) +public class HandlerGetCurLineupDataCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetCurLineupDataScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetCurSceneInfoCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetCurSceneInfoCsReq.java new file mode 100644 index 0000000..63d6875 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetCurSceneInfoCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetCurSceneInfoScRsp; + +@Opcodes(CmdId.GetCurSceneInfoCsReq) +public class HandlerGetCurSceneInfoCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetCurSceneInfoScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetFriendApplyListInfoCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetFriendApplyListInfoCsReq.java new file mode 100644 index 0000000..e640603 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetFriendApplyListInfoCsReq.java @@ -0,0 +1,16 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.GetFriendApplyListInfoCsReq) +public class HandlerGetFriendApplyListInfoCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(CmdId.GetFriendApplyListInfoScRsp); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetFriendListInfoCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetFriendListInfoCsReq.java new file mode 100644 index 0000000..6cb928c --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetFriendListInfoCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetFriendListInfoScRsp; + +@Opcodes(CmdId.GetFriendListInfoCsReq) +public class HandlerGetFriendListInfoCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetFriendListInfoScRsp()); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetGachaInfoCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetGachaInfoCsReq.java new file mode 100644 index 0000000..ba92c86 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetGachaInfoCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetGachaInfoScRsp; + +@Opcodes(CmdId.GetGachaInfoCsReq) +public class HandlerGetGachaInfoCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetGachaInfoScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetHeroBasicTypeInfoCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetHeroBasicTypeInfoCsReq.java new file mode 100644 index 0000000..cfb4acb --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetHeroBasicTypeInfoCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetHeroBasicTypeInfoScRsp; + +@Opcodes(CmdId.GetHeroBasicTypeInfoCsReq) +public class HandlerGetHeroBasicTypeInfoCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new PacketGetHeroBasicTypeInfoScRsp()); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetMailCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetMailCsReq.java new file mode 100644 index 0000000..753c65f --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetMailCsReq.java @@ -0,0 +1,16 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.GetMailCsReq) +public class HandlerGetMailCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(CmdId.GetMailScRsp); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetMissionStatusCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetMissionStatusCsReq.java new file mode 100644 index 0000000..6d2bcfc --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetMissionStatusCsReq.java @@ -0,0 +1,20 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.GetMissionStatusCsReqOuterClass.GetMissionStatusCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetMissionStatusScRsp; + +@Opcodes(CmdId.GetMissionStatusCsReq) +public class HandlerGetMissionStatusCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = GetMissionStatusCsReq.parseFrom(data); + + session.send(new PacketGetMissionStatusScRsp(req)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetPlayerDetailInfoCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetPlayerDetailInfoCsReq.java new file mode 100644 index 0000000..8ba94b1 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetPlayerDetailInfoCsReq.java @@ -0,0 +1,20 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.GetPlayerDetailInfoCsReqOuterClass.GetPlayerDetailInfoCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetPlayerDetailInfoScRsp; + +@Opcodes(CmdId.GetPlayerDetailInfoCsReq) +public class HandlerGetPlayerDetailInfoCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = GetPlayerDetailInfoCsReq.parseFrom(data); + + session.send(new PacketGetPlayerDetailInfoScRsp()); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetSceneMapInfoCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetSceneMapInfoCsReq.java new file mode 100644 index 0000000..3b8b600 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetSceneMapInfoCsReq.java @@ -0,0 +1,20 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.GetSceneMapInfoCsReqOuterClass.GetSceneMapInfoCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetSceneMapInfoScRsp; + +@Opcodes(CmdId.GetSceneMapInfoCsReq) +public class HandlerGetSceneMapInfoCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = GetSceneMapInfoCsReq.parseFrom(data); + + session.send(new PacketGetSceneMapInfoScRsp(req.getEntryIdList())); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetShopListCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetShopListCsReq.java new file mode 100644 index 0000000..4894fb4 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerGetShopListCsReq.java @@ -0,0 +1,20 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.GetShopListCsReqOuterClass.GetShopListCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketGetShopListScRsp; + +@Opcodes(CmdId.GetShopListCsReq) +public class HandlerGetShopListCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = GetShopListCsReq.parseFrom(data); + + session.send(new PacketGetShopListScRsp(req.getShopType())); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerJoinLineupCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerJoinLineupCsReq.java new file mode 100644 index 0000000..4197615 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerJoinLineupCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.JoinLineupCsReqOuterClass.JoinLineupCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.JoinLineupCsReq) +public class HandlerJoinLineupCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = JoinLineupCsReq.parseFrom(data); + + session.getPlayer().getLineupManager().joinLineup(req.getIndex(), req.getSlot(), req.getBaseAvatarId()); + session.send(new BasePacket(CmdId.JoinLineupScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerLockEquipmentCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerLockEquipmentCsReq.java new file mode 100644 index 0000000..64803d0 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerLockEquipmentCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.LockEquipmentCsReqOuterClass.LockEquipmentCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.LockEquipmentCsReq) +public class HandlerLockEquipmentCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = LockEquipmentCsReq.parseFrom(data); + + session.getServer().getInventoryService().lockEquip(session.getPlayer(), req.getEquipmentUniqueId(), req.getIsProtected()); + session.send(new BasePacket(CmdId.LockEquipmentScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerLockRelicCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerLockRelicCsReq.java new file mode 100644 index 0000000..e28c47a --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerLockRelicCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.LockRelicCsReqOuterClass.LockRelicCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.LockRelicCsReq) +public class HandlerLockRelicCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = LockRelicCsReq.parseFrom(data); + + session.getServer().getInventoryService().lockEquip(session.getPlayer(), req.getRelicUniqueId(), req.getIsProtected()); + session.send(new BasePacket(CmdId.LockRelicScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerPVEBattleResultCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPVEBattleResultCsReq.java new file mode 100644 index 0000000..c121a88 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPVEBattleResultCsReq.java @@ -0,0 +1,26 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.PVEBattleResultCsReqOuterClass.PVEBattleResultCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketPVEBattleResultScRsp; + +@Opcodes(CmdId.PVEBattleResultCsReq) +public class HandlerPVEBattleResultCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = PVEBattleResultCsReq.parseFrom(data); + + session.getServer().getBattleService().onBattleResult( + session.getPlayer(), + req.getEndStatus(), + req.getStt().getBattleAvatarList() + ); + + session.send(new PacketPVEBattleResultScRsp(req)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerGetTokenCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerGetTokenCsReq.java new file mode 100644 index 0000000..89962f9 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerGetTokenCsReq.java @@ -0,0 +1,53 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.LunarRail; +import emu.lunarcore.game.account.Account; +import emu.lunarcore.game.player.Player; +import emu.lunarcore.proto.PlayerGetTokenCsReqOuterClass.PlayerGetTokenCsReq; +import emu.lunarcore.server.game.GameSession; +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 emu.lunarcore.server.packet.send.PacketPlayerGetTokenScRsp; + +@Opcodes(CmdId.PlayerGetTokenCsReq) +public class HandlerPlayerGetTokenCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + // Parse packet data + var req = PlayerGetTokenCsReq.parseFrom(data); + + // Authenticate + Account account = LunarRail.getAccountDatabase().getObjectByField(Account.class, "_id", req.getUid()); + if (account == null || !account.getComboToken().equals(req.getToken())) { + return; + } + + // Set account object for session + session.setAccount(account); + + // Get player from database, if it doesnt exist, we create it + Player player = LunarRail.getGameDatabase().getObjectByField(Player.class, "accountUid", account.getUid()); + + if (player == null) { + player = new Player(session); + LunarRail.getGameDatabase().save(player); + } + + // Set player object for session + session.setPlayer(player); + + // Load player data from database + player.onLogin(); + + // Set session state + session.setUseSecretKey(true); + session.setState(SessionState.WAITING_FOR_LOGIN); + + // Finish and send packet + session.send(new PacketPlayerGetTokenScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerHeartBeatCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerHeartBeatCsReq.java new file mode 100644 index 0000000..505aacb --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerHeartBeatCsReq.java @@ -0,0 +1,22 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.PlayerHeartbeatCsReqOuterClass.PlayerHeartbeatCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketPlayerHeartBeatScRsp; + +@Opcodes(CmdId.PlayerHeartBeatCsReq) +public class HandlerPlayerHeartBeatCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + // Parse req data + var req = PlayerHeartbeatCsReq.parseFrom(data); + + // Send heartbeat response back + session.send(new PacketPlayerHeartBeatScRsp(req.getClientTimeMs())); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLoginCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLoginCsReq.java new file mode 100644 index 0000000..eb57e45 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLoginCsReq.java @@ -0,0 +1,22 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +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 emu.lunarcore.server.packet.send.PacketPlayerLoginScRsp; + +@Opcodes(CmdId.PlayerLoginCsReq) +public class HandlerPlayerLoginCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + // Set session flag + session.setState(SessionState.ACTIVE); + + // Send + session.send(new PacketPlayerLoginScRsp(session)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLoginFinishCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLoginFinishCsReq.java new file mode 100644 index 0000000..2348805 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLoginFinishCsReq.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.PlayerLoginFinishCsReq) +public class HandlerPlayerLoginFinishCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.send(new BasePacket(CmdId.PlayerLoginFinishScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLogoutCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLogoutCsReq.java new file mode 100644 index 0000000..7cdb966 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPlayerLogoutCsReq.java @@ -0,0 +1,16 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.PlayerLogoutCsReq) +public class HandlerPlayerLogoutCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + session.close(); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerPromoteAvatarCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPromoteAvatarCsReq.java new file mode 100644 index 0000000..351ab4a --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPromoteAvatarCsReq.java @@ -0,0 +1,19 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.PromoteAvatarCsReqOuterClass.PromoteAvatarCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.PromoteAvatarCsReq) +public class HandlerPromoteAvatarCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = PromoteAvatarCsReq.parseFrom(data); + + session.getServer().getInventoryService().promoteAvatar(session.getPlayer(), req.getBaseAvatarId()); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerPromoteEquipmentCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPromoteEquipmentCsReq.java new file mode 100644 index 0000000..1d38946 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerPromoteEquipmentCsReq.java @@ -0,0 +1,19 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.PromoteEquipmentCsReqOuterClass.PromoteEquipmentCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.PromoteEquipmentCsReq) +public class HandlerPromoteEquipmentCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = PromoteEquipmentCsReq.parseFrom(data); + + session.getServer().getInventoryService().promoteEquipment(session.getPlayer(), req.getEquipmentUniqueId()); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerQuitLineupCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerQuitLineupCsReq.java new file mode 100644 index 0000000..b71863e --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerQuitLineupCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.QuitLineupCsReqOuterClass.QuitLineupCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.QuitLineupCsReq) +public class HandlerQuitLineupCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = QuitLineupCsReq.parseFrom(data); + + session.getPlayer().getLineupManager().quitLineup(req.getIndex(), req.getBaseAvatarId()); + session.send(new BasePacket(CmdId.QuitLineupScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerRankUpAvatarCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerRankUpAvatarCsReq.java new file mode 100644 index 0000000..d0342ca --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerRankUpAvatarCsReq.java @@ -0,0 +1,19 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.RankUpAvatarCsReqOuterClass.RankUpAvatarCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.RankUpAvatarCsReq) +public class HandlerRankUpAvatarCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = RankUpAvatarCsReq.parseFrom(data); + + session.getServer().getInventoryService().rankUpAvatar(session.getPlayer(), req.getBaseAvatarId()); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerRankUpEquipmentCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerRankUpEquipmentCsReq.java new file mode 100644 index 0000000..13d7a9c --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerRankUpEquipmentCsReq.java @@ -0,0 +1,29 @@ +package emu.lunarcore.server.packet.recv; + +import java.util.ArrayList; +import java.util.List; + +import emu.lunarcore.data.common.ItemParam; +import emu.lunarcore.proto.ItemCostOuterClass.ItemCost; +import emu.lunarcore.proto.RankUpEquipmentCsReqOuterClass.RankUpEquipmentCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.RankUpEquipmentCsReq) +public class HandlerRankUpEquipmentCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = RankUpEquipmentCsReq.parseFrom(data); + + List items = new ArrayList<>(req.getItemCostList().getItemList().length()); + for (ItemCost cost : req.getItemCostList().getItemList()) { + items.add(new ItemParam(cost)); + } + + session.getServer().getInventoryService().rankUpEquipment(session.getPlayer(), req.getEquipmentUniqueId(), items); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerReplaceLineupCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerReplaceLineupCsReq.java new file mode 100644 index 0000000..586bf81 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerReplaceLineupCsReq.java @@ -0,0 +1,30 @@ +package emu.lunarcore.server.packet.recv; + +import java.util.ArrayList; +import java.util.List; + +import emu.lunarcore.proto.LineupSlotDataOuterClass.LineupSlotData; +import emu.lunarcore.proto.ReplaceLineupCsReqOuterClass.ReplaceLineupCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.ReplaceLineupCsReq) +public class HandlerReplaceLineupCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = ReplaceLineupCsReq.parseFrom(data); + + List lineupList = new ArrayList<>(req.getSlots().length()); + for (LineupSlotData slot : req.getSlots()) { + lineupList.add(slot.getId()); + } + + session.getPlayer().getLineupManager().replaceLineup(req.getIndex(), lineupList); + session.send(new BasePacket(CmdId.ReplaceLineupScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneCastSkillCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneCastSkillCsReq.java new file mode 100644 index 0000000..bf97eaa --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneCastSkillCsReq.java @@ -0,0 +1,24 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.SceneCastSkillCsReqOuterClass.SceneCastSkillCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketSceneCastSkillScRsp; + +@Opcodes(CmdId.SceneCastSkillCsReq) +public class HandlerSceneCastSkillCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SceneCastSkillCsReq.parseFrom(data); + + if (req.hasAttackedEntityIdList()) { + session.getServer().getBattleService().onBattleStart(session.getPlayer(), req.getAttackerId(), req.getAttackedEntityIdList()); + } else { + session.send(new PacketSceneCastSkillScRsp()); + } + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneEntityMoveCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneEntityMoveCsReq.java new file mode 100644 index 0000000..b9ebf08 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneEntityMoveCsReq.java @@ -0,0 +1,27 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.SceneEntityMoveCsReqOuterClass.SceneEntityMoveCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.SceneEntityMoveCsReq) +public class HandlerSceneEntityMoveCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SceneEntityMoveCsReq.parseFrom(data); + + for (var entityMotion : req.getEntityMotionList()) { + if (session.getPlayer().getScene().getAvatarEntityIds().contains(entityMotion.getEntityId())) { + var vec = entityMotion.getMotion().getPos(); + session.getPlayer().getPos().set(vec.getX(), vec.getY(), vec.getZ()); + } + } + + session.send(new BasePacket(CmdId.SceneEntityMoveScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSellItemCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSellItemCsReq.java new file mode 100644 index 0000000..f686ec0 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSellItemCsReq.java @@ -0,0 +1,29 @@ +package emu.lunarcore.server.packet.recv; + +import java.util.ArrayList; +import java.util.List; + +import emu.lunarcore.data.common.ItemParam; +import emu.lunarcore.proto.ItemCostOuterClass.ItemCost; +import emu.lunarcore.proto.SellItemCsReqOuterClass.SellItemCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.SellItemCsReq) +public class HandlerSellItemCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SellItemCsReq.parseFrom(data); + + List items = new ArrayList<>(req.getItemCostList().getItemList().length()); + for (ItemCost cost : req.getItemCostList().getItemList()) { + items.add(new ItemParam(cost)); + } + + session.getServer().getInventoryService().sellItems(session.getPlayer(), items); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSendMsgCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSendMsgCsReq.java new file mode 100644 index 0000000..285ed59 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSendMsgCsReq.java @@ -0,0 +1,29 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.MsgTypeOuterClass.MsgType; +import emu.lunarcore.proto.SendMsgCsReqOuterClass.SendMsgCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.SendMsgCsReq) +public class HandlerSendMsgCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SendMsgCsReq.parseFrom(data); + + for (int targetUid : req.getToUid()) { + if (req.getMsgType() == MsgType.MSG_TYPE_CUSTOM_TEXT) { + session.getServer().getChatService().sendPrivChat(session.getPlayer(), targetUid, req.getText()); + } else if (req.getMsgType() == MsgType.MSG_TYPE_EMOJI) { + session.getServer().getChatService().sendPrivChat(session.getPlayer(), targetUid, req.getEmote()); + } + } + + session.send(new BasePacket(CmdId.SendMsgScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetGameplayBirthdayCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetGameplayBirthdayCsReq.java new file mode 100644 index 0000000..5d9a24a --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetGameplayBirthdayCsReq.java @@ -0,0 +1,25 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.SetGameplayBirthdayCsReqOuterClass.SetGameplayBirthdayCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketSetGameplayBirthdayScRsp; + +@Opcodes(CmdId.SetGameplayBirthdayCsReq) +public class HandlerSetGameplayBirthdayCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SetGameplayBirthdayCsReq.parseFrom(data); + + int birthday = session.getPlayer().setBirthday(req.getBirthday()); + if (birthday != 0) { + session.send(new PacketSetGameplayBirthdayScRsp(birthday)); + } else { + session.send(new PacketSetGameplayBirthdayScRsp()); + } + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetLineupNameCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetLineupNameCsReq.java new file mode 100644 index 0000000..cbe7e14 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetLineupNameCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.SetLineupNameCsReqOuterClass.SetLineupNameCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketSetLineupNameScRsp; + +@Opcodes(CmdId.SetLineupNameCsReq) +public class HandlerSetLineupNameCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SetLineupNameCsReq.parseFrom(data); + + var success = session.getPlayer().getLineupManager().changeLineupName(req.getIndex(), req.getName()); + session.send(new PacketSetLineupNameScRsp(success ? req.getName() : null)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetNicknameCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetNicknameCsReq.java new file mode 100644 index 0000000..6bfca1c --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetNicknameCsReq.java @@ -0,0 +1,23 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.SetNicknameCsReqOuterClass.SetNicknameCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.SetNicknameCsReq) +public class HandlerSetNicknameCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SetNicknameCsReq.parseFrom(data); + + if (req.getNickname() != null) { + session.getPlayer().setNickname(req.getNickname()); + } + + session.send(CmdId.SetNicknameScRsp); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSwapLineupCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSwapLineupCsReq.java new file mode 100644 index 0000000..5a474df --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSwapLineupCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.SwapLineupCsReqOuterClass.SwapLineupCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.SwapLineupCsReq) +public class HandlerSwapLineupCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SwapLineupCsReq.parseFrom(data); + + session.getPlayer().getLineupManager().swapLineup(req.getIndex(), req.getSrcSlot(), req.getDstSlot()); + session.send(new BasePacket(CmdId.SwapLineupScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSwitchLineupIndexCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSwitchLineupIndexCsReq.java new file mode 100644 index 0000000..305f6fa --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSwitchLineupIndexCsReq.java @@ -0,0 +1,21 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.SwitchLineupIndexCsReqOuterClass.SwitchLineupIndexCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketSwitchLineupIndexScRsp; + +@Opcodes(CmdId.SwitchLineupIndexCsReq) +public class HandlerSwitchLineupIndexCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SwitchLineupIndexCsReq.parseFrom(data); + + session.getPlayer().getLineupManager().switchLineup(req.getIndex()); + session.send(new PacketSwitchLineupIndexScRsp(session.getPlayer().getLineupManager().getCurrentLineup())); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSyncClientResVersionCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSyncClientResVersionCsReq.java new file mode 100644 index 0000000..6484f3d --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSyncClientResVersionCsReq.java @@ -0,0 +1,20 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.SyncClientResVersionCsReqOuterClass.SyncClientResVersionCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; +import emu.lunarcore.server.packet.send.PacketSyncClientResVersionScRsp; + +@Opcodes(CmdId.SyncClientResVersionCsReq) +public class HandlerSyncClientResVersionCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = SyncClientResVersionCsReq.parseFrom(data); + + session.send(new PacketSyncClientResVersionScRsp(req.getClientResVersion())); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerTakeOffEquipmentCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerTakeOffEquipmentCsReq.java new file mode 100644 index 0000000..e4788ba --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerTakeOffEquipmentCsReq.java @@ -0,0 +1,22 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.GameConstants; +import emu.lunarcore.proto.TakeOffEquipmentCsReqOuterClass.TakeOffEquipmentCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.TakeOffEquipmentCsReq) +public class HandlerTakeOffEquipmentCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = TakeOffEquipmentCsReq.parseFrom(data); + + session.getPlayer().getInventory().unequipItem(req.getBaseAvatarId(), GameConstants.EQUIPMENT_SLOT_ID); + session.send(new BasePacket(CmdId.TakeOffEquipmentScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerTakeOffRelicCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerTakeOffRelicCsReq.java new file mode 100644 index 0000000..ead0912 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerTakeOffRelicCsReq.java @@ -0,0 +1,24 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.TakeOffRelicCsReqOuterClass.TakeOffRelicCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.TakeOffRelicCsReq) +public class HandlerTakeOffRelicCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = TakeOffRelicCsReq.parseFrom(data); + + for (int slot : req.getSlotList()) { + session.getPlayer().getInventory().unequipItem(req.getBaseAvatarId(), slot); + } + + session.send(new BasePacket(CmdId.TakeOffRelicScRsp)); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerUnlockSkilltreeCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerUnlockSkilltreeCsReq.java new file mode 100644 index 0000000..1e34473 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerUnlockSkilltreeCsReq.java @@ -0,0 +1,19 @@ +package emu.lunarcore.server.packet.recv; + +import emu.lunarcore.proto.UnlockSkilltreeCsReqOuterClass.UnlockSkilltreeCsReq; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.server.packet.Opcodes; +import emu.lunarcore.server.packet.PacketHandler; + +@Opcodes(CmdId.UnlockSkilltreeCsReq) +public class HandlerUnlockSkilltreeCsReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] data) throws Exception { + var req = UnlockSkilltreeCsReq.parseFrom(data); + + session.getServer().getInventoryService().unlockSkillTreeAvatar(session.getPlayer(), req.getPointId()); + } + +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/Packet.java b/src/main/java/emu/lunarcore/server/packet/send/Packet.java new file mode 100644 index 0000000..67ceb61 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/Packet.java @@ -0,0 +1,13 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class Packet extends BasePacket { + + public Packet() { + super(CmdId.NONE); + + // Template - Do not delete! + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketAvatarExpUpScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketAvatarExpUpScRsp.java new file mode 100644 index 0000000..02f0e4e --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketAvatarExpUpScRsp.java @@ -0,0 +1,31 @@ +package emu.lunarcore.server.packet.send; + +import java.util.Collection; + +import emu.lunarcore.game.inventory.GameItem; +import emu.lunarcore.proto.AvatarExpUpScRspOuterClass.AvatarExpUpScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketAvatarExpUpScRsp extends BasePacket { + + public PacketAvatarExpUpScRsp(Collection returnItems) { + super(CmdId.AvatarExpUpScRsp); + + var data = AvatarExpUpScRsp.newInstance(); + + for (GameItem item : returnItems) { + data.addReturnItemList(item.toPileProto()); + } + + this.setData(data); + } + + public PacketAvatarExpUpScRsp() { + super(CmdId.AvatarExpUpScRsp); + + var data = AvatarExpUpScRsp.newInstance().setRetcode(1); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketChangeLineupLeaderScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketChangeLineupLeaderScRsp.java new file mode 100644 index 0000000..373bd7a --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketChangeLineupLeaderScRsp.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.ChangeLineupLeaderScRspOuterClass.ChangeLineupLeaderScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketChangeLineupLeaderScRsp extends BasePacket { + + public PacketChangeLineupLeaderScRsp(int slot) { + super(CmdId.ChangeLineupLeaderScRsp); + + var data = ChangeLineupLeaderScRsp.newInstance() + .setSlot(slot); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketDoGachaScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketDoGachaScRsp.java new file mode 100644 index 0000000..1eaeb0e --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketDoGachaScRsp.java @@ -0,0 +1,32 @@ +package emu.lunarcore.server.packet.send; + +import java.util.List; + +import emu.lunarcore.game.gacha.GachaBanner; +import emu.lunarcore.proto.DoGachaScRspOuterClass.DoGachaScRsp; +import emu.lunarcore.proto.GachaItemOuterClass.GachaItem; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketDoGachaScRsp extends BasePacket { + + public PacketDoGachaScRsp() { + super(CmdId.DoGachaScRsp); + + this.setData(DoGachaScRsp.newInstance().setRetcode(1)); + } + + public PacketDoGachaScRsp(GachaBanner banner, int num, List items) { + super(CmdId.DoGachaScRsp); + + var data = DoGachaScRsp.newInstance() + .setGachaNum(num) + .setGachaId(banner.getId()); + + for (GachaItem item : items) { + data.addGachaItemList(item); + } + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketEnterSceneByServerScNotify.java b/src/main/java/emu/lunarcore/server/packet/send/PacketEnterSceneByServerScNotify.java new file mode 100644 index 0000000..220e1bd --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketEnterSceneByServerScNotify.java @@ -0,0 +1,19 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.game.player.Player; +import emu.lunarcore.proto.EnterSceneByServerScNotifyOuterClass.EnterSceneByServerScNotify; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketEnterSceneByServerScNotify extends BasePacket { + + public PacketEnterSceneByServerScNotify(Player player) { + super(CmdId.EnterSceneByServerScNotify); + + var data = EnterSceneByServerScNotify.newInstance() + .setLineup(player.getLineupManager().getCurrentLineup().toProto()) + .setScene(player.getScene().toProto()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketExpUpEquipmentScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketExpUpEquipmentScRsp.java new file mode 100644 index 0000000..19c5f42 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketExpUpEquipmentScRsp.java @@ -0,0 +1,31 @@ +package emu.lunarcore.server.packet.send; + +import java.util.Collection; + +import emu.lunarcore.game.inventory.GameItem; +import emu.lunarcore.proto.ExpUpEquipmentScRspOuterClass.ExpUpEquipmentScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketExpUpEquipmentScRsp extends BasePacket { + + public PacketExpUpEquipmentScRsp(Collection returnItems) { + super(CmdId.ExpUpEquipmentScRsp); + + var data = ExpUpEquipmentScRsp.newInstance(); + + for (GameItem item : returnItems) { + data.addReturnItemList(item.toPileProto()); + } + + this.setData(data); + } + + public PacketExpUpEquipmentScRsp() { + super(CmdId.ExpUpEquipmentScRsp); + + var data = ExpUpEquipmentScRsp.newInstance().setRetcode(1); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketExpUpRelicScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketExpUpRelicScRsp.java new file mode 100644 index 0000000..88b1e27 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketExpUpRelicScRsp.java @@ -0,0 +1,31 @@ +package emu.lunarcore.server.packet.send; + +import java.util.Collection; + +import emu.lunarcore.game.inventory.GameItem; +import emu.lunarcore.proto.ExpUpRelicScRspOuterClass.ExpUpRelicScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketExpUpRelicScRsp extends BasePacket { + + public PacketExpUpRelicScRsp(Collection returnItems) { + super(CmdId.ExpUpRelicScRsp); + + var data = ExpUpRelicScRsp.newInstance(); + + for (GameItem item : returnItems) { + data.addReturnItemList(item.toPileProto()); + } + + this.setData(data); + } + + public PacketExpUpRelicScRsp() { + super(CmdId.ExpUpRelicScRsp); + + var data = ExpUpRelicScRsp.newInstance().setRetcode(1); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetAllLineupDataScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetAllLineupDataScRsp.java new file mode 100644 index 0000000..32ecd4b --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetAllLineupDataScRsp.java @@ -0,0 +1,26 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.game.player.PlayerLineup; +import emu.lunarcore.game.player.LineupManager; +import emu.lunarcore.proto.GetAllLineupDataScRspOuterClass.GetAllLineupDataScRsp; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetAllLineupDataScRsp extends BasePacket { + + public PacketGetAllLineupDataScRsp(GameSession session) { + super(CmdId.GetAllLineupDataScRsp); + + LineupManager lineupManager = session.getPlayer().getLineupManager(); + + var data = GetAllLineupDataScRsp.newInstance() + .setCurIndex(session.getPlayer().getLineupManager().getCurrentIndex()); + + for (PlayerLineup lineup : lineupManager.getLineups()) { + data.addLineupList(lineup.toProto()); + } + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetAvatarDataScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetAvatarDataScRsp.java new file mode 100644 index 0000000..9494f2f --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetAvatarDataScRsp.java @@ -0,0 +1,23 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.proto.GetAvatarDataScRspOuterClass.GetAvatarDataScRsp; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetAvatarDataScRsp extends BasePacket { + + public PacketGetAvatarDataScRsp(GameSession session) { + super(CmdId.GetAvatarDataScRsp); + + var data = GetAvatarDataScRsp.newInstance() + .setIsGetAll(true); + + for (GameAvatar avatar : session.getPlayer().getAvatars()) { + data.addAvatarList(avatar.toProto()); + } + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetBagScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetBagScRsp.java new file mode 100644 index 0000000..b7c1c9f --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetBagScRsp.java @@ -0,0 +1,35 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.game.inventory.GameItem; +import emu.lunarcore.game.inventory.InventoryTab; +import emu.lunarcore.game.inventory.ItemMainType; +import emu.lunarcore.proto.GetBagScRspOuterClass.GetBagScRsp; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetBagScRsp extends BasePacket { + + public PacketGetBagScRsp(GameSession session) { + super(CmdId.GetBagScRsp); + + var data = GetBagScRsp.newInstance(); + + InventoryTab tabMaterial = session.getPlayer().getInventory().getInventoryTab(ItemMainType.Material); + for (GameItem item : tabMaterial) { + data.addMaterialList(item.toMaterialProto()); + } + + InventoryTab tabRelic = session.getPlayer().getInventory().getInventoryTab(ItemMainType.Relic); + for (GameItem item : tabRelic) { + data.addRelicList(item.toRelicProto()); + } + + InventoryTab tabEquipment = session.getPlayer().getInventory().getInventoryTab(ItemMainType.Equipment); + for (GameItem item : tabEquipment) { + data.addEquipmentList(item.toEquipmentProto()); + } + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetBasicInfoScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetBasicInfoScRsp.java new file mode 100644 index 0000000..c7fcc00 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetBasicInfoScRsp.java @@ -0,0 +1,22 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.GetBasicInfoScRspOuterClass.GetBasicInfoScRsp; +import emu.lunarcore.proto.PlayerSettingInfoOuterClass.PlayerSettingInfo; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetBasicInfoScRsp extends BasePacket { + + public PacketGetBasicInfoScRsp(GameSession session) { + super(CmdId.GetBasicInfoScRsp); + + var data = GetBasicInfoScRsp.newInstance() + .setCurDay(1) + .setNextRecoverTime(0) + .setGameplayBirthday(session.getPlayer().getBirthday()) + .setPlayerSettingInfo(PlayerSettingInfo.newInstance()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetCurLineupDataScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetCurLineupDataScRsp.java new file mode 100644 index 0000000..3a07a2d --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetCurLineupDataScRsp.java @@ -0,0 +1,18 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.GetCurLineupDataScRspOuterClass.GetCurLineupDataScRsp; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetCurLineupDataScRsp extends BasePacket { + + public PacketGetCurLineupDataScRsp(GameSession session) { + super(CmdId.GetCurLineupDataScRsp); + + var data = GetCurLineupDataScRsp.newInstance() + .setLineup(session.getPlayer().getLineupManager().getCurrentLineup().toProto()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetCurSceneInfoScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetCurSceneInfoScRsp.java new file mode 100644 index 0000000..5a7eadf --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetCurSceneInfoScRsp.java @@ -0,0 +1,18 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.GetCurSceneInfoScRspOuterClass.GetCurSceneInfoScRsp; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetCurSceneInfoScRsp extends BasePacket { + + public PacketGetCurSceneInfoScRsp(GameSession session) { + super(CmdId.GetCurSceneInfoScRsp); + + var data = GetCurSceneInfoScRsp.newInstance() + .setScene(session.getPlayer().getScene().toProto()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetFriendListInfoScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetFriendListInfoScRsp.java new file mode 100644 index 0000000..bd4f8c7 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetFriendListInfoScRsp.java @@ -0,0 +1,32 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.GameConstants; +import emu.lunarcore.proto.FriendAvatarInfoOuterClass.FriendAvatarInfo; +import emu.lunarcore.proto.FriendListInfoOuterClass.FriendListInfo; +import emu.lunarcore.proto.FriendOnlineStatusOuterClass.FriendOnlineStatus; +import emu.lunarcore.proto.GetFriendListInfoScRspOuterClass.GetFriendListInfoScRsp; +import emu.lunarcore.proto.PlatformTypeOuterClass.PlatformType; +import emu.lunarcore.proto.SimpleInfoOuterClass.SimpleInfo; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetFriendListInfoScRsp extends BasePacket { + + public PacketGetFriendListInfoScRsp() { + super(CmdId.GetFriendListInfoScRsp); + + var consoleFriend = SimpleInfo.newInstance() + .setNickname("Server") + .setLevel(1) + .setUid(GameConstants.SERVER_CONSOLE_UID) + .setOnlineStatus(FriendOnlineStatus.FRIEND_ONLINE_STATUS_ONLINE) + .setPlatformType(PlatformType.PC) + .setFriendAvatarInfo(FriendAvatarInfo.newInstance().setAvatarId(1001).setLevel(1)) + .setProfilePicture(201001); + + var data = GetFriendListInfoScRsp.newInstance() + .addFriendList(FriendListInfo.newInstance().setSimpleInfo(consoleFriend)); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetGachaInfoScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetGachaInfoScRsp.java new file mode 100644 index 0000000..2109abd --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetGachaInfoScRsp.java @@ -0,0 +1,14 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetGachaInfoScRsp extends BasePacket { + + public PacketGetGachaInfoScRsp(GameSession session) { + super(CmdId.GetGachaInfoScRsp); + + this.setData(session.getServer().getGachaService().toProto()); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetHeroBasicTypeInfoScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetHeroBasicTypeInfoScRsp.java new file mode 100644 index 0000000..fc466cf --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetHeroBasicTypeInfoScRsp.java @@ -0,0 +1,25 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.GenderOuterClass.Gender; +import emu.lunarcore.proto.GetHeroBasicTypeInfoScRspOuterClass.GetHeroBasicTypeInfoScRsp; +import emu.lunarcore.proto.HeroBasicTypeInfoOuterClass.HeroBasicTypeInfo; +import emu.lunarcore.proto.HeroBasicTypeOuterClass.HeroBasicType; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetHeroBasicTypeInfoScRsp extends BasePacket { + + public PacketGetHeroBasicTypeInfoScRsp() { + super(CmdId.GetHeroBasicTypeInfoScRsp); + + var heroBasicType = HeroBasicTypeInfo.newInstance() + .setBasicType(HeroBasicType.BoyWarrior); + + var data = GetHeroBasicTypeInfoScRsp.newInstance() + .setGender(Gender.GenderMan) + .setCurBasicType(HeroBasicType.BoyWarrior) + .addBasicTypeInfoList(heroBasicType); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetMissionStatusScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetMissionStatusScRsp.java new file mode 100644 index 0000000..1b8f56c --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetMissionStatusScRsp.java @@ -0,0 +1,31 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.GetMissionStatusCsReqOuterClass.GetMissionStatusCsReq; +import emu.lunarcore.proto.GetMissionStatusScRspOuterClass.GetMissionStatusScRsp; +import emu.lunarcore.proto.MissionOuterClass.Mission; +import emu.lunarcore.proto.MissionStatusOuterClass.MissionStatus; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetMissionStatusScRsp extends BasePacket { + + public PacketGetMissionStatusScRsp(GetMissionStatusCsReq req) { + super(CmdId.GetMissionStatusScRsp); + + var data = GetMissionStatusScRsp.newInstance(); + + for (int missionId : req.getMainMissionIdList()) { + data.addFinishedMainMissionIdList(missionId); + } + + for (int missionId : req.getSubMissionIdList()) { + var mission = Mission.newInstance() + .setId(missionId) + .setStatus(MissionStatus.MISSION_FINISH); + + data.addSubMissionStatusList(mission); + } + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetPlayerDetailInfoScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetPlayerDetailInfoScRsp.java new file mode 100644 index 0000000..6a12f64 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetPlayerDetailInfoScRsp.java @@ -0,0 +1,18 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.GetPlayerDetailInfoScRspOuterClass.GetPlayerDetailInfoScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetPlayerDetailInfoScRsp extends BasePacket { + + public PacketGetPlayerDetailInfoScRsp() { + super(CmdId.GetPlayerDetailInfoScRsp); + + // TODO handle properly + var data = GetPlayerDetailInfoScRsp.newInstance() + .setRetcode(1); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetSceneMapInfoScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetSceneMapInfoScRsp.java new file mode 100644 index 0000000..a49c932 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetSceneMapInfoScRsp.java @@ -0,0 +1,57 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.data.GameData; +import emu.lunarcore.data.config.FloorInfo; +import emu.lunarcore.data.config.GroupInfo; +import emu.lunarcore.data.excel.MapEntranceExcel; +import emu.lunarcore.proto.GetSceneMapInfoScRspOuterClass.GetSceneMapInfoScRsp; +import emu.lunarcore.proto.MapInfoChestTypeOuterClass.MapInfoChestType; +import emu.lunarcore.proto.MazeChestOuterClass.MazeChest; +import emu.lunarcore.proto.MazeGroupOuterClass.MazeGroup; +import emu.lunarcore.proto.MazeMapDataOuterClass.MazeMapData; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import us.hebi.quickbuf.RepeatedInt; + +public class PacketGetSceneMapInfoScRsp extends BasePacket { + + public PacketGetSceneMapInfoScRsp(RepeatedInt list) { + super(CmdId.GetSceneMapInfoScRsp); + + var data = GetSceneMapInfoScRsp.newInstance(); + + for (int entryId : list) { + var mazeMap = MazeMapData.newInstance() + .addUnlockedChestList(MazeChest.newInstance().setMapInfoChestType(MapInfoChestType.MAP_INFO_CHEST_TYPE_NORMAL)) + .addUnlockedChestList(MazeChest.newInstance().setMapInfoChestType(MapInfoChestType.MAP_INFO_CHEST_TYPE_PUZZLE)) + .addUnlockedChestList(MazeChest.newInstance().setMapInfoChestType(MapInfoChestType.MAP_INFO_CHEST_TYPE_CHALLENGE)) + .setEntryId(entryId); + + // Map sections. TODO un hardcode + for (int i = 0; i < 25; i++) { + mazeMap.addAllLightenSectionList(i); + } + + // Maze groups (Npc icons on the map, etc) + MapEntranceExcel excel = GameData.getMapEntranceExcelMap().get(entryId); + if (excel != null) { + FloorInfo floorInfo = GameData.getFloorInfo(excel.getPlaneID(), excel.getFloorID()); + if (floorInfo != null) { + // Add groups + for (GroupInfo groupInfo : floorInfo.getGroups().values()) { + var mazeGroup = MazeGroup.newInstance().setGroupId(groupInfo.getId()); + mazeMap.addMazeGroupList(mazeGroup); + } + // Map unlocked teleports + for (var teleport : floorInfo.getCachedTeleports().values()) { + mazeMap.addAllUnlockedTeleportList(teleport.getMappingInfoID()); + } + } + } + + data.addMapList(mazeMap); + } + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetShopListScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetShopListScRsp.java new file mode 100644 index 0000000..9a27cdc --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetShopListScRsp.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.GetShopListScRspOuterClass.GetShopListScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketGetShopListScRsp extends BasePacket { + + public PacketGetShopListScRsp(int shopType) { + super(CmdId.GetShopListScRsp); + + var data = GetShopListScRsp.newInstance() + .setShopType(shopType); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketPVEBattleResultScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketPVEBattleResultScRsp.java new file mode 100644 index 0000000..b874515 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketPVEBattleResultScRsp.java @@ -0,0 +1,27 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.ItemListOuterClass.ItemList; +import emu.lunarcore.proto.PVEBattleResultCsReqOuterClass.PVEBattleResultCsReq; +import emu.lunarcore.proto.PVEBattleResultScRspOuterClass.PVEBattleResultScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketPVEBattleResultScRsp extends BasePacket { + + public PacketPVEBattleResultScRsp(PVEBattleResultCsReq req) { + super(CmdId.PVEBattleResultScRsp); + + var data = PVEBattleResultScRsp.newInstance() + .setUnk1(ItemList.newInstance()) + .setUnk2(ItemList.newInstance()) + .setUnk3(ItemList.newInstance()) + .setResVersion(Integer.toString(req.getClientResVersion())) + .setBinVersion("") + .setBattleId(req.getBattleId()) + .setStageId(req.getStageId()) + .setEndStatus(req.getEndStatus()) + .setCheckIdentical(true); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerGetTokenScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerGetTokenScRsp.java new file mode 100644 index 0000000..e264540 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerGetTokenScRsp.java @@ -0,0 +1,20 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.BlackInfoOuterClass.BlackInfo; +import emu.lunarcore.proto.PlayerGetTokenScRspOuterClass.PlayerGetTokenScRsp; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketPlayerGetTokenScRsp extends BasePacket { + + public PacketPlayerGetTokenScRsp(GameSession session) { + super(CmdId.PlayerGetTokenScRsp); + + var data = PlayerGetTokenScRsp.newInstance() + .setUid(session.getUid()) + .setBlackInfo(BlackInfo.newInstance()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerHeartBeatScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerHeartBeatScRsp.java new file mode 100644 index 0000000..2363545 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerHeartBeatScRsp.java @@ -0,0 +1,18 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.PlayerHeartbeatScRspOuterClass.PlayerHeartbeatScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketPlayerHeartBeatScRsp extends BasePacket { + + public PacketPlayerHeartBeatScRsp(long clientTime) { + super(CmdId.PlayerHeartBeatScRsp); + + var data = PlayerHeartbeatScRsp.newInstance() + .setClientTimeMs(clientTime) + .setServerTimeMs(System.currentTimeMillis()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerLoginScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerLoginScRsp.java new file mode 100644 index 0000000..ad9cda3 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerLoginScRsp.java @@ -0,0 +1,22 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.GameConstants; +import emu.lunarcore.proto.PlayerLoginScRspOuterClass.PlayerLoginScRsp; +import emu.lunarcore.server.game.GameSession; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketPlayerLoginScRsp extends BasePacket { + + public PacketPlayerLoginScRsp(GameSession session) { + super(CmdId.PlayerLoginScRsp); + + var data = PlayerLoginScRsp.newInstance() + .setBasicInfo(session.getPlayer().toProto()) + .setCurTimezone(GameConstants.CURRENT_OFFSET.getTotalSeconds() / 60) + .setServerTimestampMs(System.currentTimeMillis()) + .setStamina(session.getPlayer().getStamina()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerSyncScNotify.java b/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerSyncScNotify.java new file mode 100644 index 0000000..a5ae09e --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketPlayerSyncScNotify.java @@ -0,0 +1,101 @@ +package emu.lunarcore.server.packet.send; + +import java.util.Collection; + +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.inventory.GameItem; +import emu.lunarcore.game.player.Player; +import emu.lunarcore.proto.AvatarSyncOuterClass.AvatarSync; +import emu.lunarcore.proto.PlayerSyncScNotifyOuterClass.PlayerSyncScNotify; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketPlayerSyncScNotify extends BasePacket { + + @Deprecated // This constructor does not create a proto + private PacketPlayerSyncScNotify() { + super(CmdId.PlayerSyncScNotify); + } + + public PacketPlayerSyncScNotify(Player player) { + this(); + + var data = PlayerSyncScNotify.newInstance() + .setBasicInfo(player.toProto()); + + this.setData(data); + } + + public PacketPlayerSyncScNotify(GameAvatar avatar) { + this(); + + var avatarSync = AvatarSync.newInstance() + .addAvatarList(avatar.toProto()); + + var data = PlayerSyncScNotify.newInstance() + .setAvatarSync(avatarSync); + + this.setData(data); + } + + public PacketPlayerSyncScNotify(GameAvatar avatar, GameItem item) { + this(); + + var avatarSync = AvatarSync.newInstance() + .addAvatarList(avatar.toProto()); + + var data = PlayerSyncScNotify.newInstance() + .setAvatarSync(avatarSync); + + this.addItemToProto(data, item); + + this.setData(data); + } + + public PacketPlayerSyncScNotify(GameItem item) { + this(); + + var data = PlayerSyncScNotify.newInstance(); + + this.addItemToProto(data, item); + + this.setData(data); + } + + public PacketPlayerSyncScNotify(Collection items) { + this(); + + var data = PlayerSyncScNotify.newInstance(); + + for (GameItem item : items) { + this.addItemToProto(data, item); + } + + this.setData(data); + } + + private void addItemToProto(PlayerSyncScNotify data, GameItem item) { + switch (item.getExcel().getItemMainType()) { + case Material -> { + data.addMaterialList(item.toMaterialProto()); + } + case Relic -> { + if (item.getCount() > 0) { + data.addRelicList(item.toRelicProto()); + } else { + data.addDelRelicList(item.getInternalUid()); + } + } + case Equipment -> { + if (item.getCount() > 0) { + data.addEquipmentList(item.toEquipmentProto()); + } else { + data.addDelEquipmentList(item.getInternalUid()); + } + } + default -> { + + } + } + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketRevcMsgScNotify.java b/src/main/java/emu/lunarcore/server/packet/send/PacketRevcMsgScNotify.java new file mode 100644 index 0000000..2c38ecc --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketRevcMsgScNotify.java @@ -0,0 +1,25 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.GameConstants; +import emu.lunarcore.game.player.Player; +import emu.lunarcore.proto.ChatTypeOuterClass.ChatType; +import emu.lunarcore.proto.MsgTypeOuterClass.MsgType; +import emu.lunarcore.proto.RevcMsgScNotifyOuterClass.RevcMsgScNotify; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketRevcMsgScNotify extends BasePacket { + + public PacketRevcMsgScNotify(Player player, String msg) { + super(CmdId.RevcMsgScNotify); + + var data = RevcMsgScNotify.newInstance() + .setText(msg) + .setChatType(ChatType.CHAT_TYPE_PRIVATE) + .setMsgType(MsgType.MSG_TYPE_CUSTOM_TEXT) + .setFromUid(GameConstants.SERVER_CONSOLE_UID) + .setToUid(player.getUid()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSceneCastSkillScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSceneCastSkillScRsp.java new file mode 100644 index 0000000..8f9c927 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSceneCastSkillScRsp.java @@ -0,0 +1,68 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.player.PlayerLineup; +import emu.lunarcore.game.player.Player; +import emu.lunarcore.game.scene.EntityMonster; +import emu.lunarcore.proto.SceneBattleInfoOuterClass.SceneBattleInfo; +import emu.lunarcore.proto.SceneCastSkillScRspOuterClass.SceneCastSkillScRsp; +import emu.lunarcore.proto.SceneMonsterOuterClass.SceneMonster; +import emu.lunarcore.proto.SceneMonsterWaveOuterClass.SceneMonsterWave; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import emu.lunarcore.util.Utils; + +public class PacketSceneCastSkillScRsp extends BasePacket { + + public PacketSceneCastSkillScRsp() { + this(0); + } + + public PacketSceneCastSkillScRsp(int retcode) { + super(CmdId.SceneCastSkillScRsp); + + var data = SceneCastSkillScRsp.newInstance() + .setRetcode(retcode); + + this.setData(data); + } + + // TODO + public PacketSceneCastSkillScRsp(Player player, EntityMonster monster) { + super(CmdId.SceneCastSkillScRsp); + + var wave = SceneMonsterWave.newInstance() + .setStageId(monster.getStage().getId()); + + int[] monsters = {101203002, 100202003, 100204007, 100205006}; + + for (int i = 0; i < 5; i++) { + var m = SceneMonster.newInstance() + .setMonsterId(Utils.randomElement(monsters)); + + wave.addMonsterList(m); + } + + var battle = SceneBattleInfo.newInstance() + .setStageId(monster.getStage().getId()) + .setLogicRandomSeed(Utils.randomRange(1, Short.MAX_VALUE)) + .addMonsterWaveList(wave) + .setWorldLevel(player.getWorldLevel()); + + // Avatars + PlayerLineup lineup = player.getLineupManager().getCurrentLineup(); + for (int i = 0; i < lineup.getAvatars().size(); i++) { + GameAvatar avatar = player.getAvatarById(lineup.getAvatars().get(i)); + if (avatar == null) continue; + + battle.addBattleAvatarList(avatar.toBattleProto(i)); + } + + // Build data + var data = SceneCastSkillScRsp.newInstance() + .setAttackedGroupId(monster.getGroupId()) + .setBattleInfo(battle); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSceneEntityUpdateScNotify.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSceneEntityUpdateScNotify.java new file mode 100644 index 0000000..f4dc7b5 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSceneEntityUpdateScNotify.java @@ -0,0 +1,18 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.game.scene.GameEntity; +import emu.lunarcore.proto.SceneEntityUpdateScNotifyOuterClass.SceneEntityUpdateScNotify; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketSceneEntityUpdateScNotify extends BasePacket { + + public PacketSceneEntityUpdateScNotify(GameEntity toAdd) { + super(CmdId.SceneEntityUpdateScNotify); + + var data = SceneEntityUpdateScNotify.newInstance() + .addEntityList(toAdd.toSceneEntityProto()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSceneGroupRefreshScNotify.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSceneGroupRefreshScNotify.java new file mode 100644 index 0000000..e2e856c --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSceneGroupRefreshScNotify.java @@ -0,0 +1,51 @@ +package emu.lunarcore.server.packet.send; + +import java.util.Collection; + +import emu.lunarcore.game.scene.GameEntity; +import emu.lunarcore.proto.SceneEntityRefreshInfoOuterClass.SceneEntityRefreshInfo; +import emu.lunarcore.proto.SceneGroupRefreshInfoOuterClass.SceneGroupRefreshInfo; +import emu.lunarcore.proto.SceneGroupRefreshScNotifyOuterClass.SceneGroupRefreshScNotify; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketSceneGroupRefreshScNotify extends BasePacket { + + public PacketSceneGroupRefreshScNotify(GameEntity toAdd, GameEntity toRemove) { + super(CmdId.SceneGroupRefreshScNotify); + + var group = SceneGroupRefreshInfo.newInstance().setGroupId(toAdd.getGroupId()); + + if (toAdd != null) { + group.addRefreshEntity(SceneEntityRefreshInfo.newInstance().setAddEntity(toAdd.toSceneEntityProto())); + } + + if (toRemove != null) { + group.addRefreshEntity(SceneEntityRefreshInfo.newInstance().setDelEntity(toRemove.getEntityId())); + } + + var data = SceneGroupRefreshScNotify.newInstance() + .addGroupRefreshInfo(group); + + this.setData(data); + } + + public PacketSceneGroupRefreshScNotify(Collection toAdd, Collection toRemove) { + super(CmdId.SceneGroupRefreshScNotify); + + var group = SceneGroupRefreshInfo.newInstance(); + + for (var entity : toAdd) { + group.addRefreshEntity(SceneEntityRefreshInfo.newInstance().setAddEntity(entity.toSceneEntityProto())); + } + + for (var entity : toRemove) { + group.addRefreshEntity(SceneEntityRefreshInfo.newInstance().setDelEntity(entity.getEntityId())); + } + + var data = SceneGroupRefreshScNotify.newInstance() + .addGroupRefreshInfo(group); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSellItemScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSellItemScRsp.java new file mode 100644 index 0000000..661403b --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSellItemScRsp.java @@ -0,0 +1,28 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.ItemListOuterClass.ItemList; +import emu.lunarcore.proto.ItemOuterClass.Item; +import emu.lunarcore.proto.SellItemScRspOuterClass.SellItemScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; +import it.unimi.dsi.fastutil.ints.Int2IntMap; + +public class PacketSellItemScRsp extends BasePacket { + + public PacketSellItemScRsp(Int2IntMap returnItems) { + super(CmdId.SellItemScRsp); + + var list = ItemList.newInstance(); + + if (returnItems != null) { + for (var item : returnItems.int2IntEntrySet()) { + list.addItemList(Item.newInstance().setItemId(item.getIntKey()).setNum(item.getIntValue())); + } + } + + var data = SellItemScRsp.newInstance() + .setReturnItemList(list); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSetGameplayBirthdayScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSetGameplayBirthdayScRsp.java new file mode 100644 index 0000000..1c721c4 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSetGameplayBirthdayScRsp.java @@ -0,0 +1,26 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.SetGameplayBirthdayScRspOuterClass.SetGameplayBirthdayScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketSetGameplayBirthdayScRsp extends BasePacket { + + public PacketSetGameplayBirthdayScRsp() { + super(CmdId.SetGameplayBirthdayScRsp); + + var data = SetGameplayBirthdayScRsp.newInstance() + .setRetcode(1); + + this.setData(data); + } + + public PacketSetGameplayBirthdayScRsp(int birthday) { + super(CmdId.SetGameplayBirthdayScRsp); + + var data = SetGameplayBirthdayScRsp.newInstance() + .setBirthday(birthday); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSetLineupNameScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSetLineupNameScRsp.java new file mode 100644 index 0000000..c1eb78a --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSetLineupNameScRsp.java @@ -0,0 +1,22 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.SetLineupNameScRspOuterClass.SetLineupNameScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketSetLineupNameScRsp extends BasePacket { + + public PacketSetLineupNameScRsp(String name) { + super(CmdId.SetLineupNameScRsp); + + var data = SetLineupNameScRsp.newInstance(); + + if (name != null) { + data.setName(name); + } else { + data.setRetcode(1); + } + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSwitchLineupIndexScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSwitchLineupIndexScRsp.java new file mode 100644 index 0000000..ebdaa4b --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSwitchLineupIndexScRsp.java @@ -0,0 +1,18 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.game.player.PlayerLineup; +import emu.lunarcore.proto.SwitchLineupIndexScRspOuterClass.SwitchLineupIndexScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketSwitchLineupIndexScRsp extends BasePacket { + + public PacketSwitchLineupIndexScRsp(PlayerLineup lineup) { + super(CmdId.SwitchLineupIndexScRsp); + + var data = SwitchLineupIndexScRsp.newInstance() + .setIndex(lineup.getIndex()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSyncClientResVersionScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSyncClientResVersionScRsp.java new file mode 100644 index 0000000..03b7e20 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSyncClientResVersionScRsp.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.SyncClientResVersionScRspOuterClass.SyncClientResVersionScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketSyncClientResVersionScRsp extends BasePacket { + + public PacketSyncClientResVersionScRsp(int res) { + super(CmdId.SyncClientResVersionScRsp); + + var data = SyncClientResVersionScRsp.newInstance() + .setClientResVersion(res); + + this.setData(data); + } +} \ No newline at end of file diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSyncLineupNotify.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSyncLineupNotify.java new file mode 100644 index 0000000..1d34089 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSyncLineupNotify.java @@ -0,0 +1,18 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.game.player.PlayerLineup; +import emu.lunarcore.proto.SyncLineupNotifyOuterClass.SyncLineupNotify; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketSyncLineupNotify extends BasePacket { + + public PacketSyncLineupNotify(PlayerLineup lineup) { + super(CmdId.SyncLineupNotify); + + var data = SyncLineupNotify.newInstance() + .setLineup(lineup.toProto()); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockSkilltreeScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockSkilltreeScRsp.java new file mode 100644 index 0000000..daedc03 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockSkilltreeScRsp.java @@ -0,0 +1,27 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.UnlockSkilltreeScRspOuterClass.UnlockSkilltreeScRsp; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketUnlockSkilltreeScRsp extends BasePacket { + + public PacketUnlockSkilltreeScRsp(int avatarId, int pointId, int level) { + super(CmdId.UnlockSkilltreeScRsp); + + var data = UnlockSkilltreeScRsp.newInstance() + .setBaseAvatarId(avatarId) + .setPointId(pointId) + .setLevel(level); + + this.setData(data); + } + + public PacketUnlockSkilltreeScRsp() { + super(CmdId.UnlockSkilltreeScRsp); + + var data = UnlockSkilltreeScRsp.newInstance().setRetcode(1); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/util/Crypto.java b/src/main/java/emu/lunarcore/util/Crypto.java new file mode 100644 index 0000000..63e0f31 --- /dev/null +++ b/src/main/java/emu/lunarcore/util/Crypto.java @@ -0,0 +1,25 @@ +package emu.lunarcore.util; + +import java.security.SecureRandom; + +import emu.lunarcore.LunarRail; + +public final class Crypto { + private static final SecureRandom secureRandom = new SecureRandom(); + + public static void xor(byte[] packet, byte[] key) { + try { + for (int i = 0; i < packet.length; i++) { + packet[i] ^= key[i % key.length]; + } + } catch (Exception e) { + LunarRail.getLogger().error("Crypto error.", e); + } + } + + public static byte[] createSessionKey(int length) { + byte[] bytes = new byte[length]; + secureRandom.nextBytes(bytes); + return bytes; + } +} diff --git a/src/main/java/emu/lunarcore/util/FileUtils.java b/src/main/java/emu/lunarcore/util/FileUtils.java new file mode 100644 index 0000000..98b623a --- /dev/null +++ b/src/main/java/emu/lunarcore/util/FileUtils.java @@ -0,0 +1,39 @@ +package emu.lunarcore.util; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import emu.lunarcore.LunarRail; + +public class FileUtils { + public static void write(String dest, byte[] bytes) { + Path path = Paths.get(dest); + + try { + Files.write(path, bytes); + } catch (IOException e) { + LunarRail.getLogger().warn("Failed to write file: " + dest); + } + } + + public static byte[] read(String dest) { + return read(Paths.get(dest)); + } + + public static byte[] read(Path path) { + try { + return Files.readAllBytes(path); + } catch (IOException e) { + LunarRail.getLogger().warn("Failed to read file: " + path); + } + + return new byte[0]; + } + + public static byte[] read(File file) { + return read(file.getPath()); + } +} diff --git a/src/main/java/emu/lunarcore/util/Handbook.java b/src/main/java/emu/lunarcore/util/Handbook.java new file mode 100644 index 0000000..2835c28 --- /dev/null +++ b/src/main/java/emu/lunarcore/util/Handbook.java @@ -0,0 +1,104 @@ +package emu.lunarcore.util; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + +import emu.lunarcore.GameConstants; +import emu.lunarcore.LunarRail; +import emu.lunarcore.data.GameData; +import emu.lunarcore.data.excel.*; + +public class Handbook { + + public static void generate() { + // Load text map + Map textMap = null; + List list = null; + + try { + textMap = JsonUtils.loadToMap(LunarRail.getConfig().getResourceDir() + "/TextMap/TextMapEN.json", Long.class, String.class); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + // Save to file + String file = "./Star Rail Handbook.txt"; + + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8), true)) { + // Format date for header + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); + LocalDateTime now = LocalDateTime.now(); + + // Header + writer.println("// Star Rail " + GameConstants.VERSION + " Handbook"); + writer.println("// Created " + dtf.format(now)); + + // Dump avatars + writer.println(System.lineSeparator()); + writer.println("// Avatars"); + list = GameData.getAvatarExcelMap().keySet().intStream().sorted().boxed().toList(); + for (int id : list) { + AvatarExcel excel = GameData.getAvatarExcelMap().get(id); + writer.print(excel.getId()); + writer.print(" : "); + writer.println(textMap.getOrDefault(excel.getAvatarName(), "null")); + } + + // Dump items + writer.println(System.lineSeparator()); + writer.println("// Items"); + list = GameData.getItemExcelMap().keySet().intStream().sorted().boxed().toList(); + for (int id : list) { + ItemExcel excel = GameData.getItemExcelMap().get(id); + writer.print(excel.getId()); + writer.print(" : "); + writer.println(textMap.getOrDefault(excel.getItemName(), "null")); + } + + // Dump npc monsters + writer.println(System.lineSeparator()); + writer.println("// NPC Monsters (Spawnable)"); + list = GameData.getNpcMonsterExcelMap().keySet().intStream().sorted().boxed().toList(); + for (int id : list) { + NpcMonsterExcel excel = GameData.getNpcMonsterExcelMap().get(id); + writer.print(excel.getId()); + writer.print(" : "); + writer.println(textMap.getOrDefault(excel.getNPCName(), "null")); + } + + // Dump stages + writer.println(System.lineSeparator()); + writer.println("// Stages"); + list = GameData.getStageExcelMap().keySet().intStream().sorted().boxed().toList(); + for (int id : list) { + StageExcel excel = GameData.getStageExcelMap().get(id); + writer.print(excel.getId()); + writer.print(" : "); + writer.print("[Level " + excel.getLevel() + "] "); + writer.println(textMap.getOrDefault(excel.getStageName(), "null")); + } + + // Dump monsters + writer.println(System.lineSeparator()); + writer.println("// Monsters"); + list = GameData.getMonsterExcelMap().keySet().intStream().sorted().boxed().toList(); + for (int id : list) { + MonsterExcel excel = GameData.getMonsterExcelMap().get(id); + writer.print(excel.getId()); + writer.print(" : "); + writer.println(textMap.getOrDefault(excel.getMonsterName(), "null")); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/emu/lunarcore/util/JsonUtils.java b/src/main/java/emu/lunarcore/util/JsonUtils.java new file mode 100644 index 0000000..2f9d6e4 --- /dev/null +++ b/src/main/java/emu/lunarcore/util/JsonUtils.java @@ -0,0 +1,108 @@ +package emu.lunarcore.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +import emu.lunarcore.Config; + +public class JsonUtils { + private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + private static final Gson gsonCompact = new GsonBuilder().create(); + + public static Gson getGsonFactory() { + return gson; + } + + /** + * Encode an object to a JSON string + */ + public static String encode(Object object) { + return gson.toJson(object); + } + + /** + * Encode an object to a JSON string + * @param object + * @param compact + * @return + */ + public static String encode(Object object, boolean compact) { + return compact ? gsonCompact.toJson(object) : gson.toJson(object); + } + + public static JsonElement encodeToElement(Object object) { + return gson.toJsonTree(object); + } + + /** + * Safely JSON decodes a given string. + * @param jsonData The JSON-encoded data. + * @return JSON decoded data, or null if an exception occurred. + */ + public static T decode(String jsonData, Class classType) { + try { + return gson.fromJson(jsonData, classType); + } catch (Exception ignored) { + return null; + } + } + + public static List decodeList(String jsonData, Class classType) { + if (jsonData == null) return null; + try { + return gson.fromJson(jsonData, TypeToken.getParameterized(List.class, classType).getType()); + } catch (Exception ignored) { + return null; + } + } + + public static Set decodeSet(String jsonData, Class classType) { + if (jsonData == null) return null; + try { + return gson.fromJson(jsonData, TypeToken.getParameterized(Set.class, classType).getType()); + } catch (Exception ignored) { + return null; + } + } + + public static T loadToClass(InputStreamReader fileReader, Class classType) throws IOException { + return gson.fromJson(fileReader, classType); + } + + public static T loadToClass(File file, Class classType) throws IOException { + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { + return loadToClass(fileReader, classType); + } + } + + public static List loadToList(InputStreamReader fileReader, Class classType) throws IOException { + return gson.fromJson(fileReader, TypeToken.getParameterized(List.class, classType).getType()); + } + + public static List loadToList(String filename, Class classType) throws IOException { + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(filename)), StandardCharsets.UTF_8)) { + return loadToList(fileReader, classType); + } + } + + public static Map loadToMap(InputStreamReader fileReader, Class keyType, Class valueType) throws IOException { + return gson.fromJson(fileReader, TypeToken.getParameterized(Map.class, keyType, valueType).getType()); + } + + public static Map loadToMap(String filename, Class keyType, Class valueType) throws IOException { + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(filename)), StandardCharsets.UTF_8)) { + return loadToMap(fileReader, keyType, valueType); + } + } +} diff --git a/src/main/java/emu/lunarcore/util/Position.java b/src/main/java/emu/lunarcore/util/Position.java new file mode 100644 index 0000000..829d8ce --- /dev/null +++ b/src/main/java/emu/lunarcore/util/Position.java @@ -0,0 +1,108 @@ +package emu.lunarcore.util; + +import dev.morphia.annotations.Entity; +import emu.lunarcore.proto.VectorOuterClass.Vector; + +@Entity(useDiscriminator = false) +public class Position { + private int x; + private int y; + private int z; + + public Position() { + + } + + public Position(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Position(Position position) { + this.x = position.getX(); + this.y = position.getY(); + this.z = position.getZ(); + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getZ() { + return z; + } + + public void setZ(int z) { + this.z = z; + } + + public void set(Position pos) { + this.x = pos.getX(); + this.y = pos.getY(); + this.z = pos.getZ(); + } + + public void set(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Position add(int x, int y, int z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + // Operations TODO + + public double get2dDist(Position pos) { + int x = this.getX() - pos.getX(); + int y = this.getY() - pos.getY(); + return Math.sqrt((x * x) + (y * y)); + } + + public int getFast2dDist(Position pos) { + int x = this.getX() - pos.getX(); + int y = this.getY() - pos.getY(); + return (x * x) + (y * y); + } + + public Vector toProto() { + return Vector.newInstance().setX(x).setY(y).setZ(z); + } + + // Overrides + + @Override + public Position clone() { + return new Position(getX(), getY(), getZ()); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Position pos) { + return getX() == pos.getX() && getY() == pos.getY() && getZ() == pos.getZ(); + } + return false; + } + + @Override + public String toString() { + return "[ " + this.getX() + " , " + this.getY() + " ]"; + } +} diff --git a/src/main/java/emu/lunarcore/util/Snowflake32.java b/src/main/java/emu/lunarcore/util/Snowflake32.java new file mode 100644 index 0000000..e5268cf --- /dev/null +++ b/src/main/java/emu/lunarcore/util/Snowflake32.java @@ -0,0 +1,25 @@ +package emu.lunarcore.util; + +public class Snowflake32 { + private static final long EPOCH = 1672531200000L; // Sunday, January 1, 2023 12:00:00 AM (GMT) + private static int cachedTimestamp; + private static byte sequence; + + public synchronized static int newUid() { + int timestamp = (int) ((System.currentTimeMillis() - EPOCH) / 1000); + + if (cachedTimestamp != timestamp) { + sequence = 0; + cachedTimestamp = timestamp; + } else { + sequence++; + } + + return (cachedTimestamp << 4) + sequence; + } + + public synchronized static int toTimestamp(int snowflake) { + return (snowflake >> 4) + (int) (EPOCH / 1000); + } + +} diff --git a/src/main/java/emu/lunarcore/util/Utils.java b/src/main/java/emu/lunarcore/util/Utils.java new file mode 100644 index 0000000..efb51da --- /dev/null +++ b/src/main/java/emu/lunarcore/util/Utils.java @@ -0,0 +1,148 @@ +package emu.lunarcore.util; + +import java.io.File; +import java.security.SecureRandom; +import java.util.Base64; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class Utils { + private static final SecureRandom secureRandom = new SecureRandom(); + private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); + + public static final Object EMPTY_OBJECT = new Object(); + public static final int[] EMPTY_ARRAY = new int[0]; + public static final String EMPTY_STRING = ""; + + public static String bytesToHex(byte[] bytes) { + if (bytes == null) return ""; + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + + public static byte[] hexToBytes(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + + public static String capitalize(String s) { + StringBuilder sb = new StringBuilder(s); + sb.setCharAt(0, Character.toUpperCase(sb.charAt(0))); + return sb.toString(); + } + + public static String lowerCaseFirstChar(String s) { + StringBuilder sb = new StringBuilder(s); + sb.setCharAt(0, Character.toLowerCase(sb.charAt(0))); + return sb.toString(); + } + + /** + * Creates a string with the path to a file. + * @param path The path to the file. + * @return A path using the operating system's file separator. + */ + public static String toFilePath(String path) { + return path.replace("/", File.separator); + } + + /** + * Checks if a file exists on the file system. + * @param path The path to the file. + * @return True if the file exists, false otherwise. + */ + public static boolean fileExists(String path) { + return new File(path).exists(); + } + + /** + * Creates a folder on the file system. + * @param path The path to the folder. + * @return True if the folder was created, false otherwise. + */ + public static boolean createFolder(String path) { + return new File(path).mkdirs(); + } + + public static long getCurrentSeconds() { + return Math.floorDiv(System.currentTimeMillis(), 1000); + } + + public static byte[] generateRandomBytes(int length) { + byte[] bytes = new byte[length]; + secureRandom.nextBytes(bytes); + return bytes; + } + + public static String generateRandomString(int length) { + return bytesToHex(generateRandomBytes(length)); + } + + public static int parseSafeInt(String s) { + int i = 0; + + try { + i = Integer.parseInt(s); + } catch (Exception e) { + i = 0; + } + + return i; + } + + public static long parseSafeLong(String s) { + long i = 0; + + try { + i = Long.parseLong(s); + } catch (Exception e) { + i = 0; + } + + return i; + } + + public static double generateRandomDouble() { + return ThreadLocalRandom.current().nextDouble(); + } + + public static int randomRange(int min, int max) { + return ThreadLocalRandom.current().nextInt(min, max + 1); + } + + public static int randomElement(int[] array) { + return array[ThreadLocalRandom.current().nextInt(0, array.length)]; + } + + public static T randomElement(List list) { + return list.get(ThreadLocalRandom.current().nextInt(0, list.size())); + } + + /** + * Base64 encodes a given byte array. + * @param toEncode An array of bytes. + * @return A base64 encoded string. + */ + public static String base64Encode(byte[] toEncode) { + return Base64.getEncoder().encodeToString(toEncode); + } + + /** + * Base64 decodes a given string. + * @param toDecode A base64 encoded string. + * @return An array of bytes. + */ + public static byte[] base64Decode(String toDecode) { + return Base64.getDecoder().decode(toDecode); + } +} diff --git a/src/main/java/emu/lunarcore/util/WeightedList.java b/src/main/java/emu/lunarcore/util/WeightedList.java new file mode 100644 index 0000000..aaf278f --- /dev/null +++ b/src/main/java/emu/lunarcore/util/WeightedList.java @@ -0,0 +1,35 @@ +package emu.lunarcore.util; + +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.ThreadLocalRandom; + +public class WeightedList { + private final NavigableMap map = new TreeMap<>(); + private double total = 0; + + public WeightedList() { + + } + + public WeightedList add(double weight, E result) { + if (weight <= 0) return this; + total += weight; + map.put(total, result); + return this; + } + + public E next() { + double value = ThreadLocalRandom.current().nextDouble() * total; + return map.higherEntry(value).getValue(); + } + + public int size() { + return map.size(); + } + + public void clear() { + this.map.clear(); + this.total = 0; + } +} \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..8f42963 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + [%d{HH:mm:ss}] [%level] %msg%n + + + + + + + + + + \ No newline at end of file