package robombs.game; import robombs.game.model.*; import robombs.game.util.Ticker; import robombs.game.view.*; import robombs.clientserver.*; import java.util.*; import com.threed.jpct.*; /** * An abstract base class for client. There is a visual client (the one that you * are using when playing the game) and a bot client. The bot client holds a * simplified version of the game world and has no display methods. * * @author EgonOlsen * */ public abstract class AbstractClient { protected final Object SYNC = new Object(); protected final Object KILL_SYNC = new Object(); protected ClientObjectManager coMan = null; protected Ticker timeout = null; protected NetState state = new NetState(); protected Ticker waitTimer = new Ticker(1000); protected SimpleClient clientImpl = null; protected ClientEventQueue eventQueue = new ClientEventQueue(); protected Level level = null; protected World world = new World(); protected Ticker fireTicker = null; protected Ticker bombTicker = null; protected List<LocalObject> toKill = new ArrayList<LocalObject>(); protected LocalBombManager bombMan = null; protected BlueThunderServer serverImpl = null; protected PlayerDummy dummy = null; protected boolean quit = false; protected boolean received = false; protected boolean shouldBeConnected = false; protected boolean firstTransferFinished = false; protected boolean entitiesUpdated = false; protected boolean isReady = false; protected boolean disConnectScheduled = false; protected MapList mapList = null; protected List<MapInfo> selectedMaps = new ArrayList<MapInfo>(); protected boolean reloadScheduled = false; protected boolean roundCompleted = false; private int currentLevel = 0; protected volatile boolean modified = false; protected int playerCount = 0; protected int respawnCount = 0; protected WaterDropper dropper = new WaterDropper(); protected float orgFOV = world.getCamera().getFOV(); protected float orgYFOV = world.getCamera().getYFOV(); protected volatile boolean respawnRunning = false; protected SimpleVector[] spawnPoints = new SimpleVector[] { new SimpleVector(60, -10, 100), new SimpleVector(40, -10, 100), new SimpleVector(60, -10, 120), new SimpleVector(40, -10, 120) }; protected boolean isFree(SimpleVector pos, CrateManager cm, LocalBombManager lbm, ClientObjectManager com) { List<SimpleVector> poss = new ArrayList<SimpleVector>(cm.getCratePositions()); poss.addAll(lbm.getObjectPositions()); poss.addAll(com.getPlayerPositions(true)); for (SimpleVector pos2 : poss) { float delta = pos.calcSub(pos2).length(); if (delta < 4f) { NetLogger.log("Client: Position " + pos + " is blocked by another entity!"); return false; } } return true; } protected boolean allPlayersHaveSpawned() { return respawnCount >= playerCount; } protected void checkWaitingState() { if (state.getState() == NetState.STATE_WAITING && waitTimer.getTicks() > 0) { // Somehow, the server didn't get it....try again... // This shouldn't be needed anymore as it obviously was a // synchronization problem, but one never knows... Event ev = new Event(Event.LEVEL_LOADED, -99, -99, clientImpl.getClientID()); ev.setSourceClientID(clientImpl.getClientID()); eventQueue.add(ev); NetLogger.log("Client " + getClientID() + ": Retransmitting state!"); } } public void ready(boolean isReady) { int m = Event.PLAYER_READY; if (!isReady) { m = Event.PLAYER_NOT_READY; } Event event = new Event(m, -99, -99, clientImpl.getClientID()); event.setSourceClientID(clientImpl.getClientID()); synchronized (SYNC) { eventQueue.add(event); } this.isReady = isReady; } public boolean isReady() { return isReady; } public int getClientID() { if (clientImpl == null) { return 0; } return clientImpl.getClientID(); } public boolean isConnected() { return clientImpl != null && clientImpl.isConnected(); } public int getMapNumber() { return currentLevel; } public void nextMap() { currentLevel++; currentLevel = currentLevel % selectedMaps.size(); } public void firstMap() { currentLevel = 0; } public NetState getState() { return state; } public List<MapInfo> getSelectedMaps() { return selectedMaps; } public void beforeSending() { synchronized (SYNC) { if (modified) { modified = false; prepareClientData(); } } } public void beforeReceiving() { received = false; if (coMan != null) { // Erstmal alle als nicht �bertragen markieren. Sollten sie es // dennoch werden, wird // unten umgesetzt. Ansonsten fliegen sie beim n�chsten Zeichnen! synchronized (SYNC) { for (ClientObject co : coMan.getClientObjects()) { co.setTransfered(false); } } } } public void afterReceiving() { if (state.getState() == NetState.STATE_RUNNING) { firstTransferFinished = true; } } public void dataReceivedEnd() { received = true; } public DataContainer[] dataReceived(DataContainer c, int type) { if (c != null && c.hasData()) { synchronized (SYNC) { if (clientImpl != null) { if (coMan != null) { if (type == MessageTypes.OBJ_TRANSFER) { ExtendedDataContainer ec = new ExtendedDataContainer(c); synchronized (world) { while (ec.hasData()) { LocalObject lo = ec.getLocalObject(); if (lo.getClientID() != clientImpl.getClientID()) { // Somebody else... coMan.getOrCreateClientObject(lo, world, eventQueue); } } } } } if (type == MessageTypes.EVENT) { EventDataContainer edc = new EventDataContainer(c); int cnt = 0; synchronized (world) { while (edc.hasData()) { Event event = edc.getEvent(); processEvent(event); cnt++; } } } if (type == MessageTypes.IMPORTANT_INFO) { InfoDataContainer idc = new InfoDataContainer(c); int cnt = 0; while (idc.hasData()) { InfoLine il = idc.getInfoLine(); processInfo(il); cnt++; } } } } } return null; } /** * Checks for a time out of other clients. If a remote client can't transmit * any data, it can't transmit that it's gone either. This is handled here * by removing objects from clients that seem to be lost. * * @throws Exception */ protected void checkTimeout() throws Exception { if (timeout.getTicks() > 0) { Collection<ClientObject> objs = coMan.getClientObjects(); List<ClientObject> toKill = new ArrayList<ClientObject>(); for (ClientObject co : objs) { if (co.isOld()) { toKill.add(co); } } for (ClientObject co : toKill) { coMan.remove(co, world); } } if (clientImpl != null && !clientImpl.isConnected()) { // Damn! Where's our connection gone? disconnect(); } } /** * Removes every object from the world that's part of the toKill-List. */ protected void cleanUp() { synchronized (KILL_SYNC) { for (Iterator<LocalObject> itty = toKill.iterator(); itty.hasNext();) { LocalObject obj = itty.next(); if (obj.getView() != null) { ClientObject view = obj.getView(); view.removeFromWorld(world); } } toKill.clear(); } } protected void updateExplosions(long ticks) { level.getExplosionManager().process(ticks); } /** * Updates all entities (like player meshes) that are remote, i.e. that came * from the server. * * @param ticks * long the number of ticks passed */ protected void updateRemoteEntities(long ticks, boolean botUsage) { if (coMan != null) { coMan.process(ticks, level, world, received, botUsage); if (firstTransferFinished) { entitiesUpdated = true; } } } protected void updateCrates(long ticks) { level.getCrateManager().process(ticks, level); level.getItemManager().process(ticks, level); } public void createSpawnPoints() { SimpleVector[] points = level.getSpawnPoints(); if (points != null && points.length != 0) { spawnPoints = points; } } /** * Taken from the jpct forums, posted by zammbi * * @param m * @return */ protected final SimpleVector matrixToRad(Matrix m) { float[] mDump = m.getDump(); float heading, attitude, bank; if (mDump[4] > 0.998) { // singularity at north pole heading = (float) Math.atan2(mDump[2], mDump[10]); attitude = (float) (Math.PI / 2); bank = 0; } else if (mDump[4] < -0.998) { // singularity at south pole heading = (float) Math.atan2(mDump[2], mDump[10]); attitude = (float) (-Math.PI / 2); bank = 0; } else { heading = (float) Math.atan2(-mDump[8], mDump[0]); bank = (float) Math.atan2(-mDump[6], mDump[5]); attitude = (float) Math.asin(mDump[4]); } return new SimpleVector(bank, heading, attitude); } abstract public void disconnect() throws Exception; abstract protected void processInfo(InfoLine il); abstract protected void processEvent(Event ev); abstract protected void prepareClientData(); }