package robombs.game; import java.util.HashSet; import java.util.List; import java.util.Set; import robombs.clientserver.ClientInfo; import robombs.clientserver.DataContainer; import robombs.clientserver.NetGlobals; import robombs.clientserver.NetLogger; import robombs.game.model.LocalBombManager; import robombs.game.model.LocalObject; import robombs.game.model.PlayerInfo; import robombs.game.model.ServerObjectManager; import robombs.game.model.Types; import com.threed.jpct.SimpleVector; /** * A helper class for the server to ease event processing. An event is send from * the client to the server and processed there. This is what this class does. */ public class ServerEventManager { private volatile boolean threadRunning = false; private volatile boolean drawDetectionDone = false; private Set<Integer> collectedItems = new HashSet<Integer>(); /** * Manages an event. * * @param event * Event the event to manage * @param servMan * ServerObjectManager the server object manager that lives on * this server * @param server * BlueThunderServer the actual server instance * @param ci * ClientInfo the client info of the client from which the event * came from * @return DataContainer[] an optional response. May be null (and usually is * in the current implementation). */ public DataContainer[] manageEvent(Event event, ServerObjectManager servMan, BlueThunderServer server, ClientInfo ci) { switch (event.getType()) { case Event.BOMB_OVERLOADED: return processBombOverload(event, servMan, server); case Event.EXPLOSION_HIT: return processExplosionHit(event, servMan, server); case Event.CRATE_HIT: return processCrateHit(event, servMan, server); case Event.BOMB_ITEM_COLLECTED: return processBombItem(event, servMan, server); case Event.FIREPOWER_ITEM_COLLECTED: return processFirepowerItem(event, servMan, server); case Event.KICK_ITEM_COLLECTED: return processKickItem(event, servMan, server); case Event.DISEASE_ITEM_COLLECTED: return processDiseaseItem(event, servMan, server); case Event.ENTITY_REMOVE: return processEntityRemoval(event, servMan, ci, server); case Event.FIRE: return processFire(event, server); case Event.PLACE_BOMB: return processBomb(event, server); case Event.BOMB_DISABLED: return processBombDisabled(event, servMan, server); case Event.BOMB_TOUCHED: return processBombKicked(event, servMan, server); case Event.PLAYER_RESPAWNED: return processRespawn(event, servMan, server); case Event.PLAYER_READY: case Event.PLAYER_NOT_READY: return processPlayerState(event, server); case Event.LEVEL_LOADED: return processLoadingState(event, server); case Event.NEXT_LEVEL_REQUEST: return processLevelRequest(event, server); case Event.ROUND_LOST: return processRoundLost(event, servMan, server); case Event.TAUNT: return processTaunt(event, server); case Event.TEAM_SET: return processTeamSet(event, server); default: return null; } } private DataContainer[] processTeamSet(Event event, BlueThunderServer server) { // *shiver* SimpleVector abusedData=event.getOrigin(); int team=(int) abusedData.x; int cid=(int) abusedData.y; int oid=(int) abusedData.z; InfoDataContainer idc = new InfoDataContainer(); InfoLine il = new InfoLine(InfoLine.TEAM_ASSIGNED, team, String.valueOf(cid), String.valueOf(oid)); idc.add(il); server.broadcast(idc); return null; } public synchronized void clearCollectedItems() { collectedItems.clear(); } public synchronized void revivePlayers(BlueThunderServer server) { List<PlayerInfo> pis = server.getPlayers(); for (PlayerInfo pi : pis) { pi.revive(); } } private int getAliveCount(BlueThunderServer server) { List<PlayerInfo> pis = server.getPlayers(); int aliveCnt = 0; for (PlayerInfo pi : pis) { if (!pi.isDead()) { aliveCnt++; } } return aliveCnt; } public synchronized DataContainer[] processRoundLost(final Event event, ServerObjectManager servMan, final BlueThunderServer server) { PlayerInfo sip = server.getPlayerInfo(event.getSourceClientID(), event.getSourceID()); sip.kill(); final List<PlayerInfo> pis = server.getPlayers(); int aliveCnt = 0; int botCnt = 0; PlayerInfo alive = null; // System.out.println(sip.getName()+" is dead!"); int[] teamAliveCount = new int[5]; for (PlayerInfo pi : pis) { // System.out.println(pi.getClientID()+"/"+pi.getName()+"/"+pi.isDead()); if (!pi.isDead()) { teamAliveCount[TeamAssigner.getTeam(pi)]++; aliveCnt++; alive = pi; if (pi.isBot()) { botCnt++; } } } boolean onlyOneTeamLeft = false; int teamCount = 0; int team = 0; for (int i = 0; i < teamAliveCount.length; i++) { if (teamAliveCount[i] != 0) { teamCount++; team = i; } } String value = ""; if (teamCount == 1 && teamAliveCount[0] == 0) { onlyOneTeamLeft = true; value = "Team " + team; } if (aliveCnt <= 1 || onlyOneTeamLeft && !threadRunning) { // Level ended! threadRunning = true; final PlayerInfo winner = alive; final boolean onlyOneTeamLeftf = onlyOneTeamLeft; final String valuef = value; final int teamf = team; new Thread() { // Start a new thread that sends a new event after some time has // passed. public void run() { try { Thread.sleep(300); // Sleep some time to detect a draw! PlayerInfo win = winner; if (getAliveCount(server) == 0) { // Somehow the winner got lost on the way? win = null; } // After this, no event shall kill the player anymore... drawDetectionDone = true; String name = ""; if (win != null) { name = win.getName(); win.won(); } for (PlayerInfo pi : pis) { if (pi != win) { if (onlyOneTeamLeftf && TeamAssigner.getTeam(pi) == teamf) { pi.won(); } else { pi.lost(); } } } InfoDataContainer dc = new InfoDataContainer(); InfoLine il = new InfoLine(InfoLine.ROUND_COMPLETED, onlyOneTeamLeftf ? teamf : 0, name, valuef); dc.add(il); server.broadcast(dc); InfoDataContainer idc = new InfoDataContainer(); server.addScores(idc); server.broadcast(idc); Thread.sleep(Globals.ROUND_COMPLETED_TIME); // Wait... processLevelRequest(event, server); // Send a // "next-level-event" Thread.sleep(300); // Give some time for the client to // get the event! } catch (Exception e) { NetLogger.log("'Round lost' processing failed!!!"); e.printStackTrace(); } threadRunning = false; drawDetectionDone = false; } }.start(); } else { if (aliveCnt == botCnt && !threadRunning && botCnt != 0) { // Only bots have survived? Accerate the game...:-) EventDataContainer edc = new EventDataContainer(); LocalObject target = servMan.getLocalObjectToIDs(event.getTargetID(), event.getTargetClientID()); Event ev = new Event(Event.ONLY_BOTS_LEFT, null, target); // Actually, // the // target // doesn't // matter... edc.add(ev); server.broadcast(edc); } } return null; } public DataContainer[] processLoadingState(Event event, BlueThunderServer server) { server.setState(event.getSourceClientID(), NetState.STATE_LEVEL_LOADED); if (server.getClientCount() == server.getStateCount(NetState.STATE_LEVEL_LOADED)) { server.reset(); // All done? Then start the game... NetLogger.log("Server: All " + server.getClientCount() + " clients are ready to go!"); InfoDataContainer dc = new InfoDataContainer(); InfoLine il = new InfoLine(InfoLine.ALL_CLIENTS_READY, 0, "playerCount", Integer.toString(server.getPlayers().size())); dc.add(il); server.broadcast(dc); server.setTimeOut(NetGlobals.serverTimeOut); clearCollectedItems(); revivePlayers(server); } else { NetLogger.log("Server: Waiting for " + (server.getClientCount() - server.getStateCount(NetState.STATE_LEVEL_LOADED)) + " to complete!"); } return null; } public DataContainer[] processPlayerState(Event event, BlueThunderServer server) { if (event.getType() == Event.PLAYER_READY) { server.setState(event.getSourceClientID(), NetState.STATE_READY); readyPlayerForClient(event.getSourceClientID(), true, server); } if (event.getType() == Event.PLAYER_NOT_READY) { server.setState(event.getSourceClientID(), NetState.STATE_CONNECTED); readyPlayerForClient(event.getSourceClientID(), false, server); } return null; } private void readyPlayerForClient(int cid, boolean ready, BlueThunderServer server) { List<PlayerInfo> pis = server.getPlayerInfo(cid); if (pis != null) { for (PlayerInfo pi : pis) { if (pi != null) { pi.ready(ready); } } } } public DataContainer[] processLevelRequest(Event event, BlueThunderServer server) { server.setStateForAll(NetState.STATE_CONNECTED); NetLogger.log("Server: Sending 'next level signal' to all clients!"); InfoDataContainer dc = new InfoDataContainer(); InfoLine il = new InfoLine(InfoLine.NEXT_LEVEL, 0, "", ""); dc.add(il); server.broadcast(dc); server.reset(); return null; } /** * Process a players respawn. * * @param event * Event the event * @param servMan * ServerObjectManager the server object manager that lives on * this server * @param server * BlueThunderServer the actual server instance * @return DataContainer[] an optional response. May be null. */ private DataContainer[] processRespawn(Event event, ServerObjectManager servMan, BlueThunderServer server) { LocalObject source = servMan.getLocalObjectToIDs(event.getSourceID(), event.getSourceClientID()); if (source != null) { source.setValue(100); // Health to max! EventDataContainer edc = new EventDataContainer(); Event ev = new Event(Event.PLAYER_RESET_HEALTH, source, source); ev.setOrigin(event.getOrigin()); edc.add(ev); server.broadcast(edc); PlayerInfo sip = server.getPlayerInfo(event.getSourceClientID(), event.getSourceID()); InfoDataContainer idc = new InfoDataContainer(); InfoLine il = new InfoLine(InfoLine.SYSTEM_OUT, 0, "msg", "'" + sip.getName() + "' has spawned!"); idc.add(il); server.broadcast(idc); } else { NetLogger.log("Server: Unable to find local object for respawn: " + event); } return null; } private DataContainer[] processTaunt(Event event, BlueThunderServer server) { EventDataContainer edc = new EventDataContainer(); edc.add(event); server.broadcast(edc); return null; } /** * Process firing. * * @param event * Event the event * @param server * BlueThunderServer the actual server instance * @return DataContainer[] an optional response. May be null. */ private DataContainer[] processFire(Event event, BlueThunderServer server) { EventDataContainer edc = new EventDataContainer(); edc.add(event); server.broadcast(edc); return null; } /** * Process bombing. * * @param event * Event the event * @param server * BlueThunderServer the actual server instance * @return DataContainer[] an optional response. May be null. */ private DataContainer[] processBomb(Event event, BlueThunderServer server) { EventDataContainer edc = new EventDataContainer(); edc.add(event); server.broadcast(edc); return null; } private DataContainer[] processBombDisabled(Event event, ServerObjectManager servMan, BlueThunderServer server) { EventDataContainer edc = new EventDataContainer(); edc.add(event); server.broadcast(edc); return null; } private DataContainer[] processBombKicked(Event event, ServerObjectManager servMan, BlueThunderServer server) { EventDataContainer edc = new EventDataContainer(); edc.add(event); server.broadcast(edc); return null; } /** * Process a client's request to remove an entity. * * @param event * Event the event * @param ci * ClientInfo the client info * @param server * BlueThunderServer the server instance * @return DataContainer[] an optional response. May be null. */ private DataContainer[] processEntityRemoval(Event event, ServerObjectManager servMan, ClientInfo ci, BlueThunderServer server) { LocalObject source = servMan.getLocalObjectToIDs(event.getSourceID(), event.getSourceClientID()); if (source != null && source.getType() == Types.BOMB) { EventDataContainer edc = new EventDataContainer(); edc.add(event); server.broadcast(edc); } server.remove(event.getSourceID(), event.getSourceClientID(), ci); return null; } private DataContainer[] processExplosionHit(Event event, ServerObjectManager servMan, BlueThunderServer server) { LocalObject target = servMan.getLocalObjectToIDs(event.getTargetID(), event.getTargetClientID()); if (target != null) { if (target.getType() == Types.PLAYER) { processPlayerKilled(null, target, event, server); } if (target.getType() == Types.BOMB && (target.getValue() == LocalBombManager.VALUE_ACTIVE || target.getValue() == LocalBombManager.VALUE_DISABLED)) { processBomb(null, target, event, server); } } else { // This may happen due to a high latency of a client, that triggers // an event on an already removed object. NetLogger.log("Server can't find client object for: " + event + "/" + target); } return null; } private DataContainer[] processBombOverload(Event event, ServerObjectManager servMan, BlueThunderServer server) { LocalObject target = servMan.getLocalObjectToIDs(event.getTargetID(), event.getTargetClientID()); if (target != null) { if (target.getType() == Types.BOMB && (target.getValue() == LocalBombManager.VALUE_ACTIVE || target.getValue() == LocalBombManager.VALUE_DISABLED)) { processBomb(null, target, event, server); } } else { // This may happen due to a high latency of a client, that triggers // an event on an already removed object. NetLogger.log("Server can't find client object for: " + event + "/" + target); } return null; } private DataContainer[] processCrateHit(Event event, ServerObjectManager servMan, BlueThunderServer server) { EventDataContainer edc = new EventDataContainer(); Event eve = new Event(Event.REMOVE_CRATE, -99, event.getTargetID(), -99);// Crate // has // been // hit eve.setOrigin(event.getOrigin()); edc.add(eve); server.broadcastToOthers(edc, event.getSourceClientID()); return null; } private DataContainer[] processBombItem(Event event, ServerObjectManager servMan, BlueThunderServer server) { return processItem(event, servMan, server); } private DataContainer[] processFirepowerItem(Event event, ServerObjectManager servMan, BlueThunderServer server) { return processItem(event, servMan, server); } private DataContainer[] processKickItem(Event event, ServerObjectManager servMan, BlueThunderServer server) { return processItem(event, servMan, server); } private DataContainer[] processDiseaseItem(Event event, ServerObjectManager servMan, BlueThunderServer server) { return processItem(event, servMan, server); } private synchronized DataContainer[] processItem(Event event, ServerObjectManager servMan, BlueThunderServer server) { if (!collectedItems.contains(event.getTargetID())) { EventDataContainer edc = new EventDataContainer(); Event eve = new Event(Event.COLLECT_ITEM, event.getSourceID(), event.getTargetID(), -99); eve.setSourceClientID(event.getSourceClientID()); eve.setOrigin(event.getOrigin()); edc.add(eve); server.broadcastToOthers(edc, event.getSourceClientID()); collectedItems.add(event.getTargetID()); } return null; } private void processBomb(LocalObject source, LocalObject target, Event event, BlueThunderServer server) { if (target.getValue() == LocalBombManager.VALUE_ACTIVE || target.getValue() == LocalBombManager.VALUE_DISABLED) { EventDataContainer edc = new EventDataContainer(); Event eve = new Event(Event.BOMB_HIT, source, target);// Bombe // getroffen if (source != null) { eve.setSourceClientID(source.getClientID()); } else { eve.setSourceClientID(-99); } edc.add(eve); server.sendToSingleClient(edc, target.getClientID()); } } private void processPlayerKilled(LocalObject source, LocalObject target, Event event, BlueThunderServer server) { // System.out.println(drawDetectionDone+"/"+threadRunning); if (target.getValue() > 0 && !drawDetectionDone) { // Lebt der noch? target.setValue(0); EventDataContainer edc = new EventDataContainer(); Event eve2 = new Event(Event.PLAYER_DEAD, source, target); // Tot! eve2.setOrigin(target.getPosition()); if (source != null) { eve2.setSourceClientID(source.getClientID()); } else { eve2.setSourceClientID(-99); } edc.add(eve2); PlayerInfo tip = server.getPlayerInfo(event.getTargetClientID(), event.getTargetID()); tip.incKilled(); InfoDataContainer idc = new InfoDataContainer(); server.addScores(idc); InfoLine il = new InfoLine(InfoLine.SYSTEM_OUT, 0, "msg", "'" + tip.getName() + "' has been killed!"); idc.add(il); server.broadcast(idc); // Highscores! server.broadcast(edc); } } }