project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java
License change: With the agreement of Xeli, I changed the Hedgeroid license to
GPLv2+ (from GPLv2).
/*
* Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
* Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.hedgewars.hedgeroid;
import java.net.ConnectException;
import org.hedgewars.hedgeroid.Datastructures.GameConfig;
import org.hedgewars.hedgeroid.frontlib.Flib;
import org.hedgewars.hedgeroid.frontlib.Frontlib;
import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback;
import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr;
import org.hedgewars.hedgeroid.frontlib.Frontlib.GameconnPtr;
import org.hedgewars.hedgeroid.frontlib.Frontlib.IntCallback;
import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback;
import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback;
import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback;
import org.hedgewars.hedgeroid.frontlib.NativeSizeT;
import org.hedgewars.hedgeroid.netplay.GameMessageListener;
import org.hedgewars.hedgeroid.netplay.Netplay;
import org.hedgewars.hedgeroid.util.TickHandler;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
/**
* This class handles both talking to the engine (IPC) for running a game, and
* coordinating with the netconn if it is a netgame, using the frontlib for the
* actual IPC networking communication.
*
* After creating the GameConnection object, it will communicate with the engine
* on its own thread. It shuts itself down as soon as the connection to the engine
* is lost.
*/
public final class GameConnection {
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
public final int port;
private final HandlerThread thread;
private final Handler handler;
private TickHandler tickHandler;
private final Netplay netplay; // ==null if not a netgame
private GameconnPtr conn;
private GameConnection(GameconnPtr conn, Netplay netplay) {
this.conn = conn;
this.port = Flib.INSTANCE.flib_gameconn_getport(conn);
this.netplay = netplay;
this.thread = new HandlerThread("IPCThread");
thread.start();
this.handler = new Handler(thread.getLooper());
}
private void setupConnection() {
tickHandler = new TickHandler(thread.getLooper(), 50, tickCb);
tickHandler.start();
if(netplay != null) {
mainHandler.post(new Runnable() {
public void run() {
netplay.registerGameMessageListener(gameMessageListener);
}
});
Flib.INSTANCE.flib_gameconn_onChat(conn, chatCb, null);
Flib.INSTANCE.flib_gameconn_onEngineMessage(conn, engineMessageCb, null);
}
Flib.INSTANCE.flib_gameconn_onConnect(conn, connectCb, null);
Flib.INSTANCE.flib_gameconn_onDisconnect(conn, disconnectCb, null);
Flib.INSTANCE.flib_gameconn_onErrorMessage(conn, errorMessageCb, null);
}
/**
* Start a new IPC server to communicate with the engine.
* Performs networking operations, don't run on the UI thread.
* @throws ConnectException if we can't set up the IPC server
*/
public static GameConnection forNetgame(final GameConfig config, Netplay netplay) throws ConnectException {
final String playerName = netplay.getPlayerName();
GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create(playerName, GameSetupPtr.createJavaOwned(config), true);
if(conn == null) {
throw new ConnectException();
}
GameConnection result = new GameConnection(conn, netplay);
result.setupConnection();
return result;
}
/**
* Start a new IPC server to communicate with the engine.
* Performs networking operations, don't run on the UI thread.
* @throws ConnectException if we can't set up the IPC server
*/
public static GameConnection forLocalGame(final GameConfig config) throws ConnectException {
GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Player", GameSetupPtr.createJavaOwned(config), false);
if(conn == null) {
throw new ConnectException();
}
GameConnection result = new GameConnection(conn, null);
result.setupConnection();
return result;
}
private final Runnable tickCb = new Runnable() {
public void run() {
Flib.INSTANCE.flib_gameconn_tick(conn);
}
};
// runs on the IPCThread
private void shutdown() {
tickHandler.stop();
thread.quit();
Flib.INSTANCE.flib_gameconn_destroy(conn);
conn = null;
if(netplay != null) {
mainHandler.post(new Runnable() {
public void run() {
netplay.unregisterGameMessageListener(gameMessageListener);
}
});
}
}
// runs on the IPCThread
private final StrBoolCallback chatCb = new StrBoolCallback() {
public void callback(Pointer context, String message, boolean teamChat) {
if(teamChat) {
netplay.sendTeamChat(message);
} else {
netplay.sendChat(message);
}
}
};
// runs on the IPCThread
private final VoidCallback connectCb = new VoidCallback() {
public void callback(Pointer context) {
Log.i("GameConnection", "Connected");
}
};
// runs on the IPCThread
private final IntCallback disconnectCb = new IntCallback() {
public void callback(Pointer context, int reason) {
if(netplay != null) {
netplay.sendRoundFinished(reason==Frontlib.GAME_END_FINISHED);
}
shutdown();
}
};
// runs on the IPCThread
private final BytesCallback engineMessageCb = new BytesCallback() {
public void callback(Pointer context, Pointer buffer, NativeSizeT size) {
netplay.sendEngineMessage(buffer.getByteArray(0, size.intValue()));
}
};
// runs on the IPCThread
private final StrCallback errorMessageCb = new StrCallback() {
public void callback(Pointer context, String message) {
Log.e("GameConnection", message);
}
};
// runs on any thread
private final GameMessageListener gameMessageListener = new GameMessageListener() {
public void onNetDisconnected() {
handler.post(new Runnable() {
public void run() {
Flib.INSTANCE.flib_gameconn_send_quit(conn);
}
});
}
public void onMessage(final int type, final String message) {
handler.post(new Runnable() {
public void run() {
Flib.INSTANCE.flib_gameconn_send_textmsg(conn, type, message);
}
});
}
public void onEngineMessage(final byte[] em) {
handler.post(new Runnable() {
public void run() {
Memory mem = new Memory(em.length);
mem.write(0, em, 0, em.length);
Flib.INSTANCE.flib_gameconn_send_enginemsg(conn, mem, NativeSizeT.valueOf(em.length));
}
});
}
public void onChatMessage(final String nick, final String message) {
handler.post(new Runnable() {
public void run() {
Flib.INSTANCE.flib_gameconn_send_chatmsg(conn, nick, message);
}
});
}
};
}