drop serverbrowser code, master server, and a bunch of other stuff
parent
735029f650
commit
44be4f3b4b
|
@ -32,7 +32,6 @@ client_src = [
|
|||
'../engine/rendertext.cc',
|
||||
'../engine/renderva.cc',
|
||||
'../engine/server.cc',
|
||||
'../engine/serverbrowser.cc',
|
||||
'../engine/shader.cc',
|
||||
'../engine/stain.cc',
|
||||
'../engine/texture.cc',
|
||||
|
|
|
@ -86,25 +86,10 @@ void connectserv(const char *servername, int serverport, const char *serverpassw
|
|||
ENetAddress address;
|
||||
address.port = serverport;
|
||||
|
||||
if(servername)
|
||||
{
|
||||
if(strcmp(servername, connectname)) setsvar("connectname", servername);
|
||||
if(serverport != connectport) setvar("connectport", serverport);
|
||||
addserver(servername, serverport, serverpassword && serverpassword[0] ? serverpassword : NULL);
|
||||
conoutf("attempting to connect to %s:%d", servername, serverport);
|
||||
if(!resolverwait(servername, &address))
|
||||
{
|
||||
conoutf("\f3could not resolve server %s", servername);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setsvar("connectname", "");
|
||||
setvar("connectport", 0);
|
||||
conoutf("attempting to connect over LAN");
|
||||
address.host = ENET_HOST_BROADCAST;
|
||||
}
|
||||
setsvar("connectname", "");
|
||||
setvar("connectport", 0);
|
||||
conoutf("attempting to connect over LAN");
|
||||
address.host = ENET_HOST_BROADCAST;
|
||||
|
||||
if(!clienthost)
|
||||
{
|
||||
|
|
|
@ -533,17 +533,10 @@ extern void cleanupserver();
|
|||
extern void serverslice(bool dedicated, uint timeout);
|
||||
extern void updatetime();
|
||||
|
||||
extern ENetSocket connectmaster(bool wait);
|
||||
extern void localclienttoserver(int chan, ENetPacket *);
|
||||
extern void localconnect();
|
||||
extern bool serveroption(char *opt);
|
||||
|
||||
// serverbrowser
|
||||
extern bool resolverwait(const char *name, ENetAddress *address);
|
||||
extern int connectwithtimeout(ENetSocket sock, const char *hostname, const ENetAddress &address);
|
||||
extern void addserver(const char *name, int port = 0, const char *password = NULL, bool keep = false);
|
||||
extern void writeservercfg();
|
||||
|
||||
// client
|
||||
extern void localdisconnect(bool cleanup = true);
|
||||
extern void localservertoclient(int chan, ENetPacket *packet);
|
||||
|
|
|
@ -32,7 +32,6 @@ extern void writeinitcfg();
|
|||
void quit() // normal exit
|
||||
{
|
||||
writeinitcfg();
|
||||
writeservercfg();
|
||||
abortconnect();
|
||||
disconnect();
|
||||
localdisconnect();
|
||||
|
|
|
@ -1,730 +0,0 @@
|
|||
#ifdef WIN32
|
||||
#define FD_SETSIZE 4096
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#undef __FD_SETSIZE
|
||||
#define __FD_SETSIZE 4096
|
||||
#endif
|
||||
|
||||
#include "cube.hh"
|
||||
#include <signal.h>
|
||||
#include <enet/time.h>
|
||||
|
||||
#define INPUT_LIMIT 4096
|
||||
#define OUTPUT_LIMIT (64*1024)
|
||||
#define CLIENT_TIME (3*60*1000)
|
||||
#define AUTH_TIME (30*1000)
|
||||
#define AUTH_LIMIT 100
|
||||
#define AUTH_THROTTLE 1000
|
||||
#define CLIENT_LIMIT 4096
|
||||
#define DUP_LIMIT 16
|
||||
#define PING_TIME 3000
|
||||
#define PING_RETRY 5
|
||||
#define KEEPALIVE_TIME (65*60*1000)
|
||||
#define SERVER_LIMIT 4096
|
||||
#define SERVER_DUP_LIMIT 10
|
||||
|
||||
FILE *logfile = NULL;
|
||||
|
||||
struct userinfo
|
||||
{
|
||||
char *name;
|
||||
void *pubkey;
|
||||
};
|
||||
hashnameset<userinfo> users;
|
||||
|
||||
void adduser(char *name, char *pubkey)
|
||||
{
|
||||
name = newstring(name);
|
||||
userinfo &u = users[name];
|
||||
u.name = name;
|
||||
u.pubkey = parsepubkey(pubkey);
|
||||
}
|
||||
COMMAND(adduser, "ss");
|
||||
|
||||
void clearusers()
|
||||
{
|
||||
enumerate(users, userinfo, u, { delete[] u.name; freepubkey(u.pubkey); });
|
||||
users.clear();
|
||||
}
|
||||
COMMAND(clearusers, "");
|
||||
|
||||
vector<ipmask> bans, servbans, gbans;
|
||||
|
||||
void clearbans()
|
||||
{
|
||||
bans.shrink(0);
|
||||
servbans.shrink(0);
|
||||
gbans.shrink(0);
|
||||
}
|
||||
COMMAND(clearbans, "");
|
||||
|
||||
void addban(vector<ipmask> &bans, const char *name)
|
||||
{
|
||||
ipmask ban;
|
||||
ban.parse(name);
|
||||
bans.add(ban);
|
||||
}
|
||||
ICOMMAND(ban, "s", (char *name), addban(bans, name));
|
||||
ICOMMAND(servban, "s", (char *name), addban(servbans, name));
|
||||
ICOMMAND(gban, "s", (char *name), addban(gbans, name));
|
||||
|
||||
bool checkban(vector<ipmask> &bans, enet_uint32 host)
|
||||
{
|
||||
loopv(bans) if(bans[i].check(host)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct authreq
|
||||
{
|
||||
enet_uint32 reqtime;
|
||||
uint id;
|
||||
void *answer;
|
||||
};
|
||||
|
||||
struct gameserver
|
||||
{
|
||||
ENetAddress address;
|
||||
string ip;
|
||||
int port, numpings;
|
||||
enet_uint32 lastping, lastpong;
|
||||
};
|
||||
vector<gameserver *> gameservers;
|
||||
|
||||
struct messagebuf
|
||||
{
|
||||
vector<messagebuf *> &owner;
|
||||
vector<char> buf;
|
||||
int refs;
|
||||
|
||||
messagebuf(vector<messagebuf *> &owner) : owner(owner), refs(0) {}
|
||||
|
||||
const char *getbuf() { return buf.getbuf(); }
|
||||
int length() { return buf.length(); }
|
||||
void purge();
|
||||
|
||||
bool equals(const messagebuf &m) const
|
||||
{
|
||||
return buf.length() == m.buf.length() && !memcmp(buf.getbuf(), m.buf.getbuf(), buf.length());
|
||||
}
|
||||
|
||||
bool endswith(const messagebuf &m) const
|
||||
{
|
||||
return buf.length() >= m.buf.length() && !memcmp(&buf[buf.length() - m.buf.length()], m.buf.getbuf(), m.buf.length());
|
||||
}
|
||||
|
||||
void concat(const messagebuf &m)
|
||||
{
|
||||
if(buf.length() && buf.last() == '\0') buf.pop();
|
||||
buf.put(m.buf.getbuf(), m.buf.length());
|
||||
}
|
||||
};
|
||||
vector<messagebuf *> gameserverlists, gbanlists;
|
||||
bool updateserverlist = true;
|
||||
|
||||
struct client
|
||||
{
|
||||
ENetAddress address;
|
||||
ENetSocket socket;
|
||||
char input[INPUT_LIMIT];
|
||||
messagebuf *message;
|
||||
vector<char> output;
|
||||
int inputpos, outputpos;
|
||||
enet_uint32 connecttime, lastinput;
|
||||
int servport;
|
||||
enet_uint32 lastauth;
|
||||
vector<authreq> authreqs;
|
||||
bool shouldpurge;
|
||||
bool registeredserver;
|
||||
|
||||
client() : message(NULL), inputpos(0), outputpos(0), servport(-1), lastauth(0), shouldpurge(false), registeredserver(false) {}
|
||||
};
|
||||
vector<client *> clients;
|
||||
|
||||
ENetSocket serversocket = ENET_SOCKET_NULL;
|
||||
|
||||
time_t starttime;
|
||||
enet_uint32 servtime = 0;
|
||||
|
||||
void fatal(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(logfile, fmt, args);
|
||||
fputc('\n', logfile);
|
||||
va_end(args);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void conoutfv(int type, const char *fmt, va_list args)
|
||||
{
|
||||
vfprintf(logfile, fmt, args);
|
||||
fputc('\n', logfile);
|
||||
}
|
||||
|
||||
void conoutf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
conoutfv(CON_INFO, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void conoutf(int type, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
conoutfv(type, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void purgeclient(int n)
|
||||
{
|
||||
client &c = *clients[n];
|
||||
if(c.message) c.message->purge();
|
||||
enet_socket_destroy(c.socket);
|
||||
delete clients[n];
|
||||
clients.remove(n);
|
||||
}
|
||||
|
||||
void output(client &c, const char *msg, int len = 0)
|
||||
{
|
||||
if(!len) len = strlen(msg);
|
||||
c.output.put(msg, len);
|
||||
}
|
||||
|
||||
void outputf(client &c, const char *fmt, ...)
|
||||
{
|
||||
defvformatstring(msg, fmt, fmt);
|
||||
|
||||
output(c, msg);
|
||||
}
|
||||
|
||||
ENetSocket pingsocket = ENET_SOCKET_NULL;
|
||||
|
||||
bool setuppingsocket(ENetAddress *address)
|
||||
{
|
||||
if(pingsocket != ENET_SOCKET_NULL) return true;
|
||||
pingsocket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
|
||||
if(pingsocket == ENET_SOCKET_NULL) return false;
|
||||
if(address && enet_socket_bind(pingsocket, address) < 0) return false;
|
||||
enet_socket_set_option(pingsocket, ENET_SOCKOPT_NONBLOCK, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setupserver(int port, const char *ip = NULL)
|
||||
{
|
||||
ENetAddress address;
|
||||
address.host = ENET_HOST_ANY;
|
||||
address.port = port;
|
||||
|
||||
if(ip)
|
||||
{
|
||||
if(enet_address_set_host(&address, ip)<0)
|
||||
fatal("failed to resolve server address: %s", ip);
|
||||
}
|
||||
serversocket = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
|
||||
if(serversocket==ENET_SOCKET_NULL ||
|
||||
enet_socket_set_option(serversocket, ENET_SOCKOPT_REUSEADDR, 1) < 0 ||
|
||||
enet_socket_bind(serversocket, &address) < 0 ||
|
||||
enet_socket_listen(serversocket, -1) < 0)
|
||||
fatal("failed to create server socket");
|
||||
if(enet_socket_set_option(serversocket, ENET_SOCKOPT_NONBLOCK, 1)<0)
|
||||
fatal("failed to make server socket non-blocking");
|
||||
if(!setuppingsocket(&address))
|
||||
fatal("failed to create ping socket");
|
||||
|
||||
enet_time_set(0);
|
||||
|
||||
starttime = time(NULL);
|
||||
char *ct = ctime(&starttime);
|
||||
if(strchr(ct, '\n')) *strchr(ct, '\n') = '\0';
|
||||
conoutf("*** Starting master server on %s %d at %s ***", ip ? ip : "localhost", port, ct);
|
||||
}
|
||||
|
||||
void genserverlist()
|
||||
{
|
||||
if(!updateserverlist) return;
|
||||
while(gameserverlists.length() && gameserverlists.last()->refs<=0)
|
||||
delete gameserverlists.pop();
|
||||
messagebuf *l = new messagebuf(gameserverlists);
|
||||
loopv(gameservers)
|
||||
{
|
||||
gameserver &s = *gameservers[i];
|
||||
if(!s.lastpong) continue;
|
||||
defformatstring(cmd, "addserver %s %d\n", s.ip, s.port);
|
||||
l->buf.put(cmd, strlen(cmd));
|
||||
}
|
||||
l->buf.add('\0');
|
||||
gameserverlists.add(l);
|
||||
updateserverlist = false;
|
||||
}
|
||||
|
||||
void gengbanlist()
|
||||
{
|
||||
messagebuf *l = new messagebuf(gbanlists);
|
||||
const char *header = "cleargbans\n";
|
||||
l->buf.put(header, strlen(header));
|
||||
string cmd = "addgban ";
|
||||
int cmdlen = strlen(cmd);
|
||||
loopv(gbans)
|
||||
{
|
||||
ipmask &b = gbans[i];
|
||||
l->buf.put(cmd, cmdlen + b.print(&cmd[cmdlen]));
|
||||
l->buf.add('\n');
|
||||
}
|
||||
if(gbanlists.length() && gbanlists.last()->equals(*l))
|
||||
{
|
||||
delete l;
|
||||
return;
|
||||
}
|
||||
while(gbanlists.length() && gbanlists.last()->refs<=0)
|
||||
delete gbanlists.pop();
|
||||
loopv(gbanlists)
|
||||
{
|
||||
messagebuf *m = gbanlists[i];
|
||||
if(m->refs > 0 && !m->endswith(*l)) m->concat(*l);
|
||||
}
|
||||
gbanlists.add(l);
|
||||
loopv(clients)
|
||||
{
|
||||
client &c = *clients[i];
|
||||
if(c.servport >= 0 && !c.message)
|
||||
{
|
||||
c.message = l;
|
||||
c.message->refs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addgameserver(client &c)
|
||||
{
|
||||
if(gameservers.length() >= SERVER_LIMIT) return;
|
||||
int dups = 0;
|
||||
loopv(gameservers)
|
||||
{
|
||||
gameserver &s = *gameservers[i];
|
||||
if(s.address.host != c.address.host) continue;
|
||||
++dups;
|
||||
if(s.port == c.servport)
|
||||
{
|
||||
s.lastping = 0;
|
||||
s.numpings = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(dups >= SERVER_DUP_LIMIT)
|
||||
{
|
||||
outputf(c, "failreg too many servers on ip\n");
|
||||
return;
|
||||
}
|
||||
string hostname;
|
||||
if(enet_address_get_host_ip(&c.address, hostname, sizeof(hostname)) < 0)
|
||||
{
|
||||
outputf(c, "failreg failed resolving ip\n");
|
||||
return;
|
||||
}
|
||||
gameserver &s = *gameservers.add(new gameserver);
|
||||
s.address.host = c.address.host;
|
||||
s.address.port = c.servport;
|
||||
copystring(s.ip, hostname);
|
||||
s.port = c.servport;
|
||||
s.numpings = 0;
|
||||
s.lastping = s.lastpong = 0;
|
||||
}
|
||||
|
||||
client *findclient(gameserver &s)
|
||||
{
|
||||
loopv(clients)
|
||||
{
|
||||
client &c = *clients[i];
|
||||
if(s.address.host == c.address.host && s.port == c.servport)
|
||||
return &c;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void servermessage(gameserver &s, const char *msg)
|
||||
{
|
||||
client *c = findclient(s);
|
||||
if(c) outputf(*c, msg);
|
||||
}
|
||||
|
||||
void checkserverpongs()
|
||||
{
|
||||
ENetBuffer buf;
|
||||
ENetAddress addr;
|
||||
static uchar pong[MAXTRANS];
|
||||
for(;;)
|
||||
{
|
||||
buf.data = pong;
|
||||
buf.dataLength = sizeof(pong);
|
||||
int len = enet_socket_receive(pingsocket, &addr, &buf, 1);
|
||||
if(len <= 0) break;
|
||||
loopv(gameservers)
|
||||
{
|
||||
gameserver &s = *gameservers[i];
|
||||
if(s.address.host == addr.host && s.address.port == addr.port)
|
||||
{
|
||||
if(s.lastping && (!s.lastpong || ENET_TIME_GREATER(s.lastping, s.lastpong)))
|
||||
{
|
||||
client *c = findclient(s);
|
||||
if(c)
|
||||
{
|
||||
c->registeredserver = true;
|
||||
outputf(*c, "succreg\n");
|
||||
if(!c->message && gbanlists.length())
|
||||
{
|
||||
c->message = gbanlists.last();
|
||||
c->message->refs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!s.lastpong) updateserverlist = true;
|
||||
s.lastpong = servtime ? servtime : 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bangameservers()
|
||||
{
|
||||
loopvrev(gameservers) if(checkban(servbans, gameservers[i]->address.host))
|
||||
{
|
||||
delete gameservers.remove(i);
|
||||
updateserverlist = true;
|
||||
}
|
||||
}
|
||||
|
||||
void checkgameservers()
|
||||
{
|
||||
ENetBuffer buf;
|
||||
loopv(gameservers)
|
||||
{
|
||||
gameserver &s = *gameservers[i];
|
||||
if(s.lastping && s.lastpong && ENET_TIME_LESS_EQUAL(s.lastping, s.lastpong))
|
||||
{
|
||||
if(ENET_TIME_DIFFERENCE(servtime, s.lastpong) > KEEPALIVE_TIME)
|
||||
{
|
||||
delete gameservers.remove(i--);
|
||||
updateserverlist = true;
|
||||
}
|
||||
}
|
||||
else if(!s.lastping || ENET_TIME_DIFFERENCE(servtime, s.lastping) > PING_TIME)
|
||||
{
|
||||
if(s.numpings >= PING_RETRY)
|
||||
{
|
||||
servermessage(s, "failreg failed pinging server\n");
|
||||
delete gameservers.remove(i--);
|
||||
updateserverlist = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const uchar ping[] = { 0xFF, 0xFF, 1 };
|
||||
buf.data = (void *)ping;
|
||||
buf.dataLength = sizeof(ping);
|
||||
s.numpings++;
|
||||
s.lastping = servtime ? servtime : 1;
|
||||
enet_socket_send(pingsocket, &s.address, &buf, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void messagebuf::purge()
|
||||
{
|
||||
refs = max(refs - 1, 0);
|
||||
if(refs<=0 && owner.last()!=this)
|
||||
{
|
||||
owner.removeobj(this);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void purgeauths(client &c)
|
||||
{
|
||||
int expired = 0;
|
||||
loopv(c.authreqs)
|
||||
{
|
||||
if(ENET_TIME_DIFFERENCE(servtime, c.authreqs[i].reqtime) >= AUTH_TIME)
|
||||
{
|
||||
outputf(c, "failauth %u\n", c.authreqs[i].id);
|
||||
freechallenge(c.authreqs[i].answer);
|
||||
expired = i + 1;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
if(expired > 0) c.authreqs.remove(0, expired);
|
||||
}
|
||||
|
||||
void reqauth(client &c, uint id, char *name)
|
||||
{
|
||||
if(ENET_TIME_DIFFERENCE(servtime, c.lastauth) < AUTH_THROTTLE)
|
||||
return;
|
||||
|
||||
c.lastauth = servtime;
|
||||
|
||||
purgeauths(c);
|
||||
|
||||
time_t t = time(NULL);
|
||||
char *ct = ctime(&t);
|
||||
if(ct)
|
||||
{
|
||||
char *newline = strchr(ct, '\n');
|
||||
if(newline) *newline = '\0';
|
||||
}
|
||||
string ip;
|
||||
if(enet_address_get_host_ip(&c.address, ip, sizeof(ip)) < 0) copystring(ip, "-");
|
||||
conoutf("%s: attempting \"%s\" as %u from %s", ct ? ct : "-", name, id, ip);
|
||||
|
||||
userinfo *u = users.access(name);
|
||||
if(!u)
|
||||
{
|
||||
outputf(c, "failauth %u\n", id);
|
||||
return;
|
||||
}
|
||||
|
||||
if(c.authreqs.length() >= AUTH_LIMIT)
|
||||
{
|
||||
outputf(c, "failauth %u\n", c.authreqs[0].id);
|
||||
freechallenge(c.authreqs[0].answer);
|
||||
c.authreqs.remove(0);
|
||||
}
|
||||
|
||||
authreq &a = c.authreqs.add();
|
||||
a.reqtime = servtime;
|
||||
a.id = id;
|
||||
uint seed[3] = { uint(starttime), servtime, randomMT() };
|
||||
static vector<char> buf;
|
||||
buf.setsize(0);
|
||||
a.answer = genchallenge(u->pubkey, seed, sizeof(seed), buf);
|
||||
|
||||
outputf(c, "chalauth %u %s\n", id, buf.getbuf());
|
||||
}
|
||||
|
||||
void confauth(client &c, uint id, const char *val)
|
||||
{
|
||||
purgeauths(c);
|
||||
|
||||
loopv(c.authreqs) if(c.authreqs[i].id == id)
|
||||
{
|
||||
string ip;
|
||||
if(enet_address_get_host_ip(&c.address, ip, sizeof(ip)) < 0) copystring(ip, "-");
|
||||
if(checkchallenge(val, c.authreqs[i].answer))
|
||||
{
|
||||
outputf(c, "succauth %u\n", id);
|
||||
conoutf("succeeded %u from %s", id, ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputf(c, "failauth %u\n", id);
|
||||
conoutf("failed %u from %s", id, ip);
|
||||
}
|
||||
freechallenge(c.authreqs[i].answer);
|
||||
c.authreqs.remove(i--);
|
||||
return;
|
||||
}
|
||||
outputf(c, "failauth %u\n", id);
|
||||
}
|
||||
|
||||
bool checkclientinput(client &c)
|
||||
{
|
||||
if(c.inputpos<0) return true;
|
||||
char *end = (char *)memchr(c.input, '\n', c.inputpos);
|
||||
while(end)
|
||||
{
|
||||
*end++ = '\0';
|
||||
c.lastinput = servtime;
|
||||
|
||||
int port;
|
||||
uint id;
|
||||
string user, val;
|
||||
if(!strncmp(c.input, "list", 4) && (!c.input[4] || c.input[4] == '\n' || c.input[4] == '\r'))
|
||||
{
|
||||
genserverlist();
|
||||
if(gameserverlists.empty() || c.message) return false;
|
||||
c.message = gameserverlists.last();
|
||||
c.message->refs++;
|
||||
c.output.setsize(0);
|
||||
c.outputpos = 0;
|
||||
c.shouldpurge = true;
|
||||
return true;
|
||||
}
|
||||
else if(sscanf(c.input, "regserv %d", &port) == 1)
|
||||
{
|
||||
if(checkban(servbans, c.address.host)) return false;
|
||||
if(port < 0 || port > 0xFFFF || (c.servport >= 0 && port != c.servport)) outputf(c, "failreg invalid port\n");
|
||||
else
|
||||
{
|
||||
c.servport = port;
|
||||
addgameserver(c);
|
||||
}
|
||||
}
|
||||
else if(sscanf(c.input, "reqauth %u %100s", &id, user) == 2)
|
||||
{
|
||||
reqauth(c, id, user);
|
||||
}
|
||||
else if(sscanf(c.input, "confauth %u %100s", &id, val) == 2)
|
||||
{
|
||||
confauth(c, id, val);
|
||||
}
|
||||
c.inputpos = &c.input[c.inputpos] - end;
|
||||
memmove(c.input, end, c.inputpos);
|
||||
|
||||
end = (char *)memchr(c.input, '\n', c.inputpos);
|
||||
}
|
||||
return c.inputpos<(int)sizeof(c.input);
|
||||
}
|
||||
|
||||
ENetSocketSet readset, writeset;
|
||||
|
||||
void checkclients()
|
||||
{
|
||||
ENetSocketSet readset, writeset;
|
||||
ENetSocket maxsock = max(serversocket, pingsocket);
|
||||
ENET_SOCKETSET_EMPTY(readset);
|
||||
ENET_SOCKETSET_EMPTY(writeset);
|
||||
ENET_SOCKETSET_ADD(readset, serversocket);
|
||||
ENET_SOCKETSET_ADD(readset, pingsocket);
|
||||
loopv(clients)
|
||||
{
|
||||
client &c = *clients[i];
|
||||
if(c.authreqs.length()) purgeauths(c);
|
||||
if(c.message || c.output.length()) ENET_SOCKETSET_ADD(writeset, c.socket);
|
||||
else ENET_SOCKETSET_ADD(readset, c.socket);
|
||||
maxsock = max(maxsock, c.socket);
|
||||
}
|
||||
if(enet_socketset_select(maxsock, &readset, &writeset, 1000)<=0) return;
|
||||
|
||||
if(ENET_SOCKETSET_CHECK(readset, pingsocket)) checkserverpongs();
|
||||
if(ENET_SOCKETSET_CHECK(readset, serversocket))
|
||||
{
|
||||
ENetAddress address;
|
||||
ENetSocket clientsocket = enet_socket_accept(serversocket, &address);
|
||||
if(clients.length()>=CLIENT_LIMIT || checkban(bans, address.host)) enet_socket_destroy(clientsocket);
|
||||
else if(clientsocket!=ENET_SOCKET_NULL)
|
||||
{
|
||||
int dups = 0, oldest = -1;
|
||||
loopv(clients) if(clients[i]->address.host == address.host)
|
||||
{
|
||||
dups++;
|
||||
if(oldest<0 || clients[i]->connecttime < clients[oldest]->connecttime) oldest = i;
|
||||
}
|
||||
if(dups >= DUP_LIMIT) purgeclient(oldest);
|
||||
|
||||
client *c = new client;
|
||||
c->address = address;
|
||||
c->socket = clientsocket;
|
||||
c->connecttime = servtime;
|
||||
c->lastinput = servtime;
|
||||
clients.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
loopv(clients)
|
||||
{
|
||||
client &c = *clients[i];
|
||||
if((c.message || c.output.length()) && ENET_SOCKETSET_CHECK(writeset, c.socket))
|
||||
{
|
||||
const char *data = c.output.length() ? c.output.getbuf() : c.message->getbuf();
|
||||
int len = c.output.length() ? c.output.length() : c.message->length();
|
||||
ENetBuffer buf;
|
||||
buf.data = (void *)&data[c.outputpos];
|
||||
buf.dataLength = len-c.outputpos;
|
||||
int res = enet_socket_send(c.socket, NULL, &buf, 1);
|
||||
if(res>=0)
|
||||
{
|
||||
c.outputpos += res;
|
||||
if(c.outputpos>=len)
|
||||
{
|
||||
if(c.output.length()) c.output.setsize(0);
|
||||
else
|
||||
{
|
||||
c.message->purge();
|
||||
c.message = NULL;
|
||||
}
|
||||
c.outputpos = 0;
|
||||
if(!c.message && c.output.empty() && c.shouldpurge)
|
||||
{
|
||||
purgeclient(i--);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { purgeclient(i--); continue; }
|
||||
}
|
||||
if(ENET_SOCKETSET_CHECK(readset, c.socket))
|
||||
{
|
||||
ENetBuffer buf;
|
||||
buf.data = &c.input[c.inputpos];
|
||||
buf.dataLength = sizeof(c.input) - c.inputpos;
|
||||
int res = enet_socket_receive(c.socket, NULL, &buf, 1);
|
||||
if(res>0)
|
||||
{
|
||||
c.inputpos += res;
|
||||
c.input[min(c.inputpos, (int)sizeof(c.input)-1)] = '\0';
|
||||
if(!checkclientinput(c)) { purgeclient(i--); continue; }
|
||||
}
|
||||
else { purgeclient(i--); continue; }
|
||||
}
|
||||
if(c.output.length() > OUTPUT_LIMIT) { purgeclient(i--); continue; }
|
||||
if(ENET_TIME_DIFFERENCE(servtime, c.lastinput) >= (c.registeredserver ? KEEPALIVE_TIME : CLIENT_TIME)) { purgeclient(i--); continue; }
|
||||
}
|
||||
}
|
||||
|
||||
void banclients()
|
||||
{
|
||||
loopvrev(clients) if(checkban(bans, clients[i]->address.host)) purgeclient(i);
|
||||
}
|
||||
|
||||
volatile int reloadcfg = 1;
|
||||
|
||||
#ifndef WIN32
|
||||
void reloadsignal(int signum)
|
||||
{
|
||||
reloadcfg = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(enet_initialize()<0) fatal("Unable to initialise network module");
|
||||
atexit(enet_deinitialize);
|
||||
|
||||
const char *dir = "", *ip = NULL;
|
||||
int port = 41999;
|
||||
if(argc>=2) dir = argv[1];
|
||||
if(argc>=3) port = atoi(argv[2]);
|
||||
if(argc>=4) ip = argv[3];
|
||||
defformatstring(logname, "%smaster.log", dir);
|
||||
defformatstring(cfgname, "%smaster.cfg", dir);
|
||||
path(logname);
|
||||
path(cfgname);
|
||||
logfile = fopen(logname, "a");
|
||||
if(!logfile) logfile = stdout;
|
||||
setvbuf(logfile, NULL, _IOLBF, BUFSIZ);
|
||||
#ifndef WIN32
|
||||
signal(SIGUSR1, reloadsignal);
|
||||
#endif
|
||||
setupserver(port, ip);
|
||||
for(;;)
|
||||
{
|
||||
if(reloadcfg)
|
||||
{
|
||||
conoutf("reloading %s", cfgname);
|
||||
execfile(cfgname);
|
||||
bangameservers();
|
||||
banclients();
|
||||
gengbanlist();
|
||||
reloadcfg = 0;
|
||||
}
|
||||
|
||||
servtime = enet_time_get();
|
||||
checkclients();
|
||||
checkgameservers();
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -352,172 +352,6 @@ void localclienttoserver(int chan, ENetPacket *packet)
|
|||
if(c) process(packet, c->num, chan);
|
||||
}
|
||||
|
||||
#ifdef STANDALONE
|
||||
bool resolverwait(const char *name, ENetAddress *address)
|
||||
{
|
||||
return enet_address_set_host(address, name) >= 0;
|
||||
}
|
||||
|
||||
int connectwithtimeout(ENetSocket sock, const char *hostname, const ENetAddress &remoteaddress)
|
||||
{
|
||||
return enet_socket_connect(sock, &remoteaddress);
|
||||
}
|
||||
#endif
|
||||
|
||||
ENetSocket mastersock = ENET_SOCKET_NULL;
|
||||
ENetAddress masteraddress = { ENET_HOST_ANY, ENET_PORT_ANY }, serveraddress = { ENET_HOST_ANY, ENET_PORT_ANY };
|
||||
int lastupdatemaster = 0, lastconnectmaster = 0, masterconnecting = 0, masterconnected = 0;
|
||||
vector<char> masterout, masterin;
|
||||
int masteroutpos = 0, masterinpos = 0;
|
||||
VARN(updatemaster, allowupdatemaster, 0, 1, 1);
|
||||
|
||||
void disconnectmaster()
|
||||
{
|
||||
if(mastersock != ENET_SOCKET_NULL)
|
||||
{
|
||||
server::masterdisconnected();
|
||||
enet_socket_destroy(mastersock);
|
||||
mastersock = ENET_SOCKET_NULL;
|
||||
}
|
||||
|
||||
masterout.setsize(0);
|
||||
masterin.setsize(0);
|
||||
masteroutpos = masterinpos = 0;
|
||||
|
||||
masteraddress.host = ENET_HOST_ANY;
|
||||
masteraddress.port = ENET_PORT_ANY;
|
||||
|
||||
lastupdatemaster = masterconnecting = masterconnected = 0;
|
||||
}
|
||||
|
||||
SVARF(mastername, server::defaultmaster(), disconnectmaster());
|
||||
VARF(masterport, 1, server::masterport(), 0xFFFF, disconnectmaster());
|
||||
|
||||
ENetSocket connectmaster(bool wait)
|
||||
{
|
||||
if(!mastername[0]) return ENET_SOCKET_NULL;
|
||||
if(masteraddress.host == ENET_HOST_ANY)
|
||||
{
|
||||
if(isdedicatedserver()) logoutf("looking up %s...", mastername);
|
||||
masteraddress.port = masterport;
|
||||
if(!resolverwait(mastername, &masteraddress)) return ENET_SOCKET_NULL;
|
||||
}
|
||||
ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
|
||||
if(sock == ENET_SOCKET_NULL)
|
||||
{
|
||||
if(isdedicatedserver()) logoutf("could not open master server socket");
|
||||
return ENET_SOCKET_NULL;
|
||||
}
|
||||
if(wait || serveraddress.host == ENET_HOST_ANY || !enet_socket_bind(sock, &serveraddress))
|
||||
{
|
||||
enet_socket_set_option(sock, ENET_SOCKOPT_NONBLOCK, 1);
|
||||
if(wait)
|
||||
{
|
||||
if(!connectwithtimeout(sock, mastername, masteraddress)) return sock;
|
||||
}
|
||||
else if(!enet_socket_connect(sock, &masteraddress)) return sock;
|
||||
}
|
||||
enet_socket_destroy(sock);
|
||||
if(isdedicatedserver()) logoutf("could not connect to master server");
|
||||
return ENET_SOCKET_NULL;
|
||||
}
|
||||
|
||||
bool requestmaster(const char *req)
|
||||
{
|
||||
if(mastersock == ENET_SOCKET_NULL)
|
||||
{
|
||||
mastersock = connectmaster(false);
|
||||
if(mastersock == ENET_SOCKET_NULL) return false;
|
||||
lastconnectmaster = masterconnecting = totalmillis ? totalmillis : 1;
|
||||
}
|
||||
|
||||
if(masterout.length() >= 4096) return false;
|
||||
|
||||
masterout.put(req, strlen(req));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool requestmasterf(const char *fmt, ...)
|
||||
{
|
||||
defvformatstring(req, fmt, fmt);
|
||||
return requestmaster(req);
|
||||
}
|
||||
|
||||
void processmasterinput()
|
||||
{
|
||||
if(masterinpos >= masterin.length()) return;
|
||||
|
||||
char *input = &masterin[masterinpos], *end = (char *)memchr(input, '\n', masterin.length() - masterinpos);
|
||||
while(end)
|
||||
{
|
||||
*end = '\0';
|
||||
|
||||
const char *args = input;
|
||||
while(args < end && !iscubespace(*args)) args++;
|
||||
int cmdlen = args - input;
|
||||
while(args < end && iscubespace(*args)) args++;
|
||||
|
||||
if(matchstring(input, cmdlen, "failreg"))
|
||||
conoutf(CON_ERROR, "master server registration failed: %s", args);
|
||||
else if(matchstring(input, cmdlen, "succreg"))
|
||||
conoutf("master server registration succeeded");
|
||||
else server::processmasterinput(input, cmdlen, args);
|
||||
|
||||
end++;
|
||||
masterinpos = end - masterin.getbuf();
|
||||
input = end;
|
||||
end = (char *)memchr(input, '\n', masterin.length() - masterinpos);
|
||||
}
|
||||
|
||||
if(masterinpos >= masterin.length())
|
||||
{
|
||||
masterin.setsize(0);
|
||||
masterinpos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void flushmasteroutput()
|
||||
{
|
||||
if(masterconnecting && totalmillis - masterconnecting >= 60000)
|
||||
{
|
||||
logoutf("could not connect to master server");
|
||||
disconnectmaster();
|
||||
}
|
||||
if(masterout.empty() || !masterconnected) return;
|
||||
|
||||
ENetBuffer buf;
|
||||
buf.data = &masterout[masteroutpos];
|
||||
buf.dataLength = masterout.length() - masteroutpos;
|
||||
int sent = enet_socket_send(mastersock, NULL, &buf, 1);
|
||||
if(sent >= 0)
|
||||
{
|
||||
masteroutpos += sent;
|
||||
if(masteroutpos >= masterout.length())
|
||||
{
|
||||
masterout.setsize(0);
|
||||
masteroutpos = 0;
|
||||
}
|
||||
}
|
||||
else disconnectmaster();
|
||||
}
|
||||
|
||||
void flushmasterinput()
|
||||
{
|
||||
if(masterin.length() >= masterin.capacity())
|
||||
masterin.reserve(4096);
|
||||
|
||||
ENetBuffer buf;
|
||||
buf.data = masterin.getbuf() + masterin.length();
|
||||
buf.dataLength = masterin.capacity() - masterin.length();
|
||||
int recv = enet_socket_receive(mastersock, NULL, &buf, 1);
|
||||
if(recv > 0)
|
||||
{
|
||||
masterin.advance(recv);
|
||||
processmasterinput();
|
||||
}
|
||||
else disconnectmaster();
|
||||
}
|
||||
|
||||
static ENetAddress serverinfoaddress;
|
||||
|
||||
void sendserverinforeply(ucharbuf &p)
|
||||
|
@ -536,12 +370,6 @@ void checkserversockets() // reply all server info requests
|
|||
ENET_SOCKETSET_EMPTY(readset);
|
||||
ENET_SOCKETSET_EMPTY(writeset);
|
||||
ENetSocket maxsock = ENET_SOCKET_NULL;
|
||||
if(mastersock != ENET_SOCKET_NULL)
|
||||
{
|
||||
maxsock = maxsock == ENET_SOCKET_NULL ? mastersock : max(maxsock, mastersock);
|
||||
ENET_SOCKETSET_ADD(readset, mastersock);
|
||||
if(!masterconnected) ENET_SOCKETSET_ADD(writeset, mastersock);
|
||||
}
|
||||
if(lansock != ENET_SOCKET_NULL)
|
||||
{
|
||||
maxsock = maxsock == ENET_SOCKET_NULL ? lansock : max(maxsock, lansock);
|
||||
|
@ -561,29 +389,6 @@ void checkserversockets() // reply all server info requests
|
|||
p.len += len-2;
|
||||
server::serverinforeply(req, p);
|
||||
}
|
||||
|
||||
if(mastersock != ENET_SOCKET_NULL)
|
||||
{
|
||||
if(!masterconnected)
|
||||
{
|
||||
if(ENET_SOCKETSET_CHECK(readset, mastersock) || ENET_SOCKETSET_CHECK(writeset, mastersock))
|
||||
{
|
||||
int error = 0;
|
||||
if(enet_socket_get_option(mastersock, ENET_SOCKOPT_ERROR, &error) < 0 || error)
|
||||
{
|
||||
logoutf("could not connect to master server");
|
||||
disconnectmaster();
|
||||
}
|
||||
else
|
||||
{
|
||||
masterconnecting = 0;
|
||||
masterconnected = totalmillis ? totalmillis : 1;
|
||||
server::masterconnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(mastersock != ENET_SOCKET_NULL && ENET_SOCKETSET_CHECK(readset, mastersock)) flushmasterinput();
|
||||
}
|
||||
}
|
||||
|
||||
static int serverinfointercept(ENetHost *host, ENetEvent *event)
|
||||
|
@ -604,13 +409,6 @@ VARF(serverport, 0, server::serverport(), 0xFFFF, { if(!serverport) serverport =
|
|||
int curtime = 0, lastmillis = 0, elapsedtime = 0, totalmillis = 0;
|
||||
#endif
|
||||
|
||||
void updatemasterserver()
|
||||
{
|
||||
if(!masterconnected && lastconnectmaster && totalmillis-lastconnectmaster <= 5*60*1000) return;
|
||||
if(mastername[0] && allowupdatemaster) requestmasterf("regserv %d\n", serverport);
|
||||
lastupdatemaster = totalmillis ? totalmillis : 1;
|
||||
}
|
||||
|
||||
uint totalsecs = 0;
|
||||
|
||||
void updatetime()
|
||||
|
@ -650,12 +448,8 @@ void serverslice(bool dedicated, uint timeout) // main server update, called f
|
|||
}
|
||||
server::serverupdate();
|
||||
|
||||
flushmasteroutput();
|
||||
checkserversockets();
|
||||
|
||||
if(!lastupdatemaster || totalmillis-lastupdatemaster>60*60*1000) // send alive signal to masterserver every hour of uptime
|
||||
updatemasterserver();
|
||||
|
||||
if(totalmillis-laststatus>60*1000) // display bandwidth stats, useful for server ops
|
||||
{
|
||||
laststatus = totalmillis;
|
||||
|
@ -1059,7 +853,6 @@ bool setuplistenserver(bool dedicated)
|
|||
if(*serverip)
|
||||
{
|
||||
if(enet_address_set_host(&address, serverip)<0) conoutf(CON_WARN, "WARNING: server ip not resolved");
|
||||
else serveraddress.host = address.host;
|
||||
}
|
||||
serverhost = enet_host_create(&address, min(maxclients + server::reserveclients(), MAXCLIENTS), server::numchannels(), 0, serveruprate);
|
||||
if(!serverhost) return servererror(dedicated, "could not create server host");
|
||||
|
@ -1095,7 +888,6 @@ void initserver(bool listen, bool dedicated)
|
|||
if(listen)
|
||||
{
|
||||
dedicatedserver = dedicated;
|
||||
updatemasterserver();
|
||||
if(dedicated) rundedicatedserver(); // never returns
|
||||
#ifndef STANDALONE
|
||||
else conoutf("listen server started");
|
||||
|
@ -1108,13 +900,7 @@ void startlistenserver(int *usemaster)
|
|||
{
|
||||
if(serverhost) { conoutf(CON_ERROR, "listen server is already running"); return; }
|
||||
|
||||
allowupdatemaster = *usemaster>0 ? 1 : 0;
|
||||
|
||||
if(!setuplistenserver(false)) return;
|
||||
|
||||
updatemasterserver();
|
||||
|
||||
conoutf("listen server started for %d clients%s", maxclients, allowupdatemaster ? " and listed with master server" : "");
|
||||
}
|
||||
COMMAND(startlistenserver, "i");
|
||||
|
||||
|
|
|
@ -1,753 +0,0 @@
|
|||
#include "engine.hh"
|
||||
|
||||
struct resolverthread
|
||||
{
|
||||
SDL_Thread *thread;
|
||||
const char *query;
|
||||
int starttime;
|
||||
};
|
||||
|
||||
struct resolverresult
|
||||
{
|
||||
const char *query;
|
||||
ENetAddress address;
|
||||
};
|
||||
|
||||
vector<resolverthread> resolverthreads;
|
||||
vector<const char *> resolverqueries;
|
||||
vector<resolverresult> resolverresults;
|
||||
SDL_mutex *resolvermutex;
|
||||
SDL_cond *querycond, *resultcond;
|
||||
|
||||
#define RESOLVERTHREADS 2
|
||||
#define RESOLVERLIMIT 3000
|
||||
|
||||
int resolverloop(void * data)
|
||||
{
|
||||
resolverthread *rt = (resolverthread *)data;
|
||||
SDL_LockMutex(resolvermutex);
|
||||
SDL_Thread *thread = rt->thread;
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
if(!thread || SDL_GetThreadID(thread) != SDL_ThreadID())
|
||||
return 0;
|
||||
while(thread == rt->thread)
|
||||
{
|
||||
SDL_LockMutex(resolvermutex);
|
||||
while(resolverqueries.empty()) SDL_CondWait(querycond, resolvermutex);
|
||||
rt->query = resolverqueries.pop();
|
||||
rt->starttime = totalmillis;
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
|
||||
ENetAddress address = { ENET_HOST_ANY, ENET_PORT_ANY };
|
||||
enet_address_set_host(&address, rt->query);
|
||||
|
||||
SDL_LockMutex(resolvermutex);
|
||||
if(rt->query && thread == rt->thread)
|
||||
{
|
||||
resolverresult &rr = resolverresults.add();
|
||||
rr.query = rt->query;
|
||||
rr.address = address;
|
||||
rt->query = NULL;
|
||||
rt->starttime = 0;
|
||||
SDL_CondSignal(resultcond);
|
||||
}
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void resolverinit()
|
||||
{
|
||||
resolvermutex = SDL_CreateMutex();
|
||||
querycond = SDL_CreateCond();
|
||||
resultcond = SDL_CreateCond();
|
||||
|
||||
SDL_LockMutex(resolvermutex);
|
||||
loopi(RESOLVERTHREADS)
|
||||
{
|
||||
resolverthread &rt = resolverthreads.add();
|
||||
rt.query = NULL;
|
||||
rt.starttime = 0;
|
||||
rt.thread = SDL_CreateThread(resolverloop, "resolver", &rt);
|
||||
}
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
}
|
||||
|
||||
void resolverstop(resolverthread &rt)
|
||||
{
|
||||
SDL_LockMutex(resolvermutex);
|
||||
if(rt.query)
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 2)
|
||||
SDL_DetachThread(rt.thread);
|
||||
#endif
|
||||
rt.thread = SDL_CreateThread(resolverloop, "resolver", &rt);
|
||||
}
|
||||
rt.query = NULL;
|
||||
rt.starttime = 0;
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
}
|
||||
|
||||
void resolverclear()
|
||||
{
|
||||
if(resolverthreads.empty()) return;
|
||||
|
||||
SDL_LockMutex(resolvermutex);
|
||||
resolverqueries.shrink(0);
|
||||
resolverresults.shrink(0);
|
||||
loopv(resolverthreads)
|
||||
{
|
||||
resolverthread &rt = resolverthreads[i];
|
||||
resolverstop(rt);
|
||||
}
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
}
|
||||
|
||||
void resolverquery(const char *name)
|
||||
{
|
||||
if(resolverthreads.empty()) resolverinit();
|
||||
|
||||
SDL_LockMutex(resolvermutex);
|
||||
resolverqueries.add(name);
|
||||
SDL_CondSignal(querycond);
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
}
|
||||
|
||||
bool resolvercheck(const char **name, ENetAddress *address)
|
||||
{
|
||||
bool resolved = false;
|
||||
SDL_LockMutex(resolvermutex);
|
||||
if(!resolverresults.empty())
|
||||
{
|
||||
resolverresult &rr = resolverresults.pop();
|
||||
*name = rr.query;
|
||||
address->host = rr.address.host;
|
||||
resolved = true;
|
||||
}
|
||||
else loopv(resolverthreads)
|
||||
{
|
||||
resolverthread &rt = resolverthreads[i];
|
||||
if(rt.query && totalmillis - rt.starttime > RESOLVERLIMIT)
|
||||
{
|
||||
resolverstop(rt);
|
||||
*name = rt.query;
|
||||
resolved = true;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
bool resolverwait(const char *name, ENetAddress *address)
|
||||
{
|
||||
if(resolverthreads.empty()) resolverinit();
|
||||
|
||||
defformatstring(text, "resolving %s... (esc to abort)", name);
|
||||
renderprogress(0, text);
|
||||
|
||||
SDL_LockMutex(resolvermutex);
|
||||
resolverqueries.add(name);
|
||||
SDL_CondSignal(querycond);
|
||||
int starttime = SDL_GetTicks(), timeout = 0;
|
||||
bool resolved = false;
|
||||
for(;;)
|
||||
{
|
||||
SDL_CondWaitTimeout(resultcond, resolvermutex, 250);
|
||||
loopv(resolverresults) if(resolverresults[i].query == name)
|
||||
{
|
||||
address->host = resolverresults[i].address.host;
|
||||
resolverresults.remove(i);
|
||||
resolved = true;
|
||||
break;
|
||||
}
|
||||
if(resolved) break;
|
||||
|
||||
timeout = SDL_GetTicks() - starttime;
|
||||
renderprogress(min(float(timeout)/RESOLVERLIMIT, 1.0f), text);
|
||||
if(interceptkey(SDLK_ESCAPE)) timeout = RESOLVERLIMIT + 1;
|
||||
if(timeout > RESOLVERLIMIT) break;
|
||||
}
|
||||
if(!resolved && timeout > RESOLVERLIMIT)
|
||||
{
|
||||
loopv(resolverthreads)
|
||||
{
|
||||
resolverthread &rt = resolverthreads[i];
|
||||
if(rt.query == name) { resolverstop(rt); break; }
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
#define CONNLIMIT 20000
|
||||
|
||||
int connectwithtimeout(ENetSocket sock, const char *hostname, const ENetAddress &address)
|
||||
{
|
||||
defformatstring(text, "connecting to %s... (esc to abort)", hostname);
|
||||
renderprogress(0, text);
|
||||
|
||||
ENetSocketSet readset, writeset;
|
||||
if(!enet_socket_connect(sock, &address)) for(int starttime = SDL_GetTicks(), timeout = 0; timeout <= CONNLIMIT;)
|
||||
{
|
||||
ENET_SOCKETSET_EMPTY(readset);
|
||||
ENET_SOCKETSET_EMPTY(writeset);
|
||||
ENET_SOCKETSET_ADD(readset, sock);
|
||||
ENET_SOCKETSET_ADD(writeset, sock);
|
||||
int result = enet_socketset_select(sock, &readset, &writeset, 250);
|
||||
if(result < 0) break;
|
||||
else if(result > 0)
|
||||
{
|
||||
if(ENET_SOCKETSET_CHECK(readset, sock) || ENET_SOCKETSET_CHECK(writeset, sock))
|
||||
{
|
||||
int error = 0;
|
||||
if(enet_socket_get_option(sock, ENET_SOCKOPT_ERROR, &error) < 0 || error) break;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
timeout = SDL_GetTicks() - starttime;
|
||||
renderprogress(min(float(timeout)/CONNLIMIT, 1.0f), text);
|
||||
if(interceptkey(SDLK_ESCAPE)) break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct pingattempts
|
||||
{
|
||||
enum { MAXATTEMPTS = 2 };
|
||||
|
||||
int offset, attempts[MAXATTEMPTS];
|
||||
|
||||
pingattempts() : offset(0) { clearattempts(); }
|
||||
|
||||
void clearattempts() { memset(attempts, 0, sizeof(attempts)); }
|
||||
|
||||
void setoffset() { offset = 1 + rnd(0xFFFFFF); }
|
||||
|
||||
int encodeping(int millis)
|
||||
{
|
||||
millis += offset;
|
||||
return millis ? millis : 1;
|
||||
}
|
||||
|
||||
int decodeping(int val)
|
||||
{
|
||||
return val - offset;
|
||||
}
|
||||
|
||||
int addattempt(int millis)
|
||||
{
|
||||
int val = encodeping(millis);
|
||||
loopk(MAXATTEMPTS-1) attempts[k+1] = attempts[k];
|
||||
attempts[0] = val;
|
||||
return val;
|
||||
}
|
||||
|
||||
bool checkattempt(int val, bool del = true)
|
||||
{
|
||||
if(val) loopk(MAXATTEMPTS) if(attempts[k] == val)
|
||||
{
|
||||
if(del) attempts[k] = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static int currentprotocol = server::protocolversion();
|
||||
|
||||
enum { UNRESOLVED = 0, RESOLVING, RESOLVED };
|
||||
|
||||
struct serverinfo : servinfo, pingattempts
|
||||
{
|
||||
enum
|
||||
{
|
||||
WAITING = INT_MAX,
|
||||
|
||||
MAXPINGS = 3
|
||||
};
|
||||
|
||||
int resolved, lastping, nextping;
|
||||
int pings[MAXPINGS];
|
||||
ENetAddress address;
|
||||
bool keep;
|
||||
const char *password;
|
||||
|
||||
serverinfo()
|
||||
: resolved(UNRESOLVED), keep(false), password(NULL)
|
||||
{
|
||||
clearpings();
|
||||
setoffset();
|
||||
}
|
||||
|
||||
~serverinfo()
|
||||
{
|
||||
DELETEA(password);
|
||||
}
|
||||
|
||||
void clearpings()
|
||||
{
|
||||
ping = WAITING;
|
||||
loopk(MAXPINGS) pings[k] = WAITING;
|
||||
nextping = 0;
|
||||
lastping = -1;
|
||||
clearattempts();
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
clearpings();
|
||||
protocol = -1;
|
||||
numplayers = maxplayers = 0;
|
||||
attr.setsize(0);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
lastping = -1;
|
||||
}
|
||||
|
||||
void checkdecay(int decay)
|
||||
{
|
||||
if(lastping >= 0 && totalmillis - lastping >= decay)
|
||||
cleanup();
|
||||
if(lastping < 0) lastping = totalmillis;
|
||||
}
|
||||
|
||||
void calcping()
|
||||
{
|
||||
int numpings = 0, totalpings = 0;
|
||||
loopk(MAXPINGS) if(pings[k] != WAITING) { totalpings += pings[k]; numpings++; }
|
||||
ping = numpings ? totalpings/numpings : WAITING;
|
||||
}
|
||||
|
||||
void addping(int rtt, int millis)
|
||||
{
|
||||
if(millis >= lastping) lastping = -1;
|
||||
pings[nextping] = rtt;
|
||||
nextping = (nextping+1)%MAXPINGS;
|
||||
calcping();
|
||||
}
|
||||
|
||||
const char *status() const
|
||||
{
|
||||
if(address.host == ENET_HOST_ANY) return "[unknown host]";
|
||||
if(ping == WAITING) return "[waiting for response]";
|
||||
if(protocol < currentprotocol) return "[older protocol]";
|
||||
if(protocol > currentprotocol) return "[newer protocol]";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool valid() const { return !status(); }
|
||||
|
||||
static bool compare(serverinfo *a, serverinfo *b)
|
||||
{
|
||||
if(a->protocol == currentprotocol)
|
||||
{
|
||||
if(b->protocol != currentprotocol) return true;
|
||||
}
|
||||
else if(b->protocol == currentprotocol) return false;
|
||||
if(a->keep > b->keep) return true;
|
||||
if(a->keep < b->keep) return false;
|
||||
if(a->numplayers < b->numplayers) return false;
|
||||
if(a->numplayers > b->numplayers) return true;
|
||||
if(a->ping > b->ping) return false;
|
||||
if(a->ping < b->ping) return true;
|
||||
int cmp = strcmp(a->name, b->name);
|
||||
if(cmp != 0) return cmp < 0;
|
||||
if(a->address.port < b->address.port) return true;
|
||||
if(a->address.port > b->address.port) return false;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
vector<serverinfo *> servers;
|
||||
ENetSocket pingsock = ENET_SOCKET_NULL;
|
||||
int lastinfo = 0;
|
||||
|
||||
static serverinfo *newserver(const char *name, int port, uint ip = ENET_HOST_ANY)
|
||||
{
|
||||
serverinfo *si = new serverinfo;
|
||||
si->address.host = ip;
|
||||
si->address.port = port;
|
||||
if(ip!=ENET_HOST_ANY) si->resolved = RESOLVED;
|
||||
|
||||
if(name) copystring(si->name, name);
|
||||
else if(ip==ENET_HOST_ANY || enet_address_get_host_ip(&si->address, si->name, sizeof(si->name)) < 0)
|
||||
{
|
||||
delete si;
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
servers.add(si);
|
||||
|
||||
return si;
|
||||
}
|
||||
|
||||
void addserver(const char *name, int port, const char *password, bool keep)
|
||||
{
|
||||
if(port <= 0) port = server::serverport();
|
||||
loopv(servers)
|
||||
{
|
||||
serverinfo *s = servers[i];
|
||||
if(strcmp(s->name, name) || s->address.port != port) continue;
|
||||
if(password && (!s->password || strcmp(s->password, password)))
|
||||
{
|
||||
DELETEA(s->password);
|
||||
s->password = newstring(password);
|
||||
}
|
||||
if(keep && !s->keep) s->keep = true;
|
||||
return;
|
||||
}
|
||||
serverinfo *s = newserver(name, port);
|
||||
if(!s) return;
|
||||
if(password) s->password = newstring(password);
|
||||
s->keep = keep;
|
||||
}
|
||||
|
||||
VARP(searchlan, 0, 0, 1);
|
||||
VARMP(servpingrate, 1, 5, 60, 1000);
|
||||
VARMP(servpingdecay, 1, 15, 60, 1000);
|
||||
VARP(maxservpings, 0, 10, 1000);
|
||||
|
||||
pingattempts lanpings;
|
||||
|
||||
template<size_t N> static inline void buildping(ENetBuffer &buf, uchar (&ping)[N], pingattempts &a)
|
||||
{
|
||||
ucharbuf p(ping, N);
|
||||
p.put(0xFF); p.put(0xFF);
|
||||
putint(p, a.addattempt(totalmillis));
|
||||
buf.data = ping;
|
||||
buf.dataLength = p.length();
|
||||
}
|
||||
|
||||
void pingservers()
|
||||
{
|
||||
if(pingsock == ENET_SOCKET_NULL)
|
||||
{
|
||||
pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
|
||||
if(pingsock == ENET_SOCKET_NULL)
|
||||
{
|
||||
lastinfo = totalmillis;
|
||||
return;
|
||||
}
|
||||
enet_socket_set_option(pingsock, ENET_SOCKOPT_NONBLOCK, 1);
|
||||
enet_socket_set_option(pingsock, ENET_SOCKOPT_BROADCAST, 1);
|
||||
|
||||
lanpings.setoffset();
|
||||
}
|
||||
|
||||
ENetBuffer buf;
|
||||
uchar ping[MAXTRANS];
|
||||
|
||||
static int lastping = 0;
|
||||
if(lastping >= servers.length()) lastping = 0;
|
||||
loopi(maxservpings ? min(servers.length(), maxservpings) : servers.length())
|
||||
{
|
||||
serverinfo &si = *servers[lastping];
|
||||
if(++lastping >= servers.length()) lastping = 0;
|
||||
if(si.address.host == ENET_HOST_ANY) continue;
|
||||
buildping(buf, ping, si);
|
||||
enet_socket_send(pingsock, &si.address, &buf, 1);
|
||||
|
||||
si.checkdecay(servpingdecay);
|
||||
}
|
||||
if(searchlan)
|
||||
{
|
||||
ENetAddress address;
|
||||
address.host = ENET_HOST_BROADCAST;
|
||||
address.port = server::laninfoport();
|
||||
buildping(buf, ping, lanpings);
|
||||
enet_socket_send(pingsock, &address, &buf, 1);
|
||||
}
|
||||
lastinfo = totalmillis;
|
||||
}
|
||||
|
||||
void checkresolver()
|
||||
{
|
||||
int resolving = 0;
|
||||
loopv(servers)
|
||||
{
|
||||
serverinfo &si = *servers[i];
|
||||
if(si.resolved == RESOLVED) continue;
|
||||
if(si.address.host == ENET_HOST_ANY)
|
||||
{
|
||||
if(si.resolved == UNRESOLVED) { si.resolved = RESOLVING; resolverquery(si.name); }
|
||||
resolving++;
|
||||
}
|
||||
}
|
||||
if(!resolving) return;
|
||||
|
||||
const char *name = NULL;
|
||||
for(;;)
|
||||
{
|
||||
ENetAddress addr = { ENET_HOST_ANY, ENET_PORT_ANY };
|
||||
if(!resolvercheck(&name, &addr)) break;
|
||||
loopv(servers)
|
||||
{
|
||||
serverinfo &si = *servers[i];
|
||||
if(name == si.name)
|
||||
{
|
||||
si.resolved = RESOLVED;
|
||||
si.address.host = addr.host;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int lastreset = 0;
|
||||
|
||||
void checkpings()
|
||||
{
|
||||
if(pingsock==ENET_SOCKET_NULL) return;
|
||||
enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
|
||||
ENetBuffer buf;
|
||||
ENetAddress addr;
|
||||
uchar ping[MAXTRANS];
|
||||
char text[MAXTRANS];
|
||||
buf.data = ping;
|
||||
buf.dataLength = sizeof(ping);
|
||||
while(enet_socket_wait(pingsock, &events, 0) >= 0 && events)
|
||||
{
|
||||
int len = enet_socket_receive(pingsock, &addr, &buf, 1);
|
||||
if(len <= 0) return;
|
||||
ucharbuf p(ping, len);
|
||||
int millis = getint(p);
|
||||
serverinfo *si = NULL;
|
||||
loopv(servers) if(addr.host == servers[i]->address.host && addr.port == servers[i]->address.port) { si = servers[i]; break; }
|
||||
if(si)
|
||||
{
|
||||
if(!si->checkattempt(millis)) continue;
|
||||
millis = si->decodeping(millis);
|
||||
}
|
||||
else if(!searchlan || !lanpings.checkattempt(millis, false)) continue;
|
||||
else
|
||||
{
|
||||
si = newserver(NULL, addr.port, addr.host);
|
||||
millis = lanpings.decodeping(millis);
|
||||
}
|
||||
int rtt = clamp(totalmillis - millis, 0, min(servpingdecay, totalmillis));
|
||||
if(millis >= lastreset && rtt < servpingdecay) si->addping(rtt, millis);
|
||||
si->protocol = getint(p);
|
||||
si->numplayers = getint(p);
|
||||
si->maxplayers = getint(p);
|
||||
int numattr = getint(p);
|
||||
si->attr.setsize(0);
|
||||
loopj(numattr) { int attr = getint(p); if(p.overread()) break; si->attr.add(attr); }
|
||||
getstring(text, p);
|
||||
filtertext(si->map, text, false);
|
||||
getstring(text, p);
|
||||
filtertext(si->desc, text, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void sortservers()
|
||||
{
|
||||
servers.sort(serverinfo::compare);
|
||||
}
|
||||
COMMAND(sortservers, "");
|
||||
|
||||
VARP(autosortservers, 0, 1, 1);
|
||||
VARP(autoupdateservers, 0, 1, 1);
|
||||
|
||||
void refreshservers()
|
||||
{
|
||||
static int lastrefresh = 0;
|
||||
if(lastrefresh==totalmillis) return;
|
||||
if(totalmillis - lastrefresh > 1000)
|
||||
{
|
||||
loopv(servers) servers[i]->reset();
|
||||
lastreset = totalmillis;
|
||||
}
|
||||
lastrefresh = totalmillis;
|
||||
|
||||
checkresolver();
|
||||
checkpings();
|
||||
if(totalmillis - lastinfo >= servpingrate/(maxservpings ? max(1, (servers.length() + maxservpings - 1) / maxservpings) : 1)) pingservers();
|
||||
if(autosortservers) sortservers();
|
||||
}
|
||||
|
||||
ICOMMAND(numservers, "", (), intret(servers.length()))
|
||||
|
||||
#define GETSERVERINFO_(idx, si, body) \
|
||||
if(servers.inrange(idx)) \
|
||||
{ \
|
||||
serverinfo &si = *servers[idx]; \
|
||||
body; \
|
||||
}
|
||||
#define GETSERVERINFO(idx, si, body) GETSERVERINFO_(idx, si, if(si.valid()) { body; })
|
||||
|
||||
ICOMMAND(servinfovalid, "i", (int *i), GETSERVERINFO_(*i, si, intret(si.valid() ? 1 : 0)));
|
||||
ICOMMAND(servinfodesc, "i", (int *i),
|
||||
GETSERVERINFO_(*i, si,
|
||||
{
|
||||
const char *status = si.status();
|
||||
result(status ? status : si.desc);
|
||||
}));
|
||||
ICOMMAND(servinfoname, "i", (int *i), GETSERVERINFO_(*i, si, result(si.name)));
|
||||
ICOMMAND(servinfoport, "i", (int *i), GETSERVERINFO_(*i, si, intret(si.address.port)));
|
||||
ICOMMAND(servinfohaspassword, "i", (int *i), GETSERVERINFO_(*i, si, intret(si.password && si.password[0] ? 1 : 0)));
|
||||
ICOMMAND(servinfokeep, "i", (int *i), GETSERVERINFO_(*i, si, intret(si.keep ? 1 : 0)));
|
||||
ICOMMAND(servinfomap, "i", (int *i), GETSERVERINFO(*i, si, result(si.map)));
|
||||
ICOMMAND(servinfoping, "i", (int *i), GETSERVERINFO(*i, si, intret(si.ping)));
|
||||
ICOMMAND(servinfonumplayers, "i", (int *i), GETSERVERINFO(*i, si, intret(si.numplayers)));
|
||||
ICOMMAND(servinfomaxplayers, "i", (int *i), GETSERVERINFO(*i, si, intret(si.maxplayers)));
|
||||
ICOMMAND(servinfoplayers, "i", (int *i),
|
||||
GETSERVERINFO(*i, si,
|
||||
{
|
||||
if(si.maxplayers <= 0) intret(si.numplayers);
|
||||
else result(tempformatstring(si.numplayers >= si.maxplayers ? "\f3%d/%d" : "%d/%d", si.numplayers, si.maxplayers));
|
||||
}));
|
||||
ICOMMAND(servinfoattr, "ii", (int *i, int *n), GETSERVERINFO(*i, si, { if(si.attr.inrange(*n)) intret(si.attr[*n]); }));
|
||||
|
||||
ICOMMAND(connectservinfo, "is", (int *i, char *pw), GETSERVERINFO_(*i, si, connectserv(si.name, si.address.port, pw[0] ? pw : si.password)));
|
||||
|
||||
servinfo *getservinfo(int i)
|
||||
{
|
||||
return servers.inrange(i) && servers[i]->valid() ? servers[i] : NULL;
|
||||
}
|
||||
|
||||
void clearservers(bool full = false)
|
||||
{
|
||||
resolverclear();
|
||||
if(full) servers.deletecontents();
|
||||
else loopvrev(servers) if(!servers[i]->keep) delete servers.remove(i);
|
||||
}
|
||||
|
||||
#define RETRIEVELIMIT 20000
|
||||
|
||||
void retrieveservers(vector<char> &data)
|
||||
{
|
||||
ENetSocket sock = connectmaster(true);
|
||||
if(sock == ENET_SOCKET_NULL) return;
|
||||
|
||||
extern char *mastername;
|
||||
defformatstring(text, "retrieving servers from %s... (esc to abort)", mastername);
|
||||
renderprogress(0, text);
|
||||
|
||||
int starttime = SDL_GetTicks(), timeout = 0;
|
||||
const char *req = "list\n";
|
||||
int reqlen = strlen(req);
|
||||
ENetBuffer buf;
|
||||
while(reqlen > 0)
|
||||
{
|
||||
enet_uint32 events = ENET_SOCKET_WAIT_SEND;
|
||||
if(enet_socket_wait(sock, &events, 250) >= 0 && events)
|
||||
{
|
||||
buf.data = (void *)req;
|
||||
buf.dataLength = reqlen;
|
||||
int sent = enet_socket_send(sock, NULL, &buf, 1);
|
||||
if(sent < 0) break;
|
||||
req += sent;
|
||||
reqlen -= sent;
|
||||
if(reqlen <= 0) break;
|
||||
}
|
||||
timeout = SDL_GetTicks() - starttime;
|
||||
renderprogress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
|
||||
if(interceptkey(SDLK_ESCAPE)) timeout = RETRIEVELIMIT + 1;
|
||||
if(timeout > RETRIEVELIMIT) break;
|
||||
}
|
||||
|
||||
if(reqlen <= 0) for(;;)
|
||||
{
|
||||
enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
|
||||
if(enet_socket_wait(sock, &events, 250) >= 0 && events)
|
||||
{
|
||||
if(data.length() >= data.capacity()) data.reserve(4096);
|
||||
buf.data = data.getbuf() + data.length();
|
||||
buf.dataLength = data.capacity() - data.length();
|
||||
int recv = enet_socket_receive(sock, NULL, &buf, 1);
|
||||
if(recv <= 0) break;
|
||||
data.advance(recv);
|
||||
}
|
||||
timeout = SDL_GetTicks() - starttime;
|
||||
renderprogress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
|
||||
if(interceptkey(SDLK_ESCAPE)) timeout = RETRIEVELIMIT + 1;
|
||||
if(timeout > RETRIEVELIMIT) break;
|
||||
}
|
||||
|
||||
if(data.length()) data.add('\0');
|
||||
enet_socket_destroy(sock);
|
||||
}
|
||||
|
||||
bool updatedservers = false;
|
||||
|
||||
void updatefrommaster()
|
||||
{
|
||||
vector<char> data;
|
||||
retrieveservers(data);
|
||||
if(data.empty()) conoutf("master server not replying");
|
||||
else
|
||||
{
|
||||
clearservers();
|
||||
char *line = data.getbuf();
|
||||
while(char *end = (char *)memchr(line, '\n', data.length() - (line - data.getbuf())))
|
||||
{
|
||||
*end = '\0';
|
||||
|
||||
const char *args = line;
|
||||
while(args < end && !iscubespace(*args)) args++;
|
||||
int cmdlen = args - line;
|
||||
while(args < end && iscubespace(*args)) args++;
|
||||
|
||||
if(matchstring(line, cmdlen, "addserver"))
|
||||
{
|
||||
string ip;
|
||||
int port;
|
||||
if(sscanf(args, "%100s %d", ip, &port) == 2) addserver(ip, port);
|
||||
}
|
||||
else if(matchstring(line, cmdlen, "echo")) conoutf("\f1%s", args);
|
||||
|
||||
line = end + 1;
|
||||
}
|
||||
}
|
||||
refreshservers();
|
||||
updatedservers = true;
|
||||
}
|
||||
|
||||
void initservers()
|
||||
{
|
||||
if(autoupdateservers && !updatedservers) updatefrommaster();
|
||||
}
|
||||
|
||||
ICOMMAND(addserver, "sis", (const char *name, int *port, const char *password), addserver(name, *port, password[0] ? password : NULL));
|
||||
ICOMMAND(keepserver, "sis", (const char *name, int *port, const char *password), addserver(name, *port, password[0] ? password : NULL, true));
|
||||
ICOMMAND(clearservers, "i", (int *full), clearservers(*full!=0));
|
||||
COMMAND(updatefrommaster, "");
|
||||
COMMAND(initservers, "");
|
||||
COMMAND(refreshservers, "");
|
||||
|
||||
void writeservercfg()
|
||||
{
|
||||
if(!game::savedservers()) return;
|
||||
stream *f = openutf8file(path(game::savedservers(), true), "w");
|
||||
if(!f) return;
|
||||
int kept = 0;
|
||||
loopv(servers)
|
||||
{
|
||||
serverinfo *s = servers[i];
|
||||
if(s->keep)
|
||||
{
|
||||
if(!kept) f->printf("// servers that should never be cleared from the server list\n\n");
|
||||
if(s->password) f->printf("keepserver %s %d %s\n", escapeid(s->name), s->address.port, escapestring(s->password));
|
||||
else f->printf("keepserver %s %d\n", escapeid(s->name), s->address.port);
|
||||
kept++;
|
||||
}
|
||||
}
|
||||
if(kept) f->printf("\n");
|
||||
f->printf("// servers connected to are added here automatically\n\n");
|
||||
loopv(servers)
|
||||
{
|
||||
serverinfo *s = servers[i];
|
||||
if(!s->keep)
|
||||
{
|
||||
if(s->password) f->printf("addserver %s %d %s\n", escapeid(s->name), s->address.port, escapestring(s->password));
|
||||
else f->printf("addserver %s %d\n", escapeid(s->name), s->address.port);
|
||||
}
|
||||
}
|
||||
delete f;
|
||||
}
|
||||
|
|
@ -135,7 +135,6 @@ static const int msgsizes[] = // size inclusive message token, 0 f
|
|||
|
||||
#define TESSERACT_SERVER_PORT 42000
|
||||
#define TESSERACT_LANINFO_PORT 41998
|
||||
#define TESSERACT_MASTER_PORT 41999
|
||||
#define PROTOCOL_VERSION 2 // bump when protocol changes
|
||||
|
||||
#define MAXNAMELEN 15
|
||||
|
|
|
@ -15,8 +15,6 @@ namespace game
|
|||
const char *gameident() { return "Tesseract"; }
|
||||
}
|
||||
|
||||
extern ENetAddress masteraddress;
|
||||
|
||||
namespace server
|
||||
{
|
||||
struct server_entity // server side version of "entity" type
|
||||
|
@ -971,18 +969,6 @@ namespace server
|
|||
return ci && ci->connected;
|
||||
}
|
||||
|
||||
void masterconnected()
|
||||
{
|
||||
}
|
||||
|
||||
void masterdisconnected()
|
||||
{
|
||||
}
|
||||
|
||||
void processmasterinput(const char *cmd, int cmdlen, const char *args)
|
||||
{
|
||||
}
|
||||
|
||||
void receivefile(int sender, uchar *data, int len)
|
||||
{
|
||||
clientinfo *ci = getinfo(sender);
|
||||
|
@ -1397,8 +1383,6 @@ namespace server
|
|||
|
||||
int laninfoport() { return TESSERACT_LANINFO_PORT; }
|
||||
int serverport() { return TESSERACT_SERVER_PORT; }
|
||||
const char *defaultmaster() { return "master.tesseract.gg"; }
|
||||
int masterport() { return TESSERACT_MASTER_PORT; }
|
||||
int numchannels() { return 3; }
|
||||
|
||||
void serverinforeply(ucharbuf &req, ucharbuf &p)
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
client_src = [
|
||||
'../shared/crypto.cc',
|
||||
'../shared/stream.cc',
|
||||
'../shared/tools.cc',
|
||||
'../engine/command.cc',
|
||||
'../engine/master.cc'
|
||||
]
|
||||
|
||||
threads_dep = dependency('threads')
|
||||
zlib_dep = dependency('zlib')
|
||||
|
||||
executable('native_master',
|
||||
client_src,
|
||||
dependencies: [
|
||||
threads_dep, libenet, zlib_dep
|
||||
],
|
||||
include_directories: octacore_includes,
|
||||
cpp_args: ['-DSTANDALONE'],
|
||||
install: true,
|
||||
install_dir: join_paths(meson.source_root(), 'bin_unix')
|
||||
)
|
|
@ -7,5 +7,4 @@ add_global_arguments('-fsigned-char', language: 'cpp')
|
|||
|
||||
subdir('enet')
|
||||
subdir('client')
|
||||
subdir('server')
|
||||
subdir('master')
|
||||
subdir('server')
|
|
@ -485,8 +485,6 @@ extern void kicknonlocalclients(int reason = DISC_NONE);
|
|||
extern bool hasnonlocalclients();
|
||||
extern bool haslocalclients();
|
||||
extern void sendserverinforeply(ucharbuf &p);
|
||||
extern bool requestmaster(const char *req);
|
||||
extern bool requestmasterf(const char *fmt, ...) PRINTFARGS(1, 2);
|
||||
extern bool isdedicatedserver();
|
||||
|
||||
// serverbrowser
|
||||
|
|
|
@ -112,11 +112,6 @@ namespace server
|
|||
extern int protocolversion();
|
||||
extern int laninfoport();
|
||||
extern int serverport();
|
||||
extern const char *defaultmaster();
|
||||
extern int masterport();
|
||||
extern void processmasterinput(const char *cmd, int cmdlen, const char *args);
|
||||
extern void masterconnected();
|
||||
extern void masterdisconnected();
|
||||
extern bool ispaused();
|
||||
extern int scaletime(int t);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue