|
1 /* |
|
2 * Hedgewars, a free turn based strategy game |
|
3 * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com> |
|
4 * |
|
5 * This program is free software; you can redistribute it and/or |
|
6 * modify it under the terms of the GNU General Public License |
|
7 * as published by the Free Software Foundation; either version 2 |
|
8 * of the License, or (at your option) any later version. |
|
9 * |
|
10 * This program is distributed in the hope that it will be useful, |
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 * GNU General Public License for more details. |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License |
|
16 * along with this program; if not, write to the Free Software |
|
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
18 */ |
|
19 |
|
20 #include "mapconn.h" |
|
21 #include "ipcbase.h" |
|
22 #include "ipcprotocol.h" |
|
23 |
|
24 #include "../util/logging.h" |
|
25 #include "../util/buffer.h" |
|
26 #include "../util/util.h" |
|
27 |
|
28 #include <stdlib.h> |
|
29 |
|
30 typedef enum { |
|
31 AWAIT_CONNECTION, |
|
32 AWAIT_REPLY, |
|
33 AWAIT_CLOSE, |
|
34 FINISHED |
|
35 } mapconn_state; |
|
36 |
|
37 struct _flib_mapconn { |
|
38 uint8_t mapBuffer[IPCBASE_MAPMSG_BYTES]; |
|
39 flib_ipcbase *ipcBase; |
|
40 flib_vector *configBuffer; |
|
41 |
|
42 mapconn_state progress; |
|
43 |
|
44 void (*onSuccessCb)(void*, const uint8_t*, int); |
|
45 void *onSuccessCtx; |
|
46 |
|
47 void (*onFailureCb)(void*, const char*); |
|
48 void *onFailureCtx; |
|
49 |
|
50 bool running; |
|
51 bool destroyRequested; |
|
52 }; |
|
53 |
|
54 static void noop_handleSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {} |
|
55 static void noop_handleFailure(void *context, const char *errormessage) {} |
|
56 |
|
57 static void clearCallbacks(flib_mapconn *conn) { |
|
58 conn->onSuccessCb = &noop_handleSuccess; |
|
59 conn->onFailureCb = &noop_handleFailure; |
|
60 } |
|
61 |
|
62 static flib_vector *createConfigBuffer(const flib_map *mapdesc) { |
|
63 flib_vector *result = NULL; |
|
64 flib_vector *tempbuffer = flib_vector_create(); |
|
65 if(tempbuffer) { |
|
66 bool error = false; |
|
67 error |= flib_ipc_append_mapconf(tempbuffer, mapdesc, true); |
|
68 error |= flib_ipc_append_message(tempbuffer, "!"); |
|
69 if(!error) { |
|
70 result = tempbuffer; |
|
71 tempbuffer = NULL; |
|
72 } |
|
73 } |
|
74 flib_vector_destroy(tempbuffer); |
|
75 return result; |
|
76 } |
|
77 |
|
78 flib_mapconn *flib_mapconn_create(const flib_map *mapdesc) { |
|
79 if(log_badargs_if(mapdesc==NULL)) { |
|
80 return NULL; |
|
81 } |
|
82 flib_mapconn *result = NULL; |
|
83 flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn)); |
|
84 if(tempConn) { |
|
85 tempConn->ipcBase = flib_ipcbase_create(); |
|
86 tempConn->configBuffer = createConfigBuffer(mapdesc); |
|
87 if(tempConn->ipcBase && tempConn->configBuffer) { |
|
88 tempConn->progress = AWAIT_CONNECTION; |
|
89 clearCallbacks(tempConn); |
|
90 result = tempConn; |
|
91 tempConn = NULL; |
|
92 } |
|
93 } |
|
94 flib_mapconn_destroy(tempConn); |
|
95 return result; |
|
96 } |
|
97 |
|
98 void flib_mapconn_destroy(flib_mapconn *conn) { |
|
99 if(conn) { |
|
100 if(conn->running) { |
|
101 /* |
|
102 * The function was called from a callback, so the tick function is still running |
|
103 * and we delay the actual destruction. We ensure no further callbacks will be |
|
104 * sent to prevent surprises. |
|
105 */ |
|
106 clearCallbacks(conn); |
|
107 conn->destroyRequested = true; |
|
108 } else { |
|
109 flib_ipcbase_destroy(conn->ipcBase); |
|
110 flib_vector_destroy(conn->configBuffer); |
|
111 free(conn); |
|
112 } |
|
113 } |
|
114 } |
|
115 |
|
116 int flib_mapconn_getport(flib_mapconn *conn) { |
|
117 if(log_badargs_if(conn==NULL)) { |
|
118 return 0; |
|
119 } |
|
120 return flib_ipcbase_port(conn->ipcBase); |
|
121 } |
|
122 |
|
123 void flib_mapconn_onSuccess(flib_mapconn *conn, void (*callback)(void* context, const uint8_t *bitmap, int numHedgehogs), void *context) { |
|
124 if(!log_badargs_if(conn==NULL)) { |
|
125 conn->onSuccessCb = callback ? callback : &noop_handleSuccess; |
|
126 conn->onSuccessCtx = context; |
|
127 } |
|
128 } |
|
129 |
|
130 void flib_mapconn_onFailure(flib_mapconn *conn, void (*callback)(void* context, const char *errormessage), void *context) { |
|
131 if(!log_badargs_if(conn==NULL)) { |
|
132 conn->onFailureCb = callback ? callback : &noop_handleFailure; |
|
133 conn->onFailureCtx = context; |
|
134 } |
|
135 } |
|
136 |
|
137 static void flib_mapconn_wrappedtick(flib_mapconn *conn) { |
|
138 if(conn->progress == AWAIT_CONNECTION) { |
|
139 flib_ipcbase_accept(conn->ipcBase); |
|
140 switch(flib_ipcbase_state(conn->ipcBase)) { |
|
141 case IPC_CONNECTED: |
|
142 { |
|
143 flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer); |
|
144 if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) { |
|
145 conn->progress = FINISHED; |
|
146 conn->onFailureCb(conn->onFailureCtx, "Error sending map information to the engine."); |
|
147 return; |
|
148 } else { |
|
149 conn->progress = AWAIT_REPLY; |
|
150 } |
|
151 } |
|
152 break; |
|
153 case IPC_NOT_CONNECTED: |
|
154 conn->progress = FINISHED; |
|
155 conn->onFailureCb(conn->onFailureCtx, "Engine connection closed unexpectedly."); |
|
156 return; |
|
157 default: |
|
158 break; |
|
159 } |
|
160 } |
|
161 |
|
162 if(conn->progress == AWAIT_REPLY) { |
|
163 if(flib_ipcbase_recv_map(conn->ipcBase, conn->mapBuffer) >= 0) { |
|
164 conn->progress = AWAIT_CLOSE; |
|
165 } else if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) { |
|
166 conn->progress = FINISHED; |
|
167 conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly."); |
|
168 return; |
|
169 } |
|
170 } |
|
171 |
|
172 if(conn->progress == AWAIT_CLOSE) { |
|
173 // Just do throwaway reads so we find out when the engine disconnects |
|
174 uint8_t buf[256]; |
|
175 flib_ipcbase_recv_message(conn->ipcBase, buf); |
|
176 if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) { |
|
177 conn->progress = FINISHED; |
|
178 conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCBASE_MAPMSG_BYTES-1]); |
|
179 return; |
|
180 } |
|
181 } |
|
182 } |
|
183 |
|
184 void flib_mapconn_tick(flib_mapconn *conn) { |
|
185 if(!log_badargs_if(conn==NULL) |
|
186 && !log_w_if(conn->running, "Call to flib_mapconn_tick from a callback") |
|
187 && !log_w_if(conn->progress == FINISHED, "We are already done.")) { |
|
188 conn->running = true; |
|
189 flib_mapconn_wrappedtick(conn); |
|
190 conn->running = false; |
|
191 |
|
192 if(conn->destroyRequested) { |
|
193 flib_mapconn_destroy(conn); |
|
194 } |
|
195 } |
|
196 } |