Modernize command system

This commit is contained in:
Melledy
2023-10-09 02:23:28 -07:00
parent f61d40a4de
commit 0d9730c498
16 changed files with 497 additions and 360 deletions

View File

@@ -8,7 +8,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import ch.qos.logback.classic.Logger;
import emu.lunarcore.commands.ServerCommands;
import emu.lunarcore.command.CommandManager;
import emu.lunarcore.data.ResourceLoader;
import emu.lunarcore.database.DatabaseManager;
import emu.lunarcore.server.game.GameServer;
@@ -28,6 +28,8 @@ public class LunarRail {
@Getter private static HttpServer httpServer;
@Getter private static GameServer gameServer;
@Getter private static CommandManager commandManager;
private static ServerType serverType = ServerType.BOTH;
// Load config first before doing anything
@@ -38,6 +40,9 @@ public class LunarRail {
public static void main(String[] args) {
// Start Server
LunarRail.getLogger().info("Starting Lunar Rail...");
// Load commands
LunarRail.commandManager = new CommandManager();
// Parse arguments
for (String arg : args) {
@@ -58,7 +63,7 @@ public class LunarRail {
return;
}
}
// Load resources
ResourceLoader.loadAll();
@@ -127,7 +132,7 @@ public class LunarRail {
String input;
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
while ((input = br.readLine()) != null) {
ServerCommands.handle(input);
LunarRail.getCommandManager().invoke(null, input);
}
} catch (Exception e) {
LunarRail.getLogger().error("Console error:", e);

View File

@@ -1,13 +1,13 @@
package emu.lunarcore.commands;
package emu.lunarcore.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 int gmLevel() default 1;
public String desc() default "";
}

View File

@@ -0,0 +1,80 @@
package emu.lunarcore.command;
import java.util.List;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
import lombok.Getter;
@Getter
public class CommandArgs {
private List<String> list;
private Player target;
private int targetUid;
private int level;
private int count;
private int rank;
private int promotion;
private static String EMPTY_STRING = "";
public CommandArgs(Player sender, List<String> 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("@")) {
this.targetUid = Utils.parseSafeInt(arg.substring(1));
it.remove();
} else if (arg.startsWith("x")) {
this.count = Utils.parseSafeInt(arg.substring(1));
it.remove();
} else if (arg.startsWith("lv")) {
this.level = Utils.parseSafeInt(arg.substring(2));
it.remove();
} else if (arg.startsWith("r")) {
this.rank = Utils.parseSafeInt(arg.substring(1));
it.remove();
} else if (arg.startsWith("p")) {
this.promotion = Utils.parseSafeInt(arg.substring(1));
it.remove();
}
}
} catch (Exception e) {
}
}
// Get target player
if (targetUid != 0) {
if (LunarRail.getGameServer() != null) {
target = LunarRail.getGameServer().getOnlinePlayerByUid(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 EMPTY_STRING;
}
return this.list.get(index);
}
}

View File

@@ -0,0 +1,22 @@
package emu.lunarcore.command;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.player.Player;
public interface CommandHandler {
public default String getLabel() {
return this.getClass().getAnnotation(Command.class).label();
}
public default void sendMessage(Player player, String message) {
if (player != null) {
player.sendMessage(message);
} else {
LunarRail.getLogger().info(message);
}
}
public void execute(Player sender, CommandArgs args);
}

View File

@@ -0,0 +1,102 @@
package emu.lunarcore.command;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.reflections.Reflections;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.player.Player;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.AccessLevel;
import lombok.Getter;
@Getter(AccessLevel.PRIVATE)
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();
}
}
}
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;
}
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;
}
public void invoke(Player sender, String message) {
List<String> args = Arrays.stream(message.split(" ")).collect(Collectors.toCollection(ArrayList::new));
// Get command label
String label = args.remove(0).toLowerCase();
if (label.startsWith("/") || label.startsWith("!")) {
label = label.substring(1);
}
// Get handler
CommandHandler handler = this.commands.get(label);
if (handler != null) {
handler.execute(sender, new CommandArgs(sender, args));
} else {
if (sender != null) {
sender.sendMessage("Inavlid Command!");
} else {
LunarRail.getLogger().info("Inavlid Command!");
}
}
}
}

View File

@@ -0,0 +1,48 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.account.AccountHelper;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
@Command(label = "account")
public class AccountCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
if (args.size() < 2) {
this.sendMessage(sender, "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)) {
this.sendMessage(sender, "Account created");
} else {
this.sendMessage(sender, "Account already exists");
}
}
case "delete" -> {
if (AccountHelper.deleteAccount(username)) {
this.sendMessage(sender, "Account deleted");
} else {
this.sendMessage(sender, "Account doesnt exist");
}
}
}
}
}

View File

@@ -0,0 +1,62 @@
package emu.lunarcore.command.commands;
import java.util.LinkedList;
import java.util.List;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.inventory.ItemMainType;
import emu.lunarcore.game.player.Player;
@Command(label = "clear")
public class ClearCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
List<GameItem> toRemove = new LinkedList<>();
String type = args.get(0).toLowerCase();
switch (type) {
case "relics", "r" -> {
for (GameItem item : args.getTarget().getInventory().getItems().values()) {
if (item.getItemMainType() == ItemMainType.Relic && item.getLevel() <= 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) {
toRemove.add(item);
}
}
}
case "equipment", "lightcones", "lc" -> {
for (GameItem item : args.getTarget().getInventory().getItems().values()) {
if (item.getItemMainType() == ItemMainType.Equipment && item.getLevel() <= 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) {
toRemove.add(item);
}
}
}
case "materials", "mats", "m" -> {
for (GameItem item : args.getTarget().getInventory().getItems().values()) {
if (item.getItemMainType() == ItemMainType.Material) {
toRemove.add(item);
}
}
}
case "items" -> {
for (GameItem item : args.getTarget().getInventory().getItems().values()) {
if (!item.isLocked() && !item.isEquipped()) {
toRemove.add(item);
}
}
}
}
args.getTarget().getInventory().removeItems(toRemove);
this.sendMessage(sender, "Removed " + toRemove.size() + " items");
}
}

View File

@@ -0,0 +1,62 @@
package emu.lunarcore.command.commands;
import java.util.ArrayList;
import java.util.List;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.inventory.ItemMainType;
import emu.lunarcore.game.inventory.ItemSubType;
import emu.lunarcore.game.player.Player;
@Command(label = "giveall", aliases = {"ga"})
public class GiveAllCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
String type = args.get(0).toLowerCase();
switch (type) {
case "materials", "mats" -> {
//
List<GameItem> items = new ArrayList<>();
// Character/Relic/Lightcone upgrade materials
for (ItemExcel excel : GameData.getItemExcelMap().values()) {
int purpose = excel.getPurposeType();
if (purpose >= 1 && purpose <= 7) {
items.add(new GameItem(excel, 1000));
}
}
// Credits
items.add(new GameItem(2, 50_000_000));
// Add
args.getTarget().getInventory().addItems(items);
// Send message
this.sendMessage(sender, "Giving " + args.getTarget().getName() + " " + items.size() + " items");
}
case "avatars" -> {
// All avatars and their eidolons
for (ItemExcel excel : GameData.getItemExcelMap().values()) {
if (excel.getItemMainType() == ItemMainType.AvatarCard) {
args.getTarget().getInventory().addItem(excel, 1);
} else if (excel.getItemSubType() == ItemSubType.Eidolon) {
args.getTarget().getInventory().addItem(excel, 6);
}
}
// Send message
this.sendMessage(sender, "Giving " + args.getTarget().getName() + " all avatars");
}
}
}
}

View File

@@ -0,0 +1,51 @@
package emu.lunarcore.command.commands;
import java.util.LinkedList;
import java.util.List;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
@Command(label = "give", aliases = {"g"})
public class GiveCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
} else if (args.getCount() <= 0) {
this.sendMessage(sender, "Error: Amount must be higher than 0");
return;
}
int itemId = Utils.parseSafeInt(args.get(0));
ItemExcel itemData = GameData.getItemExcelMap().get(itemId);
if (itemData == null) {
this.sendMessage(sender, "Error: Item data not found");
return;
}
if (itemData.isEquippable()) {
List<GameItem> items = new LinkedList<>();
for (int i = 0; i < args.getCount(); i++) {
items.add(new GameItem(itemData));
}
args.getTarget().getInventory().addItems(items);
} else {
GameItem item = new GameItem(itemData, args.getCount());
args.getTarget().getInventory().addItem(item);
}
args.getTarget().sendMessage("Giving " + args.getTarget().getName() + " " + args.getCount() + " of " + itemId);
}
}

View File

@@ -0,0 +1,18 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.LunarRail;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player;
@Command(label = "reload")
public class ReloadCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
LunarRail.loadConfig();
this.sendMessage(sender, "Reloaded the server config");
}
}

View File

@@ -0,0 +1,30 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
@Command(label = "worldlevel", aliases = {"wl"})
public class WorldLevelCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
// Set world level
int level = Utils.parseSafeInt(args.get(0));
level = Math.min(Math.max(level, 0), 6);
args.getTarget().setWorldLevel(level);
// Done
this.sendMessage(sender, "Set world level to " + level);
}
}

View File

@@ -1,249 +0,0 @@
package emu.lunarcore.commands;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.data.excel.NpcMonsterExcel;
import emu.lunarcore.data.excel.StageExcel;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.inventory.ItemMainType;
import emu.lunarcore.game.inventory.ItemSubType;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.util.Position;
@SuppressWarnings("unused")
public class PlayerCommands {
private static HashMap<String, PlayerCommand> list = new HashMap<>();
static {
try {
// Look for classes
for (Class<?> cls : PlayerCommands.class.getDeclaredClasses()) {
// Get non abstract classes
if (!Modifier.isAbstract(cls.getModifiers())) {
Command commandAnnotation = cls.getAnnotation(Command.class);
PlayerCommand command = (PlayerCommand) cls.getConstructor().newInstance();
if (commandAnnotation != null) {
command.setLevel(commandAnnotation.gmLevel());
for (String alias : commandAnnotation.aliases()) {
if (alias.length() == 0) {
continue;
}
String commandName = "!" + alias;
list.put(commandName, command);
commandName = "/" + alias;
list.put(commandName, command);
}
}
String commandName = "!" + cls.getSimpleName().toLowerCase();
list.put(commandName, command);
commandName = "/" + cls.getSimpleName().toLowerCase();
list.put(commandName, command);
}
}
} catch (Exception e) {
}
}
public static void handle(Player player, String msg) {
String[] split = msg.split(" ");
// End if invalid
if (split.length == 0) {
return;
}
//
String first = split[0].toLowerCase();
PlayerCommand c = PlayerCommands.list.get(first);
if (c != null) {
// Execute
int len = Math.min(first.length() + 1, msg.length());
c.execute(player, msg.substring(len));
} else {
player.dropMessage("Error: Invalid command!");
}
}
public static abstract class PlayerCommand {
// GM level required to use this command
private int level;
protected int getLevel() { return this.level; }
protected void setLevel(int minLevel) { this.level = minLevel; }
// Main
public abstract void execute(Player player, String raw);
}
// ================ Commands ================
@Command(aliases = {"g", "item"}, desc = "/give [item id] [count] - Gives {count} amount of {item id}")
public static class Give extends PlayerCommand {
@Override
public void execute(Player player, String raw) {
String[] split = raw.split(" ");
int itemId = 0, count = 1;
try {
itemId = Integer.parseInt(split[0]);
} catch (Exception e) {
itemId = 0;
}
try {
count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1);
} catch (Exception e) {
count = 1;
}
// Give
ItemExcel itemData = GameData.getItemExcelMap().get(itemId);
GameItem item;
if (itemData == null) {
player.dropMessage("Error: Item data not found");
return;
}
if (itemData.isEquippable()) {
//List<GameItem> items = new LinkedList<>();
for (int i = 0; i < count; i++) {
item = new GameItem(itemData);
//items.add(item);
player.getInventory().addItem(item);
}
// TODO add item hint packet
} else {
item = new GameItem(itemData, count);
player.getInventory().addItem(item);
// TODO add item hint packet
}
player.dropMessage("Giving you " + count + " of " + itemId);
}
}
@Command(aliases = {"wl", "el"}, desc = "/worldlevel [world level]")
public static class WorldLevel extends PlayerCommand {
@Override
public void execute(Player player, String raw) {
int level = 0;
try {
level = Integer.parseInt(raw);
} catch (Exception e) {
level = 0;
}
level = Math.min(Math.max(level, 0), 6);
// Set world level
player.setWorldLevel(level);
player.dropMessage("Set world level to " + level);
}
}
@Command(aliases = {"ga"}, desc = "/giveall {materials|avatars}")
public static class GiveAll extends PlayerCommand {
@Override
public void execute(Player player, String raw) {
switch (raw) {
case "materials":
//
List<GameItem> items = new ArrayList<>();
// Character/Relic/Lightcone upgrade materials
for (ItemExcel excel : GameData.getItemExcelMap().values()) {
int purpose = excel.getPurposeType();
if (purpose >= 1 && purpose <= 7) {
items.add(new GameItem(excel, 1000));
}
}
// Credits
items.add(new GameItem(2, 50_000_000));
// Add
player.getInventory().addItems(items);
break;
case "avatars":
// All avatars and their eidolons
for (ItemExcel excel : GameData.getItemExcelMap().values()) {
if (excel.getItemMainType() == ItemMainType.AvatarCard) {
player.getInventory().addItem(excel, 1);
} else if (excel.getItemSubType() == ItemSubType.Eidolon) {
player.getInventory().addItem(excel, 6);
}
}
break;
}
}
}
@Command(desc = "/clearrelics")
public static class ClearRelics extends PlayerCommand {
@Override
public void execute(Player player, String raw) {
List<GameItem> toRemove = new LinkedList<>();
for (GameItem item : player.getInventory().getItems().values()) {
if (item.getItemMainType() == ItemMainType.Relic && item.getLevel() <= 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) {
toRemove.add(item);
}
}
player.getInventory().removeItems(toRemove);
}
}
/* Temporarily disabled as spawned monsters need
@Command(desc = "/spawn [monster id] [count] - Creates {count} amount of {item id}")
public static class Spawn extends PlayerCommand {
@Override
public void execute(Player player, String raw) {
String[] split = raw.split(" ");
int monsterId = 0, stageId = 2;
try {
monsterId = Integer.parseInt(split[0]);
} catch (Exception e) {
monsterId = 0;
}
try {
stageId = Integer.parseInt(split[1]);
} catch (Exception e) {
stageId = 2;
}
// TODO
NpcMonsterExcel excel = GameData.getNpcMonsterExcelMap().get(monsterId);
if (excel == null) {
player.dropMessage("Npc monster id not found!");
return;
}
StageExcel stage = GameData.getStageExcelMap().get(stageId);
if (stage == null) {
player.dropMessage("Stage id not found!");
return;
}
Position pos = player.getPos().clone();
pos.setX(pos.getX() + 50);
// Add to scene
EntityMonster monster = new EntityMonster(excel, stage, pos);
player.getScene().addMonster(monster);
}
}
*/
}

View File

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

View File

@@ -3,7 +3,7 @@ package emu.lunarcore.game.chat;
import java.util.Collection;
import emu.lunarcore.GameConstants;
import emu.lunarcore.commands.PlayerCommands;
import emu.lunarcore.LunarRail;
import emu.lunarcore.game.player.BasePlayerManager;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.server.packet.send.PacketRevcMsgScNotify;
@@ -52,7 +52,7 @@ public class ChatManager extends BasePlayerManager {
// Check if command
if (text.charAt(0) == '!' || text.charAt(0) == '/') {
PlayerCommands.handle(getPlayer(), text);
LunarRail.getCommandManager().invoke(getPlayer(), text);
return;
}

View File

@@ -479,7 +479,7 @@ public class Player {
return true;
}
public void dropMessage(String message) {
public void sendMessage(String message) {
var msg = new ChatMessage(GameConstants.SERVER_CONSOLE_UID, this.getUid(), message);
this.getChatManager().addChatMessage(msg);
}

View File

@@ -89,6 +89,10 @@ public class Utils {
}
public static int parseSafeInt(String s) {
if (s == null) {
return 0;
}
int i = 0;
try {
@@ -101,6 +105,10 @@ public class Utils {
}
public static long parseSafeLong(String s) {
if (s == null) {
return 0;
}
long i = 0;
try {