mirror of
https://github.com/Melledy/Nebula.git
synced 2026-09-20 01:29:54 +02:00
Initial Commit
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package emu.nebula;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
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 HttpServerConfig httpServer = new HttpServerConfig(80);
|
||||
public GameServerConfig gameServer = new GameServerConfig(80);
|
||||
|
||||
public ServerOptions serverOptions = new ServerOptions();
|
||||
public ServerRates serverRates = new ServerRates();
|
||||
public LogOptions logOptions = new LogOptions();
|
||||
|
||||
public String resourceDir = "./resources";
|
||||
public String dataDir = "./data";
|
||||
|
||||
@Getter
|
||||
public static class DatabaseInfo {
|
||||
public String uri = "mongodb://localhost:27017";
|
||||
public String collection = "nebula";
|
||||
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 = "";
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static class ServerConfig {
|
||||
public boolean useSSL = false;
|
||||
public String bindAddress = "0.0.0.0";
|
||||
public int bindPort;
|
||||
public String publicAddress = "127.0.0.1"; // Will return bindAddress if publicAddress is null
|
||||
public Integer publicPort; // Will return bindPort if publicPort is null
|
||||
|
||||
public ServerConfig(int port) {
|
||||
this.bindPort = port;
|
||||
}
|
||||
|
||||
public String getPublicAddress() {
|
||||
if (this.publicAddress != null && !this.publicAddress.isEmpty()) {
|
||||
return this.publicAddress;
|
||||
}
|
||||
|
||||
return this.bindAddress;
|
||||
}
|
||||
|
||||
public int getPublicPort() {
|
||||
if (this.publicPort != null && this.publicPort != 0) {
|
||||
return this.publicPort;
|
||||
}
|
||||
|
||||
return this.bindPort;
|
||||
}
|
||||
|
||||
public String getDisplayAddress() {
|
||||
return (useSSL ? "https" : "http") + "://" + getPublicAddress() + ":" + getPublicPort();
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class HttpServerConfig extends ServerConfig {
|
||||
|
||||
public HttpServerConfig(int port) {
|
||||
super(port);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class GameServerConfig extends ServerConfig {
|
||||
|
||||
public GameServerConfig(int port) {
|
||||
super(port);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class ServerOptions {
|
||||
public Set<String> defaultPermissions = Set.of("*");
|
||||
public boolean autoCreateAccount = true;
|
||||
public boolean skipIntro = false;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class ServerRates {
|
||||
public double exp = 1.0;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class LogOptions {
|
||||
public boolean commands = true;
|
||||
public boolean packets = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package emu.nebula;
|
||||
|
||||
public class GameConstants {
|
||||
public static final int DATA_VERSION = 22;
|
||||
public static final String VERSION = "1.0.0." + DATA_VERSION;
|
||||
|
||||
public static final String PROTO_BASE_TYPE_URL = "type.googleapis.com/proto.";
|
||||
|
||||
public static final int INTRO_GUIDE_ID = 1;
|
||||
|
||||
public static final int GOLD_ITEM_ID = 1;
|
||||
|
||||
public static final int MAX_FORMATIONS = 5;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package emu.nebula;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import emu.nebula.command.CommandManager;
|
||||
import emu.nebula.data.ResourceLoader;
|
||||
import emu.nebula.database.DatabaseManager;
|
||||
import emu.nebula.game.GameContext;
|
||||
import emu.nebula.server.HttpServer;
|
||||
import emu.nebula.util.Handbook;
|
||||
import emu.nebula.util.JsonUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
public class Nebula {
|
||||
private static final Logger log = LoggerFactory.getLogger(Nebula.class);
|
||||
|
||||
// Config
|
||||
private static final File configFile = new File("./config.json");
|
||||
@Getter private static Config config;
|
||||
|
||||
// Database
|
||||
@Getter private static DatabaseManager accountDatabase;
|
||||
@Getter private static DatabaseManager gameDatabase;
|
||||
|
||||
// Server
|
||||
@Getter private static HttpServer httpServer;
|
||||
@Getter private static HttpServer gameServer; // TODO
|
||||
@Getter private static ServerType serverType = ServerType.BOTH;
|
||||
|
||||
@Getter private static GameContext gameContext;
|
||||
@Getter private static CommandManager commandManager;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Start Server
|
||||
Nebula.getLogger().info("Starting Nebula " + getJarVersion());
|
||||
Nebula.getLogger().info("Git hash: " + getGitHash());
|
||||
Nebula.getLogger().info("Game version: " + GameConstants.VERSION);
|
||||
|
||||
boolean generateHandbook = true;
|
||||
|
||||
// Load config + commands
|
||||
Nebula.loadConfig();
|
||||
|
||||
// Parse arguments
|
||||
for (String arg : args) {
|
||||
switch (arg) {
|
||||
case "-login":
|
||||
serverType = ServerType.LOGIN;
|
||||
break;
|
||||
case "-game":
|
||||
serverType = ServerType.GAME;
|
||||
break;
|
||||
case "-nohandbook":
|
||||
case "-skiphandbook":
|
||||
generateHandbook = false;
|
||||
break;
|
||||
case "-database":
|
||||
// Database only
|
||||
DatabaseManager.startInternalMongoServer(Nebula.getConfig().getInternalMongoServer());
|
||||
Nebula.getLogger().info("Running local Mongo server at " + DatabaseManager.getServer().getConnectionString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip these if we are only running the http server in dispatch mode
|
||||
if (serverType.runGame()) {
|
||||
// Load resources
|
||||
ResourceLoader.loadAll();
|
||||
// Generate handbook
|
||||
if (generateHandbook) {
|
||||
Handbook.generate();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Start Database(s)
|
||||
Nebula.initDatabases();
|
||||
} catch (Exception exception) {
|
||||
Nebula.getLogger().error("Unable to start the database(s).", exception);
|
||||
}
|
||||
|
||||
// Start game context
|
||||
Nebula.gameContext = new GameContext();
|
||||
Nebula.commandManager = new CommandManager();
|
||||
|
||||
// Start servers
|
||||
try {
|
||||
// Always run http server as it is needed by for dispatch and gateserver
|
||||
httpServer = new HttpServer(serverType);
|
||||
httpServer.start();
|
||||
} catch (Exception exception) {
|
||||
Nebula.getLogger().error("Unable to start the HTTP server.", exception);
|
||||
}
|
||||
|
||||
// Start console
|
||||
Nebula.startConsole();
|
||||
}
|
||||
|
||||
public static Logger getLogger() {
|
||||
return log;
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
private static void initDatabases() {
|
||||
if (Nebula.getConfig().useSameDatabase) {
|
||||
// Setup account and game database
|
||||
accountDatabase = new DatabaseManager(Nebula.getConfig().getAccountDatabase(), serverType);
|
||||
// Optimization: Dont run a 2nd database manager if we are not running a gameserver
|
||||
if (serverType.runGame()) {
|
||||
gameDatabase = accountDatabase;
|
||||
}
|
||||
} else {
|
||||
// Run separate databases
|
||||
accountDatabase = new DatabaseManager(Nebula.getConfig().getAccountDatabase(), ServerType.LOGIN);
|
||||
// Optimization: Dont run a 2nd database manager if we are not running a gameserver
|
||||
if (serverType.runGame()) {
|
||||
gameDatabase = new DatabaseManager(Nebula.getConfig().getGameDatabase(), ServerType.GAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Config
|
||||
|
||||
public static void loadConfig() {
|
||||
// Load from file
|
||||
try (FileReader file = new FileReader(configFile)) {
|
||||
Nebula.config = JsonUtils.loadToClass(file, Config.class);
|
||||
} catch (Exception e) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (Nebula.getConfig() == null) {
|
||||
Nebula.config = new Config();
|
||||
}
|
||||
|
||||
// Save config
|
||||
Nebula.saveConfig();
|
||||
}
|
||||
|
||||
public static void saveConfig() {
|
||||
try (FileWriter file = new FileWriter(configFile)) {
|
||||
Gson gson = new GsonBuilder()
|
||||
.setDateFormat("dd-MM-yyyy hh:mm:ss")
|
||||
.setPrettyPrinting()
|
||||
.serializeNulls()
|
||||
.create();
|
||||
|
||||
file.write(gson.toJson(config));
|
||||
} catch (Exception e) {
|
||||
getLogger().error("Config save error");
|
||||
}
|
||||
}
|
||||
|
||||
// Build Config
|
||||
|
||||
private static String getJarVersion() {
|
||||
// Safely get the build config class without errors even if it hasnt been generated yet
|
||||
try {
|
||||
Class<?> buildConfig = Class.forName(Nebula.class.getPackageName() + ".BuildConfig");
|
||||
return buildConfig.getField("VERSION").get(null).toString();
|
||||
} catch (Exception e) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String getGitHash() {
|
||||
// Use a string builder in case one of the build config fields are missing
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
// Safely get the build config class without errors even if it hasnt been generated yet
|
||||
try {
|
||||
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Class<?> buildConfig = Class.forName(Nebula.class.getPackageName() + ".BuildConfig");
|
||||
|
||||
String hash = buildConfig.getField("GIT_HASH").get(null).toString();
|
||||
builder.append(hash);
|
||||
|
||||
String timestamp = buildConfig.getField("GIT_TIMESTAMP").get(null).toString();
|
||||
long time = Long.parseLong(timestamp) * 1000;
|
||||
builder.append(" (" + sf.format(new Date(time)) + ")");
|
||||
} catch (Exception e) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
if (builder.isEmpty()) {
|
||||
return "UNKNOWN";
|
||||
} else {
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Utils
|
||||
|
||||
/**
|
||||
* Returns the current server time in seconds
|
||||
*/
|
||||
public static long getCurrentTime() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
// Console
|
||||
|
||||
private static void startConsole() {
|
||||
String input;
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
|
||||
while ((input = br.readLine()) != null) {
|
||||
Nebula.getCommandManager().invoke(null, input);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Nebula.getLogger().error("Console error:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Server enums
|
||||
|
||||
public enum ServerType {
|
||||
LOGIN (0x1),
|
||||
GAME (0x2),
|
||||
BOTH (0x3);
|
||||
|
||||
private final int flags;
|
||||
|
||||
ServerType(int flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public boolean runLogin() {
|
||||
return (this.flags & 0x1) == 0x1;
|
||||
}
|
||||
|
||||
public boolean runGame() {
|
||||
return (this.flags & 0x2) == 0x2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.command;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Command {
|
||||
public String label() default "";
|
||||
|
||||
public String[] aliases() default "";
|
||||
|
||||
public String desc() default "";
|
||||
|
||||
public String permission() default "";
|
||||
|
||||
public boolean requireTarget() default false;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package emu.nebula.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class CommandArgs {
|
||||
private String raw;
|
||||
private List<String> list;
|
||||
private Player sender;
|
||||
private Player target;
|
||||
|
||||
private int targetUid;
|
||||
private int amount;
|
||||
private int level = -1;
|
||||
private int rank = -1;
|
||||
private int promotion = -1;
|
||||
private int stage = -1;
|
||||
|
||||
private Int2IntMap map;
|
||||
private ObjectSet<String> flags;
|
||||
|
||||
public CommandArgs(Player sender, List<String> args) {
|
||||
this.sender = sender;
|
||||
this.raw = String.join(" ", args);
|
||||
this.list = args;
|
||||
|
||||
// Parse args. Maybe regex is better.
|
||||
var it = this.list.iterator();
|
||||
while (it.hasNext()) {
|
||||
// Lower case first
|
||||
String arg = it.next().toLowerCase();
|
||||
|
||||
try {
|
||||
if (arg.length() >= 2 && !Character.isDigit(arg.charAt(0)) && Character.isDigit(arg.charAt(arg.length() - 1))) {
|
||||
if (arg.startsWith("@")) { // Target UID
|
||||
this.targetUid = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("x")) { // Amount
|
||||
this.amount = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("lv")) { // Level
|
||||
this.level = Utils.parseSafeInt(arg.substring(2));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("r")) { // Rank
|
||||
this.rank = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("e")) { // Eidolons
|
||||
this.rank = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("p")) { // Promotion
|
||||
this.promotion = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("s")) { // Stage or Superimposition
|
||||
this.stage = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
}
|
||||
} else if (arg.startsWith("-")) { // Flag
|
||||
if (this.flags == null) this.flags = new ObjectOpenHashSet<>();
|
||||
this.flags.add(arg);
|
||||
it.remove();
|
||||
} else if (arg.contains(":") || arg.contains(",")) {
|
||||
String[] split = arg.split("[:,]");
|
||||
if (split.length >= 2) {
|
||||
int key = Integer.parseInt(split[0]);
|
||||
int value = Integer.parseInt(split[1]);
|
||||
|
||||
if (this.map == null) this.map = new Int2IntOpenHashMap();
|
||||
this.map.put(key, value);
|
||||
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Get target player
|
||||
if (targetUid != 0) {
|
||||
if (Nebula.getGameContext() != null) {
|
||||
target = Nebula.getGameContext().getPlayerModule().getCachedPlayerByUid(targetUid);
|
||||
}
|
||||
} else {
|
||||
target = sender;
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
this.targetUid = target.getUid();
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.list.size();
|
||||
}
|
||||
|
||||
public String get(int index) {
|
||||
if (index < 0 || index >= list.size()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return this.list.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the command sender
|
||||
* @param message
|
||||
*/
|
||||
public void sendMessage(String message) {
|
||||
if (sender != null) {
|
||||
sender.sendMessage(message);
|
||||
} else {
|
||||
Nebula.getLogger().info(message);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasFlag(String flag) {
|
||||
if (this.flags == null) return false;
|
||||
return this.flags.contains(flag);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package emu.nebula.command;
|
||||
|
||||
public interface CommandHandler {
|
||||
|
||||
public default Command getData() {
|
||||
return this.getClass().getAnnotation(Command.class);
|
||||
}
|
||||
|
||||
public default String getLabel() {
|
||||
return getData().label();
|
||||
}
|
||||
|
||||
public void execute(CommandArgs args);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package emu.nebula.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.player.Player;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class CommandManager {
|
||||
private Object2ObjectMap<String, CommandHandler> labels;
|
||||
private Object2ObjectMap<String, CommandHandler> commands;
|
||||
|
||||
public CommandManager() {
|
||||
this.labels = new Object2ObjectOpenHashMap<>();
|
||||
this.commands = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
// Scan for commands
|
||||
var commandClasses = new Reflections(CommandManager.class.getPackageName()).getTypesAnnotatedWith(Command.class);
|
||||
|
||||
for (var cls : commandClasses) {
|
||||
if (!CommandHandler.class.isAssignableFrom(cls)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
CommandHandler handler = (CommandHandler) cls.getDeclaredConstructor().newInstance();
|
||||
this.registerCommand(handler);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a command that players and server console users can use. Command handlers must have the proper command annotation attached to them.
|
||||
*/
|
||||
public CommandManager registerCommand(CommandHandler handler) {
|
||||
Command command = handler.getClass().getAnnotation(Command.class);
|
||||
if (command == null) return this;
|
||||
|
||||
this.getLabels().put(command.label(), handler);
|
||||
this.getCommands().put(command.label(), handler);
|
||||
|
||||
for (String alias : command.aliases()) {
|
||||
this.getCommands().put(alias, handler);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a command from use.
|
||||
* @param label The command name
|
||||
* @return
|
||||
*/
|
||||
public CommandManager unregisterCommand(String label) {
|
||||
CommandHandler handler = this.getLabels().get(label);
|
||||
if (handler == null) return this;
|
||||
|
||||
Command command = handler.getClass().getAnnotation(Command.class);
|
||||
if (command == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.getLabels().remove(command.label());
|
||||
this.getCommands().remove(command.label());
|
||||
|
||||
for (String alias : command.aliases()) {
|
||||
this.getCommands().remove(alias);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the sender has permission to use this command. Will always return true if the sender is the server console.
|
||||
* @param sender The sender of the command.
|
||||
* @param command
|
||||
* @return true if the sender has permission to use this command
|
||||
*/
|
||||
public boolean checkPermission(Player sender, Command command) {
|
||||
if (sender == null || command.permission().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return sender.getAccount().hasPermission(command.permission());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the sender has permission to use this command on other players. Will always return true if the sender is the server console.
|
||||
* @param sender The sender of the command.
|
||||
* @param command
|
||||
* @return true if the sender has permission to use this command
|
||||
*/
|
||||
private boolean checkTargetPermission(Player sender, Command command) {
|
||||
if (sender == null || command.permission().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return sender.getAccount().hasPermission("target." + command.permission());
|
||||
}
|
||||
|
||||
public void invoke(Player sender, String message) {
|
||||
// Parse message into arguments
|
||||
List<String> args = Arrays.stream(message.split(" ")).collect(Collectors.toCollection(ArrayList::new));
|
||||
|
||||
// Get command label
|
||||
String label = args.remove(0).toLowerCase();
|
||||
|
||||
// Filter out command prefixes
|
||||
if (label.startsWith("/") || label.startsWith("!")) {
|
||||
label = label.substring(1);
|
||||
}
|
||||
|
||||
// Get command handler
|
||||
CommandHandler handler = this.commands.get(label);
|
||||
|
||||
// Execute command
|
||||
if (handler != null) {
|
||||
// Get command annotation data
|
||||
Command command = handler.getData();
|
||||
|
||||
// Check if sender has permission to run the command.
|
||||
if (sender != null && !this.checkPermission(sender, command)) {
|
||||
// We have a double null check here just in case
|
||||
sender.sendMessage("You do not have permission to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build command arguments
|
||||
CommandArgs cmdArgs = new CommandArgs(sender, args);
|
||||
|
||||
// Check targeted permission
|
||||
if (sender != cmdArgs.getTarget() && !this.checkTargetPermission(sender, command)) {
|
||||
cmdArgs.sendMessage("You do not have permission to use this command on another player.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure our command has a target
|
||||
if (command.requireTarget() && cmdArgs.getTarget() == null) {
|
||||
cmdArgs.sendMessage("Error: Targeted player not found or offline");
|
||||
return;
|
||||
}
|
||||
|
||||
// Log
|
||||
if (sender != null && Nebula.getConfig().getLogOptions().commands) {
|
||||
Nebula.getLogger().info("[UID: " + sender.getUid() + "] " + sender.getName() + " used command: " + message);
|
||||
}
|
||||
|
||||
// Run command
|
||||
handler.execute(cmdArgs);
|
||||
} else {
|
||||
if (sender != null) {
|
||||
sender.sendMessage("Invalid Command!");
|
||||
} else {
|
||||
Nebula.getLogger().info("Invalid Command!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.game.account.AccountHelper;
|
||||
import emu.nebula.util.Utils;
|
||||
|
||||
@Command(label = "account", permission = "admin.account", desc = "/account {create | delete} [username] (reserved player uid). Creates or deletes an account.")
|
||||
public class AccountCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
if (args.size() < 2) {
|
||||
args.sendMessage("Invalid amount of args");
|
||||
return;
|
||||
}
|
||||
|
||||
String command = args.get(0).toLowerCase();
|
||||
String username = args.get(1);
|
||||
|
||||
switch (command) {
|
||||
case "create" -> {
|
||||
// Reserved player uid
|
||||
int reservedUid = 0;
|
||||
|
||||
if (args.size() >= 3) {
|
||||
reservedUid = Utils.parseSafeInt(args.get(2));
|
||||
}
|
||||
|
||||
if (AccountHelper.createAccount(username, null, reservedUid) != null) {
|
||||
args.sendMessage("Account created");
|
||||
} else {
|
||||
args.sendMessage("Account already exists");
|
||||
}
|
||||
}
|
||||
case "delete" -> {
|
||||
if (AccountHelper.deleteAccount(username)) {
|
||||
args.sendMessage("Account deleted");
|
||||
} else {
|
||||
args.sendMessage("Account doesnt exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.util.Utils;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.game.mail.GameMail;
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
|
||||
@Command(
|
||||
label = "give",
|
||||
aliases = {"g", "item"},
|
||||
permission = "player.give",
|
||||
requireTarget = true,
|
||||
desc = "/give [item id] x(amount). Gives the targeted player an item."
|
||||
)
|
||||
public class GiveCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
// Setup mail
|
||||
var mail = new GameMail("System", "Give Command Result", "");
|
||||
|
||||
// Get amount to give
|
||||
int amount = Math.max(args.getAmount(), 1);
|
||||
|
||||
// Parse items
|
||||
for (String arg : args.getList()) {
|
||||
// Parse item id
|
||||
int itemId = Utils.parseSafeInt(arg);
|
||||
|
||||
var itemData = GameData.getItemDataTable().get(itemId);
|
||||
if (itemData == null) {
|
||||
args.sendMessage("Item \"" + arg + "\" does not exist!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add
|
||||
mail.addAttachment(itemId, amount);
|
||||
}
|
||||
|
||||
// Add mail
|
||||
args.getTarget().getMailbox().sendMail(mail);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.game.mail.GameMail;
|
||||
|
||||
@Command(label = "mail", aliases = {"m"}, permission = "player.mail", requireTarget = true, desc = "/mail [content]. Sends the targeted player a system mail.")
|
||||
public class MailCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
// Setup mail
|
||||
var mail = new GameMail("System", "Test", "This is a test mail");
|
||||
|
||||
// Add mail
|
||||
args.getTarget().getMailbox().sendMail(mail);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
|
||||
@Command(label = "reload", permission = "admin.reload", desc = "/reload. Reloads the server config.")
|
||||
public class ReloadCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
Nebula.loadConfig();
|
||||
args.sendMessage("Reloaded the server config");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package emu.nebula.data;
|
||||
|
||||
public abstract class BaseDef implements Comparable<BaseDef> {
|
||||
|
||||
public abstract int getId();
|
||||
|
||||
public void onLoad() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(BaseDef o) {
|
||||
return this.getId() - o.getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package emu.nebula.data;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntCollection;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectCollection;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
|
||||
public class DataTable<T> implements Iterable<T> {
|
||||
private Int2ObjectMap<T> map;
|
||||
|
||||
public DataTable() {
|
||||
this.map = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(Object res) {
|
||||
if (res instanceof BaseDef r) {
|
||||
this.map.put(r.getId(), (T) res);
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.map.size();
|
||||
}
|
||||
|
||||
// Wrapper functions
|
||||
|
||||
/**
|
||||
* Wrapper for {@link it.unimi.dsi.fastutil.ints.Int2ObjectMap.get}
|
||||
*/
|
||||
public T get(int id) {
|
||||
return this.map.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link it.unimi.dsi.fastutil.ints.Int2ObjectMap.containsKey}
|
||||
*/
|
||||
public boolean containsKey(int id) {
|
||||
return this.map.containsKey(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link it.unimi.dsi.fastutil.ints.Int2ObjectMap.keySet}
|
||||
*/
|
||||
public IntSet keySet() {
|
||||
return this.map.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link it.unimi.dsi.fastutil.ints.Int2ObjectMap.values}
|
||||
*/
|
||||
public ObjectCollection<T> values() {
|
||||
return this.map.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link it.unimi.dsi.fastutil.ints.Int2ObjectMap.int2ObjectEntrySet}
|
||||
*/
|
||||
public ObjectSet<Int2ObjectMap.Entry<T>> int2ObjectEntrySet() {
|
||||
return this.map.int2ObjectEntrySet();
|
||||
}
|
||||
|
||||
// Iterable/Streamable
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return this.values().iterator();
|
||||
}
|
||||
|
||||
public Stream<T> stream() {
|
||||
return this.values().stream();
|
||||
}
|
||||
|
||||
// Custom
|
||||
|
||||
public IntCollection getIds() {
|
||||
return this.map.keySet();
|
||||
}
|
||||
|
||||
public IntCollection getAllIds() {
|
||||
return this.getIds();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package emu.nebula.data;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
|
||||
import emu.nebula.data.resources.*;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class GameData {
|
||||
@Getter private static DataTable<CharacterDef> CharacterDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharacterAdvanceDef> CharacterAdvanceDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharacterSkillUpgradeDef> CharacterSkillUpgradeDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharacterUpgradeDef> CharacterUpgradeDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharItemExpDef> CharItemExpDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<DiscDef> DiscDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscStrengthenDef> DiscStrengthenDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscItemExpDef> DiscItemExpDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscPromoteDef> DiscPromoteDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscPromoteLimitDef> DiscPromoteLimitDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<ItemDef> ItemDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<MallMonthlyCardDef> MallMonthlyCardDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<MallPackageDef> MallPackageDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<MallShopDef> MallShopDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<MallGemDef> MallGemDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<WorldClassDef> WorldClassDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<GuideGroupDef> GuideGroupDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<StarTowerDef> StarTowerDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<PotentialDef> PotentialDataTable = new DataTable<>();
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package emu.nebula.data;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import emu.nebula.util.JsonUtils;
|
||||
import emu.nebula.util.Utils;
|
||||
import emu.nebula.Nebula;
|
||||
|
||||
public class ResourceLoader {
|
||||
private static boolean loaded = false;
|
||||
|
||||
// Load all resources
|
||||
public static void loadAll() {
|
||||
// Make sure we don't load more than once
|
||||
if (loaded) return;
|
||||
|
||||
// Load
|
||||
loadResources();
|
||||
|
||||
// Done
|
||||
loaded = true;
|
||||
Nebula.getLogger().info("Resource loading complete");
|
||||
}
|
||||
|
||||
public static void loadResources() {
|
||||
// Get resource classes and sort
|
||||
List<Class<?>> classes = new Reflections(ResourceLoader.class.getPackage().getName())
|
||||
.getTypesAnnotatedWith(ResourceType.class)
|
||||
.stream()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
classes.sort((a, b) -> b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value());
|
||||
|
||||
// Load resource
|
||||
for (Class<?> def : classes) {
|
||||
loadFromResource(def);
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadFromResource(Class<?> resourceClass) {
|
||||
// Load to map
|
||||
DataTable<?> table = getTableForResource(GameData.class, resourceClass);
|
||||
ResourceType type = resourceClass.getAnnotation(ResourceType.class);
|
||||
|
||||
// Sanity check
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
try {
|
||||
var json = JsonUtils.loadToMap(Nebula.getConfig().resourceDir + "/bin/" + type.name(), String.class, resourceClass);
|
||||
|
||||
for (Object o : json.values()) {
|
||||
BaseDef res = (BaseDef) o;
|
||||
|
||||
if (res == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
res.onLoad();
|
||||
|
||||
count++;
|
||||
|
||||
if (table != null) {
|
||||
table.add(o);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Nebula.getLogger().error("Error loading resource file: " + type.name(), e);
|
||||
}
|
||||
|
||||
Nebula.getLogger().info("Loaded " + count + " " + resourceClass.getSimpleName() + "s.");
|
||||
}
|
||||
|
||||
// Utility
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> DataTable<T> getTableForResource(Class<?> dataClass, Class<T> resourceClass) {
|
||||
// Init
|
||||
DataTable<T> table = null;
|
||||
Field field = null;
|
||||
|
||||
// Parse out "Def" in the resource name
|
||||
String simpleName = resourceClass.getSimpleName();
|
||||
|
||||
if (simpleName.endsWith("Def")) {
|
||||
simpleName = simpleName.substring(0, simpleName.length() - 3) + "Data";
|
||||
}
|
||||
|
||||
// Get table
|
||||
try {
|
||||
field = dataClass.getDeclaredField(simpleName + "Table");
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
field = dataClass.getDeclaredField(Utils.lowerCaseFirstChar(simpleName) + "Table");
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (field != null) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
table = (DataTable<T>) field.get(null);
|
||||
} catch (Exception e) {
|
||||
|
||||
} finally {
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package emu.nebula.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;
|
||||
|
||||
Class<?> keyType() default int.class;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "CharItemExp.json")
|
||||
public class CharItemExpDef extends BaseDef {
|
||||
private int ItemId;
|
||||
private int ExpValue;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return ItemId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "CharacterAdvance.json", loadPriority = LoadPriority.LOW)
|
||||
public class CharacterAdvanceDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Group;
|
||||
private int AdvanceLvl;
|
||||
|
||||
private int Tid1;
|
||||
private int Qty1;
|
||||
private int Tid2;
|
||||
private int Qty2;
|
||||
private int Tid3;
|
||||
private int Qty3;
|
||||
private int Tid4;
|
||||
private int Qty4;
|
||||
private int GoldQty;
|
||||
|
||||
private transient ItemParamMap materials;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.materials = new ItemParamMap();
|
||||
|
||||
if (this.Tid1 > 0) {
|
||||
this.materials.add(this.Tid1, this.Qty1);
|
||||
}
|
||||
if (this.Tid2 > 0) {
|
||||
this.materials.add(this.Tid2, this.Qty2);
|
||||
}
|
||||
if (this.Tid3 > 0) {
|
||||
this.materials.add(this.Tid3, this.Qty3);
|
||||
}
|
||||
if (this.Tid4 > 0) {
|
||||
this.materials.add(this.Tid4, this.Qty4);
|
||||
}
|
||||
if (this.GoldQty > 0) {
|
||||
this.materials.add(GameConstants.GOLD_ITEM_ID, this.GoldQty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "Character.json")
|
||||
public class CharacterDef extends BaseDef {
|
||||
private int Id;
|
||||
private String Name;
|
||||
|
||||
private boolean Available;
|
||||
private int Grade;
|
||||
private int DefaultSkinId;
|
||||
private int AdvanceSkinId;
|
||||
private int AdvanceGroup;
|
||||
private int[] SkillsUpgradeGroup;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public int getSkillsUpgradeGroup(int index) {
|
||||
if (index < 0 || index >= this.SkillsUpgradeGroup.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.SkillsUpgradeGroup[index];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "CharacterSkillUpgrade.json", loadPriority = LoadPriority.LOW)
|
||||
public class CharacterSkillUpgradeDef extends BaseDef {
|
||||
private int Group;
|
||||
private int AdvanceNum;
|
||||
|
||||
private int Tid1;
|
||||
private int Qty1;
|
||||
private int Tid2;
|
||||
private int Qty2;
|
||||
private int Tid3;
|
||||
private int Qty3;
|
||||
private int Tid4;
|
||||
private int Qty4;
|
||||
private int GoldQty;
|
||||
|
||||
private transient int upgradeId;
|
||||
private transient ItemParamMap materials;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return upgradeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.materials = new ItemParamMap();
|
||||
|
||||
if (this.Tid1 > 0) {
|
||||
this.materials.add(this.Tid1, this.Qty1);
|
||||
}
|
||||
if (this.Tid2 > 0) {
|
||||
this.materials.add(this.Tid2, this.Qty2);
|
||||
}
|
||||
if (this.Tid3 > 0) {
|
||||
this.materials.add(this.Tid3, this.Qty3);
|
||||
}
|
||||
if (this.Tid4 > 0) {
|
||||
this.materials.add(this.Tid4, this.Qty4);
|
||||
}
|
||||
if (this.GoldQty > 0) {
|
||||
this.materials.add(GameConstants.GOLD_ITEM_ID, this.GoldQty);
|
||||
}
|
||||
|
||||
this.upgradeId = (this.Group * 100) + this.AdvanceNum;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "CharacterUpgrade.json")
|
||||
public class CharacterUpgradeDef extends BaseDef {
|
||||
private int Level;
|
||||
private int Exp;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Level;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "Disc.json")
|
||||
public class DiscDef extends BaseDef {
|
||||
private int Id;
|
||||
private int StrengthenGroupId;
|
||||
private int PromoteGroupId;
|
||||
private int TransformItemId;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DiscItemExp.json")
|
||||
public class DiscItemExpDef extends BaseDef {
|
||||
private int ItemId;
|
||||
private int Exp;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return ItemId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DiscPromote.json", loadPriority = LoadPriority.LOW)
|
||||
public class DiscPromoteDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Group;
|
||||
private int AdvanceLvl;
|
||||
|
||||
private int ItemId1;
|
||||
private int Num1;
|
||||
private int ItemId2;
|
||||
private int Num2;
|
||||
private int ItemId3;
|
||||
private int Num3;
|
||||
private int ExpenseGold;
|
||||
|
||||
private transient ItemParamMap materials;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.materials = new ItemParamMap();
|
||||
|
||||
if (this.ItemId1 > 0 && this.Num1 > 0) {
|
||||
this.materials.add(this.ItemId1, this.Num1);
|
||||
}
|
||||
if (this.ItemId2 > 0 && this.Num2 > 0) {
|
||||
this.materials.add(this.ItemId2, this.Num2);
|
||||
}
|
||||
if (this.ItemId3 > 0 && this.Num3 > 0) {
|
||||
this.materials.add(this.ItemId3, this.Num3);
|
||||
}
|
||||
if (this.ExpenseGold > 0) {
|
||||
this.materials.add(GameConstants.GOLD_ITEM_ID, this.ExpenseGold);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DiscPromoteLimit.json")
|
||||
public class DiscPromoteLimitDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Rarity;
|
||||
private int Phase;
|
||||
private int MaxLevel;
|
||||
private int WorldClassLimit;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DiscStrengthen.json")
|
||||
public class DiscStrengthenDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Exp;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "GuideGroup.json")
|
||||
public class GuideGroupDef extends BaseDef {
|
||||
private int Id;
|
||||
private boolean IsActive;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.inventory.ItemSubType;
|
||||
import emu.nebula.game.inventory.ItemType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "Item.json")
|
||||
public class ItemDef extends BaseDef {
|
||||
private int Id;
|
||||
private String Title;
|
||||
private int Type;
|
||||
private int Stype;
|
||||
private int Rarity;
|
||||
private boolean Stack;
|
||||
|
||||
private transient ItemType itemType;
|
||||
private transient ItemSubType itemSubType;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.itemType = ItemType.getByValue(this.Type);
|
||||
this.itemSubType = ItemSubType.getByValue(this.Stype);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "MallGem.json")
|
||||
public class MallGemDef extends BaseDef {
|
||||
@SerializedName("Id")
|
||||
private String IdString;
|
||||
private int Stock;
|
||||
private int ItemId;
|
||||
private int CurrencyItemId;
|
||||
private int ItemQty;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return IdString.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "MallMonthlyCard.json")
|
||||
public class MallMonthlyCardDef extends BaseDef {
|
||||
@SerializedName("Id")
|
||||
private String IdString;
|
||||
private int MonthlyCardId;
|
||||
private int Price;
|
||||
private int BaseItemId;
|
||||
private int BaseItemQty;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return IdString.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "MallPackage.json")
|
||||
public class MallPackageDef extends BaseDef {
|
||||
@SerializedName("Id")
|
||||
private String IdString;
|
||||
private int Stock;
|
||||
private int CurrencyType;
|
||||
private int CurrencyItemId;
|
||||
private int CurrencyItemQty;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return IdString.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "MallShop.json")
|
||||
public class MallShopDef extends BaseDef {
|
||||
@SerializedName("Id")
|
||||
private String IdString;
|
||||
private int Stock;
|
||||
private int ItemId;
|
||||
private int CurrencyItemId;
|
||||
private int ItemQty;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return IdString.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "Potential.json")
|
||||
public class PotentialDef extends BaseDef {
|
||||
private int Id;
|
||||
private int CharId;
|
||||
private int Build;
|
||||
private int MaxLevel;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "StarTower.json")
|
||||
public class StarTowerDef extends BaseDef {
|
||||
private int Id;
|
||||
private int[] FloorNum;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "WorldClass.json")
|
||||
public class WorldClassDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Exp;
|
||||
private String Reward;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package emu.nebula.database;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AccountDatabaseOnly {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package emu.nebula.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package emu.nebula.database;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import emu.nebula.Config.DatabaseInfo;
|
||||
import emu.nebula.Config.InternalMongoInfo;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.Nebula.ServerType;
|
||||
import emu.nebula.database.codecs.*;
|
||||
import emu.nebula.util.Utils;
|
||||
|
||||
import org.bson.codecs.configuration.CodecRegistries;
|
||||
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.*;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.mapping.Mapper;
|
||||
import dev.morphia.mapping.MapperOptions;
|
||||
import dev.morphia.query.filters.Filters;
|
||||
import dev.morphia.query.updates.UpdateOperators;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public final class DatabaseManager {
|
||||
@Getter
|
||||
private static MongoServer server;
|
||||
private Datastore datastore;
|
||||
|
||||
private static final InsertOneOptions INSERT_OPTIONS = new InsertOneOptions();
|
||||
private static final DeleteOptions DELETE_OPTIONS = new DeleteOptions();
|
||||
private static final DeleteOptions DELETE_MANY = new DeleteOptions().multi(true);
|
||||
|
||||
public DatabaseManager(DatabaseInfo info, ServerType type) {
|
||||
// Variables
|
||||
var internalConfig = Nebula.getConfig().getInternalMongoServer();
|
||||
String connectionString = info.getUri();
|
||||
|
||||
// Start local mongo server
|
||||
if (info.isUseInternal()) {
|
||||
if (Utils.isPortOpen(internalConfig.getAddress(), internalConfig.getPort())) {
|
||||
connectionString = startInternalMongoServer(internalConfig);
|
||||
Nebula.getLogger().info("Started local MongoDB server at " + server.getConnectionString());
|
||||
} else {
|
||||
Nebula.getLogger().warn("Local MongoDB server could not be created because the port is in use.");
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize
|
||||
MongoClient mongoClient = MongoClients.create(connectionString);
|
||||
|
||||
// Add our custom fastutil codecs
|
||||
var codecProvider = CodecRegistries.fromCodecs(
|
||||
new IntSetCodec(), new IntListCodec(), new Int2IntMapCodec(), new ItemParamMapCodec()
|
||||
);
|
||||
|
||||
// Set mapper options
|
||||
MapperOptions mapperOptions = MapperOptions.builder()
|
||||
.storeEmpties(true)
|
||||
.storeNulls(false)
|
||||
.codecProvider(codecProvider)
|
||||
.build();
|
||||
|
||||
// Create data store.
|
||||
datastore = Morphia.createDatastore(mongoClient, info.getCollection(), mapperOptions);
|
||||
|
||||
// Map classes
|
||||
var entities = new Reflections(Nebula.class.getPackageName())
|
||||
.getTypesAnnotatedWith(Entity.class)
|
||||
.stream()
|
||||
.filter(cls -> {
|
||||
Entity e = cls.getAnnotation(Entity.class);
|
||||
return e != null && !e.value().equals(Mapper.IGNORED_FIELDNAME);
|
||||
})
|
||||
.toList();
|
||||
|
||||
if (type.runLogin()) {
|
||||
// Only map account related entities
|
||||
var map = entities.stream().filter(cls -> {
|
||||
return cls.getAnnotation(AccountDatabaseOnly.class) != null;
|
||||
}).toArray(Class<?>[]::new);
|
||||
|
||||
datastore.getMapper().map(map);
|
||||
}
|
||||
if (type.runGame()) {
|
||||
// Only map game related entities
|
||||
var map = entities.stream().filter(cls -> {
|
||||
return cls.getAnnotation(AccountDatabaseOnly.class) == null;
|
||||
}).toArray(Class<?>[]::new);
|
||||
|
||||
datastore.getMapper().map(map);
|
||||
}
|
||||
|
||||
// Ensure indexes
|
||||
ensureIndexes();
|
||||
|
||||
// Done
|
||||
Nebula.getLogger().info("Connected to the MongoDB database at " + connectionString);
|
||||
}
|
||||
|
||||
public MongoDatabase getDatabase() {
|
||||
return getDatastore().getDatabase();
|
||||
}
|
||||
|
||||
private void ensureIndexes() {
|
||||
try {
|
||||
datastore.ensureIndexes();
|
||||
} catch (MongoCommandException exception) {
|
||||
Nebula.getLogger().warn("Mongo index error: ", exception);
|
||||
// Duplicate index error
|
||||
if (exception.getCode() == 85) {
|
||||
// Drop all indexes and re add them
|
||||
MongoIterable<String> collections = datastore.getDatabase().listCollectionNames();
|
||||
for (String name : collections) {
|
||||
datastore.getDatabase().getCollection(name).dropIndexes();
|
||||
}
|
||||
// Add back indexes
|
||||
datastore.ensureIndexes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Database Functions
|
||||
|
||||
public boolean checkIfObjectExists(Class<?> cls, String filter, String value) {
|
||||
return getDatastore().find(cls).filter(Filters.eq(filter, value)).count() > 0;
|
||||
}
|
||||
|
||||
public <T> T getObjectByUid(Class<T> cls, long uid) {
|
||||
return getDatastore().find(cls).filter(Filters.eq("_id", uid)).first();
|
||||
}
|
||||
|
||||
public <T> T getObjectByField(Class<T> cls, String filter, Object value) {
|
||||
return getDatastore().find(cls).filter(Filters.eq(filter, value)).first();
|
||||
}
|
||||
|
||||
public <T> T getObjectByField(Class<T> cls, String filter, long value) {
|
||||
return getDatastore().find(cls).filter(Filters.eq(filter, value)).first();
|
||||
}
|
||||
|
||||
public <T> Stream<T> getObjects(Class<T> cls, String filter, Object value) {
|
||||
return getDatastore().find(cls).filter(Filters.eq(filter, value)).stream();
|
||||
}
|
||||
|
||||
public <T> Stream<T> getObjects(Class<T> cls, String filter, long value) {
|
||||
return getDatastore().find(cls).filter(Filters.eq(filter, value)).stream();
|
||||
}
|
||||
|
||||
public <T> Stream<T> getObjects(Class<T> cls) {
|
||||
return getDatastore().find(cls).stream();
|
||||
}
|
||||
|
||||
public <T> void save(T obj) {
|
||||
getDatastore().save(obj, INSERT_OPTIONS);
|
||||
}
|
||||
|
||||
public <T> boolean delete(T obj) {
|
||||
DeleteResult result = getDatastore().delete(obj, DELETE_OPTIONS);
|
||||
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 void update(Object obj, int uid, String field, Object item) {
|
||||
update(obj, uid, field, item, false);
|
||||
}
|
||||
|
||||
public void update(Object obj, int uid, String field, Object value, boolean upsert) {
|
||||
var opt = new UpdateOptions().upsert(upsert);
|
||||
|
||||
getDatastore().find(obj.getClass())
|
||||
.filter(Filters.eq("_id", uid))
|
||||
.update(opt, UpdateOperators.set(field, value));
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public void update(Object obj, int uid, String field, Object value, String field2, Object value2) {
|
||||
getDatastore().find(obj.getClass())
|
||||
.filter(Filters.eq("_id", uid))
|
||||
.update(UpdateOperators.set(field, value), UpdateOperators.set(field2, value2));
|
||||
}
|
||||
|
||||
public void updateNested(Object obj, int uid, String filter, int filterId, String field, Object item) {
|
||||
var opt = new UpdateOptions().upsert(false);
|
||||
|
||||
getDatastore().find(obj.getClass())
|
||||
.filter(Filters.eq("_id", uid))
|
||||
.filter(Filters.eq(filter, filterId))
|
||||
.update(opt, UpdateOperators.set(field, item));
|
||||
}
|
||||
|
||||
public void addToList(Object obj, int uid, String field, Object item) {
|
||||
var opt = new UpdateOptions().upsert(false);
|
||||
|
||||
getDatastore().find(obj.getClass())
|
||||
.filter(Filters.eq("_id", uid))
|
||||
.update(opt, UpdateOperators.addToSet(field, item));
|
||||
}
|
||||
|
||||
// Database counter
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal MongoDB server
|
||||
|
||||
public static 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package emu.nebula.database;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
|
||||
public interface GameDatabaseObject {
|
||||
|
||||
public default void save() {
|
||||
Nebula.getGameDatabase().save(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package emu.nebula.database.codecs;
|
||||
|
||||
import org.bson.BsonReader;
|
||||
import org.bson.BsonType;
|
||||
import org.bson.BsonWriter;
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.bson.codecs.EncoderContext;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
|
||||
/**
|
||||
* Custom mongodb codec for encoding/decoding fastutil int2int maps.
|
||||
*/
|
||||
public class Int2IntMapCodec implements Codec<Int2IntMap> {
|
||||
|
||||
@Override
|
||||
public Class<Int2IntMap> getEncoderClass() {
|
||||
return Int2IntMap.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(BsonWriter writer, Int2IntMap collection, EncoderContext encoderContext) {
|
||||
writer.writeStartDocument();
|
||||
for (var entry : collection.int2IntEntrySet()) {
|
||||
writer.writeName(Integer.toString(entry.getIntKey()));
|
||||
writer.writeInt32(entry.getIntValue());
|
||||
}
|
||||
writer.writeEndDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2IntMap decode(BsonReader reader, DecoderContext decoderContext) {
|
||||
Int2IntMap collection = new Int2IntOpenHashMap();
|
||||
reader.readStartDocument();
|
||||
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
|
||||
collection.put(Integer.parseInt(reader.readName()), reader.readInt32());
|
||||
}
|
||||
reader.readEndDocument();
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package emu.nebula.database.codecs;
|
||||
|
||||
import org.bson.BsonReader;
|
||||
import org.bson.BsonType;
|
||||
import org.bson.BsonWriter;
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.bson.codecs.EncoderContext;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
/**
|
||||
* Custom mongodb codec for encoding/decoding fastutil int sets.
|
||||
*/
|
||||
public class IntListCodec implements Codec<IntList> {
|
||||
|
||||
@Override
|
||||
public Class<IntList> getEncoderClass() {
|
||||
return IntList.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(BsonWriter writer, IntList collection, EncoderContext encoderContext) {
|
||||
writer.writeStartArray();
|
||||
for (int value : collection) {
|
||||
writer.writeInt32(value);
|
||||
}
|
||||
writer.writeEndArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntList decode(BsonReader reader, DecoderContext decoderContext) {
|
||||
IntList collection = new IntArrayList();
|
||||
reader.readStartArray();
|
||||
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
|
||||
collection.add(reader.readInt32());
|
||||
}
|
||||
reader.readEndArray();
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package emu.nebula.database.codecs;
|
||||
|
||||
import org.bson.BsonReader;
|
||||
import org.bson.BsonType;
|
||||
import org.bson.BsonWriter;
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.bson.codecs.EncoderContext;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
/**
|
||||
* Custom mongodb codec for encoding/decoding fastutil int sets.
|
||||
*/
|
||||
public class IntSetCodec implements Codec<IntSet> {
|
||||
|
||||
@Override
|
||||
public Class<IntSet> getEncoderClass() {
|
||||
return IntSet.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(BsonWriter writer, IntSet collection, EncoderContext encoderContext) {
|
||||
writer.writeStartArray();
|
||||
for (int value : collection) {
|
||||
writer.writeInt32(value);
|
||||
}
|
||||
writer.writeEndArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntSet decode(BsonReader reader, DecoderContext decoderContext) {
|
||||
IntSet collection = new IntOpenHashSet();
|
||||
reader.readStartArray();
|
||||
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
|
||||
collection.add(reader.readInt32());
|
||||
}
|
||||
reader.readEndArray();
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package emu.nebula.database.codecs;
|
||||
|
||||
import org.bson.BsonReader;
|
||||
import org.bson.BsonType;
|
||||
import org.bson.BsonWriter;
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.bson.codecs.EncoderContext;
|
||||
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
|
||||
/**
|
||||
* Copy of Int2IntMapCodec.java
|
||||
*/
|
||||
public class ItemParamMapCodec implements Codec<ItemParamMap> {
|
||||
|
||||
@Override
|
||||
public Class<ItemParamMap> getEncoderClass() {
|
||||
return ItemParamMap.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(BsonWriter writer, ItemParamMap collection, EncoderContext encoderContext) {
|
||||
writer.writeStartDocument();
|
||||
for (var entry : collection.int2IntEntrySet()) {
|
||||
writer.writeName(Integer.toString(entry.getIntKey()));
|
||||
writer.writeInt32(entry.getIntValue());
|
||||
}
|
||||
writer.writeEndDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemParamMap decode(BsonReader reader, DecoderContext decoderContext) {
|
||||
ItemParamMap collection = new ItemParamMap();
|
||||
reader.readStartDocument();
|
||||
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
|
||||
collection.put(Integer.parseInt(reader.readName()), reader.readInt32());
|
||||
}
|
||||
reader.readEndDocument();
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package emu.nebula.game;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import emu.nebula.game.player.PlayerModule;
|
||||
import emu.nebula.net.GameSession;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class GameContext {
|
||||
private final Object2ObjectMap<String, GameSession> sessions;
|
||||
|
||||
// Modules
|
||||
private final PlayerModule playerModule;
|
||||
|
||||
// Cleanup thread
|
||||
private final Timer cleanupTimer;
|
||||
|
||||
public GameContext() {
|
||||
this.sessions = new Object2ObjectOpenHashMap<>();
|
||||
this.playerModule = new PlayerModule(this);
|
||||
|
||||
this.cleanupTimer = new Timer();
|
||||
this.cleanupTimer.scheduleAtFixedRate(new CleanupTask(this), 0, TimeUnit.SECONDS.toMillis(60));
|
||||
}
|
||||
|
||||
public synchronized GameSession getSessionByToken(String token) {
|
||||
return sessions.get(token);
|
||||
}
|
||||
|
||||
public synchronized void addSession(GameSession session) {
|
||||
this.sessions.put(session.getToken(), session);
|
||||
}
|
||||
|
||||
public synchronized void generateSessionToken(GameSession session) {
|
||||
// Remove token
|
||||
if (session.getToken() != null) {
|
||||
this.sessions.remove(session.getToken());
|
||||
}
|
||||
|
||||
// Generate token
|
||||
String token = null;
|
||||
|
||||
do {
|
||||
token = session.generateToken();
|
||||
} while (this.getSessions().containsKey(token));
|
||||
|
||||
// Register session
|
||||
this.sessions.put(session.getToken(), session);
|
||||
}
|
||||
|
||||
// TODO add timeout to config
|
||||
public synchronized void cleanupInactiveSessions() {
|
||||
var it = this.getSessions().entrySet().iterator();
|
||||
long timeout = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(600); // 10 minutes
|
||||
|
||||
while (it.hasNext()) {
|
||||
var session = it.next().getValue();
|
||||
|
||||
if (timeout > session.getLastActiveTime()) {
|
||||
// Remove from session map
|
||||
it.remove();
|
||||
|
||||
// Clear player
|
||||
session.clearPlayer(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class CleanupTask extends TimerTask {
|
||||
private GameContext gameContext;
|
||||
|
||||
public CleanupTask(GameContext gameContext) {
|
||||
this.gameContext = gameContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.getGameContext().cleanupInactiveSessions();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package emu.nebula.game;
|
||||
|
||||
public abstract class GameContextModule {
|
||||
private transient GameContext context;
|
||||
|
||||
public GameContextModule(GameContext player) {
|
||||
this.context = player;
|
||||
}
|
||||
|
||||
public GameContext getGameContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package emu.nebula.game.account;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.database.AccountDatabaseOnly;
|
||||
import emu.nebula.util.Snowflake;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AccountDatabaseOnly
|
||||
@Entity(value = "accounts", useDiscriminator = false)
|
||||
public class Account {
|
||||
@Id private String uid;
|
||||
|
||||
@Indexed
|
||||
private String email;
|
||||
private String code;
|
||||
|
||||
private String nickname;
|
||||
private String picture;
|
||||
|
||||
@Indexed private String loginToken;
|
||||
@Indexed private String gameToken;
|
||||
|
||||
private Set<String> permissions;
|
||||
|
||||
private int reservedPlayerUid;
|
||||
private long createdAt;
|
||||
|
||||
@Deprecated
|
||||
public Account() {
|
||||
// Morphia only
|
||||
}
|
||||
|
||||
public Account(String email, String password, int reservedUid) {
|
||||
this.uid = Long.toString(Snowflake.newUid());
|
||||
this.email = email;
|
||||
this.nickname = "";
|
||||
this.picture = "";
|
||||
this.permissions = new HashSet<>();
|
||||
this.reservedPlayerUid = reservedUid;
|
||||
this.createdAt = System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
public boolean verifyCode(String code) {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setNickname(String value) {
|
||||
this.nickname = value;
|
||||
}
|
||||
|
||||
// Tokens
|
||||
|
||||
public String generateLoginToken() {
|
||||
this.loginToken = AccountHelper.createSessionKey(this.getUid());
|
||||
this.save();
|
||||
return this.loginToken;
|
||||
}
|
||||
|
||||
public String generateGameToken() {
|
||||
this.gameToken = AccountHelper.createSessionKey(this.getUid());
|
||||
this.save();
|
||||
return this.gameToken;
|
||||
}
|
||||
|
||||
// Permissions
|
||||
|
||||
public Set<String> getPermissions() {
|
||||
if (this.permissions == null) {
|
||||
this.permissions = new HashSet<>();
|
||||
this.save();
|
||||
}
|
||||
return this.permissions;
|
||||
}
|
||||
|
||||
public boolean addPermission(String permission) {
|
||||
if (this.getPermissions().contains(permission)) {
|
||||
return false;
|
||||
}
|
||||
this.getPermissions().add(permission);
|
||||
this.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) {
|
||||
String[] wildcardParts = wildcard.split("\\.");
|
||||
if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < wildcardParts.length; i++) {
|
||||
switch (wildcardParts[i]) {
|
||||
case "**": // Recursing match
|
||||
return true;
|
||||
case "*": // Match only one layer
|
||||
if (i >= (permissionParts.length-1)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default: // This layer isn't a wildcard, it needs to match exactly
|
||||
if (!wildcardParts[i].equals(permissionParts[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
|
||||
return wildcardParts.length == permissionParts.length;
|
||||
}
|
||||
|
||||
public boolean hasPermission(String permission) {
|
||||
// Skip if permission isnt required
|
||||
if (permission.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Default permissions
|
||||
var defaultPermissions = Nebula.getConfig().getServerOptions().getDefaultPermissions();
|
||||
|
||||
if (defaultPermissions.contains("*")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add default permissions if it doesn't exist
|
||||
List<String> permissions = Stream.of(this.getPermissions(), defaultPermissions)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct().toList();
|
||||
|
||||
if (permissions.contains(permission)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String[] permissionParts = permission.split("\\.");
|
||||
for (String p : permissions) {
|
||||
if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false;
|
||||
if (permissionMatchesWildcard(p, permissionParts)) return true;
|
||||
}
|
||||
|
||||
return permissions.contains("*");
|
||||
}
|
||||
|
||||
public boolean removePermission(String permission) {
|
||||
boolean res = this.getPermissions().remove(permission);
|
||||
if (res) this.save();
|
||||
return res;
|
||||
}
|
||||
|
||||
public void clearPermission() {
|
||||
this.getPermissions().clear();
|
||||
this.save();
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
public void save() {
|
||||
Nebula.getAccountDatabase().save(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package emu.nebula.game.account;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
|
||||
/**
|
||||
* Helper class for handling account related stuff
|
||||
*/
|
||||
public class AccountHelper {
|
||||
|
||||
public static Account createAccount(String email, String password, int reservedUid) {
|
||||
Account account = Nebula.getAccountDatabase().getObjectByField(Account.class, "email", email);
|
||||
|
||||
if (account != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
account = new Account(email, password, reservedUid);
|
||||
account.save();
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
public static Account getAccountByEmail(String email) {
|
||||
if (email == null || email.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Nebula.getAccountDatabase().getObjectByField(Account.class, "email", email);
|
||||
}
|
||||
|
||||
public static Account getAccountByLoginToken(String token) {
|
||||
if (token == null || token.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Nebula.getAccountDatabase().getObjectByField(Account.class, "loginToken", token);
|
||||
}
|
||||
|
||||
public static boolean deleteAccount(String username) {
|
||||
Account account = Nebula.getAccountDatabase().getObjectByField(Account.class, "username", username);
|
||||
|
||||
if (account == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete the account first
|
||||
return Nebula.getAccountDatabase().delete(account);
|
||||
}
|
||||
|
||||
// Simple way to create a unique session key
|
||||
public static String createSessionKey(String accountUid) {
|
||||
byte[] random = new byte[64];
|
||||
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
secureRandom.nextBytes(random);
|
||||
|
||||
String temp = accountUid + "." + System.currentTimeMillis() + "." + secureRandom.toString();
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
||||
byte[] bytes = md.digest(temp.getBytes());
|
||||
return Base64.getEncoder().encodeToString(bytes);
|
||||
} catch (Exception e) {
|
||||
return Base64.getEncoder().encodeToString(temp.getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package emu.nebula.game.character;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.CharacterDef;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.proto.Public.Char;
|
||||
import emu.nebula.proto.Public.CharGemPreset;
|
||||
import emu.nebula.proto.Public.CharGemSlot;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerChar;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerCharGem;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "characters", useDiscriminator = false)
|
||||
public class Character implements GameDatabaseObject {
|
||||
@Id
|
||||
private ObjectId uid;
|
||||
@Indexed
|
||||
private int playerUid;
|
||||
|
||||
private transient CharacterDef data;
|
||||
private transient Player player;
|
||||
|
||||
private int charId;
|
||||
private int advance;
|
||||
private int level;
|
||||
private int exp;
|
||||
private int skin;
|
||||
private int[] skills;
|
||||
private byte[] talents;
|
||||
|
||||
private long createTime;
|
||||
|
||||
@Deprecated // Morphia only!
|
||||
public Character() {
|
||||
|
||||
}
|
||||
|
||||
public Character(Player player, int charId) {
|
||||
this(player, GameData.getCharacterDataTable().get(charId));
|
||||
}
|
||||
|
||||
public Character(Player player, CharacterDef data) {
|
||||
this.player = player;
|
||||
this.playerUid = player.getUid();
|
||||
this.charId = data.getId();
|
||||
this.data = data;
|
||||
this.level = 1;
|
||||
this.skin = data.getDefaultSkinId();
|
||||
this.skills = new int[] {1, 1, 1, 1, 1};
|
||||
this.talents = new byte[8];
|
||||
this.createTime = Nebula.getCurrentTime();
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void setData(CharacterDef data) {
|
||||
if (this.data == null && data.getId() == this.getCharId()) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxGainableExp() {
|
||||
if (this.getLevel() >= this.getMaxLevel()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int maxLevel = this.getMaxLevel();
|
||||
int max = 0;
|
||||
|
||||
for (int i = this.getLevel() + 1; i <= maxLevel; i++) {
|
||||
var data = GameData.getCharacterUpgradeDataTable().get(i);
|
||||
|
||||
if (data != null) {
|
||||
max += data.getExp();
|
||||
}
|
||||
}
|
||||
|
||||
return Math.max(max - this.getExp(), 0);
|
||||
}
|
||||
|
||||
public int getMaxExp() {
|
||||
if (this.getLevel() >= this.getMaxLevel()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var data = GameData.getCharacterUpgradeDataTable().get(this.level + 1);
|
||||
return data != null ? data.getExp() : 0;
|
||||
}
|
||||
|
||||
public int getMaxLevel() {
|
||||
return 10 + (this.getAdvance() * 10);
|
||||
}
|
||||
|
||||
public void addExp(int amount) {
|
||||
// Setup
|
||||
int expRequired = this.getMaxExp();
|
||||
|
||||
// Add exp
|
||||
this.exp += amount;
|
||||
|
||||
// Check for level ups
|
||||
while (this.exp >= expRequired && expRequired > 0) {
|
||||
this.level += 1;
|
||||
this.exp -= expRequired;
|
||||
|
||||
expRequired = this.getMaxExp();
|
||||
}
|
||||
|
||||
// Clamp exp
|
||||
if (this.getLevel() >= this.getMaxLevel()) {
|
||||
this.exp = 0;
|
||||
}
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
}
|
||||
|
||||
// Handlers
|
||||
|
||||
public PlayerChangeInfo upgrade(ItemParamMap params) {
|
||||
// Calculate exp gained
|
||||
int exp = 0;
|
||||
|
||||
// Check if item is an exp item
|
||||
for (var entry : params.getEntrySet()) {
|
||||
var data = GameData.getCharItemExpDataTable().get(entry.getIntKey());
|
||||
if (data == null) return null;
|
||||
|
||||
exp += data.getExpValue() * entry.getIntValue();
|
||||
}
|
||||
|
||||
// Clamp exp gain
|
||||
exp = Math.min(this.getMaxGainableExp(), exp);
|
||||
|
||||
// Calculate gold required
|
||||
params.add(GameConstants.GOLD_ITEM_ID, (int) Math.ceil(exp * 0.15D));
|
||||
|
||||
// Verify that the player has the items
|
||||
if (!this.getPlayer().getInventory().verifyItems(params)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove items
|
||||
var changes = this.getPlayer().getInventory().removeItems(params, null);
|
||||
|
||||
// Add exp
|
||||
this.addExp(exp);
|
||||
|
||||
// Success
|
||||
return changes.setSuccess(true);
|
||||
}
|
||||
|
||||
public PlayerChangeInfo advance() {
|
||||
// TODO check player level to make sure they can advance this character
|
||||
|
||||
// Get advance data
|
||||
int advanceId = (this.getData().getAdvanceGroup() * 100) + (this.advance + 1);
|
||||
var data = GameData.getCharacterAdvanceDataTable().get(advanceId);
|
||||
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Verify that the player has the items
|
||||
if (!this.getPlayer().getInventory().verifyItems(data.getMaterials())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove items
|
||||
var changes = this.getPlayer().getInventory().removeItems(data.getMaterials(), null);
|
||||
|
||||
// Add advance level
|
||||
this.advance++;
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Success
|
||||
return changes.setSuccess(true);
|
||||
}
|
||||
|
||||
public PlayerChangeInfo upgradeSkill(int index) {
|
||||
// TODO check player level to make sure they can advance this character
|
||||
|
||||
// Sanity check
|
||||
if (index < 0 || index >= this.skills.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get advance data
|
||||
int upgradeId = (this.getData().getSkillsUpgradeGroup(index) * 100) + (this.skills[index] + 1);
|
||||
var data = GameData.getCharacterSkillUpgradeDataTable().get(upgradeId);
|
||||
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Verify that the player has the items
|
||||
if (!this.getPlayer().getInventory().verifyItems(data.getMaterials())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove items
|
||||
var changes = this.getPlayer().getInventory().removeItems(data.getMaterials(), null);
|
||||
|
||||
// Add skill level
|
||||
this.skills[index]++;
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Success
|
||||
return changes.setSuccess(true);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public Char toProto() {
|
||||
var proto = Char.newInstance()
|
||||
.setTid(this.getCharId())
|
||||
.setLevel(this.getLevel())
|
||||
.setSkin(this.getSkin())
|
||||
.setAdvance(this.getAdvance())
|
||||
.setTalentNodes(this.getTalents())
|
||||
.addAllSkillLvs(this.getSkills())
|
||||
.setCreateTime(this.getCreateTime());
|
||||
|
||||
var gemPresets = proto.getMutableCharGemPresets()
|
||||
.getMutableCharGemPresets();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
var preset = CharGemPreset.newInstance()
|
||||
.addAllSlotGem(-1, -1, -1);
|
||||
|
||||
gemPresets.add(preset);
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
var slot = CharGemSlot.newInstance()
|
||||
.setId(i);
|
||||
|
||||
proto.addCharGemSlots(slot);
|
||||
}
|
||||
|
||||
proto.getMutableAffinityQuests();
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
public StarTowerChar toStarTowerProto() {
|
||||
var proto = StarTowerChar.newInstance()
|
||||
.setId(this.getCharId())
|
||||
.setAdvance(this.getAdvance())
|
||||
.setLevel(this.getLevel())
|
||||
.setTalentNodes(this.getTalents())
|
||||
.addAllSkillLvs(this.getSkills());
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
var slot = StarTowerCharGem.newInstance()
|
||||
.setSlotId(i)
|
||||
.addAllAttributes(new int[] {0, 0, 0, 0});
|
||||
|
||||
proto.addGems(slot);
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package emu.nebula.game.character;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.CharacterDef;
|
||||
import emu.nebula.data.resources.DiscDef;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.game.player.Player;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class CharacterStorage extends PlayerManager {
|
||||
private final Int2ObjectMap<Character> characters;
|
||||
private final Int2ObjectMap<GameDisc> discs;
|
||||
|
||||
public CharacterStorage(Player player) {
|
||||
super(player);
|
||||
|
||||
this.characters = new Int2ObjectOpenHashMap<>();
|
||||
this.discs = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
// Characters
|
||||
|
||||
public Character getCharacterById(int id) {
|
||||
if (id <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.characters.get(id);
|
||||
}
|
||||
|
||||
public boolean hasCharacter(int id) {
|
||||
return this.characters.containsKey(id);
|
||||
}
|
||||
|
||||
public Character addCharacter(int charId) {
|
||||
// Sanity check to make sure we dont have this character already
|
||||
if (this.hasCharacter(charId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.addCharacter(GameData.getCharacterDataTable().get(charId));
|
||||
}
|
||||
|
||||
private Character addCharacter(CharacterDef data) {
|
||||
// Sanity check to make sure we dont have this character already
|
||||
if (this.hasCharacter(data.getId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create character
|
||||
var character = new Character(this.getPlayer(), data);
|
||||
|
||||
// Save to database
|
||||
character.save();
|
||||
|
||||
// Add to characters
|
||||
this.characters.put(character.getCharId(), character);
|
||||
return character;
|
||||
}
|
||||
|
||||
public Collection<Character> getCharacterCollection() {
|
||||
return this.getCharacters().values();
|
||||
}
|
||||
|
||||
// Discs
|
||||
|
||||
public GameDisc getDiscById(int id) {
|
||||
if (id <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.discs.get(id);
|
||||
}
|
||||
|
||||
public boolean hasDisc(int id) {
|
||||
return this.discs.containsKey(id);
|
||||
}
|
||||
|
||||
public GameDisc addDisc(int discId) {
|
||||
// Sanity check to make sure we dont have this character already
|
||||
if (this.hasDisc(discId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.addDisc(GameData.getDiscDataTable().get(discId));
|
||||
}
|
||||
|
||||
private GameDisc addDisc(DiscDef data) {
|
||||
// Sanity check to make sure we dont have this character already
|
||||
if (this.hasDisc(data.getId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create disc
|
||||
var disc = new GameDisc(this.getPlayer(), data);
|
||||
|
||||
// Save to database
|
||||
disc.save();
|
||||
|
||||
// Add to discs
|
||||
this.discs.put(disc.getDiscId(), disc);
|
||||
return disc;
|
||||
}
|
||||
|
||||
public Collection<GameDisc> getDiscCollection() {
|
||||
return this.getDiscs().values();
|
||||
}
|
||||
|
||||
|
||||
// Database
|
||||
|
||||
public void loadFromDatabase() {
|
||||
var db = Nebula.getGameDatabase();
|
||||
|
||||
db.getObjects(Character.class, "playerUid", getPlayerUid()).forEach(character -> {
|
||||
// Get data
|
||||
var data = GameData.getCharacterDataTable().get(character.getCharId());
|
||||
|
||||
// Validate
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
character.setPlayer(this.getPlayer());
|
||||
character.setData(data);
|
||||
|
||||
// Add to characters
|
||||
this.characters.put(character.getCharId(), character);
|
||||
});
|
||||
|
||||
|
||||
|
||||
db.getObjects(GameDisc.class, "playerUid", getPlayerUid()).forEach(disc -> {
|
||||
// Get data
|
||||
var data = GameData.getDiscDataTable().get(disc.getDiscId());
|
||||
if (data == null) return;
|
||||
|
||||
disc.setPlayer(this.getPlayer());
|
||||
disc.setData(data);
|
||||
|
||||
// Add
|
||||
this.discs.put(disc.getDiscId(), disc);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
package emu.nebula.game.character;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.DiscDef;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.proto.Public.Disc;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerDisc;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "discs", useDiscriminator = false)
|
||||
public class GameDisc implements GameDatabaseObject {
|
||||
@Id
|
||||
private ObjectId uid;
|
||||
@Indexed
|
||||
private int playerUid;
|
||||
|
||||
private transient DiscDef data;
|
||||
private transient Player player;
|
||||
|
||||
private int discId;
|
||||
private int level;
|
||||
private int exp;
|
||||
private int phase;
|
||||
private int star;
|
||||
|
||||
private long createTime;
|
||||
|
||||
@Deprecated // Morphia only!
|
||||
public GameDisc() {
|
||||
|
||||
}
|
||||
|
||||
public GameDisc(Player player, int discId) {
|
||||
this(player, GameData.getDiscDataTable().get(discId));
|
||||
}
|
||||
|
||||
public GameDisc(Player player, DiscDef data) {
|
||||
this.player = player;
|
||||
this.playerUid = player.getUid();
|
||||
this.data = data;
|
||||
this.discId = data.getId();
|
||||
this.level = 1;
|
||||
this.createTime = Nebula.getCurrentTime();
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void setData(DiscDef data) {
|
||||
if (this.data == null && data.getId() == this.getDiscId()) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxGainableExp() {
|
||||
if (this.getLevel() >= this.getMaxLevel()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int maxLevel = this.getMaxLevel();
|
||||
int max = 0;
|
||||
|
||||
for (int i = this.getLevel() + 1; i <= maxLevel; i++) {
|
||||
int dataId = (this.getData().getStrengthenGroupId() * 1000) + i;
|
||||
var data = GameData.getDiscStrengthenDataTable().get(dataId);
|
||||
|
||||
if (data != null) {
|
||||
max += data.getExp();
|
||||
}
|
||||
}
|
||||
|
||||
return Math.max(max - this.getExp(), 0);
|
||||
}
|
||||
|
||||
public int getMaxExp() {
|
||||
if (this.getLevel() >= this.getMaxLevel()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dataId = (this.getData().getStrengthenGroupId() * 1000) + (this.level + 1);
|
||||
var data = GameData.getDiscStrengthenDataTable().get(dataId);
|
||||
return data != null ? data.getExp() : 0;
|
||||
}
|
||||
|
||||
public int getMaxLevel() {
|
||||
return 10 + (this.getPhase() * 10);
|
||||
}
|
||||
|
||||
public void addExp(int amount) {
|
||||
// Setup
|
||||
int expRequired = this.getMaxExp();
|
||||
|
||||
// Add exp
|
||||
this.exp += amount;
|
||||
|
||||
// Check for level ups
|
||||
while (this.exp >= expRequired && expRequired > 0) {
|
||||
this.level += 1;
|
||||
this.exp -= expRequired;
|
||||
|
||||
expRequired = this.getMaxExp();
|
||||
}
|
||||
|
||||
// Clamp exp
|
||||
if (this.getLevel() >= this.getMaxLevel()) {
|
||||
this.exp = 0;
|
||||
}
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
}
|
||||
|
||||
// Handlers
|
||||
|
||||
public PlayerChangeInfo upgrade(ItemParamMap params) {
|
||||
// Calculate exp gained
|
||||
int exp = 0;
|
||||
|
||||
// Check if item is an exp item
|
||||
for (var entry : params.getEntrySet()) {
|
||||
var data = GameData.getDiscItemExpDataTable().get(entry.getIntKey());
|
||||
if (data == null) return null;
|
||||
|
||||
exp += data.getExp() * entry.getIntValue();
|
||||
}
|
||||
|
||||
// Clamp exp gain
|
||||
exp = Math.min(this.getMaxGainableExp(), exp);
|
||||
|
||||
// Calculate gold required
|
||||
params.add(GameConstants.GOLD_ITEM_ID, (int) Math.ceil(exp * 0.25D));
|
||||
|
||||
// Verify that the player has the items
|
||||
if (!this.getPlayer().getInventory().verifyItems(params)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create change info
|
||||
var changes = new PlayerChangeInfo();
|
||||
|
||||
// Remove items
|
||||
this.getPlayer().getInventory().removeItems(params, changes);
|
||||
|
||||
// Add exp
|
||||
this.addExp(exp);
|
||||
|
||||
// Success
|
||||
return changes.setSuccess(true);
|
||||
}
|
||||
|
||||
public PlayerChangeInfo promote() {
|
||||
// TODO check player level to make sure they can advance this disc
|
||||
|
||||
// Get promote data
|
||||
int phaseId = (this.getData().getPromoteGroupId() * 1000) + (this.phase + 1);
|
||||
var data = GameData.getDiscPromoteDataTable().get(phaseId);
|
||||
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Verify that the player has the items
|
||||
if (!this.getPlayer().getInventory().verifyItems(data.getMaterials())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove items
|
||||
var changes = this.getPlayer().getInventory().removeItems(data.getMaterials(), null);
|
||||
|
||||
// Add phase level
|
||||
this.phase++;
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Success
|
||||
return changes.setSuccess(true);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public Disc toProto() {
|
||||
var proto = Disc.newInstance()
|
||||
.setId(this.getDiscId())
|
||||
.setLevel(this.getLevel())
|
||||
.setExp(this.getExp())
|
||||
.setPhase(this.getPhase())
|
||||
.setStar(this.getStar())
|
||||
.setCreateTime(this.getCreateTime());
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
public StarTowerDisc toStarTowerProto() {
|
||||
var proto = StarTowerDisc.newInstance()
|
||||
.setId(this.getDiscId())
|
||||
.setLevel(this.getLevel())
|
||||
.setPhase(this.getPhase())
|
||||
.setStar(this.getStar());
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package emu.nebula.game.formation;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.proto.Public.FormationInfo;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public class Formation {
|
||||
private int num;
|
||||
private int[] charIds;
|
||||
private int[] discIds;
|
||||
|
||||
@Deprecated
|
||||
public Formation() {
|
||||
|
||||
}
|
||||
|
||||
public Formation(int num) {
|
||||
this.num = num;
|
||||
this.charIds = new int[3];
|
||||
this.discIds = new int[6];
|
||||
}
|
||||
|
||||
public Formation(FormationInfo formation) {
|
||||
this.num = formation.getNumber();
|
||||
this.charIds = formation.getCharIds().toArray();
|
||||
this.discIds = formation.getDiscIds().toArray();
|
||||
}
|
||||
|
||||
public int getCharIdAt(int i) {
|
||||
if (i < 0 || i >= this.charIds.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.charIds[i];
|
||||
}
|
||||
|
||||
public int getDiscIdAt(int i) {
|
||||
if (i < 0 || i >= this.discIds.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.discIds[i];
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public FormationInfo toProto() {
|
||||
var proto = FormationInfo.newInstance()
|
||||
.setNumber(this.getNum())
|
||||
.addAllCharIds(this.getCharIds())
|
||||
.addAllDiscIds(this.getDiscIds());
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package emu.nebula.game.formation;
|
||||
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.proto.Public.FormationInfo;
|
||||
import emu.nebula.proto.Public.TowerFormation;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "formations", useDiscriminator = false)
|
||||
public class FormationManager extends PlayerManager implements GameDatabaseObject {
|
||||
@Id
|
||||
private int uid;
|
||||
|
||||
private Map<Integer, Formation> formations;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public FormationManager() {
|
||||
|
||||
}
|
||||
|
||||
public FormationManager(Player player) {
|
||||
super(player);
|
||||
this.uid = player.getUid();
|
||||
this.formations = new HashMap<>();
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public Formation getFormationById(int num) {
|
||||
return this.formations.get(num);
|
||||
}
|
||||
|
||||
public boolean updateFormation(FormationInfo info) {
|
||||
// Sanity check
|
||||
if (info.getNumber() < 1 || info.getNumber() > GameConstants.MAX_FORMATIONS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// More sanity
|
||||
if (info.getCharIds().length() < 1 || info.getCharIds().length() > 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.getDiscIds().length() < 3 || info.getDiscIds().length() > 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate formation to make sure we have all the chars and discs
|
||||
// TODO
|
||||
|
||||
// Create formation
|
||||
var formation = new Formation(info);
|
||||
|
||||
// Add to formations map
|
||||
this.formations.put(formation.getNum(), formation);
|
||||
|
||||
// Save to db
|
||||
Nebula.getGameDatabase().update(this, this.getPlayerUid(), "formations." + formation.getNum(), formation, true);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public TowerFormation toProto() {
|
||||
var proto = TowerFormation.newInstance();
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package emu.nebula.game.inventory;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.proto.Public.Item;
|
||||
import emu.nebula.util.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "items", useDiscriminator = false)
|
||||
public class GameItem implements GameDatabaseObject {
|
||||
@Id
|
||||
private ObjectId uid;
|
||||
@Indexed
|
||||
private int playerUid;
|
||||
|
||||
private int itemId;
|
||||
private int count;
|
||||
|
||||
@Deprecated
|
||||
public GameItem() {
|
||||
|
||||
}
|
||||
|
||||
public GameItem(Player player, int id, int count) {
|
||||
this.playerUid = player.getUid();
|
||||
this.itemId = id;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int add(int amount) {
|
||||
int oldCount = this.count;
|
||||
this.count = Utils.safeAdd(this.count, amount, Integer.MAX_VALUE, 0);
|
||||
return this.count - oldCount;
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
if (this.getCount() <= 0) {
|
||||
if (this.getUid() != null) {
|
||||
Nebula.getGameDatabase().delete(this);
|
||||
}
|
||||
} else {
|
||||
Nebula.getGameDatabase().save(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public Item toProto() {
|
||||
var proto = Item.newInstance()
|
||||
.setTid(this.getItemId())
|
||||
.setQty(this.getCount());
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package emu.nebula.game.inventory;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.proto.Public.Res;
|
||||
import emu.nebula.util.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "resources", useDiscriminator = false)
|
||||
public class GameResource implements GameDatabaseObject {
|
||||
@Id
|
||||
private ObjectId uid;
|
||||
@Indexed
|
||||
private int playerUid;
|
||||
|
||||
public int resourceId;
|
||||
public int count;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public GameResource() {
|
||||
|
||||
}
|
||||
|
||||
public GameResource(Player player, int id, int count) {
|
||||
this.playerUid = player.getUid();
|
||||
this.resourceId = id;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int add(int amount) {
|
||||
int oldCount = this.count;
|
||||
this.count = Utils.safeAdd(this.count, amount, Integer.MAX_VALUE, 0);
|
||||
return this.count - oldCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
if (this.getCount() <= 0) {
|
||||
if (this.getUid() != null) {
|
||||
Nebula.getGameDatabase().delete(this);
|
||||
}
|
||||
} else {
|
||||
Nebula.getGameDatabase().save(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public Res toProto() {
|
||||
var proto = Res.newInstance()
|
||||
.setTid(this.getResourceId())
|
||||
.setQty(this.getCount());
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
package emu.nebula.game.inventory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.proto.Public.Item;
|
||||
import emu.nebula.proto.Public.Res;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class Inventory extends PlayerManager {
|
||||
private final Int2ObjectMap<GameResource> resources;
|
||||
private final Int2ObjectMap<GameItem> items;
|
||||
|
||||
public Inventory(Player player) {
|
||||
super(player);
|
||||
|
||||
this.resources = new Int2ObjectOpenHashMap<>();
|
||||
this.items = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
// Resources
|
||||
|
||||
public synchronized int getResourceCount(int id) {
|
||||
var res = this.resources.get(id);
|
||||
return res != null ? res.getCount() : 0;
|
||||
}
|
||||
|
||||
// Items
|
||||
|
||||
public synchronized int getItemCount(int id) {
|
||||
var item = this.getItems().get(id);
|
||||
return item != null ? item.getCount() : 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
public synchronized PlayerChangeInfo addItem(int id, int count, PlayerChangeInfo changes) {
|
||||
// Changes
|
||||
if (changes == null) {
|
||||
changes = new PlayerChangeInfo();
|
||||
}
|
||||
|
||||
// Sanity
|
||||
if (count == 0) {
|
||||
return changes;
|
||||
}
|
||||
|
||||
// Get game data
|
||||
var data = GameData.getItemDataTable().get(id);
|
||||
if (data == null) {
|
||||
return changes;
|
||||
}
|
||||
|
||||
// Set amount
|
||||
int amount = count;
|
||||
|
||||
// Add item
|
||||
switch (data.getItemType()) {
|
||||
case Res -> {
|
||||
var res = this.resources.get(id);
|
||||
int diff = 0;
|
||||
|
||||
if (amount > 0) {
|
||||
// Add resource
|
||||
if (res == null) {
|
||||
res = new GameResource(this.getPlayer(), id, amount);
|
||||
this.resources.put(res.getResourceId(), res);
|
||||
|
||||
diff = amount;
|
||||
} else {
|
||||
diff = res.add(amount);
|
||||
}
|
||||
|
||||
res.save();
|
||||
} else {
|
||||
// Remove resource
|
||||
if (res == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
diff = res.add(amount);
|
||||
res.save();
|
||||
|
||||
if (res.getCount() < 0) {
|
||||
this.resources.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (diff != 0) {
|
||||
var change = Res.newInstance()
|
||||
.setTid(id)
|
||||
.setQty(diff);
|
||||
|
||||
changes.add(change);
|
||||
}
|
||||
}
|
||||
case Item -> {
|
||||
var item = this.items.get(id);
|
||||
int diff = 0;
|
||||
|
||||
if (amount > 0) {
|
||||
// Add resource
|
||||
if (item == null) {
|
||||
item = new GameItem(this.getPlayer(), id, amount);
|
||||
this.items.put(item.getItemId(), item);
|
||||
|
||||
diff = amount;
|
||||
} else {
|
||||
diff = item.add(amount);
|
||||
}
|
||||
|
||||
item.save();
|
||||
} else {
|
||||
// Remove resource
|
||||
if (item == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
diff = item.add(amount);
|
||||
item.save();
|
||||
|
||||
if (item.getCount() < 0) {
|
||||
this.resources.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (diff != 0) {
|
||||
var change = Item.newInstance()
|
||||
.setTid(id)
|
||||
.setQty(diff);
|
||||
|
||||
changes.add(change);
|
||||
}
|
||||
}
|
||||
case Disc -> {
|
||||
if (amount <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
var disc = getPlayer().getCharacters().addDisc(id);
|
||||
|
||||
if (disc != null) {
|
||||
changes.add(disc.toProto());
|
||||
}
|
||||
}
|
||||
case Char -> {
|
||||
if (amount <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
var character = getPlayer().getCharacters().addCharacter(id);
|
||||
|
||||
if (character != null) {
|
||||
changes.add(character.toProto());
|
||||
}
|
||||
}
|
||||
case WorldRankExp -> {
|
||||
this.getPlayer().addExp(amount, changes);
|
||||
}
|
||||
default -> {
|
||||
// Not implemented
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return changes;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public synchronized PlayerChangeInfo addItems(List<ItemParam> params, PlayerChangeInfo changes) {
|
||||
// Changes
|
||||
if (changes == null) {
|
||||
changes = new PlayerChangeInfo();
|
||||
}
|
||||
|
||||
for (ItemParam param : params) {
|
||||
this.addItem(param.getId(), param.getCount(), changes);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public synchronized PlayerChangeInfo addItems(ItemParamMap params) {
|
||||
return this.addItems(params, null);
|
||||
}
|
||||
|
||||
public synchronized PlayerChangeInfo addItems(ItemParamMap params, PlayerChangeInfo changes) {
|
||||
// Changes
|
||||
if (changes == null) {
|
||||
changes = new PlayerChangeInfo();
|
||||
}
|
||||
|
||||
for (var param : params.getEntrySet()) {
|
||||
this.addItem(param.getIntKey(), param.getIntValue(), changes);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public synchronized PlayerChangeInfo removeItem(int id, int count, PlayerChangeInfo changes) {
|
||||
if (count > 0) {
|
||||
count = -count;
|
||||
}
|
||||
|
||||
return this.addItem(id, count, changes);
|
||||
}
|
||||
|
||||
public synchronized PlayerChangeInfo removeItems(ItemParamMap params) {
|
||||
return this.removeItems(params, null);
|
||||
}
|
||||
|
||||
public synchronized PlayerChangeInfo removeItems(ItemParamMap params, PlayerChangeInfo changes) {
|
||||
// Changes
|
||||
if (changes == null) {
|
||||
changes = new PlayerChangeInfo();
|
||||
}
|
||||
|
||||
for (var param : params.getEntrySet()) {
|
||||
this.removeItem(param.getIntKey(), param.getIntValue(), changes);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has enough quanity of this item
|
||||
*/
|
||||
public synchronized boolean verifyItem(int id, int count) {
|
||||
// Sanity check
|
||||
if (count == 0) {
|
||||
return true;
|
||||
} else if (count < 0) {
|
||||
// Return false if we are trying to verify negative numbers
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get game data
|
||||
var data = GameData.getItemDataTable().get(id);
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = switch (data.getItemType()) {
|
||||
case Res -> {
|
||||
yield this.getResourceCount(id) >= count;
|
||||
}
|
||||
case Item -> {
|
||||
yield this.getItemCount(id) >= count;
|
||||
}
|
||||
case Disc -> {
|
||||
yield getPlayer().getCharacters().hasDisc(id);
|
||||
}
|
||||
case Char -> {
|
||||
yield getPlayer().getCharacters().hasCharacter(id);
|
||||
}
|
||||
default -> {
|
||||
// Not implemented
|
||||
yield false;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized boolean verifyItems(ItemParamMap params) {
|
||||
boolean hasItems = true;
|
||||
|
||||
for (var param : params.getEntrySet()) {
|
||||
hasItems = this.verifyItem(param.getIntKey(), param.getIntValue());
|
||||
|
||||
if (!hasItems) {
|
||||
return hasItems;
|
||||
}
|
||||
}
|
||||
|
||||
return hasItems;
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
public void loadFromDatabase() {
|
||||
var db = Nebula.getGameDatabase();
|
||||
|
||||
db.getObjects(GameItem.class, "playerUid", getPlayerUid()).forEach(item -> {
|
||||
// Get data
|
||||
var data = GameData.getItemDataTable().get(item.getItemId());
|
||||
if (data == null) return;
|
||||
|
||||
// Add
|
||||
this.items.put(item.getItemId(), item);
|
||||
});
|
||||
|
||||
db.getObjects(GameResource.class, "playerUid", getPlayerUid()).forEach(res -> {
|
||||
// Get data
|
||||
var data = GameData.getItemDataTable().get(res.getResourceId());
|
||||
if (data == null) return;
|
||||
|
||||
// Add
|
||||
this.resources.put(res.getResourceId(), res);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package emu.nebula.game.inventory;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.proto.Public.ItemTpl;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public class ItemParam {
|
||||
public int id;
|
||||
public int count;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public ItemParam() {
|
||||
|
||||
}
|
||||
|
||||
public ItemParam(int id, int count) {
|
||||
this.id = id;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public ItemTpl toProto() {
|
||||
var proto = ItemTpl.newInstance()
|
||||
.setTid(this.getId())
|
||||
.setQty(this.getCount());
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package emu.nebula.game.inventory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import emu.nebula.proto.Public.ItemInfo;
|
||||
import emu.nebula.proto.Public.ItemTpl;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import us.hebi.quickbuf.RepeatedMessage;
|
||||
|
||||
public class ItemParamMap extends Int2IntOpenHashMap {
|
||||
private static final long serialVersionUID = -4186524272780523459L;
|
||||
|
||||
public FastEntrySet entries() {
|
||||
return this.int2IntEntrySet();
|
||||
}
|
||||
|
||||
@Override @Deprecated
|
||||
public int addTo(int itemId, int count) {
|
||||
return this.add(itemId, count);
|
||||
}
|
||||
|
||||
public int add(int itemId, int count) {
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return super.addTo(itemId, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all item params from the other map to this one
|
||||
* @param map The other item param map
|
||||
*/
|
||||
public void add(ItemParamMap map) {
|
||||
for (var entry : map.entries()) {
|
||||
this.add(entry.getIntKey(), entry.getIntValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new ItemParamMap with item amounts multiplied
|
||||
* @param mult Value to multiply all item amounts in this map by
|
||||
* @return
|
||||
*/
|
||||
public ItemParamMap mulitply(int multiplier) {
|
||||
var params = new ItemParamMap();
|
||||
|
||||
for (var entry : this.int2IntEntrySet()) {
|
||||
params.put(entry.getIntKey(), entry.getIntValue() * multiplier);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
public FastEntrySet getEntrySet() {
|
||||
return this.int2IntEntrySet();
|
||||
}
|
||||
|
||||
public List<ItemParam> toList() {
|
||||
List<ItemParam> list = new ArrayList<>();
|
||||
|
||||
for (var entry : this.int2IntEntrySet()) {
|
||||
list.add(new ItemParam(entry.getIntKey(), entry.getIntValue()));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public Stream<ItemTpl> itemTemplateStream() {
|
||||
return getEntrySet()
|
||||
.stream()
|
||||
.map(e -> ItemTpl.newInstance().setTid(e.getIntKey()).setQty(e.getIntValue()));
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public static ItemParamMap fromTemplates(RepeatedMessage<ItemTpl> items) {
|
||||
var map = new ItemParamMap();
|
||||
|
||||
for (var template : items) {
|
||||
map.add(template.getTid(), template.getQty());
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static ItemParamMap fromItemInfos(RepeatedMessage<ItemInfo> items) {
|
||||
var map = new ItemParamMap();
|
||||
|
||||
for (var template : items) {
|
||||
map.add(template.getTid(), template.getQty());
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package emu.nebula.game.inventory;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum ItemSubType {
|
||||
Res (1),
|
||||
Item (2),
|
||||
Char (3),
|
||||
Energy (4),
|
||||
WorldRankExp (5),
|
||||
CharShard (6),
|
||||
Disc (8),
|
||||
TalentStrengthen (9),
|
||||
DiscStrengthen (12),
|
||||
DiscPromote (13),
|
||||
TreasureBox (17),
|
||||
GearTreasureBox (18),
|
||||
SubNoteSkill (19),
|
||||
SkillStrengthen (24),
|
||||
CharacterLimitBreak (25),
|
||||
MonthlyCard (30),
|
||||
EnergyItem (31),
|
||||
ComCYO (32),
|
||||
OutfitCYO (33),
|
||||
RandomPackage (34),
|
||||
Equipment (35),
|
||||
FateCard (37),
|
||||
EquipmentExp (38),
|
||||
DiscLimitBreak (40),
|
||||
Potential (41),
|
||||
SpecificPotential (42),
|
||||
Honor (43),
|
||||
CharacterYO (44),
|
||||
PlayHead (45),
|
||||
CharacterSkin (46);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
private final static Int2ObjectMap<ItemSubType> map = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
for (ItemSubType type : ItemSubType.values()) {
|
||||
map.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
private ItemSubType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ItemSubType getByValue(int value) {
|
||||
return map.get(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package emu.nebula.game.inventory;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum ItemType {
|
||||
Res (1),
|
||||
Item (2),
|
||||
Char (3),
|
||||
Energy (4),
|
||||
WorldRankExp (5),
|
||||
RogueItem (6),
|
||||
Disc (7),
|
||||
Equipment (8),
|
||||
CharacterSkin (9),
|
||||
MonthlyCard (10),
|
||||
Title (11),
|
||||
Honor (12),
|
||||
HeadItem (13);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
private final static Int2ObjectMap<ItemType> map = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
for (ItemType type : ItemType.values()) {
|
||||
map.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
private ItemType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ItemType getByValue(int value) {
|
||||
return map.get(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package emu.nebula.game.mail;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.proto.Public.Mail;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public class GameMail {
|
||||
private int id;
|
||||
|
||||
private String author;
|
||||
private String subject;
|
||||
private String desc;
|
||||
|
||||
private ItemParamMap attachments;
|
||||
|
||||
@Setter private boolean read;
|
||||
@Setter private boolean recv;
|
||||
@Setter private boolean pin;
|
||||
|
||||
private long flag;
|
||||
private long time;
|
||||
private long expiry;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public GameMail() {
|
||||
|
||||
}
|
||||
|
||||
public GameMail(String author, String subject, String desc) {
|
||||
this.author = author;
|
||||
this.subject = subject;
|
||||
this.desc = desc;
|
||||
this.time = Nebula.getCurrentTime();
|
||||
this.expiry = this.time + TimeUnit.DAYS.toSeconds(30);
|
||||
}
|
||||
|
||||
protected void setId(int id) {
|
||||
if (this.id == 0) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canRemove() {
|
||||
return (this.isRead() || this.isRecv()) && !this.isPin() && (this.hasAttachments() && this.isRecv());
|
||||
}
|
||||
|
||||
public boolean hasAttachments() {
|
||||
return this.attachments != null;
|
||||
}
|
||||
|
||||
public void addAttachment(int itemId, int count) {
|
||||
if (this.attachments == null) {
|
||||
this.attachments = new ItemParamMap();
|
||||
}
|
||||
|
||||
this.attachments.add(itemId, count);
|
||||
}
|
||||
|
||||
public Mail toProto() {
|
||||
var proto = Mail.newInstance()
|
||||
.setId(this.getId())
|
||||
.setAuthor(this.getAuthor())
|
||||
.setSubject(this.getSubject())
|
||||
.setDesc(this.getDesc())
|
||||
.setTime(this.getTime())
|
||||
.setRead(this.isRead())
|
||||
.setRecv(this.isRecv())
|
||||
.setPin(this.isPin())
|
||||
.setFlag(this.getFlag())
|
||||
.setDeadline(this.getExpiry());
|
||||
|
||||
if (this.getAttachments() != null) {
|
||||
this.getAttachments().itemTemplateStream()
|
||||
.forEach(proto::addAttachments);
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package emu.nebula.game.mail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "mailbox", useDiscriminator = false)
|
||||
public class Mailbox implements GameDatabaseObject, Iterable<GameMail> {
|
||||
@Id
|
||||
private int uid;
|
||||
private int lastMailId;
|
||||
|
||||
private List<GameMail> list;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public Mailbox() {
|
||||
|
||||
}
|
||||
|
||||
public Mailbox(Player player) {
|
||||
this.uid = player.getUid();
|
||||
this.list = new ArrayList<>();
|
||||
this.save();
|
||||
}
|
||||
|
||||
// TODO optimize to an O(n) algorithm like a map
|
||||
public GameMail getMailById(int id) {
|
||||
return this.getList().stream()
|
||||
.filter(m -> m.getId() == id)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public void sendMail(GameMail mail) {
|
||||
// Set mail id
|
||||
mail.setId(++this.lastMailId);
|
||||
|
||||
// Add to mail list
|
||||
this.list.add(mail);
|
||||
|
||||
// Save to database
|
||||
Nebula.getGameDatabase().update(this, getUid(), "lastMailId", this.getLastMailId());
|
||||
Nebula.getGameDatabase().addToList(this, getUid(), "list", mail);
|
||||
}
|
||||
|
||||
public boolean readMail(int id, long flag) {
|
||||
// Get mail
|
||||
var mail = this.getMailById(id);
|
||||
|
||||
if (mail == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set read
|
||||
mail.setRead(true);
|
||||
|
||||
// Update in database
|
||||
Nebula.getGameDatabase().updateNested(this, getUid(), "list.id", id, "list.$.read", true);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
public GameMail pinMail(int id, long flag, boolean pin) {
|
||||
// Get mail
|
||||
var mail = this.getMailById(id);
|
||||
|
||||
if (mail == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set pin
|
||||
mail.setPin(pin);
|
||||
|
||||
// Update in database
|
||||
Nebula.getGameDatabase().updateNested(this, getUid(), "list.id", id, "list.$.pin", true);
|
||||
|
||||
// Success
|
||||
return mail;
|
||||
}
|
||||
|
||||
public PlayerChangeInfo recvMail(Player player, int id) {
|
||||
// Get mails that we want to claim
|
||||
List<GameMail> mails = null;
|
||||
|
||||
if (id == 0) {
|
||||
// Claim all
|
||||
mails = this.getList()
|
||||
.stream()
|
||||
.filter(mail -> !mail.isRecv() && mail.hasAttachments())
|
||||
.toList();
|
||||
} else {
|
||||
// Claim one
|
||||
var mail = this.getMailById(id);
|
||||
|
||||
if (mail != null && !mail.isRecv() && mail.hasAttachments()) {
|
||||
mails = List.of(mail);
|
||||
}
|
||||
}
|
||||
|
||||
// Create change info
|
||||
var changes = new PlayerChangeInfo();
|
||||
|
||||
// Sanity
|
||||
if (mails == null || mails.isEmpty()) {
|
||||
return changes;
|
||||
}
|
||||
|
||||
// Recieved mail id list
|
||||
var recvMails = new IntArrayList();
|
||||
|
||||
// Recv mails
|
||||
for (var mail : mails) {
|
||||
// Add attachments to player
|
||||
player.getInventory().addItems(mail.getAttachments(), changes);
|
||||
|
||||
// Set claimed flag
|
||||
mail.setRecv(true);
|
||||
|
||||
// Add to recvied mail list
|
||||
recvMails.add(mail.getId());
|
||||
|
||||
// Update in database
|
||||
Nebula.getGameDatabase().updateNested(this, getUid(), "list.id", mail.getId(), "list.$.recv", true);
|
||||
}
|
||||
|
||||
// Set extra change data
|
||||
changes.setExtraData(recvMails);
|
||||
|
||||
// Success
|
||||
return changes.setSuccess(true);
|
||||
}
|
||||
|
||||
public IntList removeMail(Player player, int id) {
|
||||
// Get mails that we want to claim
|
||||
Set<GameMail> toRemove = null;
|
||||
|
||||
if (id == 0) {
|
||||
// Claim all
|
||||
toRemove = this.getList()
|
||||
.stream()
|
||||
.filter(mail -> mail.canRemove())
|
||||
.collect(Collectors.toSet());
|
||||
} else {
|
||||
// Claim one
|
||||
var mail = this.getMailById(id);
|
||||
|
||||
if (mail != null && mail.canRemove()) {
|
||||
toRemove = Set.of(mail);
|
||||
}
|
||||
}
|
||||
|
||||
// Recieved mail id list
|
||||
var removed = new IntArrayList();
|
||||
|
||||
// Sanity check
|
||||
if (toRemove == null || toRemove.isEmpty()) {
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Remove
|
||||
var it = this.getList().iterator();
|
||||
while (it.hasNext()) {
|
||||
var mail = it.next();
|
||||
|
||||
if (toRemove.contains(mail)) {
|
||||
removed.add(mail.getId());
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Save
|
||||
this.save();
|
||||
|
||||
// Success
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<GameMail> iterator() {
|
||||
return this.getList().iterator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
package emu.nebula.game.player;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.account.Account;
|
||||
import emu.nebula.game.character.CharacterStorage;
|
||||
import emu.nebula.game.formation.FormationManager;
|
||||
import emu.nebula.game.inventory.Inventory;
|
||||
import emu.nebula.game.mail.Mailbox;
|
||||
import emu.nebula.game.tower.StarTowerManager;
|
||||
import emu.nebula.net.GameSession;
|
||||
import emu.nebula.proto.PlayerData.PlayerInfo;
|
||||
import emu.nebula.proto.Public.NewbieInfo;
|
||||
import emu.nebula.proto.Public.QuestType;
|
||||
import emu.nebula.proto.Public.WorldClass;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "players", useDiscriminator = false)
|
||||
public class Player implements GameDatabaseObject {
|
||||
@Id private int uid;
|
||||
@Indexed private String accountUid;
|
||||
|
||||
private transient Account account;
|
||||
private transient Set<GameSession> sessions;
|
||||
|
||||
// Details
|
||||
private String name;
|
||||
private boolean gender;
|
||||
private int headIcon;
|
||||
private int skinId;
|
||||
private int titlePrefix;
|
||||
private int titleSuffix;
|
||||
private int level;
|
||||
private int exp;
|
||||
|
||||
private int energy;
|
||||
|
||||
private IntSet boards;
|
||||
private IntSet titles;
|
||||
|
||||
private long createTime;
|
||||
|
||||
// Managers
|
||||
private final transient CharacterStorage characters;
|
||||
private final transient Inventory inventory;
|
||||
|
||||
// Referenced data
|
||||
private transient FormationManager formations;
|
||||
private transient Mailbox mailbox;
|
||||
private transient StarTowerManager starTowerManager;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public Player() {
|
||||
this.sessions = new HashSet<>();
|
||||
this.characters = new CharacterStorage(this);
|
||||
this.inventory = new Inventory(this);
|
||||
}
|
||||
|
||||
public Player(Account account, String name, boolean gender) {
|
||||
this();
|
||||
|
||||
// Set uid first
|
||||
if (account.getReservedPlayerUid() > 0) {
|
||||
this.uid = account.getReservedPlayerUid();
|
||||
} else {
|
||||
this.uid = Nebula.getGameDatabase().getNextObjectId(Player.class);
|
||||
}
|
||||
|
||||
// Set basic info
|
||||
this.accountUid = account.getUid();
|
||||
this.name = name;
|
||||
this.gender = gender;
|
||||
this.headIcon = 101;
|
||||
this.skinId = 10301;
|
||||
this.titlePrefix = 1;
|
||||
this.titleSuffix = 2;
|
||||
this.level = 1;
|
||||
this.boards = new IntOpenHashSet();
|
||||
this.titles = new IntOpenHashSet();
|
||||
this.createTime = Nebula.getCurrentTime();
|
||||
|
||||
// Add starter characters
|
||||
this.getCharacters().addCharacter(103);
|
||||
this.getCharacters().addCharacter(112);
|
||||
this.getCharacters().addCharacter(113);
|
||||
|
||||
// Add starter discs
|
||||
this.getCharacters().addDisc(211001);
|
||||
this.getCharacters().addDisc(211005);
|
||||
this.getCharacters().addDisc(211007);
|
||||
this.getCharacters().addDisc(211008);
|
||||
|
||||
// Add titles
|
||||
this.getTitles().add(this.getTitlePrefix());
|
||||
this.getTitles().add(this.getTitleSuffix());
|
||||
|
||||
// Add board ids
|
||||
this.getBoards().add(410301);
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
if (this.account == null) {
|
||||
this.account = Nebula.getAccountDatabase().getObjectByField(Account.class, "_id", this.getAccountUid());
|
||||
}
|
||||
|
||||
return this.account;
|
||||
}
|
||||
|
||||
public void addSession(GameSession session) {
|
||||
synchronized (this.sessions) {
|
||||
this.sessions.add(session);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeSession(GameSession session) {
|
||||
synchronized (this.sessions) {
|
||||
this.sessions.remove(session);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSessions() {
|
||||
synchronized (this.sessions) {
|
||||
return !this.sessions.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getGender() {
|
||||
return this.gender;
|
||||
}
|
||||
|
||||
public boolean editName(String newName) {
|
||||
// Sanity check
|
||||
if (newName == null || newName.isEmpty() || newName.equals(this.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Limit name length
|
||||
if (newName.length() > 20) {
|
||||
newName = newName.substring(0, 19);
|
||||
}
|
||||
|
||||
// Set name
|
||||
this.name = newName;
|
||||
|
||||
// Update in database
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "name", this.getName());
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
public void editGender() {
|
||||
// Set name
|
||||
this.gender = !this.gender;
|
||||
|
||||
// Update in database
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "gender", this.getGender());
|
||||
}
|
||||
|
||||
public void setNewbieInfo(int groupId, int stepId) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public int getMaxExp() {
|
||||
var data = GameData.getWorldClassDataTable().get(this.level + 1);
|
||||
return data != null ? data.getExp() : 0;
|
||||
}
|
||||
|
||||
public PlayerChangeInfo addExp(int amount, PlayerChangeInfo changes) {
|
||||
// Check if changes is null
|
||||
if (changes == null) {
|
||||
changes = new PlayerChangeInfo();
|
||||
}
|
||||
|
||||
// Sanity
|
||||
if (amount <= 0) {
|
||||
return changes;
|
||||
}
|
||||
|
||||
// Setup
|
||||
int oldLevel = this.getLevel();
|
||||
int oldExp = this.getExp();
|
||||
int expRequired = this.getMaxExp();
|
||||
|
||||
// Add exp
|
||||
this.exp += amount;
|
||||
|
||||
// Check for level ups
|
||||
while (this.exp >= expRequired && expRequired > 0) {
|
||||
this.level += 1;
|
||||
this.exp -= expRequired;
|
||||
|
||||
expRequired = this.getMaxExp();
|
||||
}
|
||||
|
||||
// Save to database
|
||||
Nebula.getGameDatabase().update(
|
||||
this,
|
||||
this.getUid(),
|
||||
"level",
|
||||
this.getLevel(),
|
||||
"exp",
|
||||
this.getExp()
|
||||
);
|
||||
|
||||
// Calculate changes
|
||||
var proto = WorldClass.newInstance()
|
||||
.setAddClass(this.getLevel() - oldLevel)
|
||||
.setExpChange(this.getExp() - oldExp);
|
||||
|
||||
changes.add(proto);
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public void sendMessage(String string) {
|
||||
// Empty
|
||||
}
|
||||
|
||||
// Login
|
||||
|
||||
public void onLoad() {
|
||||
// Load from database
|
||||
this.getCharacters().loadFromDatabase();
|
||||
this.getInventory().loadFromDatabase();
|
||||
|
||||
// Load referenced classes
|
||||
this.formations = Nebula.getGameDatabase().getObjectByField(FormationManager.class, "_id", this.getUid());
|
||||
if (this.formations == null) {
|
||||
this.formations = new FormationManager(this);
|
||||
} else {
|
||||
this.formations.setPlayer(this);
|
||||
}
|
||||
|
||||
this.mailbox = Nebula.getGameDatabase().getObjectByField(Mailbox.class, "_id", this.getUid());
|
||||
if (this.mailbox == null) {
|
||||
this.mailbox = new Mailbox(this);
|
||||
}
|
||||
|
||||
this.starTowerManager = Nebula.getGameDatabase().getObjectByField(StarTowerManager.class, "_id", this.getUid());
|
||||
if (this.starTowerManager == null) {
|
||||
this.starTowerManager = new StarTowerManager(this);
|
||||
} else {
|
||||
this.starTowerManager.setPlayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public PlayerInfo toProto() {
|
||||
PlayerInfo proto = PlayerInfo.newInstance();
|
||||
|
||||
var acc = proto.getMutableAcc()
|
||||
.setNickName(this.getName())
|
||||
.setGender(this.getGender())
|
||||
.setId(this.getUid())
|
||||
.setHeadIcon(this.getHeadIcon())
|
||||
.setSkinId(this.getSkinId())
|
||||
.setTitlePrefix(this.getTitlePrefix())
|
||||
.setTitleSuffix(this.getTitleSuffix())
|
||||
.setCreateTime(this.getCreateTime());
|
||||
|
||||
proto.getMutableWorldClass()
|
||||
.setStage(3)
|
||||
.setCur(this.getLevel())
|
||||
.setLastExp(this.getExp());
|
||||
|
||||
proto.getMutableEnergy()
|
||||
.getMutableEnergy()
|
||||
.setUpdateTime(Nebula.getCurrentTime())
|
||||
.setNextDuration(60)
|
||||
.setPrimary(240)
|
||||
.setIsPrimary(true);
|
||||
|
||||
// Add characters/discs/res/items
|
||||
for (var character : getCharacters().getCharacterCollection()) {
|
||||
proto.addChars(character.toProto());
|
||||
}
|
||||
|
||||
for (var disc : getCharacters().getDiscCollection()) {
|
||||
proto.addDiscs(disc.toProto());
|
||||
}
|
||||
|
||||
for (var item : getInventory().getItems().values()) {
|
||||
proto.addItems(item.toProto());
|
||||
}
|
||||
|
||||
for (var res : getInventory().getResources().values()) {
|
||||
proto.addRes(res.toProto());
|
||||
}
|
||||
|
||||
// Formations
|
||||
for (var f : this.getFormations().getFormations().values()) {
|
||||
proto.getMutableFormation().addInfo(f.toProto());
|
||||
}
|
||||
|
||||
// Set state
|
||||
var state = proto.getMutableState()
|
||||
.setStorySet(true);
|
||||
|
||||
state.getMutableMail();
|
||||
state.getMutableBattlePass();
|
||||
state.getMutableWorldClassReward();
|
||||
state.getMutableFriendEnergy();
|
||||
state.getMutableMallPackage();
|
||||
state.getMutableAchievement();
|
||||
state.getMutableTravelerDuelQuest()
|
||||
.setType(QuestType.TravelerDuel);
|
||||
state.getMutableTravelerDuelChallengeQuest()
|
||||
.setType(QuestType.TravelerDuelChallenge);
|
||||
state.getMutableStarTower();
|
||||
state.getMutableStarTowerBook();
|
||||
state.getMutableScoreBoss();
|
||||
state.getMutableCharAffinityRewards();
|
||||
|
||||
// Force complete tutorials
|
||||
for (var guide : GameData.getGuideGroupDataTable()) {
|
||||
var info = NewbieInfo.newInstance()
|
||||
.setGroupId(guide.getId())
|
||||
.setStepId(-1);
|
||||
|
||||
acc.addNewbies(info);
|
||||
}
|
||||
|
||||
acc.addNewbies(NewbieInfo.newInstance().setGroupId(GameConstants.INTRO_GUIDE_ID).setStepId(-1));
|
||||
|
||||
//
|
||||
proto.addBoard(410301);
|
||||
proto.setServerTs(Nebula.getCurrentTime());
|
||||
|
||||
// Extra
|
||||
proto.setAchievements(new byte[64]);
|
||||
|
||||
proto.getMutableVampireSurvivorRecord()
|
||||
.getMutableSeason();
|
||||
|
||||
proto.getMutableQuests();
|
||||
proto.getMutableAgent();
|
||||
proto.getMutablePhone();
|
||||
proto.getMutableStory();
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package emu.nebula.game.player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.proto.AnyOuterClass.Any;
|
||||
import emu.nebula.proto.Public.ChangeInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
@Getter
|
||||
public class PlayerChangeInfo {
|
||||
private boolean success;
|
||||
private List<Any> list;
|
||||
|
||||
@Setter
|
||||
private Object extraData;
|
||||
|
||||
public PlayerChangeInfo() {
|
||||
this.list = new ArrayList<>();
|
||||
}
|
||||
|
||||
public PlayerChangeInfo setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void add(ProtoMessage<?> proto) {
|
||||
var any = Any.newInstance()
|
||||
.setTypeUrl(GameConstants.PROTO_BASE_TYPE_URL + proto.getClass().getSimpleName())
|
||||
.setValue(proto.toByteArray());
|
||||
|
||||
this.list.add(any);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public ChangeInfo toProto() {
|
||||
var proto = ChangeInfo.newInstance();
|
||||
|
||||
for (var any : this.getList()) {
|
||||
proto.addProps(any);
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package emu.nebula.game.player;
|
||||
|
||||
public abstract class PlayerManager {
|
||||
private transient Player player;
|
||||
|
||||
public PlayerManager() {
|
||||
|
||||
}
|
||||
|
||||
public PlayerManager(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
if (this.player == null) {
|
||||
this.player = player;
|
||||
}
|
||||
}
|
||||
|
||||
public int getPlayerUid() {
|
||||
return this.getPlayer().getUid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package emu.nebula.game.player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.GameContext;
|
||||
import emu.nebula.game.GameContextModule;
|
||||
import emu.nebula.game.account.Account;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
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;
|
||||
|
||||
public class PlayerModule extends GameContextModule {
|
||||
private final Int2ObjectMap<Player> cachedPlayers;
|
||||
private final Object2ObjectMap<String, Player> cachedPlayersByAccount;
|
||||
|
||||
public PlayerModule(GameContext gameContext) {
|
||||
super(gameContext);
|
||||
|
||||
this.cachedPlayers = new Int2ObjectOpenHashMap<>();
|
||||
this.cachedPlayersByAccount = new Object2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public Int2ObjectMap<Player> getCachedPlayers() {
|
||||
return cachedPlayers;
|
||||
}
|
||||
|
||||
private void addToCache(Player player) {
|
||||
this.cachedPlayers.put(player.getUid(), player);
|
||||
this.cachedPlayersByAccount.put(player.getAccountUid(), player);
|
||||
}
|
||||
|
||||
public void removeFromCache(Player player) {
|
||||
this.cachedPlayers.remove(player.getUid());
|
||||
this.cachedPlayersByAccount.remove(player.getAccountUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a player object that has been previously cached. Returns null if the player isnt in the cache.
|
||||
* @param uid User id of the player
|
||||
* @return
|
||||
*/
|
||||
public synchronized Player getCachedPlayerByUid(int uid) {
|
||||
return getCachedPlayers().get(uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a player object with the given account. Returns null if the player doesnt exist.
|
||||
* @param uid User id of the player
|
||||
* @return
|
||||
*/
|
||||
public synchronized Player getPlayerByAccount(Account account) {
|
||||
// Get player from cache
|
||||
Player player = this.cachedPlayersByAccount.get(account.getUid());
|
||||
|
||||
if (player == null) {
|
||||
// Retrieve player object from database if its not there
|
||||
player = Nebula.getGameDatabase().getObjectByField(Player.class, "accountUid", account.getUid());
|
||||
|
||||
if (player != null) {
|
||||
// Load player
|
||||
player.onLoad();
|
||||
|
||||
// Put in cache
|
||||
this.addToCache(player);
|
||||
}
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a player with the specified user id.
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
public synchronized Player createPlayer(GameSession session, String name, boolean gender) {
|
||||
// Make sure player doesnt already exist
|
||||
if (Nebula.getGameDatabase().checkIfObjectExists(Player.class, "accountUid", session.getAccount().getUid())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Limit name length
|
||||
if (name.length() > 20) {
|
||||
name = name.substring(0, 19);
|
||||
}
|
||||
|
||||
// Create player and save to db
|
||||
var player = new Player(session.getAccount(), name, gender);
|
||||
player.onLoad();
|
||||
player.save();
|
||||
|
||||
// Put in cache
|
||||
this.addToCache(player);
|
||||
|
||||
// Set player for session
|
||||
session.setPlayer(player);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of recent players that have logged on (for followers)
|
||||
* @param player Player that requested this
|
||||
*/
|
||||
public synchronized List<Player> getRandomPlayerList(Player player) {
|
||||
List<Player> list = getCachedPlayers().values().stream().filter(p -> p != player).collect(Collectors.toList());
|
||||
Collections.shuffle(list);
|
||||
return list.stream().limit(15).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package emu.nebula.game.story;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "story", useDiscriminator = false)
|
||||
public class StoryManager extends PlayerManager implements GameDatabaseObject {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package emu.nebula.game.tower;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum CaseType {
|
||||
Battle (1),
|
||||
OpenDoor (2),
|
||||
PotentialSelect (3),
|
||||
FateCardSelect (4),
|
||||
NoteSelect (5),
|
||||
NpcEvent (6),
|
||||
SelectSpecialPotential (7),
|
||||
RecoveryHP (8),
|
||||
NpcRecoveryHP (9),
|
||||
Hawker (10),
|
||||
StrengthenMachine (11),
|
||||
DoorDanger (12),
|
||||
SyncHP (13);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
private final static Int2ObjectMap<CaseType> map = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
for (CaseType type : CaseType.values()) {
|
||||
map.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
private CaseType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static CaseType getByValue(int value) {
|
||||
return map.get(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package emu.nebula.game.tower;
|
||||
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class StarTowerCase {
|
||||
private int id;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
private CaseType type;
|
||||
|
||||
// Extra data
|
||||
private int teamLevel;
|
||||
|
||||
private int floorId;
|
||||
|
||||
// Select
|
||||
private int[] ids;
|
||||
|
||||
public StarTowerCase(CaseType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public StarTowerRoomCase toProto() {
|
||||
var proto = StarTowerRoomCase.newInstance()
|
||||
.setId(this.getId());
|
||||
|
||||
switch (this.type) {
|
||||
case Battle -> {
|
||||
proto.getMutableBattleCase();
|
||||
}
|
||||
case OpenDoor -> {
|
||||
proto.getMutableDoorCase();
|
||||
}
|
||||
case SyncHP -> {
|
||||
proto.getMutableSyncHPCase();
|
||||
}
|
||||
case SelectSpecialPotential -> {
|
||||
proto.getMutableSelectSpecialPotentialCase();
|
||||
}
|
||||
case PotentialSelect -> {
|
||||
proto.getMutableSelectPotentialCase();
|
||||
}
|
||||
default -> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package emu.nebula.game.tower;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.data.resources.StarTowerDef;
|
||||
import emu.nebula.game.formation.Formation;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerChar;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerDisc;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerInfo;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerApply.StarTowerApplyReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
import emu.nebula.util.Snowflake;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public class StarTowerInstance {
|
||||
private transient StarTowerManager manager;
|
||||
private transient StarTowerDef data;
|
||||
|
||||
// Tower id
|
||||
private int id;
|
||||
|
||||
// Room
|
||||
private int floor;
|
||||
private int mapId;
|
||||
private int mapTableId;
|
||||
private String mapParam;
|
||||
private int paramId;
|
||||
|
||||
// Team
|
||||
private int formationId;
|
||||
private int buildId;
|
||||
private int teamLevel;
|
||||
private int teamExp;
|
||||
private int charHp;
|
||||
private int battleTime;
|
||||
private List<StarTowerChar> chars;
|
||||
private List<StarTowerDisc> discs;
|
||||
|
||||
private int lastCaseId = 0;
|
||||
private List<StarTowerCase> cases;
|
||||
|
||||
private Int2IntMap items;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public StarTowerInstance() {
|
||||
|
||||
}
|
||||
|
||||
public StarTowerInstance(StarTowerManager manager, StarTowerDef data, Formation formation, StarTowerApplyReq req) {
|
||||
this.manager = manager;
|
||||
this.data = data;
|
||||
|
||||
this.id = req.getId();
|
||||
|
||||
this.mapId = req.getMapId();
|
||||
this.mapTableId = req.getMapTableId();
|
||||
this.mapParam = req.getMapParam();
|
||||
this.paramId = req.getParamId();
|
||||
|
||||
this.formationId = req.getFormationId();
|
||||
this.buildId = Snowflake.newUid();
|
||||
this.teamLevel = 1;
|
||||
this.floor = 1;
|
||||
this.charHp = -1;
|
||||
this.chars = new ArrayList<>();
|
||||
this.discs = new ArrayList<>();
|
||||
|
||||
this.cases = new ArrayList<>();
|
||||
this.items = new Int2IntOpenHashMap();
|
||||
|
||||
// Init formation
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int id = formation.getCharIdAt(i);
|
||||
var character = getPlayer().getCharacters().getCharacterById(id);
|
||||
|
||||
if (character != null) {
|
||||
chars.add(character.toStarTowerProto());
|
||||
} else {
|
||||
chars.add(StarTowerChar.newInstance());
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int id = formation.getDiscIdAt(i);
|
||||
var disc = getPlayer().getCharacters().getDiscById(id);
|
||||
|
||||
if (disc != null) {
|
||||
discs.add(disc.toStarTowerProto());
|
||||
} else {
|
||||
discs.add(StarTowerDisc.newInstance());
|
||||
}
|
||||
}
|
||||
|
||||
// Add cases
|
||||
this.addCase(new StarTowerCase(CaseType.Battle));
|
||||
this.addCase(new StarTowerCase(CaseType.SyncHP));
|
||||
|
||||
|
||||
var doorCase = this.addCase(new StarTowerCase(CaseType.OpenDoor));
|
||||
doorCase.setFloorId(this.getFloor() + 1);
|
||||
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.manager.getPlayer();
|
||||
}
|
||||
|
||||
public StarTowerCase addCase(StarTowerCase towerCase) {
|
||||
return this.addCase(null, towerCase);
|
||||
}
|
||||
|
||||
public StarTowerCase addCase(StarTowerInteractResp rsp, StarTowerCase towerCase) {
|
||||
// Add to cases list
|
||||
this.cases.add(towerCase);
|
||||
|
||||
// Increment id
|
||||
towerCase.setId(++this.lastCaseId);
|
||||
|
||||
// Set proto
|
||||
if (rsp != null) {
|
||||
rsp.getMutableCases().add(towerCase.toProto());
|
||||
}
|
||||
|
||||
return towerCase;
|
||||
}
|
||||
|
||||
public StarTowerInteractResp handleInteract(StarTowerInteractReq req) {
|
||||
var rsp = StarTowerInteractResp.newInstance()
|
||||
.setId(req.getId());
|
||||
|
||||
if (req.hasBattleEndReq()) {
|
||||
this.onBattleEnd(req, rsp);
|
||||
} else if (req.hasRecoveryHPReq()) {
|
||||
var proto = req.getRecoveryHPReq();
|
||||
} else if (req.hasSelectReq()) {
|
||||
|
||||
} else if (req.hasEnterReq()) {
|
||||
this.onEnterReq(req, rsp);
|
||||
}
|
||||
|
||||
// Set data protos
|
||||
rsp.getMutableData();
|
||||
rsp.getMutableChange();
|
||||
//rsp.getMutableNextPackage();
|
||||
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Interact events
|
||||
|
||||
@SneakyThrows
|
||||
public void onBattleEnd(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
var proto = req.getBattleEndReq();
|
||||
|
||||
if (proto.hasVictory()) {
|
||||
// Add team level
|
||||
this.teamLevel++;
|
||||
|
||||
// Add clear time
|
||||
this.battleTime += proto.getVictory().getTime();
|
||||
|
||||
// Handle victory
|
||||
rsp.getMutableBattleEndResp()
|
||||
.getMutableVictory()
|
||||
.setLv(this.getTeamLevel())
|
||||
.setBattleTime(this.getBattleTime());
|
||||
|
||||
// Add potential selector TODO
|
||||
} else {
|
||||
// Handle defeat
|
||||
}
|
||||
}
|
||||
|
||||
public void onSelect(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
|
||||
}
|
||||
|
||||
public void onEnterReq(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
var proto = req.getEnterReq();
|
||||
|
||||
// Set
|
||||
this.floor = this.floor++;
|
||||
this.mapId = proto.getMapId();
|
||||
this.mapTableId = proto.getMapTableId();
|
||||
|
||||
// Clear cases TODO
|
||||
this.lastCaseId = 0;
|
||||
this.cases.clear();
|
||||
|
||||
// Add cases
|
||||
var syncHpCase = this.addCase(new StarTowerCase(CaseType.SyncHP));
|
||||
var doorCase = this.addCase(new StarTowerCase(CaseType.OpenDoor));
|
||||
doorCase.setFloorId(this.getFloor() + 1);
|
||||
|
||||
// Proto
|
||||
var room = rsp.getMutableEnterResp().getMutableRoom();
|
||||
|
||||
room.getMutableData()
|
||||
.setMapId(this.getMapId())
|
||||
.setMapTableId(this.getMapTableId())
|
||||
.setFloor(this.getFloor());
|
||||
|
||||
room.addAllCases(syncHpCase.toProto(), doorCase.toProto());
|
||||
}
|
||||
|
||||
public void onRecoveryHP(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
// Add case
|
||||
this.addCase(rsp, new StarTowerCase(CaseType.RecoveryHP));
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public StarTowerInfo toProto() {
|
||||
var proto = StarTowerInfo.newInstance();
|
||||
|
||||
proto.getMutableMeta()
|
||||
.setId(this.getId())
|
||||
.setCharHp(this.getCharHp())
|
||||
.setTeamLevel(this.getTeamLevel())
|
||||
.setNPCInteractions(1)
|
||||
.setBuildId(this.getBuildId());
|
||||
|
||||
this.getChars().forEach(proto.getMutableMeta()::addChars);
|
||||
this.getDiscs().forEach(proto.getMutableMeta()::addDiscs);
|
||||
|
||||
proto.getMutableRoom().getMutableData()
|
||||
.setFloor(this.getFloor())
|
||||
.setMapId(this.getMapId())
|
||||
.setMapTableId(this.getMapTableId())
|
||||
.setMapParam(this.getMapParam())
|
||||
.setParamId(this.getParamId());
|
||||
|
||||
// Cases
|
||||
for (var starTowerCase : this.getCases()) {
|
||||
proto.getMutableRoom().addCases(starTowerCase.toProto());
|
||||
}
|
||||
|
||||
// TODO
|
||||
proto.getMutableBag();
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package emu.nebula.game.tower;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.proto.StarTowerApply.StarTowerApplyReq;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "star_tower", useDiscriminator = false)
|
||||
public class StarTowerManager extends PlayerManager implements GameDatabaseObject {
|
||||
@Id
|
||||
private int uid;
|
||||
|
||||
private transient StarTowerInstance instance;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public StarTowerManager() {
|
||||
|
||||
}
|
||||
|
||||
public StarTowerManager(Player player) {
|
||||
super(player);
|
||||
this.uid = player.getUid();
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public StarTowerInstance apply(StarTowerApplyReq req) {
|
||||
// Sanity checks
|
||||
var data = GameData.getStarTowerDataTable().get(req.getId());
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get formation
|
||||
var formation = getPlayer().getFormations().getFormationById(req.getFormationId());
|
||||
if (formation == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create instance
|
||||
this.instance = new StarTowerInstance(this, data, formation, req);
|
||||
|
||||
// Success
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package emu.nebula.net;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.GameContext;
|
||||
import emu.nebula.game.account.Account;
|
||||
import emu.nebula.game.account.AccountHelper;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.util.AeadHelper;
|
||||
import lombok.Getter;
|
||||
import us.hebi.quickbuf.RepeatedByte;
|
||||
|
||||
@Getter
|
||||
public class GameSession {
|
||||
private String token;
|
||||
private Account account;
|
||||
private Player player;
|
||||
|
||||
// Crypto
|
||||
private byte[] clientPublicKey;
|
||||
private byte[] serverPublicKey;
|
||||
private byte[] serverPrivateKey;
|
||||
private byte[] key;
|
||||
|
||||
//
|
||||
private long lastActiveTime;
|
||||
|
||||
public GameSession() {
|
||||
this.updateLastActiveTime();
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.player.addSession(this);
|
||||
}
|
||||
|
||||
public void clearPlayer(GameContext context) {
|
||||
// Sanity check
|
||||
if (this.player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear player
|
||||
var player = this.player;
|
||||
this.player = null;
|
||||
|
||||
// Remove session from player
|
||||
player.removeSession(this);
|
||||
|
||||
// Clean up from player module
|
||||
if (!player.hasSessions()) {
|
||||
context.getPlayerModule().removeFromCache(player);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPlayer() {
|
||||
return this.player != null;
|
||||
}
|
||||
|
||||
public void setClientKey(RepeatedByte key) {
|
||||
this.clientPublicKey = key.toArray();
|
||||
}
|
||||
|
||||
public void generateServerKey() {
|
||||
var pair = AeadHelper.generateECDHKEyPair();
|
||||
|
||||
this.serverPrivateKey = ((ECPrivateKeyParameters) pair.getPrivate()).getD().toByteArray();
|
||||
this.serverPublicKey = ((ECPublicKeyParameters) pair.getPublic()).getQ().getEncoded(false);
|
||||
}
|
||||
|
||||
public void calculateKey() {
|
||||
this.key = AeadHelper.generateKey(clientPublicKey, serverPublicKey, serverPrivateKey);
|
||||
}
|
||||
|
||||
public String generateToken() {
|
||||
String temp = System.currentTimeMillis() + ":" + AeadHelper.generateBytes(64).toString();
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
||||
byte[] bytes = md.digest(temp.getBytes());
|
||||
|
||||
this.token = Base64.getEncoder().encodeToString(bytes);
|
||||
} catch (Exception e) {
|
||||
this.token = Base64.getEncoder().encodeToString(temp.getBytes());
|
||||
}
|
||||
|
||||
return this.token;
|
||||
}
|
||||
|
||||
public boolean login(String loginToken) {
|
||||
// Sanity check
|
||||
if (this.account != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get account
|
||||
this.account = AccountHelper.getAccountByLoginToken(loginToken);
|
||||
|
||||
if (account == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: We should cache players in case multiple sessions try to login to the same player at the time
|
||||
// Get player by account
|
||||
var player = Nebula.getGameContext().getPlayerModule().getPlayerByAccount(account);
|
||||
|
||||
// Skip intro
|
||||
if (player == null && Nebula.getConfig().getServerOptions().skipIntro) {
|
||||
player = Nebula.getGameContext().getPlayerModule().createPlayer(this, "Test", false);
|
||||
}
|
||||
|
||||
// Set player
|
||||
if (player != null) {
|
||||
this.setPlayer(player);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void updateLastActiveTime() {
|
||||
this.lastActiveTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package emu.nebula.net;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
@Retention(RUNTIME)
|
||||
public @interface HandlerId {
|
||||
public int value();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package emu.nebula.net;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
import us.hebi.quickbuf.ProtoSink;
|
||||
|
||||
public abstract class NetHandler {
|
||||
|
||||
public boolean requireSession() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean requirePlayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] encodeMsg(int msgId, byte[] packet) {
|
||||
// Create data array
|
||||
byte[] data = new byte[packet.length + 2];
|
||||
|
||||
// Encode msgId
|
||||
short id = (short) msgId;
|
||||
data[0] = (byte) (id >> 8);
|
||||
data[1] = (byte) id;
|
||||
|
||||
// Copy packet to data array
|
||||
System.arraycopy(packet, 0, data, 2, packet.length);
|
||||
|
||||
// Complete
|
||||
return data;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public byte[] encodeMsg(int msgId, ProtoMessage<?> proto) {
|
||||
// Create data array
|
||||
byte[] data = new byte[proto.getCachedSize() + 2];
|
||||
|
||||
// Encode msgId
|
||||
short id = (short) msgId;
|
||||
data[0] = (byte) (id >> 8);
|
||||
data[1] = (byte) id;
|
||||
|
||||
// Create proto sink
|
||||
var output = ProtoSink.newInstance(data, 2, proto.getCachedSize());
|
||||
|
||||
// Copy packet to data array
|
||||
proto.writeTo(output);
|
||||
|
||||
// Complete
|
||||
return data;
|
||||
}
|
||||
|
||||
public byte[] encodeMsg(int msgId) {
|
||||
// Create data array
|
||||
byte[] data = new byte[2];
|
||||
|
||||
// Encode msgId
|
||||
short id = (short) msgId;
|
||||
data[0] = (byte) (id >> 8);
|
||||
data[1] = (byte) id;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public abstract byte[] handle(GameSession session, byte[] message) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,780 @@
|
||||
package emu.nebula.net;
|
||||
|
||||
public class NetMsgId {
|
||||
public static final int none = 0;
|
||||
|
||||
public static final int clear_all_activity_levels_notify = -10046; // 一键通关当前所有活动关卡
|
||||
public static final int clear_all_activity_avg_notify = -10045; // 一键通关当前所有活动剧情
|
||||
public static final int vs_add_fate_card_notify = -10044; // 返回添加命运卡达到要求额外赠送卡片信息
|
||||
public static final int char_gems_import_notify = -10043; // 导入角色宝石(纹章)槽位和预设数据
|
||||
public static final int char_gems_export_notify = -10042; // 返回角色宝石(纹章)的槽位和预设数据
|
||||
public static final int clear_all_char_gem_instance_notify = -10040; // 一键通关角色宝石(纹章)副本
|
||||
public static final int change_npc_affinity_notify = -10039; // 修改NPC好感度
|
||||
public static final int tower_change_sub_note_skill_notify = -10038; // 修改星塔属性音符数量
|
||||
public static final int world_class_quest_complete_notify = -10037; // 当前世界等级阶段的任务
|
||||
public static final int score_boss_level_reset_notify = -10036; // 积分boss关卡重置通知
|
||||
public static final int score_boss_star_reward_reset_notify = -10035; // 积分boss星级奖励重置通知
|
||||
public static final int char_up_change_notify = -10034; // 角色数据变更
|
||||
public static final int tower_growth_node_change_notify = -10033; // 星塔养成节点变化通知
|
||||
public static final int st_harmony_skill_notify = -10032; // 共鸣技能新增
|
||||
public static final int clear_all_story_notify = -10031; // 一键通关故事并获得所有证据
|
||||
public static final int story_complete_notify = -10030; // 通关指定的故事并获取证据
|
||||
public static final int infinity_tower_skip_floor_notify = -10029; // 无尽之塔跳层
|
||||
public static final int infinity_tower_all_infos_notify = -10028; // 全通所有无尽之塔
|
||||
public static final int clear_all_equipment_instance_notify = -10027; // 全通所有装备副本
|
||||
public static final int st_import_build_notify = -10026; // 返回星塔build导入的build信息
|
||||
public static final int st_export_build_notify = -10025; // 返回星塔build的json序列化字符串
|
||||
public static final int st_add_new_case_notify = -10024; // 返回操作添加的新case
|
||||
public static final int st_add_team_exp_notify = -10023; // 队伍等级经验最终值及当前未处理的所有case
|
||||
public static final int st_skip_floor_notify = -10022; // 星塔跳层
|
||||
public static final int st_clear_all_star_tower_notify = -10021; // 全通所有星塔(返回通关星塔ID列表)
|
||||
public static final int st_items_change_notify = -10020; // 星塔道具变化通知
|
||||
public static final int char_affinity_final_notify = -10019; // 添加角色好感度
|
||||
public static final int clear_all_skill_instance_notify = -10018; // 全通所有技能素材本
|
||||
public static final int disc_reset_notify = -10017; // 星盘信息通知
|
||||
public static final int clear_all_traveler_due_notify = -10016; // 全通所有旅人对决
|
||||
public static final int clear_all_region_boss_level_notify = -10015; // 全通所有强敌讨伐
|
||||
public static final int clear_all_week_boss_notify = -10014; // 一键通关所有周长Boss本
|
||||
public static final int clear_all_daily_instance_notify = -10013; // 一键通关所有日常副本
|
||||
public static final int refresh_agent_notify = -10012; // 一键刷新正在进行中的委托
|
||||
public static final int star_tower_sub_note_skill_info_notify = -10011; // 星塔属性音符技能随机分布数据
|
||||
public static final int vampire_survivor_talent_node_notify = -10010; // 修改后的天赋节点最终信息值
|
||||
public static final int add_vampire_season_score_notify = -10009; // 增加吸血鬼模式副本赛季积分 返回当前总分
|
||||
public static final int clear_all_vampire_survivor_notify = -10008; // 一键通关所有吸血鬼模式副本
|
||||
public static final int region_boss_level_final_notify = -10007; // 地区boss关卡最终数据通知
|
||||
public static final int chars_final_notify = -10006; // 角色列表最终数据通知
|
||||
public static final int world_class_number_notify = -10005; // 世界等级最终值通知
|
||||
public static final int char_change_notify = -10004; // 角色数据变化通知
|
||||
public static final int char_reset_notify = -10003; // 角色信息通知
|
||||
public static final int items_change_notify = -10001; // 道具变化通知
|
||||
public static final int sudo_failed_ack = -3; // 请求失败
|
||||
public static final int sudo_succeed_ack = -2; // 成功,响应会走notify附加数据流程
|
||||
public static final int sudo_req = -1; // 客户端内置GM命令请求
|
||||
public static final int ike_req = 1; // internet key exchange
|
||||
public static final int ike_succeed_ack = 2; // 成功,返回服务器方的秘钥,之后每次请求都需要将Token写入header X-Token段,服务器以此识别用户
|
||||
public static final int ike_failed_ack = 3; // 失败
|
||||
public static final int player_login_req = 4; // 登录
|
||||
public static final int player_login_succeed_ack = 5; // 成功,将返回的新token,替换之前ike阶段的token,放置于header X-Token段,服务器以此识别用户
|
||||
public static final int player_login_failed_ack = 6; // 失败
|
||||
public static final int player_data_req = 1001; // 获取用户全量数据
|
||||
public static final int player_data_succeed_ack = 1002; // 成功,如果是新账号,会返回player_new_notify协议
|
||||
public static final int player_data_failed_ack = 1003; // 请求失败
|
||||
public static final int player_reg_req = 1004; // 注册角色
|
||||
public static final int player_reg_failed_ack = 1005; // 注册失败 返回错误,反之,成功会调用player_data_succeed_ack
|
||||
public static final int player_name_edit_req = 1006; // 请求修改昵称
|
||||
public static final int player_name_edit_succeed_ack = 1007; // 请求修改昵称成功
|
||||
public static final int player_name_edit_failed_ack = 1008; // 请求修改昵称失败
|
||||
public static final int player_head_icon_set_req = 1009; // 请求设置头像
|
||||
public static final int player_head_icon_set_succeed_ack = 1010; // 设置头像成功
|
||||
public static final int player_head_icon_set_failed_ack = 1011; // 设置头像失败
|
||||
public static final int player_ping_req = 1012; // 心跳
|
||||
public static final int player_ping_succeed_ack = 1013; // 心跳回馈
|
||||
public static final int player_ping_failed_ack = 1014; // 不会返回失败
|
||||
public static final int player_learn_req = 1015; // 新手教学@提交教学信息
|
||||
public static final int player_learn_succeed_ack = 1016; // 教学步骤记录成功
|
||||
public static final int player_learn_failed_ack = 1017; // 教学记录失败
|
||||
public static final int player_destroy_req = 1018; // 获取注销账号数据NotifyUrl
|
||||
public static final int player_destroy_succeed_ack = 1019; // 生成注销回调地址以提交到sdk server
|
||||
public static final int player_destroy_failed_ack = 1020; // 获取失败
|
||||
public static final int player_board_set_req = 1021; // 请求设置看板
|
||||
public static final int player_board_set_succeed_ack = 1022; // 设置看板成功
|
||||
public static final int player_board_set_failed_ack = 1023; // 设置看板失败
|
||||
public static final int player_world_class_reward_receive_req = 1024; // 请求领取世界等级奖励
|
||||
public static final int player_world_class_reward_receive_succeed_ack = 1025; // 请求领取世界等级奖励成功
|
||||
public static final int player_world_class_reward_receive_failed_ack = 1026; // 请求领取世界等级奖励失败
|
||||
public static final int player_signature_edit_req = 1027; // 请求修改签名
|
||||
public static final int player_signature_edit_succeed_ack = 1028; // 请求修改签名成功
|
||||
public static final int player_signature_edit_failed_ack = 1029; // 请求修改签名失败
|
||||
public static final int player_title_edit_req = 1030; // 请求修改头衔
|
||||
public static final int player_title_edit_succeed_ack = 1031; // 请求修改头衔成功
|
||||
public static final int player_title_edit_failed_ack = 1032; // 请求修改头衔失败
|
||||
public static final int player_chars_show_req = 1033; // 请求展示角色
|
||||
public static final int player_chars_show_succeed_ack = 1034; // 请求展示角色成功
|
||||
public static final int player_chars_show_failed_ack = 1035; // 请求展示角色失败
|
||||
public static final int player_skin_show_req = 1036; // 请求展示皮肤
|
||||
public static final int player_skin_show_succeed_ack = 1037; // 请求展示皮肤成功
|
||||
public static final int player_skin_show_failed_ack = 1038; // 请求展示皮肤失败
|
||||
public static final int player_gender_edit_req = 1039; // 请求切换性别
|
||||
public static final int player_gender_edit_succeed_ack = 1040; // 切换性别成功
|
||||
public static final int player_gender_edit_failed_ack = 1041; // 切换性别失败
|
||||
public static final int player_survey_req = 1042; // 申请发起调查问卷@填写问卷ID
|
||||
public static final int player_survey_succeed_ack = 1043; // 返回第三方问卷ID和回调通知地址
|
||||
public static final int player_survey_failed_ack = 1044; // 申请失败,比如过期,已经完成过
|
||||
public static final int player_exit_req = 1045; // 退出游戏
|
||||
public static final int player_exit_succeed_ack = 1046; // 退出成功
|
||||
public static final int player_exit_failed_ack = 1047; // 退出失败
|
||||
public static final int player_honor_edit_req = 1048; // 荣誉称号最新的列表
|
||||
public static final int player_honor_edit_succeed_ack = 1049; // 修改成功
|
||||
public static final int player_honor_edit_failed_ack = 1050; // 修改失败
|
||||
public static final int player_world_class_advance_req = 1051; // 请求领取世界等级阶段奖励
|
||||
public static final int player_world_class_advance_succeed_ack = 1052; // 请求领取世界等级奖励成功(等级和经验变化由changeInfo携带)
|
||||
public static final int player_world_class_advance_failed_ack = 1053; // 请求领取世界
|
||||
public static final int player_music_set_req = 1054; // 设置首页音乐(星盘ID)
|
||||
public static final int player_music_set_succeed_ack = 1055; // 设置音乐成功
|
||||
public static final int player_music_set_failed_ack = 1056; // 设置音乐失败
|
||||
public static final int player_head_icon_info_req = 1057; // 获取所有头像列表
|
||||
public static final int player_head_icon_info_succeed_ack = 1058; // 获取头像成功
|
||||
public static final int player_head_icon_info_failed_ack = 1059; // 获取头像失败
|
||||
public static final int item_use_req = 1101; // 道具使用
|
||||
public static final int item_use_succeed_ack = 1102; // 道具使用成功,返回ChangeInfo
|
||||
public static final int item_use_failed_ack = 1103; // 道具使用失败,返回错误信息
|
||||
public static final int gem_convert_req = 1104; // 砖石转换@传入钻石数量,兑换心相碎片,默认规则(免费钻不够使用付费钻)
|
||||
public static final int gem_convert_succeed_ack = 1105; // 兑换成功
|
||||
public static final int gem_convert_failed_ack = 1106; // 兑换失败
|
||||
public static final int item_product_req = 1107; // 材料合成
|
||||
public static final int item_product_succeed_ack = 1108; // 材料合成成功
|
||||
public static final int item_product_failed_ack = 1109; // 材料合成失败
|
||||
public static final int fragments_convert_req = 1110; // 所有角色溢出碎片兑换
|
||||
public static final int fragments_convert_succeed_ack = 1111; // 兑换成功
|
||||
public static final int fragments_convert_failed_ack = 1112; // 兑换失败
|
||||
public static final int daily_shop_reward_receive_req = 1113; // 领取商店每日免费赠礼
|
||||
public static final int daily_shop_reward_receive_succeed_ack = 1114; // 领取成功
|
||||
public static final int daily_shop_reward_receive_failed_ack = 1115; // 领取失败
|
||||
public static final int item_quick_growth_req = 1116; // 道具快速养成
|
||||
public static final int item_quick_growth_succeed_ack = 1117; // 养成成功
|
||||
public static final int item_quick_growth_failed_ack = 1118; // 养成失败
|
||||
public static final int friend_list_get_req = 1201; // 请求获取好友/好友申请列表
|
||||
public static final int friend_list_get_succeed_ack = 1202; // 获取好友/好友申请列表成功
|
||||
public static final int friend_list_get_failed_ack = 1203; // 获取好友/好友申请列表失败
|
||||
public static final int friend_uid_search_req = 1204; // 请求通过UId搜索用户信息
|
||||
public static final int friend_uid_search_succeed_ack = 1205; // 通过UId搜索用户信息成功
|
||||
public static final int friend_uid_search_failed_ack = 1206; // 通过UId搜索用户信息失败
|
||||
public static final int friend_name_search_req = 1207; // 搜索用户信息@传入用户昵称
|
||||
public static final int friend_name_search_succeed_ack = 1208; // 通过用户昵称搜索用户信息成功
|
||||
public static final int friend_name_search_failed_ack = 1209; // 通过用户昵称搜索用户信息失败
|
||||
public static final int friend_add_req = 1210; // 请求申请添加好友
|
||||
public static final int friend_add_succeed_ack = 1211; // 申请添加好友成功
|
||||
public static final int friend_add_failed_ack = 1212; // 申请添加好友失败
|
||||
public static final int friend_add_agree_req = 1213; // 同意添加好友请求
|
||||
public static final int friend_add_agree_succeed_ack = 1214; // 同意添加好友成功
|
||||
public static final int friend_add_agree_failed_ack = 1215; // 同意添加好友失败
|
||||
public static final int friend_all_agree_req = 1216; // 请求一键添加好友
|
||||
public static final int friend_all_agree_succeed_ack = 1217; // 一键添加好友成功
|
||||
public static final int friend_all_agree_failed_ack = 1218; // 一键添加好友失败
|
||||
public static final int friend_delete_req = 1219; // 请求删除好友
|
||||
public static final int friend_delete_succeed_ack = 1220; // 删除好友成功
|
||||
public static final int friend_delete_failed_ack = 1221; // 删除好友失败
|
||||
public static final int friend_invites_delete_req = 1222; // 请求删除好友申请
|
||||
public static final int friend_invites_delete_succeed_ack = 1223; // 删除好友申请成功
|
||||
public static final int friend_invites_delete_failed_ack = 1224; // 删除好友申请失败
|
||||
public static final int friend_star_set_req = 1225; // 请求设置星级好友
|
||||
public static final int friend_star_set_succeed_ack = 1226; // 请求设置星级好友成功
|
||||
public static final int friend_star_set_failed_ack = 1227; // 请求设置星级好友失败
|
||||
public static final int friend_receive_energy_req = 1228; // 请求领取好友赠送体力
|
||||
public static final int friend_receive_energy_succeed_ack = 1229; // 请求领取好友赠送体力成功
|
||||
public static final int friend_receive_energy_failed_ack = 1230; // 请求领取好友赠送体力失败
|
||||
public static final int friend_send_energy_req = 1231; // 请求赠送好友体力
|
||||
public static final int friend_send_energy_succeed_ack = 1232; // 请求赠送好友体力成功
|
||||
public static final int friend_send_energy_failed_ack = 1233; // 请求赠送好友体力失败
|
||||
public static final int friend_recommendation_get_req = 1234; // 请求好友推荐列表
|
||||
public static final int friend_recommendation_get_succeed_ack = 1235; // 请求好友推荐列表成功
|
||||
public static final int friend_recommendation_get_failed_ack = 1236; // 请求好友推荐列表失败
|
||||
public static final int tower_growth_detail_req = 1301; // 获取星塔养成详细信息
|
||||
public static final int tower_growth_detail_succeed_ack = 1302; // 获取成功
|
||||
public static final int tower_growth_detail_failed_ack = 1303; // 获取信息失败,返回错误
|
||||
public static final int tower_growth_node_unlock_req = 1304; // 星塔天赋解锁@传入节点ID,解锁对应的养成节点
|
||||
public static final int tower_growth_node_unlock_succeed_ack = 1305; // 解锁成功
|
||||
public static final int tower_growth_node_unlock_failed_ack = 1306; // 解锁失败,返回错误信息
|
||||
public static final int tower_growth_group_node_unlock_req = 1307; // 星塔天赋组解锁@传入节点组ID,根据材料解锁所有能解锁的节点
|
||||
public static final int tower_growth_group_node_unlock_succeed_ack = 1308; // 解锁成功
|
||||
public static final int tower_growth_group_node_unlock_failed_ack = 1309; // 解锁失败,返回错误信息
|
||||
public static final int player_formation_req = 2001; // 设置编队
|
||||
public static final int player_formation_succeed_ack = 2002; // 设置编队成功
|
||||
public static final int player_formation_failed_ack = 2003; // 设置编队失败
|
||||
public static final int char_upgrade_req = 2301; // 角色升级
|
||||
public static final int char_upgrade_succeed_ack = 2302; // 角色升级成功
|
||||
public static final int char_upgrade_failed_ack = 2303; // 角色升级失败
|
||||
public static final int char_advance_req = 2304; // 角色进阶@传入角色ID
|
||||
public static final int char_advance_succeed_ack = 2305; // 进阶成功
|
||||
public static final int char_advance_failed_ack = 2306; // 进阶失败
|
||||
public static final int char_skill_upgrade_req = 2307; // 角色技能升级
|
||||
public static final int char_skill_upgrade_succeed_ack = 2308; // 升级成功
|
||||
public static final int char_skill_upgrade_failed_ack = 2309; // 升级失败
|
||||
public static final int char_advance_reward_receive_req = 2313; // 请求领取角色进阶奖励
|
||||
public static final int char_advance_reward_receive_succeed_ack = 2314; // 请求领取角色进阶奖励成功
|
||||
public static final int char_advance_reward_receive_failed_ack = 2315; // 请求领取角色进阶奖励失败
|
||||
public static final int char_skin_set_req = 2316; // 设置角色皮肤
|
||||
public static final int char_skin_set_succeed_ack = 2317; // 设置成功
|
||||
public static final int char_skin_set_failed_ack = 2318; // 设置失败
|
||||
public static final int char_affinity_quest_reward_receive_req = 2322; // 请求领取角色好感度任务奖励
|
||||
public static final int char_affinity_quest_reward_receive_succeed_ack = 2323; // 请求领取角色好感度任务成功
|
||||
public static final int char_affinity_quest_reward_receive_failed_ack = 2324; // 请求领取角色好感度任务失败
|
||||
public static final int char_recruitment_req = 2325; // 招募角色@传入角色ID
|
||||
public static final int char_recruitment_succeed_ack = 2326; // 招募成功
|
||||
public static final int char_recruitment_failed_ack = 2327; // 招募失败
|
||||
public static final int char_affinity_gift_send_req = 2328; // 请求赠送好感度礼物
|
||||
public static final int char_affinity_gift_send_succeed_ack = 2329; // 请求赠送好感度礼物成功
|
||||
public static final int char_affinity_gift_send_failed_ack = 2330; // 请求赠送好感度礼物失败
|
||||
public static final int char_dating_landmark_select_req = 2401; // 选择地点邀约角色
|
||||
public static final int char_dating_landmark_select_succeed_ack = 2402; // 选择地点邀约角色成功
|
||||
public static final int char_dating_landmark_select_failed_ack = 2403; // 选择地点邀约角色失败
|
||||
public static final int char_dating_gift_send_req = 2404; // 请求邀约赠礼
|
||||
public static final int char_dating_gift_send_succeed_ack = 2405; // 请求邀约赠礼成功
|
||||
public static final int char_dating_gift_send_failed_ack = 2406; // 请求邀约赠礼失败
|
||||
public static final int char_dating_event_reward_receive_req = 2407; // 请求领取特殊事件奖励
|
||||
public static final int char_dating_event_reward_receive_succeed_ack = 2408; // 请求领取特殊事件奖励成功
|
||||
public static final int char_dating_event_reward_receive_failed_ack = 2409; // 请求领取特殊事件奖励失败
|
||||
public static final int char_archive_reward_receive_req = 2410; // 请求领取角色档案奖励
|
||||
public static final int char_archive_reward_receive_succeed_ack = 2411; // 请求领取角色档案奖励成功
|
||||
public static final int char_archive_reward_receive_failed_ack = 2412; // 请求领取角色档案奖励失败
|
||||
public static final int char_dating_branch_a_select_req = 2413; // 选择分支A选项
|
||||
public static final int char_dating_branch_a_select_succeed_ack = 2414; // 选择分支A选项成功
|
||||
public static final int char_dating_branch_a_select_failed_ack = 2415; // 选择分支A选项失败
|
||||
public static final int char_dating_branch_b_select_req = 2416; // 选择分支B选项
|
||||
public static final int char_dating_branch_b_select_succeed_ack = 2417; // 选择分支B选项成功
|
||||
public static final int char_dating_branch_b_select_failed_ack = 2418; // 选择分支B选项失败
|
||||
public static final int char_gem_generate_req = 2501; // 角色宝石生成
|
||||
public static final int char_gem_generate_succeed_ack = 2502; // 生成成功
|
||||
public static final int char_gem_generate_failed_ack = 2503; // 生成失败
|
||||
public static final int char_gem_refresh_req = 2504; // 角色宝石刷新
|
||||
public static final int char_gem_refresh_succeed_ack = 2505; // 刷新成功
|
||||
public static final int char_gem_refresh_failed_ack = 2506; // 刷新失败
|
||||
public static final int char_gem_replace_attribute_req = 2507; // 角色宝石属性替换
|
||||
public static final int char_gem_replace_attribute_succeed_ack = 2508; // 替换成功
|
||||
public static final int char_gem_replace_attribute_failed_ack = 2509; // 替换失败
|
||||
public static final int char_gem_update_gem_lock_status_req = 2510; // 更新角色宝石锁定状态
|
||||
public static final int char_gem_update_gem_lock_status_succeed_ack = 2511; // 更新成功
|
||||
public static final int char_gem_update_gem_lock_status_failed_ack = 2512; // 更新失败
|
||||
public static final int char_gem_use_preset_req = 2513; // 角色使用预设
|
||||
public static final int char_gem_use_preset_succeed_ack = 2514; // 使用成功
|
||||
public static final int char_gem_use_preset_failed_ack = 2515; // 使用失败
|
||||
public static final int char_gem_rename_preset_req = 2516; // 角色预设重命名
|
||||
public static final int char_gem_rename_preset_succeed_ack = 2517; // 重命名成功
|
||||
public static final int char_gem_rename_preset_failed_ack = 2518; // 重命名失败
|
||||
public static final int char_gem_equip_gem_req = 2519; // 角色宝石装备宝石
|
||||
public static final int char_gem_equip_gem_succeed_ack = 2520; // 装备成功
|
||||
public static final int char_gem_equip_gem_failed_ack = 2521; // 装备失败
|
||||
public static final int disc_strengthen_req = 3119; // 星盘强化
|
||||
public static final int disc_strengthen_succeed_ack = 3120; // 星盘强化成功
|
||||
public static final int disc_strengthen_failed_ack = 3121; // 星盘强化失败
|
||||
public static final int disc_promote_req = 3122; // 星盘升阶
|
||||
public static final int disc_promote_succeed_ack = 3123; // 星盘升阶成功
|
||||
public static final int disc_promote_failed_ack = 3124; // 星盘升阶失败
|
||||
public static final int disc_limit_break_req = 3125; // 星盘突破
|
||||
public static final int disc_limit_break_succeed_ack = 3126; // 星盘突破成功
|
||||
public static final int disc_limit_break_failed_ack = 3127; // 星盘突破失败
|
||||
public static final int disc_read_reward_receive_req = 3128; // 请求领取星盘阅读奖励
|
||||
public static final int disc_read_reward_receive_succeed_ack = 3129; // 请求领取星盘阅读奖励成功
|
||||
public static final int disc_read_reward_receive_failed_ack = 3130; // 请求领取星盘阅读奖励失败
|
||||
public static final int agent_apply_req = 3301; // 请求派遣委托
|
||||
public static final int agent_apply_succeed_ack = 3302; // 请求派遣委托成功
|
||||
public static final int agent_apply_failed_ack = 3303; // 请求派遣委托失败
|
||||
public static final int agent_give_up_req = 3304; // 请求放弃派遣委托
|
||||
public static final int agent_give_up_succeed_ack = 3305; // 请求放弃派遣委托成功
|
||||
public static final int agent_give_up_failed_ack = 3306; // 请求放弃派遣委托失败
|
||||
public static final int agent_reward_receive_req = 3307; // 请求领取派遣委托奖励
|
||||
public static final int agent_reward_receive_succeed_ack = 3308; // 请求领取派遣委托奖励成功
|
||||
public static final int agent_reward_receive_failed_ack = 3309; // 请求领取派遣委托奖励失败
|
||||
public static final int quest_tour_guide_reward_receive_req = 4201; // 领取手册任务奖励@value表示任务ID,0表示一键领取
|
||||
public static final int quest_tour_guide_reward_receive_succeed_ack = 4202; // 获取成功
|
||||
public static final int quest_tour_guide_reward_receive_failed_ack = 4203; // 获取失败
|
||||
public static final int quest_daily_reward_receive_req = 4204; // 领取日常任务奖励@value表示任务ID,0表示一键领取
|
||||
public static final int quest_daily_reward_receive_succeed_ack = 4205; // 获取成功
|
||||
public static final int quest_daily_reward_receive_failed_ack = 4206; // 获取失败
|
||||
public static final int dictionary_reward_receive_req = 4207; // 领取词条奖励
|
||||
public static final int dictionary_reward_receive_succeed_ack = 4208; // 获取成功
|
||||
public static final int dictionary_reward_receive_failed_ack = 4209; // 获取失败
|
||||
public static final int quest_tower_reward_receive_req = 4210; // 领取星塔任务奖励@value表示任务ID,0表示一键领取
|
||||
public static final int quest_tower_reward_receive_succeed_ack = 4211; // 获取成功
|
||||
public static final int quest_tower_reward_receive_failed_ack = 4212; // 获取失败
|
||||
public static final int quest_daily_active_reward_receive_req = 4213; // 领取日常任务活跃奖励
|
||||
public static final int quest_daily_active_reward_receive_succeed_ack = 4214; // 领取日常任务活跃奖励成功
|
||||
public static final int quest_daily_active_reward_receive_failed_ack = 4215; // 领取日常任务活跃奖励失败
|
||||
public static final int quest_tour_guide_group_reward_receive_req = 4216; // 领取任务组奖励
|
||||
public static final int quest_tour_guide_group_reward_receive_succeed_ack = 4217; // 获取成功
|
||||
public static final int quest_tour_guide_group_reward_receive_failed_ack = 4218; // 获取失败
|
||||
public static final int activity_task_reward_receive_req = 4301; // 请求领取活动任务完成奖励
|
||||
public static final int activity_task_reward_receive_succeed_ack = 4302; // 请求领取活动任务完成奖励成功
|
||||
public static final int activity_task_reward_receive_failed_ack = 4303; // 请求领取活动任务完成奖励失败
|
||||
public static final int activity_task_group_reward_receive_req = 4304; // 请求领取活动任务组完成奖励
|
||||
public static final int activity_task_group_reward_receive_succeed_ack = 4305; // 请求领取活动任务组完成奖励成功
|
||||
public static final int activity_task_group_reward_receive_failed_ack = 4306; // 请求领取活动任务组完成奖励失败
|
||||
public static final int achievement_reward_receive_req = 4401; // 领取成就奖励
|
||||
public static final int achievement_reward_receive_succeed_ack = 4402; // 获取成功
|
||||
public static final int achievement_reward_receive_failed_ack = 4403; // 获取失败
|
||||
public static final int achievement_info_req = 4404; // 获取成就数据
|
||||
public static final int achievement_info_succeed_ack = 4405; // 获取成功
|
||||
public static final int achievement_info_failed_ack = 4406; // 获取失败
|
||||
public static final int infinity_tower_info_req = 4501; // 申请无尽塔关卡数据
|
||||
public static final int infinity_tower_info_succeed_ack = 4502; // 申请无尽塔关卡数据成功
|
||||
public static final int infinity_tower_info_failed_ack = 4503; // 申请无尽塔关卡数据失败
|
||||
public static final int infinity_tower_apply_req = 4504; // 申请进入无尽塔关卡
|
||||
public static final int infinity_tower_apply_succeed_ack = 4505; // 申请进入无尽塔关卡成功
|
||||
public static final int infinity_tower_apply_failed_ack = 4506; // 申请进入无尽塔关卡失败
|
||||
public static final int infinity_tower_settle_req = 4507; // 申请结算无尽塔关卡
|
||||
public static final int infinity_tower_settle_succeed_ack = 4508; // 申请结算无尽塔成功 非0表示可以继续挑战关卡ID(失败当前关卡ID,成功下一个关卡ID),无需再发申请
|
||||
public static final int infinity_tower_settle_failed_ack = 4509; // 申请结算无尽塔失败
|
||||
public static final int infinity_tower_daily_reward_receive_req = 4510; // 请求领取无尽塔每日奖励
|
||||
public static final int infinity_tower_daily_reward_receive_succeed_ack = 4511; // 请求领取无尽塔每日奖励成功
|
||||
public static final int infinity_tower_daily_reward_receive_failed_ack = 4512; // 请求领取无尽塔每日奖励失败
|
||||
public static final int infinity_tower_plot_reward_receive_req = 4513; // 请求领取无尽塔剧情奖励
|
||||
public static final int infinity_tower_plot_reward_receive_succeed_ack = 4514; // 请求领取无尽塔剧情奖励成功
|
||||
public static final int infinity_tower_plot_reward_receive_failed_ack = 4515; // 请求领取无尽塔剧情奖励失败
|
||||
public static final int star_tower_apply_req = 4601; // 申请探索星塔
|
||||
public static final int star_tower_apply_succeed_ack = 4602; // 申请成功返回
|
||||
public static final int star_tower_apply_failed_ack = 4603; // 申请失败
|
||||
public static final int star_tower_interact_req = 4607; // 星塔交互请求
|
||||
public static final int star_tower_interact_succeed_ack = 4608; // 交互请求成功
|
||||
public static final int star_tower_interact_failed_ack = 4609; // 申请失败
|
||||
public static final int star_tower_info_req = 4610; // 重连获取星塔信息
|
||||
public static final int star_tower_info_succeed_ack = 4611; // 获取星塔信息成功
|
||||
public static final int star_tower_info_failed_ack = 4612; // 获取星塔信息失败
|
||||
public static final int star_tower_give_up_req = 4613; // 放弃星塔
|
||||
public static final int star_tower_give_up_succeed_ack = 4614; // 放弃星塔成功
|
||||
public static final int star_tower_give_up_failed_ack = 4615; // 放弃星塔失败
|
||||
public static final int star_tower_build_whether_save_req = 4701; // 请求是否保存星塔build
|
||||
public static final int star_tower_build_whether_save_succeed_ack = 4702; // 请求是否保存星塔build返回
|
||||
public static final int star_tower_build_whether_save_failed_ack = 4703; // 请求是否保存星塔build失败
|
||||
public static final int star_tower_build_brief_list_get_req = 4704; // 请求星塔build简要信息列表
|
||||
public static final int star_tower_build_brief_list_get_succeed_ack = 4705; // 请求星塔build简要信息列表返回
|
||||
public static final int star_tower_build_brief_list_get_failed_ack = 4706; // 请求星塔build简要信息列表失败
|
||||
public static final int star_tower_build_detail_get_req = 4707; // 请求星塔build详细信息列表
|
||||
public static final int star_tower_build_detail_get_succeed_ack = 4708; // 请求星塔build详细信息列表返回
|
||||
public static final int star_tower_build_detail_get_failed_ack = 4709; // 请求遗迹build详细信息列表失败
|
||||
public static final int star_tower_build_delete_req = 4710; // 请求解散星塔build
|
||||
public static final int star_tower_build_delete_succeed_ack = 4711; // 请求解散星塔build返回
|
||||
public static final int star_tower_build_delete_failed_ack = 4712; // 请求解散星塔build失败
|
||||
public static final int star_tower_build_name_set_req = 4713; // 请求设置星塔build名
|
||||
public static final int star_tower_build_name_set_succeed_ack = 4714; // 请求设置星塔build名返回
|
||||
public static final int star_tower_build_name_set_failed_ack = 4715; // 请求设置星塔build名失败
|
||||
public static final int star_tower_build_lock_unlock_req = 4716; // 请求星塔build加解锁
|
||||
public static final int star_tower_build_lock_unlock_succeed_ack = 4717; // 请求星塔build加解锁返回
|
||||
public static final int star_tower_build_lock_unlock_failed_ack = 4718; // 请求星塔build加解锁失败
|
||||
public static final int star_tower_build_preference_set_req = 4719; // 请求设置星塔build偏好
|
||||
public static final int star_tower_build_preference_set_succeed_ack = 4720; // 请求设置星塔build偏好返回
|
||||
public static final int star_tower_build_preference_set_failed_ack = 4721; // 请求设置星塔build偏好失败
|
||||
public static final int star_tower_book_potential_brief_list_get_req = 4901; // 请求星塔图鉴角色潜能简要信息
|
||||
public static final int star_tower_book_potential_brief_list_get_succeed_ack = 4902; // 请求星塔图鉴角色潜能简要信息成功
|
||||
public static final int star_tower_book_potential_brief_list_get_failed_ack = 4903; // 请求星塔图鉴角色潜能简要信息失败
|
||||
public static final int star_tower_book_char_potential_get_req = 4904; // 请求星塔图鉴角色潜能信息
|
||||
public static final int star_tower_book_char_potential_get_succeed_ack = 4905; // 请求星塔图鉴角色潜能信息成功
|
||||
public static final int star_tower_book_char_potential_get_failed_ack = 4906; // 请求星塔图鉴角色潜能信息失败
|
||||
public static final int star_tower_book_potential_reward_receive_req = 4907; // 请求领取星塔图鉴角色潜能奖励
|
||||
public static final int star_tower_book_potential_reward_receive_succeed_ack = 4908; // 请求领取星塔图鉴角色潜能奖励成功
|
||||
public static final int star_tower_book_potential_reward_receive_failed_ack = 4909; // 请求领取星塔图鉴角色潜能奖励失败
|
||||
public static final int star_tower_book_event_reward_receive_req = 4913; // 请求领取星塔图鉴角色潜能奖励
|
||||
public static final int star_tower_book_event_reward_receive_succeed_ack = 4914; // 请求领取星塔图鉴角色潜能奖励成功
|
||||
public static final int star_tower_book_event_reward_receive_failed_ack = 4915; // 请求领取星塔图鉴角色潜能奖励失败
|
||||
public static final int npc_affinity_book_get_req = 4916; // 请求NPC好感度图鉴信息
|
||||
public static final int npc_affinity_book_get_succeed_ack = 4917; // 请求NPC好感度图鉴信息成功
|
||||
public static final int npc_affinity_book_get_failed_ack = 4918; // 请求NPC好感度图鉴信息失败
|
||||
public static final int npc_affinity_plot_reward_receive_req = 4919; // 请求领取好感度剧情奖励
|
||||
public static final int npc_affinity_plot_reward_receive_succeed_ack = 4920; // 请求领取好感度剧情奖励成功
|
||||
public static final int npc_affinity_plot_reward_receive_failed_ack = 4921; // 请求领取好感度剧情奖励失败
|
||||
public static final int resident_shop_get_req = 5010; // 请求常驻商店信息
|
||||
public static final int resident_shop_get_succeed_ack = 5011; // 请求常驻商店信息成功
|
||||
public static final int resident_shop_get_failed_ack = 5012; // 请求常驻商店信息失败
|
||||
public static final int resident_shop_purchase_req = 5013; // 请求常驻商店购买物品
|
||||
public static final int resident_shop_purchase_succeed_ack = 5014; // 请求常驻商店购买物品成功
|
||||
public static final int resident_shop_purchase_failed_ack = 5015; // 请求常驻商店购买物品失败
|
||||
public static final int mall_gem_list_req = 5101; // 获取钻石商城产品列表
|
||||
public static final int mall_gem_list_succeed_ack = 5102; // 获取成功的列表
|
||||
public static final int mall_gem_list_failed_ack = 5103; // 获取失败
|
||||
public static final int mall_gem_order_req = 5104; // 下单购买商品
|
||||
public static final int mall_gem_order_succeed_ack = 5105; // 下单成功
|
||||
public static final int mall_gem_order_failed_ack = 5106; // 下单失败
|
||||
public static final int mall_order_cancel_req = 5107; // 取消某个尚未支付的订单
|
||||
public static final int mall_order_cancel_succeed_ack = 5108; // 取消成功
|
||||
public static final int mall_order_cancel_failed_ack = 5109; // 取消失败
|
||||
public static final int mall_order_collect_req = 5110; // 领取某个支付成功的订单的奖励
|
||||
public static final int mall_order_collect_succeed_ack = 5111; // 返回成功,请根据具体状态处理
|
||||
public static final int mall_order_collect_failed_ack = 5112; // 领取失败
|
||||
public static final int mall_monthlyCard_list_req = 5113; // 获取月卡商城产品列表
|
||||
public static final int mall_monthlyCard_list_succeed_ack = 5114; // 获取成功的列表
|
||||
public static final int mall_monthlyCard_list_failed_ack = 5115; // 获取失败
|
||||
public static final int mall_monthlyCard_order_req = 5116; // 购买月卡商城商品
|
||||
public static final int mall_monthlyCard_order_succeed_ack = 5117; // 下单成功
|
||||
public static final int mall_monthlyCard_order_failed_ack = 5118; // 获取失败
|
||||
public static final int mall_package_list_req = 5119; // 获取礼包商城商品列表
|
||||
public static final int mall_package_list_succeed_ack = 5120; // 商品列表
|
||||
public static final int mall_package_list_failed_ack = 5121; // 获取失败
|
||||
public static final int mall_package_order_req = 5122; // 购买礼包商城产品
|
||||
public static final int mall_package_order_succeed_ack = 5123; // 购买成功结果
|
||||
public static final int mall_package_order_failed_ack = 5124; // 购买失败
|
||||
public static final int mall_shop_list_req = 5125; // 获取星尘兑换商城商品列表
|
||||
public static final int mall_shop_list_succeed_ack = 5126; // 商品列表
|
||||
public static final int mall_shop_list_failed_ack = 5127; // 获取失败
|
||||
public static final int mall_shop_order_req = 5128; // 购买星尘兑换商店产品
|
||||
public static final int mall_shop_order_succeed_ack = 5129; // 购买成功结果
|
||||
public static final int mall_shop_order_failed_ack = 5130; // 购买失败
|
||||
public static final int gacha_spin_req = 6001; // 抽卡@传入卡池ID以及抽卡模式
|
||||
public static final int gacha_spin_succeed_ack = 6002; // 成功,返回掉落道具以及ChangeInfo
|
||||
public static final int gacha_spin_failed_ack = 6003; // 错误,返回错误信息
|
||||
public static final int gacha_information_req = 6004; // 获取所有卡池数据
|
||||
public static final int gacha_information_succeed_ack = 6005; // 成功,返回所有的卡池数据
|
||||
public static final int gacha_information_failed_ack = 6006; // 错误,返回错误信息
|
||||
public static final int gacha_histories_req = 6007; // 获取抽卡的历史记录@传入存盘ID
|
||||
public static final int gacha_histories_succeed_ack = 6008; // 成功,返回抽卡的历史数据
|
||||
public static final int gacha_histories_failed_ack = 6009; // 错误,返回错误
|
||||
public static final int gacha_guarantee_reward_receive_req = 6010; // 领取天井奖励@传入卡池ID
|
||||
public static final int gacha_guarantee_reward_receive_succeed_ack = 6011; // 成功,返回奖励
|
||||
public static final int gacha_guarantee_reward_receive_failed_ack = 6012; // 失败,返回错误信息
|
||||
public static final int gacha_newbie_spin_req = 6013; // 新手卡池抽卡@传入卡池ID
|
||||
public static final int gacha_newbie_spin_succeed_ack = 6014; // 成功,返回抽卡的结果
|
||||
public static final int gacha_newbie_spin_failed_ack = 6015; // 错误,返回错误
|
||||
public static final int gacha_newbie_save_req = 6016; // 新手卡池结果保存
|
||||
public static final int gacha_newbie_save_succeed_ack = 6017; // 成功,不返回任何内容
|
||||
public static final int gacha_newbie_save_failed_ack = 6018; // 错误,返回错误
|
||||
public static final int gacha_newbie_obtain_req = 6019; // 获取新手卡池结果
|
||||
public static final int gacha_newbie_obtain_succeed_ack = 6020; // 成功,返回道具变化
|
||||
public static final int gacha_newbie_obtain_failed_ack = 6021; // 错误,返回错误
|
||||
public static final int gacha_newbie_info_req = 6022; // 获取新手卡池信息@返回所有新手卡池信息
|
||||
public static final int gacha_newbie_info_succeed_ack = 6023; // 成功,返回道具变化
|
||||
public static final int gacha_newbie_info_failed_ack = 6024; // 错误,返回错误
|
||||
public static final int tower_book_fate_card_detail_req = 6101; // 获取已经拥有的命运卡图鉴
|
||||
public static final int tower_book_fate_card_detail_succeed_ack = 6102; // 成功,返回已经拥有的命运卡和已经领取的任务
|
||||
public static final int tower_book_fate_card_detail_failed_ack = 6103; // 错误,返回错误信息
|
||||
public static final int tower_book_fate_card_reward_receive_req = 6104; // 领取命运卡任务奖励
|
||||
public static final int tower_book_fate_card_reward_receive_succeed_ack = 6105; // 成功,返回任务奖励
|
||||
public static final int tower_book_fate_card_reward_receive_failed_ack = 6106; // 错误,返回错误信息
|
||||
public static final int joint_drill_apply_req = 6201; // 总力战申请
|
||||
public static final int joint_drill_apply_succeed_ack = 6202; // 总力战申请成功
|
||||
public static final int joint_drill_apply_failed_ack = 6203; // 总力战申请失败
|
||||
public static final int joint_drill_continue_req = 6204; // 总力战申请继续战斗
|
||||
public static final int joint_drill_continue_succeed_ack = 6205; // 总力战申请继续战斗成功
|
||||
public static final int joint_drill_continue_failed_ack = 6206; // 总力战申请继续战斗失败
|
||||
public static final int joint_drill_sync_req = 6207; // 总力战同步记录
|
||||
public static final int joint_drill_sync_succeed_ack = 6208; // 总力战同步记录成功
|
||||
public static final int joint_drill_sync_failed_ack = 6209; // 总力战同步记录失败
|
||||
public static final int joint_drill_give_up_req = 6210; // 总力战战斗放弃/失败
|
||||
public static final int joint_drill_give_up_succeed_ack = 6211; // 总力战申请小队战斗放弃/失败成功
|
||||
public static final int joint_drill_give_up_failed_ack = 6212; // 总力战申请小队战斗放弃/失败失败
|
||||
public static final int joint_drill_retreat_req = 6213; // 总力战申请小队战斗撤退
|
||||
public static final int joint_drill_retreat_succeed_ack = 6214; // 总力战申请小队战斗撤退成功
|
||||
public static final int joint_drill_retreat_failed_ack = 6215; // 总力战申请小队战斗撤退失败
|
||||
public static final int joint_drill_settle_req = 6216; // 总力战小队结算(胜利)申请
|
||||
public static final int joint_drill_settle_succeed_ack = 6217; // 总力战小队结算(胜利)申请成功
|
||||
public static final int joint_drill_settle_failed_ack = 6218; // 总力战小队结算(胜利)申请失败
|
||||
public static final int joint_drill_game_over_req = 6219; // 总力战结束挑战
|
||||
public static final int joint_drill_game_over_succeed_ack = 6220; // 总力战结束挑战成功
|
||||
public static final int joint_drill_game_over_failed_ack = 6221; // 总力战结束挑战失败
|
||||
public static final int joint_drill_sweep_req = 6222; // 请求扫荡总力战关卡
|
||||
public static final int joint_drill_sweep_succeed_ack = 6223; // 请求扫荡总力战关卡成功
|
||||
public static final int joint_drill_sweep_failed_ack = 6224; // 请求扫荡总力战关卡失败
|
||||
public static final int joint_drill_rank_req = 6225; // 请求总力战排行榜信息
|
||||
public static final int joint_drill_rank_succeed_ack = 6226; // 请求总力战排行榜信息成功
|
||||
public static final int joint_drill_rank_failed_ack = 6227; // 请求总力战排行榜信息失败
|
||||
public static final int joint_drill_quest_reward_receive_req = 6228; // 请求领取总力战任务奖励
|
||||
public static final int joint_drill_quest_reward_receive_succeed_ack = 6229; // 请求领取总力战任务奖励成功
|
||||
public static final int joint_drill_quest_reward_receive_failed_ack = 6230; // 请求领取总力战任务奖励失败
|
||||
public static final int plot_reward_receive_req = 7013; // 领取剧情奖励@传入剧情ID
|
||||
public static final int plot_reward_receive_succeed_ack = 7014; // 领取成功
|
||||
public static final int plot_reward_receive_failed_ack = 7015; // 领取失败
|
||||
public static final int daily_instance_apply_req = 7016; // 日常副本申请
|
||||
public static final int daily_instance_apply_succeed_ack = 7017; // 日常副本申请成功
|
||||
public static final int daily_instance_apply_failed_ack = 7018; // 错误,返回错误信息
|
||||
public static final int daily_instance_settle_req = 7019; // 日常副本结算请求
|
||||
public static final int daily_instance_settle_succeed_ack = 7020; // 日常副本结算成功
|
||||
public static final int daily_instance_settle_failed_ack = 7021; // 错误,返回错误信息
|
||||
public static final int daily_instance_raid_req = 7022; // 日常副本扫荡请求
|
||||
public static final int daily_instance_raid_succeed_ack = 7023; // 日常副本扫荡成功
|
||||
public static final int daily_instance_raid_failed_ack = 7024; // 错误,返回错误信息
|
||||
public static final int char_gem_instance_apply_req = 7028; // 角色宝石碎片副本申请
|
||||
public static final int char_gem_instance_apply_succeed_ack = 7029; // 角色宝石碎片副本申请成功
|
||||
public static final int char_gem_instance_apply_failed_ack = 7030; // 角色宝石碎片副本申请失败
|
||||
public static final int char_gem_instance_settle_req = 7031; // 角色宝石碎片副本结算请求
|
||||
public static final int char_gem_instance_settle_succeed_ack = 7032; // 角色宝石碎片副本结算成功
|
||||
public static final int char_gem_instance_settle_failed_ack = 7033; // 错误,返回错误信息
|
||||
public static final int char_gem_instance_sweep_req = 7034; // 角色宝石碎片副本扫荡请求
|
||||
public static final int char_gem_instance_sweep_succeed_ack = 7035; // 角色宝石碎片副本扫荡成功
|
||||
public static final int char_gem_instance_sweep_failed_ack = 7036; // 错误,返回错误信息
|
||||
public static final int region_boss_level_apply_req = 7101; // 请求进入地区boss关卡
|
||||
public static final int region_boss_level_apply_succeed_ack = 7102; // 请求进入地区boss关成功
|
||||
public static final int region_boss_level_apply_failed_ack = 7103; // 请求进入地区boss关卡失败
|
||||
public static final int region_boss_level_settle_req = 7104; // 请求结算地区boss关卡
|
||||
public static final int region_boss_level_settle_succeed_ack = 7105; // 请求结算地区boss关成功
|
||||
public static final int region_boss_level_settle_failed_ack = 7106; // 请求结算地区boss关卡失败
|
||||
public static final int region_boss_level_sweep_req = 7107; // 请求扫荡地区boss关卡
|
||||
public static final int region_boss_level_sweep_succeed_ack = 7108; // 请求扫荡地区boss关成功
|
||||
public static final int region_boss_level_sweep_failed_ack = 7109; // 请求结算地区boss关卡失败
|
||||
public static final int traveler_duel_level_apply_req = 7201; // 请求进入旅人对决关卡
|
||||
public static final int traveler_duel_level_apply_succeed_ack = 7202; // 请求进入旅人对决关卡成功
|
||||
public static final int traveler_duel_level_apply_failed_ack = 7203; // 请求进入旅人对决关卡失败
|
||||
public static final int traveler_duel_level_settle_req = 7204; // 请求结算旅人对决关卡
|
||||
public static final int traveler_duel_level_settle_succeed_ack = 7205; // 请求结算旅人对决关卡成功
|
||||
public static final int traveler_duel_level_settle_failed_ack = 7206; // 请求结算旅人对决关卡失败
|
||||
public static final int traveler_duel_info_req = 7207; // 请求旅人对决信息
|
||||
public static final int traveler_duel_info_succeed_ack = 7208; // 请求旅人对决信息成功
|
||||
public static final int traveler_duel_info_failed_ack = 7209; // 请求旅人对决信息失败
|
||||
public static final int traveler_duel_quest_reward_receive_req = 7210; // 领取旅人对决任务奖励
|
||||
public static final int traveler_duel_quest_reward_receive_succeed_ack = 7211; // 获取成功
|
||||
public static final int traveler_duel_quest_reward_receive_failed_ack = 7212; // 获取失败
|
||||
public static final int traveler_duel_rank_req = 7213; // 请求旅人对决排行榜信息
|
||||
public static final int traveler_duel_rank_succeed_ack = 7214; // 请求旅人对决排行榜信息成功
|
||||
public static final int traveler_duel_rank_failed_ack = 7215; // 请求旅人对决排行信息失败
|
||||
public static final int traveler_duel_rank_upload_req = 7216; // 旅人对决信息上传@请求旅人对决上传分数、附带本次战斗统计数据
|
||||
public static final int traveler_duel_rank_upload_succeed_ack = 7217; // 请求旅人对决上传分数成功
|
||||
public static final int traveler_duel_rank_upload_failed_ack = 7218; // 请求旅人对决上传分数失败
|
||||
public static final int story_apply_req = 7301; // 关卡申请
|
||||
public static final int story_apply_succeed_ack = 7302; // 申请成功,返回 Nil
|
||||
public static final int story_apply_failed_ack = 7303; // 错误,返回错误信息
|
||||
public static final int story_settle_req = 7304; // 关卡结算
|
||||
public static final int story_settle_succeed_ack = 7305; // 结算成功,发放通关奖励
|
||||
public static final int story_settle_failed_ack = 7306; // 错误,返回错误信息
|
||||
public static final int skill_instance_apply_req = 7401; // 请求进入技能素材关卡
|
||||
public static final int skill_instance_apply_succeed_ack = 7402; // 请求进入技能素材关卡成功
|
||||
public static final int skill_instance_apply_failed_ack = 7403; // 请求进入技能素材关卡失败
|
||||
public static final int skill_instance_settle_req = 7404; // 请求结算技能素材本关卡
|
||||
public static final int skill_instance_settle_succeed_ack = 7405; // 请求结算技能素材本关卡成功
|
||||
public static final int skill_instance_settle_failed_ack = 7406; // 请求结算技能素材本关卡失败
|
||||
public static final int skill_instance_sweep_req = 7407; // 请求扫荡技能素材关卡
|
||||
public static final int skill_instance_sweep_succeed_ack = 7408; // 请求扫荡技能素材关卡成功
|
||||
public static final int skill_instance_sweep_failed_ack = 7409; // 请求扫荡技能素材关卡失败
|
||||
public static final int week_boss_apply_req = 7410; // 请求进入周长boss本
|
||||
public static final int week_boss_apply_succeed_ack = 7411; // 请求进入周长boss本成功
|
||||
public static final int week_boss_apply_failed_ack = 7412; // 请求进入周长boss本失败
|
||||
public static final int week_boss_settle_req = 7413; // 请求结算周长boss本
|
||||
public static final int week_boss_settle_succeed_ack = 7414; // 请求结算周长boss本成功
|
||||
public static final int week_boss_settle_failed_ack = 7415; // 请求结算周长boss本失败
|
||||
public static final int tutorial_level_settle_req = 7501; // 教学关卡结算
|
||||
public static final int tutorial_level_settle_succeed_ack = 7502; // 教学关卡结算成功
|
||||
public static final int tutorial_level_settle_failed_ack = 7503; // 教学关卡结算失败
|
||||
public static final int tutorial_level_reward_receive_req = 7504; // 领取教学关卡奖励
|
||||
public static final int tutorial_level_reward_receive_succeed_ack = 7505; // 领取教学关卡奖励成功
|
||||
public static final int tutorial_level_reward_receive_failed_ack = 7506; // 领取教学关卡奖励失败
|
||||
public static final int story_set_info_req = 7601; // 获取故事集数据
|
||||
public static final int story_set_info_succeed_ack = 7602; // 获取故事集数据成功
|
||||
public static final int story_set_info_failed_ack = 7603; // 错误,返回错误信息
|
||||
public static final int story_set_reward_receive_req = 7604; // 领取故事集奖励
|
||||
public static final int story_set_reward_receive_succeed_ack = 7605; // 领取故事集奖励成功
|
||||
public static final int story_set_reward_receive_failed_ack = 7606; // 领取故事集奖励失败
|
||||
public static final int energy_buy_req = 8001; // 购买体力请求
|
||||
public static final int energy_buy_succeed_ack = 8002; // 购买成功,返回当日的购买次数,以及ChangeInfo
|
||||
public static final int energy_buy_failed_ack = 8003; // 购买失败,返回错误信息
|
||||
public static final int energy_extract_req = 8004; // 提取体力请求
|
||||
public static final int energy_extract_succeed_ack = 8005; // 提取成功,返回 changeInfo
|
||||
public static final int energy_extract_failed_ack = 8006; // 提取失败,返回错误信息
|
||||
public static final int client_event_report_req = 8101; // 客户端事件上报
|
||||
public static final int client_event_report_succeed_ack = 8102; // 客户端事件上报成功
|
||||
public static final int client_event_report_failed_ack = 8103; // 客户端事件上报失败
|
||||
public static final int vampire_survivor_apply_req = 8201; // 灾变防线副本申请
|
||||
public static final int vampire_survivor_apply_succeed_ack = 8202; // 灾变防线副本申请成功
|
||||
public static final int vampire_survivor_apply_failed_ack = 8203; // 灾变防线副本申请失败
|
||||
public static final int vampire_survivor_area_change_req = 8204; // 灾变防线阶段转化
|
||||
public static final int vampire_survivor_area_change_succeed_ack = 8205; // 灾变防线副本申请成功
|
||||
public static final int vampire_survivor_area_change_failed_ack = 8206; // 灾变防线副本申请失败
|
||||
public static final int vampire_survivor_settle_req = 8207; // 灾变防线副本成功/失败结算申请
|
||||
public static final int vampire_survivor_settle_succeed_ack = 8208; // 灾变防线副本结算申请成功
|
||||
public static final int vampire_survivor_settle_failed_ack = 8209; // 灾变防线副本结算申请失败
|
||||
public static final int vampire_survivor_reward_select_req = 8210; // 灾变防线副本升级申请
|
||||
public static final int vampire_survivor_reward_select_succeed_ack = 8211; // 灾变防线副本升级申请成功
|
||||
public static final int vampire_survivor_reward_select_failed_ack = 8212; // 灾变防线副本升级申请失败
|
||||
public static final int vampire_survivor_reward_chest_req = 8213; // 灾变防线开宝箱申请
|
||||
public static final int vampire_survivor_reward_chest_succeed_ack = 8214; // 灾变防线开宝箱申请成功
|
||||
public static final int vampire_survivor_reward_chest_failed_ack = 8215; // 灾变防线开宝箱申请失败
|
||||
public static final int vampire_survivor_quest_reward_receive_req = 8216; // 灾变防线领取任务奖励申请
|
||||
public static final int vampire_survivor_quest_reward_receive_succeed_ack = 8217; // 灾变防线领取任务奖励成功
|
||||
public static final int vampire_survivor_quest_reward_receive_failed_ack = 8218; // 灾变防线领取任务奖励失败
|
||||
public static final int vampire_survivor_extra_reward_select_req = 8222; // 灾变防线副本额外奖励领取
|
||||
public static final int vampire_survivor_extra_reward_select_succeed_ack = 8223; // 灾变防线副本额外奖励领取成功
|
||||
public static final int vampire_survivor_extra_reward_select_failed_ack = 8224; // 灾变防线副本额外奖励领取失败
|
||||
public static final int vampire_survivor_restart_req = 8225; // 灾变防线第二阶段重开
|
||||
public static final int vampire_survivor_restart_succeed_ack = 8226; // 灾变防线第二阶段重开成功
|
||||
public static final int vampire_survivor_restart_failed_ack = 8227; // 灾变防线第二阶段重开失败
|
||||
public static final int vampire_talent_detail_req = 8301; // 获取吸血鬼天赋信息
|
||||
public static final int vampire_talent_detail_succeed_ack = 8302; // 获取节点信息成功
|
||||
public static final int vampire_talent_detail_failed_ack = 8303; // 获取失败,返回错误信息
|
||||
public static final int vampire_talent_reset_req = 8304; // 重置吸血鬼所有的天赋
|
||||
public static final int vampire_talent_reset_succeed_ack = 8305; // 重置成功,不返回任何数据
|
||||
public static final int vampire_talent_reset_failed_ack = 8306; // 重置失败,返回错误信息
|
||||
public static final int vampire_talent_unlock_req = 8307; // 吸血鬼天赋解锁@传入节点ID,解锁对应的养成节点
|
||||
public static final int vampire_talent_unlock_succeed_ack = 8308; // 解锁成功
|
||||
public static final int vampire_talent_unlock_failed_ack = 8309; // 解锁失败,返回错误信息
|
||||
public static final int vampire_talent_show_req = 8310; // 吸血鬼天赋点数展示
|
||||
public static final int vampire_talent_show_succeed_ack = 8311; // 成功无任何返回
|
||||
public static final int vampire_talent_show_failed_ack = 8312; // 失败返回错误
|
||||
public static final int mail_list_req = 9001; // 获取邮件列表
|
||||
public static final int mail_list_succeed_ack = 9002; // 邮件列表
|
||||
public static final int mail_list_failed_ack = 9003; // 获取失败
|
||||
public static final int mail_read_req = 9004; // 标记邮件已读
|
||||
public static final int mail_read_succeed_ack = 9005; // 返回已设置为已读的邮件ID
|
||||
public static final int mail_read_failed_ack = 9006; // 设置失败
|
||||
public static final int mail_recv_req = 9007; // 领取邮件奖励@一键领取发送0,单独领取发送对应邮件ID上来
|
||||
public static final int mail_recv_succeed_ack = 9008; // 领取成功
|
||||
public static final int mail_recv_failed_ack = 9009; // 领取失败
|
||||
public static final int mail_remove_req = 9010; // 删除邮件@一键删除所有已读已领发送0,单独删除发送对应邮件ID上来
|
||||
public static final int mail_remove_succeed_ack = 9011; // 删除成功
|
||||
public static final int mail_remove_failed_ack = 9012; // 删除失败
|
||||
public static final int mail_pin_req = 9013; // 邮件星标操作
|
||||
public static final int mail_pin_succeed_ack = 9014; // 标记成功
|
||||
public static final int mail_pin_failed_ack = 9015; // 标记失败
|
||||
public static final int activity_detail_req = 9101; // 获取所有的活动数据
|
||||
public static final int activity_detail_succeed_ack = 9102; // 获取成功
|
||||
public static final int activity_detail_failed_ack = 9103; // 获取失败
|
||||
public static final int activity_periodic_reward_receive_req = 9104; // 领取周期活动奖励
|
||||
public static final int activity_periodic_reward_receive_succeed_ack = 9105; // 领取成功
|
||||
public static final int activity_periodic_reward_receive_failed_ack = 9106; // 领取失败
|
||||
public static final int activity_periodic_final_reward_receive_req = 9107; // 领取周期活动最终奖励
|
||||
public static final int activity_periodic_final_reward_receive_succeed_ack = 9108; // 领取成功
|
||||
public static final int activity_periodic_final_reward_receive_failed_ack = 9109; // 领取失败
|
||||
public static final int activity_login_reward_receive_req = 9110; // 领取登录活动奖励
|
||||
public static final int activity_login_reward_receive_succeed_ack = 9111; // 领取成功
|
||||
public static final int activity_login_reward_receive_failed_ack = 9112; // 领取失败
|
||||
public static final int activity_tower_defense_story_reward_receive_req = 9113; // 领取塔防活动剧情奖励@传入剧情ID
|
||||
public static final int activity_tower_defense_story_reward_receive_succeed_ack = 9114; // 领取成功
|
||||
public static final int activity_tower_defense_story_reward_receive_failed_ack = 9115; // 领取失败
|
||||
public static final int activity_tower_defense_quest_reward_receive_req = 9116; // 领取塔防活动任务奖励
|
||||
public static final int activity_tower_defense_quest_reward_receive_succeed_ack = 9117; // 领取成功
|
||||
public static final int activity_tower_defense_quest_reward_receive_failed_ack = 9118; // 领取失败
|
||||
public static final int activity_tower_defense_level_apply_req = 9119; // 塔防活动关卡申请
|
||||
public static final int activity_tower_defense_level_apply_succeed_ack = 9120; // 申请成功
|
||||
public static final int activity_tower_defense_level_apply_failed_ack = 9121; // 申请失败
|
||||
public static final int activity_tower_defense_level_settle_req = 9122; // 塔防活动关卡结算
|
||||
public static final int activity_tower_defense_level_settle_succeed_ack = 9123; // 结算成功(首次通关会有奖励,之后没有奖励)
|
||||
public static final int activity_tower_defense_level_settle_failed_ack = 9124; // 结算失败
|
||||
public static final int phone_contacts_info_req = 9201; // 获取手机所有联系人的数据
|
||||
public static final int phone_contacts_info_succeed_ack = 9202; // 获取联系人数据成功
|
||||
public static final int phone_contacts_info_failed_ack = 9203; // 获取联系人数据
|
||||
public static final int phone_contacts_report_req = 9204; // 联系人聊天上报
|
||||
public static final int phone_contacts_report_succeed_ack = 9205; // 上报成功
|
||||
public static final int phone_contacts_report_failed_ack = 9206; // 上报失败
|
||||
public static final int phone_contacts_top_req = 9207; // 联系人置顶
|
||||
public static final int phone_contacts_top_succeed_ack = 9208; // 置顶成功
|
||||
public static final int phone_contacts_top_failed_ack = 9209; // 置顶失败
|
||||
public static final int talent_unlock_req = 9301; // 天赋解锁@传入天赋ID
|
||||
public static final int talent_unlock_succeed_ack = 9302; // 解锁成功
|
||||
public static final int talent_unlock_failed_ack = 9303; // 解锁失败,返回错误信息
|
||||
public static final int talent_reset_req = 9304; // 角色天赋/天赋组重置@传入角色ID
|
||||
public static final int talent_reset_succeed_ack = 9305; // 重置成功
|
||||
public static final int talent_reset_failed_ack = 9306; // 重置失败,返回错误信息
|
||||
public static final int talent_node_reset_req = 9307; // 天赋重置
|
||||
public static final int talent_node_reset_succeed_ack = 9308; // 重置成功
|
||||
public static final int talent_node_reset_failed_ack = 9309; // 重置失败,返回错误信息
|
||||
public static final int talent_background_set_req = 9310; // 设置天赋背景@传入节点组
|
||||
public static final int talent_background_set_succeed_ack = 9311; // 设置成功
|
||||
public static final int talent_background_set_failed_ack = 9312; // 设置失败,返回错误信息
|
||||
public static final int talent_group_unlock_req = 9313; // 天赋组解锁
|
||||
public static final int talent_group_unlock_succeed_ack = 9314; // 解锁成功
|
||||
public static final int talent_group_unlock_failed_ack = 9315; // 解锁失败,返回错误信息
|
||||
public static final int activity_trial_reward_receive_req = 9401; // 传入试玩角色组id领取奖励
|
||||
public static final int activity_trial_reward_receive_succeed_ack = 9402; // 领取成功
|
||||
public static final int activity_trial_reward_receive_failed_ack = 9403; // 领取失败
|
||||
public static final int activity_cg_read_req = 9501; // 活动CG已读@传入活动ID
|
||||
public static final int activity_cg_read_succeed_ack = 9502; // 操作成功
|
||||
public static final int activity_cg_read_failed_ack = 9503; // 操作失败
|
||||
public static final int activity_levels_apply_req = 9601; // 申请进入活动关卡
|
||||
public static final int activity_levels_apply_succeed_ack = 9602; // 进入成功
|
||||
public static final int activity_levels_apply_failed_ack = 9603; // 进入失败
|
||||
public static final int activity_levels_settle_req = 9604; // 活动关卡结算
|
||||
public static final int activity_levels_settle_succeed_ack = 9605; // 结算成功
|
||||
public static final int activity_levels_settle_failed_ack = 9606; // 结算失败
|
||||
public static final int activity_levels_sweep_req = 9607; // 活动关卡扫荡
|
||||
public static final int activity_levels_sweep_succeed_ack = 9608; // 扫荡成功
|
||||
public static final int activity_levels_sweep_failed_ack = 9609; // 领取失败
|
||||
public static final int activity_avg_reward_receive_req = 9701; // 领取AVG活动关卡奖励
|
||||
public static final int activity_avg_reward_receive_succeed_ack = 9702; // 领取成功
|
||||
public static final int activity_avg_reward_receive_failed_ack = 9703; // 领取失败
|
||||
public static final int activity_shop_purchase_req = 9751; // 请求活动商店购买物品
|
||||
public static final int activity_shop_purchase_succeed_ack = 9752; // 请求活动商店购买物品成功
|
||||
public static final int activity_shop_purchase_failed_ack = 9753; // 请求活动商店购买物品失败
|
||||
public static final int battle_pass_info_req = 9801; // 获取当前战令信息
|
||||
public static final int battle_pass_info_succeed_ack = 9802; // 战令信息
|
||||
public static final int battle_pass_info_failed_ack = 9803; // 获取失败
|
||||
public static final int battle_pass_reward_receive_req = 9804; // 领取战令奖励@传入战令等级和版本,全部领等级取传0
|
||||
public static final int battle_pass_reward_receive_succeed_ack = 9805; // 领取战令奖励成功
|
||||
public static final int battle_pass_reward_receive_failed_ack = 9806; // 领取战令奖励失败
|
||||
public static final int battle_pass_level_buy_req = 9807; // 战令等级购买@传入需要购买的级数和版本
|
||||
public static final int battle_pass_level_buy_succeed_ack = 9808; // 购买成功
|
||||
public static final int battle_pass_level_buy_failed_ack = 9809; // 购买成功
|
||||
public static final int battle_pass_order_req = 9810; // 战令进阶下单
|
||||
public static final int battle_pass_order_succeed_ack = 9811; // 战令进阶下单成功
|
||||
public static final int battle_pass_order_failed_ack = 9812; // 领取失败
|
||||
public static final int battle_pass_order_collect_req = 9813; // 战令进阶订单收取
|
||||
public static final int battle_pass_order_collect_succeed_ack = 9814; // 返回成功,请根据具体状态处理
|
||||
public static final int battle_pass_order_collect_failed_ack = 9815; // 战令进阶失败
|
||||
public static final int battle_pass_quest_reward_receive_req = 9816; // 战令任务一键领取@value表示任务ID,0表示一键领取
|
||||
public static final int battle_pass_quest_reward_receive_succeed_ack = 9817; // 获取成功
|
||||
public static final int battle_pass_quest_reward_receive_failed_ack = 9818; // 获取失败
|
||||
public static final int redeem_code_req = 9901; // 兑换码兑换
|
||||
public static final int redeem_code_succeed_ack = 9902; // 兑换成功,返回兑换后德道具以及ChangeInfo
|
||||
public static final int redeem_code_failed_ack = 9903; // 兑换失败,返回错误信息
|
||||
public static final int system_failed_ack = 10000; // 系统级失败,主要用于http模式下,强制失败返回
|
||||
public static final int player_new_notify = 10001; // 新用户
|
||||
public static final int mail_state_notify = 10002; // 邮件状态变更
|
||||
public static final int player_relogin_notify = 10003; // 在其他地方登录
|
||||
public static final int token_expire_notify = 10004; // token过期
|
||||
public static final int player_ban_notify = 10005; // 用户被ban
|
||||
public static final int quest_change_notify = 10006; // 任务进度变更
|
||||
public static final int week_boss_refresh_ticket_notify = 10007; // 周长副本门票刷新通知
|
||||
public static final int agent_new_notify = 10008; // 每周刷新新委托ID列表
|
||||
public static final int world_class_change_notify = 10009; // 世界等级变化
|
||||
public static final int friend_energy_state_notify = 10010; // 好友赠送体力状态变更
|
||||
public static final int signin_reward_change_notify = 10011; // 登陆奖励更新
|
||||
public static final int friend_state_notify = 10012; // 好友状态变更
|
||||
public static final int order_paid_notify = 10013; // 订单已完成支付通知,可以发起领取
|
||||
public static final int order_revoke_notify = 10014; // 订单道具被撤回,主要用于恶意退款
|
||||
public static final int star_tower_book_potential_notify = 10015; // 星塔潜能图鉴状态变更
|
||||
public static final int star_tower_book_event_notify = 10016; // 星塔潜能图鉴状态变更
|
||||
public static final int battle_pass_state_notify = 10017; // 战令状态变更
|
||||
public static final int world_class_reward_state_notify = 10018; // 世界等级奖励状态变更
|
||||
public static final int char_advance_reward_state_notify = 10019; // 角色进阶奖励状态变更
|
||||
public static final int achievement_change_notify = 10020; // 成就进度变更
|
||||
public static final int achievement_state_notify = 10021; // 成就待领取红点提示
|
||||
public static final int character_skin_gain_notify = 10022; // 角色获得新皮肤,如果是重复获取,将发送转换数据
|
||||
public static final int character_skin_change_notify = 10023; // 角色装备的皮肤发生改变
|
||||
public static final int handbook_change_notify = 10024; // 图鉴数据发生变化
|
||||
public static final int monthly_card_rewards_notify = 10025; // 月卡奖励通知
|
||||
public static final int quest_state_notify = 10026; // 任务红点奖励notify
|
||||
public static final int mall_package_state_notify = 10027; // 礼包商城免费商品红点notify
|
||||
public static final int dictionary_change_notify = 10028; // 字典数据变更
|
||||
public static final int activity_change_notify = 10029; // 活动数据变化
|
||||
public static final int activity_quest_change_notify = 10030; // 活动任务数据变化
|
||||
public static final int char_affinity_reward_state_notify = 10031; // 角色好感度奖励最终值
|
||||
public static final int mail_overflow_notify = 10032; // 道具超发进邮件
|
||||
public static final int infinity_tower_rewards_state_notify = 10033; // 无尽塔是否有奖励可领最终值
|
||||
public static final int phone_chat_change_notify = 10034; // 手机新聊天变化
|
||||
public static final int character_fragments_overflow_change_notify = 10035; // 角色碎片溢出
|
||||
public static final int activity_login_rewards_notify = 10036; // 七日登录活动通知
|
||||
public static final int tower_book_fate_card_collect_notify = 10037; // 新获得的命运卡数据
|
||||
public static final int tower_book_fate_card_reward_notify = 10038; // 命运卡图鉴奖励变化
|
||||
public static final int region_boss_level_challenge_ticket_notify = 10039; // 区域boss挑战模式门票变更通知最终值
|
||||
public static final int honor_change_notify = 10040; // 荣誉称号变更通知(最终值,多个Notify以最后一个为准)
|
||||
public static final int activity_mining_daily_reward_notify = 10041; // 挖格子每日奖励发放通知
|
||||
public static final int activity_mining_supplement_reward_notify = 10042; // 挖格子活动开启后登录,补发每日奖励通知
|
||||
public static final int activity_mining_energy_convert_notify = 10043; // 挖格子活动消耗体力转换道具通知
|
||||
public static final int notice_change_notify = 10044; // 跑马灯公告变更通知
|
||||
public static final int activity_state_change_notify = 10045; // 活动状态变化通知
|
||||
public static final int activity_joint_drill_refresh_ticket_notify = 10046; // 总力战门票刷新通知
|
||||
public static final int force_update_notify = 10047; // 强制更新通知,当版本号小于此通知版本时,择期进行弹窗更新,注意,此消息可能重复发送
|
||||
public static final int player_head_icon_change_notify = 10048; // 当玩家切换性别且设置的头像为主角头像时,改通知会下发
|
||||
public static final int activity_mining_enter_layer_notify = 10049; // 挖格子活动跳层/刷新本层数据
|
||||
public static final int story_set_state_notify = 10050; // 故事集活动红点
|
||||
public static final int vampire_survivor_new_season_notify = 10051; // 吸血鬼新赛季开启通知
|
||||
public static final int order_collected_notify = 10052; // 离线期间的订单已发放到账
|
||||
public static final int activity_mining_apply_req = 11001; // 申请进入挖格子活动
|
||||
public static final int activity_mining_apply_succeed_ack = 11002; // 申请进入挖格子活动成功
|
||||
public static final int activity_mining_apply_failed_ack = 11003; // 申请进入挖格子活动失败
|
||||
public static final int activity_mining_dig_req = 11004; // 挖格子
|
||||
public static final int activity_mining_dig_succeed_ack = 11005; // 挖格子成功
|
||||
public static final int activity_mining_dig_failed_ack = 11006; // 挖格子失败
|
||||
public static final int activity_mining_move_to_next_layer_req = 11007; // 挖格子进入下一层
|
||||
public static final int activity_mining_move_to_next_layer_succeed_ack = 11008; // 挖格子进入下一层成功
|
||||
public static final int activity_mining_move_to_next_layer_failed_ack = 11009; // 挖格子进入下一层失败
|
||||
public static final int activity_mining_story_reward_receive_req = 11010; // 请求领取挖格子活动剧情奖励
|
||||
public static final int activity_mining_story_reward_receive_succeed_ack = 11011; // 求领取挖格子活动剧情奖励成功
|
||||
public static final int activity_mining_story_reward_receive_failed_ack = 11012; // 求领取挖格子活动剧情奖励失败
|
||||
public static final int activity_mining_quest_reward_receive_req = 11013; // 挖格子领取任务奖励
|
||||
public static final int activity_mining_quest_reward_receive_succeed_ack = 11014; // 挖格子领取任务奖励成功
|
||||
public static final int activity_mining_quest_reward_receive_failed_ack = 11015; // 求挖格子领取任务奖励失败
|
||||
public static final int score_boss_apply_req = 11101; // 请求挑战积分boss
|
||||
public static final int score_boss_apply_succeed_ack = 11102; // 请求挑战积分boss成功
|
||||
public static final int score_boss_apply_failed_ack = 11103; // 请求挑战积分boss失败
|
||||
public static final int score_boss_settle_req = 11104; // 挑战积分boss结算
|
||||
public static final int score_boss_settle_succeed_ack = 11105; // 挑战积分boss结算成功
|
||||
public static final int score_boss_settle_failed_ack = 11106; // 挑战积分boss结算失败
|
||||
public static final int score_boss_rank_req = 11107; // 请求积分boss排行榜
|
||||
public static final int score_boss_rank_succeed_ack = 11108; // 请求积分boss排行榜成功
|
||||
public static final int score_boss_rank_failed_ack = 11109; // 请求积分boss失败
|
||||
public static final int score_boss_star_reward_receive_req = 11110; // 挑战积分boss结算
|
||||
public static final int score_boss_star_reward_receive_succeed_ack = 11111; // 挑战积分boss结算成功
|
||||
public static final int score_boss_star_reward_receive_failed_ack = 11112; // 挑战积分boss结算失败
|
||||
public static final int score_boss_info_req = 11113; // 请求积分挑战boss信息
|
||||
public static final int score_boss_info_succeed_ack = 11114; // 请求积分挑战boss信息成功
|
||||
public static final int score_boss_info_failed_ack = 11115; // 请求积分挑战boss信息失败
|
||||
public static final int activity_cookie_settle_req = 11201; // 曲奇工坊结算请求
|
||||
public static final int activity_cookie_settle_succeed_ack = 11202; // 曲奇工坊结算成功; 如果是首通返回首通奖励
|
||||
public static final int activity_cookie_settle_failed_ack = 11203; // 曲奇工坊结算失败
|
||||
public static final int activity_cookie_quest_reward_receive_req = 11204; // 曲奇工坊领取任务奖励
|
||||
public static final int activity_cookie_quest_reward_receive_succeed_ack = 11205; // 曲奇工坊领取任务奖励成功
|
||||
public static final int activity_cookie_quest_reward_receive_failed_ack = 11206; // 求曲奇工坊领取任务奖励失败
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package emu.nebula.net;
|
||||
|
||||
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.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.util.JsonUtils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public class NetMsgIdUtils {
|
||||
|
||||
private static Int2ObjectMap<String> msgIdMap;
|
||||
|
||||
static {
|
||||
msgIdMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
Field[] fields = NetMsgId.class.getFields();
|
||||
|
||||
for (Field f : fields) {
|
||||
if (f.getType().equals(int.class)) {
|
||||
try {
|
||||
msgIdMap.put(f.getInt(null), f.getName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMsgIdName(int msgId) {
|
||||
if (msgId <= 0) return "UNKNOWN";
|
||||
return msgIdMap.getOrDefault(msgId, "UNKNOWN");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void dumpPacketIds() {
|
||||
try (FileWriter writer = new FileWriter("./MsgIds_" + GameConstants.VERSION + ".json")) {
|
||||
// Create sorted tree map
|
||||
Map<Integer, String> packetIds = msgIdMap.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));
|
||||
Nebula.getLogger().info("Dumped packet ids.");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package emu.nebula.server;
|
||||
|
||||
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.nebula.Config.HttpServerConfig;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.Nebula.ServerType;
|
||||
import emu.nebula.server.routes.*;
|
||||
import io.javalin.Javalin;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class HttpServer {
|
||||
private final Javalin app;
|
||||
private ServerType type;
|
||||
private boolean started;
|
||||
|
||||
public HttpServer(ServerType type) {
|
||||
this.app = Javalin.create();
|
||||
this.type = type;
|
||||
|
||||
this.addRoutes();
|
||||
}
|
||||
|
||||
public HttpServerConfig getServerConfig() {
|
||||
return Nebula.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(Nebula.getConfig().getKeystore().getPath());
|
||||
sslContextFactory.setKeyStorePassword(Nebula.getConfig().getKeystore().getPassword());
|
||||
sslContextFactory.setSniRequired(false);
|
||||
sslContextFactory.setRenegotiationAllowed(false);
|
||||
return sslContextFactory;
|
||||
}
|
||||
|
||||
// Start server
|
||||
|
||||
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().getBindPort());
|
||||
getApp().jettyServer().server().addConnector(sslConnector);
|
||||
|
||||
getApp().start();
|
||||
} else {
|
||||
getApp().start(getServerConfig().getBindAddress(), getServerConfig().getBindPort());
|
||||
}
|
||||
|
||||
// Done
|
||||
Nebula.getLogger().info("Http Server started on " + getServerConfig().getBindPort());
|
||||
}
|
||||
|
||||
// Server endpoints
|
||||
|
||||
private void addRoutes() {
|
||||
// Add routes
|
||||
if (this.getType().runLogin()) {
|
||||
this.addLoginServerRoutes();
|
||||
}
|
||||
|
||||
if (this.getType().runGame()) {
|
||||
this.addGameServerRoutes();
|
||||
}
|
||||
|
||||
// Exception handler
|
||||
getApp().exception(Exception.class, (e, _) -> {
|
||||
e.printStackTrace();
|
||||
});
|
||||
|
||||
// Fallback handler
|
||||
getApp().error(404, this::notFoundHandler);
|
||||
}
|
||||
|
||||
private void addLoginServerRoutes() {
|
||||
// https://en-sdk-api.yostarplat.com/
|
||||
getApp().post("/common/config", new CommonConfigHandler(this));
|
||||
getApp().post("/common/version", new HttpJsonResponse("{\"Code\":200,\"Data\":{\"Agreement\":[{\"Version\":\"0.1\",\"Type\":\"user_agreement\",\"Title\":\"用户协议\",\"Content\":\"\",\"Lang\":\"en\"},{\"Version\":\"0.1\",\"Type\":\"privacy_agreement\",\"Title\":\"隐私政策\",\"Content\":\"\",\"Lang\":\"en\"}],\"ErrorCode\":\"4.4\"},\"Msg\":\"OK\"}"));
|
||||
|
||||
getApp().post("/user/detail", new UserLoginHandler());
|
||||
getApp().post("/user/set", new UserSetDataHandler());
|
||||
getApp().post("/user/login", new UserLoginHandler());
|
||||
getApp().post("/user/quick-login", new UserLoginHandler());
|
||||
|
||||
getApp().post("/yostar/get-auth", new GetAuthHandler());
|
||||
getApp().post("/yostar/send-code", new HttpJsonResponse("{\"Code\":200,\"Data\":{},\"Msg\":\"OK\"}")); // Dummy handler
|
||||
|
||||
// https://nova-static.stellasora.global/
|
||||
getApp().get("/meta/serverlist.html", new MetaServerlistHandler(this));
|
||||
getApp().get("/meta/win.html", new MetaWinHandler());
|
||||
}
|
||||
|
||||
private void addGameServerRoutes() {
|
||||
getApp().post("/agent-zone-1/", new AgentZoneHandler());
|
||||
}
|
||||
|
||||
private void notFoundHandler(Context ctx) {
|
||||
ctx.status(404);
|
||||
ctx.contentType(ContentType.APPLICATION_JSON);
|
||||
ctx.result("{}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.none)
|
||||
public class Handler extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Template
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Public.Achievements;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.achievement_info_req)
|
||||
public class HandlerAchievementInfoReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var rsp = Achievements.newInstance();
|
||||
|
||||
return this.encodeMsg(NetMsgId.achievement_info_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.ActivityDetail.ActivityMsg;
|
||||
import emu.nebula.proto.ActivityDetail.ActivityResp;
|
||||
import emu.nebula.proto.Public.ActivityTrial;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.activity_detail_req)
|
||||
public class HandlerActivityDetailReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var rsp = ActivityResp.newInstance();
|
||||
|
||||
var activity = ActivityMsg.newInstance()
|
||||
.setId(700101)
|
||||
.setTrial(ActivityTrial.newInstance());
|
||||
|
||||
rsp.addList(activity);
|
||||
|
||||
return this.encodeMsg(NetMsgId.activity_detail_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.agent_apply_req)
|
||||
public class HandlerAgentApplyReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
return this.encodeMsg(NetMsgId.agent_apply_failed_ack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.battle_pass_info_req)
|
||||
public class HandlerBattlePassInfoReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
return this.encodeMsg(NetMsgId.battle_pass_info_failed_ack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Public.UI32;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.char_advance_req)
|
||||
public class HandlerCharAdvanceReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var req = UI32.parseFrom(message);
|
||||
|
||||
// Get character
|
||||
var character = session.getPlayer().getCharacters().getCharacterById(req.getValue());
|
||||
|
||||
if (character == null) {
|
||||
return this.encodeMsg(NetMsgId.char_advance_failed_ack);
|
||||
}
|
||||
|
||||
// Advance character
|
||||
var change = character.advance();
|
||||
|
||||
if (change == null) {
|
||||
return this.encodeMsg(NetMsgId.char_advance_failed_ack);
|
||||
}
|
||||
|
||||
return this.encodeMsg(NetMsgId.char_advance_succeed_ack, change.toProto());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.CharSkillUpgrade.CharSkillUpgradeReq;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.char_skill_upgrade_req)
|
||||
public class HandlerCharSkillUpgradeReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var req = CharSkillUpgradeReq.parseFrom(message);
|
||||
|
||||
// Get character
|
||||
var character = session.getPlayer().getCharacters().getCharacterById(req.getCharId());
|
||||
|
||||
if (character == null) {
|
||||
return this.encodeMsg(NetMsgId.char_skill_upgrade_failed_ack);
|
||||
}
|
||||
|
||||
// Advance character
|
||||
int index = req.getIndex() - 1; // Lua indexes start at 1
|
||||
var change = character.upgradeSkill(index);
|
||||
|
||||
if (change == null) {
|
||||
return this.encodeMsg(NetMsgId.char_skill_upgrade_failed_ack);
|
||||
}
|
||||
|
||||
return this.encodeMsg(NetMsgId.char_skill_upgrade_succeed_ack, change.toProto());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.CharUpgrade.CharUpgradeReq;
|
||||
import emu.nebula.proto.CharUpgrade.CharUpgradeResp;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.char_upgrade_req)
|
||||
public class HandlerCharUpgradeReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Parse request
|
||||
var req = CharUpgradeReq.parseFrom(message);
|
||||
|
||||
// Get character
|
||||
var character = session.getPlayer().getCharacters().getCharacterById(req.getCharId());
|
||||
|
||||
if (character == null) {
|
||||
return this.encodeMsg(NetMsgId.char_upgrade_failed_ack);
|
||||
}
|
||||
|
||||
// Upgrade character
|
||||
var params = ItemParamMap.fromTemplates(req.getItems());
|
||||
var change = character.upgrade(params);
|
||||
|
||||
if (change == null) {
|
||||
return this.encodeMsg(NetMsgId.char_upgrade_failed_ack);
|
||||
}
|
||||
|
||||
// Create response
|
||||
var rsp = CharUpgradeResp.newInstance()
|
||||
.setChange(change.toProto())
|
||||
.setLevel(character.getLevel())
|
||||
.setExp(character.getExp());
|
||||
|
||||
return this.encodeMsg(NetMsgId.char_upgrade_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.client_event_report_req)
|
||||
public class HandlerClientEventReportReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
return this.encodeMsg(NetMsgId.client_event_report_succeed_ack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.DiscPromote.DiscPromoteReq;
|
||||
import emu.nebula.proto.DiscPromote.DiscPromoteResp;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.disc_promote_req)
|
||||
public class HandlerDiscPromoteReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Parse request
|
||||
var req = DiscPromoteReq.parseFrom(message);
|
||||
|
||||
// Get character
|
||||
var disc = session.getPlayer().getCharacters().getDiscById(req.getId());
|
||||
|
||||
if (disc == null) {
|
||||
return this.encodeMsg(NetMsgId.disc_promote_failed_ack);
|
||||
}
|
||||
|
||||
// Advance character
|
||||
var change = disc.promote();
|
||||
|
||||
if (change == null) {
|
||||
return this.encodeMsg(NetMsgId.disc_promote_failed_ack);
|
||||
}
|
||||
|
||||
// Build request
|
||||
var rsp = DiscPromoteResp.newInstance()
|
||||
.setPhase(disc.getPhase())
|
||||
.setChange(change.toProto());
|
||||
|
||||
return this.encodeMsg(NetMsgId.disc_promote_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.disc_read_reward_receive_req)
|
||||
public class HandlerDiscReadRewardReceiveReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
return this.encodeMsg(NetMsgId.disc_read_reward_receive_failed_ack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.DiscStrengthen.DiscStrengthenReq;
|
||||
import emu.nebula.proto.DiscStrengthen.DiscStrengthenResp;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.disc_strengthen_req)
|
||||
public class HandlerDiscStrengthenReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Parse request
|
||||
var req = DiscStrengthenReq.parseFrom(message);
|
||||
|
||||
// Get character
|
||||
var disc = session.getPlayer().getCharacters().getDiscById(req.getId());
|
||||
|
||||
if (disc == null) {
|
||||
return this.encodeMsg(NetMsgId.disc_strengthen_failed_ack);
|
||||
}
|
||||
|
||||
// Upgrade character
|
||||
var params = ItemParamMap.fromItemInfos(req.getItems());
|
||||
var change = disc.upgrade(params);
|
||||
|
||||
if (change == null) {
|
||||
return this.encodeMsg(NetMsgId.disc_strengthen_failed_ack);
|
||||
}
|
||||
|
||||
// Create response
|
||||
var rsp = DiscStrengthenResp.newInstance()
|
||||
.setChange(change.toProto())
|
||||
.setLevel(disc.getLevel())
|
||||
.setExp(disc.getExp());
|
||||
|
||||
return this.encodeMsg(NetMsgId.disc_strengthen_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.FriendListGet.FriendListGetResp;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.friend_list_get_req)
|
||||
public class HandlerFriendListGetReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var rsp = FriendListGetResp.newInstance();
|
||||
|
||||
return this.encodeMsg(NetMsgId.friend_list_get_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.GachaInformation.GachaInformationResp;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.gacha_information_req)
|
||||
public class HandlerGachaInformationReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var rsp = GachaInformationResp.newInstance();
|
||||
|
||||
// TODO
|
||||
|
||||
return this.encodeMsg(NetMsgId.gacha_information_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.GachaInformation.GachaInformationResp;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.gacha_newbie_info_req)
|
||||
public class HandlerGachaNewbieInfoReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var rsp = GachaInformationResp.newInstance();
|
||||
|
||||
return this.encodeMsg(NetMsgId.gacha_newbie_info_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.GachaNewbieObtain.GachaNewbieObtainReq;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.gacha_newbie_obtain_req)
|
||||
public class HandlerGachaNewbieObtainReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
@SuppressWarnings("unused")
|
||||
var req = GachaNewbieObtainReq.parseFrom(message);
|
||||
|
||||
// TODO
|
||||
return this.encodeMsg(NetMsgId.gacha_newbie_obtain_failed_ack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.GachaNewbieSpin.GachaNewbieSpinResp;
|
||||
import emu.nebula.proto.GachaSpin.GachaSpinReq;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.gacha_newbie_spin_req)
|
||||
public class HandlerGachaNewbieSpinReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
@SuppressWarnings("unused")
|
||||
var req = GachaSpinReq.parseFrom(message);
|
||||
|
||||
// Temp
|
||||
var list = new IntArrayList();
|
||||
|
||||
for (var d : GameData.getCharacterDataTable()) {
|
||||
if (d.getGrade() == 1) {
|
||||
list.add(d.getId());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
var rsp = GachaNewbieSpinResp.newInstance();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int id = Utils.randomElement(list);
|
||||
rsp.addCards(id);
|
||||
}
|
||||
|
||||
return this.encodeMsg(NetMsgId.gacha_newbie_spin_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.GachaSpin.GachaCard;
|
||||
import emu.nebula.proto.GachaSpin.GachaSpinReq;
|
||||
import emu.nebula.proto.GachaSpin.GachaSpinResp;
|
||||
import emu.nebula.proto.Public.ItemTpl;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.gacha_spin_req)
|
||||
public class HandlerGachaSpinReq extends NetHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var req = GachaSpinReq.parseFrom(message);
|
||||
|
||||
// Temp
|
||||
var list = new IntArrayList();
|
||||
|
||||
for (var def : GameData.getCharacterDataTable()) {
|
||||
if (def.getGrade() == 1 && def.isAvailable()) {
|
||||
list.add(def.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// Build response
|
||||
var rsp = GachaSpinResp.newInstance()
|
||||
.setTime(Nebula.getCurrentTime());
|
||||
|
||||
rsp.getMutableChange();
|
||||
rsp.getMutableNextPackage();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int id = Utils.randomElement(list);
|
||||
|
||||
var card = GachaCard.newInstance()
|
||||
.setCard(ItemTpl.newInstance().setTid(id).setQty(1));
|
||||
|
||||
rsp.addCards(card);
|
||||
}
|
||||
|
||||
return this.encodeMsg(NetMsgId.gacha_spin_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Ike.IKEReq;
|
||||
import emu.nebula.proto.Ike.IKEResp;
|
||||
import emu.nebula.util.Utils;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.ike_req)
|
||||
public class HandlerIkeReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public boolean requireSession() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requirePlayer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Make sure we dont already have a session
|
||||
if (session != null) {
|
||||
return this.encodeMsg(NetMsgId.ike_failed_ack);
|
||||
}
|
||||
|
||||
// Parse
|
||||
var req = IKEReq.parseFrom(message);
|
||||
|
||||
// Create session
|
||||
session = new GameSession();
|
||||
session.setClientKey(req.getPubKey());
|
||||
session.generateServerKey();
|
||||
session.calculateKey();
|
||||
|
||||
// Register session to game context
|
||||
Nebula.getGameContext().generateSessionToken(session);
|
||||
|
||||
// Create response
|
||||
var rsp = IKEResp.newInstance()
|
||||
.setToken(session.getToken())
|
||||
.setCipher(1) // 0 = gcm, 1 = chacha20
|
||||
.setServerTs(Nebula.getCurrentTime())
|
||||
.setPubKey(session.getServerPublicKey());
|
||||
|
||||
// Debug
|
||||
Nebula.getLogger().info("Client Public: " + Utils.base64Encode(session.getClientPublicKey()));
|
||||
Nebula.getLogger().info("Server Public: " + Utils.base64Encode(session.getServerPublicKey()));
|
||||
Nebula.getLogger().info("Server Private: " + Utils.base64Encode(session.getServerPrivateKey()));
|
||||
Nebula.getLogger().info("Key: " + Utils.base64Encode(session.getKey()));
|
||||
|
||||
// Encode and send to client
|
||||
return this.encodeMsg(NetMsgId.ike_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Public.Mails;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.mail_list_req)
|
||||
public class HandlerMailListReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Build mail list proto
|
||||
var rsp = Mails.newInstance();
|
||||
|
||||
for (var mail : session.getPlayer().getMailbox()) {
|
||||
rsp.addList(mail.toProto());
|
||||
}
|
||||
|
||||
return this.encodeMsg(NetMsgId.mail_list_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.MailPin.MailPinRequest;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.game.mail.GameMail;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.mail_pin_req)
|
||||
public class HandlerMailPinReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Parse request
|
||||
var req = MailPinRequest.parseFrom(message);
|
||||
|
||||
// Pin mail
|
||||
GameMail mail = session.getPlayer().getMailbox().pinMail(req.getId(), req.getFlag(), req.hasPin());
|
||||
|
||||
// Sanity check
|
||||
if (mail == null) {
|
||||
return this.encodeMsg(NetMsgId.mail_pin_failed_ack);
|
||||
}
|
||||
|
||||
// Build response
|
||||
var rsp = MailPinRequest.newInstance()
|
||||
.setId(mail.getId())
|
||||
.setFlag(mail.getFlag())
|
||||
.setPin(mail.isPin());
|
||||
|
||||
return this.encodeMsg(NetMsgId.mail_pin_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Public.MailRequest;
|
||||
import emu.nebula.proto.Public.UI32;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.mail_read_req)
|
||||
public class HandlerMailReadReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var req = MailRequest.parseFrom(message);
|
||||
|
||||
boolean result = session.getPlayer().getMailbox().readMail(req.getId(), req.getFlag());
|
||||
|
||||
if (!result) {
|
||||
return this.encodeMsg(NetMsgId.mail_read_failed_ack);
|
||||
}
|
||||
|
||||
return this.encodeMsg(NetMsgId.mail_read_succeed_ack, UI32.newInstance().setValue(req.getId()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.MailRecv.MailRecvResp;
|
||||
import emu.nebula.proto.Public.MailRequest;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.mail_recv_req)
|
||||
public class HandlerMailRecvReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Parse request
|
||||
var req = MailRequest.parseFrom(message);
|
||||
|
||||
// Claim mail
|
||||
PlayerChangeInfo changes = session.getPlayer().getMailbox().recvMail(session.getPlayer(), req.getId());
|
||||
|
||||
// Build response
|
||||
var rsp = MailRecvResp.newInstance()
|
||||
.setItems(changes.toProto());
|
||||
|
||||
var recvList = (IntList) changes.getExtraData();
|
||||
|
||||
for (int id : recvList) {
|
||||
rsp.addIds(id);
|
||||
}
|
||||
|
||||
return this.encodeMsg(NetMsgId.mail_recv_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package emu.nebula.server.handlers;
|
||||
|
||||
import emu.nebula.net.NetHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.MailRemove.MailRemoveResp;
|
||||
import emu.nebula.proto.Public.MailRequest;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@HandlerId(NetMsgId.mail_remove_req)
|
||||
public class HandlerMailRemoveReq extends NetHandler {
|
||||
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
// Parse request
|
||||
var req = MailRequest.parseFrom(message);
|
||||
|
||||
// Claim mail
|
||||
IntList removed = session.getPlayer().getMailbox().removeMail(session.getPlayer(), req.getId());
|
||||
|
||||
// Build response
|
||||
var rsp = MailRemoveResp.newInstance();
|
||||
|
||||
for (int id : removed) {
|
||||
rsp.addIds(id);
|
||||
}
|
||||
|
||||
return this.encodeMsg(NetMsgId.mail_remove_succeed_ack, rsp);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user