package javastory.login;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import javastory.config.ChannelInfo;
import javastory.db.Database;
import javastory.registry.Nexus;
import javastory.registry.WorldRegistry;
import javastory.rmi.LoginWorldInterface;
import javastory.rmi.WorldLoginInterface;
import javastory.server.GameService;
import javastory.server.TimerManager;
import javastory.server.handling.PacketHandler;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
public class LoginServer extends GameService {
// TODO: This should be a per-channel constant.
public static final int USER_LIMIT = 1200;
private LoginWorldInterface lwi;
private WorldLoginInterface wli;
private final Properties subnetInfo = new Properties();
private final Map<Integer, LoginChannelInfo> channels = Maps.newHashMap();
private static final LoginServer instance = new LoginServer();
public static LoginServer getInstance() {
return instance;
}
private LoginServer() {
super(8484);
}
public final void addChannel(final ChannelInfo info) {
final int channelId = info.getId();
this.channels.put(channelId, new LoginChannelInfo(info, 0));
}
public final void removeChannel(final int channelId) {
this.channels.remove(channelId);
}
public final String getChannelServerIP(final int channelId) {
return this.channels.get(channelId).getHost();
}
// TODO: Use proper IP + port instead.
// This is only useful if the channel is on a well-known host (such as 127.0.0.1)
public final int getChannelServerPort(final int channelId) {
return this.channels.get(channelId).getPort();
}
//
public final Map<Integer, LoginChannelInfo> getChannels() {
return ImmutableMap.copyOf(this.channels);
}
public final void pingWorld() {
try {
this.wli.ping();
} catch (final RemoteException ex) {
if (this.isWorldReady.compareAndSet(true, false)) {
this.connectToWorld();
}
}
}
@Override
protected final void connectToWorld() {
try {
final WorldRegistry worldRegistry = Nexus.getOrBindWorldRegistry();
this.lwi = new LoginWorldInterfaceImpl();
this.wli = worldRegistry.registerLoginServer(this.lwi);
} catch (final NotBoundException ex) {
throw new RuntimeException("[EXCEPTION] Attempting lookup or unbind in the registry a name that has no associated binding.");
} catch (final RemoteException ex) {
throw new RuntimeException("[EXCEPTION] Could not connect to world server.");
}
this.isWorldReady.compareAndSet(false, true);
}
@Override
protected final void loadSettings() {
}
private void initialize() {
this.connectToWorld();
this.loadSettings();
try (PreparedStatement ps = Database.getConnection().prepareStatement("UPDATE accounts SET loggedin = 0")) {
ps.executeUpdate();
} catch (final SQLException ex) {
throw new RuntimeException("[EXCEPTION] Please check if the SQL server is active.");
}
final PacketHandler packetHandler = new LoginPacketHandler();
super.bind(packetHandler);
}
@Override
public final void shutdown() {
System.out.println("Shutting down...");
try {
final WorldRegistry worldRegistry = Nexus.getWorldRegistry();
worldRegistry.deregisterLoginServer(this.lwi);
} catch (final RemoteException e) {
//
}
TimerManager.getInstance().stop();
System.exit(0);
}
public final WorldLoginInterface getWorldInterface() {
if (!this.isWorldReady.get()) {
throw new IllegalStateException("The world server is not ready.");
}
return this.wli;
}
public static void start() {
try {
instance.initialize();
} catch (final Exception ex) {
System.err.println("Error initializing loginserver : " + ex);
}
}
public final Properties getSubnetInfo() {
return this.subnetInfo;
}
public void setLoad(final int channelId, final int load) {
final LoginChannelInfo info = this.channels.get(channelId);
if (info == null) {
throw new IllegalArgumentException("'channelId' is not a valid channel ID");
}
info.setLoad(load);
}
}