package org.andengine.examples.game.pong; import java.io.IOException; import java.util.ArrayList; import org.andengine.engine.handler.IUpdateHandler; import org.andengine.examples.adt.messages.MessageConstants; import org.andengine.examples.adt.messages.client.ClientMessageFlags; import org.andengine.examples.adt.messages.client.ConnectionCloseClientMessage; import org.andengine.examples.adt.messages.client.ConnectionEstablishClientMessage; import org.andengine.examples.adt.messages.client.ConnectionPingClientMessage; import org.andengine.examples.adt.messages.server.ConnectionEstablishedServerMessage; import org.andengine.examples.adt.messages.server.ConnectionPongServerMessage; import org.andengine.examples.adt.messages.server.ConnectionRejectedProtocolMissmatchServerMessage; import org.andengine.examples.adt.messages.server.ServerMessageFlags; import org.andengine.examples.game.pong.adt.PaddleUserData; import org.andengine.examples.game.pong.adt.Score; import org.andengine.examples.game.pong.adt.messages.client.MovePaddleClientMessage; import org.andengine.examples.game.pong.adt.messages.server.SetPaddleIDServerMessage; import org.andengine.examples.game.pong.adt.messages.server.UpdateBallServerMessage; import org.andengine.examples.game.pong.adt.messages.server.UpdatePaddleServerMessage; import org.andengine.examples.game.pong.adt.messages.server.UpdateScoreServerMessage; import org.andengine.examples.game.pong.util.constants.PongConstants; import org.andengine.extension.multiplayer.protocol.adt.message.IMessage; import org.andengine.extension.multiplayer.protocol.adt.message.client.IClientMessage; import org.andengine.extension.multiplayer.protocol.server.IClientMessageHandler; import org.andengine.extension.multiplayer.protocol.server.SocketServer; import org.andengine.extension.multiplayer.protocol.server.SocketServer.ISocketServerListener.DefaultSocketServerListener; import org.andengine.extension.multiplayer.protocol.server.connector.ClientConnector; import org.andengine.extension.multiplayer.protocol.server.connector.SocketConnectionClientConnector; import org.andengine.extension.multiplayer.protocol.server.connector.SocketConnectionClientConnector.ISocketConnectionClientConnectorListener; import org.andengine.extension.multiplayer.protocol.shared.SocketConnection; import org.andengine.extension.multiplayer.protocol.util.MessagePool; import org.andengine.extension.physics.box2d.FixedStepPhysicsWorld; import org.andengine.extension.physics.box2d.PhysicsFactory; import org.andengine.extension.physics.box2d.PhysicsWorld; import org.andengine.extension.physics.box2d.util.Vector2Pool; import org.andengine.extension.physics.box2d.util.constants.PhysicsConstants; import org.andengine.util.adt.list.SmartList; import org.andengine.util.debug.Debug; import org.andengine.util.math.MathUtils; import android.util.SparseArray; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.Body; import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; import com.badlogic.gdx.physics.box2d.Contact; import com.badlogic.gdx.physics.box2d.ContactImpulse; import com.badlogic.gdx.physics.box2d.ContactListener; import com.badlogic.gdx.physics.box2d.Fixture; import com.badlogic.gdx.physics.box2d.FixtureDef; import com.badlogic.gdx.physics.box2d.Manifold; /** * (c) 2010 Nicolas Gramlich * (c) 2011 Zynga Inc. * * @author Nicolas Gramlich * @since 20:00:09 - 28.02.2011 */ public class PongServer extends SocketServer<SocketConnectionClientConnector> implements IUpdateHandler, PongConstants, ContactListener, ClientMessageFlags, ServerMessageFlags { // =========================================================== // Constants // =========================================================== private static final FixtureDef PADDLE_FIXTUREDEF = PhysicsFactory.createFixtureDef(1, 1, 0); private static final FixtureDef BALL_FIXTUREDEF = PhysicsFactory.createFixtureDef(1, 1, 0); private static final FixtureDef WALL_FIXTUREDEF = PhysicsFactory.createFixtureDef(1, 1, 0); // =========================================================== // Fields // =========================================================== private final PhysicsWorld mPhysicsWorld; private final Body mBallBody; private final SparseArray<Body> mPaddleBodies = new SparseArray<Body>(); private boolean mResetBall = true; private final SparseArray<Score> mPaddleScores = new SparseArray<Score>(); private final MessagePool<IMessage> mMessagePool = new MessagePool<IMessage>(); private final ArrayList<UpdatePaddleServerMessage> mUpdatePaddleServerMessages = new ArrayList<UpdatePaddleServerMessage>(); // =========================================================== // Constructors // =========================================================== public PongServer(final ISocketConnectionClientConnectorListener pSocketConnectionClientConnectorListener) { super(SERVER_PORT, pSocketConnectionClientConnectorListener, new DefaultSocketServerListener<SocketConnectionClientConnector>()); this.initMessagePool(); this.mPaddleScores.put(PADDLE_LEFT.getOwnerID(), new Score()); this.mPaddleScores.put(PADDLE_RIGHT.getOwnerID(), new Score()); this.mPhysicsWorld = new FixedStepPhysicsWorld(FPS, 2, new Vector2(0, 0), false, 8, 8); this.mPhysicsWorld.setContactListener(this); /* Ball */ this.mBallBody = PhysicsFactory.createCircleBody(this.mPhysicsWorld, 0, 0, BALL_RADIUS, BodyType.DynamicBody, BALL_FIXTUREDEF); this.mBallBody.setBullet(true); /* Paddles */ final Body paddleBodyLeft = PhysicsFactory.createBoxBody(this.mPhysicsWorld, -GAME_WIDTH_HALF, 0, PADDLE_WIDTH, PADDLE_HEIGHT, BodyType.KinematicBody, PADDLE_FIXTUREDEF); paddleBodyLeft.setUserData(PADDLE_LEFT); this.mPaddleBodies.put(PADDLE_LEFT.getOwnerID(), paddleBodyLeft); final Body paddleBodyRight = PhysicsFactory.createBoxBody(this.mPhysicsWorld, GAME_WIDTH_HALF - PADDLE_WIDTH_HALF, 0, PADDLE_WIDTH, PADDLE_HEIGHT, BodyType.KinematicBody, PADDLE_FIXTUREDEF); paddleBodyRight.setUserData(PADDLE_RIGHT); this.mPaddleBodies.put(PADDLE_RIGHT.getOwnerID(), paddleBodyRight); this.initWalls(); } private void initMessagePool() { this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_CONNECTION_ESTABLISHED, UpdateScoreServerMessage.class); this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_CONNECTION_PONG, UpdateScoreServerMessage.class); this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_CONNECTION_CLOSE, UpdateScoreServerMessage.class); this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_UPDATE_SCORE, UpdateScoreServerMessage.class); this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_UPDATE_BALL, UpdateBallServerMessage.class); this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_UPDATE_PADDLE, UpdatePaddleServerMessage.class); } private void initWalls() { WALL_FIXTUREDEF.isSensor = true; final Body leftBody = PhysicsFactory.createLineBody(this.mPhysicsWorld, -GAME_WIDTH_HALF, -GAME_HEIGHT_HALF, -GAME_WIDTH_HALF, GAME_HEIGHT_HALF, WALL_FIXTUREDEF); leftBody.setUserData(this.mPaddleBodies.get(PADDLE_LEFT.getOwnerID())); final Body rightBody = PhysicsFactory.createLineBody(this.mPhysicsWorld, GAME_WIDTH_HALF, -GAME_HEIGHT_HALF, GAME_WIDTH_HALF, GAME_HEIGHT_HALF, WALL_FIXTUREDEF); rightBody.setUserData(this.mPaddleBodies.get(PADDLE_RIGHT.getOwnerID())); WALL_FIXTUREDEF.isSensor = false; PhysicsFactory.createLineBody(this.mPhysicsWorld, -GAME_WIDTH_HALF, -GAME_HEIGHT_HALF, GAME_WIDTH_HALF, -GAME_HEIGHT_HALF, WALL_FIXTUREDEF); PhysicsFactory.createLineBody(this.mPhysicsWorld, -GAME_WIDTH_HALF, GAME_HEIGHT_HALF, GAME_WIDTH_HALF, GAME_HEIGHT_HALF, WALL_FIXTUREDEF); } // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @Override public void preSolve(final Contact pContact, final Manifold pManifold) { } @Override public void postSolve(final Contact pContact, final ContactImpulse pContactImpulse) { } @Override public void beginContact(final Contact pContact) { final Fixture fixtureA = pContact.getFixtureA(); final Body bodyA = fixtureA.getBody(); final Object userDataA = bodyA.getUserData(); final Fixture fixtureB = pContact.getFixtureB(); final Body bodyB = fixtureB.getBody(); final Object userDataB = bodyB.getUserData(); final boolean isScoreSensorA = userDataA != null && userDataA instanceof Body; final boolean isScoreSensorB = userDataB != null && userDataB instanceof Body; if(isScoreSensorA || isScoreSensorB) { this.mResetBall = true; final PaddleUserData paddleUserData = (isScoreSensorA) ? (PaddleUserData)(((Body)userDataA).getUserData()) : (PaddleUserData)(((Body)userDataA).getUserData()); final int opponentID = paddleUserData.getOpponentID(); final Score opponentPaddleScore = this.mPaddleScores.get(opponentID); opponentPaddleScore.increase(); final UpdateScoreServerMessage updateScoreServerMessage = (UpdateScoreServerMessage)this.mMessagePool.obtainMessage(FLAG_MESSAGE_SERVER_UPDATE_SCORE); updateScoreServerMessage.set(opponentID, opponentPaddleScore.getScore()); final SmartList<SocketConnectionClientConnector> clientConnectors = this.mClientConnectors; for(int i = 0; i < clientConnectors.size(); i++) { try { final ClientConnector<SocketConnection> clientConnector = clientConnectors.get(i); clientConnector.sendServerMessage(updateScoreServerMessage); } catch (final IOException e) { Debug.e(e); } } this.mMessagePool.recycleMessage(updateScoreServerMessage); } } @Override public void endContact(final Contact pContact) { } @Override public void onUpdate(final float pSecondsElapsed) { if(this.mResetBall) { this.mResetBall = false; final Vector2 vector2 = Vector2Pool.obtain(0, 0); this.mBallBody.setTransform(vector2, 0); vector2.set(MathUtils.randomSign() * MathUtils.random(3, 4), MathUtils.randomSign() * MathUtils.random(3, 4)); this.mBallBody.setLinearVelocity(vector2); Vector2Pool.recycle(vector2); } this.mPhysicsWorld.onUpdate(pSecondsElapsed); /* Prepare UpdateBallServerMessage. */ final Vector2 ballPosition = this.mBallBody.getPosition(); final float ballX = ballPosition.x * PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT - BALL_RADIUS; final float ballY = ballPosition.y * PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT - BALL_RADIUS; final UpdateBallServerMessage updateBallServerMessage = (UpdateBallServerMessage)this.mMessagePool.obtainMessage(FLAG_MESSAGE_SERVER_UPDATE_BALL); updateBallServerMessage.set(ballX, ballY); final ArrayList<UpdatePaddleServerMessage> updatePaddleServerMessages = this.mUpdatePaddleServerMessages; /* Prepare UpdatePaddleServerMessages. */ final SparseArray<Body> paddleBodies = this.mPaddleBodies; for(int j = 0; j < paddleBodies.size(); j++) { final int paddleID = paddleBodies.keyAt(j); final Body paddleBody = paddleBodies.get(paddleID); final Vector2 paddlePosition = paddleBody.getPosition(); final float paddleX = paddlePosition.x * PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT - PADDLE_WIDTH_HALF; final float paddleY = paddlePosition.y * PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT - PADDLE_HEIGHT_HALF; final UpdatePaddleServerMessage updatePaddleServerMessage = (UpdatePaddleServerMessage)this.mMessagePool.obtainMessage(FLAG_MESSAGE_SERVER_UPDATE_PADDLE); updatePaddleServerMessage.set(paddleID, paddleX, paddleY); updatePaddleServerMessages.add(updatePaddleServerMessage); } try { /* Update Ball. */ this.sendBroadcastServerMessage(updateBallServerMessage); /* Update Paddles. */ for(int j = 0; j < updatePaddleServerMessages.size(); j++) { this.sendBroadcastServerMessage(updatePaddleServerMessages.get(j)); } this.sendBroadcastServerMessage(updateBallServerMessage); } catch (final IOException e) { Debug.e(e); } /* Recycle messages. */ this.mMessagePool.recycleMessage(updateBallServerMessage); this.mMessagePool.recycleMessages(updatePaddleServerMessages); updatePaddleServerMessages.clear(); } @Override public void reset() { /* Nothing. */ } @Override protected SocketConnectionClientConnector newClientConnector(final SocketConnection pSocketConnection) throws IOException { final SocketConnectionClientConnector clientConnector = new SocketConnectionClientConnector(pSocketConnection); clientConnector.registerClientMessage(FLAG_MESSAGE_CLIENT_MOVE_PADDLE, MovePaddleClientMessage.class, new IClientMessageHandler<SocketConnection>() { @Override public void onHandleMessage(final ClientConnector<SocketConnection> pClientConnector, final IClientMessage pClientMessage) throws IOException { final MovePaddleClientMessage movePaddleClientMessage = (MovePaddleClientMessage)pClientMessage; final Body paddleBody = PongServer.this.mPaddleBodies.get(movePaddleClientMessage.mPaddleID); final Vector2 paddlePosition = paddleBody.getTransform().getPosition(); final float paddleY = MathUtils.bringToBounds(-GAME_HEIGHT_HALF + PADDLE_HEIGHT_HALF, GAME_HEIGHT_HALF - PADDLE_HEIGHT_HALF, movePaddleClientMessage.mY); paddlePosition.set(paddlePosition.x, paddleY / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT); paddleBody.setTransform(paddlePosition, 0); } }); clientConnector.registerClientMessage(FLAG_MESSAGE_CLIENT_CONNECTION_CLOSE, ConnectionCloseClientMessage.class, new IClientMessageHandler<SocketConnection>() { @Override public void onHandleMessage(final ClientConnector<SocketConnection> pClientConnector, final IClientMessage pClientMessage) throws IOException { pClientConnector.terminate(); } }); clientConnector.registerClientMessage(FLAG_MESSAGE_CLIENT_CONNECTION_ESTABLISH, ConnectionEstablishClientMessage.class, new IClientMessageHandler<SocketConnection>() { @Override public void onHandleMessage(final ClientConnector<SocketConnection> pClientConnector, final IClientMessage pClientMessage) throws IOException { final ConnectionEstablishClientMessage connectionEstablishClientMessage = (ConnectionEstablishClientMessage) pClientMessage; if(connectionEstablishClientMessage.getProtocolVersion() == MessageConstants.PROTOCOL_VERSION) { final ConnectionEstablishedServerMessage connectionEstablishedServerMessage = (ConnectionEstablishedServerMessage) PongServer.this.mMessagePool.obtainMessage(FLAG_MESSAGE_SERVER_CONNECTION_ESTABLISHED); try { pClientConnector.sendServerMessage(connectionEstablishedServerMessage); } catch (IOException e) { Debug.e(e); } PongServer.this.mMessagePool.recycleMessage(connectionEstablishedServerMessage); } else { final ConnectionRejectedProtocolMissmatchServerMessage connectionRejectedProtocolMissmatchServerMessage = (ConnectionRejectedProtocolMissmatchServerMessage) PongServer.this.mMessagePool.obtainMessage(FLAG_MESSAGE_SERVER_CONNECTION_REJECTED_PROTOCOL_MISSMATCH); connectionRejectedProtocolMissmatchServerMessage.setProtocolVersion(MessageConstants.PROTOCOL_VERSION); try { pClientConnector.sendServerMessage(connectionRejectedProtocolMissmatchServerMessage); } catch (IOException e) { Debug.e(e); } PongServer.this.mMessagePool.recycleMessage(connectionRejectedProtocolMissmatchServerMessage); } } }); clientConnector.registerClientMessage(FLAG_MESSAGE_CLIENT_CONNECTION_PING, ConnectionPingClientMessage.class, new IClientMessageHandler<SocketConnection>() { @Override public void onHandleMessage(final ClientConnector<SocketConnection> pClientConnector, final IClientMessage pClientMessage) throws IOException { final ConnectionPongServerMessage connectionPongServerMessage = (ConnectionPongServerMessage) PongServer.this.mMessagePool.obtainMessage(FLAG_MESSAGE_SERVER_CONNECTION_PONG); try { pClientConnector.sendServerMessage(connectionPongServerMessage); } catch (IOException e) { Debug.e(e); } PongServer.this.mMessagePool.recycleMessage(connectionPongServerMessage); } }); clientConnector.sendServerMessage(new SetPaddleIDServerMessage(this.mClientConnectors.size())); // TODO should not be size(), as it only works properly for first two connections! return clientConnector; } // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== }