package game.impl;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import game.ClientRemovedEvent;
import game.EventsListener;
import game.FireEvent;
import game.GameAdminEvent;
import game.GameCommands;
import game.GameEvent;
import game.GameListener;
import game.GameObjectLocation;
import game.NewClientEvent;
import game.ObjectMoveEvent;
import game.ObjectsUpdateEvent;
import game.RemoveClientEvent;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.serialization.ObjectDecoder;
import org.jboss.netty.handler.codec.serialization.ObjectEncoder;
public class NetworkGameControllerServer {
public final static int GAME_PORT = 9000;
private ExecutorService executor;
private SimpleGameController gameController;
private IncomingEventsDispatcher incomingEventsDispatcher;
private Channel serverChannel;
private EventsListener eventsListener;
public NetworkGameControllerServer() throws IOException {
executor = Executors.newCachedThreadPool();
gameController = new SimpleGameController();
executor.execute(gameController);
incomingEventsDispatcher = new IncomingEventsDispatcher();
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(executor, executor));
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new ObjectEncoder(), new ObjectDecoder(), new Handler());
}
});
serverChannel = bootstrap.bind(new InetSocketAddress(GAME_PORT));
}
public void shutdown() {
gameController.stop();
serverChannel.disconnect().awaitUninterruptibly();
serverChannel.close().awaitUninterruptibly();
serverChannel.unbind().awaitUninterruptibly();
executor.shutdownNow();
try {
executor.awaitTermination(60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
}
}
public void setEventsListener(EventsListener eventsListener) {
this.eventsListener = eventsListener;
}
class Handler extends SimpleChannelHandler {
Map<String, ClientWriteThread> channels = Collections.synchronizedMap(new HashMap<String, ClientWriteThread>());
List<ClientWriteThread> clients = Collections.synchronizedList(new ArrayList<ClientWriteThread>());
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Object message = e.getMessage();
if (message instanceof GameEvent) {
GameEvent gameEvent = (GameEvent) message;
ClientWriteThread writeThread = channels.get(gameEvent.getSource());
if (writeThread == null) {
synchronized (clients) {
for (int i = 0; i < clients.size(); i++) {
if (clients.get(i).getChannel() == ctx.getChannel()) {
channels.put(gameEvent.getSource(), clients.get(i));
clients.remove(i);
break;
}
}
}
}
incomingEventsDispatcher.dispatchEvent(gameEvent);
}
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
clients.add(new ClientWriteThread(ctx.getChannel()));
super.channelConnected(ctx, e);
}
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
synchronized (channels) {
Set<String> keys = channels.keySet();
for (String key : keys) {
if (channels.get(key).getChannel() == ctx.getChannel()) {
gameController.removeGameListener(channels.get(key));
gameController.onRemoveClient(new RemoveClientEvent(key));
channels.remove(key);
break;
}
}
}
super.channelDisconnected(ctx, e);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
e.getCause().printStackTrace();
super.exceptionCaught(ctx, e);
}
}
class ClientWriteThread implements GameListener {
private final Channel channel;
public ClientWriteThread(Channel channel) {
this.channel = channel;
gameController.addGameListener(this);
}
@Override
public void onObjectsUpdate(ObjectsUpdateEvent e) {
channel.write(e);
notifyEventsListener(e);
}
private void notifyEventsListener(GameEvent e) {
if (eventsListener != null) {
eventsListener.onOutgoingEvent(e);
}
}
@Override
public void onClientRemoved(ClientRemovedEvent e) {
channel.write(e);
}
public Channel getChannel() {
return channel;
}
}
class IncomingEventsDispatcher implements GameCommands {
public void dispatchEvent(GameEvent gameEvent) {
if (gameEvent instanceof NewClientEvent) {
onNewClient((NewClientEvent) gameEvent);
} else if (gameEvent instanceof GameAdminEvent) {
onGameAdmin((GameAdminEvent) gameEvent);
} else if (gameEvent instanceof ObjectMoveEvent) {
onUserControl((ObjectMoveEvent) gameEvent);
} else if (gameEvent instanceof FireEvent) {
onFire((FireEvent) gameEvent);
} else if (gameEvent instanceof RemoveClientEvent) {
onRemoveClient((RemoveClientEvent) gameEvent);
}
if (eventsListener != null) {
eventsListener.onIncomingEvent(gameEvent);
}
}
@Override
public void onNewClient(NewClientEvent e) {
gameController.onNewClient(e);
}
@Override
public void onRemoveClient(RemoveClientEvent e) {
gameController.onRemoveClient(e);
}
@Override
public void onGameAdmin(GameAdminEvent e) {
gameController.onGameAdmin(e);
}
@Override
public void onUserControl(ObjectMoveEvent e) {
gameController.onUserControl(e);
}
@Override
public void onFire(FireEvent e) {
gameController.onFire(e);
}
@Override
public Collection<GameObjectLocation> getAllObjects() {
return null; // TODO to be implemented
}
}
}