package services.login;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
import database.odb.ODBCursor;
import database.odb.ObjectDatabase;
import protocol.swg.CharacterCreationDisabled;
import protocol.swg.EnumerateCharacterId;
import protocol.swg.ErrorMessage;
import protocol.swg.LoginClientId;
import protocol.swg.LoginClientToken;
import protocol.swg.LoginClusterStatus;
import protocol.swg.LoginEnumCluster;
import protocol.swg.ServerNowEpochTime;
import protocol.swg.StationIdHasJediSlot;
import utils.Opcodes;
import main.Core;
import network.Client;
import network.PacketHandler;
import network.Service;
public class LoginService implements Service {
public enum LoginType {
Local,
VBulletin,
PHPBB
}
private Core core;
private String requiredVersion;
private LoginType loginType;
private boolean autoRegistration;
private Random rand = new Random();
private ObjectDatabase accountODB;
private ObjectDatabase characterODB;
private AtomicLong highestAccountId;
private ILoginProvider loginProvider;
private String serverIP;
private int maxCharacters;
public LoginService(Core core) {
this.core = core;
requiredVersion = core.getConfig().getString("RequiredVersion");
autoRegistration = core.getConfig().getBoolean("AutoRegistration");
serverIP = core.getConfig().getString("ServerIP");
maxCharacters = core.getConfig().getInt("MaxCharacters");
byte index = core.getConfig().getByte("LoginType");
if(index < 0 || index > 2)
index = 0;
loginType = LoginType.values()[index];
accountODB = core.getObjectDatabase(0);
switch (loginType) {
case Local:
loginProvider = new LocalDbLoginProvider(accountODB);
break;
// TODO: implement these
case VBulletin:
case PHPBB:
default:
loginProvider = new LocalDbLoginProvider(accountODB);
break;
}
characterODB = core.getObjectDatabase(1);
highestAccountId = new AtomicLong(0);
ODBCursor cursor = accountODB.getCursor();
while(cursor.hasNext()) {
Account acc = (Account) cursor.next();
if(acc == null)
continue;
if(acc.getId() > highestAccountId.get())
highestAccountId.set(acc.getId());
}
cursor.close();
}
@Override
public void handlePackets(Map<Integer, PacketHandler> handlers) {
handlers.put(Opcodes.LoginClientId, (client, packet) -> {
LoginClientId clientId = new LoginClientId();
clientId.deserialize(packet);
String userName = clientId.getAccountName();
String password = clientId.getPassword();
String version = clientId.getVersion();
if(!version.equals(requiredVersion)) {
ErrorMessage errMsg = new ErrorMessage("Invalid Version", "You have an invalid Client version installed, please update to the correct version.");
client.sendPacket(errMsg.serialize());
return;
}
long accountID = loginProvider.getAccountId(userName, password, client.getAddress().toString());
if(accountID == -1) {
ErrorMessage errMsg = new ErrorMessage("DB Error", "The database has encountered a problem while trying to retrieve your account.");
client.sendPacket(errMsg.serialize());
return;
} else if(accountID == -2) {
ErrorMessage errMsg = new ErrorMessage("Invalid account name", "An account with that name does not exist.");
client.sendPacket(errMsg.serialize());
return;
} else if(accountID == -3) {
ErrorMessage errMsg = new ErrorMessage("Invalid password", "The given password is not valid.");
client.sendPacket(errMsg.serialize());
return;
} else if(accountID == -4) {
ErrorMessage errMsg = new ErrorMessage("Account banned", "Your account is currently banned from playing on this server.");
client.sendPacket(errMsg.serialize());
return;
}
client.setAccountID(accountID);
// The session key will be send back to the client in LoginClientToken so the client can use it for connecting to the zone server
client.setSessionKey(generateSessionKey());
LoginClientToken clientToken = new LoginClientToken(client.getSessionKey(), (int) accountID, userName);
LoginEnumCluster servers = getLoginCluster();
LoginClusterStatus serverStatus = getLoginClusterStatus(client);
EnumerateCharacterId characters = getEnumerateCharacterId(accountID);
CharacterCreationDisabled charCreationDisabled = new CharacterCreationDisabled(0);
StationIdHasJediSlot jediSlot = new StationIdHasJediSlot(false);
ServerNowEpochTime time = new ServerNowEpochTime((int) (System.currentTimeMillis() / 1000));
client.sendPacket(time.serialize());
client.sendPacket(clientToken.serialize());
client.sendPacket(servers.serialize());
client.sendPacket(charCreationDisabled.serialize());
client.sendPacket(serverStatus.serialize());
client.sendPacket(jediSlot.serialize());
client.sendPacket(characters.serialize());
});
}
private EnumerateCharacterId getEnumerateCharacterId(long accountID) {
Account account = (Account) accountODB.get(accountID);
EnumerateCharacterId enumCharacters = new EnumerateCharacterId();
if(account == null)
return enumCharacters;
for(long characterId : account.getCharacters()) {
Character character = (Character) characterODB.get(characterId);
if(character == null)
continue;
enumCharacters.addCharacter(character.getName(), character.getAppearanceCRC(), characterId, 1, character.getType());
}
return enumCharacters;
}
private LoginClusterStatus getLoginClusterStatus(Client client) {
LoginClusterStatus clusterStatus = new LoginClusterStatus();
clusterStatus.addServer(1, serverIP, core.getZoneServer().getPort(), core.getPingServer().getPort(), maxCharacters, core.getGalaxyStatus().ordinal(), 0, core.getNumberOfConnectedCharacters());
return clusterStatus;
}
private LoginEnumCluster getLoginCluster() {
LoginEnumCluster enumCluster = new LoginEnumCluster(maxCharacters);
enumCluster.addServer(1, core.getGalaxyName());
return enumCluster;
}
public boolean isAutoRegistration() {
return autoRegistration;
}
/**
* Generates the next account ID.
* @return the generated account ID.
*/
public long generateAccountId() {
return highestAccountId.incrementAndGet();
}
/**
* Generates random Session Key.
* @return random Session Key.
*/
private byte[] generateSessionKey() {
byte[] bytes = new byte[24];
rand.nextBytes(bytes);
return bytes;
}
@Override
public void handleObjControllerPackets(Map<Integer, PacketHandler> handlers) {
}
}