1
// all server side masterserver and pinging functionality
7
bool resolverwait(const char *name, ENetAddress *address)
9
return enet_address_set_host(address, name) >= 0;
12
int connectwithtimeout(ENetSocket sock, const char *hostname, ENetAddress &remoteaddress)
14
int result = enet_socket_connect(sock, &remoteaddress);
15
if(result<0) enet_socket_destroy(sock);
20
ENetSocket httpgetsend(ENetAddress &remoteaddress, const char *hostname, const char *req, const char *ref, const char *agent, ENetAddress *localaddress = NULL)
22
if(remoteaddress.host==ENET_HOST_ANY)
24
logline(ACLOG_INFO, "looking up %s...", hostname);
25
if(!resolverwait(hostname, &remoteaddress)) return ENET_SOCKET_NULL;
27
ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
28
if(sock!=ENET_SOCKET_NULL && localaddress && enet_socket_bind(sock, localaddress) < 0)
30
enet_socket_destroy(sock);
31
sock = ENET_SOCKET_NULL;
33
if(sock==ENET_SOCKET_NULL || connectwithtimeout(sock, hostname, remoteaddress)<0)
35
logline(ACLOG_WARNING, sock==ENET_SOCKET_NULL ? "could not open socket" : "could not connect");
36
return ENET_SOCKET_NULL;
39
s_sprintfd(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent);
41
buf.dataLength = strlen((char *)buf.data);
42
logline(ACLOG_INFO, "sending request to %s...", hostname);
43
enet_socket_send(sock, NULL, &buf, 1);
47
bool httpgetreceive(ENetSocket sock, ENetBuffer &buf, int timeout = 0)
49
if(sock==ENET_SOCKET_NULL) return false;
50
enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
51
if(enet_socket_wait(sock, &events, timeout) >= 0 && events)
53
int len = enet_socket_receive(sock, NULL, &buf, 1);
56
enet_socket_destroy(sock);
59
buf.data = ((char *)buf.data)+len;
60
((char*)buf.data)[0] = 0;
61
buf.dataLength -= len;
66
uchar *stripheader(uchar *b)
68
char *s = strstr((char *)b, "\n\r\n");
69
if(!s) s = strstr((char *)b, "\n\n");
70
return s ? (uchar *)s : b;
73
ENetSocket mssock = ENET_SOCKET_NULL;
74
ENetAddress msaddress = { ENET_HOST_ANY, ENET_PORT_ANY };
75
ENetAddress masterserver = { ENET_HOST_ANY, 80 };
76
int lastupdatemaster = 0;
79
uchar masterrep[MAXTRANS];
82
// send alive signal to masterserver every hour of uptime
83
void updatemasterserver(int millis, const ENetAddress &localaddr)
85
if(!millis || millis/(60*60*1000)!=lastupdatemaster)
87
s_sprintfd(path)("%sregister.do?action=add&port=%d", masterpath, localaddr.port);
88
s_sprintfd(agent)("AssaultCube Server %d", AC_VERSION);
89
mssock = httpgetsend(masterserver, masterbase, path, "assaultcubeserver", agent, &msaddress);
91
masterb.data = masterrep;
92
masterb.dataLength = MAXTRANS-1;
93
lastupdatemaster = millis/(60*60*1000);
97
void checkmasterreply()
99
if(mssock!=ENET_SOCKET_NULL && !httpgetreceive(mssock, masterb))
101
mssock = ENET_SOCKET_NULL;
103
filtertext(text, (const char *) stripheader(masterrep));
104
logline(ACLOG_INFO, "masterserver reply: %s", text);
110
#define RETRIEVELIMIT 20000
112
uchar *retrieveservers(uchar *buf, int buflen)
116
s_sprintfd(path)("%sretrieve.do?item=list", masterpath);
117
s_sprintfd(agent)("AssaultCube Client %d", AC_VERSION);
118
ENetAddress address = masterserver;
119
ENetSocket sock = httpgetsend(address, masterbase, path, "assaultcubeclient", agent);
120
if(sock==ENET_SOCKET_NULL) return buf;
121
/* only cache this if connection succeeds */
122
masterserver = address;
124
s_sprintfd(text)("retrieving servers from %s... (esc to abort)", masterbase);
125
show_out_of_renderloop_progress(0, text);
129
eb.dataLength = buflen-1;
131
int starttime = SDL_GetTicks(), timeout = 0;
132
while(httpgetreceive(sock, eb, 250))
134
timeout = SDL_GetTicks() - starttime;
135
show_out_of_renderloop_progress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
137
while(SDL_PollEvent(&event))
139
if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) timeout = RETRIEVELIMIT + 1;
141
if(timeout > RETRIEVELIMIT)
144
enet_socket_destroy(sock);
149
return stripheader(buf);
153
ENetSocket pongsock = ENET_SOCKET_NULL, lansock = ENET_SOCKET_NULL;
154
extern int getpongflags(enet_uint32 ip);
156
void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, const ENetAddress &localaddr, int protocol_version)
159
if(protocol_version > 0 || strncmp(masterpath, AC_MASTER_URI, 23)) updatemasterserver(millis, localaddr);
161
static ENetSocketSet sockset;
162
ENET_SOCKETSET_EMPTY(sockset);
163
ENetSocket maxsock = pongsock;
164
ENET_SOCKETSET_ADD(sockset, pongsock);
165
if(lansock != ENET_SOCKET_NULL)
167
maxsock = max(maxsock, lansock);
168
ENET_SOCKETSET_ADD(sockset, lansock);
170
if(enet_socketset_select(maxsock, &sockset, NULL, 0) <= 0) return;
172
// reply all server info requests
175
uchar data[MAXTRANS];
181
ENetSocket sock = i ? lansock : pongsock;
182
if(sock == ENET_SOCKET_NULL || !ENET_SOCKETSET_CHECK(sockset, sock)) continue;
184
buf.dataLength = sizeof(data);
185
len = enet_socket_receive(sock, &addr, &buf, 1);
186
if(len < 0) continue;
189
ucharbuf pi(data, len), po(&data[len], sizeof(data)-len);
191
if(getint(pi) != 0) // std pong
193
extern struct servercommandline scl;
194
extern string servdesc_current;
195
putint(po, protocol_version);
197
putint(po, numplayers);
198
putint(po, minremain);
199
sendstring(smapname, po);
200
sendstring(servdesc_current, po);
201
putint(po, scl.maxclients);
202
putint(po, getpongflags(addr.host));
205
int query = getint(pi);
208
case EXTPING_NAMELIST:
210
extern void extping_namelist(ucharbuf &p);
212
extping_namelist(po);
215
case EXTPING_SERVERINFO:
217
extern void extping_serverinfo(ucharbuf &pi, ucharbuf &po);
219
extping_serverinfo(pi, po);
224
extern void extping_maprot(ucharbuf &po);
231
putint(po, EXTPING_NOP);
236
else // ext pong - additional server infos
238
int extcmd = getint(pi);
240
putint(po, EXT_VERSION);
244
case EXT_UPTIME: // uptime in seconds
246
putint(po, uint(millis)/1000);
250
case EXT_PLAYERSTATS: // playerstats
252
int cn = getint(pi); // get requested player, -1 for all
253
if(!valid_client(cn) && cn != -1)
255
putint(po, EXT_ERROR);
258
putint(po, EXT_ERROR_NONE); // add no error flag
260
int bpos = po.length(); // remember buffer position
261
putint(po, EXT_PLAYERSTATS_RESP_IDS); // send player ids following
262
extinfo_cnbuf(po, cn);
263
buf.dataLength = len + po.length();
264
enet_socket_send(pongsock, &addr, &buf, 1); // send all available player ids
267
extinfo_statsbuf(po, cn, bpos, pongsock, addr, buf, len);
272
extinfo_teamscorebuf(po);
276
putint(po,EXT_ERROR);
281
buf.dataLength = len + po.length();
282
enet_socket_send(pongsock, &addr, &buf, 1);
286
void servermsinit(const char *master, const char *ip, int infoport, bool listen)
288
const char *mid = strstr(master, "/");
289
if(mid) s_strncpy(masterbase, master, mid-master+1);
290
else s_strcpy(masterbase, (mid = master));
291
s_strcpy(masterpath, mid);
295
ENetAddress address = { ENET_HOST_ANY, infoport };
298
if(enet_address_set_host(&address, ip)<0) logline(ACLOG_WARNING, "server ip not resolved");
299
else msaddress.host = address.host;
301
pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
302
if(pongsock != ENET_SOCKET_NULL && enet_socket_bind(pongsock, &address) < 0)
304
enet_socket_destroy(pongsock);
305
pongsock = ENET_SOCKET_NULL;
307
if(pongsock == ENET_SOCKET_NULL) fatal("could not create server info socket");
308
else enet_socket_set_option(pongsock, ENET_SOCKOPT_NONBLOCK, 1);
309
address.port = CUBE_SERVINFO_PORT_LAN;
310
lansock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
311
if(lansock != ENET_SOCKET_NULL && (enet_socket_set_option(lansock, ENET_SOCKOPT_REUSEADDR, 1) < 0 || enet_socket_bind(lansock, &address) < 0))
313
enet_socket_destroy(lansock);
314
lansock = ENET_SOCKET_NULL;
316
if(lansock == ENET_SOCKET_NULL) logline(ACLOG_WARNING, "could not create LAN server info socket");
317
else enet_socket_set_option(lansock, ENET_SOCKOPT_NONBLOCK, 1);