1 // server.h 2 3 #define gamemode smode // allows the gamemode macros to work with the server mode 4 5 #define SERVER_PROTOCOL_VERSION (PROTOCOL_VERSION) // server without any gameplay modification 6 //#define SERVER_PROTOCOL_VERSION (-PROTOCOL_VERSION) // server with gameplay modification but compatible to vanilla client (using /modconnect) 7 //#define SERVER_PROTOCOL_VERSION (PROTOCOL_VERSION) // server with incompatible protocol (change PROTOCOL_VERSION in file protocol.h to a negative number!) 8 9 #define valid_flag(f) (f >= 0 && f < 2) 10 11 enum { GE_NONE = 0, GE_SHOT, GE_EXPLODE, GE_HIT, GE_AKIMBO, GE_RELOAD, GE_SUICIDE, GE_PICKUP }; 12 enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; 13 14 extern int smode, servmillis; 15 16 struct shotevent 17 { 18 int type; 19 int millis, id; 20 int gun; 21 float from[3], to[3]; 22 }; 23 24 struct explodeevent 25 { 26 int type; 27 int millis, id; 28 int gun; 29 }; 30 31 struct hitevent 32 { 33 int type; 34 int target; 35 int lifesequence; 36 union 37 { 38 int info; 39 float dist; 40 }; 41 float dir[3]; 42 }; 43 44 struct suicideevent 45 { 46 int type; 47 }; 48 49 struct pickupevent 50 { 51 int type; 52 int ent; 53 }; 54 55 struct akimboevent 56 { 57 int type; 58 int millis, id; 59 }; 60 61 struct reloadevent 62 { 63 int type; 64 int millis, id; 65 int gun; 66 }; 67 68 union gameevent 69 { 70 int type; 71 shotevent shot; 72 explodeevent explode; 73 hitevent hit; 74 suicideevent suicide; 75 pickupevent pickup; 76 akimboevent akimbo; 77 reloadevent reload; 78 }; 79 80 template <int N> 81 struct projectilestate 82 { 83 int projs[N]; 84 int numprojs; 85 projectilestateprojectilestate86 projectilestate() : numprojs(0) {} 87 resetprojectilestate88 void reset() { numprojs = 0; } 89 addprojectilestate90 void add(int val) 91 { 92 if(numprojs>=N) numprojs = 0; 93 projs[numprojs++] = val; 94 } 95 removeprojectilestate96 bool remove(int val) 97 { 98 loopi(numprojs) if(projs[i]==val) 99 { 100 projs[i] = projs[--numprojs]; 101 return true; 102 } 103 return false; 104 } 105 }; 106 107 static const int DEATHMILLIS = 300; 108 109 struct clientstate : playerstate 110 { 111 vec o; 112 int state; 113 int lastdeath, lastspawn, spawn, lifesequence; 114 bool forced; 115 int lastshot; 116 projectilestate<8> grenades; 117 int akimbomillis; 118 bool scoped; 119 int flagscore, frags, teamkills, deaths, shotdamage, damage, points, events, lastdisc, reconnections; 120 clientstateclientstate121 clientstate() : state(CS_DEAD) {} 122 isaliveclientstate123 bool isalive(int gamemillis) 124 { 125 return state==CS_ALIVE || (state==CS_DEAD && gamemillis - lastdeath <= DEATHMILLIS); 126 } 127 waitexpiredclientstate128 bool waitexpired(int gamemillis) 129 { 130 int wait = gamemillis - lastshot; 131 loopi(NUMGUNS) if(wait < gunwait[i]) return false; 132 return true; 133 } 134 resetclientstate135 void reset() 136 { 137 state = CS_DEAD; 138 lifesequence = -1; 139 grenades.reset(); 140 akimbomillis = 0; 141 scoped = forced = false; 142 flagscore = frags = teamkills = deaths = shotdamage = damage = points = events = lastdisc = reconnections = 0; 143 respawn(); 144 } 145 respawnclientstate146 void respawn() 147 { 148 playerstate::respawn(); 149 o = vec(-1e10f, -1e10f, -1e10f); 150 lastdeath = 0; 151 lastspawn = -1; 152 spawn = 0; 153 lastshot = 0; 154 akimbomillis = 0; 155 scoped = false; 156 } 157 }; 158 159 struct savedscore 160 { 161 string name; 162 uint ip; 163 int frags, flagscore, deaths, teamkills, shotdamage, damage, team, points, events, lastdisc, reconnections; 164 bool valid, forced; 165 resetsavedscore166 void reset() 167 { 168 // to avoid 2 connections with the same score... this can disrupt some laggers that eventually produces 2 connections (but it is rare) 169 frags = flagscore = deaths = teamkills = shotdamage = damage = points = events = lastdisc = reconnections = 0; 170 } 171 savesavedscore172 void save(clientstate &cs, int t) 173 { 174 frags = cs.frags; 175 flagscore = cs.flagscore; 176 deaths = cs.deaths; 177 teamkills = cs.teamkills; 178 shotdamage = cs.shotdamage; 179 damage = cs.damage; 180 points = cs.points; 181 forced = cs.forced; 182 events = cs.events; 183 lastdisc = cs.lastdisc; 184 reconnections = cs.reconnections; 185 team = t; 186 valid = true; 187 } 188 restoresavedscore189 void restore(clientstate &cs) 190 { 191 cs.frags = frags; 192 cs.flagscore = flagscore; 193 cs.deaths = deaths; 194 cs.teamkills = teamkills; 195 cs.shotdamage = shotdamage; 196 cs.damage = damage; 197 cs.points = points; 198 cs.forced = forced; 199 cs.events = events; 200 cs.lastdisc = lastdisc; 201 cs.reconnections = reconnections; 202 reset(); 203 } 204 }; 205 206 struct medals 207 { 208 int dpt, lasthit, lastgun, ncovers, nhs; 209 int combohits, combo, combofrags, combotime, combodamage, ncombos; 210 int ask, askmillis, linked, linkmillis, linkreason, upmillis, flagmillis; 211 int totalhits, totalshots; 212 bool updated, combosend; 213 vec pos, flagpos; resetmedals214 void reset() 215 { 216 dpt = lasthit = lastgun = ncovers = nhs = 0; 217 combohits = combo = combofrags = combotime = combodamage = ncombos = 0; 218 askmillis = linkmillis = upmillis = flagmillis = 0; 219 linkreason = linked = ask = -1; 220 totalhits = totalshots = 0; 221 updated = combosend = false; 222 pos = flagpos = vec(-1e10f, -1e10f, -1e10f); 223 } 224 }; 225 226 struct client // server side version of "dynent" type 227 { 228 int type; 229 int clientnum; 230 ENetPeer *peer; 231 string hostname; 232 string name; 233 int team; 234 char lang[3]; 235 int ping; 236 int skin[2]; 237 int vote; 238 int role; 239 int connectmillis, lmillis, ldt, spj; 240 int mute, spam, lastvc; // server side voice comm spam control 241 int acversion, acbuildtype; 242 bool isauthed; // for passworded servers 243 bool haswelcome; 244 bool isonrightmap, loggedwrongmap, freshgame; 245 bool timesync; 246 int overflow; 247 int gameoffset, lastevent, lastvotecall; 248 int lastprofileupdate, fastprofileupdates; 249 int demoflags; 250 clientstate state; 251 vector<gameevent> events; 252 vector<uchar> position, messages; 253 string lastsaytext; 254 int saychars, lastsay, spamcount, badspeech, badmillis; 255 int at3_score, at3_lastforce, eff_score; 256 bool at3_dontmove; 257 int spawnindex; 258 int spawnperm, spawnpermsent; 259 int salt; 260 string pwd; 261 uint authreq; // for AUTH 262 string authname; // for AUTH 263 int mapcollisions, farpickups; 264 enet_uint32 bottomRTT; 265 medals md; 266 bool upspawnp; 267 int lag; 268 vec spawnp; 269 int nvotes; 270 int input, inputmillis; 271 int ffire, wn, f, g, t, y, p; 272 int yb, pb, oy, op, lda, ldda, fam; 273 int nt[10], np, lp, ls, lsm, ld, nd, nlt, lem, led; 274 vec cp[10], dp[10], d0, lv, lt, le; 275 float dda, tr, sda; 276 int ps, ph, tcn, bdt, pws; 277 float pr; 278 int yls, pls, tls; 279 int bs, bt, blg, bp; 280 addeventclient281 gameevent &addevent() 282 { 283 static gameevent dummy; 284 if(events.length()>100) return dummy; 285 return events.add(); 286 } 287 288 void mapchange(bool getmap = false) 289 { 290 state.reset(); 291 events.setsize(0); 292 overflow = 0; 293 timesync = false; 294 isonrightmap = m_coop; 295 spawnperm = SP_WRONGMAP; 296 spawnpermsent = servmillis; 297 if(!getmap) 298 { 299 loggedwrongmap = false; 300 freshgame = true; // permission to spawn at once 301 } 302 lastevent = 0; 303 at3_lastforce = eff_score = 0; 304 mapcollisions = farpickups = 0; 305 md.reset(); 306 upspawnp = false; 307 lag = 0; 308 spawnp = vec(-1e10f, -1e10f, -1e10f); 309 lmillis = ldt = spj = 0; 310 ffire = 0; 311 f = g = y = p = t = 0; 312 yb = pb = oy = op = lda = ldda = fam = 0; 313 np = lp = ls = lsm = ld = nd = nlt = lem = led = 0; 314 d0 = lv = lt = le = vec(0,0,0); 315 loopi(10) { cp[i] = dp[i] = vec(0,0,0); nt[i] = 0; } 316 dda = tr = sda = 0; 317 ps = ph = bdt = pws = 0; 318 tcn = -1; 319 pr = 0.0f; 320 yls = pls = tls = 0; 321 } 322 resetclient323 void reset() 324 { 325 name[0] = pwd[0] = demoflags = 0; 326 bottomRTT = ping = 9999; 327 team = TEAM_SPECT; 328 state.state = CS_SPECTATE; 329 loopi(2) skin[i] = 0; 330 position.setsize(0); 331 messages.setsize(0); 332 isauthed = haswelcome = false; 333 role = CR_DEFAULT; 334 lastvotecall = 0; 335 lastprofileupdate = fastprofileupdates = 0; 336 vote = VOTE_NEUTRAL; 337 lastsaytext[0] = '\0'; 338 saychars = 0; 339 spawnindex = -1; 340 authreq = 0; // for AUTH 341 mapchange(); 342 freshgame = false; // don't spawn into running games 343 mute = spam = lastvc = badspeech = badmillis = nvotes = 0; 344 input = inputmillis = 0; 345 wn = -1; 346 bs = bt = blg = bp = 0; 347 } 348 zapclient349 void zap() 350 { 351 type = ST_EMPTY; 352 role = CR_DEFAULT; 353 isauthed = haswelcome = false; 354 } 355 }; 356 357 struct ban 358 { 359 ENetAddress address; 360 int millis, type; 361 }; 362 363 struct worldstate 364 { 365 enet_uint32 uses; 366 vector<uchar> positions, messages; 367 }; 368 369 struct server_entity // server side version of "entity" type 370 { 371 int type; 372 bool spawned, legalpickup, twice; 373 int spawntime; 374 short x, y; 375 }; 376 377 struct clientidentity 378 { 379 uint ip; 380 int clientnum; 381 }; 382 383 struct demofile 384 { 385 string info; 386 string file; 387 uchar *data; 388 int len; 389 vector<clientidentity> clientssent; 390 }; 391 392 void startgame(const char *newname, int newmode, int newtime = -1, bool notify = true); 393 void disconnect_client(int n, int reason = -1); 394 void sendiplist(int receiver, int cn = -1); 395 int clienthasflag(int cn); 396 bool refillteams(bool now = false, int ftr = FTR_AUTOTEAM); 397 void changeclientrole(int client, int role, char *pwd = NULL, bool force=false); 398 mapstats *getservermapstats(const char *mapname, bool getlayout = false, int *maploc = NULL); 399 int findmappath(const char *mapname, char *filename = NULL); 400 int calcscores(); 401 void recordpacket(int chan, void *data, int len); 402 void senddisconnectedscores(int cn); 403 void process(ENetPacket *packet, int sender, int chan); 404 void welcomepacket(packetbuf &p, int n); 405 void sendwelcome(client *cl, int chan = 1); 406 void sendpacket(int n, int chan, ENetPacket *packet, int exclude = -1, bool demopacket = false); 407 int numclients(); 408 bool updateclientteam(int cln, int newteam, int ftr); 409 void forcedeath(client *cl); 410 void sendf(int cn, int chan, const char *format, ...); 411 412 extern bool isdedicated; 413 extern string smapname; 414 extern mapstats smapstats; 415 extern char *maplayout; 416 417 const char *messagenames[SV_NUM] = 418 { 419 "SV_SERVINFO", "SV_WELCOME", "SV_INITCLIENT", "SV_POS", "SV_POSC", "SV_POSN", "SV_TEXT", "SV_TEAMTEXT", "SV_TEXTME", "SV_TEAMTEXTME", "SV_TEXTPRIVATE", 420 "SV_SOUND", "SV_VOICECOM", "SV_VOICECOMTEAM", "SV_CDIS", 421 "SV_SHOOT", "SV_EXPLODE", "SV_SUICIDE", "SV_AKIMBO", "SV_RELOAD", "SV_AUTHT", "SV_AUTHREQ", "SV_AUTHTRY", "SV_AUTHANS", "SV_AUTHCHAL", 422 "SV_GIBDIED", "SV_DIED", "SV_GIBDAMAGE", "SV_DAMAGE", "SV_HITPUSH", "SV_SHOTFX", "SV_THROWNADE", 423 "SV_TRYSPAWN", "SV_SPAWNSTATE", "SV_SPAWN", "SV_SPAWNDENY", "SV_FORCEDEATH", "SV_RESUME", 424 "SV_DISCSCORES", "SV_TIMEUP", "SV_EDITENT", "SV_ITEMACC", 425 "SV_MAPCHANGE", "SV_ITEMSPAWN", "SV_ITEMPICKUP", 426 "SV_PING", "SV_PONG", "SV_CLIENTPING", "SV_GAMEMODE", 427 "SV_EDITMODE", "SV_EDITH", "SV_EDITT", "SV_EDITS", "SV_EDITD", "SV_EDITE", "SV_NEWMAP", 428 "SV_SENDMAP", "SV_RECVMAP", "SV_REMOVEMAP", 429 "SV_SERVMSG", "SV_ITEMLIST", "SV_WEAPCHANGE", "SV_PRIMARYWEAP", 430 "SV_FLAGACTION", "SV_FLAGINFO", "SV_FLAGMSG", "SV_FLAGCNT", 431 "SV_ARENAWIN", 432 "SV_SETADMIN", "SV_SERVOPINFO", 433 "SV_CALLVOTE", "SV_CALLVOTESUC", "SV_CALLVOTEERR", "SV_VOTE", "SV_VOTERESULT", 434 "SV_SETTEAM", "SV_TEAMDENY", "SV_SERVERMODE", 435 "SV_IPLIST", 436 "SV_LISTDEMOS", "SV_SENDDEMOLIST", "SV_GETDEMO", "SV_SENDDEMO", "SV_DEMOPLAYBACK", 437 "SV_CONNECT", 438 "SV_SWITCHNAME", "SV_SWITCHSKIN", "SV_SWITCHTEAM", 439 "SV_CLIENT", 440 "SV_EXTENSION", 441 "SV_MAPIDENT", "SV_HUDEXTRAS", "SV_POINTS" 442 }; 443 444 const char *entnames[MAXENTTYPES] = 445 { 446 "none?", 447 "light", "playerstart", "pistol", "ammobox","grenades", 448 "health", "helmet", "armour", "akimbo", 449 "mapmodel", "trigger", "ladder", "ctf-flag", "sound", "clip", "plclip" 450 }; 451 452 // see entity.h:61: struct itemstat { int add, start, max, sound; }; 453 // Please update ./ac_website/htdocs/docs/introduction.html if these figures change. 454 itemstat ammostats[NUMGUNS] = 455 { 456 { 1, 1, 1, S_ITEMAMMO }, // knife dummy 457 { 20, 60, 100, S_ITEMAMMO }, // pistol 458 { 15, 30, 30, S_ITEMAMMO }, // carbine 459 { 14, 28, 21, S_ITEMAMMO }, // shotgun 460 { 60, 90, 90, S_ITEMAMMO }, // subgun 461 { 10, 20, 15, S_ITEMAMMO }, // sniper 462 { 40, 60, 60, S_ITEMAMMO }, // assault 463 { 30, 45, 75, S_ITEMAMMO }, // cpistol 464 { 1, 0, 3, S_ITEMAMMO }, // grenade 465 {100, 0, 100, S_ITEMAKIMBO} // akimbo 466 }; 467 468 itemstat powerupstats[I_ARMOUR-I_HEALTH+1] = 469 { 470 {33, 0, 100, S_ITEMHEALTH}, // 0 health 471 {25, 0, 100, S_ITEMHELMET}, // 1 helmet 472 {50, 0, 100, S_ITEMARMOUR}, // 2 armour 473 }; 474 475 guninfo guns[NUMGUNS] = 476 { 477 // Please update ./ac_website/htdocs/docs/introduction.html if these figures change. 478 //mKR: mdl_kick_rot && mKB: mdl_kick_back 479 //reI: recoilincrease && reB: recoilbase && reM: maxrecoil && reF: recoilbackfade 480 //pFX: pushfactor 481 //modelname reload attackdelay piercing part recoil mKR reI reM pFX 482 // sound reloadtime damage projspeed spread magsize mKB reB reF isauto 483 { "knife", S_KNIFE, S_NULL, 0, 500, 50, 100, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, false }, 484 { "pistol", S_PISTOL, S_RPISTOL, 1400, 160, 18, 0, 0, 0, 53, 10, 10, 6, 5, 6, 35, 58, 125, 1, false }, 485 { "carbine", S_CARBINE, S_RCARBINE, 1800, 720, 60, 40, 0, 0, 10, 60, 10, 4, 4, 10, 60, 60, 150, 1, false }, 486 { "shotgun", S_SHOTGUN, S_RSHOTGUN, 2400, 880, 1, 0, 0, 0, 1, 35, 7, 9, 9, 10, 140, 140, 125, 1, false }, // CAUTION dmg only sane for server! 487 { "subgun", S_SUBGUN, S_RSUBGUN, 1650, 80, 16, 0, 0, 0, 45, 15, 30, 1, 2, 5, 25, 50, 188, 1, true }, 488 { "sniper", S_SNIPER, S_RSNIPER, 1950, 1500, 82, 25, 0, 0, 50, 50, 5, 4, 4, 10, 85, 85, 100, 1, false }, 489 { "assault", S_ASSAULT, S_RASSAULT, 2000, 120, 22, 0, 0, 0, 18, 30, 20, 0, 2, 3, 25, 50, 115, 1, true }, 490 { "cpistol", S_PISTOL, S_RPISTOL, 1400, 120, 19, 0, 0, 0, 35, 10, 15, 6, 5, 6, 35, 50, 125, 1, false }, // temporary 491 { "grenade", S_NULL, S_NULL, 1000, 650, 200, 0, 20, 6, 1, 1, 1, 3, 1, 0, 0, 0, 0, 3, false }, 492 { "pistol", S_PISTOL, S_RAKIMBO, 1400, 80, 19, 0, 0, 0, 50, 10, 20, 6, 5, 4, 15, 25, 115, 1, true }, 493 }; 494 495 const char *teamnames[TEAM_NUM+1] = {"CLA", "RVSF", "CLA-SPECT", "RVSF-SPECT", "SPECTATOR", "void"}; 496 const char *teamnames_s[TEAM_NUM+1] = {"CLA", "RVSF", "CSPC", "RSPC", "SPEC", "void"}; 497 498 // for both client and server 499 // default messages are hardcoded ! 500 char killmessages[2][NUMGUNS][MAXKILLMSGLEN] = {{ "", "busted", "picked off", "peppered", "sprayed", "punctured", "shredded", "busted", "", "busted" }, { "slashed", "", "", "splattered", "", "headshot", "", "", "gibbed", "" }}; 501