Log every refused character creation with the account and IP
All validation refusals in generatePCFromCommitNewCharacterMsg now go through refuseCreation, which warns with the reason, account and IP. The negative stat line used to be the only one that named the account. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6a4e7f0e1b
commit
2798d29d0a
1 file changed
+33
-50
@@ -730,6 +730,14 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
|
||||
}
|
||||
|
||||
// The client enforces these rules, so a refusal usually means a modified client.
|
||||
|
||||
private static PlayerCharacter refuseCreation(Account account, ClientConnection clientConnection, String reason) {
|
||||
Logger.warn("Character creation refused: " + reason + " ACCOUNT: " + account.getUname() + "(" + account.getObjectUUID()
|
||||
+ ") IP ADDRESS: " + clientConnection.getClientIpAddress());
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PlayerCharacter generatePCFromCommitNewCharacterMsg(Account a, CommitNewCharacterMsg msg, ClientConnection clientConnection) {
|
||||
|
||||
String firstName = msg.getFirstName().trim();
|
||||
@@ -768,8 +776,7 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
Race race = Race.getRace(raceID);
|
||||
|
||||
if (race == null) {
|
||||
Logger.info("Invalid RaceID: " + raceID);
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Invalid RaceID: " + raceID);
|
||||
}
|
||||
|
||||
// Verify BaseClass Object.
|
||||
@@ -777,8 +784,7 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
BaseClass baseClass = DbManager.BaseClassQueries.GET_BASE_CLASS(baseClassID);
|
||||
|
||||
if (baseClass == null) {
|
||||
Logger.info("Invalid BaseClasID: " + baseClassID);
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Invalid BaseClasID: " + baseClassID);
|
||||
}
|
||||
|
||||
// Verify Race/baseClass combo.
|
||||
@@ -793,8 +799,7 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
Logger.info("Invalid BaseClass/Race Combo");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Invalid BaseClass/Race Combo");
|
||||
}
|
||||
|
||||
// Verify HairStyle/BeardStyle/SkinColor/HairColor/BeardColor
|
||||
@@ -805,23 +810,19 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
int beardColorID = msg.getBeardColor();
|
||||
|
||||
if (!race.isValidHairStyle(hairStyleID)) {
|
||||
Logger.info("Invalid HairStyleID: " + hairStyleID + " for race: " + race.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Invalid HairStyleID: " + hairStyleID + " for race: " + race.getName());
|
||||
}
|
||||
|
||||
if (!race.isValidSkinColor(skinColorID)) {
|
||||
Logger.info("Invalid skinColorID: " + skinColorID + " for race: " + race.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Invalid skinColorID: " + skinColorID + " for race: " + race.getName());
|
||||
}
|
||||
|
||||
if (!race.isValidHairColor(hairColorID)) {
|
||||
Logger.info("Invalid hairColorID: " + hairColorID + " for race: " + race.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Invalid hairColorID: " + hairColorID + " for race: " + race.getName());
|
||||
}
|
||||
|
||||
if (!race.isValidBeardColor(beardColorID)) {
|
||||
Logger.info("Invalid beardColorID: " + beardColorID + " for race: " + race.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Invalid beardColorID: " + beardColorID + " for race: " + race.getName());
|
||||
}
|
||||
|
||||
// Get stat modifiers
|
||||
@@ -833,8 +834,7 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
|
||||
|
||||
if (intMod < -5 || dexMod < -5 || conMod < -5 || strMod < -5 || spiMod < -5) {
|
||||
Logger.error("NEGATIVE STAT CHEAT ATTEMPTED! ACCOUNT: " + a.getUname() + "(" + a.getObjectUUID() + ") IP ADDRESS: " + clientConnection.getClientIpAddress());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Negative stat mod");
|
||||
}
|
||||
|
||||
// calculate current stats:
|
||||
@@ -855,8 +855,7 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
int numRunes = msg.getNumRunes();
|
||||
|
||||
if (numRunes > 10) {
|
||||
Logger.info("Too many Runes applied");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Too many Runes applied");
|
||||
}
|
||||
|
||||
// Get Runes
|
||||
@@ -907,39 +906,33 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
|
||||
// Null check
|
||||
if (runeBase == null) {
|
||||
Logger.info("GOM returned NULL RuneBase");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "GOM returned NULL RuneBase");
|
||||
}
|
||||
|
||||
// Validate Rune against Race
|
||||
if (!race.isAllowedRune(runeBase)) {
|
||||
Logger.info("Trait Not valid for Race");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Trait Not valid for Race");
|
||||
}
|
||||
|
||||
// Validate BaseClass against Race
|
||||
if (!baseClass.isAllowedRune(runeBase)) {
|
||||
Logger.info("Trait Not valid for BaseClass");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Trait Not valid for BaseClass");
|
||||
}
|
||||
|
||||
if (!usedRuneIDs.add(runeBase.getObjectUUID())) {
|
||||
Logger.info("Duplicate RuneBase");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Duplicate RuneBase");
|
||||
}
|
||||
|
||||
// Creation talents are rank 0, so two of the same sub type block each other (client 0x4695D0)
|
||||
if (runeBase.getSubTypeToken() != 0 && !usedSubTypeTokens.add(runeBase.getSubTypeToken())) {
|
||||
Logger.info("Duplicate RuneBase Subtype");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Duplicate RuneBase Subtype");
|
||||
}
|
||||
|
||||
usedRunes.add(runeBase);
|
||||
|
||||
// Talent cap of 8 (client 0x4695D0)
|
||||
if (usedRunes.size() > 8) {
|
||||
Logger.info("Too many talents applied");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Too many talents applied");
|
||||
}
|
||||
|
||||
int maxValue = 0;
|
||||
@@ -984,43 +977,37 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
remainingPoints -= value;
|
||||
continue;
|
||||
}
|
||||
Logger.info("Not enough points left");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Not enough points left");
|
||||
case MBServerStatics.RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID:
|
||||
|
||||
if (strCur >= value)
|
||||
continue;
|
||||
|
||||
Logger.info("STR fails to meet Rune Minimum --> " + rb.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "STR fails to meet Rune Minimum --> " + rb.getName());
|
||||
case MBServerStatics.RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID:
|
||||
|
||||
if (dexCur >= value)
|
||||
continue;
|
||||
|
||||
Logger.info("DEX fails to meet Rune Minimum --> " + rb.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "DEX fails to meet Rune Minimum --> " + rb.getName());
|
||||
case MBServerStatics.RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID:
|
||||
|
||||
if (conCur >= value)
|
||||
continue;
|
||||
|
||||
Logger.info("CON fails to meet Rune Minimum --> " + rb.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "CON fails to meet Rune Minimum --> " + rb.getName());
|
||||
case MBServerStatics.RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID:
|
||||
|
||||
if (intCur >= value)
|
||||
continue;
|
||||
|
||||
Logger.info("INT fails to meet Rune Minimum --> " + rb.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "INT fails to meet Rune Minimum --> " + rb.getName());
|
||||
case MBServerStatics.RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID:
|
||||
|
||||
if (spiCur >= value)
|
||||
continue;
|
||||
|
||||
Logger.info("SPI fails to meet Rune Minimum --> " + rb.getName());
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "SPI fails to meet Rune Minimum --> " + rb.getName());
|
||||
case MBServerStatics.RUNE_STR_ATTRIBUTE_ID:
|
||||
|
||||
if (value < 0)
|
||||
@@ -1085,21 +1072,18 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
continue;
|
||||
|
||||
default:
|
||||
Logger.info("Unknown ATTRIBUTE_ID while checking RuneBaseAttributes: " + attrID);
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Unknown ATTRIBUTE_ID while checking RuneBaseAttributes: " + attrID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checked before the penalties, as the client's +/- does (0xBAC140): Giant's Blood cuts a max below its stat
|
||||
if (strCur < 1 || dexCur < 1 || conCur < 1 || intCur < 1 || spiCur < 1) {
|
||||
Logger.info("Attribute below 1");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Attribute below 1");
|
||||
}
|
||||
|
||||
if (strCur > strMax || dexCur > dexMax || conCur > conMax || intCur > intMax || spiCur > spiMax) {
|
||||
Logger.info("Attribute above racial maximum");
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Attribute above racial maximum");
|
||||
}
|
||||
|
||||
// Add in all of the penalties.
|
||||
@@ -1130,8 +1114,7 @@ public class PlayerCharacter extends AbstractCharacter {
|
||||
}
|
||||
|
||||
if (kit == null) {
|
||||
Logger.info("Unable to find matching kitID: " + kitID);
|
||||
return null;
|
||||
return refuseCreation(a, clientConnection, "Unable to find matching kitID: " + kitID);
|
||||
}
|
||||
|
||||
byte runningTrains = 0;
|
||||
|
||||
Reference in new issue
Block a user