|
1 package org.hedgewars.hedgeroid.netplay; |
|
2 |
|
3 import java.io.File; |
|
4 import java.io.FileNotFoundException; |
|
5 |
|
6 import org.hedgewars.hedgeroid.R; |
|
7 import org.hedgewars.hedgeroid.Utils; |
|
8 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.IntStrCallback; |
|
9 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.MetaschemePtr; |
|
10 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.NetconnPtr; |
|
11 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomArrayPtr; |
|
12 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomCallback; |
|
13 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomListCallback; |
|
14 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomPtr; |
|
15 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrCallback; |
|
16 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrRoomCallback; |
|
17 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrStrCallback; |
|
18 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.VoidCallback; |
|
19 |
|
20 import android.content.Context; |
|
21 import android.content.Intent; |
|
22 import android.content.res.Resources; |
|
23 import android.os.Handler; |
|
24 import android.os.HandlerThread; |
|
25 import android.os.Looper; |
|
26 import android.os.Message; |
|
27 import android.support.v4.content.LocalBroadcastManager; |
|
28 import android.util.Log; |
|
29 import android.util.Pair; |
|
30 |
|
31 import com.sun.jna.Pointer; |
|
32 |
|
33 /** |
|
34 * This class manages the application's networking state. |
|
35 */ |
|
36 public class Netplay { |
|
37 public static enum State { NOT_CONNECTED, CONNECTING, LOBBY, ROOM, INGAME } |
|
38 |
|
39 // Extras in broadcasts |
|
40 public static final String EXTRA_PLAYERNAME = "playerName"; |
|
41 public static final String EXTRA_MESSAGE = "message"; |
|
42 public static final String EXTRA_HAS_ERROR = "hasError"; |
|
43 |
|
44 private static final String ACTIONPREFIX = "org.hedgewars.hedgeroid.netconn."; |
|
45 public static final String ACTION_DISCONNECTED = ACTIONPREFIX+"DISCONNECTED"; |
|
46 public static final String ACTION_CONNECTED = ACTIONPREFIX+"CONNECTED"; |
|
47 public static final String ACTION_PASSWORD_REQUESTED = ACTIONPREFIX+"PASSWORD_REQUESTED"; |
|
48 |
|
49 public static final String DEFAULT_SERVER = "netserver.hedgewars.org"; |
|
50 public static final int DEFAULT_PORT = 46631; |
|
51 |
|
52 private final Context appContext; |
|
53 private final LocalBroadcastManager broadcastManager; |
|
54 private final FromNetHandler fromNetHandler = new FromNetHandler(); |
|
55 |
|
56 private State state; |
|
57 private int foregroundUsers = 0; |
|
58 |
|
59 // null if there is no running connection (==state is NOT_CONNECTED) |
|
60 private ThreadedNetConnection connection; |
|
61 |
|
62 public final PlayerList playerList = new PlayerList(); |
|
63 public final RoomList roomList = new RoomList(); |
|
64 public final MessageLog lobbyChatlog; |
|
65 public final MessageLog roomChatlog; |
|
66 |
|
67 public Netplay(Context appContext) { |
|
68 this.appContext = appContext; |
|
69 broadcastManager = LocalBroadcastManager.getInstance(appContext); |
|
70 lobbyChatlog = new MessageLog(appContext); |
|
71 roomChatlog = new MessageLog(appContext); |
|
72 state = State.NOT_CONNECTED; |
|
73 } |
|
74 |
|
75 private void clearState() { |
|
76 playerList.clear(); |
|
77 roomList.clear(); |
|
78 lobbyChatlog.clear(); |
|
79 roomChatlog.clear(); |
|
80 } |
|
81 |
|
82 public void connectToDefaultServer(String playerName) { |
|
83 connect(playerName, DEFAULT_SERVER, DEFAULT_PORT); |
|
84 } |
|
85 |
|
86 /** |
|
87 * Establish a new connection. Only call if the current state is NOT_CONNECTED. |
|
88 * |
|
89 * The state will switch to CONNECTING immediately. After that, it can asynchronously change to any other state. |
|
90 * State changes are indicated by broadcasts. In particular, if an error occurs while trying to connect, the state |
|
91 * will change back to NOT_CONNECTED and an ACTION_DISCONNECTED broadcast is sent. |
|
92 */ |
|
93 public void connect(String name, String host, int port) { |
|
94 if(state != State.NOT_CONNECTED) { |
|
95 throw new IllegalStateException("Attempt to start a new connection while the old one was still running."); |
|
96 } |
|
97 |
|
98 clearState(); |
|
99 state = State.CONNECTING; |
|
100 connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port); |
|
101 connection.setFastTickRate(foregroundUsers > 0); |
|
102 } |
|
103 |
|
104 public void sendNick(String nick) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_NICK, nick); } |
|
105 public void sendPassword(String password) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PASSWORD, password); } |
|
106 public void sendQuit(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_QUIT, message); } |
|
107 public void sendRoomlistRequest() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ROOMLIST_REQUEST); } |
|
108 public void sendPlayerInfoQuery(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PLAYER_INFO_REQUEST, name); } |
|
109 public void sendChat(final String s) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CHAT, s); } |
|
110 public void disconnect() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_DISCONNECT, "User Quit"); } |
|
111 |
|
112 private static Netplay instance; |
|
113 |
|
114 /** |
|
115 * Retrieve the single app-wide instance of the netplay interface, creating it if it |
|
116 * does not exist yet. |
|
117 * |
|
118 * @param applicationContext |
|
119 * @return |
|
120 */ |
|
121 public static Netplay getAppInstance(Context applicationContext) { |
|
122 if(instance == null) { |
|
123 // We'll just do it here and never quit it again... |
|
124 if(Flib.INSTANCE.flib_init() != 0) { |
|
125 throw new RuntimeException("Unable to start frontlib"); |
|
126 } |
|
127 instance = new Netplay(applicationContext); |
|
128 } |
|
129 return instance; |
|
130 } |
|
131 |
|
132 public State getState() { |
|
133 return state; |
|
134 } |
|
135 |
|
136 /** |
|
137 * Indicate that you want network messages to be checked regularly (several times per second). |
|
138 * As long as nobody requests fast ticks, the network is only checked once every few seconds |
|
139 * to conserve battery power. |
|
140 * Once you no longer need fast updates, call unrequestFastTicks. |
|
141 */ |
|
142 public void requestFastTicks() { |
|
143 if(foregroundUsers == Integer.MAX_VALUE) { |
|
144 throw new RuntimeException("Reference counter overflow"); |
|
145 } |
|
146 if(foregroundUsers == 0 && connection != null) { |
|
147 connection.setFastTickRate(true); |
|
148 } |
|
149 foregroundUsers++; |
|
150 } |
|
151 |
|
152 public void unrequestFastTicks() { |
|
153 if(foregroundUsers == 0) { |
|
154 throw new RuntimeException("Reference counter underflow"); |
|
155 } |
|
156 foregroundUsers--; |
|
157 if(foregroundUsers == 0 && connection != null) { |
|
158 connection.setFastTickRate(false); |
|
159 } |
|
160 } |
|
161 |
|
162 private boolean sendToNet(int what) { |
|
163 if(connection != null) { |
|
164 Handler handler = connection.toNetHandler; |
|
165 return handler.sendMessage(handler.obtainMessage(what)); |
|
166 } else { |
|
167 return false; |
|
168 } |
|
169 } |
|
170 |
|
171 private boolean sendToNet(int what, Object obj) { |
|
172 if(connection != null) { |
|
173 Handler handler = connection.toNetHandler; |
|
174 return handler.sendMessage(handler.obtainMessage(what, obj)); |
|
175 } else { |
|
176 return false; |
|
177 } |
|
178 } |
|
179 |
|
180 private MessageLog getCurrentLog() { |
|
181 if(state == State.ROOM || state == State.INGAME) { |
|
182 return roomChatlog; |
|
183 } else { |
|
184 return lobbyChatlog; |
|
185 } |
|
186 } |
|
187 |
|
188 /** |
|
189 * Processes messages from the networking system. Always runs on the main thread. |
|
190 */ |
|
191 final class FromNetHandler extends Handler { |
|
192 public static final int MSG_LOBBY_JOIN = 0; |
|
193 public static final int MSG_LOBBY_LEAVE = 1; |
|
194 public static final int MSG_CHAT = 2; |
|
195 public static final int MSG_MESSAGE = 3; |
|
196 public static final int MSG_ROOM_ADD = 4; |
|
197 public static final int MSG_ROOM_UPDATE = 5; |
|
198 public static final int MSG_ROOM_DELETE = 6; |
|
199 public static final int MSG_ROOMLIST = 7; |
|
200 public static final int MSG_CONNECTED = 8; |
|
201 public static final int MSG_DISCONNECTED = 9; |
|
202 public static final int MSG_PASSWORD_REQUEST = 10; |
|
203 |
|
204 public FromNetHandler() { |
|
205 super(Looper.getMainLooper()); |
|
206 } |
|
207 |
|
208 @SuppressWarnings("unchecked") |
|
209 @Override |
|
210 public void handleMessage(Message msg) { |
|
211 switch(msg.what) { |
|
212 case MSG_LOBBY_JOIN: { |
|
213 String name = (String)msg.obj; |
|
214 playerList.addPlayerWithNewId(name); |
|
215 lobbyChatlog.appendPlayerJoin(name); |
|
216 break; |
|
217 } |
|
218 case MSG_LOBBY_LEAVE: { |
|
219 Pair<String, String> args = (Pair<String, String>)msg.obj; |
|
220 playerList.removePlayer(args.first); |
|
221 lobbyChatlog.appendPlayerLeave(args.first, args.second); |
|
222 break; |
|
223 } |
|
224 case MSG_CHAT: { |
|
225 Pair<String, String> args = (Pair<String, String>)msg.obj; |
|
226 getCurrentLog().appendChat(args.first, args.second); |
|
227 break; |
|
228 } |
|
229 case MSG_MESSAGE: { |
|
230 getCurrentLog().appendMessage(msg.arg1, (String)msg.obj); |
|
231 break; |
|
232 } |
|
233 case MSG_ROOM_ADD: { |
|
234 roomList.addRoomWithNewId((Room)msg.obj); |
|
235 break; |
|
236 } |
|
237 case MSG_ROOM_UPDATE: { |
|
238 Pair<String, Room> args = (Pair<String, Room>)msg.obj; |
|
239 roomList.updateRoom(args.first, args.second); |
|
240 break; |
|
241 } |
|
242 case MSG_ROOM_DELETE: { |
|
243 roomList.removeRoom((String)msg.obj); |
|
244 break; |
|
245 } |
|
246 case MSG_ROOMLIST: { |
|
247 roomList.updateList((Room[])msg.obj); |
|
248 break; |
|
249 } |
|
250 case MSG_CONNECTED: { |
|
251 state = State.LOBBY; |
|
252 broadcastManager.sendBroadcast(new Intent(ACTION_CONNECTED)); |
|
253 break; |
|
254 } |
|
255 case MSG_DISCONNECTED: { |
|
256 Pair<Boolean, String> args = (Pair<Boolean, String>)msg.obj; |
|
257 state = State.NOT_CONNECTED; |
|
258 connection = null; |
|
259 Intent intent = new Intent(ACTION_DISCONNECTED); |
|
260 intent.putExtra(EXTRA_HAS_ERROR, args.first); |
|
261 intent.putExtra(EXTRA_MESSAGE, args.second); |
|
262 broadcastManager.sendBroadcastSync(intent); |
|
263 break; |
|
264 } |
|
265 case MSG_PASSWORD_REQUEST: { |
|
266 Intent intent = new Intent(ACTION_PASSWORD_REQUESTED); |
|
267 intent.putExtra(EXTRA_PLAYERNAME, (String)msg.obj); |
|
268 broadcastManager.sendBroadcast(intent); |
|
269 break; |
|
270 } |
|
271 default: { |
|
272 Log.e("FromNetHandler", "Unknown message type: "+msg.what); |
|
273 break; |
|
274 } |
|
275 } |
|
276 } |
|
277 } |
|
278 |
|
279 /** |
|
280 * This class handles the actual communication with the networking library, on a separate thread. |
|
281 */ |
|
282 private static class ThreadedNetConnection { |
|
283 private static final long TICK_INTERVAL_FAST = 100; |
|
284 private static final long TICK_INTERVAL_SLOW = 5000; |
|
285 private static final JnaFrontlib FLIB = Flib.INSTANCE; |
|
286 |
|
287 public final ToNetHandler toNetHandler; |
|
288 |
|
289 private final Context appContext; |
|
290 private final FromNetHandler fromNetHandler; |
|
291 private final TickHandler tickHandler; |
|
292 |
|
293 /** |
|
294 * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting, |
|
295 * in the same message (the looper is shut down on disconnect, so there will be no messages after that). |
|
296 */ |
|
297 private NetconnPtr conn; |
|
298 private String playerName; |
|
299 |
|
300 private ThreadedNetConnection(Context appContext, FromNetHandler fromNetHandler) { |
|
301 this.appContext = appContext; |
|
302 this.fromNetHandler = fromNetHandler; |
|
303 |
|
304 HandlerThread thread = new HandlerThread("NetThread"); |
|
305 thread.start(); |
|
306 toNetHandler = new ToNetHandler(thread.getLooper()); |
|
307 tickHandler = new TickHandler(thread.getLooper(), TICK_INTERVAL_FAST, tickCb); |
|
308 } |
|
309 |
|
310 private void connect(final String name, final String host, final int port) { |
|
311 toNetHandler.post(new Runnable() { |
|
312 public void run() { |
|
313 playerName = name == null ? "Player" : name; |
|
314 MetaschemePtr meta = null; |
|
315 File dataPath; |
|
316 try { |
|
317 dataPath = Utils.getDataPathFile(appContext); |
|
318 } catch (FileNotFoundException e) { |
|
319 shutdown(true, appContext.getString(R.string.sdcard_not_mounted)); |
|
320 return; |
|
321 } |
|
322 String metaschemePath = new File(dataPath, "metasettings.ini").getAbsolutePath(); |
|
323 meta = FLIB.flib_metascheme_from_ini(metaschemePath); |
|
324 if(meta == null) { |
|
325 shutdown(true, appContext.getString(R.string.error_unexpected, "Missing metasettings.ini")); |
|
326 return; |
|
327 } |
|
328 conn = FLIB.flib_netconn_create(playerName, meta, dataPath.getAbsolutePath(), host, port); |
|
329 if(conn == null) { |
|
330 shutdown(true, appContext.getString(R.string.error_connection_failed)); |
|
331 return; |
|
332 } |
|
333 FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null); |
|
334 FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null); |
|
335 FLIB.flib_netconn_onChat(conn, chatCb, null); |
|
336 FLIB.flib_netconn_onMessage(conn, messageCb, null); |
|
337 FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null); |
|
338 FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null); |
|
339 FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null); |
|
340 FLIB.flib_netconn_onConnected(conn, connectedCb, null); |
|
341 FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null); |
|
342 FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null); |
|
343 FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null); |
|
344 FLIB.flib_metascheme_release(meta); |
|
345 tickHandler.start(); |
|
346 } |
|
347 }); |
|
348 } |
|
349 |
|
350 public static ThreadedNetConnection startConnection(Context appContext, FromNetHandler fromNetHandler, String playerName, String host, int port) { |
|
351 ThreadedNetConnection result = new ThreadedNetConnection(appContext, fromNetHandler); |
|
352 result.connect(playerName, host, port); |
|
353 return result; |
|
354 } |
|
355 |
|
356 public void setFastTickRate(boolean fastTickRate) { |
|
357 tickHandler.setInterval(fastTickRate ? TICK_INTERVAL_FAST : TICK_INTERVAL_SLOW); |
|
358 } |
|
359 |
|
360 private final Runnable tickCb = new Runnable() { |
|
361 public void run() { |
|
362 FLIB.flib_netconn_tick(conn); |
|
363 } |
|
364 }; |
|
365 |
|
366 private final StrCallback lobbyJoinCb = new StrCallback() { |
|
367 public void callback(Pointer context, String name) { |
|
368 sendFromNet(FromNetHandler.MSG_LOBBY_JOIN, name); |
|
369 } |
|
370 }; |
|
371 |
|
372 private final StrStrCallback lobbyLeaveCb = new StrStrCallback() { |
|
373 public void callback(Pointer context, String name, String msg) { |
|
374 sendFromNet(FromNetHandler.MSG_LOBBY_LEAVE, Pair.create(name, msg)); |
|
375 } |
|
376 }; |
|
377 |
|
378 private final StrStrCallback chatCb = new StrStrCallback() { |
|
379 public void callback(Pointer context, String name, String msg) { |
|
380 sendFromNet(FromNetHandler.MSG_CHAT, Pair.create(name, msg)); |
|
381 } |
|
382 }; |
|
383 |
|
384 private final IntStrCallback messageCb = new IntStrCallback() { |
|
385 public void callback(Pointer context, int type, String msg) { |
|
386 sendFromNet(FromNetHandler.MSG_MESSAGE, type, msg); |
|
387 } |
|
388 }; |
|
389 |
|
390 private final RoomCallback roomAddCb = new RoomCallback() { |
|
391 public void callback(Pointer context, RoomPtr roomPtr) { |
|
392 sendFromNet(FromNetHandler.MSG_ROOM_ADD, roomPtr.deref()); |
|
393 } |
|
394 }; |
|
395 |
|
396 private final StrRoomCallback roomUpdateCb = new StrRoomCallback() { |
|
397 public void callback(Pointer context, String name, RoomPtr roomPtr) { |
|
398 sendFromNet(FromNetHandler.MSG_ROOM_UPDATE, Pair.create(name, roomPtr.deref())); |
|
399 } |
|
400 }; |
|
401 |
|
402 private final StrCallback roomDeleteCb = new StrCallback() { |
|
403 public void callback(Pointer context, final String name) { |
|
404 sendFromNet(FromNetHandler.MSG_ROOM_DELETE, name); |
|
405 } |
|
406 }; |
|
407 |
|
408 private final RoomListCallback roomlistCb = new RoomListCallback() { |
|
409 public void callback(Pointer context, RoomArrayPtr arg1, int count) { |
|
410 sendFromNet(FromNetHandler.MSG_ROOMLIST, arg1.getRooms(count)); |
|
411 } |
|
412 }; |
|
413 |
|
414 private final VoidCallback connectedCb = new VoidCallback() { |
|
415 public void callback(Pointer context) { |
|
416 FLIB.flib_netconn_send_request_roomlist(conn); |
|
417 playerName = FLIB.flib_netconn_get_playername(conn); |
|
418 sendFromNet(FromNetHandler.MSG_CONNECTED, playerName); |
|
419 } |
|
420 }; |
|
421 |
|
422 private final StrCallback passwordRequestCb = new StrCallback() { |
|
423 public void callback(Pointer context, String nickname) { |
|
424 sendFromNet(FromNetHandler.MSG_PASSWORD_REQUEST, playerName); |
|
425 } |
|
426 }; |
|
427 |
|
428 private void shutdown(boolean error, String message) { |
|
429 if(conn != null) { |
|
430 FLIB.flib_netconn_destroy(conn); |
|
431 conn = null; |
|
432 } |
|
433 tickHandler.stop(); |
|
434 toNetHandler.getLooper().quit(); |
|
435 sendFromNet(FromNetHandler.MSG_DISCONNECTED, Pair.create(error, message)); |
|
436 } |
|
437 |
|
438 private final IntStrCallback disconnectCb = new IntStrCallback() { |
|
439 public void callback(Pointer context, int reason, String message) { |
|
440 Boolean error = reason != JnaFrontlib.NETCONN_DISCONNECT_NORMAL; |
|
441 String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message); |
|
442 shutdown(error, messageForUser); |
|
443 } |
|
444 }; |
|
445 |
|
446 private static String createDisconnectUserMessage(Resources res, int reason, String message) { |
|
447 switch(reason) { |
|
448 case JnaFrontlib.NETCONN_DISCONNECT_AUTH_FAILED: |
|
449 return res.getString(R.string.error_auth_failed); |
|
450 case JnaFrontlib.NETCONN_DISCONNECT_CONNLOST: |
|
451 return res.getString(R.string.error_connection_lost, message); |
|
452 case JnaFrontlib.NETCONN_DISCONNECT_INTERNAL_ERROR: |
|
453 return res.getString(R.string.error_unexpected, message); |
|
454 case JnaFrontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD: |
|
455 return res.getString(R.string.error_server_too_old); |
|
456 default: |
|
457 return message; |
|
458 } |
|
459 } |
|
460 |
|
461 private boolean sendFromNet(int what, Object obj) { |
|
462 return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what, obj)); |
|
463 } |
|
464 |
|
465 private boolean sendFromNet(int what, int arg1, Object obj) { |
|
466 return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what, arg1, 0, obj)); |
|
467 } |
|
468 |
|
469 /** |
|
470 * Processes messages to the networking system. Runs on a non-main thread. |
|
471 */ |
|
472 public final class ToNetHandler extends Handler { |
|
473 public static final int MSG_SEND_NICK = 0; |
|
474 public static final int MSG_SEND_PASSWORD = 1; |
|
475 public static final int MSG_SEND_QUIT = 2; |
|
476 public static final int MSG_SEND_ROOMLIST_REQUEST = 3; |
|
477 public static final int MSG_SEND_PLAYER_INFO_REQUEST = 4; |
|
478 public static final int MSG_SEND_CHAT = 5; |
|
479 public static final int MSG_DISCONNECT = 6; |
|
480 |
|
481 public ToNetHandler(Looper looper) { |
|
482 super(looper); |
|
483 } |
|
484 |
|
485 @Override |
|
486 public void handleMessage(Message msg) { |
|
487 switch(msg.what) { |
|
488 case MSG_SEND_NICK: { |
|
489 FLIB.flib_netconn_send_nick(conn, (String)msg.obj); |
|
490 break; |
|
491 } |
|
492 case MSG_SEND_PASSWORD: { |
|
493 FLIB.flib_netconn_send_password(conn, (String)msg.obj); |
|
494 break; |
|
495 } |
|
496 case MSG_SEND_QUIT: { |
|
497 FLIB.flib_netconn_send_quit(conn, (String)msg.obj); |
|
498 break; |
|
499 } |
|
500 case MSG_SEND_ROOMLIST_REQUEST: { |
|
501 FLIB.flib_netconn_send_request_roomlist(conn); // TODO restrict to lobby state? |
|
502 break; |
|
503 } |
|
504 case MSG_SEND_PLAYER_INFO_REQUEST: { |
|
505 FLIB.flib_netconn_send_playerInfo(conn, (String)msg.obj); |
|
506 break; |
|
507 } |
|
508 case MSG_SEND_CHAT: { |
|
509 if(FLIB.flib_netconn_send_chat(conn, (String)msg.obj) == 0) { |
|
510 sendFromNet(FromNetHandler.MSG_CHAT, Pair.create(playerName, (String)msg.obj)); |
|
511 } |
|
512 break; |
|
513 } |
|
514 case MSG_DISCONNECT: { |
|
515 FLIB.flib_netconn_send_quit(conn, (String)msg.obj); |
|
516 shutdown(false, "User quit"); |
|
517 break; |
|
518 } |
|
519 default: { |
|
520 Log.e("ToNetHandler", "Unknown message type: "+msg.what); |
|
521 break; |
|
522 } |
|
523 } |
|
524 } |
|
525 } |
|
526 } |
|
527 } |