// server.h #define gamemode smode // allows the gamemode macros to work with the server mode #define SERVER_PROTOCOL_VERSION (PROTOCOL_VERSION) // server without any gameplay modification //#define SERVER_PROTOCOL_VERSION (-PROTOCOL_VERSION) // server with gameplay modification but compatible to vanilla client (using /modconnect) //#define SERVER_PROTOCOL_VERSION (PROTOCOL_VERSION) // server with incompatible protocol (change PROTOCOL_VERSION in file protocol.h to a negative number!) #define valid_flag(f) (f >= 0 && f < 2) enum { GE_NONE = 0, GE_SHOT, GE_EXPLODE, GE_HIT, GE_AKIMBO, GE_RELOAD, GE_SCOPING, GE_SUICIDE, GE_PICKUP }; enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; extern int smode, servmillis; struct shotevent { int type; int millis, id; int gun; float from[3], to[3]; }; struct explodeevent { int type; int millis, id; int gun; }; struct hitevent { int type; int target; int lifesequence; union { int info; float dist; }; float dir[3]; }; struct suicideevent { int type; }; struct pickupevent { int type; int ent; }; struct akimboevent { int type; int millis, id; }; struct reloadevent { int type; int millis, id; int gun; }; struct scopeevent { int type; int millis, id; bool scoped; }; union gameevent { int type; shotevent shot; explodeevent explode; hitevent hit; suicideevent suicide; pickupevent pickup; akimboevent akimbo; reloadevent reload; scopeevent scoping; }; template struct projectilestate { int projs[N]; int numprojs; projectilestate() : numprojs(0) {} void reset() { numprojs = 0; } void add(int val) { if(numprojs>=N) numprojs = 0; projs[numprojs++] = val; } bool remove(int val) { loopi(numprojs) if(projs[i]==val) { projs[i] = projs[--numprojs]; return true; } return false; } }; static const int DEATHMILLIS = 300; struct clientstate : playerstate { vec o; int state; int lastdeath, lastspawn, spawn, lifesequence; bool forced; int lastshot; projectilestate<8> grenades; int akimbomillis; bool scoped; int flagscore, frags, teamkills, deaths, shotdamage, damage, points, events; clientstate() : state(CS_DEAD) {} bool isalive(int gamemillis) { return state==CS_ALIVE || (state==CS_DEAD && gamemillis - lastdeath <= DEATHMILLIS); } bool waitexpired(int gamemillis) { int wait = gamemillis - lastshot; loopi(NUMGUNS) if(wait < gunwait[i]) return false; return true; } void reset() { state = CS_DEAD; lifesequence = -1; grenades.reset(); akimbomillis = 0; scoped = forced = false; flagscore = frags = teamkills = deaths = shotdamage = damage = points = events = 0; respawn(); } void respawn() { playerstate::respawn(); o = vec(-1e10f, -1e10f, -1e10f); lastdeath = 0; lastspawn = -1; spawn = 0; lastshot = 0; akimbomillis = 0; scoped = false; } }; struct savedscore { string name; uint ip; int frags, flagscore, deaths, teamkills, shotdamage, damage, team, points, events; bool valid, forced; void save(clientstate &cs, int t) { frags = cs.frags; flagscore = cs.flagscore; deaths = cs.deaths; teamkills = cs.teamkills; shotdamage = cs.shotdamage; damage = cs.damage; points = cs.points; forced = cs.forced; events = cs.events; team = t; valid = true; } void restore(clientstate &cs) { cs.frags = frags; cs.flagscore = flagscore; cs.deaths = deaths; cs.teamkills = teamkills; cs.shotdamage = shotdamage; cs.damage = damage; cs.points = points; cs.forced = forced; cs.events = events; } }; struct medals { int dpt, lasthit, lastgun, ncovers, nhs; int combohits, combo, combofrags, combotime, combodamage, ncombos; int ask, askmillis, linked, linkmillis, linkreason, upmillis, flagmillis; bool updated, combosend; vec pos, flagpos; void reset() { dpt = lasthit = lastgun = ncovers = nhs = 0; combohits = combo = combofrags = combotime = combodamage = ncombos = 0; askmillis = linkmillis = upmillis = flagmillis = 0; linkreason = linked = ask = -1; updated = combosend = false; pos = flagpos = vec(-1e10f, -1e10f, -1e10f); } }; struct client // server side version of "dynent" type { int type; int clientnum; ENetPeer *peer; string hostname; string name; int team; char lang[3]; int ping; int skin[2]; int vote; int role; int connectmillis, lmillis, ldt, spj; int mute, spam, lastvc; // server side voice comm spam control int acversion, acbuildtype; bool isauthed; // for passworded servers bool haswelcome; bool isonrightmap, loggedwrongmap, freshgame; bool timesync; int overflow; int gameoffset, lastevent, lastvotecall; int demoflags; clientstate state; int spectcn; vector events; vector position, messages; string lastsaytext; int saychars, lastsay, spamcount, badspeech, badmillis; int at3_score, at3_lastforce, eff_score; bool at3_dontmove; int spawnindex; int spawnperm, spawnpermsent; int salt; string pwd; int mapcollisions, farpickups; enet_uint32 bottomRTT; medals md; bool upspawnp; vec spawnp; int nvotes; int input, inputmillis; int ffire, wn; gameevent &addevent() { static gameevent dummy; if(events.length()>100) return dummy; return events.add(); } void mapchange(bool getmap = false) { state.reset(); events.setsize(0); overflow = 0; timesync = false; isonrightmap = m_coop; spawnperm = SP_WRONGMAP; spawnpermsent = servmillis; if(!getmap) { loggedwrongmap = false; freshgame = true; // permission to spawn at once } lastevent = 0; at3_lastforce = eff_score = 0; mapcollisions = farpickups = 0; md.reset(); upspawnp = false; spawnp = vec(-1e10f, -1e10f, -1e10f); lmillis = ldt = spj = 0; ffire = 0; } void reset() { name[0] = demoflags = 0; bottomRTT = ping = 9999; team = TEAM_SPECT; state.state = CS_SPECTATE; loopi(2) skin[i] = 0; position.setsize(0); messages.setsize(0); isauthed = haswelcome = false; role = CR_DEFAULT; lastvotecall = 0; vote = VOTE_NEUTRAL; lastsaytext[0] = '\0'; saychars = 0; spawnindex = -1; spectcn = FPCN_VOID; mapchange(); freshgame = false; // don't spawn into running games mute = spam = lastvc = badspeech = badmillis = nvotes = 0; input = inputmillis = 0; wn = -1; } void zap() { type = ST_EMPTY; role = CR_DEFAULT; isauthed = haswelcome = false; } }; struct ban { ENetAddress address; int millis; }; struct worldstate { enet_uint32 uses; vector positions, messages; }; struct server_entity // server side version of "entity" type { int type; bool spawned, legalpickup, twice; int spawntime; short x, y; }; struct demofile { string info; string file; uchar *data; int len; }; void startgame(const char *newname, int newmode, int newtime = -1, bool notify = true); void disconnect_client(int n, int reason = -1); int clienthasflag(int cn); bool refillteams(bool now = false, int ftr = FTR_AUTOTEAM); void changeclientrole(int client, int role, char *pwd = NULL, bool force=false); mapstats *getservermapstats(const char *mapname, bool getlayout = false, int *maploc = NULL); int findmappath(const char *mapname, char *filename = NULL); int calcscores(); void recordpacket(int chan, void *data, int len); void senddisconnectedscores(int cn); void process(ENetPacket *packet, int sender, int chan); void welcomepacket(packetbuf &p, int n); void sendwelcome(client *cl, int chan = 1); void sendpacket(int n, int chan, ENetPacket *packet, int exclude = -1); int numclients(); bool updateclientteam(int cln, int newteam, int ftr); void forcedeath(client *cl); void sendf(int cn, int chan, const char *format, ...); extern bool isdedicated; extern string smapname; extern mapstats smapstats; extern char *maplayout; const char *messagenames[SV_NUM] = { "SV_SERVINFO", "SV_WELCOME", "SV_INITCLIENT", "SV_POS", "SV_POSC", "SV_POSN", "SV_TEXT", "SV_TEAMTEXT", "SV_TEXTME", "SV_TEAMTEXTME", "SV_SOUND", "SV_VOICECOM", "SV_VOICECOMTEAM", "SV_CDIS", "SV_SHOOT", "SV_EXPLODE", "SV_SUICIDE", "SV_AKIMBO", "SV_RELOAD", "SV_SCOPE", "SV_GIBDIED", "SV_DIED", "SV_GIBDAMAGE", "SV_DAMAGE", "SV_HITPUSH", "SV_SHOTFX", "SV_THROWNADE", "SV_TRYSPAWN", "SV_SPAWNSTATE", "SV_SPAWN", "SV_SPAWNDENY", "SV_FORCEDEATH", "SV_RESUME", "SV_DISCSCORES", "SV_TIMEUP", "SV_EDITENT", "SV_ITEMACC", "SV_MAPCHANGE", "SV_ITEMSPAWN", "SV_ITEMPICKUP", "SV_PING", "SV_PONG", "SV_CLIENTPING", "SV_GAMEMODE", "SV_EDITMODE", "SV_EDITH", "SV_EDITT", "SV_EDITS", "SV_EDITD", "SV_EDITE", "SV_NEWMAP", "SV_SENDMAP", "SV_RECVMAP", "SV_REMOVEMAP", "SV_SERVMSG", "SV_ITEMLIST", "SV_WEAPCHANGE", "SV_PRIMARYWEAP", "SV_FLAGACTION", "SV_FLAGINFO", "SV_FLAGMSG", "SV_FLAGCNT", "SV_ARENAWIN", "SV_LMSITEM", "SV_SETADMIN", "SV_SERVOPINFO", "SV_CALLVOTE", "SV_CALLVOTESUC", "SV_CALLVOTEERR", "SV_VOTE", "SV_VOTERESULT", "SV_SETTEAM", "SV_TEAMDENY", "SV_SERVERMODE", "SV_WHOIS", "SV_WHOISINFO", "SV_LISTDEMOS", "SV_SENDDEMOLIST", "SV_GETDEMO", "SV_SENDDEMO", "SV_DEMOPLAYBACK", "SV_CONNECT", "SV_SPECTCN", "SV_SWITCHNAME", "SV_SWITCHSKIN", "SV_SWITCHTEAM", "SV_CLIENT", "SV_EXTENSION", "SV_MAPIDENT", "SV_HUDEXTRAS", "SV_POINTS" }; const char *entnames[MAXENTTYPES] = { "none?", "light", "playerstart", "pistol", "ammobox","grenades", "health", "helmet", "armour", "akimbo", "mapmodel", "trigger", "ladder", "ctf-flag", "sound", "clip", "plclip" }; itemstat ammostats[NUMGUNS] = { {1, 1, 1, S_ITEMAMMO}, //knife dummy {16, 32, 72, S_ITEMAMMO}, //pistol {15, 30, 30, S_ITEMAMMO}, //rifle {14, 28, 21, S_ITEMAMMO}, //shotgun {60, 90, 90, S_ITEMAMMO}, //subgun {10, 20, 15, S_ITEMAMMO}, //sniper {40, 60, 60, S_ITEMAMMO}, //assault {30, 45, 75, S_ITEMAMMO}, //cpistol {1, 0, 3, S_ITEMAMMO}, //grenade {72, 0, 72, S_ITEMAKIMBO} //akimbo }; itemstat powerupstats[I_ARMOUR-I_HEALTH+1] = { {33, 0, 100, S_ITEMHEALTH}, // 0 health {25, 0, 100, S_ITEMARMOUR}, // 1 helmet {50, 0, 100, S_ITEMARMOUR}, // 2 armour }; guninfo guns[NUMGUNS] = { { "knife", S_KNIFE, S_NULL, 0, 500, 50, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, false }, { "pistol", S_PISTOL, S_RPISTOL, 1400, 160, 18, 0, 0, 55, 10, 8, 6, 5, 6, 35, 58, 125, 1, false }, { "rifle", S_RIFLE, S_RRIFLE, 1800, 720, 60, 0, 0, 10, 60, 10, 4, 4, 10, 60, 60, 150, 1, false }, { "shotgun", S_SHOTGUN, S_RSHOTGUN, 2400, 1000, 5, 0, 0, 1, 35, 7, 9, 9, 10, 115, 115, 150, 1, false }, { "subgun", S_SUBGUN, S_RSUBGUN, 1650, 80, 15, 0, 0, 40, 15, 30, 1, 2, 4, 15, 40, 175, 1, true }, { "sniper", S_SNIPER, S_RSNIPER, 1950, 1500, 80, 0, 0, 60, 50, 5, 4, 4, 10, 85, 85, 100, 1, false }, { "assault", S_ASSAULT, S_RASSAULT, 2000, 120, 24, 0, 0, 20, 40, 20, 0, 2, 2, 18, 50, 125, 1, true }, { "cpistol", S_PISTOL, S_RPISTOL, 1400, 120, 19, 0, 0, 35, 10, 15, 6, 5, 6, 35, 58, 125, 1, false }, // temporary { "grenade", S_NULL, S_NULL, 1000, 650, 200, 20, 6, 1, 1, 1, 3, 1, 0, 0, 0, 0, 3, false }, { "pistol", S_PISTOL, S_RAKIMBO, 1400, 80, 19, 0, 0, 45, 10, 16, 6, 5, 8, 10, 18, 150, 1, true }, }; const char *teamnames[TEAM_NUM+1] = {"CLA", "RVSF", "CLA-SPECT", "RVSF-SPECT", "SPECTATOR", "void"}; const char *teamnames_s[TEAM_NUM+1] = {"CLA", "RVSF", "CSPC", "RSPC", "SPEC", "void"};