package org.andengine.examples.game.pong;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import org.andengine.engine.Engine;
import org.andengine.engine.LimitedFPSEngine;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.primitive.Line;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.text.Text;
import org.andengine.entity.util.FPSLogger;
import org.andengine.examples.adt.messages.MessageConstants;
import org.andengine.examples.adt.messages.client.ConnectionPingClientMessage;
import org.andengine.examples.adt.messages.server.ConnectionCloseServerMessage;
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.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.server.IServerMessage;
import org.andengine.extension.multiplayer.protocol.client.IServerMessageHandler;
import org.andengine.extension.multiplayer.protocol.client.connector.ServerConnector;
import org.andengine.extension.multiplayer.protocol.client.connector.SocketConnectionServerConnector.ISocketConnectionServerConnectorListener;
import org.andengine.extension.multiplayer.protocol.server.connector.ClientConnector;
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.WifiUtils;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.font.Font;
import org.andengine.opengl.font.FontFactory;
import org.andengine.opengl.texture.ITexture;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.ui.activity.SimpleBaseGameActivity;
import org.andengine.util.debug.Debug;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.graphics.Color;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.Toast;
/**
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga
*
* @author Nicolas Gramlich
* @since 19:36:45 - 28.02.2011
*/
public class PongGameActivity extends SimpleBaseGameActivity implements PongConstants, IOnSceneTouchListener {
// ===========================================================
// Constants
// ===========================================================
private static final String LOCALHOST_IP = "127.0.0.1";
private static final int CAMERA_WIDTH = GAME_WIDTH;
private static final int CAMERA_HEIGHT = GAME_HEIGHT;
private static final int DIALOG_CHOOSE_SERVER_OR_CLIENT_ID = 0;
private static final int DIALOG_ENTER_SERVER_IP_ID = DIALOG_CHOOSE_SERVER_OR_CLIENT_ID + 1;
private static final int DIALOG_SHOW_SERVER_IP_ID = DIALOG_ENTER_SERVER_IP_ID + 1;
private static final int PADDLEID_NOT_SET = -1;
private static final int MENU_PING = Menu.FIRST;
// ===========================================================
// Fields
// ===========================================================
private Camera mCamera;
private String mServerIP = LOCALHOST_IP;
private int mPaddleID = PADDLEID_NOT_SET;
private PongServer mServer;
private PongServerConnector mServerConnector;
private Rectangle mBall;
private final SparseArray<Rectangle> mPaddleMap = new SparseArray<Rectangle>();
private final SparseArray<Text> mScoreTextMap = new SparseArray<Text>();
private Font mScoreFont;
private float mPaddleCenterY;
// ===========================================================
// Constructors
// ===========================================================
@Override
public EngineOptions onCreateEngineOptions() {
this.mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
this.mCamera.setCenter(0,0);
return new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), this.mCamera);
}
@Override
public Engine onCreateEngine(final EngineOptions pEngineOptions) {
this.showDialog(DIALOG_CHOOSE_SERVER_OR_CLIENT_ID);
return new LimitedFPSEngine(pEngineOptions, FPS);
}
@Override
public void onCreateResources() {
final ITexture scoreFontTexture = new BitmapTextureAtlas(this.getTextureManager(), 256, 256, TextureOptions.BILINEAR);
FontFactory.setAssetBasePath("font/");
this.mScoreFont = FontFactory.createFromAsset(this.getFontManager(), scoreFontTexture, this.getAssets(), "LCD.ttf", 32, true, Color.WHITE);
this.mScoreFont.load();
}
@Override
public Scene onCreateScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
final VertexBufferObjectManager vertexBufferObjectManager = this.getVertexBufferObjectManager();
final Scene scene = new Scene();
/* Ball */
this.mBall = new Rectangle(0, 0, BALL_WIDTH, BALL_HEIGHT, vertexBufferObjectManager);
scene.attachChild(this.mBall);
/* Walls */
scene.attachChild(new Line(-GAME_WIDTH_HALF + 1, -GAME_HEIGHT_HALF, -GAME_WIDTH_HALF + 1, GAME_HEIGHT_HALF, vertexBufferObjectManager)); // Left
scene.attachChild(new Line(GAME_WIDTH_HALF, -GAME_HEIGHT_HALF, GAME_WIDTH_HALF, GAME_HEIGHT_HALF, vertexBufferObjectManager)); // Right
scene.attachChild(new Line(-GAME_WIDTH_HALF, -GAME_HEIGHT_HALF + 1, GAME_WIDTH_HALF , -GAME_HEIGHT_HALF + 1, vertexBufferObjectManager)); // Top
scene.attachChild(new Line(-GAME_WIDTH_HALF, GAME_HEIGHT_HALF, GAME_WIDTH_HALF, GAME_HEIGHT_HALF, vertexBufferObjectManager)); // Bottom
scene.attachChild(new Line(0, -GAME_HEIGHT_HALF, 0, GAME_HEIGHT_HALF, vertexBufferObjectManager)); // Middle
/* Paddles */
final Rectangle paddleLeft = new Rectangle(0, 0, PADDLE_WIDTH, PADDLE_HEIGHT, vertexBufferObjectManager);
final Rectangle paddleRight = new Rectangle(0, 0, PADDLE_WIDTH, PADDLE_HEIGHT, vertexBufferObjectManager);
this.mPaddleMap.put(PADDLE_LEFT.getOwnerID(), paddleLeft);
this.mPaddleMap.put(PADDLE_RIGHT.getOwnerID(), paddleRight);
scene.attachChild(paddleLeft);
scene.attachChild(paddleRight);
/* Scores */
final Text scoreLeft = new Text(0, -GAME_HEIGHT_HALF + SCORE_PADDING, this.mScoreFont, "0", 2, vertexBufferObjectManager);
scoreLeft.setPosition(-scoreLeft.getWidth() - SCORE_PADDING, scoreLeft.getY());
final Text scoreRight = new Text(SCORE_PADDING, -GAME_HEIGHT_HALF + SCORE_PADDING, this.mScoreFont, "0", 2, vertexBufferObjectManager);
this.mScoreTextMap.put(PADDLE_LEFT.getOwnerID(), scoreLeft);
this.mScoreTextMap.put(PADDLE_RIGHT.getOwnerID(), scoreRight);
scene.attachChild(scoreLeft);
scene.attachChild(scoreRight);
scene.setOnSceneTouchListener(this);
scene.registerUpdateHandler(new IUpdateHandler() {
@Override
public void onUpdate(final float pSecondsElapsed) {
if(PongGameActivity.this.mPaddleID != PADDLEID_NOT_SET) {
try {
PongGameActivity.this.mServerConnector.sendClientMessage(new MovePaddleClientMessage(PongGameActivity.this.mPaddleID, PongGameActivity.this.mPaddleCenterY));
} catch (final IOException e) {
Debug.e(e);
}
}
}
@Override
public void reset() {}
});
return scene;
}
@Override
public void onGameCreated() {
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public boolean onCreateOptionsMenu(final Menu pMenu) {
pMenu.add(Menu.NONE, MENU_PING, Menu.NONE, "Ping Server");
return super.onCreateOptionsMenu(pMenu);
}
@SuppressWarnings("deprecation")
@Override
public boolean onMenuItemSelected(final int pFeatureId, final MenuItem pItem) {
switch(pItem.getItemId()) {
case MENU_PING:
try {
final ConnectionPingClientMessage connectionPingClientMessage = new ConnectionPingClientMessage(); // TODO Pooling
connectionPingClientMessage.setTimestamp(System.currentTimeMillis());
this.mServerConnector.sendClientMessage(connectionPingClientMessage);
} catch (final IOException e) {
Debug.e(e);
}
return true;
default:
return super.onMenuItemSelected(pFeatureId, pItem);
}
}
@Override
protected Dialog onCreateDialog(final int pID) {
switch(pID) {
case DIALOG_SHOW_SERVER_IP_ID:
try {
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_info)
.setTitle("Your Server-IP ...")
.setCancelable(false)
.setMessage("The IP of your Server is:\n" + WifiUtils.getWifiIPv4Address(this))
.setPositiveButton(android.R.string.ok, null)
.create();
} catch (final UnknownHostException e) {
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle("Your Server-IP ...")
.setCancelable(false)
.setMessage("Error retrieving IP of your Server: " + e)
.setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(final DialogInterface pDialog, final int pWhich) {
PongGameActivity.this.finish();
}
})
.create();
}
case DIALOG_ENTER_SERVER_IP_ID:
final EditText ipEditText = new EditText(this);
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_info)
.setTitle("Enter Server-IP ...")
.setCancelable(false)
.setView(ipEditText)
.setPositiveButton("Connect", new OnClickListener() {
@Override
public void onClick(final DialogInterface pDialog, final int pWhich) {
PongGameActivity.this.mServerIP = ipEditText.getText().toString();
PongGameActivity.this.initClient();
}
})
.setNegativeButton(android.R.string.cancel, new OnClickListener() {
@Override
public void onClick(final DialogInterface pDialog, final int pWhich) {
PongGameActivity.this.finish();
}
})
.create();
case DIALOG_CHOOSE_SERVER_OR_CLIENT_ID:
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_info)
.setTitle("Be Server or Client ...")
.setCancelable(false)
.setPositiveButton("Client", new OnClickListener() {
@Override
public void onClick(final DialogInterface pDialog, final int pWhich) {
PongGameActivity.this.showDialog(DIALOG_ENTER_SERVER_IP_ID);
}
})
.setNeutralButton("Server", new OnClickListener() {
@Override
public void onClick(final DialogInterface pDialog, final int pWhich) {
PongGameActivity.this.initServerAndClient();
PongGameActivity.this.showDialog(DIALOG_SHOW_SERVER_IP_ID);
}
})
.create();
default:
return super.onCreateDialog(pID);
}
}
@Override
public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
this.mPaddleCenterY = pSceneTouchEvent.getY();
return true;
}
@Override
protected void onDestroy() {
if(this.mServer != null) {
try {
this.mServer.sendBroadcastServerMessage(new ConnectionCloseServerMessage());
} catch (final IOException e) {
Debug.e(e);
}
this.mServer.terminate();
}
if(this.mServerConnector != null) {
this.mServerConnector.terminate();
}
super.onDestroy();
}
@Override
public boolean onKeyUp(final int pKeyCode, final KeyEvent pEvent) {
switch(pKeyCode) {
case KeyEvent.KEYCODE_BACK:
this.finish();
return true;
}
return super.onKeyUp(pKeyCode, pEvent);
}
// ===========================================================
// Methods
// ===========================================================
public void updateScore(final int pPaddleID, final int pPoints) {
final Text scoreText = this.mScoreTextMap.get(pPaddleID);
scoreText.setText(String.valueOf(pPoints));
/* Adjust position of left Score, so that it doesn't overlap the middle line. */
if(pPaddleID == PADDLE_LEFT.getOwnerID()) {
scoreText.setPosition(-scoreText.getWidth() - SCORE_PADDING, scoreText.getY());
}
}
public void setPaddleID(final int pPaddleID) {
this.mPaddleID = pPaddleID;
}
public void updatePaddle(final int pPaddleID, final float pX, final float pY) {
this.mPaddleMap.get(pPaddleID).setPosition(pX, pY);
}
public void updateBall(final float pX, final float pY) {
this.mBall.setPosition(pX, pY);
}
private void initServerAndClient() {
PongGameActivity.this.initServer();
/* Wait some time after the server has been started, so it actually can start up. */
try {
Thread.sleep(500);
} catch (final Throwable t) {
Debug.e(t);
}
PongGameActivity.this.initClient();
}
private void initServer() {
this.mServer = new PongServer(new ExampleClientConnectorListener());
this.mServer.start();
this.mEngine.registerUpdateHandler(this.mServer);
}
private void initClient() {
try {
this.mServerConnector = new PongServerConnector(this.mServerIP, new ExampleServerConnectorListener());
this.mServerConnector.getConnection().start();
} catch (final Throwable t) {
Debug.e(t);
}
}
private void toast(final String pMessage) {
this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(PongGameActivity.this, pMessage, Toast.LENGTH_SHORT).show();
}
});
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
private class PongServerConnector extends ServerConnector<SocketConnection> implements PongConstants, ServerMessageFlags {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
// ===========================================================
// Constructors
// ===========================================================
public PongServerConnector(final String pServerIP, final ISocketConnectionServerConnectorListener pSocketConnectionServerConnectorListener) throws IOException {
super(new SocketConnection(new Socket(pServerIP, SERVER_PORT)), pSocketConnectionServerConnectorListener);
this.registerServerMessage(FLAG_MESSAGE_SERVER_CONNECTION_CLOSE, ConnectionCloseServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
PongGameActivity.this.finish();
}
});
this.registerServerMessage(FLAG_MESSAGE_SERVER_CONNECTION_ESTABLISHED, ConnectionEstablishedServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
Debug.d("CLIENT: Connection established.");
}
});
this.registerServerMessage(FLAG_MESSAGE_SERVER_CONNECTION_REJECTED_PROTOCOL_MISSMATCH, ConnectionRejectedProtocolMissmatchServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
final ConnectionRejectedProtocolMissmatchServerMessage connectionRejectedProtocolMissmatchServerMessage = (ConnectionRejectedProtocolMissmatchServerMessage)pServerMessage;
if(connectionRejectedProtocolMissmatchServerMessage.getProtocolVersion() > MessageConstants.PROTOCOL_VERSION) {
// Toast.makeText(context, text, duration).show();
} else if(connectionRejectedProtocolMissmatchServerMessage.getProtocolVersion() < MessageConstants.PROTOCOL_VERSION) {
// Toast.makeText(context, text, duration).show();
}
PongGameActivity.this.finish();
}
});
this.registerServerMessage(FLAG_MESSAGE_SERVER_CONNECTION_PONG, ConnectionPongServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
final ConnectionPongServerMessage connectionPongServerMessage = (ConnectionPongServerMessage) pServerMessage;
final long roundtripMilliseconds = System.currentTimeMillis() - connectionPongServerMessage.getTimestamp();
Debug.v("Ping: " + roundtripMilliseconds / 2 + "ms");
}
});
this.registerServerMessage(FLAG_MESSAGE_SERVER_SET_PADDLEID, SetPaddleIDServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
final SetPaddleIDServerMessage setPaddleIDServerMessage = (SetPaddleIDServerMessage) pServerMessage;
PongGameActivity.this.setPaddleID(setPaddleIDServerMessage.mPaddleID);
}
});
this.registerServerMessage(FLAG_MESSAGE_SERVER_UPDATE_SCORE, UpdateScoreServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
final UpdateScoreServerMessage updateScoreServerMessage = (UpdateScoreServerMessage) pServerMessage;
PongGameActivity.this.updateScore(updateScoreServerMessage.mPaddleID, updateScoreServerMessage.mScore);
}
});
this.registerServerMessage(FLAG_MESSAGE_SERVER_UPDATE_BALL, UpdateBallServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
final UpdateBallServerMessage updateBallServerMessage = (UpdateBallServerMessage) pServerMessage;
PongGameActivity.this.updateBall(updateBallServerMessage.mX, updateBallServerMessage.mY);
}
});
this.registerServerMessage(FLAG_MESSAGE_SERVER_UPDATE_PADDLE, UpdatePaddleServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
final UpdatePaddleServerMessage updatePaddleServerMessage = (UpdatePaddleServerMessage) pServerMessage;
PongGameActivity.this.updatePaddle(updatePaddleServerMessage.mPaddleID, updatePaddleServerMessage.mX, updatePaddleServerMessage.mY);
}
});
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}
private class ExampleServerConnectorListener implements ISocketConnectionServerConnectorListener {
@Override
public void onStarted(final ServerConnector<SocketConnection> pServerConnector) {
PongGameActivity.this.toast("CLIENT: Connected to server.");
}
@Override
public void onTerminated(final ServerConnector<SocketConnection> pServerConnector) {
PongGameActivity.this.toast("CLIENT: Disconnected from Server.");
PongGameActivity.this.finish();
}
}
private class ExampleClientConnectorListener implements ISocketConnectionClientConnectorListener {
@Override
public void onStarted(final ClientConnector<SocketConnection> pClientConnector) {
PongGameActivity.this.toast("SERVER: Client connected: " + pClientConnector.getConnection().getSocket().getInetAddress().getHostAddress());
}
@Override
public void onTerminated(final ClientConnector<SocketConnection> pClientConnector) {
PongGameActivity.this.toast("SERVER: Client disconnected: " + pClientConnector.getConnection().getSocket().getInetAddress().getHostAddress());
}
}
}