OctaCore/src/engine/server.cc

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();
}