/*
* Copyright (c) 2009, Hamish Morgan. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the University of Sussex nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package locusts.server;
import java.awt.geom.Area;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import locusts.client.ClientConfig;
import locusts.common.Constants;
import locusts.common.Player;
import locusts.common.entities.CollidableEntity;
import locusts.common.entities.Entity;
import locusts.common.net.FullUpdateRequest;
import locusts.common.net.InputMessage;
import locusts.common.net.PlayerMessage;
import locusts.common.net.PlayerMessage.NewPlayerRequest;
import locusts.common.net.PlayerMessage.NewPlayerResponse;
import locusts.common.net.PlayerMessage.PlayerDetailsUpdate;
import locusts.common.net.WorldUpdateMessage;
import locusts.common.vibro.VTAccumulator;
import locusts.common.vibro.VTMessage;
import org.apache.mina.common.IoSession;
import org.apache.mina.handler.demux.MessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Hamish Morgan
*/
public class Server implements GameListener {
private static final Logger LOG = LoggerFactory.getLogger(Server.class);
private final Game game;
private final ServerNetHandler netHandler;
private VTMessage vtmessage = null;
private Map<IoSession, ClientConfig> clientConfigs;
public Server(Game g, int port) throws IOException {
clientConfigs = new HashMap<IoSession, ClientConfig>();
this.game = g;
this.game.addGameListener(this);
this.netHandler = new ServerNetHandler(port);
netHandler.addMessageHandler(InputMessage.class,
game.getController().inputHandler);
netHandler.addMessageHandler(PlayerMessage.class,
new PlayerMessageHandler());
// netHandler.addMessageHandler(Player.class, new MessageHandler<Player>() {
//
// public void messageReceived(IoSession session, Player e)
// throws Exception {
// Player p = new Player(e.getName(), e.getScore(), e.getCrops());
// LOG.info("Player joined: {}", e);
// game.addPlayer(e);
// session.write(p);
// session.setAttribute("Player Data Sent");
// }
// });
netHandler.addMessageHandler(FullUpdateRequest.class,
new FullUpdateRequestHandler());
netHandler.addMessageHandler(ClientConfig.class,
new ClientConfigHandler());
netHandler.start();
game.getPlayers().addListDataListener(new ListDataListener() {
public void intervalAdded(ListDataEvent e) {
netHandler.broadcast(game.getPlayers());
}
public void intervalRemoved(ListDataEvent e) {
netHandler.broadcast(game.getPlayers());
}
public void contentsChanged(ListDataEvent e) {
netHandler.broadcast(game.getPlayers());
}
});
}
public Map<IoSession, ClientConfig> getClientConfigs() {
return clientConfigs;
}
private class FullUpdateRequestHandler implements
MessageHandler<FullUpdateRequest> {
public void messageReceived(IoSession session, FullUpdateRequest e)
throws Exception {
LOG.info("Full Update Requested: {}", e);
session.write(WorldUpdateMessage.createFrom(game.getWorld(),
true));
}
}
private class ClientConfigHandler implements MessageHandler<ClientConfig> {
public void messageReceived(IoSession session, ClientConfig message)
throws Exception {
if (clientConfigs.containsKey(session)) {
final ClientConfig current = clientConfigs.get(session);
current.set(message);
current.addChangeListener(new ClientConfigChangeHandler(
session));
} else {
clientConfigs.put(session, message);
message.addChangeListener(new ClientConfigChangeHandler(
session));
}
}
public class ClientConfigChangeHandler implements ChangeListener {
private IoSession session;
public ClientConfigChangeHandler(IoSession session) {
this.session = session;
}
public void stateChanged(ChangeEvent e) {
// System.out.println("Sending state change update.");
// System.out.println(e.getSource().getClass());
// System.out.println(
// "fireStateChangeEvent stateChanged ========================================");
netHandler.broadcast(e.getSource());
// session.write(e.getSource());
}
}
}
private class PlayerMessageHandler implements
MessageHandler<PlayerMessage> {
public void messageReceived(IoSession session, PlayerMessage message)
throws Exception {
if (message.getClass().equals(NewPlayerRequest.class)) {
handleMessage(session, (NewPlayerRequest) message);
} else if (message.getClass().equals(NewPlayerResponse.class)) {
handleMessage(session, (NewPlayerResponse) message);
} else if (message.getClass().equals(PlayerDetailsUpdate.class)) {
handleMessage(session, (PlayerDetailsUpdate) message);
}
// throw new UnsupportedOperationException("Not supported yet.");
}
public void handleMessage(IoSession session, NewPlayerRequest message) {
Player p = new Player();
LOG.info("Player joined: {}", p);
game.addPlayer(p);
session.write(new NewPlayerResponse(message.getLocalId(), p));
// session.write(p);
// session.setAttribute("Player Data Sent");
}
public void handleMessage(IoSession session, NewPlayerResponse message) {
// should not be recieved by a server ever!
}
public void handleMessage(IoSession session,
PlayerDetailsUpdate message) {
List<Player> players = game.getPlayers();
for (Player player : players) {
if (player.getId() == message.getPlayer().getId()) {
message.applyTo(player);
return;
}
}
LOG.warn("Player not found for update message.");
}
}
public Game getGame() {
return game;
}
public ServerNetHandler getNetHandler() {
return netHandler;
}
public void gameUpdated(Game game) {
netHandler.broadcast(
WorldUpdateMessage.createFrom(game.getWorld(), false));
for (Player player : game.getPlayers()) {
if (!player.isModified())
continue;
netHandler.broadcast(new PlayerDetailsUpdate(player));
player.setModified(false);
}
final VTMessage m = makeVTMessage(game);
if (!m.equals(vtmessage)) {
netHandler.broadcast(m);
vtmessage = m;
}
}
private static VTMessage makeVTMessage(Game game) {
// create a accumulator for quantising the input entities
VTAccumulator vta = new VTAccumulator(4, 4, game.getWorld().getWidth(), game.getWorld().
getHeight());
int downId =
game.getEntityTypes().getName(Constants.MOUSE_DOWN).getId();
int moveId =
game.getEntityTypes().getName(Constants.MOUSE_MOVE).getId();
final long time = System.currentTimeMillis();
final long minAge = 500;
for (Entity e : game.getEntities()) {
if (e.getTypeId() != downId && e.getTypeId() != moveId)
continue;
if (e.getCreationTime() + minAge < time)
continue;
final
Area area;
if (e instanceof CollidableEntity)
area = ((CollidableEntity) e).geTransformedArea();
else
area = game.getEntityData().get(e).getArea().
createTransformedArea(e.getAffineTransform());
vta.add(area);
}
VTMessage m = VTMessage.createFrom(vta);
return m;
}
public void playerJoined(Game game, Player player) {
netHandler.broadcast(WorldUpdateMessage.createFrom(game.getWorld(),
true));
// throw new UnsupportedOperationException("Not supported yet.");
}
public void playerLeft(Game game, Player player) {
netHandler.broadcast(WorldUpdateMessage.createFrom(game.getWorld(),
true));
// throw new UnsupportedOperationException("Not supported yet.");
}
public void gameEntitiesAdded(Game game) {
// throw new UnsupportedOperationException("Not supported yet.");
}
public void gameEntitiesRemoved(Game game) {
// throw new UnsupportedOperationException("Not supported yet.");
}
}
|