Implement ban functionality

- Added ban module
- Added Ban and Unban commands, supporting banning and unbanning players through various command parameters.
- Added timestamp formatting method in Utils utility class for displaying ban expiration times
- Introduced PlayerErrorCode enum defining various error codes including ErrBan
- Added dual ban checking for both IP and user during player login
- Optimized login failure responses to provide specific error reasons and parameters
This commit is contained in:
HongchengQ
2025-12-09 22:21:57 +08:00
committed by Melledy
parent 6483e8a5a7
commit 3a6387c2bd
11 changed files with 750 additions and 9 deletions

View File

@@ -0,0 +1,115 @@
package emu.nebula.command.commands;
import emu.nebula.Nebula;
import emu.nebula.command.Command;
import emu.nebula.command.CommandArgs;
import emu.nebula.command.CommandHandler;
import emu.nebula.game.player.Player;
import emu.nebula.util.Utils;
import java.util.Locale;
@Command(label = "ban",
permission = "admin.ban",
desc = """
!ban {all | ip | uid} [player uid | ip] (end timestamp) (reason) - Ban a player\
- all mode bans both the player object and their IP address by UID, so the next parameter should be UID instead of IP
- ip mode can only ban IP addresses, so the next parameter should be an IP
- uid mode can only ban UIDs, so the next parameter should be a UID
- If you don't fill in the end timestamp, it will be permanently banned by default\
""")
public class BanCommand implements CommandHandler {
@Override
public String execute(CommandArgs args) {
if (args.size() < 2) {
return "Invalid amount of args";
}
int bannedUid = 0;
long banEndTime = 0;
String bannedIp = null;
String reason = null;
String mode = args.get(0).toLowerCase(Locale.ROOT);
switch (args.size()) {
case 4:
reason = args.get(3);
case 3: {
try {
banEndTime = Long.parseLong(args.get(2));
} catch (NumberFormatException ignored) {
return "Unable to parse timestamp.";
}
}
case 2: {
if (mode.equals("ip")) {
bannedIp = args.get(1);
} else {
try {
bannedUid = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
return "Unable to parse uid.";
}
}
}
case 1: {
if (!mode.equals("all") && !mode.equals("uid") && !mode.equals("ip"))
return "Unable to parse mode.";
}
default: break;
}
if (banEndTime != 0 && banEndTime < System.currentTimeMillis()) {
return "Failed, the end timestamp must be greater than the current time";
}
var banModule = Nebula.getGameContext().getBanModule();
Player player;
if (!mode.equals("ip")) {
player = Nebula.getGameContext().getPlayerModule().getPlayer(bannedUid);
if (player == null) {
return "Failed, player not found.";
}
}
switch (mode) {
case "all" -> {
banModule.banPlayer(
bannedUid,
banEndTime,
reason,
true,
args.getSender() != null ? String.valueOf(args.getSender().getUid()) : "Console");
return "Banned player all mode " + bannedUid + " until " + Utils.formatTimestamp(banEndTime) +
(reason != null ? " (" + reason + ")" : "");
}
case "ip" -> {
banModule.banIp(
bannedIp,
banEndTime,
reason,
args.getSender() != null ? String.valueOf(args.getSender().getUid()) : "Console");
return "Banned ip " + bannedIp + " until " + Utils.formatTimestamp(banEndTime) +
(reason != null ? " (" + reason + ")" : "");
}
case "uid" -> {
banModule.banPlayer(
bannedUid,
banEndTime,
reason,
false,
args.getSender() != null ? String.valueOf(args.getSender().getUid()) : "Console");
return "Banned player " + bannedUid + " until " + Utils.formatTimestamp(banEndTime) +
(reason != null ? " (" + reason + ")" : "");
}
default -> {
return "Ban sub command not found";
}
}
}
}

View File

@@ -0,0 +1,81 @@
package emu.nebula.command.commands;
import emu.nebula.Nebula;
import emu.nebula.command.Command;
import emu.nebula.command.CommandArgs;
import emu.nebula.command.CommandHandler;
import emu.nebula.game.ban.BanInfo;
import emu.nebula.game.player.Player;
import java.util.Locale;
@Command(label = "unban", permission = "admin.ban", desc = """
!unban {all | ip | uid} [player uid | ip]\
- all mode unbans both the player object and their IP address by UID, so the next parameter should be UID instead of IP
- ip mode can only unban IP addresses, so the next parameter should be an IP
- uid mode can only unban UIDs, so the next parameter should be a UID\
""")
public class UnbanCommand implements CommandHandler {
@Override
public String execute(CommandArgs args) {
if (args.size() < 2) {
return "Invalid amount of args";
}
int unbannedUid = 0;
String unbannedIp = null;
String mode = args.get(0).toLowerCase(Locale.ROOT);
if (!mode.equals("all") && !mode.equals("uid") && !mode.equals("ip"))
return "Unable to parse mode.";
if (mode.equals("ip")) {
unbannedIp = args.get(1);
} else {
try {
unbannedUid = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
return "Unable to parse uid.";
}
}
var banModule = Nebula.getGameContext().getBanModule();
Player player = null;
if (!mode.equals("ip")) {
player = Nebula.getGameContext().getPlayerModule().getPlayer(unbannedUid);
if (player == null) {
return "Failed, player not found.";
}
}
switch (mode) {
case "all" -> {
BanInfo banInfo = banModule.getPlayerBanInfo(player.getUid());
banModule.unbanPlayer(player.getUid());
if (banInfo != null) {
unbannedIp = banInfo.getIpAddress();
if (unbannedIp != null)
banModule.unbanIp(unbannedIp);
}
return "Unban a player all mode " + unbannedUid;
} case "uid" -> {
banModule.unbanPlayer(player.getUid());
return "Unban a player " + unbannedUid;
} case "ip" -> {
banModule.unbanIp(unbannedIp);
return "Unban a ip " + unbannedIp;
}
default -> {
// Fallback
return "Unban sub command not found";
}
}
}
}

View File

@@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit;
import emu.nebula.GameConstants; import emu.nebula.GameConstants;
import emu.nebula.Nebula; import emu.nebula.Nebula;
import emu.nebula.game.activity.ActivityModule; import emu.nebula.game.activity.ActivityModule;
import emu.nebula.game.ban.BanModule;
import emu.nebula.game.gacha.GachaModule; import emu.nebula.game.gacha.GachaModule;
import emu.nebula.game.player.PlayerModule; import emu.nebula.game.player.PlayerModule;
import emu.nebula.game.scoreboss.ScoreBossModule; import emu.nebula.game.scoreboss.ScoreBossModule;
@@ -30,6 +31,7 @@ public class GameContext implements Runnable {
private final TutorialModule tutorialModule; private final TutorialModule tutorialModule;
private final ActivityModule activityModule; private final ActivityModule activityModule;
private final ScoreBossModule scoreBossModule; private final ScoreBossModule scoreBossModule;
private final BanModule banModule;
// Game loop // Game loop
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
@@ -48,6 +50,7 @@ public class GameContext implements Runnable {
this.tutorialModule = new TutorialModule(this); this.tutorialModule = new TutorialModule(this);
this.activityModule = new ActivityModule(this); this.activityModule = new ActivityModule(this);
this.scoreBossModule = new ScoreBossModule(this); this.scoreBossModule = new ScoreBossModule(this);
this.banModule = new BanModule(this);
// Run game loop // Run game loop
this.scheduler = Executors.newScheduledThreadPool(1); this.scheduler = Executors.newScheduledThreadPool(1);

View File

@@ -0,0 +1,65 @@
package emu.nebula.game.ban;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.nebula.Nebula;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.player.PlayerErrorCode;
import emu.nebula.proto.Public.Error;
import emu.nebula.util.Utils;
import lombok.Getter;
import java.util.Date;
@Getter
@Entity(value = "bans", useDiscriminator = false)
public class BanInfo implements GameDatabaseObject {
@Id
private String id;
private int playerUid;
private long startTime;
private long endTime;
private String reason;
private String bannedBy;
private String ipAddress;
@Deprecated // Morphia only
public BanInfo() {
}
public BanInfo(int playerUid, long endTime, String reason, String bannedBy, String ipAddress) {
this.playerUid = playerUid;
this.startTime = System.currentTimeMillis();
this.endTime = endTime;
this.reason = reason;
this.bannedBy = bannedBy;
this.ipAddress = ipAddress;
// Generate ID based on either player UID or IP address
this.id = (ipAddress != null && !ipAddress.isEmpty() && playerUid == 0) ? "ip_" + ipAddress :
"player_" + playerUid;
}
public boolean isExpired() {
return endTime != 0 && endTime < System.currentTimeMillis();
}
public String getExpirationDateString() {
if (endTime == 0) {
return "Never";
}
return Utils.formatTimestamp(this.endTime);
}
@Override
public void save() {
GameDatabaseObject.super.save();
}
public Error toProto() {
return Error.newInstance()
.setCode(PlayerErrorCode.ErrBan.getValue())
.addArguments(
getExpirationDateString() + "\n" +
(this.reason != null ? "\n (" + this.reason + ")" : "\n" + this.id));
}
}

View File

@@ -0,0 +1,242 @@
package emu.nebula.game.ban;
import emu.nebula.Nebula;
import emu.nebula.game.player.Player;
import emu.nebula.game.GameContext;
import emu.nebula.game.GameContextModule;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BanModule extends GameContextModule {
private final Map<String, BanInfo> cachedIpBans = new ConcurrentHashMap<>();
private final Map<Integer, BanInfo> cachedPlayerBans = new ConcurrentHashMap<>();
public BanModule(GameContext context) {
super(context);
}
/**
* Ban a player by UID
* @param uid Player UID
* @param endTime Ban expiration time (0 = permanent)
* @param isBanIp Ban Ip
* @param reason Ban reason
* @param bannedBy Who banned the player
*/
public void banPlayer(int uid, long endTime, String reason, boolean isBanIp, String bannedBy) {
Player player = getGameContext().getPlayerModule().getPlayer(uid);
// It is only used as a supplement to find the banned IP when unblocking a player
String playerBindIp = null;
if (isBanIp) {
if (player != null && player.getSession() != null) {
String ipAddress = player.getSession().getIpAddress();
if (ipAddress != null && !ipAddress.isEmpty()) {
playerBindIp = ipAddress;
banIp(ipAddress, endTime, reason, bannedBy);
}
}
}
BanInfo banInfo = new BanInfo(uid, endTime, reason, bannedBy, playerBindIp);
cachedPlayerBans.put(uid, banInfo);
banInfo.save();
// Kick player
if (player != null && player.isLoaded()) {
player.setSession(null);
}
}
/**
* Ban an IP address
* <p>
* Please be cautious about disabling IPs
* in some regions where IPs are scarce, many people may share a public IP
* and restarting the optical cat device will reassign a new IP
*
* @param ipAddress IP address to ban
* @param endTime Ban expiration time (0 = permanent)
* @param reason Ban reason
* @param bannedBy Who banned the IP
*/
public void banIp(String ipAddress, long endTime, String reason, String bannedBy) {
BanInfo banInfo = new BanInfo(0, endTime, reason, bannedBy, ipAddress);
cachedIpBans.put(ipAddress, banInfo);
banInfo.save();
List<Player> playerList = Nebula.getGameContext().getPlayerModule().getCachedPlayers()
.values().stream().toList();
String playerIpAddress;
for (Player player : playerList) {
playerIpAddress = player.getSession().getIpAddress();
if (playerIpAddress == null) {
return;
}
// Kick player
if (playerIpAddress.equals(ipAddress)) {
player.setSession(null);
}
}
}
/**
* Unban a player
* @param uid Player UID
*/
public void unbanPlayer(int uid) {
BanInfo banInfo = cachedPlayerBans.remove(uid);
if (banInfo == null) {
banInfo = getPlayerBanInfo(uid);
}
if (banInfo == null) {
return;
}
deleteBan(banInfo);
}
/**
* Unban an IP address
* @param ipAddress IP address to unban
*/
public void unbanIp(String ipAddress) {
BanInfo banInfo = cachedIpBans.remove(ipAddress);
if (banInfo == null) {
banInfo = getIpBanInfo(ipAddress);
}
if (banInfo == null) {
return;
}
deleteBan(banInfo);
}
/**
* Check if a player is banned
* @param uid Player UID
* @return True if banned, false otherwise
*/
public boolean isPlayerBanned(int uid) {
BanInfo banInfo = cachedPlayerBans.get(uid);
if (banInfo == null) {
banInfo = loadPlayerBanFromDatabase(uid);
if (banInfo == null) {
return false;
}
cachedPlayerBans.put(uid, banInfo);
}
// Check if ban has expired
if (banInfo.isExpired()) {
unbanPlayer(uid);
return false;
}
return true;
}
/**
* Check if an IP address is banned
* @param ipAddress IP address
* @return True if banned, false otherwise
*/
public boolean isIpBanned(String ipAddress) {
BanInfo banInfo = cachedIpBans.get(ipAddress);
if (banInfo == null) {
banInfo = loadIpBanFromDatabase(ipAddress);
if (banInfo == null) {
return false;
}
cachedIpBans.put(ipAddress, banInfo);
}
// Check if ban has expired
if (banInfo.isExpired()) {
unbanIp(ipAddress);
return false;
}
return true;
}
/**
* Get ban info for a player
* @param uid Player UID
* @return BanInfo or null if not banned
*/
public BanInfo getPlayerBanInfo(int uid) {
BanInfo banInfo = cachedPlayerBans.get(uid);
if (banInfo == null) {
banInfo = loadPlayerBanFromDatabase(uid);
if (banInfo != null) {
cachedPlayerBans.put(uid, banInfo);
}
}
return banInfo;
}
/**
* Get ban info for an IP
* @param ipAddress IP address
* @return BanInfo or null if not banned
*/
public BanInfo getIpBanInfo(String ipAddress) {
BanInfo banInfo = cachedIpBans.get(ipAddress);
if (banInfo == null) {
banInfo = loadIpBanFromDatabase(ipAddress);
if (banInfo != null) {
cachedIpBans.put(ipAddress, banInfo);
}
}
return banInfo;
}
/**
* Delete ban from database
*/
private void deleteBan(BanInfo banInfo) {
try {
Nebula.getGameDatabase().delete(banInfo);
} catch (Exception e) {
Nebula.getLogger().error("Failed to delete ban info from database", e);
}
}
/**
* Load player ban from database
*/
private BanInfo loadPlayerBanFromDatabase(int uid) {
if (uid == 0)
return null;
try {
return Nebula.getGameDatabase().getObjectByField(BanInfo.class, "playerUid", uid);
} catch (Exception e) {
Nebula.getLogger().error("Failed to load player ban from database", e);
}
return null;
}
/**
* Load IP ban from database
*/
private BanInfo loadIpBanFromDatabase(String ipAddress) {
if (ipAddress == null || ipAddress.isEmpty())
return null;
try {
return Nebula.getGameDatabase().getObjectByField(BanInfo.class, "ipAddress", ipAddress);
} catch (Exception e) {
Nebula.getLogger().error("Failed to load IP ban from database", e);
}
return null;
}
}

View File

@@ -0,0 +1,192 @@
package emu.nebula.game.player;
import lombok.Getter;
// resources\bin\ErrorCode.json
// resources\language\en_US\ErrorCode.json
@Getter
public enum PlayerErrorCode {
ErrInvalidToken(100101),
ErrInvalidTimestamp(100102),
ErrInvalidMsgId(100103),
ErrInvalidMsgBody(100104),
ErrAgentBusy(100105),
ErrServiceUnavailable(100106),
ErrRequestTimeout(100107),
ErrLoginFailed(100108),
ErrInvalidVersion(100109),
ErrLogin(100110),
ErrMask(110101),
ErrServerBusy(110102),
ErrData(110103),
ErrArgs(110104),
ErrInternal(110105),
ErrTokenExpire(110106),
ErrRelogin(110107),
ErrInvalidReq(110108),
ErrApiDisabled(110109),
ErrResUpdated(110110),
ErrOperateTooFast(110111),
ErrNickLenViolation(110201),
ErrNickIllegal(110202),
ErrNickForbidden(110203),
ErrBan(110205),
ErrSignatureForbidden(110206),
ErrSignatureLenViolation(110207),
ErrSignatureIllegal(110208),
ErrTitleNotFound(110209),
ErrSurveyNotFound(110210),
ErrSurveyNotOpenYet(110211),
ErrSurveyHasCompleted(110212),
ErrMailMiss(110401),
ErrMailRevoked(110402),
ErrMailUpdate(110403),
ErrMailPinned(110404),
ErrQuestNotComplete(110501),
ErrQuestClosed(110502),
ErrProductNotReady(110601),
ErrProductRestockLimit(110602),
ErrGoodsNotFound(110603),
ErrGoodsLockPurchase(110604),
ErrGoodsDelisted(110605),
ErrShopNotFound(110606),
ErrShopAlreadyClosed(110607),
ErrRecvReward(110701),
ErrMainlineCondPremission(110702),
ErrEnergyHoldLimit(110801),
ErrEnergyBuyLimit(110802),
ErrEnergyLack(110803),
ErrEnergyRestore(110804),
ErrGachaSpinLimit(110901),
ErrItemUseRefused(111001),
ErrRglInProgress(111101),
ErrRglCond(111102),
ErrRglBuildNotFound(111103),
ErrRglBuildLock(111104),
ErrFormationIdNeed(111105),
ErrFormationFullNeed(111106),
ErrBuildNameMinLenLimit(111107),
ErrBuildNameMaxLenLimit(111108),
ErrBuildNameViolation(111109),
ErrOutfitStrengthenMax(111201),
ErrOutfitNoData(111202),
ErrOutfitBeEquipped(111203),
ErrOutfitBeLocked(111204),
ErrOutfitPhaseMax(111205),
ErrOutfitStarMax(111206),
ErrOutfitLevelNotReached(111207),
ErrGiftPackageInvalid(111301),
ErrGiftPackageExpired(111302),
ErrGiftPackError(111303),
ErrRedeemCodeReachedLimit(111304),
ErrUserExchangeCountReachedLimit(111305),
ErrGiftPackageAlreadyExists(111306),
ErrGiftPackageInfoInvalid(111307),
ErrRedeemCodeItemListEmpty(111308),
ErrRedeemCodeCodeItemInvalid(111309),
ErrGiftPackageStartTimeError(111310),
ErrGiftPackageEndTimeError(111311),
ErrGiftPackageRedeemCodeQuantityExceedsLimit(111312),
ErrFixedRedeemCodeAlreadyExists(111313),
ErrFixedRedeemCodeCannotBeEmpty(111314),
ErrRedeemCodeQuantityCannotBeZero(111315),
ErrFixedRedeemCodeUserUsageQuantityRequired(111316),
ErrRedeemCodeInvalid(111317),
ErrRedeemCodeFailed(111318),
ErrRedeemCodeAlreadyUsed(111319),
ErrRedeemFailed(111320),
ErrInviteNotFound(111401),
ErrUserInviteCountLimit(111402),
ErrUserFriendCountLimit(111403),
ErrAlreadyExistFriend(111404),
ErrFriendEnergyReceiveMax(110681),
ErrMallPackageNotListed(111501),
ErrMallPackageDelisted(111502),
ErrMallPackageStockLimit(111503),
ErrMallPackageOrderLimit(111504),
ErrOrderCantRemove(111505),
ErrHeartStoneMax(111601),
ErrWeaponMaxAdvance(111602),
ErrWeaponCondNoMet(111603),
ErrCharMaxLevel(111604),
ErrCharCondMaxAdvance(111605),
ErrCharMaxAdvance(111606),
ErrCharSkillMaxLevel(111607),
ErrCharSkillCondNotMet(111608),
ErrPresentsNoData(111609),
ErrPresentsUpgradeMax(111610),
ErrPresentsBeEquipped(111611),
ErrPresentsBeLocked(111612),
ErrNotAllowedRecruit(111613),
ErrAffinityMaxLevel(111614),
ErrAffinityGiftSendCountLimit(111615),
ErrDatingCountLimit(111616),
ErrDatingSendCountLimit(111617),
ErrCharWorldClassCondNotMet(111618),
ErrDatingCharLimit(111619),
ErrCharArchiveRewardReceived(111620),
ErrReceiveEnergyFriendNotData(111621),
ErrBattlePassVersionMismatch(111701),
ErrTravelerDuelSeasonEnd(111801),
ErrProductionFormulaUnactivated(111901),
ErrTalentAlreayActiveated(112001),
ErrTalentGroupNotYetOpen(112002),
ErrTalentKeyNodesInsufficientQuantity(112003),
ErrTalentOrdinaryNodesInsufficientQuantity(112004),
ErrTalentListEmpty(112005),
ErrTalentResetFrequently(112006),
ErrTalentActivationLimit(112007),
ErrTalentNotYetActivated(112008),
ErrEquipmentNoData(112101),
ErrEquipmentUpgradeMax(112102),
ErrEquipmentBeEquipped(112103),
ErrEquipmentBeLocked(112104),
ErrDiscNoData(112201),
ErrDiscLevelNotReached(112202),
ErrDiscStrengthenMax(112203),
ErrDiscPhaseMax(112204),
ErrDiscStarMax(112205),
ErrStarTowerRankSeasonNoData(112301),
ErrStarTowerRankSeasonNotYetOpen(112302),
ErrStarTowerRankSeasonEnd(112303),
ErrAgentNoData(112401),
ErrAgentInProcess(112402),
ErrAgentCond(112403),
ErrAgentCharOccupied(112404),
ErrAgentBuildOccupied(112405),
ErrAgentBuildScoreLow(112406),
ErrAgentCountLimit(112407),
ErrAgentDailyLimit(112408),
ErrAgentWeeklyLimit(112409),
ErrAgentNotCompleted(112410),
ErrVampireSurvivorBuildNum(112501),
ErrVampireSurvivorBuildCharRepetition(112502),
ErrTowerGrowthGroupActivated(112601),
ErrJointDrillNotExist(112701),
ErrJointDrillInProgress(112702),
ErrJointDrillNotYetOpen(112703),
ErrJointDrillAlreadyClosed(112704),
ErrScoreBossNotOpen(112801),
ErrNickNameResetLimit(112901),
ErrCapOverflow(119901),
ErrConfig(119902),
ErrCondCheck(119903),
ErrNotYetOpen(119904),
ErrAlreadyClosed(119905),
ErrCondNotMet(119906),
ErrInsufficientWorldClass(119907),
ErrNoRewardsToReceive(119908),
ErrAlreayExists(119909),
ErrExchangeNotSupported(119910),
ErrRequestTooFrequent(119911),
ErrAlreaydReceive(119912),
ErrLimit(119913),
ErrDataUpdated(119914);
final int value;
PlayerErrorCode(int value) {
this.value = value;
}
}

View File

@@ -24,6 +24,7 @@ public class GameSession {
private String token; private String token;
private Account account; private Account account;
private Player player; private Player player;
private String ipAddress;
// Crypto // Crypto
private int encryptMethod; // 0 = gcm, 1 = chacha20 private int encryptMethod; // 0 = gcm, 1 = chacha20
@@ -143,6 +144,10 @@ public class GameSession {
this.lastActiveTime = System.currentTimeMillis(); this.lastActiveTime = System.currentTimeMillis();
} }
public void updateIpAddress(String ip) {
this.ipAddress = ip;
}
// Packet encoding helper functions // Packet encoding helper functions
@SneakyThrows @SneakyThrows

View File

@@ -1,12 +1,15 @@
package emu.nebula.server.handlers; package emu.nebula.server.handlers;
import emu.nebula.Nebula;
import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerErrorCode;
import emu.nebula.net.GameSession;
import emu.nebula.net.HandlerId;
import emu.nebula.net.NetHandler; import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId; import emu.nebula.net.NetMsgId;
import emu.nebula.proto.PlayerLogin.LoginReq; import emu.nebula.proto.PlayerLogin.LoginReq;
import emu.nebula.proto.PlayerLogin.LoginResp; import emu.nebula.proto.PlayerLogin.LoginResp;
import emu.nebula.net.HandlerId; import emu.nebula.proto.Public.Error;
import emu.nebula.Nebula;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.player_login_req) @HandlerId(NetMsgId.player_login_req)
public class HandlerPlayerLoginReq extends NetHandler { public class HandlerPlayerLoginReq extends NetHandler {
@@ -20,20 +23,35 @@ public class HandlerPlayerLoginReq extends NetHandler {
// Parse request // Parse request
var req = LoginReq.parseFrom(message); var req = LoginReq.parseFrom(message);
// os // OS
String loginToken = req.getOfficialOverseas().getToken(); String loginToken = req.getOfficialOverseas().getToken();
if (loginToken == null || loginToken.isEmpty()) { if (loginToken.isEmpty()) {
// cn // CN
loginToken = req.getOfficial().getToken(); loginToken = req.getOfficial().getToken();
} }
var banModule = Nebula.getGameContext().getBanModule();
// Check IP ban
if (banModule.isIpBanned(session.getIpAddress())) {
var banInfo = banModule.getIpBanInfo(session.getIpAddress());
return session.encodeMsg(NetMsgId.player_login_failed_ack, banInfo.toProto());
}
// Login // Login
boolean result = session.login(loginToken); boolean result = session.login(loginToken);
if (!result) { if (!result) {
return session.encodeMsg(NetMsgId.player_login_failed_ack); Error errorCause = Error.newInstance().setCode(PlayerErrorCode.ErrLogin.getValue());
return session.encodeMsg(NetMsgId.player_login_failed_ack, errorCause);
}
// Check player ban
int playerUid = session.getPlayer().getUid();
if (banModule.isPlayerBanned(playerUid)) {
var banInfo = banModule.getPlayerBanInfo(playerUid);
return session.encodeMsg(NetMsgId.player_login_failed_ack, banInfo.toProto());
} }
// Regenerate session token because we are switching encrpytion method // Regenerate session token because we are switching encrpytion method

View File

@@ -1,5 +1,6 @@
package emu.nebula.server.handlers; package emu.nebula.server.handlers;
import emu.nebula.game.player.PlayerErrorCode;
import emu.nebula.net.NetHandler; import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId; import emu.nebula.net.NetMsgId;
import emu.nebula.proto.PlayerSignatureEdit.PlayerSignatureEditReq; import emu.nebula.proto.PlayerSignatureEdit.PlayerSignatureEditReq;
@@ -27,7 +28,9 @@ public class HandlerPlayerSignatureEdit extends NetHandler {
return session.encodeMsg( return session.encodeMsg(
NetMsgId.player_signature_edit_failed_ack, NetMsgId.player_signature_edit_failed_ack,
Error.newInstance().setCode(119902).addArguments("\nCommand Result: " + result.getMessage()) Error.newInstance()
.setCode(PlayerErrorCode.ErrConfig.getValue())
.addArguments("\nCommand Result: " + result.getMessage())
); );
} }

View File

@@ -111,6 +111,7 @@ public class AgentZoneHandler implements Handler {
// Update last active time for session // Update last active time for session
if (session != null) { if (session != null) {
session.updateLastActiveTime(); session.updateLastActiveTime();
session.updateIpAddress(ctx.ip());
} }
// Handle packet // Handle packet

View File

@@ -4,13 +4,17 @@ import java.io.File;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.YearMonth; import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.Base64; import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import emu.nebula.GameConstants;
import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntList;
public class Utils { public class Utils {
@@ -321,4 +325,16 @@ public class Utils {
var month = YearMonth.of(date.getYear(), date.getMonthValue()); var month = YearMonth.of(date.getYear(), date.getMonthValue());
return month.lengthOfMonth(); return month.lengthOfMonth();
} }
public static String formatTimestamp(long timestamp) {
return Instant.ofEpochMilli(timestamp)
.atZone(GameConstants.UTC_ZONE)
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
public static String formatTimestamp(long timestamp, String pattern) {
return Instant.ofEpochMilli(timestamp)
.atZone(GameConstants.UTC_ZONE)
.format(DateTimeFormatter.ofPattern(pattern));
}
} }