package javastory.world;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javastory.channel.Guild;
import javastory.channel.GuildMember;
import javastory.channel.Messenger;
import javastory.channel.MessengerMember;
import javastory.channel.Party;
import javastory.channel.PlayerBuffStorage;
import javastory.channel.client.MemberRank;
import javastory.config.ChannelInfo;
import javastory.db.Database;
import javastory.registry.WorldRegistry;
import javastory.rmi.ChannelWorldInterface;
import javastory.rmi.GenericRemoteObject;
import javastory.rmi.LoginWorldInterface;
import javastory.rmi.WorldChannelInterface;
import javastory.rmi.WorldLoginInterface;
import javastory.world.core.ServerStatus;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class WorldRegistryImpl extends GenericRemoteObject implements WorldRegistry {
private static final long serialVersionUID = -5170574938159280746L;
private static WorldRegistryImpl instance = null;
//
private final ServerStatus loginStatus;
private final List<LoginWorldInterface> logins = Lists.newLinkedList();
//
private final Map<Integer, ServerStatus> channelStatus;
private final Map<Integer, ChannelWorldInterface> channels;
//
private final AtomicInteger runningMessengerId = new AtomicInteger();
private final Map<Integer, Messenger> messengers = Maps.newHashMap();
//
private final AtomicInteger runningPartyId = new AtomicInteger();
private final Map<Integer, Party> parties = Maps.newHashMap();
//
private final Map<Integer, Guild> guilds = Maps.newLinkedHashMap();
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
private final Lock guildMutex = new ReentrantLock();
private WorldRegistryImpl() throws RemoteException {
super();
final Connection con = Database.getConnection();
try ( PreparedStatement ps = con.prepareStatement("SELECT MAX(party)+1 FROM characters");
ResultSet rs = ps.executeQuery()) {
rs.next();
this.runningPartyId.set(rs.getInt(1));
} catch (final SQLException ex) {
System.err.println("Could not load max party ID: " + ex);
}
this.channelStatus = Maps.newLinkedHashMap();
this.channels = Maps.newLinkedHashMap();
this.loginStatus = ServerStatus.OFFLINE;
this.runningMessengerId.set(1);
}
public static WorldRegistry getInstance() {
if (instance == null) {
try {
instance = new WorldRegistryImpl();
} catch (final RemoteException e) {
throw new RuntimeException(e);
}
}
return instance;
}
private boolean isChannelActive(final int channelId) {
final ServerStatus status = this.getChannelStatus(channelId);
return !status.equals(ServerStatus.OFFLINE);
}
private boolean isLoginActive() {
return !this.loginStatus.equals(ServerStatus.OFFLINE);
}
private ServerStatus getChannelStatus(final int channelId) {
ServerStatus status = this.channelStatus.get(channelId);
if (status == null) {
status = ServerStatus.OFFLINE;
this.channelStatus.put(channelId, status);
}
return status;
}
@Override
public WorldChannelInterface registerChannelServer(final ChannelInfo info, final ChannelWorldInterface channel) throws RemoteException {
final int id = info.getId();
if (this.isChannelActive(id)) {
throw new IllegalStateException("The specified channel slot is already active.");
}
this.channels.put(id, channel);
final WorldChannelInterface ret = new WorldChannelInterfaceImpl(channel, id);
return ret;
}
@Override
public void deregisterChannelServer(final int channelId) throws RemoteException {
if (!this.isChannelActive(channelId)) {
throw new IllegalStateException("The specified channel slot is not currently active.");
}
this.channels.remove(channelId);
this.channelStatus.put(channelId, ServerStatus.OFFLINE);
for (final LoginWorldInterface wli : this.logins) {
wli.channelOffline(channelId);
}
System.out.println("Channel " + channelId + " is offline.");
}
@Override
public WorldLoginInterface registerLoginServer(final LoginWorldInterface login) throws RemoteException {
if (this.isLoginActive()) {
throw new IllegalStateException("The login server is already active.");
}
final WorldLoginInterface ret = new WorldLoginInterfaceImpl();
this.logins.add(login);
for (final ChannelWorldInterface cwi : this.channels.values()) {
login.channelOnline(cwi.getChannelInfo());
}
return ret;
}
@Override
public void deregisterLoginServer(final LoginWorldInterface cb) throws RemoteException {
if (!this.isLoginActive()) {
throw new IllegalStateException("The login server is not currently active.");
}
this.logins.remove(cb);
}
@Override
public List<LoginWorldInterface> getLoginServer() {
return Lists.newLinkedList(this.logins);
}
@Override
public ChannelWorldInterface getChannel(final int channel) {
return this.channels.get(channel);
}
@Override
public ImmutableSet<Integer> getActiveChannels() {
return ImmutableSet.copyOf(this.channels.keySet());
}
@Override
public Collection<ChannelWorldInterface> getAllChannelServers() {
return this.channels.values();
}
@Override
public Party createParty() {
final int partyid = this.runningPartyId.getAndIncrement();
final Party party = new Party(partyid);
this.parties.put(party.getId(), party);
return party;
}
@Override
public Party getParty(final int partyid) {
return this.parties.get(partyid);
}
@Override
public Party disbandParty(final int partyid) {
return this.parties.remove(partyid);
}
@Override
public final String getStatus() throws RemoteException {
final StringBuilder ret = new StringBuilder();
final List<Entry<Integer, ChannelWorldInterface>> channelServers = Lists.newArrayList(this.channels.entrySet());
int totalUsers = 0;
for (final Entry<Integer, ChannelWorldInterface> cs : channelServers) {
ret.append("Channel ");
ret.append(cs.getKey());
try {
cs.getValue().ping();
ret.append(": online, ");
final int channelUsers = cs.getValue().getConnected();
totalUsers += channelUsers;
ret.append(channelUsers);
ret.append(" users\n");
} catch (final RemoteException e) {
ret.append(": offline\n");
}
}
ret.append("Total users online: ");
ret.append(totalUsers);
ret.append("\n");
// Properties props = new
// Properties(WorldServer.getInstance().getWorldProperties());
for (final LoginWorldInterface lwi : this.logins) {
ret.append("Login: ");
try {
lwi.ping();
ret.append("online\n");
} catch (final RemoteException e) {
ret.append("offline\n");
}
}
return ret.toString();
}
@Override
public final int createGuild(final int leaderId, final String name) {
return Guild.createGuild(leaderId, name);
}
@Override
public final Guild getGuild(final int guildId) {
this.guildMutex.lock();
try {
return this.loadGuildIfAbsent(guildId);
} finally {
this.guildMutex.unlock();
}
}
private Guild loadGuildIfAbsent(final int guildId) {
Guild guild = this.guilds.get(guildId);
if (guild == null) {
guild = new Guild(guildId);
if (guild.getId() == -1) {
return null;
}
this.guilds.put(guildId, guild);
}
return guild;
}
@Override
public void setGuildMemberOnline(final GuildMember mgc, final boolean bOnline, final int channel) {
this.getGuild(mgc.getGuildId()).setOnline(mgc.getCharacterId(), bOnline, channel);
}
@Override
public final boolean addGuildMember(final GuildMember mgc) {
final Guild guild = this.guilds.get(mgc.getGuildId());
return guild != null && guild.addGuildMember(mgc);
}
@Override
public void leaveGuild(final GuildMember mgc) {
final Guild guild = this.guilds.get(mgc.getGuildId());
if (guild != null) {
guild.leaveGuild(mgc);
}
}
@Override
public void guildChat(final int gid, final String name, final int cid, final String msg) throws RemoteException {
final Guild guild = this.guilds.get(gid);
if (guild != null) {
guild.guildChat(name, cid, msg);
}
}
@Override
public void changeRank(final int gid, final int cid, final MemberRank newRank) throws RemoteException {
final Guild guild = this.guilds.get(gid);
if (guild != null) {
guild.changeRank(cid, newRank);
}
}
@Override
public void expelMember(final GuildMember initiator, final int cid) throws RemoteException {
final Guild guild = this.guilds.get(initiator.getGuildId());
if (guild != null) {
guild.expelMember(initiator, cid);
}
}
@Override
public void setGuildNotice(final int gid, final String notice) throws RemoteException {
final Guild guild = this.guilds.get(gid);
if (guild != null) {
guild.setGuildNotice(notice);
}
}
@Override
public void updateGuildMemberLevel(final int guildId, final int characterId, final int level) throws RemoteException {
final Guild guild = this.guilds.get(guildId);
if (guild != null) {
guild.updateMemberLevel(characterId, level);
}
}
@Override
public void updateGuildMemberJob(final int guildId, final int characterId, final int jobId) throws RemoteException {
final Guild guild = this.guilds.get(guildId);
if (guild != null) {
guild.updateMemberJob(characterId, jobId);
}
}
@Override
public void changeRankTitle(final int gid, final String[] ranks) throws RemoteException {
final Guild guild = this.guilds.get(gid);
if (guild != null) {
guild.changeRankTitle(ranks);
}
}
@Override
public void setGuildEmblem(final int gid, final short bg, final byte bgcolor, final short logo, final byte logocolor) throws RemoteException {
final Guild guild = this.guilds.get(gid);
if (guild != null) {
guild.setGuildEmblem(logo, logocolor, bg, bgcolor);
}
}
@Override
public void disbandGuild(final int guildId) throws RemoteException {
this.guildMutex.lock();
try {
this.guilds.get(guildId).disbandGuild();
this.guilds.remove(guildId);
} finally {
this.guildMutex.unlock();
}
}
@Override
public final boolean increaseGuildCapacity(final int guildId) throws RemoteException {
final Guild guild = this.guilds.get(guildId);
if (guild != null) {
return guild.increaseCapacity();
}
return false;
}
@Override
public void gainGP(final int guildId, final int amount) throws RemoteException {
final Guild guild = this.guilds.get(guildId);
if (guild != null) {
guild.gainGuildPoints(amount);
}
}
@Override
public final Messenger createMessenger(final MessengerMember chrfor) throws RemoteException {
final int messengerid = this.runningMessengerId.getAndIncrement();
final Messenger messenger = new Messenger(messengerid, chrfor);
this.messengers.put(messenger.getId(), messenger);
return messenger;
}
@Override
public final Messenger getMessenger(final int messengerid) throws RemoteException {
return this.messengers.get(messengerid);
}
@Override
public final PlayerBuffStorage getPlayerBuffStorage() throws RemoteException {
return this.buffStorage;
}
}