project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ThreadedNetConnection.java
changeset 7508 763d3961400b
child 7558 983ff426f91e
equal deleted inserted replaced
7504:ed1d52c5aa94 7508:763d3961400b
       
     1 package org.hedgewars.hedgeroid.netplay;
       
     2 
       
     3 import static org.hedgewars.hedgeroid.netplay.Netplay.FromNetMsgType.*;
       
     4 
       
     5 import java.io.File;
       
     6 import java.io.FileNotFoundException;
       
     7 import java.util.Arrays;
       
     8 import java.util.Collections;
       
     9 import java.util.List;
       
    10 
       
    11 import org.hedgewars.hedgeroid.R;
       
    12 import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
       
    13 import org.hedgewars.hedgeroid.Datastructures.Scheme;
       
    14 import org.hedgewars.hedgeroid.Datastructures.Team;
       
    15 import org.hedgewars.hedgeroid.Datastructures.Weaponset;
       
    16 import org.hedgewars.hedgeroid.frontlib.Flib;
       
    17 import org.hedgewars.hedgeroid.frontlib.Frontlib;
       
    18 import org.hedgewars.hedgeroid.frontlib.Frontlib.BoolCallback;
       
    19 import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback;
       
    20 import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr;
       
    21 import org.hedgewars.hedgeroid.frontlib.Frontlib.IntStrCallback;
       
    22 import org.hedgewars.hedgeroid.frontlib.Frontlib.MapIntCallback;
       
    23 import org.hedgewars.hedgeroid.frontlib.Frontlib.MapRecipePtr;
       
    24 import org.hedgewars.hedgeroid.frontlib.Frontlib.NetconnPtr;
       
    25 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomArrayPtr;
       
    26 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomCallback;
       
    27 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomListCallback;
       
    28 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomPtr;
       
    29 import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemeCallback;
       
    30 import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemePtr;
       
    31 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback;
       
    32 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback;
       
    33 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrIntCallback;
       
    34 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrRoomCallback;
       
    35 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrCallback;
       
    36 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamCallback;
       
    37 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr;
       
    38 import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback;
       
    39 import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetCallback;
       
    40 import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetPtr;
       
    41 import org.hedgewars.hedgeroid.netplay.Netplay.FromNetHandler;
       
    42 import org.hedgewars.hedgeroid.netplay.Netplay.FromNetMsgType;
       
    43 import org.hedgewars.hedgeroid.util.FileUtils;
       
    44 import org.hedgewars.hedgeroid.util.TickHandler;
       
    45 
       
    46 import android.annotation.SuppressLint;
       
    47 import android.content.Context;
       
    48 import android.content.res.Resources;
       
    49 import android.os.Handler;
       
    50 import android.os.HandlerThread;
       
    51 import android.os.Looper;
       
    52 import android.os.Message;
       
    53 import android.util.Log;
       
    54 import android.util.Pair;
       
    55 
       
    56 import com.sun.jna.Memory;
       
    57 import com.sun.jna.NativeLong;
       
    58 import com.sun.jna.Pointer;
       
    59 
       
    60 /**
       
    61  * This class handles the actual communication with the networking library, running on a separate thread.
       
    62  * 
       
    63  * In order to process net messages, this class regularly runs a tick() function on the frontlib. This
       
    64  * usually happens several times per second, but it can be slowed down a lot if no fast reaction to
       
    65  * events is required (e.g. to conserve battery if the application is in the background).
       
    66  */
       
    67 class ThreadedNetConnection {
       
    68 	private static final long TICK_INTERVAL_FAST = 100;
       
    69 	private static final long TICK_INTERVAL_SLOW = 5000;
       
    70 	private static final Frontlib FLIB = Flib.INSTANCE;
       
    71 	
       
    72 	public final ToNetHandler toNetHandler;
       
    73 	
       
    74 	private final Context appContext;
       
    75 	private final FromNetHandler fromNetHandler;
       
    76 	private final TickHandler tickHandler;
       
    77 
       
    78 	/**
       
    79 	 * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting,
       
    80 	 * in the same message (the looper is shut down on disconnect, so there will be no messages after that).
       
    81 	 */
       
    82 	private NetconnPtr conn;
       
    83 	private String playerName;
       
    84 	
       
    85 	private ThreadedNetConnection(Context appContext, FromNetHandler fromNetHandler) {
       
    86 		this.appContext = appContext;
       
    87 		this.fromNetHandler = fromNetHandler;
       
    88 		
       
    89 		HandlerThread thread = new HandlerThread("NetThread");
       
    90 		thread.start();
       
    91 		toNetHandler = new ToNetHandler(thread.getLooper());
       
    92 		tickHandler = new TickHandler(thread.getLooper(), TICK_INTERVAL_FAST, tickCb);
       
    93 	}
       
    94 	
       
    95 	private void connect(final String name, final String host, final int port) {
       
    96 		toNetHandler.post(new Runnable() {
       
    97 			public void run() {
       
    98 				playerName = name == null ? "Player" : name;
       
    99 				File dataPath;
       
   100 				try {
       
   101 					dataPath = FileUtils.getDataPathFile(appContext);
       
   102 				} catch (FileNotFoundException e) {
       
   103 					shutdown(true, appContext.getString(R.string.sdcard_not_mounted));
       
   104 					return;
       
   105 				}
       
   106 				conn = FLIB.flib_netconn_create(playerName, dataPath.getAbsolutePath()+"/", host, port);
       
   107 				if(conn == null) {
       
   108 					shutdown(true, appContext.getString(R.string.error_connection_failed));
       
   109 					return;
       
   110 				}
       
   111 
       
   112 				//FLIB.flib_netconn_onAdminAccess(conn, adminAccessCb, null)
       
   113 				FLIB.flib_netconn_onCfgScheme(conn, cfgSchemeCb, null);
       
   114 				FLIB.flib_netconn_onChat(conn, chatCb, null);
       
   115 				FLIB.flib_netconn_onConnected(conn, connectedCb, null);
       
   116 				FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null);
       
   117 				FLIB.flib_netconn_onEngineMessage(conn, engineMessageCb, null);
       
   118 				FLIB.flib_netconn_onEnterRoom(conn, enterRoomCb, null);
       
   119 				FLIB.flib_netconn_onHogCountChanged(conn, hogCountChangedCb, null);
       
   120 				FLIB.flib_netconn_onLeaveRoom(conn, leaveRoomCb, null);
       
   121 				FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null);
       
   122 				FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null);
       
   123 				FLIB.flib_netconn_onMapChanged(conn, mapChangedCb, null);
       
   124 				FLIB.flib_netconn_onMessage(conn, messageCb, null);
       
   125 				FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null);
       
   126 				FLIB.flib_netconn_onReadyState(conn, readyStateCb, null);
       
   127 				FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null);
       
   128 				FLIB.flib_netconn_onRoomChiefStatus(conn, roomChiefStatusCb, null);
       
   129 				FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null);
       
   130 				FLIB.flib_netconn_onRoomJoin(conn, roomJoinCb, null);
       
   131 				FLIB.flib_netconn_onRoomLeave(conn, roomLeaveCb, null);
       
   132 				FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null);
       
   133 				FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null);
       
   134 				FLIB.flib_netconn_onRunGame(conn, runGameCb, null);
       
   135 				FLIB.flib_netconn_onScriptChanged(conn, scriptChangedCb, null);
       
   136 				// FLIB.flib_netconn_onServerVar(conn, serverVarCb, null);
       
   137 				FLIB.flib_netconn_onTeamAccepted(conn, teamAcceptedCb, null);
       
   138 				FLIB.flib_netconn_onTeamAdd(conn, teamAddedCb, null);
       
   139 				FLIB.flib_netconn_onTeamColorChanged(conn, teamColorChangedCb, null);
       
   140 				FLIB.flib_netconn_onTeamDelete(conn, teamDeletedCb, null);
       
   141 				FLIB.flib_netconn_onWeaponsetChanged(conn, weaponsetChangedCb, null);
       
   142 				
       
   143 				tickHandler.start();
       
   144 			}
       
   145 		});
       
   146 	}
       
   147 	
       
   148 	public static ThreadedNetConnection startConnection(Context appContext, FromNetHandler fromNetHandler, String playerName, String host, int port) {
       
   149 		ThreadedNetConnection result = new ThreadedNetConnection(appContext, fromNetHandler);
       
   150 		result.connect(playerName, host, port);
       
   151 		return result;
       
   152 	}
       
   153 	
       
   154 	public void setFastTickRate(boolean fastTickRate) {
       
   155 		tickHandler.setInterval(fastTickRate ? TICK_INTERVAL_FAST : TICK_INTERVAL_SLOW);
       
   156 	}
       
   157 	
       
   158 	private final Runnable tickCb = new Runnable() {
       
   159 		public void run() {
       
   160 			FLIB.flib_netconn_tick(conn);
       
   161 		}
       
   162 	};
       
   163 	
       
   164 	private final SchemeCallback cfgSchemeCb = new SchemeCallback() {
       
   165 		public void callback(Pointer context, SchemePtr schemePtr) {
       
   166 			sendFromNet(MSG_SCHEME_CHANGED, schemePtr.deref());
       
   167 		}
       
   168 	};
       
   169 	
       
   170 	private final MapIntCallback mapChangedCb = new MapIntCallback() {
       
   171 		public void callback(Pointer context, MapRecipePtr mapPtr, int updateType) {
       
   172 			sendFromNet(MSG_MAP_CHANGED, updateType, mapPtr.deref());
       
   173 		}
       
   174 	};
       
   175 	
       
   176 	private final BoolCallback roomChiefStatusCb = new BoolCallback() {
       
   177 		public void callback(Pointer context, boolean chief) {
       
   178 			sendFromNet(MSG_ROOM_CHIEF_STATUS_CHANGED, chief);
       
   179 		}
       
   180 	};
       
   181 	
       
   182 	private final StrCallback scriptChangedCb = new StrCallback() {
       
   183 		public void callback(Pointer context, String script) {
       
   184 			sendFromNet(MSG_SCRIPT_CHANGED, script);
       
   185 		}
       
   186 	};
       
   187 	
       
   188 	private final WeaponsetCallback weaponsetChangedCb = new WeaponsetCallback() {
       
   189 		public void callback(Pointer context, WeaponsetPtr weaponsetPtr) {
       
   190 			sendFromNet(MSG_WEAPONSET_CHANGED, weaponsetPtr.deref());				
       
   191 		}
       
   192 	};
       
   193 	
       
   194 	private final StrCallback lobbyJoinCb = new StrCallback() {
       
   195 		public void callback(Pointer context, String name) {
       
   196 			sendFromNet(MSG_LOBBY_JOIN, name);
       
   197 		}
       
   198 	};
       
   199 	
       
   200 	private final StrStrCallback lobbyLeaveCb = new StrStrCallback() {
       
   201 		public void callback(Pointer context, String name, String msg) {
       
   202 			sendFromNet(MSG_LOBBY_LEAVE, Pair.create(name, msg));
       
   203 		}
       
   204 	};
       
   205 	
       
   206 	private final StrCallback roomJoinCb = new StrCallback() {
       
   207 		public void callback(Pointer context, String name) {
       
   208 			sendFromNet(MSG_ROOM_JOIN, name);
       
   209 		}
       
   210 	};
       
   211 	private final StrStrCallback roomLeaveCb = new StrStrCallback() {
       
   212 		public void callback(Pointer context, String name, String message) {
       
   213 			sendFromNet(MSG_ROOM_LEAVE, Pair.create(name, message));
       
   214 		}
       
   215 	};
       
   216 	private final StrStrCallback chatCb = new StrStrCallback() {
       
   217 		public void callback(Pointer context, String name, String msg) {
       
   218 			sendFromNet(MSG_CHAT, Pair.create(name, msg));
       
   219 		}
       
   220 	};
       
   221 	
       
   222 	private final IntStrCallback messageCb = new IntStrCallback() {
       
   223 		public void callback(Pointer context, int type, String msg) {
       
   224 			sendFromNet(MSG_MESSAGE, type, msg);
       
   225 		}
       
   226 	};
       
   227 	
       
   228 	private final RoomCallback roomAddCb = new RoomCallback() {
       
   229 		public void callback(Pointer context, RoomPtr roomPtr) {
       
   230 			sendFromNet(MSG_ROOM_ADD, roomPtr.deref());
       
   231 		}
       
   232 	};
       
   233 	
       
   234 	private final StrRoomCallback roomUpdateCb = new StrRoomCallback() {
       
   235 		public void callback(Pointer context, String name, RoomPtr roomPtr) {
       
   236 			sendFromNet(MSG_ROOM_UPDATE, Pair.create(name, roomPtr.deref()));
       
   237 		}
       
   238 	};
       
   239 	
       
   240 	private final StrCallback roomDeleteCb = new StrCallback() {
       
   241 		public void callback(Pointer context, final String name) {
       
   242 			sendFromNet(MSG_ROOM_DELETE, name);
       
   243 		}
       
   244 	};
       
   245 	
       
   246 	private final RoomListCallback roomlistCb = new RoomListCallback() {
       
   247 		public void callback(Pointer context, RoomArrayPtr arg1, int count) {
       
   248 			sendFromNet(MSG_ROOMLIST, arg1.getRooms(count));
       
   249 		}
       
   250 	};
       
   251 	
       
   252 	private final VoidCallback connectedCb = new VoidCallback() {
       
   253 		public void callback(Pointer context) {
       
   254 			FLIB.flib_netconn_send_request_roomlist(conn);
       
   255 			playerName = FLIB.flib_netconn_get_playername(conn);
       
   256 			sendFromNet(MSG_CONNECTED, playerName);
       
   257 		}
       
   258 	};
       
   259 	
       
   260 	private final StrCallback passwordRequestCb = new StrCallback() {
       
   261 		public void callback(Pointer context, String nickname) {
       
   262 			sendFromNet(MSG_PASSWORD_REQUEST, playerName);
       
   263 		}
       
   264 	};
       
   265 	
       
   266 	private final BoolCallback enterRoomCb = new BoolCallback() {
       
   267 		public void callback(Pointer context, boolean isChief) {
       
   268 			sendFromNet(MSG_ENTER_ROOM_FROM_LOBBY, isChief);
       
   269 		}
       
   270 	};
       
   271 	
       
   272 	private final IntStrCallback leaveRoomCb = new IntStrCallback() {
       
   273 		public void callback(Pointer context, int reason, String message) {
       
   274 			sendFromNet(MSG_LEAVE_ROOM, reason, message);
       
   275 		}
       
   276 	};
       
   277 	
       
   278 	private final StrBoolCallback readyStateCb = new StrBoolCallback() {
       
   279 		public void callback(Pointer context, String player, boolean ready) {
       
   280 			sendFromNet(MSG_READYSTATE, Pair.create(player, ready));
       
   281 		}
       
   282 	};
       
   283 	
       
   284 	private final TeamCallback teamAddedCb = new TeamCallback() {
       
   285 		public void callback(Pointer context, TeamPtr team) {
       
   286 			sendFromNet(MSG_TEAM_ADDED, team.deref().team);
       
   287 		}
       
   288 	};
       
   289 	
       
   290 	private final StrCallback teamDeletedCb = new StrCallback() {
       
   291 		public void callback(Pointer context, String teamName) {
       
   292 			sendFromNet(MSG_TEAM_DELETED, teamName);
       
   293 		}
       
   294 	};
       
   295 	
       
   296 	private final StrCallback teamAcceptedCb = new StrCallback() {
       
   297 		public void callback(Pointer context, String teamName) {
       
   298 			sendFromNet(MSG_TEAM_ACCEPTED, teamName);
       
   299 		}
       
   300 	};
       
   301 	
       
   302 	private final StrIntCallback teamColorChangedCb = new StrIntCallback() {
       
   303 		public void callback(Pointer context, String teamName, int colorIndex) {
       
   304 			sendFromNet(MSG_TEAM_COLOR_CHANGED, colorIndex, teamName);
       
   305 		}
       
   306 	};
       
   307 	
       
   308 	private final StrIntCallback hogCountChangedCb = new StrIntCallback() {
       
   309 		public void callback(Pointer context, String teamName, int hogCount) {
       
   310 			sendFromNet(MSG_HOG_COUNT_CHANGED, hogCount, teamName);
       
   311 		}
       
   312 	};
       
   313 	
       
   314 	private final BytesCallback engineMessageCb = new BytesCallback() {
       
   315 		public void callback(Pointer context, Pointer buffer, NativeLong size) {
       
   316 			sendFromNet(MSG_ENGINE_MESSAGE, buffer.getByteArray(0, size.intValue()));
       
   317 		}
       
   318 	};
       
   319 	
       
   320 	private final VoidCallback runGameCb = new VoidCallback() {
       
   321 		public void callback(Pointer context) {
       
   322 			GameSetupPtr configPtr = FLIB.flib_netconn_create_gamesetup(conn);
       
   323 			sendFromNet(MSG_RUN_GAME, configPtr.deref());
       
   324 			FLIB.flib_gamesetup_destroy(configPtr);
       
   325 		}
       
   326 	};
       
   327 	
       
   328 	private void shutdown(boolean error, String message) {
       
   329 		if(conn != null) {
       
   330 			FLIB.flib_netconn_destroy(conn);
       
   331 			conn = null;
       
   332 		}
       
   333 		tickHandler.stop();
       
   334 		toNetHandler.getLooper().quit();
       
   335 		sendFromNet(MSG_DISCONNECTED, Pair.create(error, message));
       
   336 	}
       
   337 	
       
   338 	private final IntStrCallback disconnectCb = new IntStrCallback() {
       
   339 		public void callback(Pointer context, int reason, String message) {
       
   340 			Boolean error = reason != Frontlib.NETCONN_DISCONNECT_NORMAL;
       
   341 			String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message);
       
   342 			shutdown(error, messageForUser);
       
   343 		}
       
   344 	};
       
   345 	
       
   346 	private static String createDisconnectUserMessage(Resources res, int reason, String message) {
       
   347 		switch(reason) {
       
   348 		case Frontlib.NETCONN_DISCONNECT_AUTH_FAILED:
       
   349 			return res.getString(R.string.error_auth_failed);
       
   350 		case Frontlib.NETCONN_DISCONNECT_CONNLOST:
       
   351 			return res.getString(R.string.error_connection_lost);
       
   352 		case Frontlib.NETCONN_DISCONNECT_INTERNAL_ERROR:
       
   353 			return res.getString(R.string.error_unexpected, message);
       
   354 		case Frontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD:
       
   355 			return res.getString(R.string.error_server_too_old);
       
   356 		default:
       
   357 			return message;
       
   358 		}
       
   359 	}
       
   360 	
       
   361 	private boolean sendFromNet(FromNetMsgType what, Object obj) {
       
   362 		return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what.ordinal(), obj));
       
   363 	}
       
   364 	
       
   365 	private boolean sendFromNet(FromNetMsgType what, int arg1, Object obj) {
       
   366 		return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what.ordinal(), arg1, 0, obj));
       
   367 	}
       
   368 	
       
   369 	static enum ToNetMsgType {
       
   370 		MSG_SEND_NICK,
       
   371 		MSG_SEND_PASSWORD,
       
   372 		MSG_SEND_QUIT,
       
   373 		MSG_SEND_ROOMLIST_REQUEST,
       
   374 		MSG_SEND_PLAYER_INFO_REQUEST,
       
   375 		MSG_SEND_CHAT,
       
   376 		MSG_SEND_TEAMCHAT,
       
   377 		MSG_SEND_FOLLOW_PLAYER,
       
   378 		MSG_SEND_JOIN_ROOM,
       
   379 		MSG_SEND_CREATE_ROOM,
       
   380 		MSG_SEND_LEAVE_ROOM,
       
   381 		MSG_SEND_KICK,
       
   382 		MSG_SEND_ADD_TEAM,
       
   383 		MSG_SEND_REMOVE_TEAM,
       
   384 		MSG_DISCONNECT,
       
   385 		MSG_SEND_TEAM_COLOR_INDEX,
       
   386 		MSG_SEND_TEAM_HOG_COUNT,
       
   387 		MSG_SEND_ENGINE_MESSAGE,
       
   388 		MSG_SEND_ROUND_FINISHED,
       
   389 		MSG_SEND_TOGGLE_READY,
       
   390 		MSG_SEND_WEAPONSET,
       
   391 		MSG_SEND_MAP,
       
   392 		MSG_SEND_MAP_NAME,
       
   393 		MSG_SEND_MAP_GENERATOR,
       
   394 		MSG_SEND_MAP_TEMPLATE,
       
   395 		MSG_SEND_MAZE_SIZE,
       
   396 		MSG_SEND_MAP_SEED,
       
   397 		MSG_SEND_MAP_THEME,
       
   398 		MSG_SEND_MAP_DRAWDATA,
       
   399 		MSG_SEND_GAMESTYLE,
       
   400 		MSG_SEND_SCHEME;
       
   401 		
       
   402 		static final List<ThreadedNetConnection.ToNetMsgType> values = Collections.unmodifiableList(Arrays.asList(ToNetMsgType.values()));
       
   403 	}
       
   404 	
       
   405 	/**
       
   406 	 * Processes messages to the networking system. Runs on a non-main thread.
       
   407 	 */
       
   408 	@SuppressLint("HandlerLeak")
       
   409 	public final class ToNetHandler extends Handler {
       
   410 		
       
   411 		public ToNetHandler(Looper looper) {
       
   412 			super(looper);
       
   413 		}
       
   414 		
       
   415 		@Override
       
   416 		public void handleMessage(Message msg) {
       
   417 			switch(ToNetMsgType.values.get(msg.what)) {
       
   418 			case MSG_SEND_NICK: {
       
   419 				FLIB.flib_netconn_send_nick(conn, (String)msg.obj);
       
   420 				break;
       
   421 			}
       
   422 			case MSG_SEND_PASSWORD: {
       
   423 				FLIB.flib_netconn_send_password(conn, (String)msg.obj);
       
   424 				break;
       
   425 			}
       
   426 			case MSG_SEND_QUIT: {
       
   427 				FLIB.flib_netconn_send_quit(conn, (String)msg.obj);
       
   428 				break;
       
   429 			}
       
   430 			case MSG_SEND_ROOMLIST_REQUEST: {
       
   431 				FLIB.flib_netconn_send_request_roomlist(conn);
       
   432 				break;
       
   433 			}
       
   434 			case MSG_SEND_PLAYER_INFO_REQUEST: {
       
   435 				FLIB.flib_netconn_send_playerInfo(conn, (String)msg.obj);
       
   436 				break;
       
   437 			}
       
   438 			case MSG_SEND_CHAT: {
       
   439 				if(FLIB.flib_netconn_send_chat(conn, (String)msg.obj) == 0) {
       
   440 					sendFromNet(MSG_CHAT, Pair.create(playerName, (String)msg.obj));
       
   441 				}
       
   442 				break;
       
   443 			}
       
   444 			case MSG_SEND_TEAMCHAT: {
       
   445 				FLIB.flib_netconn_send_teamchat(conn, (String)msg.obj);
       
   446 				break;
       
   447 			}
       
   448 			case MSG_SEND_FOLLOW_PLAYER: {
       
   449 				FLIB.flib_netconn_send_playerFollow(conn, (String)msg.obj);
       
   450 				break;
       
   451 			}
       
   452 			case MSG_SEND_JOIN_ROOM: {
       
   453 				FLIB.flib_netconn_send_joinRoom(conn, (String)msg.obj);
       
   454 				break;
       
   455 			}
       
   456 			case MSG_SEND_CREATE_ROOM: {
       
   457 				FLIB.flib_netconn_send_createRoom(conn, (String)msg.obj);
       
   458 				break;
       
   459 			}
       
   460 			case MSG_SEND_LEAVE_ROOM: {
       
   461 				if(FLIB.flib_netconn_send_leaveRoom(conn, (String)msg.obj) == 0) {
       
   462 					sendFromNet(MSG_LEAVE_ROOM, -1, "");
       
   463 				}
       
   464 				break;
       
   465 			}
       
   466 			case MSG_SEND_KICK: {
       
   467 				FLIB.flib_netconn_send_kick(conn, (String)msg.obj);
       
   468 				break;
       
   469 			}
       
   470 			case MSG_SEND_ADD_TEAM: {
       
   471 				FLIB.flib_netconn_send_addTeam(conn, TeamPtr.createJavaOwned((Team)msg.obj));
       
   472 				break;
       
   473 			}
       
   474 			case MSG_SEND_REMOVE_TEAM: {
       
   475 				if(FLIB.flib_netconn_send_removeTeam(conn, (String)msg.obj)==0) {
       
   476 					sendFromNet(MSG_TEAM_DELETED, msg.obj);
       
   477 				}
       
   478 				break;
       
   479 			}
       
   480 			case MSG_DISCONNECT: {
       
   481 				FLIB.flib_netconn_send_quit(conn, (String)msg.obj);
       
   482 				shutdown(false, "User quit");
       
   483 				break;
       
   484 			}
       
   485 			case MSG_SEND_TEAM_COLOR_INDEX: {
       
   486 				if(FLIB.flib_netconn_send_teamColor(conn, (String)msg.obj, msg.arg1)==0) {
       
   487 					sendFromNet(MSG_TEAM_COLOR_CHANGED, msg.arg1, msg.obj);
       
   488 				}
       
   489 				break;
       
   490 			}
       
   491 			case MSG_SEND_TEAM_HOG_COUNT: {
       
   492 				if(FLIB.flib_netconn_send_teamHogCount(conn, (String)msg.obj, msg.arg1)==0) {
       
   493 					sendFromNet(MSG_HOG_COUNT_CHANGED, msg.arg1, msg.obj);
       
   494 				}
       
   495 				break;
       
   496 			}
       
   497 			case MSG_SEND_ENGINE_MESSAGE: {
       
   498 				byte[] message = (byte[])msg.obj;
       
   499 				Memory mem = new Memory(message.length);
       
   500 				mem.write(0, message, 0, message.length);
       
   501 				FLIB.flib_netconn_send_engineMessage(conn, mem, new NativeLong(message.length));
       
   502 				break;
       
   503 			}
       
   504 			case MSG_SEND_ROUND_FINISHED: {
       
   505 				FLIB.flib_netconn_send_roundfinished(conn, (Boolean)msg.obj);
       
   506 				break;
       
   507 			}
       
   508 			case MSG_SEND_TOGGLE_READY: {
       
   509 				FLIB.flib_netconn_send_toggleReady(conn);
       
   510 				break;
       
   511 			}
       
   512 			case MSG_SEND_WEAPONSET: {
       
   513 				FLIB.flib_netconn_send_weaponset(conn, WeaponsetPtr.createJavaOwned((Weaponset)msg.obj));
       
   514 				break;
       
   515 			}
       
   516 			case MSG_SEND_MAP: {
       
   517 				FLIB.flib_netconn_send_map(conn, MapRecipePtr.createJavaOwned((MapRecipe)msg.obj));
       
   518 				break;
       
   519 			}
       
   520 			case MSG_SEND_MAP_NAME: {
       
   521 				FLIB.flib_netconn_send_mapName(conn, (String)msg.obj);
       
   522 				break;
       
   523 			}
       
   524 			case MSG_SEND_MAP_GENERATOR: {
       
   525 				FLIB.flib_netconn_send_mapGen(conn, msg.arg1);
       
   526 				break;
       
   527 			}
       
   528 			case MSG_SEND_MAP_TEMPLATE: {
       
   529 				FLIB.flib_netconn_send_mapTemplate(conn, msg.arg1);
       
   530 				break;
       
   531 			}
       
   532 			case MSG_SEND_MAZE_SIZE: {
       
   533 				FLIB.flib_netconn_send_mapMazeSize(conn, msg.arg1);
       
   534 				break;
       
   535 			}
       
   536 			case MSG_SEND_MAP_SEED: {
       
   537 				FLIB.flib_netconn_send_mapSeed(conn, (String) msg.obj);
       
   538 				break;
       
   539 			}
       
   540 			case MSG_SEND_MAP_THEME: {
       
   541 				FLIB.flib_netconn_send_mapTheme(conn, (String) msg.obj);
       
   542 				break;
       
   543 			}
       
   544 			case MSG_SEND_MAP_DRAWDATA: {
       
   545 				byte[] message = (byte[])msg.obj;
       
   546 				Memory mem = new Memory(message.length);
       
   547 				mem.write(0, message, 0, message.length);
       
   548 				FLIB.flib_netconn_send_mapDrawdata(conn, mem, new NativeLong(message.length));
       
   549 				break;
       
   550 			}
       
   551 			case MSG_SEND_GAMESTYLE: {
       
   552 				FLIB.flib_netconn_send_script(conn, (String) msg.obj);
       
   553 				break;
       
   554 			}
       
   555 			case MSG_SEND_SCHEME: {
       
   556 				FLIB.flib_netconn_send_scheme(conn, SchemePtr.createJavaOwned((Scheme) msg.obj));
       
   557 				break;
       
   558 			}
       
   559 			default: {
       
   560 				Log.e("ToNetHandler", "Unknown message type: "+msg.what);
       
   561 				break;
       
   562 			}
       
   563 			}
       
   564 		}
       
   565 	}
       
   566 }