1 package org.hedgewars.hedgeroid.netplay; |
1 package org.hedgewars.hedgeroid.netplay; |
2 |
2 |
3 import java.io.IOException; |
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 com.sun.jna.Pointer; |
4 |
21 |
5 import android.app.Service; |
22 import android.app.Service; |
6 import android.content.Intent; |
23 import android.content.Intent; |
7 import android.os.Binder; |
24 import android.os.Binder; |
8 import android.os.CountDownTimer; |
|
9 import android.os.IBinder; |
25 import android.os.IBinder; |
|
26 import android.support.v4.content.LocalBroadcastManager; |
10 import android.util.Log; |
27 import android.util.Log; |
11 |
28 |
12 public class NetplayService extends Service { |
29 public class NetplayService extends Service { |
|
30 // Parameter extras for starting the service |
|
31 public static final String EXTRA_PLAYERNAME = "playername"; |
|
32 public static final String EXTRA_PORT = "port"; |
|
33 public static final String EXTRA_HOST = "host"; |
|
34 |
|
35 // Extras in broadcasts |
|
36 public static final String EXTRA_MESSAGE = "message"; |
|
37 public static final String EXTRA_HAS_ERROR = "hasError"; |
|
38 |
|
39 |
|
40 private static final String ACTIONPREFIX = "org.hedgewars.hedgeroid.netconn."; |
|
41 public static final String ACTION_DISCONNECTED = ACTIONPREFIX+"DISCONNECTED"; |
|
42 public static final String ACTION_CONNECTED = ACTIONPREFIX+"CONNECTED"; |
|
43 |
|
44 private static final JnaFrontlib FLIB = Flib.INSTANCE; |
|
45 public static final String DEFAULT_SERVER = "netserver.hedgewars.org"; |
|
46 public static final int DEFAULT_PORT = 46631; |
|
47 |
|
48 private static final long TICK_INTERVAL_MS_BOUND = 100; |
|
49 private static final long TICK_INTERVAL_MS_UNBOUND = 2000; |
|
50 |
|
51 // null if the service is not active. Only updated from the main thread. |
|
52 public static NetplayService instance; |
|
53 |
13 private final NetplayBinder binder = new NetplayBinder(); |
54 private final NetplayBinder binder = new NetplayBinder(); |
14 public Netconn netconn; |
55 private TickHandler tickHandler; |
15 private CountDownTimer timer; |
56 private LocalBroadcastManager broadcastManager; |
|
57 |
|
58 private String playerName; |
|
59 private NetconnPtr conn; |
|
60 private boolean joined; // True once we have been admitted to the lobby |
|
61 |
|
62 public final PlayerList playerList = new PlayerList(); |
|
63 public final RoomList roomList = new RoomList(); |
|
64 public MessageLog lobbyChatlog; |
|
65 public MessageLog roomChatlog; |
16 |
66 |
17 @Override |
67 @Override |
18 public IBinder onBind(Intent intent) { |
68 public IBinder onBind(Intent intent) { |
|
69 Log.d("NetplayService", "onBind"); |
|
70 tickHandler.setInterval(TICK_INTERVAL_MS_BOUND); |
19 return binder; |
71 return binder; |
20 } |
72 } |
21 |
73 |
22 @Override |
74 @Override |
|
75 public void onRebind(Intent intent) { |
|
76 Log.d("NetplayService", "onRebind"); |
|
77 tickHandler.setInterval(TICK_INTERVAL_MS_BOUND); |
|
78 } |
|
79 |
|
80 @Override |
|
81 public boolean onUnbind(Intent intent) { |
|
82 Log.d("NetplayService", "onUnbind"); |
|
83 tickHandler.setInterval(TICK_INTERVAL_MS_UNBOUND); |
|
84 return true; |
|
85 } |
|
86 |
|
87 @Override |
|
88 public int onStartCommand(Intent intent, int flags, int startId) { |
|
89 Log.d("NetplayService", "onStartCommand"); |
|
90 if(conn != null) { |
|
91 Log.e("NetplayService", "Attempt to start while running"); |
|
92 return START_NOT_STICKY; |
|
93 } |
|
94 joined = false; |
|
95 playerList.clear(); |
|
96 roomList.clear(); |
|
97 lobbyChatlog.clear(); |
|
98 roomChatlog.clear(); |
|
99 |
|
100 playerName = intent.getStringExtra(EXTRA_PLAYERNAME); |
|
101 if(playerName == null) playerName = "Player"; |
|
102 int port = intent.getIntExtra(EXTRA_PORT, DEFAULT_PORT); |
|
103 String host = intent.getStringExtra(EXTRA_HOST); |
|
104 if(host==null) host = DEFAULT_SERVER; |
|
105 |
|
106 MetaschemePtr meta = null; |
|
107 File dataPath; |
|
108 try { |
|
109 dataPath = Utils.getDataPathFile(getApplicationContext()); |
|
110 } catch (FileNotFoundException e) { |
|
111 stopWithError(getString(R.string.sdcard_not_mounted)); |
|
112 return START_NOT_STICKY; |
|
113 } |
|
114 String metaschemePath = new File(dataPath, "metasettings.ini").getAbsolutePath(); |
|
115 meta = FLIB.flib_metascheme_from_ini(metaschemePath); |
|
116 if(meta == null) { |
|
117 stopWithError(getString(R.string.error_unexpected, "Missing metasettings.ini")); |
|
118 return START_NOT_STICKY; |
|
119 } |
|
120 conn = FLIB.flib_netconn_create(playerName, meta, dataPath.getAbsolutePath(), host, port); |
|
121 if(conn == null) { |
|
122 stopWithError(getString(R.string.error_connection_failed)); |
|
123 return START_NOT_STICKY; |
|
124 } |
|
125 FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null); |
|
126 FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null); |
|
127 FLIB.flib_netconn_onChat(conn, chatCb, null); |
|
128 FLIB.flib_netconn_onMessage(conn, messageCb, null); |
|
129 FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null); |
|
130 FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null); |
|
131 FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null); |
|
132 FLIB.flib_netconn_onConnected(conn, connectedCb, null); |
|
133 FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null); |
|
134 FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null); |
|
135 FLIB.flib_metascheme_release(meta); |
|
136 tickHandler.start(); |
|
137 instance = this; |
|
138 return START_NOT_STICKY; |
|
139 } |
|
140 |
|
141 private void stopWithoutError() { |
|
142 Intent intent = new Intent(ACTION_DISCONNECTED); |
|
143 intent.putExtra(EXTRA_HAS_ERROR, false); |
|
144 broadcastManager.sendBroadcast(intent); |
|
145 stopSelf(); |
|
146 } |
|
147 |
|
148 private void stopWithError(String userMessage) { |
|
149 Intent intent = new Intent(ACTION_DISCONNECTED); |
|
150 intent.putExtra(EXTRA_MESSAGE, userMessage); |
|
151 intent.putExtra(EXTRA_HAS_ERROR, true); |
|
152 broadcastManager.sendBroadcast(intent); |
|
153 stopSelf(); |
|
154 } |
|
155 |
|
156 @Override |
23 public void onCreate() { |
157 public void onCreate() { |
24 Log.d("NetplayService", "Creating"); |
158 Log.d("NetplayService", "onCreate"); |
25 if(Flib.INSTANCE.flib_init() != 0) { |
159 broadcastManager = LocalBroadcastManager.getInstance(getApplicationContext()); |
26 throw new RuntimeException("Unable to start frontlib"); |
160 lobbyChatlog = new MessageLog(getApplicationContext()); |
27 } |
161 roomChatlog = new MessageLog(getApplicationContext()); |
28 try { |
162 tickHandler = new TickHandler(getMainLooper(), TICK_INTERVAL_MS_UNBOUND, new Runnable() { |
29 netconn = new Netconn(getApplicationContext(), "AndroidTester"); |
163 public void run() { |
30 } catch (IOException e) { |
164 if(conn != null) { |
31 // TODO better handling |
165 FLIB.flib_netconn_tick(conn); |
32 throw new RuntimeException("Unable to start frontlib", e); |
|
33 } |
|
34 timer = new CountDownTimer(Long.MAX_VALUE, 50) { |
|
35 @Override |
|
36 public void onTick(long millisUntilFinished) { |
|
37 if(netconn != null && netconn.isConnected()) { |
|
38 netconn.tick(); |
|
39 } |
166 } |
40 } |
167 } |
41 |
168 }); |
42 @Override |
169 if(Flib.INSTANCE.flib_init() != 0) { |
43 public void onFinish() { |
170 stopWithError(getString(R.string.error_unexpected, "Unable to start frontlib")); |
|
171 } |
|
172 } |
|
173 |
|
174 @Override |
|
175 public void onDestroy() { |
|
176 instance = null; |
|
177 Log.d("NetplayService", "onDestroy"); |
|
178 tickHandler.stop(); |
|
179 if(conn != null) { |
|
180 FLIB.flib_netconn_destroy(conn); |
|
181 conn = null; |
|
182 } |
|
183 Flib.INSTANCE.flib_quit(); |
|
184 } |
|
185 |
|
186 public class NetplayBinder extends Binder { |
|
187 NetplayService getService() { |
|
188 return NetplayService.this; |
|
189 } |
|
190 } |
|
191 |
|
192 private StrCallback lobbyJoinCb = new StrCallback() { |
|
193 public void callback(Pointer context, String arg1) { |
|
194 playerList.addPlayerWithNewId(arg1); |
|
195 lobbyChatlog.appendPlayerJoin(arg1); |
|
196 } |
|
197 }; |
|
198 |
|
199 private StrStrCallback lobbyLeaveCb = new StrStrCallback() { |
|
200 public void callback(Pointer context, String name, String msg) { |
|
201 playerList.removePlayer(name); |
|
202 lobbyChatlog.appendPlayerLeave(name, msg); |
|
203 } |
|
204 }; |
|
205 |
|
206 private StrStrCallback chatCb = new StrStrCallback() { |
|
207 public void callback(Pointer context, String name, String msg) { |
|
208 getCurrentLog().appendChat(name, msg); |
|
209 } |
|
210 }; |
|
211 |
|
212 private IntStrCallback messageCb = new IntStrCallback() { |
|
213 public void callback(Pointer context, int type, String msg) { |
|
214 getCurrentLog().appendMessage(type, msg); |
|
215 } |
|
216 }; |
|
217 |
|
218 private RoomCallback roomAddCb = new RoomCallback() { |
|
219 public void callback(Pointer context, RoomPtr roomPtr) { |
|
220 roomList.addRoomWithNewId(roomPtr); |
|
221 } |
|
222 }; |
|
223 |
|
224 private StrRoomCallback roomUpdateCb = new StrRoomCallback() { |
|
225 public void callback(Pointer context, String name, RoomPtr roomPtr) { |
|
226 roomList.updateRoom(name, roomPtr); |
|
227 } |
|
228 }; |
|
229 |
|
230 private StrCallback roomDeleteCb = new StrCallback() { |
|
231 public void callback(Pointer context, String name) { |
|
232 roomList.removeRoom(name); |
|
233 } |
|
234 }; |
|
235 |
|
236 private VoidCallback connectedCb = new VoidCallback() { |
|
237 public void callback(Pointer context) { |
|
238 broadcastManager.sendBroadcast(new Intent(ACTION_CONNECTED)); |
|
239 joined = true; |
|
240 FLIB.flib_netconn_send_request_roomlist(conn); |
|
241 } |
|
242 }; |
|
243 |
|
244 private RoomListCallback roomlistCb = new RoomListCallback() { |
|
245 public void callback(Pointer context, RoomArrayPtr arg1, int count) { |
|
246 roomList.updateList(arg1.getRooms(count)); |
|
247 } |
|
248 }; |
|
249 |
|
250 private IntStrCallback disconnectCb = new IntStrCallback() { |
|
251 public void callback(Pointer context, int reason, String arg2) { |
|
252 switch(reason) { |
|
253 case JnaFrontlib.NETCONN_DISCONNECT_AUTH_FAILED: |
|
254 stopWithError(getString(R.string.error_auth_failed)); |
|
255 break; |
|
256 case JnaFrontlib.NETCONN_DISCONNECT_CONNLOST: |
|
257 stopWithError(getString(R.string.error_connection_lost)); |
|
258 break; |
|
259 case JnaFrontlib.NETCONN_DISCONNECT_INTERNAL_ERROR: |
|
260 stopWithError(getString(R.string.error_unexpected, arg2)); |
|
261 break; |
|
262 case JnaFrontlib.NETCONN_DISCONNECT_NORMAL: |
|
263 stopWithoutError(); |
|
264 break; |
|
265 case JnaFrontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD: |
|
266 stopWithError(getString(R.string.error_server_too_old)); |
|
267 break; |
|
268 default: |
|
269 stopWithError(arg2); |
|
270 break; |
44 } |
271 } |
45 }; |
272 FLIB.flib_netconn_destroy(conn); |
46 timer.start(); |
273 conn = null; |
47 } |
274 } |
48 |
275 }; |
49 @Override |
276 |
50 public void onDestroy() { |
277 public void disconnect() { |
51 Log.d("NetplayService", "Destroying"); |
278 if(conn != null) { |
52 timer.cancel(); |
279 FLIB.flib_netconn_send_quit(conn, "User quit"); |
53 netconn.disconnect(); |
280 FLIB.flib_netconn_destroy(conn); |
54 Flib.INSTANCE.flib_quit(); |
281 conn = null; |
55 } |
282 } |
56 |
283 stopWithoutError(); |
57 public class NetplayBinder extends Binder { |
284 } |
58 Netconn getNetconn() { |
285 |
59 return netconn; |
286 public void sendChat(String s) { |
60 } |
287 FLIB.flib_netconn_send_chat(conn, s); |
61 } |
288 if(FLIB.flib_netconn_is_in_room_context(conn)) { |
62 |
289 roomChatlog.appendChat(playerName, s); |
|
290 } else { |
|
291 lobbyChatlog.appendChat(playerName, s); |
|
292 } |
|
293 } |
|
294 |
|
295 private MessageLog getCurrentLog() { |
|
296 if(FLIB.flib_netconn_is_in_room_context(conn)) { |
|
297 return roomChatlog; |
|
298 } else { |
|
299 return lobbyChatlog; |
|
300 } |
|
301 } |
|
302 |
|
303 public void sendNick(String nick) { FLIB.flib_netconn_send_nick(conn, nick); } |
|
304 public void sendPassword(String password) { FLIB.flib_netconn_send_password(conn, password); } |
|
305 public void sendQuit(String message) { FLIB.flib_netconn_send_quit(conn, message); } |
|
306 public void sendRoomlistRequest() { if(joined) FLIB.flib_netconn_send_request_roomlist(conn); } |
|
307 public void sendPlayerInfoQuery(String name) { FLIB.flib_netconn_send_playerInfo(conn, name); } |
|
308 |
63 public boolean isConnected() { |
309 public boolean isConnected() { |
64 return netconn!=null; |
310 return conn != null; |
|
311 } |
|
312 |
|
313 public static boolean isActive() { |
|
314 return instance!=null; |
65 } |
315 } |
66 } |
316 } |
|
317 |