drop remaining network functionality
This commit is contained in:
parent
e6a11d01f6
commit
1a8a0e10ac
|
@ -1,82 +0,0 @@
|
|||
// basic server configuration script
|
||||
// note that some commands are commented out with //, and need to be uncommented and set to a value to work
|
||||
|
||||
// optional specific IP for server to use
|
||||
// serverip N.N.N.N
|
||||
|
||||
// optional specific port for server to use
|
||||
// serverport 42000
|
||||
|
||||
// maximum number of allowed clients
|
||||
maxclients 8
|
||||
|
||||
// maximum number of bots a master can add - admins are unaffected by this limit
|
||||
// set to 0 to disallow bots
|
||||
serverbotlimit 8
|
||||
|
||||
// controls whether or not the server is intended for "public" use
|
||||
// when set to 0, allows "setmaster 1" and locked/private mastermodes (for coop-editing and such)
|
||||
// when set to 1, can only gain master by "auth" or admin, and doesn't allow locked/private mastermodes
|
||||
// when set to 2, allows "setmaster 1" but disallows private mastermode (for public coop-editing)
|
||||
publicserver 0
|
||||
|
||||
// description server shows for the server browser
|
||||
serverdesc ""
|
||||
|
||||
// password required to connect to the server
|
||||
// generally unnecessary unless you really don't want anyone connecting to your server
|
||||
// using the serverauth option allows people with an appropriate local authkey to connect regardless
|
||||
// serverpass ""
|
||||
|
||||
// password that allows you to gain admin by "setmaster password_here"
|
||||
// local authkeys are more secure than this and should be used instead if possible
|
||||
// adminpass ""
|
||||
|
||||
// domain to use for local authkeys to the server so people can authenticate by "auth domain_here"
|
||||
// must be a non-empty string that should be unique to your server to prevent clashes with other servers
|
||||
// serverauth ""
|
||||
|
||||
// controls whether or not the server reports to the masterserver
|
||||
updatemaster 1
|
||||
|
||||
// optional message of the day to send to players on connect
|
||||
// servermotd "..."
|
||||
|
||||
// controls whether admin privs are necessary to record a demo
|
||||
// when 1 requires admin (default)
|
||||
// when 0 only requires master
|
||||
// restrictdemos 1
|
||||
|
||||
// maximum number of demos the server will store
|
||||
// maxdemos 5
|
||||
|
||||
// maximum size a demo is allowed to grow to in megabytes
|
||||
// maxdemosize 16
|
||||
|
||||
// controls whether admin privs are necessary to pause a game
|
||||
// when 1 requires admin (default)
|
||||
// when 0 only requires master
|
||||
// restrictpausegame 1
|
||||
|
||||
// whether or not to allow players to vote on maps not in the rotation
|
||||
// when 1 requires master
|
||||
// when 2 requires admin
|
||||
// when 0 allows any votes (default)
|
||||
// lockmaprotation 0
|
||||
|
||||
dmmaps = [
|
||||
complex alphacorp ot turbine reflection
|
||||
]
|
||||
ctfmaps = [
|
||||
alphacorp steelribs test_ctf reflection waterworks
|
||||
]
|
||||
tdmmaps = (listunion $dmmaps $ctfmaps)
|
||||
|
||||
maprotationreset
|
||||
maprotation "*" $dmmaps
|
||||
maprotation "?tdm" $tdmmaps
|
||||
maprotation "?ctf" $ctfmaps
|
||||
|
||||
teamkillkickreset
|
||||
teamkillkick "*" 7 30
|
||||
|
|
@ -2,258 +2,43 @@
|
|||
|
||||
#include "engine.hh"
|
||||
|
||||
ENetHost *clienthost = NULL;
|
||||
ENetPeer *curpeer = NULL, *connpeer = NULL;
|
||||
int connmillis = 0, connattempts = 0, discmillis = 0;
|
||||
|
||||
bool multiplayer(bool msg)
|
||||
{
|
||||
bool val = curpeer || hasnonlocalclients();
|
||||
if(val && msg) conoutf(CON_ERROR, "operation not available in multiplayer");
|
||||
return val;
|
||||
}
|
||||
|
||||
void setrate(int rate)
|
||||
{
|
||||
if(!curpeer) return;
|
||||
enet_host_bandwidth_limit(clienthost, rate*1024, rate*1024);
|
||||
}
|
||||
|
||||
VARF(rate, 0, 0, 1024, setrate(rate));
|
||||
|
||||
void throttle();
|
||||
|
||||
VARF(throttle_interval, 0, 5, 30, throttle());
|
||||
VARF(throttle_accel, 0, 2, 32, throttle());
|
||||
VARF(throttle_decel, 0, 2, 32, throttle());
|
||||
|
||||
void throttle()
|
||||
{
|
||||
if(!curpeer) return;
|
||||
ASSERT(ENET_PEER_PACKET_THROTTLE_SCALE==32);
|
||||
enet_peer_throttle_configure(curpeer, throttle_interval*1000, throttle_accel, throttle_decel);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isconnected(bool attempt, bool local)
|
||||
{
|
||||
return curpeer || (attempt && connpeer) || (local && haslocalclients());
|
||||
return haslocalclients();
|
||||
}
|
||||
|
||||
ICOMMAND(isconnected, "bb", (int *attempt, int *local), intret(isconnected(*attempt > 0, *local != 0) ? 1 : 0));
|
||||
|
||||
const ENetAddress *connectedpeer()
|
||||
void trydisconnect()
|
||||
{
|
||||
return curpeer ? &curpeer->address : NULL;
|
||||
}
|
||||
|
||||
ICOMMAND(connectedip, "", (),
|
||||
{
|
||||
const ENetAddress *address = connectedpeer();
|
||||
string hostname;
|
||||
result(address && enet_address_get_host_ip(address, hostname, sizeof(hostname)) >= 0 ? hostname : "");
|
||||
});
|
||||
|
||||
ICOMMAND(connectedport, "", (),
|
||||
{
|
||||
const ENetAddress *address = connectedpeer();
|
||||
intret(address ? address->port : -1);
|
||||
});
|
||||
|
||||
void abortconnect()
|
||||
{
|
||||
if(!connpeer) return;
|
||||
game::connectfail();
|
||||
if(connpeer->state!=ENET_PEER_STATE_DISCONNECTED) enet_peer_reset(connpeer);
|
||||
connpeer = NULL;
|
||||
if(curpeer) return;
|
||||
enet_host_destroy(clienthost);
|
||||
clienthost = NULL;
|
||||
}
|
||||
|
||||
SVARP(connectname, "");
|
||||
VARP(connectport, 0, 0, 0xFFFF);
|
||||
|
||||
void connectserv(const char *servername, int serverport, const char *serverpassword)
|
||||
{
|
||||
if(connpeer)
|
||||
{
|
||||
conoutf("aborting connection attempt");
|
||||
abortconnect();
|
||||
}
|
||||
|
||||
if(serverport <= 0) serverport = server::serverport();
|
||||
|
||||
ENetAddress address;
|
||||
address.port = serverport;
|
||||
|
||||
setsvar("connectname", "");
|
||||
setvar("connectport", 0);
|
||||
conoutf("attempting to connect over LAN");
|
||||
address.host = ENET_HOST_BROADCAST;
|
||||
|
||||
if(!clienthost)
|
||||
{
|
||||
clienthost = enet_host_create(NULL, 2, server::numchannels(), rate*1024, rate*1024);
|
||||
if(!clienthost)
|
||||
{
|
||||
conoutf("\f3could not connect to server");
|
||||
return;
|
||||
}
|
||||
clienthost->duplicatePeers = 0;
|
||||
}
|
||||
|
||||
connpeer = enet_host_connect(clienthost, &address, server::numchannels(), 0);
|
||||
enet_host_flush(clienthost);
|
||||
connmillis = totalmillis;
|
||||
connattempts = 0;
|
||||
|
||||
game::connectattempt(servername ? servername : "", serverpassword ? serverpassword : "", address);
|
||||
}
|
||||
|
||||
void reconnect(const char *serverpassword)
|
||||
{
|
||||
if(!connectname[0] || connectport <= 0)
|
||||
{
|
||||
conoutf(CON_ERROR, "no previous connection");
|
||||
return;
|
||||
}
|
||||
|
||||
connectserv(connectname, connectport, serverpassword);
|
||||
}
|
||||
|
||||
void disconnect(bool async, bool cleanup)
|
||||
{
|
||||
if(curpeer)
|
||||
{
|
||||
if(!discmillis)
|
||||
{
|
||||
enet_peer_disconnect(curpeer, DISC_NONE);
|
||||
enet_host_flush(clienthost);
|
||||
discmillis = totalmillis;
|
||||
}
|
||||
if(curpeer->state!=ENET_PEER_STATE_DISCONNECTED)
|
||||
{
|
||||
if(async) return;
|
||||
enet_peer_reset(curpeer);
|
||||
}
|
||||
curpeer = NULL;
|
||||
discmillis = 0;
|
||||
conoutf("disconnected");
|
||||
game::gamedisconnect(cleanup);
|
||||
mainmenu = 1;
|
||||
}
|
||||
if(!connpeer && clienthost)
|
||||
{
|
||||
enet_host_destroy(clienthost);
|
||||
clienthost = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void trydisconnect(bool local)
|
||||
{
|
||||
if(connpeer)
|
||||
{
|
||||
conoutf("aborting connection attempt");
|
||||
abortconnect();
|
||||
}
|
||||
else if(curpeer)
|
||||
{
|
||||
conoutf("attempting to disconnect...");
|
||||
disconnect(!discmillis);
|
||||
}
|
||||
else if(local && haslocalclients()) localdisconnect();
|
||||
if(haslocalclients()) localdisconnect();
|
||||
else conoutf("not connected");
|
||||
}
|
||||
|
||||
ICOMMAND(connect, "sis", (char *name, int *port, char *pw), connectserv(name, *port, pw));
|
||||
ICOMMAND(lanconnect, "is", (int *port, char *pw), connectserv(NULL, *port, pw));
|
||||
COMMAND(reconnect, "s");
|
||||
ICOMMAND(disconnect, "b", (int *local), trydisconnect(*local != 0));
|
||||
ICOMMAND(disconnect, "", (), trydisconnect());
|
||||
ICOMMAND(localconnect, "", (), { if(!isconnected()) localconnect(); });
|
||||
ICOMMAND(localdisconnect, "", (), { if(haslocalclients()) localdisconnect(); });
|
||||
|
||||
void sendclientpacket(ENetPacket *packet, int chan)
|
||||
{
|
||||
if(curpeer) enet_peer_send(curpeer, chan, packet);
|
||||
else localclienttoserver(chan, packet);
|
||||
}
|
||||
|
||||
void flushclient()
|
||||
{
|
||||
if(clienthost) enet_host_flush(clienthost);
|
||||
}
|
||||
|
||||
void neterr(const char *s, bool disc)
|
||||
{
|
||||
conoutf(CON_ERROR, "\f3illegal network message (%s)", s);
|
||||
if(disc) disconnect();
|
||||
}
|
||||
|
||||
void localservertoclient(int chan, ENetPacket *packet) // processes any updates from the server
|
||||
{
|
||||
packetbuf p(packet);
|
||||
game::parsepacketclient(chan, p);
|
||||
}
|
||||
|
||||
void clientkeepalive() { if(clienthost) enet_host_service(clienthost, NULL, 0); }
|
||||
|
||||
void gets2c() // get updates from the server
|
||||
void sendclientpacket(ENetPacket *packet, int chan)
|
||||
{
|
||||
ENetEvent event;
|
||||
if(!clienthost) return;
|
||||
if(connpeer && totalmillis/3000 > connmillis/3000)
|
||||
{
|
||||
conoutf("attempting to connect...");
|
||||
connmillis = totalmillis;
|
||||
++connattempts;
|
||||
if(connattempts > 3)
|
||||
{
|
||||
conoutf("\f3could not connect to server");
|
||||
abortconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
while(clienthost && enet_host_service(clienthost, &event, 0)>0)
|
||||
switch(event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
disconnect(false, false);
|
||||
localdisconnect(false);
|
||||
curpeer = connpeer;
|
||||
connpeer = NULL;
|
||||
conoutf("connected to server");
|
||||
throttle();
|
||||
if(rate) setrate(rate);
|
||||
game::gameconnect(true);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
if(discmillis) conoutf("attempting to disconnect...");
|
||||
else localservertoclient(event.channelID, event.packet);
|
||||
enet_packet_destroy(event.packet);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
if(event.data>=DISC_NUM) event.data = DISC_NONE;
|
||||
if(event.peer==connpeer)
|
||||
{
|
||||
conoutf("\f3could not connect to server");
|
||||
abortconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!discmillis || event.data)
|
||||
{
|
||||
const char *msg = disconnectreason(event.data);
|
||||
if(msg) conoutf("\f3server network error, disconnecting (%s) ...", msg);
|
||||
else conoutf("\f3server network error, disconnecting...");
|
||||
}
|
||||
disconnect();
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
localclienttoserver(chan, packet);
|
||||
}
|
||||
|
||||
void clientkeepalive() {}
|
||||
|
||||
void gets2c() {}
|
||||
|
||||
void neterr(const char *s, bool disc)
|
||||
{
|
||||
conoutf(CON_ERROR, "\f3illegal network message (%s)", s);
|
||||
if(disc) localdisconnect();
|
||||
}
|
||||
|
|
|
@ -526,22 +526,16 @@ extern void renderwaterfog(int mat, float blend);
|
|||
extern void preloadwatershaders(bool force = false);
|
||||
|
||||
// server
|
||||
extern vector<const char *> gameargs;
|
||||
|
||||
extern void initserver(bool listen, bool dedicated);
|
||||
extern void initserver();
|
||||
extern void cleanupserver();
|
||||
extern void serverslice(bool dedicated, uint timeout);
|
||||
extern void updatetime();
|
||||
|
||||
extern void localclienttoserver(int chan, ENetPacket *);
|
||||
extern void localconnect();
|
||||
extern bool serveroption(char *opt);
|
||||
|
||||
// client
|
||||
extern void localdisconnect(bool cleanup = true);
|
||||
extern void localservertoclient(int chan, ENetPacket *packet);
|
||||
extern void connectserv(const char *servername, int port, const char *serverpassword);
|
||||
extern void abortconnect();
|
||||
extern void clientkeepalive();
|
||||
|
||||
// command
|
||||
|
|
|
@ -32,8 +32,6 @@ extern void writeinitcfg();
|
|||
void quit() // normal exit
|
||||
{
|
||||
writeinitcfg();
|
||||
abortconnect();
|
||||
disconnect();
|
||||
localdisconnect();
|
||||
writecfg();
|
||||
cleanup();
|
||||
|
@ -1022,7 +1020,6 @@ int main(int argc, char **argv)
|
|||
|
||||
setlogfile(NULL);
|
||||
|
||||
int dedicated = 0;
|
||||
char *load = NULL, *initscript = NULL;
|
||||
|
||||
initing = INIT_RESET;
|
||||
|
@ -1048,7 +1045,6 @@ int main(int argc, char **argv)
|
|||
break;
|
||||
}
|
||||
case 'g': break;
|
||||
case 'd': dedicated = atoi(&argv[i][2]); if(dedicated<=0) dedicated = 2; break;
|
||||
case 'w': scr_w = clamp(atoi(&argv[i][2]), SCR_MINW, SCR_MAXW); if(!findarg(argc, argv, "-h")) scr_h = -1; break;
|
||||
case 'h': scr_h = clamp(atoi(&argv[i][2]), SCR_MINH, SCR_MAXH); if(!findarg(argc, argv, "-w")) scr_w = -1; break;
|
||||
case 'f': fullscreen = atoi(&argv[i][2]); break;
|
||||
|
@ -1061,29 +1057,18 @@ int main(int argc, char **argv)
|
|||
break;
|
||||
}
|
||||
case 'x': initscript = &argv[i][2]; break;
|
||||
default: if(!serveroption(argv[i])) gameargs.add(argv[i]); break;
|
||||
default: break;
|
||||
}
|
||||
else gameargs.add(argv[i]);
|
||||
}
|
||||
|
||||
numcpus = clamp(SDL_GetCPUCount(), 1, 16);
|
||||
|
||||
if(dedicated <= 1)
|
||||
{
|
||||
logoutf("init: sdl");
|
||||
logoutf("init: sdl");
|
||||
|
||||
if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0) fatal("Unable to initialize SDL: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
logoutf("init: net");
|
||||
if(enet_initialize()<0) fatal("Unable to initialise network module");
|
||||
atexit(enet_deinitialize);
|
||||
enet_time_set(0);
|
||||
if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0) fatal("Unable to initialize SDL: %s", SDL_GetError());
|
||||
|
||||
logoutf("init: game");
|
||||
game::parseoptions(gameargs);
|
||||
initserver(dedicated>0, dedicated>1); // never returns if dedicated
|
||||
ASSERT(dedicated <= 1);
|
||||
initserver();
|
||||
game::initclient();
|
||||
|
||||
logoutf("init: video");
|
||||
|
@ -1197,7 +1182,8 @@ int main(int argc, char **argv)
|
|||
|
||||
checksleep(lastmillis);
|
||||
|
||||
serverslice(false, 0);
|
||||
server::serverupdate();
|
||||
server::sendpackets();
|
||||
|
||||
if(frames) updatefpshistory(elapsedtime);
|
||||
frames++;
|
||||
|
|
|
@ -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()
|
||||
void initserver()
|
||||
{
|
||||
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)
|
||||
{
|
||||
#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
|
||||
|
|
|
@ -51,13 +51,7 @@ namespace game
|
|||
|
||||
bool allowedittoggle()
|
||||
{
|
||||
if(editmode) return true;
|
||||
if(isconnected() && multiplayer(false) && !m_edit)
|
||||
{
|
||||
conoutf(CON_ERROR, "editing in multiplayer requires edit mode");
|
||||
return false;
|
||||
}
|
||||
return execidentbool("allowedittoggle", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void edittoggled(bool on)
|
||||
|
@ -71,7 +65,7 @@ namespace game
|
|||
void changemapserv(const char *name, int mode) // forced map change from the server
|
||||
{
|
||||
if(editmode) toggleedit();
|
||||
if((m_edit && !name[0]) || !load_world(name))
|
||||
if(!name[0] || !load_world(name))
|
||||
{
|
||||
emptymap(0, true, name);
|
||||
}
|
||||
|
@ -195,7 +189,6 @@ namespace game
|
|||
player1->respawn();
|
||||
player1->lifesequence = 0;
|
||||
player1->state = CS_ALIVE;
|
||||
clearclients(false);
|
||||
if(cleanup)
|
||||
{
|
||||
clientmap[0] = '\0';
|
||||
|
@ -224,7 +217,6 @@ namespace game
|
|||
if(totalmillis - lastupdate < 40 && !force) return; // don't update faster than 30fps
|
||||
lastupdate = totalmillis;
|
||||
sendmessages();
|
||||
flushclient();
|
||||
}
|
||||
|
||||
void sendintro()
|
||||
|
@ -255,13 +247,7 @@ namespace game
|
|||
{
|
||||
case N_SERVINFO: // welcome messsage from the server
|
||||
{
|
||||
int mycn = getint(p), prot = getint(p);
|
||||
if(prot!=PROTOCOL_VERSION)
|
||||
{
|
||||
conoutf(CON_ERROR, "you are using a different game protocol (you: %d, server: %d)", PROTOCOL_VERSION, prot);
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
int mycn = getint(p);
|
||||
sessionid = getint(p);
|
||||
player1->clientnum = mycn; // we are now connected
|
||||
sendintro();
|
||||
|
@ -275,25 +261,14 @@ namespace game
|
|||
break;
|
||||
}
|
||||
|
||||
case N_CLIENT:
|
||||
{
|
||||
int cn = getint(p), len = getuint(p);
|
||||
ucharbuf q = p.subbuf(len);
|
||||
parsemessages(cn, getclient(cn), q);
|
||||
break;
|
||||
}
|
||||
|
||||
case N_MAPCHANGE:
|
||||
getstring(text, p);
|
||||
changemapserv(text, 0);
|
||||
break;
|
||||
|
||||
case N_CDIS:
|
||||
clientdisconnected(getint(p));
|
||||
break;
|
||||
|
||||
case N_SPAWN:
|
||||
{
|
||||
printf("spawn\n");
|
||||
if(d)
|
||||
{
|
||||
d->respawn();
|
||||
|
@ -313,7 +288,6 @@ namespace game
|
|||
}
|
||||
|
||||
default:
|
||||
neterr("type", cn < 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,30 +116,10 @@ namespace game
|
|||
return true;
|
||||
}
|
||||
|
||||
vector<gameent *> clients;
|
||||
|
||||
gameent *getclient(int cn) // ensure valid entity
|
||||
{
|
||||
if(cn == player1->clientnum) return player1;
|
||||
return clients.inrange(cn) ? clients[cn] : NULL;
|
||||
}
|
||||
|
||||
void clientdisconnected(int cn, bool notify)
|
||||
{
|
||||
if(!clients.inrange(cn)) return;
|
||||
gameent *d = clients[cn];
|
||||
if(d)
|
||||
{
|
||||
removetrackedparticles(d);
|
||||
removetrackeddynlights(d);
|
||||
DELETEP(clients[cn]);
|
||||
cleardynentcache();
|
||||
}
|
||||
}
|
||||
|
||||
void clearclients(bool notify)
|
||||
{
|
||||
loopv(clients) if(clients[i]) clientdisconnected(i, notify);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void initclient()
|
||||
|
@ -185,10 +165,6 @@ namespace game
|
|||
|
||||
void physicstrigger(physent *d, bool local, int floorlevel, int waterlevel, int material)
|
||||
{
|
||||
if (waterlevel>0) { if(material!=MAT_LAVA) playsound(S_SPLASHOUT, d==player1 ? NULL : &d->o); }
|
||||
else if(waterlevel<0) playsound(material==MAT_LAVA ? S_BURN : S_SPLASHIN, d==player1 ? NULL : &d->o);
|
||||
if (floorlevel>0) { if(d==player1 || d->type!=ENT_PLAYER) msgsound(S_JUMP, d); }
|
||||
else if(floorlevel<0) { if(d==player1 || d->type!=ENT_PLAYER) msgsound(S_LAND, d); }
|
||||
}
|
||||
|
||||
void dynentcollide(physent *d, physent *o, const vec &dir)
|
||||
|
@ -266,7 +242,5 @@ namespace game
|
|||
{
|
||||
execfile("config/auth.cfg", false);
|
||||
}
|
||||
|
||||
bool clientoption(const char *arg) { return false; }
|
||||
}
|
||||
|
||||
|
|
|
@ -82,44 +82,28 @@ enum
|
|||
M_EDIT = 1<<0
|
||||
};
|
||||
|
||||
#define m_edit true
|
||||
|
||||
// hardcoded sounds, defined in sounds.cfg
|
||||
enum
|
||||
{
|
||||
S_JUMP = 0, S_LAND,
|
||||
S_SPLASHIN, S_SPLASHOUT, S_BURN,
|
||||
S_ITEMSPAWN, S_TELEPORT, S_JUMPPAD,
|
||||
S_MELEE, S_PULSE1, S_PULSE2, S_PULSEEXPLODE, S_RAIL1, S_RAIL2,
|
||||
S_WEAPLOAD, S_NOAMMO, S_HIT,
|
||||
S_PAIN1, S_PAIN2, S_DIE1, S_DIE2
|
||||
};
|
||||
|
||||
// network messages codes, c2s, c2c, s2c
|
||||
|
||||
enum
|
||||
{
|
||||
N_CONNECT = 0, N_SERVINFO, N_WELCOME, N_CDIS,
|
||||
N_CONNECT = 0, N_SERVINFO, N_WELCOME,
|
||||
N_SPAWN,
|
||||
N_MAPCHANGE,
|
||||
N_NEWMAP,
|
||||