|
|
|
@ -65,44 +65,6 @@ static void writelogv(FILE *file, const char *fmt, va_list args) |
|
|
|
|
writelog(file, buf); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef STANDALONE |
|
|
|
|
void fatal(const char *fmt, ...) |
|
|
|
|
{ |
|
|
|
|
void cleanupserver(); |
|
|
|
|
cleanupserver(); |
|
|
|
|
defvformatstring(msg,fmt,fmt); |
|
|
|
|
if(logfile) logoutf("%s", msg); |
|
|
|
|
#ifdef WIN32 |
|
|
|
|
MessageBox(NULL, msg, "Tesseract fatal error", MB_OK|MB_SYSTEMMODAL); |
|
|
|
|
#else |
|
|
|
|
fprintf(stderr, "server error: %s\n", msg); |
|
|
|
|
#endif |
|
|
|
|
closelogfile(); |
|
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void conoutfv(int type, const char *fmt, va_list args) |
|
|
|
|
{ |
|
|
|
|
logoutfv(fmt, args); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#define DEFAULTCLIENTS 8 |
|
|
|
|
|
|
|
|
|
enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; |
|
|
|
@ -118,13 +80,11 @@ struct client // server side version of "dynent" type |
|
|
|
|
|
|
|
|
|
vector<client *> clients; |
|
|
|
|
|
|
|
|
|
ENetHost *serverhost = NULL; |
|
|
|
|
int laststatus = 0; |
|
|
|
|
ENetSocket lansock = ENET_SOCKET_NULL; |
|
|
|
|
|
|
|
|
|
int localclients = 0, nonlocalclients = 0; |
|
|
|
|
int localclients = 0; |
|
|
|
|
|
|
|
|
|
bool hasnonlocalclients() { return nonlocalclients!=0; } |
|
|
|
|
bool hasnonlocalclients() { return false; } |
|
|
|
|
bool haslocalclients() { return localclients!=0; } |
|
|
|
|
|
|
|
|
|
client &addclient(int type) |
|
|
|
@ -145,7 +105,6 @@ client &addclient(int type) |
|
|
|
|
c->type = type; |
|
|
|
|
switch(type) |
|
|
|
|
{ |
|
|
|
|
case ST_TCPIP: nonlocalclients++; break; |
|
|
|
|
case ST_LOCAL: localclients++; break; |
|
|
|
|
} |
|
|
|
|
return *c; |
|
|
|
@ -156,7 +115,6 @@ void delclient(client *c) |
|
|
|
|
if(!c) return; |
|
|
|
|
switch(c->type) |
|
|
|
|
{ |
|
|
|
|
case ST_TCPIP: nonlocalclients--; if(c->peer) c->peer->data = NULL; break; |
|
|
|
|
case ST_LOCAL: localclients--; break; |
|
|
|
|
case ST_EMPTY: return; |
|
|
|
|
} |
|
|
|
@ -171,24 +129,16 @@ void delclient(client *c) |
|
|
|
|
|
|
|
|
|
void cleanupserver() |
|
|
|
|
{ |
|
|
|
|
if(serverhost) enet_host_destroy(serverhost); |
|
|
|
|
serverhost = NULL; |
|
|
|
|
|
|
|
|
|
if(lansock != ENET_SOCKET_NULL) enet_socket_destroy(lansock); |
|
|
|
|
lansock = ENET_SOCKET_NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VARF(maxclients, 0, DEFAULTCLIENTS, MAXCLIENTS, { if(!maxclients) maxclients = DEFAULTCLIENTS; }); |
|
|
|
|
VARF(maxdupclients, 0, 0, MAXCLIENTS, { if(serverhost) serverhost->duplicatePeers = maxdupclients ? maxdupclients : MAXCLIENTS; }); |
|
|
|
|
|
|
|
|
|
void process(ENetPacket *packet, int sender, int chan); |
|
|
|
|
//void disconnect_client(int n, int reason);
|
|
|
|
|
|
|
|
|
|
int getservermtu() { return serverhost ? serverhost->mtu : -1; } |
|
|
|
|
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 clients.inrange(i) && clients[i]->type==ST_TCPIP ? clients[i]->peer : NULL; } |
|
|
|
|
ENetPeer *getclientpeer(int i) { return NULL; } |
|
|
|
|
int getnumclients() { return clients.length(); } |
|
|
|
|
uint getclientip(int n) { return clients.inrange(n) && clients[n]->type==ST_TCPIP ? clients[n]->peer->address.host : 0; } |
|
|
|
|
uint getclientip(int n) { return 0; } |
|
|
|
|
|
|
|
|
|
void sendpacket(int n, int chan, ENetPacket *packet, int exclude) |
|
|
|
|
{ |
|
|
|
@ -199,17 +149,9 @@ void sendpacket(int n, int chan, ENetPacket *packet, int exclude) |
|
|
|
|
} |
|
|
|
|
switch(clients[n]->type) |
|
|
|
|
{ |
|
|
|
|
case ST_TCPIP: |
|
|
|
|
{ |
|
|
|
|
enet_peer_send(clients[n]->peer, chan, packet); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifndef STANDALONE |
|
|
|
|
case ST_LOCAL: |
|
|
|
|
localservertoclient(chan, packet); |
|
|
|
|
break; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -261,46 +203,6 @@ ENetPacket *sendf(int cn, int chan, const char *format, ...) |
|
|
|
|
return packet->referenceCount > 0 ? packet : NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ENetPacket *sendfile(int cn, int chan, stream *file, const char *format, ...) |
|
|
|
|
{ |
|
|
|
|
if(cn < 0) |
|
|
|
|
{ |
|
|
|
|
#ifdef STANDALONE |
|
|
|
|
return NULL; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
else if(!clients.inrange(cn)) return NULL; |
|
|
|
|
|
|
|
|
|
int len = (int)min(file->size(), stream::offset(INT_MAX)); |
|
|
|
|
if(len <= 0 || len > 16<<20) return NULL; |
|
|
|
|
|
|
|
|
|
packetbuf p(MAXTRANS+len, ENET_PACKET_FLAG_RELIABLE); |
|
|
|
|
va_list args; |
|
|
|
|
va_start(args, format); |
|
|
|
|
while(*format) switch(*format++) |
|
|
|
|
{ |
|
|
|
|
case 'i': |
|
|
|
|
{ |
|
|
|
|
int n = isdigit(*format) ? *format++-'0' : 1; |
|
|
|
|
loopi(n) putint(p, va_arg(args, int)); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case 's': sendstring(va_arg(args, const char *), p); break; |
|
|
|
|
case 'l': putint(p, len); break; |
|
|
|
|
} |
|
|
|
|
va_end(args); |
|
|
|
|
|
|
|
|
|
file->seek(0, SEEK_SET); |
|
|
|
|
file->read(p.subbuf(len).buf, len); |
|
|
|
|
|
|
|
|
|
ENetPacket *packet = p.finalize(); |
|
|
|
|
if(cn >= 0) sendpacket(cn, chan, packet, -1); |
|
|
|
|
#ifndef STANDALONE |
|
|
|
|
else sendclientpacket(packet, chan); |
|
|
|
|
#endif |
|
|
|
|
return packet->referenceCount > 0 ? packet : NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const char *disconnectreason(int reason) |
|
|
|
|
{ |
|
|
|
|
switch(reason) |
|
|
|
@ -321,21 +223,10 @@ const char *disconnectreason(int reason) |
|
|
|
|
|
|
|
|
|
void disconnect_client(int n, int reason) |
|
|
|
|
{ |
|
|
|
|
if(!clients.inrange(n) || clients[n]->type!=ST_TCPIP) return; |
|
|
|
|
enet_peer_disconnect(clients[n]->peer, reason); |
|
|
|
|
server::clientdisconnect(n); |
|
|
|
|
delclient(clients[n]); |
|
|
|
|
const char *msg = disconnectreason(reason); |
|
|
|
|
string s; |
|
|
|
|
if(msg) formatstring(s, "client (%s) disconnected because: %s", clients[n]->hostname, msg); |
|
|
|
|
else formatstring(s, "client (%s) disconnected", clients[n]->hostname); |
|
|
|
|
logoutf("%s", s); |
|
|
|
|
server::sendservmsg(s); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void kicknonlocalclients(int reason) |
|
|
|
|
{ |
|
|
|
|
loopv(clients) if(clients[i]->type==ST_TCPIP) disconnect_client(i, reason); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void process(ENetPacket *packet, int sender, int chan) // sender may be -1
|
|
|
|
@ -352,163 +243,10 @@ void localclienttoserver(int chan, ENetPacket *packet) |
|
|
|
|
if(c) process(packet, c->num, chan); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static ENetAddress serverinfoaddress; |
|
|
|
|
|
|
|
|
|
void sendserverinforeply(ucharbuf &p) |
|
|
|
|
{ |
|
|
|
|
ENetBuffer buf; |
|
|
|
|
buf.data = p.buf; |
|
|
|
|
buf.dataLength = p.length(); |
|
|
|
|
enet_socket_send(serverhost->socket, &serverinfoaddress, &buf, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#define MAXPINGDATA 32 |
|
|
|
|
|
|
|
|
|
void checkserversockets() // reply all server info requests
|
|
|
|
|
{ |
|
|
|
|
static ENetSocketSet readset, writeset; |
|
|
|
|
ENET_SOCKETSET_EMPTY(readset); |
|
|
|
|
ENET_SOCKETSET_EMPTY(writeset); |
|
|
|
|
ENetSocket maxsock = ENET_SOCKET_NULL; |
|
|
|
|
if(lansock != ENET_SOCKET_NULL) |
|
|
|
|
{ |
|
|
|
|
maxsock = maxsock == ENET_SOCKET_NULL ? lansock : max(maxsock, lansock); |
|
|
|
|
ENET_SOCKETSET_ADD(readset, lansock); |
|
|
|
|
} |
|
|
|
|
if(maxsock == ENET_SOCKET_NULL || enet_socketset_select(maxsock, &readset, &writeset, 0) <= 0) return; |
|
|
|
|
|
|
|
|
|
if(lansock != ENET_SOCKET_NULL && ENET_SOCKETSET_CHECK(readset, lansock)) |
|
|
|
|
{ |
|
|
|
|
ENetBuffer buf; |
|
|
|
|
uchar data[MAXTRANS]; |
|
|
|
|
buf.data = data; |
|
|
|
|
buf.dataLength = sizeof(data); |
|
|
|
|
int len = enet_socket_receive(lansock, &serverinfoaddress, &buf, 1); |
|
|
|
|
if(len < 2 || data[0] != 0xFF || data[1] != 0xFF || len-2 > MAXPINGDATA) return; |
|
|
|
|
ucharbuf req(data+2, len-2), p(data+2, sizeof(data)-2); |
|
|
|
|
p.len += len-2; |
|
|
|
|
server::serverinforeply(req, p); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int serverinfointercept(ENetHost *host, ENetEvent *event) |
|
|
|
|
{ |
|
|
|
|
if(host->receivedDataLength < 2 || host->receivedData[0] != 0xFF || host->receivedData[1] != 0xFF || host->receivedDataLength-2 > MAXPINGDATA) return 0; |
|
|
|
|
serverinfoaddress = host->receivedAddress; |
|
|
|
|
ucharbuf req(host->receivedData+2, host->receivedDataLength-2), p(host->receivedData+2, sizeof(host->packetData[0])-2); |
|
|
|
|
p.len += host->receivedDataLength-2; |
|
|
|
|
server::serverinforeply(req, p); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VAR(serveruprate, 0, 0, INT_MAX); |
|
|
|
|
SVAR(serverip, ""); |
|
|
|
|
VARF(serverport, 0, server::serverport(), 0xFFFF, { if(!serverport) serverport = server::serverport(); }); |
|
|
|
|
|
|
|
|
|
#ifdef STANDALONE |
|
|
|
|
int curtime = 0, lastmillis = 0, elapsedtime = 0, totalmillis = 0; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
uint totalsecs = 0; |
|
|
|
|
|
|
|
|
|
void updatetime() |
|
|
|
|
{ |
|
|
|
|
static int lastsec = 0; |
|
|
|
|
if(totalmillis - lastsec >= 1000) |
|
|
|
|
{ |
|
|
|
|
int cursecs = (totalmillis - lastsec) / 1000; |
|
|
|
|
totalsecs += cursecs; |
|
|
|
|
lastsec += cursecs * 1000; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void serverslice(bool dedicated, uint timeout) // main server update, called from main loop in sp, or from below in dedicated server
|
|
|
|
|
{ |
|
|
|
|
if(!serverhost) |
|
|
|
|
{ |
|
|
|
|
server::serverupdate(); |
|
|
|
|
server::sendpackets(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// below is network only
|
|
|
|
|
|
|
|
|
|
if(dedicated) |
|
|
|
|
{ |
|
|
|
|
int millis = (int)enet_time_get(); |
|
|
|
|
elapsedtime = millis - totalmillis; |
|
|
|
|
static int timeerr = 0; |
|
|
|
|
int scaledtime = server::scaletime(elapsedtime) + timeerr; |
|
|
|
|
curtime = scaledtime/100; |
|
|
|
|
timeerr = scaledtime%100; |
|
|
|
|
if(server::ispaused()) curtime = 0; |
|
|
|
|
lastmillis += curtime; |
|
|
|
|
totalmillis = millis; |
|
|
|
|
updatetime(); |
|
|
|
|
} |
|
|
|
|
server::serverupdate(); |
|
|
|
|
|
|
|
|
|
checkserversockets(); |
|
|
|
|
|
|
|
|
|
if(totalmillis-laststatus>60*1000) // display bandwidth stats, useful for server ops
|
|
|
|
|
{ |
|
|
|
|
laststatus = totalmillis; |
|
|
|
|
if(nonlocalclients || serverhost->totalSentData || serverhost->totalReceivedData) logoutf("status: %d remote clients, %.1f send, %.1f rec (K/sec)", nonlocalclients, serverhost->totalSentData/60.0f/1024, serverhost->totalReceivedData/60.0f/1024); |
|
|
|
|
serverhost->totalSentData = serverhost->totalReceivedData = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ENetEvent event; |
|
|
|
|
bool serviced = false; |
|
|
|
|
while(!serviced) |
|
|
|
|
{ |
|
|
|
|
if(enet_host_check_events(serverhost, &event) <= 0) |
|
|
|
|
{ |
|
|
|
|
if(enet_host_service(serverhost, &event, timeout) <= 0) break; |
|
|
|
|
serviced = true; |
|
|
|
|
} |
|
|
|
|
switch(event.type) |
|
|
|
|
{ |
|
|
|
|
case ENET_EVENT_TYPE_CONNECT: |
|
|
|
|
{ |
|
|
|
|
client &c = addclient(ST_TCPIP); |
|
|
|
|
c.peer = event.peer; |
|
|
|
|
c.peer->data = &c; |
|
|
|
|
string hn; |
|
|
|
|
copystring(c.hostname, (enet_address_get_host_ip(&c.peer->address, hn, sizeof(hn))==0) ? hn : "unknown"); |
|
|
|
|
logoutf("client connected (%s)", c.hostname); |
|
|
|
|
int reason = server::clientconnect(c.num, c.peer->address.host); |
|
|
|
|
if(reason) disconnect_client(c.num, reason); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case ENET_EVENT_TYPE_RECEIVE: |
|
|
|
|
{ |
|
|
|
|
client *c = (client *)event.peer->data; |
|
|
|
|
if(c) process(event.packet, c->num, event.channelID); |
|
|
|
|
if(event.packet->referenceCount==0) enet_packet_destroy(event.packet); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT: |
|
|
|
|
{ |
|
|
|
|
client *c = (client *)event.peer->data; |
|
|
|
|
if(!c) break; |
|
|
|
|
logoutf("disconnected client (%s)", c->hostname); |
|
|
|
|
server::clientdisconnect(c->num); |
|
|
|
|
delclient(c); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
default: |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if(server::sendpackets()) enet_host_flush(serverhost); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void flushserver(bool force) |
|
|
|
|
{ |
|
|
|
|
if(server::sendpackets(force) && serverhost) enet_host_flush(serverhost); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifndef STANDALONE |
|
|
|
|
void localdisconnect(bool cleanup) |
|
|
|
|
{ |
|
|
|
|
bool disconnected = false; |
|
|
|
@ -531,272 +269,6 @@ void localconnect() |
|
|
|
|
game::gameconnect(false); |
|
|
|
|
server::localconnect(c.num); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#ifdef WIN32 |
|
|
|
|
#include "shellapi.h" |
|
|
|
|
|
|
|
|
|
#define IDI_ICON1 1 |
|
|
|
|
|
|
|
|
|
static string apptip = ""; |
|
|
|
|
static HINSTANCE appinstance = NULL; |
|
|
|
|
static ATOM wndclass = 0; |
|
|
|
|
static HWND appwindow = NULL, conwindow = NULL; |
|
|
|
|
static HICON appicon = NULL; |
|
|
|
|
static HMENU appmenu = NULL; |
|
|
|
|
static HANDLE outhandle = NULL; |
|
|
|
|
static const int MAXLOGLINES = 200; |
|
|
|
|
struct logline { int len; char buf[LOGSTRLEN]; }; |
|
|
|
|
static queue<logline, MAXLOGLINES> loglines; |
|
|
|
|
|
|
|
|
|
static void cleanupsystemtray() |
|
|
|
|
{ |
|
|
|
|
NOTIFYICONDATA nid; |
|
|
|
|
memset(&nid, 0, sizeof(nid)); |
|
|
|
|
nid.cbSize = sizeof(nid); |
|
|
|
|
nid.hWnd = appwindow; |
|
|
|
|
nid.uID = IDI_ICON1; |
|
|
|
|
Shell_NotifyIcon(NIM_DELETE, &nid); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool setupsystemtray(UINT uCallbackMessage) |
|
|
|
|
{ |
|
|
|
|
NOTIFYICONDATA nid; |
|
|
|
|
memset(&nid, 0, sizeof(nid)); |
|
|
|
|
nid.cbSize = sizeof(nid); |
|
|
|
|
nid.hWnd = appwindow; |
|
|
|
|
nid.uID = IDI_ICON1; |
|
|
|
|
nid.uCallbackMessage = uCallbackMessage; |
|
|
|
|
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; |
|
|
|
|
nid.hIcon = appicon; |
|
|
|
|
strcpy(nid.szTip, apptip); |
|
|
|
|
if(Shell_NotifyIcon(NIM_ADD, &nid) != TRUE) |
|
|
|
|
return false; |
|
|
|
|
atexit(cleanupsystemtray); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if 0 |
|
|
|
|
static bool modifysystemtray() |
|
|
|
|
{ |
|
|
|
|
NOTIFYICONDATA nid; |
|
|
|
|
memset(&nid, 0, sizeof(nid)); |
|
|
|
|
nid.cbSize = sizeof(nid); |
|
|
|
|
nid.hWnd = appwindow; |
|
|
|
|
nid.uID = IDI_ICON1; |
|
|
|
|
nid.uFlags = NIF_TIP; |
|
|
|
|
strcpy(nid.szTip, apptip); |
|
|
|
|
return Shell_NotifyIcon(NIM_MODIFY, &nid) == TRUE; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static void cleanupwindow() |
|
|
|
|
{ |
|
|
|
|
if(!appinstance) return; |
|
|
|
|
if(appmenu) |
|
|
|
|
{ |
|
|
|
|
DestroyMenu(appmenu); |
|
|
|
|
appmenu = NULL; |
|
|
|
|
} |
|
|
|
|
if(wndclass) |
|
|
|
|
{ |
|
|
|
|
UnregisterClass(MAKEINTATOM(wndclass), appinstance); |
|
|
|
|
wndclass = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static BOOL WINAPI consolehandler(DWORD dwCtrlType) |
|
|
|
|
{ |
|
|
|
|
switch(dwCtrlType) |
|
|
|
|
{ |
|
|
|
|
case CTRL_C_EVENT: |
|
|
|
|
case CTRL_BREAK_EVENT: |
|
|
|
|
case CTRL_CLOSE_EVENT: |
|
|
|
|
exit(EXIT_SUCCESS); |
|
|
|
|
return TRUE; |
|
|
|
|
} |
|
|
|
|
return FALSE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void writeline(logline &line) |
|
|
|
|
{ |
|
|
|
|
static uchar ubuf[512]; |
|
|
|
|
size_t len = strlen(line.buf), carry = 0; |
|
|
|
|
while(carry < len) |
|
|
|
|
{ |
|
|
|
|
size_t numu = encodeutf8(ubuf, sizeof(ubuf), &((uchar *)line.buf)[carry], len - carry, &carry); |
|
|
|
|
DWORD written = 0; |
|
|
|
|
WriteConsole(outhandle, ubuf, numu, &written, NULL); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void setupconsole() |
|
|
|
|
{ |
|
|
|
|
if(conwindow) return; |
|
|
|
|
if(!AllocConsole()) return; |
|
|
|
|
SetConsoleCtrlHandler(consolehandler, TRUE); |
|
|
|
|
conwindow = GetConsoleWindow(); |
|
|
|
|
SetConsoleTitle(apptip); |
|
|
|
|
//SendMessage(conwindow, WM_SETICON, ICON_SMALL, (LPARAM)appicon);
|
|
|
|
|
SendMessage(conwindow, WM_SETICON, ICON_BIG, (LPARAM)appicon); |
|
|
|
|
outhandle = GetStdHandle(STD_OUTPUT_HANDLE); |
|
|
|
|
CONSOLE_SCREEN_BUFFER_INFO coninfo; |
|
|
|
|
GetConsoleScreenBufferInfo(outhandle, &coninfo); |
|
|
|
|
coninfo.dwSize.Y = MAXLOGLINES; |
|
|
|
|
SetConsoleScreenBufferSize(outhandle, coninfo.dwSize); |
|
|
|
|
SetConsoleCP(CP_UTF8); |
|
|
|
|
SetConsoleOutputCP(CP_UTF8); |
|
|
|
|
loopv(loglines) writeline(loglines[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{ |
|
|
|
|
MENU_OPENCONSOLE = 0, |
|
|
|
|
MENU_SHOWCONSOLE, |
|
|
|
|
MENU_HIDECONSOLE, |
|
|
|
|
MENU_EXIT |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static LRESULT CALLBACK handlemessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
|
|
|
|
{ |
|
|
|
|
switch(uMsg) |
|
|
|
|
{ |
|
|
|
|
case WM_APP: |
|
|
|
|
SetForegroundWindow(hWnd); |
|
|
|
|
switch(lParam) |
|
|
|
|
{ |
|
|
|
|
//case WM_MOUSEMOVE:
|
|
|
|
|
// break;
|
|
|
|
|
case WM_LBUTTONUP: |
|
|
|
|
case WM_RBUTTONUP: |
|
|
|
|
{ |
|
|
|
|
POINT pos; |
|
|
|
|
GetCursorPos(&pos); |
|
|
|
|
TrackPopupMenu(appmenu, TPM_CENTERALIGN|TPM_BOTTOMALIGN|TPM_RIGHTBUTTON, pos.x, pos.y, 0, hWnd, NULL); |
|
|
|
|
PostMessage(hWnd, WM_NULL, 0, 0); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
case WM_COMMAND: |
|
|
|
|
switch(LOWORD(wParam)) |
|
|
|
|
{ |
|
|
|
|
case MENU_OPENCONSOLE: |
|
|
|
|
setupconsole(); |
|
|
|
|
if(conwindow) ModifyMenu(appmenu, 0, MF_BYPOSITION|MF_STRING, MENU_HIDECONSOLE, "Hide Console"); |
|
|
|
|
break; |
|
|
|
|
case MENU_SHOWCONSOLE: |
|
|
|
|
ShowWindow(conwindow, SW_SHOWNORMAL); |
|
|
|
|
ModifyMenu(appmenu, 0, MF_BYPOSITION|MF_STRING, MENU_HIDECONSOLE, "Hide Console"); |
|
|
|
|
break; |
|
|
|
|
case MENU_HIDECONSOLE: |
|
|
|
|
ShowWindow(conwindow, SW_HIDE); |
|
|
|
|
ModifyMenu(appmenu, 0, MF_BYPOSITION|MF_STRING, MENU_SHOWCONSOLE, "Show Console"); |
|
|
|
|
break; |
|
|
|
|
case MENU_EXIT: |
|
|
|
|
PostMessage(hWnd, WM_CLOSE, 0, 0); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
case WM_CLOSE: |
|
|
|
|
PostQuitMessage(0); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void setupwindow(const char *title) |
|
|
|
|
{ |
|
|
|
|
copystring(apptip, title); |
|
|
|
|
//appinstance = GetModuleHandle(NULL);
|
|
|
|
|
if(!appinstance) fatal("failed getting application instance"); |
|
|
|
|
appicon = LoadIcon(appinstance, MAKEINTRESOURCE(IDI_ICON1));//(HICON)LoadImage(appinstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
|
|
|
|
|
if(!appicon) fatal("failed loading icon"); |
|
|
|
|
|
|
|
|
|
appmenu = CreatePopupMenu(); |
|
|
|
|
if(!appmenu) fatal("failed creating popup menu"); |
|
|
|
|
AppendMenu(appmenu, MF_STRING, MENU_OPENCONSOLE, "Open Console"); |
|
|
|
|
AppendMenu(appmenu, MF_SEPARATOR, 0, NULL); |
|
|
|
|
AppendMenu(appmenu, MF_STRING, MENU_EXIT, "Exit"); |
|
|
|
|
//SetMenuDefaultItem(appmenu, 0, FALSE);
|
|
|
|
|
|
|
|
|
|
WNDCLASS wc; |
|
|
|
|
memset(&wc, 0, sizeof(wc)); |
|
|
|
|
wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW);
|
|
|
|
|
wc.hIcon = appicon; |
|
|
|
|
wc.lpszMenuName = NULL; |
|
|
|
|
wc.lpszClassName = title; |
|
|
|
|
wc.style = 0; |
|
|
|
|
wc.hInstance = appinstance; |
|
|
|
|
wc.lpfnWndProc = handlemessages; |
|
|
|
|
wc.cbWndExtra = 0; |
|
|
|
|
wc.cbClsExtra = 0; |
|
|
|
|
wndclass = RegisterClass(&wc); |
|
|
|
|
if(!wndclass) fatal("failed registering window class"); |
|
|
|
|
|
|
|
|
|
appwindow = CreateWindow(MAKEINTATOM(wndclass), title, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_MESSAGE, NULL, appinstance, NULL); |
|
|
|
|
if(!appwindow) fatal("failed creating window"); |
|
|
|
|
|
|
|
|
|
atexit(cleanupwindow); |
|
|
|
|
|
|
|
|
|
if(!setupsystemtray(WM_APP)) fatal("failed adding to system tray"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static char *parsecommandline(const char *src, vector<char *> &args) |
|
|
|
|
{ |
|
|
|
|
char *buf = new char[strlen(src) + 1], *dst = buf; |
|
|
|
|
for(;;) |
|
|
|
|
{ |
|
|
|
|
while(isspace(*src)) src++; |
|
|
|
|
if(!*src) break; |
|
|
|
|
args.add(dst); |
|
|
|
|
for(bool quoted = false; *src && (quoted || !isspace(*src)); src++) |
|
|
|
|
{ |
|
|
|
|
if(*src != '"') *dst++ = *src; |
|
|
|
|
else if(dst > buf && src[-1] == '\\') dst[-1] = '"'; |
|
|
|
|
else quoted = !quoted; |
|
|
|
|
} |
|
|
|
|
*dst++ = '\0'; |
|
|
|
|
} |
|
|
|
|
args.add(NULL); |
|
|
|
|
return buf; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) |
|
|
|
|
{ |
|
|
|
|
vector<char *> args; |
|
|
|
|
char *buf = parsecommandline(GetCommandLine(), args); |
|
|
|
|
appinstance = hInst; |
|
|
|
|
#ifdef STANDALONE |
|
|
|
|
int standalonemain(int argc, char **argv); |
|
|
|
|
int status = standalonemain(args.length()-1, args.getbuf()); |
|
|
|
|
#define main standalonemain |
|
|
|
|
#else |
|
|
|
|
SDL_SetMainReady(); |
|
|
|
|
int status = SDL_main(args.length()-1, args.getbuf()); |
|
|
|
|
#endif |
|
|
|
|
delete[] buf; |
|
|
|
|
exit(status); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void logoutfv(const char *fmt, va_list args) |
|
|
|
|
{ |
|
|
|
|
if(appwindow) |
|
|
|
|
{ |
|
|
|
|
logline &line = loglines.add(); |
|
|
|
|
vformatstring(line.buf, fmt, args, sizeof(line.buf)); |
|
|
|
|
if(logfile) writelog(logfile, line.buf); |
|
|
|
|
line.len = min(strlen(line.buf), sizeof(line.buf)-2); |
|
|
|
|
line.buf[line.len++] = '\n'; |
|
|
|
|
line.buf[line.len] = '\0'; |
|
|
|
|
if(outhandle) writeline(line); |
|
|
|
|
} |
|
|
|
|
else if(logfile) writelogv(logfile, fmt, args); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
|
|
void logoutfv(const char *fmt, va_list args) |
|
|
|
|
{ |
|
|
|
@ -804,144 +276,7 @@ void logoutfv(const char *fmt, va_list args) |
|
|
|
|
if(f) writelogv(f, fmt, args); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static bool dedicatedserver = false; |
|
|
|
|
|
|
|
|
|
bool isdedicatedserver() { return dedicatedserver; } |
|
|
|
|
|
|
|
|
|
void rundedicatedserver() |
|
|
|
|
{ |
|
|
|
|
dedicatedserver = true; |
|
|
|
|
logoutf("dedicated server started, waiting for clients..."); |
|
|
|
|
#ifdef WIN32 |
|
|
|
|
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); |
|
|
|
|
for(;;) |
|
|
|
|
{ |
|
|
|
|
MSG msg; |
|
|
|
|
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) |
|
|
|
|
{ |
|
|
|
|
if(msg.message == WM_QUIT) exit(EXIT_SUCCESS); |
|
|
|
|
TranslateMessage(&msg); |
|
|
|
|
DispatchMessage(&msg); |
|
|
|
|
} |
|
|
|
|
serverslice(true, 5); |
|
|
|
|
} |
|
|
|
|
#else |
|
|
|
|
for(;;) serverslice(true, 5); |
|
|
|
|
#endif |
|
|
|
|
dedicatedserver = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool servererror(bool dedicated, const char *desc) |
|
|
|
|
void initserver() |
|
|
|
|
{ |
|
|
|
|
#ifndef STANDALONE |
|
|
|
|
if(!dedicated) |
|
|
|
|
{ |
|
|
|
|
conoutf(CON_ERROR, "%s", desc); |
|
|
|
|
cleanupserver(); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
#endif |
|
|
|
|
fatal("%s", desc); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool setuplistenserver(bool dedicated) |
|
|
|
|
{ |
|
|
|
|
ENetAddress address = { ENET_HOST_ANY, enet_uint16(serverport <= 0 ? server::serverport() : serverport) }; |
|
|
|
|
if(*serverip) |
|
|
|
|
{ |
|
|
|
|
if(enet_address_set_host(&address, serverip)<0) conoutf(CON_WARN, "WARNING: server ip not resolved"); |
|
|
|
|
} |
|
|
|
|
serverhost = enet_host_create(&address, min(maxclients + server::reserveclients(), MAXCLIENTS), server::numchannels(), 0, serveruprate); |
|
|
|
|
if(!serverhost) return servererror(dedicated, "could not create server host"); |
|
|
|
|
serverhost->duplicatePeers = maxdupclients ? maxdupclients : MAXCLIENTS; |
|
|
|
|
serverhost->intercept = serverinfointercept; |
|
|
|
|
address.port = server::laninfoport(); |
|
|
|
|
lansock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM); |
|
|
|
|
if(lansock != ENET_SOCKET_NULL && (enet_socket_set_option(lansock, ENET_SOCKOPT_REUSEADDR, 1) < 0 || enet_socket_bind(lansock, &address) < 0)) |
|
|
|
|
{ |
|
|
|
|
enet_socket_destroy(lansock); |
|
|
|
|
lansock = ENET_SOCKET_NULL; |
|
|
|
|
} |
|
|
|
|
if(lansock == ENET_SOCKET_NULL) conoutf(CON_WARN, "WARNING: could not create LAN server info socket"); |
|
|
|
|
else enet_socket_set_option(lansock, ENET_SOCKOPT_NONBLOCK, 1); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void initserver(bool listen, bool dedicated) |
|
|
|
|
{ |
|
|
|
|
if(dedicated) |
|
|
|
|
{ |
|
|
|
|
#ifdef WIN32 |
|
|
|
|
setupwindow("Tesseract server"); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
execfile("config/server-init.cfg", false); |
|
|
|
|
|
|
|
|
|
if(listen) setuplistenserver(dedicated); |
|
|
|
|
|
|
|
|
|
server::serverinit(); |
|
|
|
|
|
|
|
|
|
if(listen) |
|
|
|
|
{ |
|
|
|
|
dedicatedserver = dedicated; |
|
|
|
|
if(dedicated) rundedicatedserver(); // never returns
|
|
|
|
|
#ifndef STANDALONE |
|
|
|
|
else conoutf("listen server started"); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifndef STANDALONE |
|
|
|
|
void startlistenserver(int *usemaster) |
|
|
|
|
{ |
|
|
|
|
if(serverhost) { conoutf(CON_ERROR, "listen server is already running"); return; } |
|
|
|
|
|
|
|
|
|
if(!setuplistenserver(false)) return; |
|
|
|
|
} |
|
|
|
|
COMMAND(startlistenserver, "i"); |
|
|
|
|
|
|
|
|
|
void stoplistenserver() |
|
|
|
|
{ |
|
|
|
|
if(!serverhost) { conoutf(CON_ERROR, "listen server is not running"); return; } |
|
|
|
|
|
|
|
|
|
kicknonlocalclients(); |
|
|
|
|
enet_host_flush(serverhost); |
|
|
|
|
cleanupserver(); |
|
|
|
|
|
|
|
|
|
conoutf("listen server stopped"); |
|
|
|
|
} |
|
|
|
|
COMMAND(stoplistenserver, ""); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
bool serveroption(char *opt) |
|
|
|
|
{ |
|
|
|
|
switch(opt[1]) |
|
|
|
|
{ |
|
|
|
|
#ifdef STANDALONE |
|
|
|
|
case 'u': logoutf("Using home directory: %s", opt); sethomedir(opt+2); return true; |
|
|
|
|
case 'k': logoutf("Adding package directory: %s", opt); addpackagedir(opt+2); return true; |
|
|
|
|
case 'g': logoutf("Setting log file: %s", opt); setlogfile(opt+2); return true; |
|
|
|
|
#endif |
|
|
|
|
default: return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
vector<const char *> gameargs; |
|
|
|
|
|
|
|
|
|
#ifdef STANDALONE |
|
|
|
|
int main(int argc, char **argv) |
|
|
|
|
{ |
|
|
|
|
setlogfile(NULL); |
|
|
|
|
if(enet_initialize()<0) fatal("Unable to initialise network module"); |
|
|
|
|
atexit(enet_deinitialize); |
|
|
|
|
enet_time_set(0); |
|
|
|
|
for(int i = 1; i<argc; i++) if(argv[i][0]!='-' || !serveroption(argv[i])) gameargs.add(argv[i]); |
|
|
|
|
game::parseoptions(gameargs); |
|
|
|
|
initserver(true, true); |
|
|
|
|
return EXIT_SUCCESS; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|