// 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; string hostname; void *info; }; vector 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->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; } void cleanupserver() { } 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 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); } void logoutfv(const char *fmt, va_list args) { FILE *f = getlogfile(); if(f) writelogv(f, fmt, args); } void initserver() { server::serverinit(); }