package net.scapeemulator.game.net.login; import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import net.scapeemulator.game.io.Serializer; import net.scapeemulator.game.io.Serializer.SerializeResult; import net.scapeemulator.game.model.MobList; import net.scapeemulator.game.model.World; import net.scapeemulator.game.model.player.Player; public final class LoginService implements Runnable { private static abstract class Job { public abstract void perform(LoginService service); } private static class LoginJob extends Job { private final LoginSession session; private final LoginRequest request; public LoginJob(LoginSession session, LoginRequest request) { this.session = session; this.request = request; } @Override public void perform(LoginService service) { SerializeResult result = service.serializer.loadPlayer(request.getUsername(), request.getPassword()); int status = result.getStatus(); if (status != LoginResponse.STATUS_OK) { /* send failure response immediately */ session.sendLoginFailure(status); } else { /* add to new player queue */ synchronized (service.newPlayers) { service.newPlayers.add(new SessionPlayerPair(session, result.getPlayer())); } } } } private static class LogoutJob extends Job { private final Player player; public LogoutJob(Player player) { this.player = player; } @Override public void perform(LoginService service) { service.serializer.savePlayer(player); player.unregister(); World.getWorld().getPlayers().remove(player); } } private static class SessionPlayerPair { private final LoginSession session; private final Player player; public SessionPlayerPair(LoginSession session, Player player) { this.session = session; this.player = player; } } private final Serializer serializer; private final BlockingQueue<Job> jobs = new LinkedBlockingDeque<>(); private final Queue<SessionPlayerPair> newPlayers = new ArrayDeque<>(); private final Queue<Player> oldPlayers = new ArrayDeque<>(); public LoginService(Serializer serializer) { this.serializer = serializer; } public void addLoginRequest(LoginSession session, LoginRequest request) { jobs.offer(new LoginJob(session, request)); } public void addLogoutRequest(Player player) { player.setSession(null); synchronized (oldPlayers) { oldPlayers.add(player); } } public void registerNewPlayers(World world) { MobList<Player> players = world.getPlayers(); synchronized (oldPlayers) { Player player; while ((player = oldPlayers.peek()) != null) { // TODO change this to check for logout conditions (combat) if (true) { oldPlayers.poll(); jobs.add(new LogoutJob(player)); } } } synchronized (newPlayers) { SessionPlayerPair pair; while ((pair = newPlayers.poll()) != null) { if (world.getPlayerByName(pair.player.getUsername()) != null) { /* player is already online */ pair.session.sendLoginFailure(LoginResponse.STATUS_ALREADY_ONLINE); } else if (!players.add(pair.player)) { /* world is full */ pair.session.sendLoginFailure(LoginResponse.STATUS_WORLD_FULL); } else { /* send success packet & switch to GameSession */ pair.session.sendLoginSuccess(LoginResponse.STATUS_OK, pair.player); } } } } @Override public void run() { for (;;) { try { jobs.take().perform(this); } catch (InterruptedException e) { /* ignore */ } } } }