mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +00:00
Detach network code from game and gui.
This commit is contained in:
25
forge-net/src/main/java/forge/net/FServer.java
Normal file
25
forge-net/src/main/java/forge/net/FServer.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
7
forge-net/src/main/java/forge/net/IClientSocket.java
Normal file
7
forge-net/src/main/java/forge/net/IClientSocket.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package forge.net;
|
||||
|
||||
public interface IClientSocket {
|
||||
boolean isOpen();
|
||||
void send(String message);
|
||||
void close(String farewell);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
38
forge-net/src/main/java/forge/net/Lobby.java
Normal file
38
forge-net/src/main/java/forge/net/Lobby.java
Normal 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
|
||||
}
|
||||
}
|
||||
42
forge-net/src/main/java/forge/net/LobbyPlayerRemote.java
Normal file
42
forge-net/src/main/java/forge/net/LobbyPlayerRemote.java
Normal 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");
|
||||
}
|
||||
}
|
||||
131
forge-net/src/main/java/forge/net/NetServer.java
Normal file
131
forge-net/src/main/java/forge/net/NetServer.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
25
forge-net/src/main/java/forge/net/client/INetClient.java
Normal file
25
forge-net/src/main/java/forge/net/client/INetClient.java
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
98
forge-net/src/main/java/forge/net/client/NetClient.java
Normal file
98
forge-net/src/main/java/forge/net/client/NetClient.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
/** Model (as in model-view-controller) for Forge. */
|
||||
package forge.net.client;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
/** Model (as in model-view-controller) for Forge. */
|
||||
package forge.net.client.state;
|
||||
|
||||
3
forge-net/src/main/java/forge/net/package-info.java
Normal file
3
forge-net/src/main/java/forge/net/package-info.java
Normal file
@@ -0,0 +1,3 @@
|
||||
/** Model (as in model-view-controller) for Forge. */
|
||||
package forge.net;
|
||||
|
||||
Reference in New Issue
Block a user