Server instance moved to FControl,

HomeUI buttons get enabled/disabled to indicate server status
Server - clientstates form a chain of responsibility for packets processing
packets for autorization (no checks performed, used to get player's name)
This commit is contained in:
Maxmtg
2013-04-14 10:51:04 +00:00
parent 7f50bb3515
commit 3eed2f993b
25 changed files with 359 additions and 107 deletions

View File

@@ -46,6 +46,7 @@ import forge.gui.match.VMatchUI;
import forge.gui.match.controllers.CDock;
import forge.gui.toolbox.CardFaceSymbols;
import forge.gui.toolbox.FSkin;
import forge.net.NetServer;
import forge.properties.NewConstants;
import forge.quest.data.QuestPreferences.QPref;
import forge.quest.io.QuestDataIO;
@@ -316,4 +317,14 @@ public enum FControl {
public SoundSystem getSoundSystem() {
return soundSystem;
}
/**
* TODO: Write javadoc for this method.
* @return
*/
private final NetServer server = new NetServer();
public NetServer getServer() {
// TODO Auto-generated method stub
return server;
}
}

View File

@@ -8,7 +8,6 @@ import forge.gui.deckeditor.controllers.CEditorConstructed;
import forge.gui.framework.EDocID;
import forge.gui.framework.ICDoc;
import forge.gui.home.sanctioned.VSubmenuConstructed;
import forge.net.NetServer;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
@@ -56,7 +55,6 @@ public enum CHomeUI implements ICDoc {
/* (non-Javadoc)
* @see forge.view.home.ICDoc#intialize()
*/
private final NetServer server = new NetServer();
@SuppressWarnings("serial")
@Override
@@ -80,14 +78,18 @@ public enum CHomeUI implements ICDoc {
VHomeUI.SINGLETON_INSTANCE.getLblStartServer().setCommand(new Command() {
@Override
public void execute() {
server.listen();
FControl.SINGLETON_INSTANCE.getServer().listen();
VHomeUI.SINGLETON_INSTANCE.getLblStopServer().setEnabled(true);
VHomeUI.SINGLETON_INSTANCE.getLblStartServer().setEnabled(false);
}
});
VHomeUI.SINGLETON_INSTANCE.getLblStopServer().setCommand(new Command() {
@Override
public void execute() {
server.stop();
FControl.SINGLETON_INSTANCE.getServer().stop();
VHomeUI.SINGLETON_INSTANCE.getLblStopServer().setEnabled(false);
VHomeUI.SINGLETON_INSTANCE.getLblStartServer().setEnabled(true);
}
});
}

View File

@@ -59,7 +59,6 @@ import forge.gui.home.variant.VSubmenuPlanechase;
import forge.gui.home.variant.VSubmenuVanguard;
import forge.gui.toolbox.FLabel;
import forge.gui.toolbox.FSkin;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.view.FView;
@@ -162,6 +161,7 @@ public enum VHomeUI implements IVTopLevelUI {
if ( Singletons.getModel().getPreferences().getPrefBoolean(FPref.DEV_MODE_ENABLED) ) {
pnlButtons.add(lblStartServer, "w 170px!, h 25px!, gap 0 10px 10px 0, sx 2 ");
pnlButtons.add(lblStopServer, "w 50px!, h 25px!, gap 0 0 10px 0");
lblStopServer.setEnabled(false);
}
pnlMenu.add(pnlButtons, "w 230px!, gap 10px 0 10px 10px");

View File

@@ -7,6 +7,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
import javax.servlet.http.HttpServletRequest;
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;
@@ -27,6 +28,18 @@ public class NetServer {
private final Server srv = new Server();
private final Set<ClientSocket> _openSockets = new CopyOnWriteArraySet<ClientSocket>();
public NetServer() {
SelectChannelConnector connector= new SelectChannelConnector();
connector.setPort(81);
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
{
@@ -86,19 +99,10 @@ public class NetServer {
public void listen() {
if (!srv.isStarted())
{
SelectChannelConnector connector= new SelectChannelConnector();
connector.setPort(81);
srv.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
ServletHolder sh = new ServletHolder(new GameServlet());
context.addServlet(sh, "/*");
//context.setContextPath("/");
srv.setHandler(context);
URI serverUri = null;
try {
srv.start();
Connector connector = srv.getConnectors()[0];
int port = connector.getLocalPort();
String host = connector.getHost();
serverUri = new URI(String.format("ws://%s:%d/", host == null ? "localhost" : host ,port));

View File

@@ -1,6 +1,8 @@
package forge.net.client;
import forge.net.protocol.outcoming.Message;
import forge.game.player.LobbyPlayer;
import forge.net.client.state.IClientState;
import forge.net.protocol.outcoming.IMessage;
/**
* TODO: Write javadoc for this type.
@@ -12,6 +14,12 @@ public interface INetClient {
* TODO: Write javadoc for this method.
* @param echoMessage
*/
void send(Message echoMessage);
void send(IMessage message);
void setPlayer(LobbyPlayer lobbyPlayer);
LobbyPlayer getPlayer();
void replaceState(IClientState old, IClientState newState);
}

View File

@@ -1,27 +1,32 @@
package forge.net.client;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import forge.game.player.LobbyPlayer;
import forge.game.player.PlayerType;
import forge.net.IClientSocket;
import forge.net.IConnectionObserver;
import forge.net.client.state.ClientStateUnauthorized;
import forge.net.client.state.ConnectedClientState;
import forge.net.client.state.UnauthorizedClientState;
import forge.net.client.state.IClientState;
import forge.net.protocol.incoming.Packet;
import forge.net.protocol.incoming.PacketOpcode;
import forge.net.protocol.outcoming.Message;
import forge.net.protocol.outcoming.IMessage;
public class NetClient implements IConnectionObserver, INetClient{
private final IClientSocket socket;
private IClientState state = new ClientStateUnauthorized(this);
private BlockingDeque<IClientState> state = new LinkedBlockingDeque<IClientState>();
private LobbyPlayer player = null;
public NetClient(IClientSocket clientSocket) {
socket = clientSocket;
state.push(new ConnectedClientState(this));
state.push(new UnauthorizedClientState(this));
}
public void autorized() {
player = new LobbyPlayer(PlayerType.REMOTE, "Guest");
}
/* (non-Javadoc)
@@ -33,16 +38,42 @@ public class NetClient implements IConnectionObserver, INetClient{
}
@Override
public final LobbyPlayer getPlayer() {
return player;
}
/** Receives input from network client */
@Override
public void onMessage(String data) {
Packet p = PacketOpcode.decode(data);
state.onPacket(p);
for(IClientState s : state) {
if ( s.processPacket(p) )
break;
}
}
@Override
public void send(Message message) {
public void send(IMessage message) {
socket.send(message.toNetString());
}
/* (non-Javadoc)
* @see forge.net.client.INetClient#setPlayer(forge.game.player.LobbyPlayer)
*/
@Override
public final void setPlayer(LobbyPlayer lobbyPlayer) {
player = lobbyPlayer;
}
/* (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

@@ -1,37 +0,0 @@
package forge.net.client.state;
import forge.net.client.INetClient;
import forge.net.protocol.incoming.EchoPacket;
import forge.net.protocol.incoming.Packet;
import forge.net.protocol.outcoming.EchoMessage;
import forge.net.protocol.outcoming.UnknownPacketMessage;
/**
* TODO: Write javadoc for this type.
*
*/
public abstract class ClientState implements IClientState {
private final INetClient client;
protected ClientState(INetClient client)
{
this.client = client;
}
@Override
public void onPacket(Packet packet ) {
switch( packet.getOpCode() ) {
case Echo:
EchoPacket p = (EchoPacket)packet;
client.send(new EchoMessage(p.getMessage()));
break;
default:
client.send(new UnknownPacketMessage());
break;
}
}
}

View File

@@ -1,19 +0,0 @@
package forge.net.client.state;
import forge.net.client.INetClient;
/**
* TODO: Write javadoc for this type.
*
*/
public class ClientStateUnauthorized extends ClientState implements IClientState {
/**
* TODO: Write javadoc for Constructor.
* @param client
*/
public ClientStateUnauthorized(INetClient client) {
super(client);
}
}

View File

@@ -0,0 +1,40 @@
package forge.net.client.state;
import forge.net.client.INetClient;
import forge.net.protocol.incoming.EchoPacket;
import forge.net.protocol.incoming.IncorrectPacket;
import forge.net.protocol.incoming.Packet;
import forge.net.protocol.outcoming.EchoMessage;
import forge.net.protocol.outcoming.IncorrectPacketMessage;
import forge.net.protocol.outcoming.UnknownPacketMessage;
public class ConnectedClientState implements IClientState {
private final INetClient client;
public ConnectedClientState(INetClient client) {
this.client = client;
}
@Override
public boolean processPacket(Packet packet ) {
switch( packet.getOpCode() ) {
case Echo:
EchoPacket pe = (EchoPacket)packet;
client.send(new EchoMessage(pe.getMessage()));
return true;
case Incorrect:
IncorrectPacket pi = (IncorrectPacket)packet;
client.send(new IncorrectPacketMessage(pi));
return true;
default:
client.send(new UnknownPacketMessage());
return true;
}
}
}

View File

@@ -7,5 +7,5 @@ import forge.net.protocol.incoming.Packet;
*
*/
public interface IClientState {
void onPacket(Packet data);
boolean processPacket(Packet data);
}

View File

@@ -0,0 +1,21 @@
package forge.net.client.state;
import forge.net.client.INetClient;
import forge.net.protocol.incoming.Packet;
/**
* 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(Packet data) {
return false;
}
}

View File

@@ -0,0 +1,44 @@
package forge.net.client.state;
import forge.game.player.LobbyPlayer;
import forge.game.player.PlayerType;
import forge.net.client.INetClient;
import forge.net.protocol.incoming.AuthorizePacket;
import forge.net.protocol.incoming.Packet;
import forge.net.protocol.incoming.PacketOpcode;
import forge.net.protocol.outcoming.AuthorizationSuccessfulMessage;
/**
* 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(Packet packet) {
if( packet.getOpCode() == PacketOpcode.Authorize ) {
AuthorizePacket p = (AuthorizePacket)packet;
if( true ) { // check credentials here!
client.send(new AuthorizationSuccessfulMessage(p.getUsername()));
client.setPlayer(new LobbyPlayer(PlayerType.REMOTE, p.getUsername()));
client.replaceState(this, new InLobbyClientState(client));
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,46 @@
package forge.net.protocol.incoming;
import org.apache.commons.lang3.StringUtils;
import forge.util.TextUtil;
/**
* TODO: Write javadoc for this type.
*
*/
public class AuthorizePacket extends Packet {
private final String username;
private final String password;
private AuthorizePacket(String name, String pass) {
super(PacketOpcode.Authorize);
username = name;
password = pass;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public static Packet parse(String data) {
String[] parts = TextUtil.splitWithParenthesis(data, ' ', '\"', '\"');
if(parts.length == 1 || parts.length == 2) {
if(!StringUtils.isAlphanumericSpace(parts[0]))
return new IncorrectPacket(PacketOpcode.Authorize, 0, parts[0]);
if( parts.length == 1)
return new AuthorizePacket(parts[0], null);
if(!StringUtils.isAsciiPrintable(parts[1]))
return new IncorrectPacket(PacketOpcode.Authorize, 1, parts[1]);
else
return new AuthorizePacket(parts[0], parts[1]);
}
return UnknownPacket.parse(data);
}
}

View File

@@ -8,10 +8,15 @@ package forge.net.protocol.incoming;
public class EchoPacket extends Packet {
private final String message;
public EchoPacket(String data) {
private EchoPacket(String data) {
super(PacketOpcode.Echo);
message = data;
}
public static EchoPacket parse(String data) {
return new EchoPacket(data);
}
public String getMessage() {
return message;
}

View File

@@ -0,0 +1,32 @@
package forge.net.protocol.incoming;
/**
* TODO: Write javadoc for this type.
*
*/
public class IncorrectPacket extends Packet {
private final PacketOpcode intendedCode;
private final int index;
private final String sParam;
public IncorrectPacket(PacketOpcode code, int iParameter, String value) {
super(PacketOpcode.Incorrect);
intendedCode = code;
index = iParameter;
sParam = value;
}
public String getString() {
return sParam;
}
public int getIndex() {
return index;
}
public PacketOpcode getIntendedCode() {
return intendedCode;
}
}

View File

@@ -8,7 +8,10 @@ package forge.net.protocol.incoming;
public enum PacketOpcode {
Echo("/echo"),
Chat("/s"),
Unknown("");
Authorize("/auth"),
Incorrect(null),
Unknown(null);
@@ -18,6 +21,10 @@ public enum PacketOpcode {
opcode = code;
}
public final String getOpcode() {
return opcode;
}
/**
* TODO: Write javadoc for this method.
* @param data
@@ -25,11 +32,11 @@ public enum PacketOpcode {
*/
public static Packet decode(String data) {
for(PacketOpcode s : PacketOpcode.values()) {
if ( data.startsWith(s.opcode) )
if ( s.opcode != null && data.startsWith(s.opcode) )
return decodePacket(s, data.substring(s.opcode.length()).trim());
}
if( data.startsWith("/") )
return new UnknownPacket(data.substring(1));
return UnknownPacket.parse(data.substring(1));
else
return new ChatPacket(data);
}
@@ -38,9 +45,11 @@ public enum PacketOpcode {
private static Packet decodePacket(PacketOpcode code, String data) {
switch(code) {
case Echo:
return new EchoPacket(data);
return EchoPacket.parse(data);
case Authorize:
return AuthorizePacket.parse(data);
default:
return new UnknownPacket(data);
return UnknownPacket.parse(data);
}
}
}

View File

@@ -8,12 +8,20 @@ package forge.net.protocol.incoming;
public class UnknownPacket extends Packet {
private final String message;
public UnknownPacket(String data) {
private UnknownPacket(String data) {
super(PacketOpcode.Unknown);
message = data;
}
public String getMessage() {
return message;
}
/**
* TODO: Write javadoc for this method.
* @param substring
* @return
*/
public static Packet parse(String substring) {
return new UnknownPacket(substring);
}
}

View File

@@ -0,0 +1,21 @@
package forge.net.protocol.outcoming;
/**
* TODO: Write javadoc for this type.
*
*/
public class AuthorizationSuccessfulMessage implements IMessage {
private final String username;
public AuthorizationSuccessfulMessage(String user) {
username = user;
}
@Override
public String toNetString() {
// TODO Auto-generated method stub
return "Authorization Successful. Welcome, " + username;
}
}

View File

@@ -4,7 +4,7 @@ package forge.net.protocol.outcoming;
* TODO: Write javadoc for this type.
*
*/
public class EchoMessage extends Message {
public class EchoMessage implements IMessage {
private final String message;

View File

@@ -4,6 +4,6 @@ package forge.net.protocol.outcoming;
* TODO: Write javadoc for this type.
*
*/
public abstract class Message {
public abstract String toNetString();
public interface IMessage {
public String toNetString();
}

View File

@@ -0,0 +1,18 @@
package forge.net.protocol.outcoming;
import forge.net.protocol.incoming.IncorrectPacket;
public class IncorrectPacketMessage implements IMessage {
IncorrectPacket badPacket;
public IncorrectPacketMessage(IncorrectPacket packet) {
badPacket = packet;
}
@Override
public String toNetString() {
return String.format("Wrong syntax for %s command: parameter #%d is %s", badPacket.getIntendedCode().getOpcode(), 1+badPacket.getIndex(), badPacket.getString());
}
}

View File

@@ -4,7 +4,7 @@ package forge.net.protocol.outcoming;
* TODO: Write javadoc for this type.
*
*/
public class UnknownPacketMessage extends Message {
public class UnknownPacketMessage implements IMessage {
/* (non-Javadoc)
* @see forge.net.protocol.outcoming.Message#toNetString()

View File

@@ -7,6 +7,7 @@ import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
/**
* TODO: Write javadoc for this type.
@@ -58,14 +59,16 @@ public class TextUtil {
*/
public static String[] splitWithParenthesis(CharSequence input, char delimiter, int maxEntries, char openPar, char closePar, boolean skipEmpty) {
List<String> result = new ArrayList<String>();
// Assume that when equal non-zero parenthesis are passed, they need to be discarded
boolean trimParenthesis = openPar == closePar && openPar > 0;
int nPar = 0;
int len = input.length();
int start = 0;
int idx = 1;
for (int iC = 0; iC < len; iC++ ) {
char c = input.charAt(iC);
if( openPar > 0 && c == openPar ) nPar++;
if( closePar > 0 && c == closePar ) { nPar = nPar > 0 ? nPar - 1 : 0; }
if( closePar > 0 && c == closePar && nPar > 0 ) { nPar--; }
else if( openPar > 0 && c == openPar ) nPar++;
if( c == delimiter && nPar == 0 && idx < maxEntries) {
if( iC > start || !skipEmpty ) {
@@ -79,7 +82,8 @@ public class TextUtil {
if( len > start || !skipEmpty )
result.add(input.subSequence(start, len).toString());
return result.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
String[] toReturn = result.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
return trimParenthesis ? StringUtils.stripAll(toReturn, String.valueOf(openPar)) : toReturn;
}
/**