~ubuntu-branches/debian/stretch/assaultcube-data/stretch

« back to all changes in this revision

Viewing changes to source/src/serverms.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Gonéri Le Bouder, Ansgar Burchardt, Gonéri Le Bouder
  • Date: 2010-04-02 23:37:55 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100402233755-kf74fxwlu634o6vg
Tags: 1.0.4+repack1-1
[ Ansgar Burchardt ]
* debian/control: fix typo in short description

[ Gonéri Le Bouder ]
* Upgrade to 1.0.4
* bump standards-version to 3.8.4
* Add Depends: ${misc:Depends} just to avoid a lintian warning
* Add a debian/source/format file for the same reason

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// all server side masterserver and pinging functionality
 
2
 
 
3
#include "pch.h"
 
4
#include "cube.h"
 
5
 
 
6
#ifdef STANDALONE
 
7
bool resolverwait(const char *name, ENetAddress *address)
 
8
{
 
9
    return enet_address_set_host(address, name) >= 0;
 
10
}
 
11
 
 
12
int connectwithtimeout(ENetSocket sock, const char *hostname, ENetAddress &remoteaddress)
 
13
{
 
14
    int result = enet_socket_connect(sock, &remoteaddress);
 
15
    if(result<0) enet_socket_destroy(sock);
 
16
    return result;
 
17
}
 
18
#endif
 
19
 
 
20
ENetSocket httpgetsend(ENetAddress &remoteaddress, const char *hostname, const char *req, const char *ref, const char *agent, ENetAddress *localaddress = NULL)
 
21
{
 
22
    if(remoteaddress.host==ENET_HOST_ANY)
 
23
    {
 
24
        logline(ACLOG_INFO, "looking up %s...", hostname);
 
25
        if(!resolverwait(hostname, &remoteaddress)) return ENET_SOCKET_NULL;
 
26
    }
 
27
    ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
 
28
    if(sock!=ENET_SOCKET_NULL && localaddress && enet_socket_bind(sock, localaddress) < 0)
 
29
    {
 
30
        enet_socket_destroy(sock);
 
31
        sock = ENET_SOCKET_NULL;
 
32
    }
 
33
    if(sock==ENET_SOCKET_NULL || connectwithtimeout(sock, hostname, remoteaddress)<0)
 
34
    {
 
35
        logline(ACLOG_WARNING, sock==ENET_SOCKET_NULL ? "could not open socket" : "could not connect");
 
36
        return ENET_SOCKET_NULL;
 
37
    }
 
38
    ENetBuffer buf;
 
39
    s_sprintfd(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent);
 
40
    buf.data = httpget;
 
41
    buf.dataLength = strlen((char *)buf.data);
 
42
    logline(ACLOG_INFO, "sending request to %s...", hostname);
 
43
    enet_socket_send(sock, NULL, &buf, 1);
 
44
    return sock;
 
45
}
 
46
 
 
47
bool httpgetreceive(ENetSocket sock, ENetBuffer &buf, int timeout = 0)
 
48
{
 
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)
 
52
    {
 
53
        int len = enet_socket_receive(sock, NULL, &buf, 1);
 
54
        if(len<=0)
 
55
        {
 
56
            enet_socket_destroy(sock);
 
57
            return false;
 
58
        }
 
59
        buf.data = ((char *)buf.data)+len;
 
60
        ((char*)buf.data)[0] = 0;
 
61
        buf.dataLength -= len;
 
62
    }
 
63
    return true;
 
64
}
 
65
 
 
66
uchar *stripheader(uchar *b)
 
67
{
 
68
    char *s = strstr((char *)b, "\n\r\n");
 
69
    if(!s) s = strstr((char *)b, "\n\n");
 
70
    return s ? (uchar *)s : b;
 
71
}
 
72
 
 
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;
 
77
string masterbase;
 
78
string masterpath;
 
79
uchar masterrep[MAXTRANS];
 
80
ENetBuffer masterb;
 
81
 
 
82
// send alive signal to masterserver every hour of uptime
 
83
void updatemasterserver(int millis, const ENetAddress &localaddr)
 
84
{
 
85
    if(!millis || millis/(60*60*1000)!=lastupdatemaster)
 
86
    {
 
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);
 
90
                masterrep[0] = 0;
 
91
                masterb.data = masterrep;
 
92
                masterb.dataLength = MAXTRANS-1;
 
93
        lastupdatemaster = millis/(60*60*1000);
 
94
    }
 
95
}
 
96
 
 
97
void checkmasterreply()
 
98
{
 
99
    if(mssock!=ENET_SOCKET_NULL && !httpgetreceive(mssock, masterb))
 
100
    {
 
101
        mssock = ENET_SOCKET_NULL;
 
102
        string text;
 
103
        filtertext(text, (const char *) stripheader(masterrep));
 
104
        logline(ACLOG_INFO, "masterserver reply: %s", text);
 
105
    }
 
106
}
 
107
 
 
108
#ifndef STANDALONE
 
109
 
 
110
#define RETRIEVELIMIT 20000
 
111
 
 
112
uchar *retrieveservers(uchar *buf, int buflen)
 
113
{
 
114
    buf[0] = '\0';
 
115
 
 
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;
 
123
 
 
124
    s_sprintfd(text)("retrieving servers from %s... (esc to abort)", masterbase);
 
125
    show_out_of_renderloop_progress(0, text);
 
126
 
 
127
    ENetBuffer eb;
 
128
    eb.data = buf;
 
129
    eb.dataLength = buflen-1;
 
130
 
 
131
    int starttime = SDL_GetTicks(), timeout = 0;
 
132
    while(httpgetreceive(sock, eb, 250))
 
133
    {
 
134
        timeout = SDL_GetTicks() - starttime;
 
135
        show_out_of_renderloop_progress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
 
136
        SDL_Event event;
 
137
        while(SDL_PollEvent(&event))
 
138
        {
 
139
            if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) timeout = RETRIEVELIMIT + 1;
 
140
        }
 
141
        if(timeout > RETRIEVELIMIT)
 
142
        {
 
143
            buf[0] = '\0';
 
144
            enet_socket_destroy(sock);
 
145
            return buf;
 
146
        }
 
147
    }
 
148
 
 
149
    return stripheader(buf);
 
150
}
 
151
#endif
 
152
 
 
153
ENetSocket pongsock = ENET_SOCKET_NULL, lansock = ENET_SOCKET_NULL;
 
154
extern int getpongflags(enet_uint32 ip);
 
155
 
 
156
void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, const ENetAddress &localaddr, int protocol_version)
 
157
{
 
158
    checkmasterreply();
 
159
    if(protocol_version > 0 || strncmp(masterpath, AC_MASTER_URI, 23)) updatemasterserver(millis, localaddr);
 
160
 
 
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)
 
166
    {
 
167
        maxsock = max(maxsock, lansock);
 
168
        ENET_SOCKETSET_ADD(sockset, lansock);
 
169
    }
 
170
    if(enet_socketset_select(maxsock, &sockset, NULL, 0) <= 0) return;
 
171
 
 
172
    // reply all server info requests
 
173
    ENetBuffer buf;
 
174
    ENetAddress addr;
 
175
    uchar data[MAXTRANS];
 
176
    buf.data = data;
 
177
    int len;
 
178
 
 
179
    loopi(2)
 
180
    {
 
181
        ENetSocket sock = i ? lansock : pongsock;
 
182
        if(sock == ENET_SOCKET_NULL || !ENET_SOCKETSET_CHECK(sockset, sock)) continue;
 
183
 
 
184
        buf.dataLength = sizeof(data);
 
185
        len = enet_socket_receive(sock, &addr, &buf, 1);
 
186
        if(len < 0) continue;
 
187
 
 
188
        // ping & pong buf
 
189
        ucharbuf pi(data, len), po(&data[len], sizeof(data)-len);
 
190
 
 
191
        if(getint(pi) != 0) // std pong
 
192
        {
 
193
            extern struct servercommandline scl;
 
194
            extern string servdesc_current;
 
195
            putint(po, protocol_version);
 
196
            putint(po, mode);
 
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));
 
203
            if(pi.remaining())
 
204
            {
 
205
                int query = getint(pi);
 
206
                switch(query)
 
207
                {
 
208
                    case EXTPING_NAMELIST:
 
209
                    {
 
210
                        extern void extping_namelist(ucharbuf &p);
 
211
                        putint(po, query);
 
212
                        extping_namelist(po);
 
213
                        break;
 
214
                    }
 
215
                    case EXTPING_SERVERINFO:
 
216
                    {
 
217
                        extern void extping_serverinfo(ucharbuf &pi, ucharbuf &po);
 
218
                        putint(po, query);
 
219
                        extping_serverinfo(pi, po);
 
220
                        break;
 
221
                    }
 
222
                    case EXTPING_MAPROT:
 
223
                    {
 
224
                        extern void extping_maprot(ucharbuf &po);
 
225
                        putint(po, query);
 
226
                        extping_maprot(po);
 
227
                        break;
 
228
                    }
 
229
                    case EXTPING_NOP:
 
230
                    default:
 
231
                        putint(po, EXTPING_NOP);
 
232
                        break;
 
233
                }
 
234
            }
 
235
        }
 
236
        else // ext pong - additional server infos
 
237
        {
 
238
            int extcmd = getint(pi);
 
239
            putint(po, EXT_ACK);
 
240
            putint(po, EXT_VERSION);
 
241
 
 
242
            switch(extcmd)
 
243
            {
 
244
                case EXT_UPTIME:        // uptime in seconds
 
245
                {
 
246
                    putint(po, uint(millis)/1000);
 
247
                    break;
 
248
                }
 
249
 
 
250
                case EXT_PLAYERSTATS:   // playerstats
 
251
                {
 
252
                    int cn = getint(pi);     // get requested player, -1 for all
 
253
                    if(!valid_client(cn) && cn != -1)
 
254
                    {
 
255
                        putint(po, EXT_ERROR);
 
256
                        break;
 
257
                    }
 
258
                    putint(po, EXT_ERROR_NONE);              // add no error flag
 
259
 
 
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
 
265
                    po.len = bpos;
 
266
 
 
267
                    extinfo_statsbuf(po, cn, bpos, pongsock, addr, buf, len);
 
268
                    return;
 
269
                }
 
270
 
 
271
                case EXT_TEAMSCORE:
 
272
                    extinfo_teamscorebuf(po);
 
273
                    break;
 
274
 
 
275
                default:
 
276
                    putint(po,EXT_ERROR);
 
277
                    break;
 
278
            }
 
279
        }
 
280
 
 
281
        buf.dataLength = len + po.length();
 
282
        enet_socket_send(pongsock, &addr, &buf, 1);
 
283
    }
 
284
}
 
285
 
 
286
void servermsinit(const char *master, const char *ip, int infoport, bool listen)
 
287
{
 
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);
 
292
 
 
293
        if(listen)
 
294
        {
 
295
        ENetAddress address = { ENET_HOST_ANY, infoport };
 
296
        if(*ip)
 
297
        {
 
298
            if(enet_address_set_host(&address, ip)<0) logline(ACLOG_WARNING, "server ip not resolved");
 
299
            else msaddress.host = address.host;
 
300
        }
 
301
        pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
 
302
        if(pongsock != ENET_SOCKET_NULL && enet_socket_bind(pongsock, &address) < 0)
 
303
        {
 
304
            enet_socket_destroy(pongsock);
 
305
            pongsock = ENET_SOCKET_NULL;
 
306
        }
 
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))
 
312
        {
 
313
            enet_socket_destroy(lansock);
 
314
            lansock = ENET_SOCKET_NULL;
 
315
        }
 
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);
 
318
        }
 
319
}