Detach network code from game and gui.

This commit is contained in:
Maxmtg
2014-05-02 05:40:50 +00:00
parent 876d333498
commit 74f9a5f722
76 changed files with 552 additions and 448 deletions

View File

@@ -0,0 +1,25 @@
package forge.net;
public class FServer {
private FServer() {
}
private static Lobby lobby = new Lobby();
public static Lobby getLobby() {
return lobby;
}
/**
* TODO: Write javadoc for this method.
* @return
*/
private static final NetServer server = new NetServer();
public static NetServer getServer() {
// TODO Auto-generated method stub
return server;
}
}

View File

@@ -0,0 +1,7 @@
package forge.net;
public interface IClientSocket {
boolean isOpen();
void send(String message);
void close(String farewell);
}

View File

@@ -0,0 +1,9 @@
package forge.net;
public interface IConnectionObserver {
/** Notifies that the client is gone, it's too late to send anything */
public void onConnectionClosed();
/** Notifies of an incoming message */
public void onMessage(String data);
}

View File

@@ -0,0 +1,38 @@
package forge.net;
import forge.LobbyPlayer;
import forge.net.client.INetClient;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* TODO: Write javadoc for this type.
*
*/
public class Lobby {
private Map<String, LobbyPlayerRemote> remotePlayers = new ConcurrentHashMap<String, LobbyPlayerRemote>();
/**
* TODO: Write javadoc for this method.
* @param name
* @return
*/
public synchronized LobbyPlayer findOrCreateRemotePlayer(String name, INetClient client) {
if (remotePlayers.containsKey(name))
return remotePlayers.get(name);
LobbyPlayerRemote res = new LobbyPlayerRemote(name, client);
// speak(ChatArea.Room, system, res.getName() + " has joined the server.");
// have to load avatar from remote user's preferences here
remotePlayers.put(name, res);
return res;
}
public void disconnectPlayer(LobbyPlayer player) {
// Should set up a timer here to discard player and all of his games after 20 minutes of being offline
}
}

View File

@@ -0,0 +1,42 @@
package forge.net;
import forge.LobbyPlayer;
import forge.game.Game;
import forge.game.player.IGameEntitiesFactory;
import forge.game.player.Player;
import forge.game.player.PlayerController;
import forge.net.client.INetClient;
import forge.net.protocol.toclient.ChatPacketClt;
public class LobbyPlayerRemote extends LobbyPlayer implements IGameEntitiesFactory {
private final INetClient connection;
public LobbyPlayerRemote(String name, INetClient netClient) { // This is actually a doubtful idea - this means 1 window per player.
super(name);
connection = netClient;
}
/* (non-Javadoc)
* @see forge.game.player.LobbyPlayer#getPlayer(forge.game.GameState)
*/
@Override
public Player createIngamePlayer(Game gameState) {
// Cannot create remote players yet
throw new UnsupportedOperationException("method is not implemented");
}
@Override
public void hear(LobbyPlayer player, String message) {
connection.send(new ChatPacketClt(player.getName(), message));
}
/* (non-Javadoc)
* @see forge.game.player.LobbyPlayer#createControllerFor(forge.game.player.Player)
*/
@Override
public PlayerController createControllerFor(Player p) {
// Cannot create remote players yet
throw new UnsupportedOperationException("method is not implemented");
}
}

View File

@@ -0,0 +1,131 @@
package forge.net;
import forge.net.client.NetClient;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URI;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public class NetServer {
private final Server srv = new Server();
private final Set<ClientSocket> _openSockets = new CopyOnWriteArraySet<ClientSocket>();
public int portNumber;
public final int getPortNumber() {
return portNumber;
}
public NetServer() {
SelectChannelConnector connector= new SelectChannelConnector();
connector.setMaxIdleTime(1200000); // 20 minutes
srv.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
ServletHolder sh = new ServletHolder(new GameServlet());
context.addServlet(sh, "/*");
//context.setContextPath("/");
srv.setHandler(context);
}
@SuppressWarnings("serial")
public class GameServlet extends WebSocketServlet
{
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
System.out.printf("Connection from %s recieved%n", request.getRemoteAddr());
return new ClientSocket();
}
}
public class ClientSocket implements WebSocket.OnTextMessage, IClientSocket
{
private Connection _connection;
private IConnectionObserver _client;
@Override
public void onClose(int closeCode, String message) {
System.out.println("Lost connection: " + closeCode + ", " + message);
_openSockets.remove(_client);
_client.onConnectionClosed();
}
public void send(String data) {
try {
_connection.sendMessage(data);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void onMessage(String data) {
_client.onMessage(data);
}
public boolean isOpen() {
return _connection.isOpen();
}
@Override
public void onOpen(Connection connection) {
_connection = connection;
_openSockets.add(this);
_client = new NetClient(this);
}
@Override
public void close(String farewell) {
_connection.close(1000, farewell);
}
}
public void listen(int port) {
if (!srv.isStarted())
{
portNumber = port;
URI serverUri = null;
try {
Connector connector = srv.getConnectors()[0];
connector.setPort(portNumber);
srv.start();
String host = connector.getHost();
serverUri = new URI(String.format("ws://%s:%d/", host == null ? "localhost" : host ,port));
} catch (Exception e) {
throw new RuntimeException(e);
}
System.out.println("Server started @ " + serverUri);
}
else {
System.out.println("Server was already started");
}
}
public void stop() {
try {
srv.stop();
portNumber = -1;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,25 @@
package forge.net.client;
import forge.LobbyPlayer;
import forge.net.client.state.IClientState;
import forge.net.protocol.toclient.IPacketClt;
/**
* TODO: Write javadoc for this type.
*
*/
public interface INetClient {
/**
* TODO: Write javadoc for this method.
* @param echoMessage
*/
void send(IPacketClt message);
void createPlayer(String playerName);
LobbyPlayer getPlayer();
void replaceState(IClientState old, IClientState newState);
}

View File

@@ -0,0 +1,29 @@
package forge.net.client;
/**
* Indicates incorrect field in an incoming packet
*/
public class InvalidFieldInPacketException extends RuntimeException {
private static final long serialVersionUID = 4505312413627923468L;
/**
* TODO: Write javadoc for Constructor.
* @param message
*/
public InvalidFieldInPacketException(String message) {
super(message);
}
/**
* TODO: Write javadoc for Constructor.
* @param message
* @param cause
*/
public InvalidFieldInPacketException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,98 @@
package forge.net.client;
import forge.LobbyPlayer;
import forge.net.FServer;
import forge.net.IClientSocket;
import forge.net.IConnectionObserver;
import forge.net.client.state.ConnectedClientState;
import forge.net.client.state.IClientState;
import forge.net.client.state.UnauthorizedClientState;
import forge.net.protocol.ClientProtocol;
import forge.net.protocol.ClientProtocolJson;
import forge.net.protocol.toclient.ErrorIncorrectPacketClt;
import forge.net.protocol.toclient.ErrorNoStateForPacketClt;
import forge.net.protocol.toclient.IPacketClt;
import forge.net.protocol.toclient.WelcomePacketClt;
import forge.net.protocol.toserver.IPacketSrv;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
public class NetClient implements IConnectionObserver, INetClient{
private final IClientSocket socket;
private final BlockingDeque<IClientState> state = new LinkedBlockingDeque<IClientState>();
private LobbyPlayer player = null;
private final ClientProtocol<IPacketSrv, IPacketClt> protocol;
public NetClient(IClientSocket clientSocket) {
socket = clientSocket;
state.push(new ConnectedClientState(this));
state.push(new UnauthorizedClientState(this));
protocol = new ClientProtocolJson();
send(new WelcomePacketClt("Welcome to server"));
}
public void autorized() {
}
/* (non-Javadoc)
* @see forge.net.client.IConnectionObserver#onConnectionClosed()
*/
@Override
public void onConnectionClosed() {
// Tell the game, the client is gone.
if ( player != null ) {
FServer.getLobby().disconnectPlayer(player);
}
}
@Override
public final LobbyPlayer getPlayer() {
return player;
}
/** Receives input from network client */
@Override
public void onMessage(String data) {
IPacketSrv p = protocol.decodePacket(data);
boolean processed = false;
try{
for(IClientState s : state) {
if ( s.processPacket(p) ) { processed = true; break; }
}
if (!processed)
send(new ErrorNoStateForPacketClt(p.getClass().getSimpleName()));
} catch ( InvalidFieldInPacketException ex ) {
send(new ErrorIncorrectPacketClt(ex.getMessage()));
}
}
@Override
public void send(IPacketClt message) {
String rawData = protocol.encodePacket(message);
socket.send(rawData);
}
/* (non-Javadoc)
* @see forge.net.client.INetClient#setPlayer(forge.game.player.LobbyPlayer)
*/
@Override
public final void createPlayer(String name) {
player = FServer.getLobby().findOrCreateRemotePlayer(name, this);
}
/* (non-Javadoc)
* @see forge.net.client.INetClient#replaceState(forge.net.client.state.IClientState, forge.net.client.state.IClientState)
*/
@Override
public synchronized void replaceState(IClientState old, IClientState newState) {
state.removeFirstOccurrence(old);
state.push(newState);
}
}

View File

@@ -0,0 +1,3 @@
/** Model (as in model-view-controller) for Forge. */
package forge.net.client;

View File

@@ -0,0 +1,33 @@
package forge.net.client.state;
import forge.net.client.INetClient;
import forge.net.protocol.toclient.EchoPacketClt;
import forge.net.protocol.toclient.ErrorIncorrectPacketClt;
import forge.net.protocol.toserver.EchoPacketSrv;
import forge.net.protocol.toserver.IPacketSrv;
import forge.net.protocol.toserver.IncorrectPacketSrv;
public class ConnectedClientState implements IClientState {
private final INetClient client;
public ConnectedClientState(INetClient client) {
this.client = client;
}
@Override
public boolean processPacket(IPacketSrv packet ) {
if( packet instanceof EchoPacketSrv) {
client.send(new EchoPacketClt(((EchoPacketSrv)packet).getMessage()));
return true;
}
if( packet instanceof IncorrectPacketSrv) {
client.send(new ErrorIncorrectPacketClt(((IncorrectPacketSrv)packet).getMessage()));
return true;
}
return false;
}
}

View File

@@ -0,0 +1,11 @@
package forge.net.client.state;
import forge.net.protocol.toserver.IPacketSrv;
/**
* TODO: Write javadoc for this type.
*
*/
public interface IClientState {
boolean processPacket(IPacketSrv data);
}

View File

@@ -0,0 +1,30 @@
package forge.net.client.state;
import forge.net.client.INetClient;
import forge.net.protocol.toserver.ChatPacketSrv;
import forge.net.protocol.toserver.IPacketSrv;
/**
* TODO: Write javadoc for this type.
*
*/
public class InLobbyClientState implements IClientState {
private final INetClient client;
protected InLobbyClientState(INetClient client) {
this.client = client;
}
@Override
public boolean processPacket(IPacketSrv data) {
if( data instanceof ChatPacketSrv) {
ChatPacketSrv cp = (ChatPacketSrv) data;
// if ( not muted )
// FServer.getLobby().speak(ChatArea.Room, client.getPlayer(), cp.getMessage());
// else
// client.send("You are banned and cannot speak");
return true;
}
return false;
}
}

View File

@@ -0,0 +1,47 @@
package forge.net.client.state;
import forge.net.client.INetClient;
import forge.net.client.InvalidFieldInPacketException;
import forge.net.protocol.toclient.AuthResultPacketClt;
import forge.net.protocol.toserver.AuthorizePacketSrv;
import forge.net.protocol.toserver.IPacketSrv;
import org.apache.commons.lang3.StringUtils;
/**
* TODO: Write javadoc for this type.
*
*/
public class UnauthorizedClientState implements IClientState {
/**
* TODO: Write javadoc for Constructor.
* @param client
*/
private final INetClient client;
public UnauthorizedClientState(INetClient client) {
this.client = client;
}
@Override
public boolean processPacket(IPacketSrv packet) {
if( packet instanceof AuthorizePacketSrv ) {
AuthorizePacketSrv p = (AuthorizePacketSrv)packet;
if( StringUtils.isBlank(p.getUsername()))
throw new InvalidFieldInPacketException("username is blank");
// check credentials here!
client.createPlayer(p.getUsername());
client.send(new AuthResultPacketClt(client.getPlayer().getName(), true));
client.replaceState(this, new InLobbyClientState(client));
return true;
}
return false;
}
}

View File

@@ -0,0 +1,3 @@
/** Model (as in model-view-controller) for Forge. */
package forge.net.client.state;

View File

@@ -0,0 +1,3 @@
/** Model (as in model-view-controller) for Forge. */
package forge.net;