1 #include "ipcconn.h" |
1 #include "ipcconn.h" |
2 #include "logging.h" |
2 #include "logging.h" |
3 #include "nonblocksockets.h" |
3 #include "socket.h" |
4 |
4 |
5 #include <SDL_net.h> |
|
6 #include <time.h> |
|
7 #include <string.h> |
5 #include <string.h> |
8 #include <stdbool.h> |
6 #include <stdbool.h> |
9 |
7 #include <stdlib.h> |
10 static TCPsocket ipcListenSocket; |
8 #include <stdio.h> |
11 static NonBlockSocket ipcConnSocket; |
9 |
12 |
10 typedef struct _flib_ipcconn { |
13 static uint8_t ipcReadBuffer[256]; |
11 char playerName[256]; |
14 static int ipcReadBufferSize; |
12 |
15 |
13 uint8_t readBuffer[256]; |
16 static flib_vector demoBuffer; |
14 int readBufferSize; |
17 static char localPlayerName[255]; |
15 |
18 |
16 flib_acceptor acceptor; |
19 void flib_ipcconn_init() { |
17 uint16_t port; |
20 ipcListenSocket = NULL; |
18 |
21 ipcConnSocket = NULL; |
19 flib_tcpsocket sock; |
22 ipcReadBufferSize = 0; |
20 flib_vector demoBuffer; |
23 demoBuffer=NULL; |
21 } _flib_ipcconn; |
24 strncpy(localPlayerName, "Local Player", 255); |
22 |
25 } |
23 flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) { |
26 |
24 flib_ipcconn result = malloc(sizeof(_flib_ipcconn)); |
27 void flib_ipcconn_quit() { |
25 flib_acceptor acceptor = flib_acceptor_create(0); |
28 flib_vector_destroy(&demoBuffer); |
26 |
29 flib_ipcconn_close(); |
27 if(!result || !acceptor) { |
30 } |
28 free(result); |
31 |
29 flib_acceptor_close(&acceptor); |
32 int flib_ipcconn_start(bool recordDemo) { |
30 return NULL; |
33 if(ipcListenSocket || ipcConnSocket) { |
31 } |
34 flib_log_e("flib_ipcconn_listen: Already listening or connected."); |
32 |
35 return -1; |
33 result->acceptor = acceptor; |
36 } |
34 result->sock = NULL; |
37 IPaddress addr; |
35 result->readBufferSize = 0; |
38 addr.host = INADDR_ANY; |
36 result->port = flib_acceptor_listenport(acceptor); |
39 |
37 |
40 /* SDL_net does not seem to have a way to listen on a random unused port |
38 if(localPlayerName) { |
41 and find out which port that is, so let's try to find one ourselves. */ |
39 strncpy(result->playerName, localPlayerName, 255); |
42 // TODO: Is socket binding fail-fast on all platforms? |
40 } else { |
43 srand(time(NULL)); |
41 strncpy(result->playerName, "Player", 255); |
44 rand(); |
42 } |
45 for(int i=0; i<1000; i++) { |
43 |
46 // IANA suggests using ports in the range 49152-65535 for things like this |
44 if(recordDemo) { |
47 int ipcPort = 49152+(rand()%(65535-49152)); |
45 result->demoBuffer = flib_vector_create(); |
48 SDLNet_Write16(ipcPort, &addr.port); |
46 } |
49 ipcListenSocket = SDLNet_TCP_Open(&addr); |
47 |
50 if(!ipcListenSocket) { |
48 flib_log_i("Started listening for IPC connections on port %u", result->port); |
51 flib_log_w("Failed to start an IPC listening socket on port %i: %s", ipcPort, SDLNet_GetError()); |
49 return result; |
52 } else { |
50 } |
53 flib_log_i("Listening for IPC connections on port %i.", ipcPort); |
51 |
54 if(recordDemo) { |
52 uint16_t flib_ipcconn_port(flib_ipcconn ipc) { |
55 flib_vector_destroy(&demoBuffer); |
53 return ipc->port; |
56 demoBuffer = flib_vector_create(); |
54 } |
57 } |
55 |
58 return ipcPort; |
56 void flib_ipcconn_destroy(flib_ipcconn *ipcptr) { |
59 } |
57 if(!ipcptr || !*ipcptr) { |
60 } |
58 return; |
61 flib_log_e("Unable to find a free port for IPC."); |
59 } |
62 return -1; |
60 flib_ipcconn ipc = *ipcptr; |
63 } |
61 flib_acceptor_close(&ipc->acceptor); |
64 |
62 flib_socket_close(&ipc->sock); |
65 void flib_ipcconn_close() { |
63 flib_vector_destroy(&ipc->demoBuffer); |
66 if(ipcListenSocket) { |
64 free(ipc); |
67 SDLNet_TCP_Close(ipcListenSocket); |
65 *ipcptr = NULL; |
68 ipcListenSocket = NULL; |
66 } |
69 } |
67 |
70 flib_nbsocket_close(&ipcConnSocket); |
68 IpcConnState flib_ipcconn_state(flib_ipcconn ipc) { |
71 ipcReadBufferSize = 0; |
69 if(ipc && ipc->sock) { |
72 } |
|
73 |
|
74 IpcConnState flib_ipcconn_state() { |
|
75 if(ipcConnSocket) { |
|
76 return IPC_CONNECTED; |
70 return IPC_CONNECTED; |
77 } else if(ipcListenSocket) { |
71 } else if(ipc && ipc->acceptor) { |
78 return IPC_LISTENING; |
72 return IPC_LISTENING; |
79 } else { |
73 } else { |
80 return IPC_NOT_CONNECTED; |
74 return IPC_NOT_CONNECTED; |
81 } |
75 } |
82 } |
76 } |
83 |
77 |
84 static void demo_record(const void *data, size_t len) { |
78 static void demo_record(flib_ipcconn ipc, const void *data, size_t len) { |
85 if(demoBuffer) { |
79 if(ipc->demoBuffer) { |
86 if(flib_vector_append(demoBuffer, data, len) < len) { |
80 if(flib_vector_append(ipc->demoBuffer, data, len) < len) { |
87 // Out of memory, fail demo recording |
81 // Out of memory, fail demo recording |
88 flib_vector_destroy(&demoBuffer); |
82 flib_vector_destroy(&ipc->demoBuffer); |
89 } |
83 } |
90 } |
84 } |
91 } |
85 } |
92 |
86 |
93 static void demo_record_from_engine(const uint8_t *message) { |
87 static void demo_record_from_engine(flib_ipcconn ipc, const uint8_t *message) { |
94 if(!demoBuffer || message[0]==0) { |
88 if(!ipc->demoBuffer || message[0]==0) { |
95 return; |
89 return; |
96 } |
90 } |
97 if(strchr("?CEiQqHb", message[1])) { |
91 if(strchr("?CEiQqHb", message[1])) { |
98 // Those message types are not recorded in a demo. |
92 // Those message types are not recorded in a demo. |
99 return; |
93 return; |
108 chatMsg[message[0]-3] = 0; |
102 chatMsg[message[0]-3] = 0; |
109 |
103 |
110 char converted[257]; |
104 char converted[257]; |
111 bool memessage = message[0] >= 7 && !memcmp(message+2, "/me ", 4); |
105 bool memessage = message[0] >= 7 && !memcmp(message+2, "/me ", 4); |
112 const char *template = memessage ? "s\x02* %s %s " : "s\x01%s: %s "; |
106 const char *template = memessage ? "s\x02* %s %s " : "s\x01%s: %s "; |
113 int size = snprintf(converted+1, 256, template, localPlayerName, chatMsg); |
107 int size = snprintf(converted+1, 256, template, ipc->playerName, chatMsg); |
114 converted[0] = size>255 ? 255 : size; |
108 converted[0] = size>255 ? 255 : size; |
115 demo_record(converted, converted[0]+1); |
109 demo_record(ipc, converted, converted[0]+1); |
116 } |
110 } |
117 } else { |
111 } else { |
118 demo_record(message, message[0]+1); |
112 demo_record(ipc, message, message[0]+1); |
119 } |
113 } |
120 } |
114 } |
121 |
115 |
122 /** |
116 /** |
123 * Receive a single message and copy it into the data buffer. |
117 * Receive a single message and copy it into the data buffer. |
124 * Returns the length of the received message, -1 when nothing is received. |
118 * Returns the length of the received message, -1 when nothing is received. |
125 */ |
119 */ |
126 int flib_ipcconn_recv_message(void *data) { |
120 int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) { |
127 flib_ipcconn_tick(); |
121 flib_ipcconn_tick(ipc); |
128 |
122 |
129 if(ipcConnSocket) { |
123 if(ipc->sock) { |
130 int size = flib_nbsocket_recv(ipcConnSocket, ipcReadBuffer+ipcReadBufferSize, sizeof(ipcReadBuffer)-ipcReadBufferSize); |
124 int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize); |
131 if(size>=0) { |
125 if(size>=0) { |
132 ipcReadBufferSize += size; |
126 ipc->readBufferSize += size; |
133 } else { |
127 } else { |
134 flib_nbsocket_close(&ipcConnSocket); |
128 flib_socket_close(&ipc->sock); |
135 } |
129 } |
136 } |
130 } |
137 |
131 |
138 int msgsize = ipcReadBuffer[0]; |
132 int msgsize = ipc->readBuffer[0]; |
139 if(ipcReadBufferSize > msgsize) { |
133 if(ipc->readBufferSize > msgsize) { |
140 demo_record_from_engine(ipcReadBuffer); |
134 demo_record_from_engine(ipc, ipc->readBuffer); |
141 memcpy(data, ipcReadBuffer+1, msgsize); |
135 memcpy(data, ipc->readBuffer+1, msgsize); |
142 memmove(ipcReadBuffer, ipcReadBuffer+msgsize+1, ipcReadBufferSize-(msgsize+1)); |
136 memmove(ipc->readBuffer, ipc->readBuffer+msgsize+1, ipc->readBufferSize-(msgsize+1)); |
143 ipcReadBufferSize -= (msgsize+1); |
137 ipc->readBufferSize -= (msgsize+1); |
144 return msgsize; |
138 return msgsize; |
145 } else if(!ipcConnSocket && ipcReadBufferSize>0) { |
139 } else if(!ipc->sock && ipc->readBufferSize>0) { |
146 flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipcReadBufferSize-1, msgsize); |
140 flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipc->readBufferSize-1, msgsize); |
147 ipcReadBufferSize = 0; |
141 ipc->readBufferSize = 0; |
148 return -1; |
142 return -1; |
149 } else { |
143 } else { |
150 return -1; |
144 return -1; |
151 } |
145 } |
152 } |
146 } |
153 |
147 |
154 int flib_ipcconn_send_message(void *data, size_t len) { |
148 int flib_ipcconn_send_raw(flib_ipcconn ipc, void *data, size_t len) { |
155 flib_ipcconn_tick(); |
149 flib_ipcconn_tick(ipc); |
156 |
150 |
157 if(!ipcConnSocket) { |
151 if(!ipc->sock) { |
158 flib_log_w("flib_ipcconn_send_message: Not connected."); |
152 flib_log_w("flib_ipcconn_send_message: Not connected."); |
159 return -1; |
153 return -1; |
160 } |
154 } |
|
155 |
|
156 if(flib_socket_send(ipc->sock, data, len) == len) { |
|
157 demo_record(ipc, data, len); |
|
158 return 0; |
|
159 } else { |
|
160 flib_log_w("Failed or incomplete ICP write: engine connection lost."); |
|
161 flib_socket_close(&ipc->sock); |
|
162 return -1; |
|
163 } |
|
164 } |
|
165 |
|
166 int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) { |
161 if(len>255) { |
167 if(len>255) { |
162 flib_log_e("Attempt to send too much data to the engine in a single message."); |
168 flib_log_e("Attempt to send too much data to the engine in a single message."); |
163 return -1; |
169 return -1; |
164 } |
170 } |
165 |
171 |
166 uint8_t sendbuf[256]; |
172 uint8_t sendbuf[256]; |
167 sendbuf[0] = len; |
173 sendbuf[0] = len; |
168 memcpy(sendbuf+1, data, len); |
174 memcpy(sendbuf+1, data, len); |
169 if(flib_nbsocket_blocksend(ipcConnSocket, sendbuf, len+1) == len+1) { |
175 |
170 demo_record(sendbuf, len+1); |
176 return flib_ipcconn_send_raw(ipc, sendbuf, len+1); |
171 return 0; |
177 } |
172 } else { |
178 |
173 flib_log_w("Failed or incomplete ICP write: engine connection lost."); |
179 int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data) { |
174 flib_nbsocket_close(&ipcConnSocket); |
180 return flib_ipcconn_send_message(ipc, data, strlen(data)); |
175 return -1; |
181 } |
176 } |
182 |
177 } |
183 void flib_ipcconn_tick(flib_ipcconn ipc) { |
178 |
184 if(!ipc->sock && ipc->acceptor) { |
179 int flib_ipcconn_send_messagestr(char *data) { |
185 ipc->sock = flib_socket_accept(ipc->acceptor, true); |
180 return flib_ipcconn_send_message(data, strlen(data)); |
186 if(ipc->sock) { |
181 } |
187 flib_acceptor_close(&ipc->acceptor); |
182 |
|
183 void flib_ipcconn_tick() { |
|
184 if(!ipcConnSocket && ipcListenSocket) { |
|
185 ipcConnSocket = flib_nbsocket_accept(ipcListenSocket, true); |
|
186 if(ipcConnSocket) { |
|
187 SDLNet_TCP_Close(ipcListenSocket); |
|
188 ipcListenSocket = NULL; |
|
189 } |
188 } |
190 } |
189 } |
191 } |
190 } |
192 |
191 |
193 static void replace_gamemode(flib_buffer buf, char gamemode) { |
192 static void replace_gamemode(flib_buffer buf, char gamemode) { |
199 } |
198 } |
200 msgStart += (uint8_t)data[msgStart]+1; |
199 msgStart += (uint8_t)data[msgStart]+1; |
201 } |
200 } |
202 } |
201 } |
203 |
202 |
204 flib_constbuffer flib_ipcconn_getdemo() { |
203 flib_constbuffer flib_ipcconn_getdemo(flib_ipcconn ipc) { |
205 if(!demoBuffer) { |
204 if(!ipc->demoBuffer) { |
206 flib_constbuffer result = {NULL, 0}; |
205 flib_constbuffer result = {NULL, 0}; |
207 return result; |
206 return result; |
208 } |
207 } |
209 replace_gamemode(flib_vector_as_buffer(demoBuffer), 'D'); |
208 replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), 'D'); |
210 return flib_vector_as_constbuffer(demoBuffer); |
209 return flib_vector_as_constbuffer(ipc->demoBuffer); |
211 } |
210 } |
212 |
211 |
213 flib_constbuffer flib_ipcconn_getsave() { |
212 flib_constbuffer flib_ipcconn_getsave(flib_ipcconn ipc) { |
214 if(!demoBuffer) { |
213 if(!ipc->demoBuffer) { |
215 flib_constbuffer result = {NULL, 0}; |
214 flib_constbuffer result = {NULL, 0}; |
216 return result; |
215 return result; |
217 } |
216 } |
218 replace_gamemode(flib_vector_as_buffer(demoBuffer), 'S'); |
217 replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), 'S'); |
219 return flib_vector_as_constbuffer(demoBuffer); |
218 return flib_vector_as_constbuffer(ipc->demoBuffer); |
220 } |
219 } |