package org.andengine.examples;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.scene.IOnAreaTouchListener;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.ITouchArea;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.entity.sprite.Sprite;
import org.andengine.entity.util.FPSLogger;
import org.andengine.examples.adt.messages.client.ClientMessageFlags;
import org.andengine.examples.adt.messages.server.ConnectionCloseServerMessage;
import org.andengine.examples.adt.messages.server.ServerMessageFlags;
import org.andengine.extension.multiplayer.protocol.adt.message.IMessage;
import org.andengine.extension.multiplayer.protocol.adt.message.server.IServerMessage;
import org.andengine.extension.multiplayer.protocol.adt.message.server.ServerMessage;
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;
import org.andengine.extension.multiplayer.protocol.client.connector.SocketConnectionServerConnector.ISocketConnectionServerConnectorListener;
import org.andengine.extension.multiplayer.protocol.server.SocketServer;
import org.andengine.extension.multiplayer.protocol.server.SocketServer.ISocketServerListener;
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.multiplayer.protocol.util.WifiUtils;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.andengine.opengl.texture.region.ITextureRegion;
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.util.SparseArray;
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.Toast;
/**
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga
*
* @author Nicolas Gramlich
* @since 17:10:24 - 19.06.2010
*/
public class MultiplayerExample extends SimpleBaseGameActivity implements ClientMessageFlags, ServerMessageFlags {
// ===========================================================
// Constants
// ===========================================================
private static final String LOCALHOST_IP = "127.0.0.1";
private static final int CAMERA_WIDTH = 720;
private static final int CAMERA_HEIGHT = 480;
private static final int SERVER_PORT = 4444;
private static final short FLAG_MESSAGE_SERVER_ADD_FACE = 1;
private static final short FLAG_MESSAGE_SERVER_MOVE_FACE = FLAG_MESSAGE_SERVER_ADD_FACE + 1;
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;
// ===========================================================
// Fields
// ===========================================================
private BitmapTextureAtlas mBitmapTextureAtlas;
private ITextureRegion mFaceTextureRegion;
private int mFaceIDCounter;
private final SparseArray<Sprite> mFaces = new SparseArray<Sprite>();
private String mServerIP = LOCALHOST_IP;
private SocketServer<SocketConnectionClientConnector> mSocketServer;
private ServerConnector<SocketConnection> mServerConnector;
private final MessagePool<IMessage> mMessagePool = new MessagePool<IMessage>();
// ===========================================================
// Constructors
// ===========================================================
public MultiplayerExample() {
this.initMessagePool();
}
private void initMessagePool() {
this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_ADD_FACE, AddFaceServerMessage.class);
this.mMessagePool.registerMessage(FLAG_MESSAGE_SERVER_MOVE_FACE, MoveFaceServerMessage.class);
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public EngineOptions onCreateEngineOptions() {
this.showDialog(DIALOG_CHOOSE_SERVER_OR_CLIENT_ID);
final Camera camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
return new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);
}
@Override
public void onCreateResources() {
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
this.mBitmapTextureAtlas = new BitmapTextureAtlas(this.getTextureManager(), 32, 32, TextureOptions.BILINEAR);
this.mFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mBitmapTextureAtlas, this, "face_box.png", 0, 0);
this.mBitmapTextureAtlas.load();
}
@Override
public Scene onCreateScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
final Scene scene = new Scene();
scene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
/* We allow only the server to actively send around messages. */
if(MultiplayerExample.this.mSocketServer != null) {
scene.setOnSceneTouchListener(new IOnSceneTouchListener() {
@Override
public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
if(pSceneTouchEvent.isActionDown()) {
try {
final AddFaceServerMessage addFaceServerMessage = (AddFaceServerMessage) MultiplayerExample.this.mMessagePool.obtainMessage(FLAG_MESSAGE_SERVER_ADD_FACE);
addFaceServerMessage.set(MultiplayerExample.this.mFaceIDCounter++, pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
MultiplayerExample.this.mSocketServer.sendBroadcastServerMessage(addFaceServerMessage);
MultiplayerExample.this.mMessagePool.recycleMessage(addFaceServerMessage);
} catch (final IOException e) {
Debug.e(e);
}
return true;
} else {
return true;
}
}
});
scene.setOnAreaTouchListener(new IOnAreaTouchListener() {
@Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final ITouchArea pTouchArea, final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
try {
final Sprite face = (Sprite)pTouchArea;
final Integer faceID = (Integer)face.getUserData();
final MoveFaceServerMessage moveFaceServerMessage = (MoveFaceServerMessage) MultiplayerExample.this.mMessagePool.obtainMessage(FLAG_MESSAGE_SERVER_MOVE_FACE);
moveFaceServerMessage.set(faceID, pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
MultiplayerExample.this.mSocketServer.sendBroadcastServerMessage(moveFaceServerMessage);
MultiplayerExample.this.mMessagePool.recycleMessage(moveFaceServerMessage);
} catch (final IOException e) {
Debug.e(e);
return false;
}
return true;
}
});
scene.setTouchAreaBindingOnActionDownEnabled(true);
}
return scene;
}
@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) {
MultiplayerExample.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) {
MultiplayerExample.this.mServerIP = ipEditText.getText().toString();
MultiplayerExample.this.initClient();
}
})
.setNegativeButton(android.R.string.cancel, new OnClickListener() {
@Override
public void onClick(final DialogInterface pDialog, final int pWhich) {
MultiplayerExample.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) {
MultiplayerExample.this.showDialog(DIALOG_ENTER_SERVER_IP_ID);
}
})
.setNeutralButton("Server", new OnClickListener() {
@Override
public void onClick(final DialogInterface pDialog, final int pWhich) {
MultiplayerExample.this.toast("You can add and move sprites, which are only shown on the clients.");
MultiplayerExample.this.initServer();
MultiplayerExample.this.showDialog(DIALOG_SHOW_SERVER_IP_ID);
}
})
.setNegativeButton("Both", new OnClickListener() {
@Override
public void onClick(final DialogInterface pDialog, final int pWhich) {
MultiplayerExample.this.toast("You can add sprites and move them, by dragging them.");
MultiplayerExample.this.initServerAndClient();
MultiplayerExample.this.showDialog(DIALOG_SHOW_SERVER_IP_ID);
}
})
.create();
default:
return super.onCreateDialog(pID);
}
}
@Override
protected void onDestroy() {
if(this.mSocketServer != null) {
try {
this.mSocketServer.sendBroadcastServerMessage(new ConnectionCloseServerMessage());
} catch (final IOException e) {
Debug.e(e);
}
this.mSocketServer.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 addFace(final int pID, final float pX, final float pY) {
final Scene scene = this.mEngine.getScene();
/* Create the face and add it to the scene. */
final Sprite face = new Sprite(0, 0, this.mFaceTextureRegion, this.getVertexBufferObjectManager());
face.setPosition(pX - face.getWidth() * 0.5f, pY - face.getHeight() * 0.5f);
face.setUserData(pID);
this.mFaces.put(pID, face);
scene.registerTouchArea(face);
scene.attachChild(face);
}
public void moveFace(final int pID, final float pX, final float pY) {
/* Find and move the face. */
final Sprite face = this.mFaces.get(pID);
face.setPosition(pX - face.getWidth() * 0.5f, pY - face.getHeight() * 0.5f);
}
private void initServerAndClient() {
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);
}
this.initClient();
}
private void initServer() {
this.mSocketServer = new SocketServer<SocketConnectionClientConnector>(SERVER_PORT, new ExampleClientConnectorListener(), new ExampleServerStateListener()) {
@Override
protected SocketConnectionClientConnector newClientConnector(final SocketConnection pSocketConnection) throws IOException {
return new SocketConnectionClientConnector(pSocketConnection);
}
};
this.mSocketServer.start();
}
private void initClient() {
try {
this.mServerConnector = new SocketConnectionServerConnector(new SocketConnection(new Socket(this.mServerIP, SERVER_PORT)), new ExampleServerConnectorListener());
this.mServerConnector.registerServerMessage(FLAG_MESSAGE_SERVER_CONNECTION_CLOSE, ConnectionCloseServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
MultiplayerExample.this.finish();
}
});
this.mServerConnector.registerServerMessage(FLAG_MESSAGE_SERVER_ADD_FACE, AddFaceServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
final AddFaceServerMessage addFaceServerMessage = (AddFaceServerMessage)pServerMessage;
MultiplayerExample.this.addFace(addFaceServerMessage.mID, addFaceServerMessage.mX, addFaceServerMessage.mY);
}
});
this.mServerConnector.registerServerMessage(FLAG_MESSAGE_SERVER_MOVE_FACE, MoveFaceServerMessage.class, new IServerMessageHandler<SocketConnection>() {
@Override
public void onHandleMessage(final ServerConnector<SocketConnection> pServerConnector, final IServerMessage pServerMessage) throws IOException {
final MoveFaceServerMessage moveFaceServerMessage = (MoveFaceServerMessage)pServerMessage;
MultiplayerExample.this.moveFace(moveFaceServerMessage.mID, moveFaceServerMessage.mX, moveFaceServerMessage.mY);
}
});
this.mServerConnector.getConnection().start();
} catch (final Throwable t) {
Debug.e(t);
}
}
private void log(final String pMessage) {
Debug.d(pMessage);
}
private void toast(final String pMessage) {
this.log(pMessage);
this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MultiplayerExample.this, pMessage, Toast.LENGTH_SHORT).show();
}
});
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
public static class AddFaceServerMessage extends ServerMessage {
private int mID;
private float mX;
private float mY;
public AddFaceServerMessage() {
}
public AddFaceServerMessage(final int pID, final float pX, final float pY) {
this.mID = pID;
this.mX = pX;
this.mY = pY;
}
public void set(final int pID, final float pX, final float pY) {
this.mID = pID;
this.mX = pX;
this.mY = pY;
}
@Override
public short getFlag() {
return FLAG_MESSAGE_SERVER_ADD_FACE;
}
@Override
protected void onReadTransmissionData(final DataInputStream pDataInputStream) throws IOException {
this.mID = pDataInputStream.readInt();
this.mX = pDataInputStream.readFloat();
this.mY = pDataInputStream.readFloat();
}
@Override
protected void onWriteTransmissionData(final DataOutputStream pDataOutputStream) throws IOException {
pDataOutputStream.writeInt(this.mID);
pDataOutputStream.writeFloat(this.mX);
pDataOutputStream.writeFloat(this.mY);
}
}
public static class MoveFaceServerMessage extends ServerMessage {
private int mID;
private float mX;
private float mY;
public MoveFaceServerMessage() {
}
public MoveFaceServerMessage(final int pID, final float pX, final float pY) {
this.mID = pID;
this.mX = pX;
this.mY = pY;
}
public void set(final int pID, final float pX, final float pY) {
this.mID = pID;
this.mX = pX;
this.mY = pY;
}
@Override
public short getFlag() {
return FLAG_MESSAGE_SERVER_MOVE_FACE;
}
@Override
protected void onReadTransmissionData(final DataInputStream pDataInputStream) throws IOException {
this.mID = pDataInputStream.readInt();
this.mX = pDataInputStream.readFloat();
this.mY = pDataInputStream.readFloat();
}
@Override
protected void onWriteTransmissionData(final DataOutputStream pDataOutputStream) throws IOException {
pDataOutputStream.writeInt(this.mID);
pDataOutputStream.writeFloat(this.mX);
pDataOutputStream.writeFloat(this.mY);
}
}
private class ExampleServerConnectorListener implements ISocketConnectionServerConnectorListener {
@Override
public void onStarted(final ServerConnector<SocketConnection> pConnector) {
MultiplayerExample.this.toast("CLIENT: Connected to server.");
}
@Override
public void onTerminated(final ServerConnector<SocketConnection> pConnector) {
MultiplayerExample.this.toast("CLIENT: Disconnected from Server...");
MultiplayerExample.this.finish();
}
}
private class ExampleServerStateListener implements ISocketServerListener<SocketConnectionClientConnector> {
@Override
public void onStarted(final SocketServer<SocketConnectionClientConnector> pSocketServer) {
MultiplayerExample.this.toast("SERVER: Started.");
}
@Override
public void onTerminated(final SocketServer<SocketConnectionClientConnector> pSocketServer) {
MultiplayerExample.this.toast("SERVER: Terminated.");
}
@Override
public void onException(final SocketServer<SocketConnectionClientConnector> pSocketServer, final Throwable pThrowable) {
Debug.e(pThrowable);
MultiplayerExample.this.toast("SERVER: Exception: " + pThrowable);
}
}
private class ExampleClientConnectorListener implements ISocketConnectionClientConnectorListener {
@Override
public void onStarted(final ClientConnector<SocketConnection> pConnector) {
MultiplayerExample.this.toast("SERVER: Client connected: " + pConnector.getConnection().getSocket().getInetAddress().getHostAddress());
}
@Override
public void onTerminated(final ClientConnector<SocketConnection> pConnector) {
MultiplayerExample.this.toast("SERVER: Client disconnected: " + pConnector.getConnection().getSocket().getInetAddress().getHostAddress());
}
}
}