Fix login queue.

This commit is contained in:
Major-
2015-08-30 21:20:24 +01:00
parent ca36264b35
commit ec95fe9d61
5 changed files with 110 additions and 76 deletions
@@ -7,9 +7,9 @@ import com.google.common.base.Preconditions;
/**
* A {@link MobRepository} is a repository of {@link Mob}s that are currently active in the game world.
*
* @param <T> The type of Mob.
* @author Graham
* @author Ryley
* @param <T> The type of Mob.
*/
public final class MobRepository<T extends Mob> implements Iterable<T> {
@@ -95,10 +95,10 @@ public final class MobRepository<T extends Mob> implements Iterable<T> {
* Adds a Mob to the repository.
*
* @param mob The Mob to add.
* @return {@code true} if the Mob was added, {@code false} if the size has reached the capacity of this repository.
* @return {@code true} if the Mob was added, {@code false} if this MobRepository is at maximum capacity.
*/
public boolean add(T mob) {
if (size == capacity()) {
if (full()) {
return false;
}
@@ -126,6 +126,15 @@ public final class MobRepository<T extends Mob> implements Iterable<T> {
return mobs.length;
}
/**
* Returns whether or not this MobRepository has reached its maxmimum capacity.
*
* @return {@code true} iff this MobRepository is full.
*/
public boolean full() {
return size == mobs.length;
}
/**
* Gets the Mob at the given index.
*
@@ -447,25 +447,6 @@ public final class Player extends Mob {
public PrivilegeLevel getPrivilegeLevel() {
return privilegeLevel;
}
/**
* Determines the {@link RegistrationStatus} for this player. This method
* can remain lock-free since writes to the player {@link MobRepository} are
* only happening on the game thread.
*
* @return The status.
*/
public RegistrationStatus getRegistrationStatus() {
MobRepository<Player> repository = world.getPlayerRepository();
if (world.isPlayerOnline(getUsername())) {
return RegistrationStatus.ALREADY_ONLINE;
} else if (repository.capacity() == repository.size()) {
return RegistrationStatus.WORLD_FULL;
}
return RegistrationStatus.OK;
}
/**
* Gets the player's run energy.
*
@@ -19,7 +19,9 @@ import org.apollo.game.model.area.Region;
import org.apollo.game.model.entity.MobRepository;
import org.apollo.game.model.entity.Player;
import org.apollo.game.session.GameSession;
import org.apollo.game.session.LoginSession;
import org.apollo.game.sync.ClientSynchronizer;
import org.apollo.net.codec.login.LoginConstants;
import org.apollo.util.ThreadUtil;
import org.apollo.util.xml.XmlNode;
import org.apollo.util.xml.XmlParser;
@@ -32,6 +34,34 @@ import org.xml.sax.SAXException;
*/
public final class GameService extends Service {
/**
* A utility class wrapping a {@link Player} and their corresponding {@link LoginSession}.
*/
private static final class LoginPlayerRequest {
/**
* The Player.
*/
private final Player player;
/**
* The LoginSession.
*/
private final LoginSession session;
/**
* Creates the LoginPlayerRequest.
*
* @param player The {@link Player} logging in.
* @param session The {@link LoginSession} of the Player.
*/
public LoginPlayerRequest(Player player, LoginSession session) {
this.player = player;
this.session = session;
}
}
/**
* The amount of players to deregister per cycle. This is to ensure the saving threads don't get swamped with
* requests and slow everything down.
@@ -54,12 +84,12 @@ public final class GameService extends Service {
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(ThreadUtil.create("GameService"));
/**
* A queue of players to add.
* The Queue of LoginPlayers to add.
*/
private final Queue<Player> newPlayers = new ConcurrentLinkedQueue<>();
private final Queue<LoginPlayerRequest> newPlayers = new ConcurrentLinkedQueue<>();
/**
* A queue of players to remove.
* The Queue of Players to remove.
*/
private final Queue<Player> oldPlayers = new ConcurrentLinkedQueue<>();
@@ -138,13 +168,13 @@ public final class GameService extends Service {
}
/**
* Registers a player. Returns immediately. The player is registered at the
* start of the next cycle.
* Registers a {@link Player} at the end of the next cycle.
*
* @param player The player.
* @param player The Player to register.
* @param session the {@link LoginSession} of the Player.
*/
public void registerPlayer(Player player) {
newPlayers.add(player);
public void registerPlayer(Player player, LoginSession session) {
newPlayers.add(new LoginPlayerRequest(player, session));
}
/**
@@ -177,11 +207,20 @@ public final class GameService extends Service {
*/
private void finalizeRegistrations() {
for (int count = 0; count < REGISTRATIONS_PER_CYCLE; count++) {
Player player = newPlayers.poll();
if (player == null) {
LoginPlayerRequest request = newPlayers.poll();
if (request == null) {
break;
}
Player player = request.player;
if (world.isPlayerOnline(player.getUsername())) {
request.session.sendLoginFailure(LoginConstants.STATUS_ACCOUNT_ONLINE);
} else if (world.getPlayerRepository().full()) {
request.session.sendLoginFailure(LoginConstants.STATUS_SERVER_FULL);
} else {
request.session.sendLoginSuccess(player);
}
finalizePlayerRegistration(player);
}
}
@@ -31,10 +31,15 @@ import org.apollo.util.security.IsaacRandomPair;
public final class LoginSession extends Session {
/**
* The server context.
* The ServerContext.
*/
private final ServerContext context;
/**
* The LoginRequest for this LoginSession.
*/
private LoginRequest request;
/**
* Creates a login session for the specified channel.
*
@@ -58,52 +63,14 @@ public final class LoginSession extends Session {
* @param response The response.
*/
public void handlePlayerLoaderResponse(LoginRequest request, PlayerLoaderResponse response) {
this.request = request;
GameService service = context.getGameService();
Channel channel = getChannel();
Optional<Player> optional = response.getPlayer();
int status = response.getStatus(), rights = 0;
boolean flagged = false;
if (optional.isPresent()) {
Player player = optional.get();
rights = player.getPrivilegeLevel().toInteger();
RegistrationStatus registration = player.getRegistrationStatus();
if (registration != RegistrationStatus.OK) {
optional = Optional.empty();
rights = 0;
status = registration == RegistrationStatus.ALREADY_ONLINE ? LoginConstants.STATUS_ACCOUNT_ONLINE
: LoginConstants.STATUS_SERVER_FULL;
} else {
GameSession session = new GameSession(channel, context, player, request.isReconnecting());
channel.attr(ApolloHandler.SESSION_KEY).set(session);
player.setSession(session);
}
}
ChannelFuture future = channel.write(new LoginResponse(status, rights, flagged));
destroy();
if (optional.isPresent()) {
IsaacRandomPair randomPair = request.getRandomPair();
Release release = context.getRelease();
channel.pipeline().addFirst("messageEncoder", new GameMessageEncoder(release));
channel.pipeline().addBefore("messageEncoder", "gameEncoder", new GamePacketEncoder(randomPair.getEncodingRandom()));
channel.pipeline().addBefore("handler", "gameDecoder",
new GamePacketDecoder(randomPair.getDecodingRandom(), context.getRelease()));
channel.pipeline().addAfter("gameDecoder", "messageDecoder", new GameMessageDecoder(release));
channel.pipeline().remove("loginDecoder");
channel.pipeline().remove("loginEncoder");
service.registerPlayer(optional.get());
service.registerPlayer(optional.get(), this);
} else {
future.addListener(ChannelFutureListener.CLOSE);
sendLoginFailure(response.getStatus());
}
}
@@ -114,6 +81,46 @@ public final class LoginSession extends Session {
}
}
/**
* Sends a failed {@link LoginResponse} to the client.
*
* @param status The failure status.
*/
public void sendLoginFailure(int status) {
boolean flagged = false;
LoginResponse response = new LoginResponse(status, 0, flagged);
channel.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
/**
* Sends a succesfull {@link LoginResponse} to the client.
*
* @param player The {@link Player} that successfully logged in.
*/
public void sendLoginSuccess(Player player) {
IsaacRandomPair randomPair = request.getRandomPair();
boolean flagged = false;
GameSession session = new GameSession(channel, context, player, request.isReconnecting());
channel.attr(ApolloHandler.SESSION_KEY).set(session);
player.setSession(session);
int rights = player.getPrivilegeLevel().toInteger();
channel.writeAndFlush(new LoginResponse(LoginConstants.STATUS_OK, rights, flagged));
Release release = context.getRelease();
channel.pipeline().addFirst("messageEncoder", new GameMessageEncoder(release));
channel.pipeline().addBefore("messageEncoder", "gameEncoder", new GamePacketEncoder(randomPair.getEncodingRandom()));
channel.pipeline().addBefore("handler", "gameDecoder",
new GamePacketDecoder(randomPair.getDecodingRandom(), context.getRelease()));
channel.pipeline().addAfter("gameDecoder", "messageDecoder", new GameMessageDecoder(release));
channel.pipeline().remove("loginDecoder");
channel.pipeline().remove("loginEncoder");
}
/**
* Handles a login request.
*
@@ -213,8 +213,6 @@ public final class LoginDecoder extends StatefulFrameDecoder<LoginDecoderState>
private void writeResponseCode(ChannelHandlerContext ctx, int response) {
ByteBuf buffer = ctx.alloc().buffer(Byte.BYTES);
buffer.writeByte(response);
System.out.println("Sending response: " + response);
ctx.writeAndFlush(buffer).addListener(ChannelFutureListener.CLOSE);
}