283 lines
6.1 KiB
C++
283 lines
6.1 KiB
C++
// server.cpp: little more than enhanced multicaster
|
|
// runs dedicated or as client coroutine
|
|
|
|
#include "engine.hh"
|
|
|
|
#define LOGSTRLEN 512
|
|
|
|
static FILE *logfile = NULL;
|
|
|
|
void closelogfile()
|
|
{
|
|
if(logfile)
|
|
{
|
|
fclose(logfile);
|
|
logfile = NULL;
|
|
}
|
|
}
|
|
|
|
FILE *getlogfile()
|
|
{
|
|
#ifdef WIN32
|
|
return logfile;
|
|
#else
|
|
return logfile ? logfile : stdout;
|
|
#endif
|
|
}
|
|
|
|
void setlogfile(const char *fname)
|
|
{
|
|
closelogfile();
|
|
if(fname && fname[0])
|
|
{
|
|
fname = findfile(fname, "w");
|
|
if(fname) logfile = fopen(fname, "w");
|
|
}
|
|
FILE *f = getlogfile();
|
|
if(f) setvbuf(f, NULL, _IOLBF, BUFSIZ);
|
|
}
|
|
|
|
void logoutf(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
logoutfv(fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
static void writelog(FILE *file, const char *buf)
|
|
{
|
|
static uchar ubuf[512];
|
|
size_t len = strlen(buf), carry = 0;
|
|
while(carry < len)
|
|
{
|
|
size_t numu = encodeutf8(ubuf, sizeof(ubuf)-1, &((const uchar *)buf)[carry], len - carry, &carry);
|
|
if(carry >= len) ubuf[numu++] = '\n';
|
|
fwrite(ubuf, 1, numu, file);
|
|
}
|
|
}
|
|
|
|
static void writelogv(FILE *file, const char *fmt, va_list args)
|
|
{
|
|
static char buf[LOGSTRLEN];
|
|
vformatstring(buf, fmt, args, sizeof(buf));
|
|
writelog(file, buf);
|
|
}
|
|
|
|
#define DEFAULTCLIENTS 8
|
|
|
|
enum { ST_EMPTY, ST_LOCAL, ST_TCPIP };
|
|
|
|
struct client // server side version of "dynent" type
|
|
{
|
|
int type;
|
|
int num;
|
|
ENetPeer *peer;
|
|
string hostname;
|
|
void *info;
|
|
};
|
|
|
|
vector<client *> clients;
|
|
|
|
int laststatus = 0;
|
|
|
|
int localclients = 0;
|
|
|
|
bool hasnonlocalclients() { return false; }
|
|
bool haslocalclients() { return localclients!=0; }
|
|
|
|
client &addclient(int type)
|
|
{
|
|
client *c = NULL;
|
|
loopv(clients) if(clients[i]->type==ST_EMPTY)
|
|
{
|
|
c = clients[i];
|
|
break;
|
|
}
|
|
if(!c)
|
|
{
|
|
c = new client;
|
|
c->num = clients.length();
|
|
clients.add(c);
|
|
}
|
|
c->info = server::newclientinfo();
|
|
c->type = type;
|
|
switch(type)
|
|
{
|
|
case ST_LOCAL: localclients++; break;
|
|
}
|
|
return *c;
|
|
}
|
|
|
|
void delclient(client *c)
|
|
{
|
|
if(!c) return;
|
|
switch(c->type)
|
|
{
|
|
case ST_LOCAL: localclients--; break;
|
|
case ST_EMPTY: return;
|
|
}
|
|
c->type = ST_EMPTY;
|
|
c->peer = NULL;
|
|
if(c->info)
|
|
{
|
|
server::deleteclientinfo(c->info);
|
|
c->info = NULL;
|
|
}
|
|
}
|
|
|
|
void cleanupserver()
|
|
{
|
|
}
|
|
|
|
void process(ENetPacket *packet, int sender, int chan);
|
|
//void disconnect_client(int n, int reason);
|
|
|
|
int getservermtu() { return -1; }
|
|
void *getclientinfo(int i) { return !clients.inrange(i) || clients[i]->type==ST_EMPTY ? NULL : clients[i]->info; }
|
|
ENetPeer *getclientpeer(int i) { return NULL; }
|
|
int getnumclients() { return clients.length(); }
|
|
uint getclientip(int n) { return 0; }
|
|
|
|
void sendpacket(int n, int chan, ENetPacket *packet, int exclude)
|
|
{
|
|
if(n<0)
|
|
{
|
|
loopv(clients) if(i!=exclude && server::allowbroadcast(i)) sendpacket(i, chan, packet);
|
|
return;
|
|
}
|
|
switch(clients[n]->type)
|
|
{
|
|
case ST_LOCAL:
|
|
localservertoclient(chan, packet);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ENetPacket *sendf(int cn, int chan, const char *format, ...)
|
|
{
|
|
int exclude = -1;
|
|
bool reliable = false;
|
|
if(*format=='r') { reliable = true; ++format; }
|
|
packetbuf p(MAXTRANS, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
|
|
va_list args;
|
|
va_start(args, format);
|
|
while(*format) switch(*format++)
|
|
{
|
|
case 'x':
|
|
exclude = va_arg(args, int);
|
|
break;
|
|
|
|
case 'v':
|
|
{
|
|
int n = va_arg(args, int);
|
|
int *v = va_arg(args, int *);
|
|
loopi(n) putint(p, v[i]);
|
|
break;
|
|
}
|
|
|
|
case 'i':
|
|
{
|
|
int n = isdigit(*format) ? *format++-'0' : 1;
|
|
loopi(n) putint(p, va_arg(args, int));
|
|
break;
|
|
}
|
|
case 'f':
|
|
{
|
|
int n = isdigit(*format) ? *format++-'0' : 1;
|
|
loopi(n) putfloat(p, (float)va_arg(args, double));
|
|
break;
|
|
}
|
|
case 's': sendstring(va_arg(args, const char *), p); break;
|
|
case 'm':
|
|
{
|
|
int n = va_arg(args, int);
|
|
p.put(va_arg(args, uchar *), n);
|
|
break;
|
|
}
|
|
}
|
|
va_end(args);
|
|
ENetPacket *packet = p.finalize();
|
|
sendpacket(cn, chan, packet, exclude);
|
|
return packet->referenceCount > 0 ? packet : NULL;
|
|
}
|
|
|
|
const char *disconnectreason(int reason)
|
|
{
|
|
switch(reason)
|
|
{
|
|
case DISC_EOP: return "end of packet";
|
|
case DISC_LOCAL: return "server is in local mode";
|
|
case DISC_KICK: return "kicked/banned";
|
|
case DISC_MSGERR: return "message error";
|
|
case DISC_IPBAN: return "ip is banned";
|
|
case DISC_PRIVATE: return "server is in private mode";
|
|
case DISC_MAXCLIENTS: return "server FULL";
|
|
case DISC_TIMEOUT: return "connection timed out";
|
|
case DISC_OVERFLOW: return "overflow";
|
|
case DISC_PASSWORD: return "invalid password";
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
void disconnect_client(int n, int reason)
|
|
{
|
|
}
|
|
|
|
void kicknonlocalclients(int reason)
|
|
{
|
|
}
|
|
|
|
void process(ENetPacket *packet, int sender, int chan) // sender may be -1
|
|
{
|
|
packetbuf p(packet);
|
|
server::parsepacket(sender, chan, p);
|
|
if(p.overread()) { disconnect_client(sender, DISC_EOP); return; }
|
|
}
|
|
|
|
void localclienttoserver(int chan, ENetPacket *packet)
|
|
{
|
|
client *c = NULL;
|
|
loopv(clients) if(clients[i]->type==ST_LOCAL) { c = clients[i]; break; }
|
|
if(c) process(packet, c->num, chan);
|
|
}
|
|
|
|
void updatetime()
|
|
{
|
|
}
|
|
|
|
void localdisconnect(bool cleanup)
|
|
{
|
|
bool disconnected = false;
|
|
loopv(clients) if(clients[i]->type==ST_LOCAL)
|
|
{
|
|
server::localdisconnect(i);
|
|
delclient(clients[i]);
|
|
disconnected = true;
|
|
}
|
|
if(!disconnected) return;
|
|
game::gamedisconnect(cleanup);
|
|
mainmenu = 1;
|
|
}
|
|
|
|
void localconnect()
|
|
{
|
|
if(initing) return;
|
|
client &c = addclient(ST_LOCAL);
|
|
copystring(c.hostname, "local");
|
|
game::gameconnect(false);
|
|
server::localconnect(c.num);
|
|
}
|
|
|
|
void logoutfv(const char *fmt, va_list args)
|
|
{
|
|
FILE *f = getlogfile();
|
|
if(f) writelogv(f, fmt, args);
|
|
}
|
|
|
|
void initserver()
|
|
{
|
|
server::serverinit();
|
|
}
|