mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-10 10:20:41 -07:00
Introduced modular components for authentication, including the storage backend
Servers in dev mode now have a no-auth equivalent that allows any user/pass combo without registration Added a ban command Added a mute command Hooked up the mute functionality of the report screen (for pmods+) Cleaned up all the now-unused classes for player SQL stuff Player SQL stuff now uses entirely prepared statements No longer storing PC name, MAC address, serial number as these are inauthentic components of the protocol Packet to be corrected in the future to allow closer compatibility with authentic clients Used less threading for the SQL queries/updates as these were causing issues both with the old system and the new Updated ::resetpassword and ::setpasswordother commands to use the new server authentication pipeline (to ensure things are always correctly set) Refactored the login read event, now handles more exceptions and edge cases
This commit is contained in:
parent
dd1eed7e39
commit
9ab9885eef
41 changed files with 1264 additions and 1424 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package core.game.content.global.report;
|
||||
|
||||
import core.game.node.entity.player.Player;
|
||||
import rs09.game.system.command.CommandMapping;
|
||||
|
||||
/**
|
||||
* Represents an abuse report to file.
|
||||
|
|
@ -45,6 +46,9 @@ public final class AbuseReport {
|
|||
* @param player the player.
|
||||
*/
|
||||
public void construct(Player player, boolean mute) {
|
||||
if (mute) {
|
||||
CommandMapping.INSTANCE.get("mute").attemptHandling(player, new String[] {"mute", victim, "48h"});
|
||||
}
|
||||
player.getPacketDispatch().sendMessage("Thank-you, your abuse report has been received.");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -385,6 +385,7 @@ public class Player extends Entity {
|
|||
CommunicationInfo.notifyPlayers(this, false, false);
|
||||
HouseManager.leave(this);
|
||||
UpdateSequence.getRenderablePlayers().remove(this);
|
||||
details.save();
|
||||
Repository.getDisconnectionQueue().add(this);
|
||||
}
|
||||
|
||||
|
|
@ -867,7 +868,6 @@ public class Player extends Entity {
|
|||
if (this.details != null) {
|
||||
details.setBanTime(this.details.getBanTime());
|
||||
details.setMuteTime(this.details.getMuteTime());
|
||||
details.setIcon(this.details.getIcon());
|
||||
}
|
||||
details.getSession().setObject(this);
|
||||
this.details = details;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package core.game.node.entity.player.info;
|
||||
|
||||
import core.game.node.entity.player.info.portal.Icon;
|
||||
import core.game.node.entity.player.info.portal.PlayerSQLManager;
|
||||
import core.game.system.communication.CommunicationInfo;
|
||||
import core.net.IoSession;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import rs09.auth.UserAccountInfo;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
|
@ -13,75 +15,27 @@ import java.util.concurrent.TimeUnit;
|
|||
*
|
||||
*/
|
||||
public class PlayerDetails {
|
||||
|
||||
/**
|
||||
* The sql manager for this account.
|
||||
*/
|
||||
private final PlayerSQLManager sqlManager = new PlayerSQLManager(this);
|
||||
public UserAccountInfo accountInfo = UserAccountInfo.createDefault();
|
||||
|
||||
/**
|
||||
* The communication info.
|
||||
*/
|
||||
private final CommunicationInfo communicationInfo = new CommunicationInfo();
|
||||
|
||||
public int getCredits() {
|
||||
return accountInfo.getCredits();
|
||||
}
|
||||
|
||||
public void setCredits(int amount) {
|
||||
accountInfo.setCredits(amount);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The unique id info.
|
||||
*/
|
||||
private final UIDInfo info = new UIDInfo();
|
||||
|
||||
/**
|
||||
* The username of the account.
|
||||
*/
|
||||
private final String username;
|
||||
|
||||
/**
|
||||
* The password of the account.
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* The unique id of the account.
|
||||
*/
|
||||
private int uid;
|
||||
|
||||
/**
|
||||
* The account's last game login.
|
||||
*/
|
||||
private long lastLogin = -1;
|
||||
|
||||
/**
|
||||
* The account's last game login.
|
||||
*/
|
||||
private long timePlayed = 0;
|
||||
|
||||
/**
|
||||
* The time the player is muted for.
|
||||
*/
|
||||
private long muteTime;
|
||||
|
||||
/**
|
||||
* The time the player is banned for.
|
||||
*/
|
||||
private long banTime;
|
||||
|
||||
/**
|
||||
* If the sql data has been parsed.
|
||||
*/
|
||||
private boolean parsed;
|
||||
|
||||
/**
|
||||
* The rights of the player.
|
||||
*/
|
||||
private Rights rights = Rights.REGULAR_PLAYER;
|
||||
|
||||
/**
|
||||
* The chat icon value.
|
||||
*/
|
||||
private Icon icon;
|
||||
|
||||
public int credits;
|
||||
|
||||
/**
|
||||
* Represents the session.
|
||||
*/
|
||||
|
|
@ -89,62 +43,10 @@ public class PlayerDetails {
|
|||
|
||||
/**
|
||||
* Constructs a new {@code PlayerDetails}.
|
||||
* @param username the username to set.
|
||||
* @param password the password to set.
|
||||
*/
|
||||
public PlayerDetails(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.uid = username.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@Code PlayerDetails} {@Code Object}
|
||||
* @param username the username to set.
|
||||
*/
|
||||
public PlayerDetails(String username) {
|
||||
this(username, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the details from the database for this object.
|
||||
* @return {@code True} if parsed.
|
||||
*/
|
||||
public boolean parse() {
|
||||
if (sqlManager.parse()) {
|
||||
return parsed = true;
|
||||
}
|
||||
return parsed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the player's details.
|
||||
* @return {@code True} if saved.
|
||||
*/
|
||||
public boolean save() {
|
||||
sqlManager.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player details for an account name.
|
||||
* @param username the username.
|
||||
* @return the player details.
|
||||
*/
|
||||
public static PlayerDetails getDetails(String username) {
|
||||
PlayerDetails details = new PlayerDetails(username);
|
||||
if (!details.parse()) {
|
||||
return details = null;
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the ban is permanent.
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean isPermBan() {
|
||||
return TimeUnit.MILLISECONDS.toDays(banTime - System.currentTimeMillis()) > 1000;
|
||||
accountInfo.setUsername(username);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -152,7 +54,7 @@ public class PlayerDetails {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean isBanned() {
|
||||
return banTime > System.currentTimeMillis();
|
||||
return accountInfo.getBanEndTime() > System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,7 +62,7 @@ public class PlayerDetails {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean isPermMute() {
|
||||
return TimeUnit.MILLISECONDS.toDays(muteTime - System.currentTimeMillis()) > 1000;
|
||||
return TimeUnit.MILLISECONDS.toDays(accountInfo.getMuteEndTime() - System.currentTimeMillis()) > 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -168,15 +70,7 @@ public class PlayerDetails {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean isMuted() {
|
||||
return muteTime > System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sql manager.
|
||||
* @return the sql manager.
|
||||
*/
|
||||
public PlayerSQLManager getSqlManager() {
|
||||
return sqlManager;
|
||||
return accountInfo.getMuteEndTime() > System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -184,7 +78,7 @@ public class PlayerDetails {
|
|||
* @return The rights.
|
||||
*/
|
||||
public Rights getRights() {
|
||||
return rights;
|
||||
return Rights.values()[accountInfo.getRights()];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -192,7 +86,7 @@ public class PlayerDetails {
|
|||
* @param rights The credentials to set.
|
||||
*/
|
||||
public void setRights(Rights rights) {
|
||||
this.rights = rights;
|
||||
this.accountInfo.setRights(rights.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -216,7 +110,7 @@ public class PlayerDetails {
|
|||
* @param password the password.
|
||||
*/
|
||||
public void setPassword(final String password) {
|
||||
this.password = password;
|
||||
this.accountInfo.setPassword(password);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -225,7 +119,7 @@ public class PlayerDetails {
|
|||
*/
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
return this.accountInfo.getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -233,7 +127,7 @@ public class PlayerDetails {
|
|||
* @return the uid.
|
||||
*/
|
||||
public int getUid() {
|
||||
return uid;
|
||||
return this.accountInfo.getUid();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -241,7 +135,7 @@ public class PlayerDetails {
|
|||
* @param uid the uid.
|
||||
*/
|
||||
public void setUid(int uid) {
|
||||
this.uid = uid;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -249,15 +143,7 @@ public class PlayerDetails {
|
|||
* @return The password.
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the details have been parsed.
|
||||
* @return {@code True} if parsed.
|
||||
*/
|
||||
public boolean isParsed() {
|
||||
return parsed;
|
||||
return this.accountInfo.getPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -311,28 +197,12 @@ public class PlayerDetails {
|
|||
return communicationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the icon.
|
||||
* @return the icon.
|
||||
*/
|
||||
public Icon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon.
|
||||
* @param icon the icon to set
|
||||
*/
|
||||
public void setIcon(Icon icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the lastLogin.
|
||||
* @return the lastLogin.
|
||||
*/
|
||||
public long getLastLogin() {
|
||||
return lastLogin;
|
||||
return this.accountInfo.getLastLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -340,7 +210,7 @@ public class PlayerDetails {
|
|||
* @param lastLogin the lastLogin to set
|
||||
*/
|
||||
public void setLastLogin(long lastLogin) {
|
||||
this.lastLogin = lastLogin;
|
||||
this.accountInfo.setLastLogin(lastLogin);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -348,7 +218,7 @@ public class PlayerDetails {
|
|||
* @return the timePlayed.
|
||||
*/
|
||||
public long getTimePlayed() {
|
||||
return timePlayed;
|
||||
return this.accountInfo.getTimePlayed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -356,7 +226,7 @@ public class PlayerDetails {
|
|||
* @param timePlayed the timePlayed to set
|
||||
*/
|
||||
public void setTimePlayed(long timePlayed) {
|
||||
this.timePlayed = timePlayed;
|
||||
this.accountInfo.setTimePlayed(timePlayed);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -364,7 +234,7 @@ public class PlayerDetails {
|
|||
* @param muteTime the mute time.
|
||||
*/
|
||||
public void setMuteTime(long muteTime) {
|
||||
this.muteTime = muteTime;
|
||||
this.accountInfo.setMuteEndTime(muteTime);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -372,7 +242,7 @@ public class PlayerDetails {
|
|||
* @return The mute time.
|
||||
*/
|
||||
public long getMuteTime() {
|
||||
return muteTime;
|
||||
return this.accountInfo.getMuteEndTime();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -380,7 +250,7 @@ public class PlayerDetails {
|
|||
* @return the banTime.
|
||||
*/
|
||||
public long getBanTime() {
|
||||
return banTime;
|
||||
return this.accountInfo.getBanEndTime();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -388,12 +258,17 @@ public class PlayerDetails {
|
|||
* @param banTime the banTime to set
|
||||
*/
|
||||
public void setBanTime(long banTime) {
|
||||
this.banTime = banTime;
|
||||
this.accountInfo.setBanEndTime(banTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlayerDetails [sqlManager=" + sqlManager + ", communicationInfo=" + communicationInfo + ", info=" + info + ", username=" + username + ", password=" + password + ", uid=" + uid + ", lastLogin=" + lastLogin + ", muteTime=" + muteTime + ", parsed=" + parsed + ", rights=" + rights + ", icon=" + icon + ", session=" + session + "]";
|
||||
public void save() {
|
||||
if(isBanned()) return;
|
||||
try {
|
||||
GameWorld.getAccountStorage().update(accountInfo);
|
||||
} catch (IllegalStateException ignored) {}
|
||||
}
|
||||
|
||||
public static PlayerDetails getDetails(@NotNull String username) {
|
||||
return new PlayerDetails(username);
|
||||
}
|
||||
}
|
||||
|
|
@ -106,7 +106,7 @@ public final class LoginConfiguration {
|
|||
player.getPacketDispatch().sendString("Discord Invite", 378, 14);
|
||||
player.getPacketDispatch().sendString("Discord Invite", 378, 129);
|
||||
player.getPacketDispatch().sendString("Credits", 378, 94);
|
||||
player.getPacketDispatch().sendString(player.getDetails().credits + "", 378, 96);
|
||||
player.getPacketDispatch().sendString(player.getDetails().getCredits() + "", 378, 96);
|
||||
player.getPacketDispatch().sendString(" ", 378, 229);
|
||||
player.getPacketDispatch().sendString("Want to contribute to 2009scape? <br>Visit the github using the link below!", 378, 230);
|
||||
player.getPacketDispatch().sendString(" ", 378, 231);
|
||||
|
|
@ -266,10 +266,11 @@ public final class LoginConfiguration {
|
|||
* @return the last login.
|
||||
*/
|
||||
public static String getLastLogin(Player player) {
|
||||
String lastIp = (String) player.getDetails().getSqlManager().getTable().getColumn("lastGameIp").getValue();
|
||||
if (lastIp == null || lastIp == "") {
|
||||
String lastIp = player.getDetails().accountInfo.getLastUsedIp();
|
||||
if (lastIp.equals("")) {
|
||||
lastIp = player.getDetails().getIpAddress();
|
||||
}
|
||||
player.getDetails().accountInfo.setLastUsedIp(player.getDetails().getIpAddress());
|
||||
String string = "You last logged in @timeAgo from: " + lastIp;
|
||||
long time = player.getDetails().getLastLogin();
|
||||
Date lastLogin = new Date(time);
|
||||
|
|
|
|||
|
|
@ -1,236 +0,0 @@
|
|||
package core.game.node.entity.player.info.portal;
|
||||
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.info.PlayerDetails;
|
||||
import core.game.node.entity.player.info.Rights;
|
||||
import core.game.node.entity.player.info.login.Response;
|
||||
import core.game.system.SystemManager;
|
||||
import core.game.system.mysql.SQLColumn;
|
||||
import core.game.system.mysql.SQLEntryHandler;
|
||||
import core.game.system.mysql.SQLManager;
|
||||
import core.game.system.mysql.SQLTable;
|
||||
import core.game.system.mysql.impl.PlayerSQLHandler;
|
||||
import core.net.amsc.WorldCommunicator;
|
||||
import rs09.game.ge.GrandExchangeRecords;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Manages the SQL aspect for an account.
|
||||
* @author Vexia
|
||||
*
|
||||
*/
|
||||
public final class PlayerSQLManager {
|
||||
|
||||
/**
|
||||
* The representation of an account sql table.
|
||||
*/
|
||||
private final SQLTable table = new SQLTable(
|
||||
new SQLColumn("UID", Integer.class),
|
||||
new SQLColumn("rights", Integer.class),
|
||||
new SQLColumn("donatorType", Integer.class),
|
||||
new SQLColumn("credits", Integer.class),
|
||||
new SQLColumn("icon", Integer.class),
|
||||
new SQLColumn("perks", String.class),
|
||||
new SQLColumn("ip", String.class, false),
|
||||
new SQLColumn("mac", String.class, false),
|
||||
new SQLColumn("serial", String.class, false),
|
||||
new SQLColumn("computerName", String.class, false),
|
||||
new SQLColumn("netWorth", BigInteger.class, false),
|
||||
new SQLColumn("ironManMode", String.class, false),
|
||||
new SQLColumn("bank", String.class, false),
|
||||
new SQLColumn("inventory", String.class, false),
|
||||
new SQLColumn("equipment", String.class, false),
|
||||
new SQLColumn("ge", String.class, false),
|
||||
new SQLColumn("muteTime", Long.class),
|
||||
new SQLColumn("banTime", Long.class),
|
||||
new SQLColumn("contacts", String.class),
|
||||
new SQLColumn("blocked", String.class),
|
||||
new SQLColumn("clanName", String.class),
|
||||
new SQLColumn("currentClan", String.class),
|
||||
new SQLColumn("clanReqs", String.class),
|
||||
new SQLColumn("timePlayed", Long.class),
|
||||
new SQLColumn("lastLogin", Long.class),
|
||||
new SQLColumn("online", Integer.class),
|
||||
new SQLColumn("lastGameIp", String.class));
|
||||
|
||||
/**
|
||||
* The details the SQL manager is managing.
|
||||
*/
|
||||
private final PlayerDetails details;
|
||||
|
||||
/**
|
||||
* Constructs a new {@Code PlayerSQLManager} {@Code Object}
|
||||
* @param details the details.
|
||||
*/
|
||||
public PlayerSQLManager(PlayerDetails details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the SQL table.
|
||||
* @return {@code True} if parsed.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean parse() {
|
||||
if(!SQLEntryHandler.read(new PlayerSQLHandler(details))) {
|
||||
return false;
|
||||
}
|
||||
details.getCommunication().parse(table);
|
||||
details.credits = (int) table.getColumn("credits").getValue();
|
||||
details.setBanTime((long) table.getColumn("banTime").getValue());
|
||||
details.setMuteTime((long) table.getColumn("muteTime").getValue());
|
||||
details.setIcon(Icon.forId((int) table.getColumn("icon").getValue()));
|
||||
details.setRights(Rights.forId((int) table.getColumn("rights").getValue()));
|
||||
details.setLastLogin(System.currentTimeMillis());
|
||||
details.setTimePlayed((long) table.getColumn("timePlayed").getValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the changed SQL columns to the database.
|
||||
*/
|
||||
public void save() {
|
||||
SQLEntryHandler.write(new PlayerSQLHandler(details));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates column values with the player instance.
|
||||
* @param player The player instance.
|
||||
*/
|
||||
public void update(Player player) {
|
||||
if (!WorldCommunicator.isEnabled()) {
|
||||
details.getCommunication().save(table);
|
||||
}
|
||||
table.getColumn("credits").updateValue(player.getDetails().credits);
|
||||
table.getColumn("bank").updateValue(player.getBank().format());
|
||||
table.getColumn("lastLogin").updateValue(player.getDetails().getLastLogin());
|
||||
table.getColumn("ge").updateValue(GrandExchangeRecords.getInstance(player).format());
|
||||
table.getColumn("inventory").updateValue(player.getInventory().format());
|
||||
table.getColumn("equipment").updateValue(player.getEquipment().format());
|
||||
table.getColumn("netWorth").updateValue(player.getMonitor().getNetworth());
|
||||
table.getColumn("lastGameIp").updateValue(player.getDetails().getIpAddress());
|
||||
table.getColumn("ironManMode").updateValue(player.getIronmanManager().getMode().name());
|
||||
table.getColumn("timePlayed").updateValue(player.getDetails().getTimePlayed() + (System.currentTimeMillis() - player.getDetails().getLastLogin()));
|
||||
table.getColumn("ip").updateValue(getAddressLog((String) table.getColumn("ip").getValue(), details.getInfo().getIp()));
|
||||
table.getColumn("mac").updateValue(getAddressLog((String) table.getColumn("mac").getValue(), details.getInfo().getMac()));
|
||||
table.getColumn("serial").updateValue(getAddressLog((String) table.getColumn("serial").getValue(), details.getInfo().getSerial()));
|
||||
table.getColumn("computerName").updateValue(getAddressLog((String) table.getColumn("computerName").getValue(), details.getInfo().getCompName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an account was created under a name.
|
||||
* @param name the name.
|
||||
* @param field the field
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public static boolean hasSqlAccount(String name, String field) throws SQLException {
|
||||
Connection connection = SQLManager.getConnection();
|
||||
if (connection == null) {
|
||||
return true;
|
||||
}
|
||||
ResultSet result = null;
|
||||
PreparedStatement statement;
|
||||
statement = connection.prepareStatement("SELECT * FROM " + "members" + " WHERE " + "" + field + "='" + name.toLowerCase() + "' LIMIT 1");
|
||||
result = statement.executeQuery();
|
||||
if (result == null || !result.next()) {
|
||||
SQLManager.close(connection);
|
||||
return false;
|
||||
}
|
||||
SQLManager.close(connection);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Response updatePassword(String name, String pass) throws SQLException{
|
||||
if (!SQLManager.isInitialized()) {
|
||||
return Response.INVALID_CREDENTIALS;
|
||||
}
|
||||
Connection connection = SQLManager.getConnection();
|
||||
if (connection == null) {
|
||||
return Response.INVALID_LOGIN_SERVER;
|
||||
}
|
||||
PreparedStatement statement;
|
||||
statement = connection.prepareStatement("UPDATE " + "members" + " SET password='" + pass + "' WHERE " + "username" + "='" + name.toLowerCase() + "' LIMIT 1");
|
||||
boolean Success = statement.execute();
|
||||
if(Success) return Response.SUCCESSFUL;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a username & password are correct.
|
||||
* @param name the name.
|
||||
* @param pass the pass.
|
||||
* @return the response.
|
||||
* @throws SQLException the exception if thrown.
|
||||
*/
|
||||
public static Response getCredentialResponse(String name, String pass) throws SQLException {
|
||||
if (!SQLManager.isInitialized()) {
|
||||
return Response.INVALID_CREDENTIALS;
|
||||
}
|
||||
Connection connection = SQLManager.getConnection();
|
||||
if (connection == null) {
|
||||
return Response.INVALID_LOGIN_SERVER;
|
||||
}
|
||||
ResultSet result = null;
|
||||
PreparedStatement statement;
|
||||
statement = connection.prepareStatement("SELECT * FROM " + "members" + " WHERE " + "" + "username" + "='" + name.toLowerCase() + "' LIMIT 1");
|
||||
result = statement.executeQuery();
|
||||
if (result == null || !result.next()) {
|
||||
SQLManager.close(connection);
|
||||
return Response.INVALID_CREDENTIALS;
|
||||
}
|
||||
String realPass = result.getString("password");
|
||||
if (SystemManager.getEncryption().checkPassword(pass, realPass)) {
|
||||
SQLManager.close(connection);
|
||||
return Response.SUCCESSFUL;
|
||||
}
|
||||
SQLManager.close(connection);
|
||||
return Response.INVALID_CREDENTIALS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address log.
|
||||
* @param original the original log.
|
||||
* @param address the address.
|
||||
* @return the address.
|
||||
*/
|
||||
private String getAddressLog(String original, String address) {
|
||||
String log = "";
|
||||
if (original != null && original.length() > 0) {
|
||||
log += original;
|
||||
if (log.charAt(log.length() - 1) != '|') {
|
||||
log += "|";
|
||||
}
|
||||
}
|
||||
if (address != null && address.length() > 0 && (original == null || !original.contains(address))) {
|
||||
log += address + "|";
|
||||
}
|
||||
if (log.length() > 0 && log.charAt(log.length() - 1) == '|') {
|
||||
log = log.substring(0, log.length() - 1);
|
||||
}
|
||||
if (log == null) {
|
||||
log = "";
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the details.
|
||||
* @return the details.
|
||||
*/
|
||||
public PlayerDetails getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the table.
|
||||
* @return the table.
|
||||
*/
|
||||
public SQLTable getTable() {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
package core.game.system.mysql;
|
||||
|
||||
import rs09.game.system.SystemLogger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Handles an SQL entry.
|
||||
* @author Emperor
|
||||
* @param <T> The entry type.
|
||||
*/
|
||||
public abstract class SQLEntryHandler<T> {
|
||||
|
||||
/**
|
||||
* The connection.
|
||||
*/
|
||||
protected Connection connection;
|
||||
|
||||
/**
|
||||
* The entry.
|
||||
*/
|
||||
protected T entry;
|
||||
|
||||
/**
|
||||
* The result set.
|
||||
*/
|
||||
protected ResultSet result;
|
||||
|
||||
/**
|
||||
* The table name.
|
||||
*/
|
||||
protected String table;
|
||||
|
||||
/**
|
||||
* The column name.
|
||||
*/
|
||||
private String column;
|
||||
|
||||
/**
|
||||
* The value to use.
|
||||
*/
|
||||
protected String value;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code SQLEntryHandler} {@code Object}.
|
||||
* @param entry The entry.
|
||||
* @param table The table name.
|
||||
* @param column column name.
|
||||
* @param value The column value.
|
||||
*/
|
||||
public SQLEntryHandler(T entry, String table, String column, String value) {
|
||||
this.entry = entry;
|
||||
this.table = table;
|
||||
this.column = column;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the SQL entry.
|
||||
* @param entry The entry.
|
||||
*/
|
||||
public static boolean read(SQLEntryHandler<?> entry) {
|
||||
if (!SQLManager.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
entry.connection = entry.getConnection();
|
||||
if (entry.connection == null) {
|
||||
SystemLogger.logErr("Could not read SQL data: connection is null!");
|
||||
return false;
|
||||
}
|
||||
boolean success = false;
|
||||
try {
|
||||
entry.read();
|
||||
if (entry.result == null || !entry.result.next()) {
|
||||
entry.create();
|
||||
} else {
|
||||
entry.parse();
|
||||
}
|
||||
success = true;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
SQLManager.close(entry.connection);
|
||||
entry.connection = null;
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the entry data on the SQL database.
|
||||
* @param entry The entry.
|
||||
*/
|
||||
public static void write(SQLEntryHandler<?> entry) {
|
||||
write(entry, entry.getConnection(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the SQL entry.
|
||||
* @param entry The entry.
|
||||
*/
|
||||
public static void write(SQLEntryHandler<?> entry, Connection connection, boolean commit) {
|
||||
if (connection == null) {
|
||||
SystemLogger.logErr("Could not write SQL data: connection is null!");
|
||||
return;
|
||||
}
|
||||
entry.connection = connection;
|
||||
try {
|
||||
if (!commit) {
|
||||
entry.connection.setAutoCommit(false);
|
||||
}
|
||||
entry.save();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (commit) {
|
||||
SQLManager.close(entry.connection);
|
||||
}
|
||||
entry.connection = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the table from the SQL database.
|
||||
* @throws SQLException When an SQL exception occurs.
|
||||
*/
|
||||
public void read() throws SQLException {
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT " + getReadSelection() + " FROM " + table + " WHERE " + column + " = ?");
|
||||
statement.setString(1, value);
|
||||
result = statement.executeQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result.
|
||||
* @return the result.
|
||||
*/
|
||||
public ResultSet getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selection.
|
||||
* @return The selection.
|
||||
*/
|
||||
public String getReadSelection() {
|
||||
return "*";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the writing statement.
|
||||
* @param create If we are creating a new row.
|
||||
* @param columns The columns to update.
|
||||
*/
|
||||
public PreparedStatement getWritingStatement(boolean create, String... columns) {
|
||||
PreparedStatement statement = null;
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (create) {
|
||||
sb.append("INSERT INTO ").append(table).append(" (").append(column);
|
||||
for (String name : columns) {
|
||||
sb.append(",").append(name);
|
||||
}
|
||||
sb.append(") VALUES (?");
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
sb.append(",?");
|
||||
}
|
||||
sb.append(")");
|
||||
} else {
|
||||
sb.append("UPDATE ").append(table).append(" SET ");
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
sb.append(columns[i]).append("=?");
|
||||
if (i < columns.length - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
sb.append(" WHERE ").append(column).append("=?");
|
||||
}
|
||||
statement = connection.prepareStatement(sb.toString());
|
||||
statement.setString(create ? 1 : columns.length + 1, value);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the entry.
|
||||
* @throws SQLException When an SQL exception occurs.
|
||||
*/
|
||||
public abstract void parse() throws SQLException;
|
||||
|
||||
/**
|
||||
* Creates a new row in the database.
|
||||
* @throws SQLException When an SQL exception occurs.
|
||||
*/
|
||||
public abstract void create() throws SQLException;
|
||||
|
||||
/**
|
||||
* Saves the entry.
|
||||
* @throws SQLException When an SQL exception occurs.
|
||||
*/
|
||||
public abstract void save() throws SQLException;
|
||||
|
||||
/**
|
||||
* Gets the connection.
|
||||
* @return The connection.
|
||||
*/
|
||||
public abstract Connection getConnection();
|
||||
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
package core.game.system.mysql.impl;
|
||||
|
||||
import rs09.ServerConstants;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.info.Rights;
|
||||
import core.game.system.mysql.SQLEntryHandler;
|
||||
import core.game.system.mysql.SQLManager;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Handles the sql handler.
|
||||
* @author Vexia
|
||||
*/
|
||||
public final class HighscoreSQLHandler extends SQLEntryHandler<Player> {
|
||||
|
||||
/**
|
||||
* Constructs a new {@code HighscoreSQLHandler} {@code Object}.
|
||||
*/
|
||||
public HighscoreSQLHandler(Player entry) {
|
||||
super(entry, (SQLManager.LOCAL ? "global" : ServerConstants.DATABASE_NAME) + ".highscores", "username", entry.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() throws SQLException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() throws SQLException {
|
||||
if (entry.getDetails().getRights() == Rights.ADMINISTRATOR || GameWorld.getSettings().isDevMode()) {
|
||||
return;
|
||||
}
|
||||
StringBuilder b = new StringBuilder("INSERT highscores(username,overall_xp,total_level,ironManMode,xp_0,xp_1,xp_2,xp_3,xp_4,xp_5,xp_6,xp_7,xp_8,xp_9,xp_10,xp_11,xp_12,xp_13,xp_14,xp_15,xp_16,xp_17,xp_18,xp_19,xp_20,xp_21,xp_22,xp_23) ");
|
||||
b.append("VALUES('" + value + "', '" + entry.getSkills().getTotalXp() + "', '" + entry.getSkills().getTotalLevel() + "', '" + entry.getIronmanManager().getMode().name() + "', ");
|
||||
int xp;
|
||||
for (int i = 0; i < Skills.SKILL_NAME.length; i++) {
|
||||
xp = (int) entry.getSkills().getExperience(i);
|
||||
b.append("'" + xp + "'" + (i == Skills.SKILL_NAME.length - 1 ? "" : ","));
|
||||
}
|
||||
b.append(")");
|
||||
PreparedStatement statement = connection.prepareStatement(b.toString());
|
||||
statement.executeUpdate();
|
||||
SQLManager.close(statement.getConnection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws SQLException {
|
||||
if (entry.getDetails().getRights() == Rights.ADMINISTRATOR || GameWorld.getSettings().isDevMode()) {
|
||||
return;
|
||||
}
|
||||
super.read();
|
||||
if (result == null || !result.next()) {
|
||||
create();
|
||||
return;
|
||||
}
|
||||
if (entry.getSavedData().getActivityData().getHardcoreDeath() == true){
|
||||
//Update the SQL table to indicate the player was a Hardcore ironman that died, do not update hiscores
|
||||
StringBuilder b = new StringBuilder("UPDATE highscores SET ironManMode='HARDCORE_DEAD' WHERE username ='" + value + "'");
|
||||
PreparedStatement statement = connection.prepareStatement(b.toString());
|
||||
statement.executeUpdate();
|
||||
SQLManager.close(statement.getConnection());
|
||||
return;
|
||||
}
|
||||
StringBuilder b = new StringBuilder("UPDATE highscores SET overall_xp='" + entry.getSkills().getTotalXp() + "', total_level='" + entry.getSkills().getTotalLevel() + "', ironManMode='" + entry.getIronmanManager().getMode().name() + "', ");
|
||||
int xp;
|
||||
for (int i = 0; i < Skills.SKILL_NAME.length; i++) {
|
||||
xp = (int) entry.getSkills().getExperience(i);
|
||||
b.append("xp_" + i + "='" + xp + "'" + (i == Skills.SKILL_NAME.length - 1 ? "" : ","));
|
||||
}
|
||||
b.append("WHERE username='" + value + "'");
|
||||
PreparedStatement statement = connection.prepareStatement(b.toString());
|
||||
statement.executeUpdate();
|
||||
SQLManager.close(statement.getConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data.
|
||||
* @param statement the statement.
|
||||
* @param startIndex the start index.
|
||||
* @throws SQLException the exception.
|
||||
*/
|
||||
public void setData(PreparedStatement statement, int startIndex) throws SQLException {
|
||||
for (int i = 0; i < Skills.SKILL_NAME.length; i++) {
|
||||
statement.setInt(startIndex + i, entry.getSkills().getStaticLevel(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() {
|
||||
return SQLManager.getConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
package core.game.system.mysql.impl;
|
||||
|
||||
import rs09.ServerConstants;
|
||||
import core.game.system.monitor.MessageLog;
|
||||
import core.game.system.monitor.PlayerMonitor;
|
||||
import core.game.system.mysql.SQLEntryHandler;
|
||||
import core.game.system.mysql.SQLManager;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SQL Entry handler to log player's messages, duplications, & addresses.
|
||||
* @author Vexia
|
||||
*
|
||||
*/
|
||||
public class PlayerLogSQLHandler extends SQLEntryHandler<PlayerMonitor> {
|
||||
|
||||
/**
|
||||
* The column names.
|
||||
*/
|
||||
private static final String[] MESSAGE_COLUMNS = new String[] { "public_chat", "private_chat", "clan_chat", "address_log", "command_log", "trade_log", "ge_log", "duel_log" };
|
||||
|
||||
/**
|
||||
* Constructs a new {@Code PlayerLogSQLHandler} {@Code Object}
|
||||
* @param entry the player monitor entry.
|
||||
*/
|
||||
public PlayerLogSQLHandler(PlayerMonitor entry, String playerName) {
|
||||
super(entry, (SQLManager.LOCAL ? "global" : ServerConstants.DATABASE_NAME) + ".player_logs", "username", playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() throws SQLException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() throws SQLException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws SQLException {
|
||||
Connection connection = getConnection();
|
||||
if (connection.prepareStatement("SELECT * FROM " + table + " WHERE username='" + value + "' LIMIT 1").executeQuery().next()) {
|
||||
String b = "SET ";
|
||||
int size = 0;
|
||||
List<Integer> columns = new ArrayList<>(20);
|
||||
for (int i = 0; i < MESSAGE_COLUMNS.length; i++) {
|
||||
if (!entry.getLogs()[i].getMessages().isEmpty()) {
|
||||
b += MESSAGE_COLUMNS[i] + " = CONCAT(" + MESSAGE_COLUMNS[i] + ", ?)" + (i == MESSAGE_COLUMNS.length - 1 ? "" : ",");
|
||||
size++;
|
||||
columns.add(i);
|
||||
}
|
||||
}
|
||||
if (!entry.getDuplicationLog().getMessages().isEmpty()) {
|
||||
b += (b.charAt(b.length() - 1) != ',' ? "," : "") + "duplication_log = CONCAT(duplication_log, ?)";
|
||||
}
|
||||
if (b.charAt(b.length() - 1) == ',') {
|
||||
b = b.substring(0, b.length() - 1);
|
||||
}
|
||||
PreparedStatement statement = connection.prepareStatement("UPDATE " + table + " " + b + " WHERE username='" + value + "'");
|
||||
for (int i = 0; i < size; i++) {
|
||||
writeLog(statement, 1 + i, columns.get(i));
|
||||
}
|
||||
if (!entry.getDuplicationLog().getMessages().isEmpty()) {
|
||||
String log = "";
|
||||
MessageLog messageLog = entry.getDuplicationLog();
|
||||
for (int i = 0; i < messageLog.getMessages().size(); i++) {
|
||||
if (log.contains(messageLog.getMessages().get(i))) {
|
||||
continue;
|
||||
}
|
||||
log += messageLog.getMessages().get(i) + "\n";
|
||||
}
|
||||
statement.setString(size + 1, log);
|
||||
}
|
||||
//statement.executeUpdate();
|
||||
} else {
|
||||
PreparedStatement statement = connection.prepareStatement("INSERT INTO " + table + " (username,public_chat,private_chat,clan_chat,address_log,command_log,trade_log,ge_log,duel_log,duplication_log) VALUES(?,?,?,?,?,?,?,?,?,?)");
|
||||
statement.setString(1, value);
|
||||
for (int i = 0; i < MESSAGE_COLUMNS.length; i++) {
|
||||
//writeLog(statement, 2 + i, i);
|
||||
}
|
||||
String log = "";
|
||||
MessageLog messageLog = entry.getDuplicationLog();
|
||||
for (int i = 0; i < messageLog.getMessages().size(); i++) {
|
||||
if (log.contains(messageLog.getMessages().get(i))) {
|
||||
continue;
|
||||
}
|
||||
log += messageLog.getMessages().get(i) + "\n";
|
||||
}
|
||||
statement.setString(10, log);
|
||||
//statement.executeUpdate();
|
||||
}
|
||||
entry.clear();
|
||||
SQLManager.close(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a message log to the prepared statement.
|
||||
* @param statement the statement.
|
||||
* @param columnIndex the column index.
|
||||
* @param logIndex the log index.
|
||||
* @throws SQLException the exception if thrown.
|
||||
*/
|
||||
private void writeLog(PreparedStatement statement, int columnIndex, int logIndex) throws SQLException {
|
||||
String log = "";
|
||||
MessageLog messageLog = entry.getLogs()[logIndex];
|
||||
for (int i = 0; i < messageLog.getMessages().size(); i++) {
|
||||
log += messageLog.getMessages().get(i) + "\n";
|
||||
}
|
||||
statement.setString(columnIndex, log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() {
|
||||
return SQLManager.getConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
package core.game.system.mysql.impl;
|
||||
|
||||
import rs09.ServerConstants;
|
||||
import core.game.node.entity.player.info.PlayerDetails;
|
||||
import core.game.system.mysql.SQLColumn;
|
||||
import core.game.system.mysql.SQLEntryHandler;
|
||||
import core.game.system.mysql.SQLManager;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handles player details parsing/saving using SQL.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class PlayerSQLHandler extends SQLEntryHandler<PlayerDetails> {
|
||||
|
||||
/**
|
||||
* Constructs a new {@code PlayerSQLHandler} {@code Object}.
|
||||
* @param entry The player details.
|
||||
*/
|
||||
public PlayerSQLHandler(PlayerDetails entry) {
|
||||
super(entry, (SQLManager.LOCAL ? "global" : ServerConstants.DATABASE_NAME) + ".members", "username", entry.getUsername());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() throws SQLException {
|
||||
for (SQLColumn column : entry.getSqlManager().getTable().getColumns()) {
|
||||
if (!column.isParse()) {
|
||||
continue;
|
||||
}
|
||||
if (column.getType() == Integer.class) {
|
||||
column.setValue(result.getInt(column.getName()));
|
||||
} else if (column.getType() == String.class) {
|
||||
column.setValue(result.getString(column.getName()));
|
||||
} else if (column.getType() == Boolean.class) {
|
||||
column.setValue(result.getBoolean(column.getName()));
|
||||
} else if (column.getType() == Long.class) {
|
||||
column.setValue(result.getLong(column.getName()));
|
||||
} else if (column.getType() == Timestamp.class) {
|
||||
column.setValue(result.getTimestamp(column.getName()));
|
||||
} else if (column.getType() == BigInteger.class) {
|
||||
column.setValue(result.getLong(column.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReadSelection() {
|
||||
StringBuilder selection = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (SQLColumn column : entry.getSqlManager().getTable().getColumns()) {
|
||||
if (!first) {
|
||||
selection.append(",");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
selection.append(column.getName());
|
||||
}
|
||||
return selection.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() throws SQLException {
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
String[] names = new String[entry.getSqlManager().getTable().getColumns().length];
|
||||
for (int i = 0; i < entry.getSqlManager().getTable().getColumns().length; i++) {
|
||||
names[i] = entry.getSqlManager().getTable().getColumns()[i].getName();
|
||||
}
|
||||
PreparedStatement statement = getWritingStatement(true, names);
|
||||
for (int i = 0; i < entry.getSqlManager().getTable().getColumns().length; i++) {
|
||||
SQLColumn column = entry.getSqlManager().getTable().getColumns()[i];
|
||||
if (column.getType() == Integer.class) {
|
||||
statement.setInt(i + 2, (int) column.getValue());
|
||||
} else if (column.getType() == String.class) {
|
||||
statement.setString(i + 2, (String) column.getValue());
|
||||
} else if (column.getType() == Boolean.class) {
|
||||
statement.setBoolean(i + 2, (boolean) column.getValue());
|
||||
} else if (column.getType() == Long.class) {
|
||||
statement.setLong(i + 2, (Long) column.getValue());
|
||||
} else if (column.getType() == Timestamp.class) {
|
||||
statement.setTimestamp(i + 2, (Timestamp) column.getValue());
|
||||
} else if (column.getType() == java.math.BigInteger.class) {
|
||||
statement.setLong(i + 2, (Long) column.getValue());
|
||||
}
|
||||
}
|
||||
statement.executeUpdate();
|
||||
SQLManager.close(statement.getConnection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws SQLException {
|
||||
List<SQLColumn> updated = entry.getSqlManager().getTable().getChanged();
|
||||
if (updated.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String[] names = new String[updated.size()];
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
names[i] = updated.get(i).getName();
|
||||
}
|
||||
PreparedStatement statement = getWritingStatement(false, names);
|
||||
for (int i = 0; i < updated.size(); i++) {
|
||||
SQLColumn column = updated.get(i);
|
||||
if (column.getType() == Integer.class) {
|
||||
statement.setInt(i + 1, (int) column.getValue());
|
||||
} else if (column.getType() == String.class) {
|
||||
statement.setString(i + 1, (String) column.getValue());
|
||||
} else if (column.getType() == Boolean.class) {
|
||||
statement.setBoolean(i + 1, (boolean) column.getValue());
|
||||
} else if (column.getType() == Long.class) {
|
||||
statement.setLong(i + 1, (Long) column.getValue());
|
||||
} else if (column.getType() == Timestamp.class) {
|
||||
statement.setTimestamp(i + 1, (Timestamp) column.getValue());
|
||||
} else if (column.getType() == BigInteger.class) {
|
||||
statement.setLong(i + 1, (Long) column.getValue());
|
||||
}
|
||||
}
|
||||
statement.executeUpdate();
|
||||
SQLManager.close(statement.getConnection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() {
|
||||
return SQLManager.getConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import core.game.node.entity.player.Player;
|
|||
import core.game.node.entity.player.info.ClientInfo;
|
||||
import core.game.node.entity.player.info.login.Response;
|
||||
import core.game.system.task.Pulse;
|
||||
import rs09.auth.AuthResponse;
|
||||
import rs09.game.world.GameWorld;
|
||||
import core.net.producer.HSEventProducer;
|
||||
import core.net.producer.LoginEventProducer;
|
||||
|
|
@ -142,7 +143,7 @@ public class IoSession {
|
|||
if (context == null) {
|
||||
throw new IllegalStateException("Invalid writing context!");
|
||||
}
|
||||
if (!(context instanceof Response) && producer instanceof LoginEventProducer) {
|
||||
if (!(context instanceof AuthResponse) && producer instanceof LoginEventProducer) {
|
||||
// new Throwable().printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import core.net.packet.context.MessageContext;
|
|||
import core.net.packet.out.CommunicationMessage;
|
||||
import core.net.packet.out.ContactPackets;
|
||||
import core.net.packet.out.UpdateClanChat;
|
||||
import rs09.auth.AuthResponse;
|
||||
import rs09.game.node.entity.player.info.login.LoginParser;
|
||||
import rs09.game.system.SystemLogger;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
|
@ -306,17 +307,17 @@ public final class MSPacketRepository {
|
|||
LoginParser parser = WorldCommunicator.finishLoginAttempt(username);
|
||||
if (parser != null) {
|
||||
PlayerDetails details = parser.getDetails();
|
||||
Response response = Response.get(opcode);
|
||||
AuthResponse response = AuthResponse.values()[opcode];
|
||||
Player player = null;
|
||||
switch (response) {
|
||||
case ALREADY_ONLINE:
|
||||
case AlreadyOnline:
|
||||
player = Repository.getPlayerByName(username);
|
||||
if (player == null || player.getSession().isActive() || !player.getSession().getAddress().equals(details.getSession().getAddress())) {
|
||||
details.getSession().write(response, true);
|
||||
break;
|
||||
}
|
||||
player.getPacketDispatch().sendLogout();
|
||||
case SUCCESSFUL:
|
||||
case Success:
|
||||
if (!details.getSession().isActive()) {
|
||||
sendPlayerRemoval(username);
|
||||
break;
|
||||
|
|
@ -326,10 +327,10 @@ public final class MSPacketRepository {
|
|||
} else {
|
||||
player.updateDetails(details);
|
||||
}
|
||||
parser.initialize(player, response == Response.ALREADY_ONLINE);
|
||||
parser.initialize(player, response == AuthResponse.AlreadyOnline);
|
||||
break;
|
||||
|
||||
case MOVING_WORLD:
|
||||
case MovingWorld:
|
||||
details.getSession().setServerKey(buffer.get());
|
||||
default:
|
||||
details.getSession().write(response, true);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import core.net.IoSession;
|
|||
import core.net.NioReactor;
|
||||
import core.net.producer.MSHSEventProducer;
|
||||
import kotlin.Unit;
|
||||
import rs09.auth.AuthResponse;
|
||||
import rs09.game.node.entity.player.info.login.LoginParser;
|
||||
import rs09.game.system.SystemLogger;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
|
@ -73,15 +74,11 @@ public final class WorldCommunicator {
|
|||
public static void register(final LoginParser parser) {
|
||||
LoginParser p = loginAttempts.get(parser.getDetails().getUsername());
|
||||
if (p != null && GameWorld.getTicks() - p.getTimeStamp() < 50 && p.getDetails().getRights() == Rights.REGULAR_PLAYER) {
|
||||
parser.getDetails().getSession().write(Response.ALREADY_ONLINE, true);
|
||||
parser.getDetails().getSession().write(AuthResponse.AlreadyOnline, true);
|
||||
return;
|
||||
}
|
||||
loginAttempts.put(parser.getDetails().getUsername(), parser);
|
||||
TaskExecutor.executeSQL(() -> {
|
||||
if (!parser.getDetails().parse()) {
|
||||
parser.getDetails().getSession().write(Response.INVALID_LOGIN_SERVER, true);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
MSPacketRepository.sendPlayerRegistry(parser);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import core.net.EventProducer;
|
|||
import core.net.IoSession;
|
||||
import core.net.IoWriteEvent;
|
||||
import core.net.producer.GameEventProducer;
|
||||
import rs09.auth.AuthResponse;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
|
@ -31,10 +32,10 @@ public final class LoginWriteEvent extends IoWriteEvent {
|
|||
|
||||
@Override
|
||||
public void write(IoSession session, Object context) {
|
||||
Response response = (Response) context;
|
||||
AuthResponse response = (AuthResponse) context;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(500);
|
||||
buffer.put((byte) response.opcode());
|
||||
switch (response.opcode()) {
|
||||
buffer.put((byte) response.ordinal());
|
||||
switch (response.ordinal()) {
|
||||
case 2: //successful login
|
||||
buffer.put(getWorldResponse(session));
|
||||
session.setProducer(GAME_PRODUCER);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import core.game.node.entity.player.Player;
|
|||
import core.net.packet.IncomingPacket;
|
||||
import core.net.packet.IoBuffer;
|
||||
import core.tools.StringUtils;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
|
@ -21,8 +22,7 @@ public class ReportAbusePacket implements IncomingPacket {
|
|||
String target = StringUtils.longToString(buffer.getLong());
|
||||
Rule rule = Rule.forId(buffer.get());
|
||||
boolean mute = buffer.get() == 1;
|
||||
File file = new File(ServerConstants.PLAYER_SAVE_PATH + target + ".save");
|
||||
if (!file.exists()) {
|
||||
if (!GameWorld.getAccountStorage().checkUsernameTaken(target.toLowerCase())) {
|
||||
player.getPacketDispatch().sendMessage("Invalid player name.");
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,17 @@
|
|||
package core.net.registry;
|
||||
|
||||
import core.cache.misc.buffer.ByteBufferUtils;
|
||||
import core.game.node.entity.player.info.portal.PlayerSQLManager;
|
||||
import core.game.system.SystemManager;
|
||||
import core.game.system.mysql.SQLEntryHandler;
|
||||
import core.game.system.mysql.SQLManager;
|
||||
import core.game.system.task.TaskExecutor;
|
||||
import core.game.system.task.Pulse;
|
||||
import core.net.Constants;
|
||||
import core.net.IoSession;
|
||||
import kotlin.Unit;
|
||||
import rs09.ServerConstants;
|
||||
import rs09.auth.UserAccountInfo;
|
||||
import rs09.game.system.SystemLogger;
|
||||
import rs09.game.world.GameWorld;
|
||||
import rs09.net.event.LoginReadEvent;
|
||||
import rs09.net.packet.in.Login;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.sql.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
|
@ -24,31 +20,12 @@ import java.util.regex.Pattern;
|
|||
* @author Vexia
|
||||
*
|
||||
*/
|
||||
public class AccountRegister extends SQLEntryHandler<RegistryDetails> {
|
||||
|
||||
/**
|
||||
* The table name.
|
||||
*/
|
||||
private static final String TABLE = "members";
|
||||
|
||||
/**
|
||||
* The column name.
|
||||
*/
|
||||
private static final String COLUMN = "username";
|
||||
|
||||
public class AccountRegister {
|
||||
/**
|
||||
* The pattern compiler.
|
||||
*/
|
||||
private static final Pattern PATTERN = Pattern.compile("[a-z0-9_]{1,12}");
|
||||
|
||||
/**
|
||||
* Constructs a new {@Code AccountRegister} {@Code Object}
|
||||
* @param entry The registry entry.
|
||||
*/
|
||||
public AccountRegister(RegistryDetails entry) {
|
||||
super(entry, TABLE, COLUMN, entry.getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the incoming opcode of an account register.
|
||||
* @param session the session.
|
||||
|
|
@ -57,6 +34,7 @@ public class AccountRegister extends SQLEntryHandler<RegistryDetails> {
|
|||
*/
|
||||
public static void read(final IoSession session, int opcode, ByteBuffer buffer) {
|
||||
int day,month,year,country;
|
||||
UserAccountInfo info = UserAccountInfo.createDefault();
|
||||
switch (opcode) {
|
||||
case 147://details
|
||||
day = buffer.get();
|
||||
|
|
@ -67,33 +45,29 @@ public class AccountRegister extends SQLEntryHandler<RegistryDetails> {
|
|||
break;
|
||||
case 186://username
|
||||
final String username = ByteBufferUtils.getString(buffer).replace(" ", "_").toLowerCase().replace("|", "");
|
||||
info.setUsername(username);
|
||||
if (username.length() <= 0 || username.length() > 12) {
|
||||
response(session, RegistryResponse.INVALID_USERNAME);
|
||||
break;
|
||||
}
|
||||
if (!validUsername(username)) {
|
||||
if (invalidUsername(username)) {
|
||||
System.out.println("AHAHHA " + username);
|
||||
response(session,RegistryResponse.INVALID_USERNAME);
|
||||
break;
|
||||
}
|
||||
System.out.println(username);
|
||||
TaskExecutor.executeSQL(() -> {
|
||||
try {
|
||||
if (PlayerSQLManager.hasSqlAccount(username, "username")) {
|
||||
response(session, RegistryResponse.NOT_AVAILBLE_USER);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
response(session, RegistryResponse.SUCCESS);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
if (!GameWorld.getAuthenticator().canCreateAccountWith(info)) {
|
||||
response(session, RegistryResponse.NOT_AVAILBLE_USER);
|
||||
return;
|
||||
}
|
||||
response(session, RegistryResponse.SUCCESS);
|
||||
break;
|
||||
case 36://Register details
|
||||
SystemLogger.logInfo("Made it to final stage");
|
||||
buffer.get(); //Useless size being written that is already written in the RSA block
|
||||
buffer = LoginReadEvent.getRSABlock(buffer);
|
||||
buffer = Login.decryptRSABuffer(buffer, ServerConstants.EXPONENT, ServerConstants.MODULUS);
|
||||
if(buffer.get() != 10){ //RSA header (aka did this decrypt properly)
|
||||
SystemLogger.logInfo("Decryption failed during registration :(");
|
||||
response(session, RegistryResponse.CANNOT_CREATE);
|
||||
break;
|
||||
}
|
||||
|
|
@ -106,6 +80,8 @@ public class AccountRegister extends SQLEntryHandler<RegistryDetails> {
|
|||
final String name = ByteBufferUtils.getString(buffer).replace(" ", "_").toLowerCase().replace("|", "");
|
||||
buffer.getInt();
|
||||
String password = ByteBufferUtils.getString(buffer);
|
||||
info.setUsername(name);
|
||||
info.setPassword(password);
|
||||
if (password.length() < 5 || password.length() > 20) {
|
||||
response(session, RegistryResponse.INVALID_PASS_LENGTH);
|
||||
break;
|
||||
|
|
@ -114,7 +90,7 @@ public class AccountRegister extends SQLEntryHandler<RegistryDetails> {
|
|||
response(session, RegistryResponse.PASS_SIMILAR_TO_USER);
|
||||
break;
|
||||
}
|
||||
if (!validUsername(name)) {
|
||||
if (invalidUsername(name)) {
|
||||
response(session, RegistryResponse.INVALID_USERNAME);
|
||||
break;
|
||||
}
|
||||
|
|
@ -126,21 +102,17 @@ public class AccountRegister extends SQLEntryHandler<RegistryDetails> {
|
|||
year = buffer.getShort();
|
||||
country = buffer.getShort();
|
||||
buffer.getInt();
|
||||
@SuppressWarnings("deprecation")
|
||||
final RegistryDetails details = new RegistryDetails(name, SystemManager.getEncryption().hashPassword(password), new Date(year, month, day), country);
|
||||
TaskExecutor.execute(() -> {
|
||||
try {
|
||||
if (PlayerSQLManager.hasSqlAccount(name, "username")) {
|
||||
response(session, RegistryResponse.CANNOT_CREATE);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
SQLEntryHandler.write(new AccountRegister(details));
|
||||
if (!GameWorld.getAuthenticator().canCreateAccountWith(info)) {
|
||||
response(session, RegistryResponse.CANNOT_CREATE);
|
||||
return;
|
||||
}
|
||||
GameWorld.getAuthenticator().createAccountWith(info);
|
||||
GameWorld.getPulser().submit(new Pulse() {
|
||||
@Override
|
||||
public boolean pulse() {
|
||||
response(session, RegistryResponse.SUCCESS);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
response(session, RegistryResponse.CANNOT_CREATE);
|
||||
return true;
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
|
|
@ -149,28 +121,6 @@ public class AccountRegister extends SQLEntryHandler<RegistryDetails> {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() throws SQLException {}
|
||||
|
||||
@Override
|
||||
public void parse() throws SQLException {}
|
||||
|
||||
@Override
|
||||
public void save() throws SQLException {
|
||||
PreparedStatement statement = getWritingStatement(true, "password", "salt", "birthday", "countryCode", "joined_date","currentClan");
|
||||
statement.setString(1, entry.getUsername());
|
||||
statement.setString(2, entry.getPassword());
|
||||
statement.setString(3, entry.getPassword().substring(0, 29));
|
||||
statement.setDate(4, entry.getBirth());
|
||||
statement.setInt(5, entry.getCountry());
|
||||
statement.setTimestamp(6, new Timestamp(System.currentTimeMillis()));
|
||||
|
||||
statement.setString(7, GameWorld.getSettings().getEnable_default_clan() ? ServerConstants.SERVER_NAME.toLowerCase(): null);
|
||||
|
||||
statement.executeUpdate();
|
||||
SQLManager.close(statement.getConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a registry response code.
|
||||
* @param response the response.
|
||||
|
|
@ -178,22 +128,15 @@ public class AccountRegister extends SQLEntryHandler<RegistryDetails> {
|
|||
private static void response(IoSession session, RegistryResponse response) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(100);
|
||||
buf.put((byte) response.getId());
|
||||
session.queue((ByteBuffer) buf.flip());
|
||||
session.queue(buf.flip());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a username is valid.
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public static boolean validUsername(final String username) {
|
||||
public static boolean invalidUsername(final String username) {
|
||||
Matcher matcher = PATTERN.matcher(username);
|
||||
return matcher.matches();
|
||||
|
||||
return !matcher.matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() {
|
||||
return SQLManager.getConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ public final class PlayerLoader {
|
|||
* @return the player.
|
||||
*/
|
||||
public static Player getPlayerFile(String name) {
|
||||
final PlayerDetails playerDetails = new PlayerDetails(name, "");
|
||||
playerDetails.parse();
|
||||
final PlayerDetails playerDetails = new PlayerDetails(name);
|
||||
//playerDetails.parse();
|
||||
final Player player = new Player(playerDetails);
|
||||
PlayerParser.parse(player);
|
||||
// GameWorld.getWorld().getAccountService().loadPlayer(player);
|
||||
|
|
@ -34,8 +34,8 @@ public final class PlayerLoader {
|
|||
* @return the details
|
||||
*/
|
||||
public static PlayerDetails getPlayerDetailFile(String name) {
|
||||
final PlayerDetails playerDetails = new PlayerDetails(name, "");
|
||||
playerDetails.parse();
|
||||
final PlayerDetails playerDetails = new PlayerDetails(name);
|
||||
// playerDetails.parse();
|
||||
return playerDetails;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
package core.tools.mysql;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import rs09.ServerConstants;
|
||||
import rs09.game.system.SystemLogger;
|
||||
|
||||
public class DatabaseManager {
|
||||
|
||||
private Map<String, Connection> connections = new HashMap<>();
|
||||
private Map<String, Database> databases;
|
||||
|
||||
private Database db;
|
||||
private boolean connected;
|
||||
|
||||
public DatabaseManager(Database db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
public DatabaseManager connect() {
|
||||
try {
|
||||
Class.forName("com.mysql.cj.jdbc.Driver");
|
||||
Connection connection = DriverManager.getConnection("jdbc:mysql://" + db.host() + "/" + db.name() + "?useTimezone=true&serverTimezone=UTC", db.username(), db.password());
|
||||
connections.put(db.name(), connection);
|
||||
|
||||
SystemLogger.logInfo("Successfully connected with '" + db.name() + "'.");
|
||||
|
||||
this.connected = true;
|
||||
|
||||
|
||||
} catch (SQLException e) {
|
||||
SystemLogger.logErr("Couldn't connect to the database.");
|
||||
e.printStackTrace();
|
||||
ServerConstants.MYSQL = false;
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultSet query(String name, String query) {
|
||||
|
||||
try {
|
||||
|
||||
Connection connection = connections().get(name);
|
||||
|
||||
if (connection == null)
|
||||
return null;
|
||||
|
||||
Statement statement = connection.createStatement();
|
||||
|
||||
return statement.executeQuery(query);
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int update(String name, String query) {
|
||||
|
||||
try {
|
||||
|
||||
Connection connection = connections().get(name);
|
||||
|
||||
if (connection == null)
|
||||
return -1;
|
||||
|
||||
Statement statement = connection.createStatement();
|
||||
|
||||
return statement.executeUpdate(query);
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Map<String, Connection> connections() {
|
||||
return connections;
|
||||
}
|
||||
|
||||
public Map<String, Database> databases() {
|
||||
return databases;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -57,21 +57,6 @@ public class ResourceAIPManager {
|
|||
}
|
||||
|
||||
public ResourceAIPManager load(Player player) {
|
||||
try {
|
||||
Statement statement = GameWorld.getDatabaseManager().connections().get("global").createStatement();
|
||||
|
||||
ResultSet result = statement.executeQuery("SELECT * FROM `members` WHERE username='" + player.getUsername() + "'");
|
||||
// Results result = new Results(GameWorld.getDatabaseManager().query("global", "SELECT * FROM `members` WHERE username='" + player.getUsername() + "'"));
|
||||
|
||||
while (result.next()) {
|
||||
String eventName = result.getString("taskName");
|
||||
String eventTime = result.getString("taskTime");
|
||||
reActivate(eventName, Long.valueOf(eventTime));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +72,6 @@ public class ResourceAIPManager {
|
|||
StringBuilder query = new StringBuilder();
|
||||
query.append("UPDATE `members` SET `taskName`='" + entry.getKey().getTaskName() + "',`taskTime`='" + entry.getValue() + "' WHERE `username`='" + player.getUsername() + "'");
|
||||
System.out.println("ResourceAIPManager: " + query.toString());
|
||||
GameWorld.getDatabaseManager().update("global", query.toString());
|
||||
|
||||
}
|
||||
return this;
|
||||
|
|
|
|||
22
Server/src/main/kotlin/rs09/auth/AuthProvider.kt
Normal file
22
Server/src/main/kotlin/rs09/auth/AuthProvider.kt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package rs09.auth
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import rs09.storage.AccountStorageProvider
|
||||
|
||||
abstract class AuthProvider<T: AccountStorageProvider> {
|
||||
lateinit var storageProvider: T
|
||||
|
||||
abstract fun configureFor(provider: T)
|
||||
|
||||
fun canCreateAccountWith(info: UserAccountInfo) : Boolean {
|
||||
return !storageProvider.checkUsernameTaken(info.username)
|
||||
}
|
||||
|
||||
abstract fun createAccountWith(info: UserAccountInfo) : Boolean
|
||||
|
||||
abstract fun checkLogin(username: String, password: String) : Pair<AuthResponse,UserAccountInfo?>
|
||||
|
||||
abstract fun checkPassword(player: Player, password: String) : Boolean
|
||||
|
||||
abstract fun updatePassword(username: String, newPassword: String)
|
||||
}
|
||||
27
Server/src/main/kotlin/rs09/auth/AuthResponse.kt
Normal file
27
Server/src/main/kotlin/rs09/auth/AuthResponse.kt
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package rs09.auth
|
||||
|
||||
enum class AuthResponse {
|
||||
UnexpectedError,
|
||||
CouldNotAd,
|
||||
Success,
|
||||
InvalidCredentials,
|
||||
AccountDisabled,
|
||||
AlreadyOnline,
|
||||
Updated,
|
||||
FullWorld,
|
||||
LoginServerOffline,
|
||||
LoginLimitExceeded,
|
||||
BadSessionID,
|
||||
WeakPassword,
|
||||
MembersWorld,
|
||||
CouldNotLogin,
|
||||
Updating,
|
||||
TooManyIncorrectLogins,
|
||||
StandingInMembersArea,
|
||||
AccountLocked,
|
||||
ClosedBeta,
|
||||
InvalidLoginServer,
|
||||
MovingWorld,
|
||||
ErrorLoadingProfile,
|
||||
BannedUser
|
||||
}
|
||||
39
Server/src/main/kotlin/rs09/auth/DevelopmentAuthenticator.kt
Normal file
39
Server/src/main/kotlin/rs09/auth/DevelopmentAuthenticator.kt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package rs09.auth
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import rs09.storage.InMemoryStorageProvider
|
||||
|
||||
class DevelopmentAuthenticator : AuthProvider<InMemoryStorageProvider>() {
|
||||
override fun configureFor(provider: InMemoryStorageProvider) {
|
||||
storageProvider = provider
|
||||
}
|
||||
|
||||
override fun checkLogin(username: String, password: String): Pair<AuthResponse, UserAccountInfo?> {
|
||||
val info: UserAccountInfo
|
||||
if(!storageProvider.checkUsernameTaken(username.toLowerCase())) {
|
||||
info = UserAccountInfo.createDefault()
|
||||
info.username = username
|
||||
createAccountWith(info)
|
||||
} else {
|
||||
info = storageProvider.getAccountInfo(username.toLowerCase())
|
||||
}
|
||||
return Pair(AuthResponse.Success, storageProvider.getAccountInfo(username))
|
||||
}
|
||||
|
||||
override fun createAccountWith(info: UserAccountInfo): Boolean {
|
||||
info.username = info.username.toLowerCase()
|
||||
info.rights = 2
|
||||
storageProvider.store(info)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun checkPassword(player: Player, password: String): Boolean {
|
||||
return password == player.details.password
|
||||
}
|
||||
|
||||
override fun updatePassword(username: String, newPassword: String) {
|
||||
val info = storageProvider.getAccountInfo(username)
|
||||
info.password = newPassword
|
||||
storageProvider.update(info)
|
||||
}
|
||||
}
|
||||
61
Server/src/main/kotlin/rs09/auth/ProductionAuthenticator.kt
Normal file
61
Server/src/main/kotlin/rs09/auth/ProductionAuthenticator.kt
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package rs09.auth
|
||||
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.system.SystemManager
|
||||
import rs09.ServerConstants
|
||||
import rs09.storage.AccountStorageProvider
|
||||
import rs09.storage.SQLStorageProvider
|
||||
import java.sql.SQLDataException
|
||||
|
||||
class ProductionAuthenticator : AuthProvider<AccountStorageProvider>() {
|
||||
override fun configureFor(provider: AccountStorageProvider) {
|
||||
storageProvider = provider
|
||||
if (provider is SQLStorageProvider) {
|
||||
provider.configure(ServerConstants.DATABASE_ADDRESS!!, ServerConstants.DATABASE_NAME!!, ServerConstants.DATABASE_USER!!, ServerConstants.DATABASE_PASS!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createAccountWith(info: UserAccountInfo): Boolean {
|
||||
try {
|
||||
info.password = SystemManager.getEncryption().hashPassword(info.password)
|
||||
storageProvider.store(info)
|
||||
} catch (e: SQLDataException) {
|
||||
return false
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun checkLogin(username: String, password: String): Pair<AuthResponse, UserAccountInfo?> {
|
||||
val info: UserAccountInfo
|
||||
try {
|
||||
if (!storageProvider.checkUsernameTaken(username.toLowerCase())) {
|
||||
return Pair(AuthResponse.InvalidCredentials, null)
|
||||
}
|
||||
info = storageProvider.getAccountInfo(username.toLowerCase())
|
||||
val passCorrect = SystemManager.getEncryption().checkPassword(password, info.password)
|
||||
if(!passCorrect || info.password.isEmpty())
|
||||
return Pair(AuthResponse.InvalidCredentials, null)
|
||||
if(info.banEndTime > System.currentTimeMillis())
|
||||
return Pair(AuthResponse.AccountDisabled, null)
|
||||
if(info.online)
|
||||
return Pair(AuthResponse.AlreadyOnline, null)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return Pair(AuthResponse.CouldNotLogin, null)
|
||||
}
|
||||
return Pair(AuthResponse.Success, info)
|
||||
}
|
||||
|
||||
override fun checkPassword(player: Player, password: String): Boolean {
|
||||
return SystemManager.getEncryption().checkPassword(password, player.details.password)
|
||||
}
|
||||
|
||||
override fun updatePassword(username: String, newPassword: String) {
|
||||
val info = storageProvider.getAccountInfo(username)
|
||||
info.password = SystemManager.getEncryption().hashPassword(newPassword)
|
||||
storageProvider.update(info)
|
||||
}
|
||||
}
|
||||
81
Server/src/main/kotlin/rs09/auth/UserAccountInfo.kt
Normal file
81
Server/src/main/kotlin/rs09/auth/UserAccountInfo.kt
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
package rs09.auth
|
||||
|
||||
class UserAccountInfo(
|
||||
var username: String,
|
||||
var password: String,
|
||||
var uid: Int,
|
||||
var rights: Int,
|
||||
var credits: Int,
|
||||
var ip: String,
|
||||
var lastUsedIp: String,
|
||||
var muteEndTime: Long,
|
||||
var banEndTime: Long,
|
||||
var contacts: String,
|
||||
var blocked: String,
|
||||
var clanName: String,
|
||||
var currentClan: String,
|
||||
var clanReqs: String,
|
||||
var timePlayed: Long,
|
||||
var lastLogin: Long,
|
||||
var online: Boolean
|
||||
) {
|
||||
companion object {
|
||||
@JvmStatic fun createDefault() : UserAccountInfo {
|
||||
return UserAccountInfo("", "", 0, 0, 0, "", "", 0L, 0L, "", "", "", "", "1,0,8,9", 0L, 0L, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "USER:$username,PASS:$password,UID:$uid,RIGHTS:$rights,CREDITS:$credits,IP:$ip,LASTIP:$lastUsedIp"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as UserAccountInfo
|
||||
|
||||
if (username != other.username) return false
|
||||
if (password != other.password) return false
|
||||
if (uid != other.uid) return false
|
||||
if (rights != other.rights) return false
|
||||
if (credits != other.credits) return false
|
||||
if (ip != other.ip) return false
|
||||
if (lastUsedIp != other.lastUsedIp) return false
|
||||
if (muteEndTime != other.muteEndTime) return false
|
||||
if (banEndTime != other.banEndTime) return false
|
||||
if (contacts != other.contacts) return false
|
||||
if (blocked != other.blocked) return false
|
||||
if (clanName != other.clanName) return false
|
||||
if (currentClan != other.currentClan) return false
|
||||
if (clanReqs != other.clanReqs) return false
|
||||
if (timePlayed != other.timePlayed) return false
|
||||
if (lastLogin != other.lastLogin) return false
|
||||
if (online != other.online) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = username.hashCode()
|
||||
result = 31 * result + password.hashCode()
|
||||
result = 31 * result + uid
|
||||
result = 31 * result + rights
|
||||
result = 31 * result + credits
|
||||
result = 31 * result + ip.hashCode()
|
||||
result = 31 * result + lastUsedIp.hashCode()
|
||||
result = 31 * result + muteEndTime.hashCode()
|
||||
result = 31 * result + banEndTime.hashCode()
|
||||
result = 31 * result + contacts.hashCode()
|
||||
result = 31 * result + blocked.hashCode()
|
||||
result = 31 * result + clanName.hashCode()
|
||||
result = 31 * result + currentClan.hashCode()
|
||||
result = 31 * result + clanReqs.hashCode()
|
||||
result = 31 * result + timePlayed.hashCode()
|
||||
result = 31 * result + lastLogin.hashCode()
|
||||
result = 31 * result + online.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ class EasterBunnyDialogueFile(val NEED_BASKET : Boolean) : DialogueFile() {
|
|||
player!!.dialogueInterpreter.sendDialogue("You need 5 eggs to afford that.")
|
||||
} else {
|
||||
player!!.incrementAttribute(EGG_ATTRIBUTE, -5)
|
||||
player!!.details.credits += 1
|
||||
player!!.details.accountInfo.credits += 1
|
||||
player!!.dialogueInterpreter.sendDialogue(
|
||||
"You turn in 5 eggs in exchange for a credit.",
|
||||
"You now have ${player!!.getAttribute(EGG_ATTRIBUTE, 0)} eggs."
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import core.game.system.task.Pulse
|
|||
import core.net.amsc.MSPacketRepository
|
||||
import core.net.amsc.ManagementServerState
|
||||
import core.net.amsc.WorldCommunicator
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.GameWorld.loginListeners
|
||||
|
|
@ -70,7 +71,7 @@ class LoginParser(
|
|||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
try {
|
||||
flag(Response.ERROR_LOADING_PROFILE)
|
||||
flag(AuthResponse.ErrorLoadingProfile)
|
||||
Repository.LOGGED_IN_PLAYERS.remove(details.username)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
|
|
@ -84,10 +85,6 @@ class LoginParser(
|
|||
*/
|
||||
private fun handleLogin() {
|
||||
val p = worldInstance
|
||||
if (!details.parse()) {
|
||||
flag(Response.INVALID_LOGIN_SERVER)
|
||||
return
|
||||
}
|
||||
val player = p ?: Player(details)
|
||||
player.setAttribute("login_type", type)
|
||||
if (p != null) { // Reconnecting
|
||||
|
|
@ -122,7 +119,7 @@ class LoginParser(
|
|||
Repository.lobbyPlayers.remove(player)
|
||||
Repository.playerNames.remove(player.name)
|
||||
MSPacketRepository.sendPlayerRemoval(player.name)
|
||||
flag(Response.ERROR_LOADING_PROFILE)
|
||||
flag(AuthResponse.ErrorLoadingProfile)
|
||||
}
|
||||
//Repository.getPlayerNames().put(player.getName(), player);
|
||||
GameWorld.Pulser.submit(object : Pulse(1) {
|
||||
|
|
@ -140,7 +137,7 @@ class LoginParser(
|
|||
Repository.addPlayer(player)
|
||||
}
|
||||
player.details.session.setObject(player)
|
||||
flag(Response.SUCCESSFUL)
|
||||
flag(AuthResponse.Success)
|
||||
player.init()
|
||||
player.monitor.log(player.details.ipAddress, PlayerMonitor.ADDRESS_LOG)
|
||||
player.monitor.log(player.details.serial, PlayerMonitor.ADDRESS_LOG)
|
||||
|
|
@ -156,7 +153,7 @@ class LoginParser(
|
|||
Repository.lobbyPlayers.remove(player)
|
||||
Repository.playerNames.remove(player.name)
|
||||
MSPacketRepository.sendPlayerRemoval(player.name)
|
||||
flag(Response.ERROR_LOADING_PROFILE)
|
||||
flag(AuthResponse.ErrorLoadingProfile)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -185,7 +182,7 @@ class LoginParser(
|
|||
Repository.disconnectionQueue.remove(details.username)
|
||||
player.initReconnect()
|
||||
player.isActive = true
|
||||
flag(Response.SUCCESSFUL)
|
||||
flag(AuthResponse.Success)
|
||||
player.updateSceneGraph(true)
|
||||
player.configManager.init()
|
||||
LoginConfiguration.configureGameWorld(player)
|
||||
|
|
@ -208,22 +205,22 @@ class LoginParser(
|
|||
//This is supposed to prevent the double-logging issue. Will it work? Who knows.
|
||||
if (Repository.LOGGED_IN_PLAYERS.contains(details.username)) {
|
||||
SystemLogger.logWarn("LOGGED_IN_PLAYERS contains ${details.username}")
|
||||
return flag(Response.ALREADY_ONLINE)
|
||||
return flag(AuthResponse.AlreadyOnline)
|
||||
}
|
||||
if (WorldCommunicator.getState() == ManagementServerState.CONNECTING) {
|
||||
return flag(Response.LOGIN_SERVER_OFFLINE)
|
||||
return flag(AuthResponse.LoginServerOffline)
|
||||
}
|
||||
if (!details.session.isActive) {
|
||||
return false
|
||||
}
|
||||
if (SystemManager.isUpdating()) {
|
||||
return flag(Response.UPDATING)
|
||||
return flag(AuthResponse.Updating)
|
||||
}
|
||||
if (Repository.getPlayerByName(details.username).also { gamePlayer = it } != null && gamePlayer!!.session.isActive) {
|
||||
return flag(Response.ALREADY_ONLINE)
|
||||
return flag(AuthResponse.AlreadyOnline)
|
||||
}
|
||||
return if (details.isBanned) {
|
||||
flag(Response.ACCOUNT_DISABLED)
|
||||
flag(AuthResponse.AccountDisabled)
|
||||
} else true
|
||||
}
|
||||
|
||||
|
|
@ -232,9 +229,9 @@ class LoginParser(
|
|||
* @param response the [Response].
|
||||
* @return `True` if successfully logged in.
|
||||
*/
|
||||
fun flag(response: Response): Boolean {
|
||||
fun flag(response: AuthResponse): Boolean {
|
||||
details.session.write(response, true)
|
||||
return response == Response.SUCCESSFUL
|
||||
return response == AuthResponse.Success
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
27
Server/src/main/kotlin/rs09/game/system/Auth.kt
Normal file
27
Server/src/main/kotlin/rs09/game/system/Auth.kt
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package rs09.game.system
|
||||
|
||||
import rs09.auth.AuthProvider
|
||||
import rs09.auth.DevelopmentAuthenticator
|
||||
import rs09.auth.ProductionAuthenticator
|
||||
import rs09.storage.AccountStorageProvider
|
||||
import rs09.storage.InMemoryStorageProvider
|
||||
import rs09.storage.SQLStorageProvider
|
||||
|
||||
object Auth {
|
||||
lateinit var authenticator: AuthProvider<*>
|
||||
lateinit var storageProvider: AccountStorageProvider
|
||||
|
||||
fun configureFor(devMode: Boolean) {
|
||||
if (devMode) {
|
||||
authenticator = DevelopmentAuthenticator()
|
||||
storageProvider = InMemoryStorageProvider()
|
||||
(authenticator as DevelopmentAuthenticator).configureFor(storageProvider as InMemoryStorageProvider)
|
||||
SystemLogger.logInfo("[AUTH] Using Development Authenticator with In-Memory Storage")
|
||||
} else {
|
||||
authenticator = ProductionAuthenticator()
|
||||
storageProvider = SQLStorageProvider()
|
||||
(authenticator as ProductionAuthenticator).configureFor(storageProvider as SQLStorageProvider)
|
||||
SystemLogger.logInfo("[AUTH] Using Production Authenticator with SQL Storage")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package rs09.game.system.command.sets
|
|||
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.info.Rights
|
||||
import rs09.game.system.command.Command
|
||||
import core.game.system.task.Pulse
|
||||
import rs09.game.world.GameWorld
|
||||
import core.game.world.map.Location
|
||||
|
|
@ -25,7 +24,7 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){
|
|||
* Kick a player
|
||||
* =============================================================================================================
|
||||
*/
|
||||
define("kick", Privilege.ADMIN){ player, args ->
|
||||
define("kick", Privilege.MODERATOR){ player, args ->
|
||||
val playerToKick: Player? = Repository.getPlayerByName(args[1])
|
||||
if (playerToKick != null) {
|
||||
playerToKick.clear(true)
|
||||
|
|
@ -38,6 +37,100 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){
|
|||
* =============================================================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ban a player
|
||||
* =============================================================================================================
|
||||
*/
|
||||
define("ban", Privilege.ADMIN){ player, args ->
|
||||
val name = args[1]
|
||||
if(!GameWorld.accountStorage.checkUsernameTaken(name)) {
|
||||
reject(player, "Invalid username: $name")
|
||||
}
|
||||
val playerToKick: Player? = Repository.getPlayerByName(name)
|
||||
val durationString = args[2]
|
||||
val durationTokens = durationString.toCharArray()
|
||||
var intToken = ""
|
||||
var durationMillis = 0L
|
||||
var durationUnit: TimeUnit = TimeUnit.NANOSECONDS
|
||||
for(token in durationTokens){
|
||||
if(token.toString().toIntOrNull() != null) intToken += token
|
||||
else {
|
||||
val durationInt: Int = (intToken.toIntOrNull() ?: -1).also { if(it == -1) reject(player, "Invalid duration: $intToken") }
|
||||
durationUnit = when(token) {
|
||||
'd' -> TimeUnit.DAYS
|
||||
's' -> TimeUnit.SECONDS
|
||||
'm' -> TimeUnit.MINUTES
|
||||
'h' -> TimeUnit.HOURS
|
||||
else -> TimeUnit.SECONDS
|
||||
}
|
||||
durationMillis = durationUnit.toMillis(durationInt.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
playerToKick?.details?.accountInfo?.banEndTime = System.currentTimeMillis() + durationMillis
|
||||
playerToKick?.clear(true)
|
||||
GameWorld.Pulser.submit(object : Pulse(2) {
|
||||
override fun pulse(): Boolean {
|
||||
val info = GameWorld.accountStorage.getAccountInfo(name)
|
||||
info.banEndTime = System.currentTimeMillis() + durationMillis
|
||||
GameWorld.accountStorage.update(info)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
notify(player, "Banned user $name for $intToken ${durationUnit.name.toLowerCase()}.")
|
||||
}
|
||||
/**
|
||||
* =============================================================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mute a player
|
||||
* =============================================================================================================
|
||||
*/
|
||||
define("mute", Privilege.MODERATOR){ player, args ->
|
||||
val name = args[1]
|
||||
if(!GameWorld.accountStorage.checkUsernameTaken(name)) {
|
||||
reject(player, "Invalid username: $name")
|
||||
}
|
||||
val playerToMute: Player? = Repository.getPlayerByName(name)
|
||||
val durationString = args[2]
|
||||
val durationTokens = durationString.toCharArray()
|
||||
var intToken = ""
|
||||
var durationMillis = 0L
|
||||
var durationUnit: TimeUnit = TimeUnit.NANOSECONDS
|
||||
for(token in durationTokens){
|
||||
if(token.toString().toIntOrNull() != null) intToken += token
|
||||
else {
|
||||
val durationInt: Int = (intToken.toIntOrNull() ?: -1).also { if(it == -1) reject(player, "Invalid duration: $intToken") }
|
||||
durationUnit = when(token) {
|
||||
'd' -> TimeUnit.DAYS
|
||||
's' -> TimeUnit.SECONDS
|
||||
'm' -> TimeUnit.MINUTES
|
||||
'h' -> TimeUnit.HOURS
|
||||
else -> TimeUnit.SECONDS
|
||||
}
|
||||
durationMillis = durationUnit.toMillis(durationInt.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
playerToMute?.details?.accountInfo?.muteEndTime = System.currentTimeMillis() + durationMillis
|
||||
if(playerToMute == null) { //Player was offline at the time
|
||||
GameWorld.Pulser.submit(object : Pulse(2) {
|
||||
override fun pulse(): Boolean {
|
||||
val info = GameWorld.accountStorage.getAccountInfo(name)
|
||||
info.muteEndTime = System.currentTimeMillis() + durationMillis
|
||||
GameWorld.accountStorage.update(info)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
notify(player, "Muted user $name for $intToken ${durationUnit.name.toLowerCase()}.")
|
||||
}
|
||||
/**
|
||||
* =============================================================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jail a player
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
package rs09.game.system.command.sets
|
||||
|
||||
import core.cache.Cache
|
||||
import core.cache.def.Definition
|
||||
import api.InputType
|
||||
import api.runTask
|
||||
import api.sendDialogue
|
||||
import api.sendInputDialogue
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.game.node.entity.player.info.login.Response
|
||||
import core.game.node.entity.player.info.portal.PlayerSQLManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.SystemManager
|
||||
import core.game.system.SystemState
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.system.command.Command
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.system.command.Privilege
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.repository.Repository
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
|
@ -40,31 +41,29 @@ class SystemCommandSet : CommandSet(Privilege.ADMIN) {
|
|||
* Allows a player to reset their password
|
||||
*/
|
||||
define("resetpassword", Privilege.STANDARD) { player, args ->
|
||||
if (args.size != 3) {
|
||||
reject(player, "Usage: ::resetpassword current new", "WARNING: THIS IS PERMANENT.", "WARNING: PASSWORD CAN NOT CONTAIN SPACES.")
|
||||
sendInputDialogue(player, InputType.STRING_SHORT, "Enter Current Password:"){value ->
|
||||
val pass = value.toString()
|
||||
SystemLogger.logInfo(pass)
|
||||
runTask(player) {
|
||||
if (GameWorld.authenticator.checkPassword(player, pass)) {
|
||||
sendInputDialogue(player, InputType.STRING_SHORT, "Enter New Password:") { value2 ->
|
||||
val newPass = value2.toString()
|
||||
if (pass == newPass) {
|
||||
sendDialogue(player, "Failed: Passwords Match")
|
||||
} else if (newPass.length !in 5..20) {
|
||||
sendDialogue(player, "Failed: Password Too Long Or Too Short")
|
||||
} else if (newPass == player.details.accountInfo.username) {
|
||||
sendDialogue(player, "Failed: Password Is Username")
|
||||
} else {
|
||||
GameWorld.authenticator.updatePassword(player.details.accountInfo.username, newPass)
|
||||
sendDialogue(player, "Success: Password Updated!")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sendDialogue(player, "Fail: Wrong Password.")
|
||||
}
|
||||
}
|
||||
}
|
||||
val oldPass = args[1]
|
||||
var newPass = args[2]
|
||||
|
||||
if (PlayerSQLManager.getCredentialResponse(player.details.username, oldPass) != Response.SUCCESSFUL) {
|
||||
reject(player, "INVALID PASSWORD!")
|
||||
}
|
||||
|
||||
if (newPass.length < 5 || newPass.length > 20) {
|
||||
reject(player, "NEW PASSWORD MUST BE BETWEEN 5 AND 20 CHARACTERS")
|
||||
}
|
||||
|
||||
if (newPass == player.username) {
|
||||
reject(player, "PASSWORD CAN NOT BE SAME AS USERNAME.")
|
||||
}
|
||||
|
||||
if (newPass == oldPass) {
|
||||
reject(player, "PASSWORDS CAN NOT BE THE SAME")
|
||||
}
|
||||
|
||||
newPass = SystemManager.getEncryption().hashPassword(newPass)
|
||||
PlayerSQLManager.updatePassword(player.username.toLowerCase().replace(" ", "_"), newPass)
|
||||
notify(player, "Password updated successfully.")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,9 +74,9 @@ class SystemCommandSet : CommandSet(Privilege.ADMIN) {
|
|||
reject(player, "Usage: ::resetpasswordother user new", "WARNING: THIS IS PERMANENT.", "WARNING: PASSWORD CAN NOT CONTAIN SPACES.")
|
||||
}
|
||||
val otherUser = args[1]
|
||||
var newPass = args[2]
|
||||
val newPass = args[2]
|
||||
|
||||
if (PlayerSQLManager.hasSqlAccount(otherUser, "username")) {
|
||||
if (GameWorld.accountStorage.checkUsernameTaken(otherUser)) {
|
||||
|
||||
if (newPass.length < 5 || newPass.length > 20) {
|
||||
reject(player, "NEW PASSWORD MUST BE BETWEEN 5 AND 20 CHARACTERS")
|
||||
|
|
@ -87,8 +86,7 @@ class SystemCommandSet : CommandSet(Privilege.ADMIN) {
|
|||
reject(player, "PASSWORD CAN NOT BE SAME AS USERNAME.")
|
||||
}
|
||||
|
||||
newPass = SystemManager.getEncryption().hashPassword(newPass)
|
||||
PlayerSQLManager.updatePassword(otherUser.toLowerCase().replace(" ","_"),newPass)
|
||||
GameWorld.authenticator.updatePassword(otherUser, newPass)
|
||||
notify(player, "Password updated successfully.")
|
||||
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -12,13 +12,15 @@ import core.game.world.map.Location
|
|||
import core.game.world.map.RegionManager
|
||||
import core.plugin.CorePluginTypes.StartupPlugin
|
||||
import core.tools.RandomFunction
|
||||
import core.tools.mysql.DatabaseManager
|
||||
import rs09.ServerConstants
|
||||
import rs09.auth.AuthProvider
|
||||
import rs09.game.system.Auth
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.system.SystemLogger.logInfo
|
||||
import rs09.game.system.config.ConfigParser
|
||||
import rs09.game.world.repository.Repository
|
||||
import rs09.plugin.ClassScanner
|
||||
import rs09.storage.AccountStorageProvider
|
||||
import rs09.worker.MajorUpdateWorker
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
|
@ -83,14 +85,17 @@ object GameWorld {
|
|||
*/
|
||||
@JvmStatic
|
||||
var settings: GameSettings? = null
|
||||
@JvmStatic
|
||||
val authenticator: AuthProvider<*>
|
||||
get() = Auth.authenticator
|
||||
@JvmStatic
|
||||
val accountStorage: AccountStorageProvider
|
||||
get() = Auth.storageProvider
|
||||
/**
|
||||
* The current amount of (600ms) cycles elapsed.
|
||||
*/
|
||||
@JvmStatic
|
||||
var ticks = 0
|
||||
@JvmStatic
|
||||
var databaseManager: DatabaseManager? = null
|
||||
private set
|
||||
|
||||
@JvmStatic
|
||||
var Pulser = PulseRunner()
|
||||
|
|
@ -158,9 +163,12 @@ object GameWorld {
|
|||
fun prompt(run: Boolean, directory: String?){
|
||||
logInfo("Prompting ${settings?.name} Game World...")
|
||||
Cache.init(ServerConstants.CACHE_PATH)
|
||||
databaseManager = DatabaseManager(ServerConstants.DATABASE)
|
||||
databaseManager!!.connect()
|
||||
configParser.prePlugin()
|
||||
//go overboard with checks to make sure dev mode authenticator never triggers on live
|
||||
Auth.configureFor(
|
||||
settings!!.isDevMode
|
||||
&& ServerConstants.MS_SECRET_KEY == "2009scape_development"
|
||||
)
|
||||
ConfigParser().prePlugin()
|
||||
ClassScanner.scanClasspath()
|
||||
ClassScanner.loadPureInterfaces()
|
||||
worldPersists.forEach { it.parse() }
|
||||
|
|
|
|||
|
|
@ -195,12 +195,6 @@ class DisconnectionQueue {
|
|||
fun save(player: Player, sql: Boolean): Boolean {
|
||||
try {
|
||||
PlayerParser.save(player)
|
||||
if (sql) {
|
||||
player.details.sqlManager.update(player)
|
||||
player.details.save()
|
||||
/*SQLEntryHandler.write(HighscoreSQLHandler(player))
|
||||
SQLEntryHandler.write(PlayerLogSQLHandler(player.monitor, player.name))*/
|
||||
}
|
||||
return true
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
|
|
|
|||
|
|
@ -1,190 +1,39 @@
|
|||
package rs09.net.event
|
||||
|
||||
import core.cache.Cache
|
||||
import core.cache.crypto.ISAACCipher
|
||||
import core.cache.crypto.ISAACPair
|
||||
import core.cache.misc.buffer.ByteBufferUtils
|
||||
import core.game.node.entity.player.info.ClientInfo
|
||||
import core.game.node.entity.player.info.PlayerDetails
|
||||
import core.game.node.entity.player.info.UIDInfo
|
||||
import core.game.node.entity.player.info.login.LoginType
|
||||
import core.game.node.entity.player.info.login.Response
|
||||
import core.game.node.entity.player.info.portal.PlayerSQLManager
|
||||
import core.game.system.task.TaskExecutor
|
||||
import core.net.Constants
|
||||
import core.net.IoReadEvent
|
||||
import core.net.IoSession
|
||||
import core.net.amsc.WorldCommunicator
|
||||
import core.tools.StringUtils
|
||||
import rs09.ServerConstants
|
||||
import rs09.game.node.entity.player.info.login.LoginParser
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.repository.Repository
|
||||
import java.lang.Runnable
|
||||
import java.math.BigInteger
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.net.packet.`in`.Login
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Handles login reading events.
|
||||
* @author Emperor
|
||||
* @author Ceikry
|
||||
*/
|
||||
class LoginReadEvent
|
||||
/**
|
||||
* Constructs a new `LoginReadEvent`.
|
||||
* @param session The session.
|
||||
* @param buffer The buffer with data to read from.
|
||||
*/
|
||||
(session: IoSession?, buffer: ByteBuffer?) : IoReadEvent(session, buffer) {
|
||||
class LoginReadEvent(session: IoSession?, buffer: ByteBuffer?) : IoReadEvent(session, buffer) {
|
||||
override fun read(session: IoSession, buffer: ByteBuffer) {
|
||||
SystemLogger.logInfo("login read")
|
||||
val opcode: Int = buffer.get().toInt()
|
||||
if (buffer.short.toInt() != buffer.remaining()) {
|
||||
session.write(Response.BAD_SESSION_ID)
|
||||
val (response, info) = Login.decodeFromBuffer(buffer)
|
||||
|
||||
if(response != AuthResponse.Success || info == null) {
|
||||
session.write(response)
|
||||
return
|
||||
}
|
||||
val build = buffer.int
|
||||
if (build != Constants.REVISION) { // || buffer.getInt() != Constants.CLIENT_BUILD) {
|
||||
session.write(Response.UPDATED)
|
||||
|
||||
val (authResponse, accountInfo) = GameWorld.authenticator.checkLogin(info.username, info.password)
|
||||
|
||||
if(authResponse != AuthResponse.Success || accountInfo == null) {
|
||||
session.write(authResponse)
|
||||
return
|
||||
}
|
||||
when (opcode) {
|
||||
12 -> println("User details event detected")
|
||||
16, 18 -> decodeWorld(opcode, session, buffer)
|
||||
else -> {
|
||||
SystemLogger.logErr("[Login] Unhandled login type [opcode=$opcode]!")
|
||||
session.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val details = PlayerDetails(info.username)
|
||||
details.accountInfo = accountInfo
|
||||
session.clientInfo = ClientInfo(info.displayMode, info.windowMode, info.screenWidth, info.screenHeight)
|
||||
session.isaacPair = info.isaacPair
|
||||
|
||||
/**
|
||||
* Decodes a world login request.
|
||||
* @param session The session.
|
||||
* @param buffer The buffer to read from.
|
||||
*/
|
||||
private fun decodeWorld(opcode: Int, session: IoSession, buffer: ByteBuffer) {
|
||||
var buffer = buffer
|
||||
val d = buffer.get() // Memory?
|
||||
val e = buffer.get() // no advertisement = 1
|
||||
val f = buffer.get() // 1
|
||||
val windowMode = buffer.get().toInt() // Screen size mode
|
||||
val screenWidth = buffer.short.toInt() // Screen size Width
|
||||
val screenHeight = buffer.short.toInt() // Screen size Height
|
||||
val displayMode = buffer.get().toInt() // Display mode
|
||||
val data = ByteArray(24) // random.dat data.
|
||||
buffer[data]
|
||||
ByteBufferUtils.getString(buffer)
|
||||
buffer.int // Affiliate id
|
||||
buffer.int // Hash containing a bunch of settings
|
||||
val curpackets = buffer.short //Current interface packet counter.
|
||||
for (i in Cache.getIndexes().indices) {
|
||||
val crc = if (Cache.getIndexes()[i] == null) 0 else Cache.getIndexes()[i].information.informationContainer.crc
|
||||
if (crc != buffer.int && crc != 0) {
|
||||
/*session.write(Response.UPDATED);
|
||||
return;*/
|
||||
}
|
||||
}
|
||||
buffer = getRSABlock(buffer)
|
||||
buffer.rewind()
|
||||
if(buffer.get().toInt() != 10){
|
||||
session.write(Response.COULD_NOT_LOGIN)
|
||||
return
|
||||
}
|
||||
val isaacSeed = getISAACSeed(buffer)
|
||||
val inCipher = ISAACCipher(isaacSeed)
|
||||
for(i in 0..curpackets){
|
||||
inCipher.nextValue
|
||||
}
|
||||
for (i in 0..3) {
|
||||
isaacSeed[i] += 50
|
||||
}
|
||||
val outCipher = ISAACCipher(isaacSeed)
|
||||
session.isaacPair = ISAACPair(inCipher, outCipher)
|
||||
session.clientInfo = ClientInfo(displayMode, windowMode, screenWidth, screenHeight)
|
||||
val b = buffer
|
||||
SystemLogger.logInfo("spawning thread to handle login")
|
||||
TaskExecutor.executeSQL {
|
||||
SystemLogger.logInfo("login thread start")
|
||||
Thread.currentThread().name = "Login Password Response"
|
||||
SystemLogger.logInfo("login thread named")
|
||||
try {
|
||||
val username = StringUtils.longToString(b.long)
|
||||
SystemLogger.logInfo("got username")
|
||||
val password = ByteBufferUtils.getString(b)
|
||||
SystemLogger.logInfo("got password")
|
||||
val response = PlayerSQLManager.getCredentialResponse(username, password)
|
||||
SystemLogger.logInfo("got sql response")
|
||||
if (response != Response.SUCCESSFUL) {
|
||||
SystemLogger.logInfo("not success :(")
|
||||
session.write(response, true)
|
||||
return@executeSQL
|
||||
}
|
||||
SystemLogger.logInfo("great success, attempting login")
|
||||
login(PlayerDetails(username, password), session, b, opcode)
|
||||
SystemLogger.logInfo("done")
|
||||
} catch (e: Exception) {
|
||||
SystemLogger.logInfo("big whoops")
|
||||
e.printStackTrace()
|
||||
session.write(Response.COULD_NOT_LOGIN)
|
||||
}
|
||||
SystemLogger.logInfo("end login thread")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the login procedure after we check an acc is registered & certified.
|
||||
* @param details the player's details.
|
||||
* @param session the session.
|
||||
* @param buffer the byte buffer.
|
||||
* @param opcode the opcode.
|
||||
*/
|
||||
@JvmStatic
|
||||
private fun login(details: PlayerDetails, session: IoSession, buffer: ByteBuffer, opcode: Int) {
|
||||
SystemLogger.logInfo("login")
|
||||
if(!Repository.LOGGED_IN_PLAYERS.contains(details.username))
|
||||
Repository.LOGGED_IN_PLAYERS.add(details.username)
|
||||
val parser = LoginParser(details, LoginType.fromType(opcode))
|
||||
details.session = session
|
||||
details.info.translate(UIDInfo(details.ipAddress, ByteBufferUtils.getString(buffer), ByteBufferUtils.getString(buffer), ByteBufferUtils.getString(buffer)))
|
||||
if (WorldCommunicator.isEnabled()) {
|
||||
WorldCommunicator.register(parser)
|
||||
} else {
|
||||
TaskExecutor.executeSQL {parser.run()}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ISAAC seed from the buffer.
|
||||
* @param buffer The buffer to read from.
|
||||
* @return The ISAAC seed.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getISAACSeed(buffer: ByteBuffer): IntArray {
|
||||
val seed = IntArray(4)
|
||||
for (i in 0..3) {
|
||||
seed[i] = buffer.int
|
||||
}
|
||||
return seed
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the RSA block buffer.
|
||||
* @param buffer The buffer to get the RSA block from.
|
||||
* @return The RSA block buffer.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getRSABlock(buffer: ByteBuffer): ByteBuffer {
|
||||
SystemLogger.logInfo("getRSABlock")
|
||||
fun ByteArray.toHex(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
|
||||
SystemLogger.logInfo(buffer.array().sliceArray(0..1500).toHex())
|
||||
val numBytes = 256 + buffer.get()
|
||||
val encryptedByteArray = ByteArray(numBytes)
|
||||
buffer.get(encryptedByteArray)
|
||||
val encryptedBytes = BigInteger(encryptedByteArray)
|
||||
return ByteBuffer.wrap(encryptedBytes.modPow(ServerConstants.EXPONENT, ServerConstants.MODULUS).toByteArray())
|
||||
}
|
||||
Login.proceedWith(session, details, info.opcode)
|
||||
}
|
||||
}
|
||||
114
Server/src/main/kotlin/rs09/net/packet/in/Login.kt
Normal file
114
Server/src/main/kotlin/rs09/net/packet/in/Login.kt
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package rs09.net.packet.`in`
|
||||
|
||||
import core.cache.crypto.ISAACCipher
|
||||
import core.cache.crypto.ISAACPair
|
||||
import core.cache.misc.buffer.ByteBufferUtils
|
||||
import core.game.node.entity.player.info.PlayerDetails
|
||||
import core.game.node.entity.player.info.UIDInfo
|
||||
import core.game.node.entity.player.info.login.LoginType
|
||||
import core.net.Constants
|
||||
import core.net.IoSession
|
||||
import core.net.amsc.WorldCommunicator
|
||||
import core.tools.StringUtils
|
||||
import rs09.ServerConstants
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.game.node.entity.player.info.login.LoginParser
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.repository.Repository
|
||||
import java.math.BigInteger
|
||||
import java.nio.BufferUnderflowException
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
object Login {
|
||||
private const val ENCRYPTION_VERIFICATION_BYTE: Int = 10
|
||||
private const val NORMAL_LOGIN_OP = 16
|
||||
private const val RECONNECT_LOGIN_OP = 18
|
||||
const val CACHE_INDEX_COUNT = 29
|
||||
|
||||
fun decodeFromBuffer(buffer: ByteBuffer) : Pair<AuthResponse, LoginInfo?> {
|
||||
try {
|
||||
val info = LoginInfo.createDefault()
|
||||
|
||||
info.opcode = buffer.get().toInt()
|
||||
if (buffer.short.toInt() != buffer.remaining()) {
|
||||
return Pair(AuthResponse.BadSessionID, null)
|
||||
}
|
||||
val revision = buffer.int
|
||||
if (revision != Constants.REVISION) {
|
||||
return Pair(AuthResponse.Updated, null)
|
||||
}
|
||||
if (info.opcode != NORMAL_LOGIN_OP && info.opcode != RECONNECT_LOGIN_OP) {
|
||||
SystemLogger.logInfo("Invalid Login Opcode: ${info.opcode}")
|
||||
return Pair(AuthResponse.InvalidLoginServer, null)
|
||||
}
|
||||
|
||||
noop(buffer)
|
||||
info.showAds = buffer.get().toInt() == 1
|
||||
noop(buffer)
|
||||
info.windowMode = buffer.get().toInt()
|
||||
info.screenWidth = buffer.short.toInt()
|
||||
info.screenHeight = buffer.short.toInt()
|
||||
info.displayMode = buffer.get().toInt()
|
||||
noop(buffer, 24) //Skip past a bunch of random (actually random) data the client sends
|
||||
ByteBufferUtils.getString(buffer) //same as above
|
||||
info.adAffiliateId = buffer.int
|
||||
info.settingsHash = buffer.int
|
||||
info.currentPacketCount = buffer.short.toInt()
|
||||
|
||||
//Read client-reported CRC sums
|
||||
for (i in 0 until CACHE_INDEX_COUNT) info.crcSums[i] = buffer.int
|
||||
|
||||
val decryptedBuffer = decryptRSABuffer(buffer, ServerConstants.EXPONENT, ServerConstants.MODULUS)
|
||||
decryptedBuffer.rewind()
|
||||
|
||||
if (decryptedBuffer.get().toInt() != ENCRYPTION_VERIFICATION_BYTE) {
|
||||
return Pair(AuthResponse.UnexpectedError, info)
|
||||
}
|
||||
|
||||
info.isaacPair = produceISAACPairFrom(decryptedBuffer)
|
||||
info.username = StringUtils.longToString(decryptedBuffer.long)
|
||||
info.password = ByteBufferUtils.getString(decryptedBuffer)
|
||||
return Pair(AuthResponse.Success, info)
|
||||
} catch (e: Exception) {
|
||||
SystemLogger.logErr("Exception encountered during login packet parsing! See stack trace below.")
|
||||
e.printStackTrace()
|
||||
return Pair(AuthResponse.UnexpectedError, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun produceISAACPairFrom(buffer: ByteBuffer): ISAACPair {
|
||||
val incomingSeed = IntArray(4)
|
||||
for(i in incomingSeed.indices) {
|
||||
incomingSeed[i] = buffer.int
|
||||
}
|
||||
val inCipher = ISAACCipher(incomingSeed)
|
||||
for(i in incomingSeed.indices) {
|
||||
incomingSeed[i] += 50
|
||||
}
|
||||
val outCipher = ISAACCipher(incomingSeed)
|
||||
return ISAACPair(inCipher, outCipher)
|
||||
}
|
||||
|
||||
@JvmStatic fun decryptRSABuffer(buffer: ByteBuffer, exponent: BigInteger, modulus: BigInteger): ByteBuffer {
|
||||
return try {
|
||||
val numBytes = buffer.get().toInt() and 0xFF
|
||||
val encryptedBytes = ByteArray(numBytes)
|
||||
buffer[encryptedBytes]
|
||||
|
||||
val encryptedBigInt = BigInteger(encryptedBytes)
|
||||
ByteBuffer.wrap(encryptedBigInt.modPow(exponent, modulus).toByteArray())
|
||||
} catch (e: BufferUnderflowException) {
|
||||
ByteBuffer.wrap(byteArrayOf(-1))
|
||||
}
|
||||
}
|
||||
|
||||
private fun noop(buffer: ByteBuffer, amount: Int = 1) {buffer[ByteArray(amount)]}
|
||||
|
||||
fun proceedWith(session: IoSession, details: PlayerDetails, opcode: Int) {
|
||||
if (!Repository.LOGGED_IN_PLAYERS.contains(details.username))
|
||||
Repository.LOGGED_IN_PLAYERS.add(details.username)
|
||||
details.session = session
|
||||
details.info.translate(UIDInfo(details.ipAddress, "DEPRECATED", "DEPRECATED", "DEPRECATED"))
|
||||
WorldCommunicator.register(LoginParser(details, LoginType.fromType(opcode)))
|
||||
}
|
||||
}
|
||||
30
Server/src/main/kotlin/rs09/net/packet/in/LoginInfo.kt
Normal file
30
Server/src/main/kotlin/rs09/net/packet/in/LoginInfo.kt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package rs09.net.packet.`in`
|
||||
|
||||
import core.cache.crypto.ISAACCipher
|
||||
import core.cache.crypto.ISAACPair
|
||||
|
||||
class LoginInfo(
|
||||
var showAds: Boolean, //Unused
|
||||
var windowMode : Int,
|
||||
var screenWidth: Int,
|
||||
var screenHeight: Int,
|
||||
var displayMode: Int,
|
||||
var adAffiliateId: Int, //Unused
|
||||
var settingsHash: Int,
|
||||
var currentPacketCount: Int,
|
||||
var username: String,
|
||||
var password: String,
|
||||
var isaacPair: ISAACPair,
|
||||
var opcode: Int,
|
||||
var crcSums: IntArray
|
||||
) {
|
||||
companion object {
|
||||
fun createDefault(): LoginInfo {
|
||||
return LoginInfo(false, 0, 0, 0, 0, 0, 0, 0, "", "", ISAACPair(ISAACCipher(intArrayOf()), ISAACCipher(intArrayOf())), 0, IntArray(Login.CACHE_INDEX_COUNT))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ads:$showAds,wm:$windowMode,sw:$screenWidth,sh:$screenHeight,dm:$displayMode,adid:$adAffiliateId,settings:$settingsHash,pkt:$currentPacketCount,un:$username,pw:$password"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package rs09.storage
|
||||
|
||||
import rs09.auth.UserAccountInfo
|
||||
|
||||
interface AccountStorageProvider {
|
||||
fun checkUsernameTaken(username: String): Boolean
|
||||
fun getAccountInfo(username: String): UserAccountInfo
|
||||
fun store(info: UserAccountInfo)
|
||||
fun update(info: UserAccountInfo)
|
||||
fun remove(info: UserAccountInfo)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package rs09.storage
|
||||
|
||||
import rs09.auth.UserAccountInfo
|
||||
|
||||
class InMemoryStorageProvider : AccountStorageProvider {
|
||||
private val storage = HashMap<String, UserAccountInfo>()
|
||||
|
||||
override fun checkUsernameTaken(username: String): Boolean {
|
||||
return storage[username] != null
|
||||
}
|
||||
|
||||
override fun getAccountInfo(username: String): UserAccountInfo {
|
||||
return storage[username] ?: UserAccountInfo.createDefault()
|
||||
}
|
||||
|
||||
override fun store(info: UserAccountInfo) {
|
||||
storage[info.username] = info
|
||||
}
|
||||
|
||||
override fun update(info: UserAccountInfo) {
|
||||
storage[info.username] = info
|
||||
}
|
||||
|
||||
override fun remove(info: UserAccountInfo) {
|
||||
storage.remove(info.username)
|
||||
}
|
||||
}
|
||||
214
Server/src/main/kotlin/rs09/storage/SQLStorageProvider.kt
Normal file
214
Server/src/main/kotlin/rs09/storage/SQLStorageProvider.kt
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
package rs09.storage
|
||||
|
||||
import rs09.auth.UserAccountInfo
|
||||
import java.lang.Long.max
|
||||
import java.sql.*
|
||||
|
||||
class SQLStorageProvider : AccountStorageProvider {
|
||||
var connectionString = ""
|
||||
var connectionUsername = ""
|
||||
var connectionPassword = ""
|
||||
|
||||
fun getConnection(): Connection {
|
||||
Class.forName("com.mysql.cj.jdbc.Driver")
|
||||
return DriverManager.getConnection(connectionString, connectionUsername, connectionPassword)
|
||||
}
|
||||
|
||||
fun configure(host: String, databaseName: String, username: String, password: String) {
|
||||
connectionString = "jdbc:mysql://$host/$databaseName?useTimezone=true&serverTimezone=UTC"
|
||||
connectionUsername = username
|
||||
connectionPassword = password
|
||||
}
|
||||
|
||||
override fun checkUsernameTaken(username: String): Boolean {
|
||||
val conn = getConnection()
|
||||
conn.use {
|
||||
val compiledUsernameQuery = it.prepareStatement(usernameQuery)
|
||||
compiledUsernameQuery.setString(1, username.toLowerCase())
|
||||
val result = compiledUsernameQuery.executeQuery()
|
||||
val exists = result.next()
|
||||
result.close()
|
||||
return exists
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAccountInfo(username: String): UserAccountInfo {
|
||||
val conn = getConnection()
|
||||
conn.use { con ->
|
||||
val compiledAccountInfoQuery = con.prepareStatement(accountInfoQuery)
|
||||
compiledAccountInfoQuery.setString(1, username.toLowerCase())
|
||||
val result = compiledAccountInfoQuery.executeQuery()
|
||||
if (result.next()) {
|
||||
val userData = UserAccountInfo.createDefault()
|
||||
userData.username = username
|
||||
|
||||
result.getString(2) ?.let { userData.password = it }
|
||||
result.getInt(3) .let { userData.uid = it }
|
||||
result.getInt(4) .let { userData.rights = it }
|
||||
result.getInt(5) .let { userData.credits = it }
|
||||
result.getString(6) ?.let { userData.ip = it }
|
||||
result.getString(7) ?.let { userData.lastUsedIp = it }
|
||||
result.getLong(8) .let { userData.muteEndTime = max(0L, it) }
|
||||
result.getLong(9) .let { userData.banEndTime = max(0L, it) }
|
||||
result.getString(10)?.let { userData.contacts = it }
|
||||
result.getString(11)?.let { userData.blocked = it }
|
||||
result.getString(12)?.let { userData.clanName = it }
|
||||
result.getString(13)?.let { userData.currentClan = it }
|
||||
result.getString(14)?.let { userData.clanReqs = it }
|
||||
result.getLong(15) .let { userData.timePlayed = max(0L, it) }
|
||||
result.getLong(16) .let { userData.lastLogin = max(0L, it) }
|
||||
result.getBoolean(17).let { userData.online = it }
|
||||
|
||||
return userData
|
||||
} else {
|
||||
return UserAccountInfo.createDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun store(info: UserAccountInfo) {
|
||||
val conn = getConnection()
|
||||
conn.use {
|
||||
val compiledInsertInfoQuery = it.prepareStatement(insertInfoQuery, Statement.RETURN_GENERATED_KEYS)
|
||||
val emptyInfo = UserAccountInfo.createDefault()
|
||||
if (info == emptyInfo) {
|
||||
throw IllegalStateException("Tried to store empty data!")
|
||||
}
|
||||
emptyInfo.username = info.username
|
||||
if (info == emptyInfo) {
|
||||
throw IllegalStateException("Tried to store empty data!")
|
||||
}
|
||||
|
||||
if (checkUsernameTaken(info.username)) {
|
||||
throw SQLDataException("Account already exists!")
|
||||
}
|
||||
compiledInsertInfoQuery.setString(1, info.username)
|
||||
compiledInsertInfoQuery.setString(2, info.password)
|
||||
compiledInsertInfoQuery.setInt(3, info.rights)
|
||||
compiledInsertInfoQuery.setInt(4, info.credits)
|
||||
compiledInsertInfoQuery.setString(5, info.ip)
|
||||
compiledInsertInfoQuery.setString(6, info.ip)
|
||||
compiledInsertInfoQuery.setLong(7, info.muteEndTime)
|
||||
compiledInsertInfoQuery.setLong(8, info.banEndTime)
|
||||
compiledInsertInfoQuery.setString(9, info.contacts)
|
||||
compiledInsertInfoQuery.setString(10, info.blocked)
|
||||
compiledInsertInfoQuery.setString(11, info.clanName)
|
||||
compiledInsertInfoQuery.setString(12, info.currentClan)
|
||||
compiledInsertInfoQuery.setString(13, info.clanReqs)
|
||||
compiledInsertInfoQuery.setLong(14, info.timePlayed)
|
||||
compiledInsertInfoQuery.setLong(15, info.lastLogin)
|
||||
compiledInsertInfoQuery.setBoolean(16, info.online)
|
||||
compiledInsertInfoQuery.execute()
|
||||
val result = compiledInsertInfoQuery.generatedKeys
|
||||
if (result.next()) {
|
||||
info.uid = result.getInt(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(info: UserAccountInfo) {
|
||||
val conn = getConnection()
|
||||
conn.use {
|
||||
val compiledUpdateInfoQuery = it.prepareStatement(updateInfoQuery)
|
||||
val emptyInfo = UserAccountInfo.createDefault()
|
||||
if (info == emptyInfo) {
|
||||
throw IllegalStateException("Tried to store empty data!")
|
||||
}
|
||||
emptyInfo.username = info.username
|
||||
if (info == emptyInfo) {
|
||||
throw IllegalStateException("Tried to store empty data!")
|
||||
}
|
||||
emptyInfo.password = info.password
|
||||
if (info == emptyInfo) {
|
||||
throw IllegalStateException("Tried to store empty data!")
|
||||
}
|
||||
|
||||
compiledUpdateInfoQuery.setString(1, info.username)
|
||||
compiledUpdateInfoQuery.setString(2, info.password)
|
||||
compiledUpdateInfoQuery.setInt(3, info.rights)
|
||||
compiledUpdateInfoQuery.setInt(4, info.credits)
|
||||
compiledUpdateInfoQuery.setString(5, info.ip)
|
||||
compiledUpdateInfoQuery.setLong(6, info.muteEndTime)
|
||||
compiledUpdateInfoQuery.setLong(7, info.banEndTime)
|
||||
compiledUpdateInfoQuery.setString(8, info.contacts)
|
||||
compiledUpdateInfoQuery.setString(9, info.blocked)
|
||||
compiledUpdateInfoQuery.setString(10, info.clanName)
|
||||
compiledUpdateInfoQuery.setString(11, info.currentClan)
|
||||
compiledUpdateInfoQuery.setString(12, info.clanReqs)
|
||||
compiledUpdateInfoQuery.setLong(13, info.timePlayed)
|
||||
compiledUpdateInfoQuery.setLong(14, info.lastLogin)
|
||||
compiledUpdateInfoQuery.setBoolean(15, info.online)
|
||||
compiledUpdateInfoQuery.setInt(16, info.uid)
|
||||
compiledUpdateInfoQuery.execute()
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(info: UserAccountInfo) {
|
||||
val conn = getConnection()
|
||||
conn.use {
|
||||
val compiledRemoveInfoQuery = it.prepareStatement(removeInfoQuery)
|
||||
compiledRemoveInfoQuery.setString(1, info.username)
|
||||
compiledRemoveInfoQuery.execute()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val usernameQuery = "SELECT username FROM members WHERE username = ?;"
|
||||
private const val removeInfoQuery = "DELETE FROM members WHERE username = ?;"
|
||||
private const val accountInfoQuery = "SELECT " +
|
||||
"username," +
|
||||
"password," +
|
||||
"UID," +
|
||||
"rights," +
|
||||
"credits," +
|
||||
"ip," +
|
||||
"lastGameIp," +
|
||||
"muteTime," +
|
||||
"banTime," +
|
||||
"contacts," +
|
||||
"blocked," +
|
||||
"clanName," +
|
||||
"currentClan," +
|
||||
"clanReqs," +
|
||||
"timePlayed," +
|
||||
"lastLogin," +
|
||||
"online" +
|
||||
" FROM members WHERE username = ?;"
|
||||
private const val insertInfoQuery = "INSERT INTO members (" +
|
||||
"username," +
|
||||
"password," +
|
||||
"rights," +
|
||||
"credits," +
|
||||
"ip," +
|
||||
"lastGameIp," +
|
||||
"muteTime," +
|
||||
"banTime," +
|
||||
"contacts," +
|
||||
"blocked," +
|
||||
"clanName," +
|
||||
"currentClan," +
|
||||
"clanReqs," +
|
||||
"timePlayed," +
|
||||
"lastLogin," +
|
||||
"online" +
|
||||
") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"
|
||||
|
||||
private const val updateInfoQuery = "UPDATE members SET " +
|
||||
"username = ?," +
|
||||
"password = ?," +
|
||||
"rights = ?," +
|
||||
"credits = ?," +
|
||||
"lastGameIp = ?," +
|
||||
"muteTime = ?," +
|
||||
"banTime = ?," +
|
||||
"contacts = ?," +
|
||||
"blocked = ?," +
|
||||
"clanName = ?," +
|
||||
"currentClan = ?," +
|
||||
"clanReqs = ?," +
|
||||
"timePlayed = ?," +
|
||||
"lastLogin = ?," +
|
||||
"online = ?" +
|
||||
" WHERE uid = ?;"
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ import java.nio.ByteBuffer
|
|||
|
||||
object TestUtils {
|
||||
fun getMockPlayer(name: String, ironman: IronmanMode = IronmanMode.NONE): Player {
|
||||
val p = Player(PlayerDetails(name, name))
|
||||
val p = Player(PlayerDetails(name))
|
||||
p.details.session = MockSession()
|
||||
p.ironmanManager.mode = ironman
|
||||
Repository.addPlayer(p)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
package core.auth
|
||||
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import rs09.auth.DevelopmentAuthenticator
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.auth.UserAccountInfo
|
||||
import rs09.storage.InMemoryStorageProvider
|
||||
|
||||
class DevelopmentAuthenticatorTests {
|
||||
private val authProvider = DevelopmentAuthenticator()
|
||||
private val storageProvider = InMemoryStorageProvider()
|
||||
|
||||
init {
|
||||
authProvider.configureFor(storageProvider)
|
||||
}
|
||||
|
||||
@Test fun shouldAllowCheckingIfAccountExists() {
|
||||
val info = UserAccountInfo.createDefault()
|
||||
info.username = "Billy"
|
||||
Assertions.assertEquals(true, authProvider.canCreateAccountWith(info))
|
||||
authProvider.createAccountWith(info)
|
||||
Assertions.assertEquals(false, authProvider.canCreateAccountWith(info))
|
||||
}
|
||||
|
||||
@Test fun loginWithValidAccountInfoReturnsSuccess() {
|
||||
val info = UserAccountInfo.createDefault()
|
||||
info.username = "Billy"
|
||||
authProvider.createAccountWith(info)
|
||||
Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("Billy", "").first)
|
||||
}
|
||||
|
||||
//Development authenticator should work regardless if account exists or not.
|
||||
@Test fun loginWithInvalidAccountInfoReturnsSuccess() {
|
||||
val info = UserAccountInfo.createDefault()
|
||||
info.username = "Billy"
|
||||
authProvider.createAccountWith(info)
|
||||
Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("Bilbo", "ebbeb").first)
|
||||
}
|
||||
|
||||
@Test fun loginUsernameIsNotCaseSensitive() {
|
||||
val info = UserAccountInfo.createDefault()
|
||||
info.username = "Billy"
|
||||
authProvider.createAccountWith(info)
|
||||
Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("Billy", "").first)
|
||||
Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("billy", "").first)
|
||||
}
|
||||
|
||||
//Development authenticator should basically bypass needing/creating an account entirely. useful for SP too.
|
||||
@Test fun loginToUnregisteredAccountCreatesIt() {
|
||||
authProvider.checkLogin("masterbaggins", "whatever")
|
||||
val info = storageProvider.getAccountInfo("masterbaggins")
|
||||
Assertions.assertEquals(2, info.rights)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package core.auth
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.auth.ProductionAuthenticator
|
||||
import rs09.auth.UserAccountInfo
|
||||
import rs09.storage.InMemoryStorageProvider
|
||||
|
||||
class ProductionAuthenticatorTests {
|
||||
companion object {
|
||||
private val authProvider = ProductionAuthenticator()
|
||||
private val storageProvider = InMemoryStorageProvider()
|
||||
|
||||
init {
|
||||
authProvider.configureFor(storageProvider)
|
||||
}
|
||||
|
||||
@BeforeAll @JvmStatic fun createTestAccount() {
|
||||
val details = UserAccountInfo.createDefault()
|
||||
details.username = "test"
|
||||
details.password = "testing"
|
||||
if(!storageProvider.checkUsernameTaken("test")) {
|
||||
authProvider.createAccountWith(details)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun shouldRejectLoginWithInvalidDetails() {
|
||||
Assertions.assertEquals(AuthResponse.InvalidCredentials, authProvider.checkLogin("test", "test2").first)
|
||||
}
|
||||
|
||||
@Test fun loginUsernameIsNotCaseSensitive() {
|
||||
Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("Test", "testing").first)
|
||||
Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("test", "testing").first)
|
||||
}
|
||||
|
||||
@Test fun shouldHashPasswords() {
|
||||
Assertions.assertNotEquals("testing", storageProvider.getAccountInfo("test").password)
|
||||
}
|
||||
|
||||
@Test fun shouldNotAllowBannedLogin() {
|
||||
val info = storageProvider.getAccountInfo("test")
|
||||
info.banEndTime = System.currentTimeMillis() + 1000L
|
||||
storageProvider.update(info)
|
||||
Assertions.assertEquals(AuthResponse.AccountDisabled, authProvider.checkLogin("test", "testing").first)
|
||||
info.banEndTime = 0L
|
||||
storageProvider.update(info)
|
||||
Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("test", "testing").first)
|
||||
}
|
||||
|
||||
@Test fun shouldNotAllowAlreadyOnlineLogin() {
|
||||
val info = storageProvider.getAccountInfo("test")
|
||||
info.online = true
|
||||
storageProvider.update(info)
|
||||
Assertions.assertEquals(AuthResponse.AlreadyOnline, authProvider.checkLogin("test", "testing").first)
|
||||
info.online = false
|
||||
storageProvider.update(info)
|
||||
}
|
||||
}
|
||||
40
Server/src/test/kotlin/core/net/LoginTests.kt
Normal file
40
Server/src/test/kotlin/core/net/LoginTests.kt
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package core.net
|
||||
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.net.packet.`in`.Login
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class LoginTests {
|
||||
@Test fun shouldDecodeCorrectlyEncodedPacket() {
|
||||
val localCopy = validLoginPacket.copyOf(validLoginPacket.size)
|
||||
val loginInfo = Login.decodeFromBuffer(ByteBuffer.wrap(localCopy))
|
||||
Assertions.assertEquals(AuthResponse.Success, loginInfo.first, "Info: ${loginInfo.second}")
|
||||
}
|
||||
|
||||
@Test fun shouldNeverThrowExceptionButReturnUnexpectedErrorResponseInstead() {
|
||||
val localCopy = validLoginPacket.copyOf(validLoginPacket.size)
|
||||
for(i in 15 until validLoginPacket.size) {
|
||||
localCopy[i] = 0 //corrupt some data on purpose
|
||||
}
|
||||
var response: AuthResponse = AuthResponse.Updating
|
||||
Assertions.assertDoesNotThrow {
|
||||
val (res, _) = Login.decodeFromBuffer(ByteBuffer.wrap(localCopy))
|
||||
response = res
|
||||
}
|
||||
|
||||
Assertions.assertEquals(AuthResponse.UnexpectedError, response)
|
||||
}
|
||||
|
||||
@Test fun loginPacketWithInvalidOpcodeShouldReturnAHelpfulResponse() {
|
||||
val localCopy = validLoginPacket.copyOf(validLoginPacket.size)
|
||||
localCopy[0] = 2 //set to invalid login opcode
|
||||
val (response, _) = Login.decodeFromBuffer(ByteBuffer.wrap(localCopy))
|
||||
Assertions.assertEquals(AuthResponse.InvalidLoginServer, response)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val validLoginPacket = byteArrayOf(16, 1, 80, 0, 0, 2, 18, 0, 1, 1, 2, 2, -3, 1, -9, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 107, 75, 109, 111, 107, 51, 107, 74, 113, 79, 101, 78, 54, 68, 51, 109, 68, 100, 105, 104, 99, 111, 51, 111, 80, 101, 89, 78, 50, 75, 70, 121, 54, 87, 53, 45, 45, 118, 90, 85, 98, 78, 65, 0, 0, 0, 0, 0, 1, 89, -73, -29, 0, 0, -47, -49, 34, 92, -124, 110, -97, 46, -33, -18, 122, 8, 76, 93, -82, 16, 53, 23, -113, -73, 101, 126, -93, 125, -65, 115, -59, 19, 90, -54, -102, -76, -99, -5, -68, -51, 95, -20, -27, 10, 60, 60, 108, 5, 76, 9, -63, 97, 106, 74, 116, 0, 58, 0, 75, -111, -128, -34, 12, 64, 47, -92, -33, -120, -109, -7, 23, 124, 122, 40, -107, 56, -34, -93, 64, -82, 58, -90, 7, 127, -15, -85, 125, 43, 15, 0, 112, 60, 4, 75, 72, 55, 18, 83, -119, -39, 32, -113, 21, 104, -49, 66, -102, 104, -13, 32, 117, -106, 94, -30, 37, -56, -67, 21, 77, 70, -128, 113, 86, 84, 83, 115, 3, 55, -106, 127, -14, -15, 6, 42, -92, -56, 114, 24, 83, 32, -127, 78, 14, -98, -30, -38, -115, 9, -106, 120, 101, -117, -50, -40, -64, -50, -106, -98, 5, -86, 28, -127, -5, 109, -107, 49, -107, 63, 81, -81, -109, -21, 25, -68, 63, 102, -5, 8, -96, 126, -128, -116, -32, 26, 76, 54, -63, -37, 41, 57, 65, -53, -22, 83, 61, -128, -17, -21, 87, 76, -8, -95, -45, -80, -60, -62, 96, 49, 26, 49, 94, 80, 11, -12, -95, -58, -116, -54, -107, 91, 104, 58, 33, 20, -93, -68, -83, -116, 63, -18, 36, 30, -98, 77, -107, 122, 79, -27, 67, -94, 125, -81, -21, -75, -71, -45, -39, 112, 40)
|
||||
}
|
||||
}
|
||||
181
Server/src/test/kotlin/core/storage/SQLStorageProviderTests.kt
Normal file
181
Server/src/test/kotlin/core/storage/SQLStorageProviderTests.kt
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
package core.storage
|
||||
|
||||
import core.auth.ProductionAuthenticatorTests
|
||||
import org.junit.After
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import rs09.auth.UserAccountInfo
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.storage.SQLStorageProvider
|
||||
import java.sql.SQLDataException
|
||||
|
||||
class SQLStorageProviderTests {
|
||||
companion object {
|
||||
val storage = SQLStorageProvider()
|
||||
val testAccountNames = ArrayList<String>()
|
||||
init {
|
||||
storage.configure("localhost", "global", "root", "")
|
||||
val details = UserAccountInfo.createDefault()
|
||||
details.rights = 2
|
||||
details.username = "test"
|
||||
}
|
||||
|
||||
@AfterAll @JvmStatic fun cleanup() {
|
||||
SystemLogger.logInfo("Cleaning up unit test accounts")
|
||||
testAccountNames.forEach {name ->
|
||||
SystemLogger.logInfo("Removing test account $name")
|
||||
val info = UserAccountInfo.createDefault()
|
||||
info.username = name
|
||||
storage.remove(info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldReturnTrueIfUsernameExists() {
|
||||
val data = UserAccountInfo.createDefault()
|
||||
data.username = "test123123"
|
||||
data.password = "test"
|
||||
testAccountNames.add("test123123")
|
||||
storage.store(data)
|
||||
val exists = storage.checkUsernameTaken("test")
|
||||
Assertions.assertEquals(true, exists)
|
||||
}
|
||||
|
||||
@Test fun shouldReturnCorrectUserData() {
|
||||
val data = UserAccountInfo.createDefault()
|
||||
data.username = "test111"
|
||||
data.password = "test"
|
||||
data.rights = 2
|
||||
testAccountNames.add("test111")
|
||||
storage.store(data)
|
||||
val accountInfo = storage.getAccountInfo("test111")
|
||||
Assertions.assertEquals(2, accountInfo.rights)
|
||||
}
|
||||
|
||||
@Test fun shouldAllowStoreUserData() {
|
||||
val userData = UserAccountInfo.createDefault()
|
||||
userData.username = "storageTest"
|
||||
userData.password = "test"
|
||||
testAccountNames.add("storageTest")
|
||||
storage.store(userData)
|
||||
val exists = storage.checkUsernameTaken("storageTest")
|
||||
Assertions.assertEquals(true, exists)
|
||||
}
|
||||
|
||||
@Test fun shouldNotAllowDuplicateAccountStorage() {
|
||||
val userData = UserAccountInfo.createDefault()
|
||||
userData.username = "bilbo111"
|
||||
userData.password = "test"
|
||||
testAccountNames.add("bilbo111")
|
||||
storage.store(userData)
|
||||
Assertions.assertThrows(SQLDataException::class.java) {
|
||||
storage.store(userData)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun shouldAllowRemoveUserInfo() {
|
||||
val userData = UserAccountInfo.createDefault()
|
||||
userData.username = "bepis"
|
||||
userData.password = "test"
|
||||
testAccountNames.add("bepis")
|
||||
storage.store(userData)
|
||||
storage.remove(userData)
|
||||
Assertions.assertEquals(false, storage.checkUsernameTaken("bepis"))
|
||||
}
|
||||
|
||||
@Test fun shouldUpdateUserData() {
|
||||
val userData = UserAccountInfo.createDefault()
|
||||
userData.username = "borpis"
|
||||
userData.password = "test"
|
||||
testAccountNames.add("borpis")
|
||||
storage.store(userData)
|
||||
userData.credits = 2
|
||||
storage.update(userData)
|
||||
val data = storage.getAccountInfo(userData.username)
|
||||
Assertions.assertEquals(2, data.credits, "Wrong data: $data")
|
||||
}
|
||||
|
||||
@Test fun shouldNotAllowStoreOrUpdateEmptyData() {
|
||||
val info = UserAccountInfo.createDefault()
|
||||
Assertions.assertThrows(IllegalStateException::class.java) {
|
||||
storage.store(info)
|
||||
}
|
||||
Assertions.assertThrows(IllegalStateException::class.java) {
|
||||
storage.update(info)
|
||||
}
|
||||
info.username = "test"
|
||||
Assertions.assertThrows(IllegalStateException::class.java) {
|
||||
storage.store(info)
|
||||
}
|
||||
Assertions.assertThrows(IllegalStateException::class.java) {
|
||||
storage.update(info)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun shouldSetDefaultValuesWhenDBFieldIsNull() {
|
||||
val defaultData = UserAccountInfo.createDefault()
|
||||
|
||||
//manually insert a definitely-mostly-null entry into the DB
|
||||
val conn = storage.getConnection()
|
||||
conn.use {
|
||||
val stmt = conn.prepareStatement("INSERT INTO members (username) VALUES (?);")
|
||||
stmt.setString(1, "nulltestacc")
|
||||
testAccountNames.add("nulltestacc")
|
||||
stmt.execute()
|
||||
}
|
||||
|
||||
var data: UserAccountInfo = UserAccountInfo.createDefault()
|
||||
Assertions.assertDoesNotThrow {
|
||||
data = storage.getAccountInfo("nulltestacc")
|
||||
}
|
||||
|
||||
Assertions.assertEquals(defaultData.password, data.password)
|
||||
Assertions.assertEquals(defaultData.rights, data.rights)
|
||||
Assertions.assertEquals(defaultData.credits, data.credits)
|
||||
Assertions.assertEquals(defaultData.ip, data.ip)
|
||||
Assertions.assertEquals(defaultData.lastUsedIp, data.lastUsedIp)
|
||||
Assertions.assertEquals(defaultData.muteEndTime, data.muteEndTime)
|
||||
Assertions.assertEquals(defaultData.banEndTime, data.banEndTime)
|
||||
Assertions.assertEquals(defaultData.contacts, data.contacts)
|
||||
Assertions.assertEquals(defaultData.blocked, data.blocked)
|
||||
Assertions.assertEquals(defaultData.clanName, data.clanName)
|
||||
Assertions.assertEquals(defaultData.currentClan, data.currentClan)
|
||||
Assertions.assertEquals(defaultData.clanReqs, data.clanReqs)
|
||||
Assertions.assertEquals(defaultData.timePlayed, data.timePlayed)
|
||||
Assertions.assertEquals(defaultData.lastLogin, data.lastLogin)
|
||||
Assertions.assertEquals(defaultData.online, data.online)
|
||||
}
|
||||
|
||||
@Test fun updatingPropertiesOnTheDatabaseEndShouldBePreservedWhenFetchingAccountInfo() {
|
||||
val conn = storage.getConnection()
|
||||
conn.use {
|
||||
val stmt = conn.prepareStatement("INSERT INTO members (username) VALUES (?);")
|
||||
stmt.setString(1, "dbupdateacc")
|
||||
testAccountNames.add("dbupdateacc")
|
||||
stmt.execute()
|
||||
stmt.close()
|
||||
|
||||
val stmt2 = conn.prepareStatement("UPDATE members SET rights = 2 WHERE username = \"dbupdateacc\";")
|
||||
stmt2.execute()
|
||||
}
|
||||
|
||||
val info = storage.getAccountInfo("dbupdateacc")
|
||||
Assertions.assertEquals(2, info.rights)
|
||||
info.rights = 1
|
||||
storage.update(info)
|
||||
|
||||
val info2 = storage.getAccountInfo("dbupdateacc")
|
||||
Assertions.assertEquals(1, info2.rights)
|
||||
|
||||
val conn2 = storage.getConnection()
|
||||
conn2.use {
|
||||
val stmt = conn2.prepareStatement("UPDATE members SET rights = 2 WHERE username = \"dbupdateacc\";")
|
||||
stmt.execute()
|
||||
}
|
||||
|
||||
val info3 = storage.getAccountInfo("dbupdateacc")
|
||||
Assertions.assertEquals(2, info3.rights)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue