diff --git a/config/forbidden.cfg b/config/forbidden.cfg deleted file mode 100644 index 06034320..00000000 --- a/config/forbidden.cfg +++ /dev/null @@ -1,27 +0,0 @@ -// Put in this file the words you consider to be inappropriate in your server -// The system automatically pass all uppercase letters to lowercase -// You can put pairs of words which should not come together in the same phrase -// so "banana caramelada" will block "eu tenho uma banana, que eh muito caramelada" -// but will not block "minha banana eh grande" -// Below it is presented some default examples - -fuck -bitch -8== -==8 -your mother -suck dick -big cock -// from TyD nickblacklist -asshole // "ass" is not blacklisted -horny -hurensohn // "Son of a bitch" in german. -masturb // See "wank". -necrop // Ban names that mention some form of necrophilia. -pedop -penis -pussy -filho puta // "whore" or "slut" in portugese. -redtube -porntube -youporn diff --git a/config/menus_multiplayer.cfg b/config/menus_multiplayer.cfg index 04cfc0c9..6d5c98e2 100644 --- a/config/menus_multiplayer.cfg +++ b/config/menus_multiplayer.cfg @@ -78,6 +78,7 @@ menuinitselection 13 //// In-Game admin //// (In-game menu that opens if when you press escape if you're admin state and connected to a game.) newmenu [In-game admin] +menuadminonly 1 menuinit [ resetcurmenu menuitem "Set mode/map\i\3" [ showmenu Gamemode ] @@ -158,6 +159,7 @@ menuitem [Turn auto-team off] [ autoteam 0 ] //// In-Game admin > Mastermode //// newmenu Mastermode +menuadminonly 2 menuitem [Unlock the server (open)] [ mastermode 0 ] menuitem [Lock the server (private)] [ mastermode 1 ] menuitem [Lock the server (match mode)] [ mastermode 2; showmenu [Match Settings] ] @@ -165,17 +167,18 @@ menuitem [Lock the server (match mode)] [ mastermode 2; showmenu [Match Settings //// In-Game admin > Match Settings //// newmenu [Match Settings] +menuadminonly 2 menuinit [ ] -menuitemslider "Recoil Multiplier: " 50 500 "$matchrecoilmul" 10 [ matchrecoilmul $arg1 ] -menuitemslider "Spread Multiplier: " 50 500 "$matchspreadmul" 10 [ matchspreadmul $arg1 ] -menuitemslider "Speed Multiplier: " 50 500 "$matchspeedmul" 10 [ matchspeedmul $arg1 ] -menuitemslider "Gravity Multiplier: " 50 500 "$matchgravity" 10 [ matchgravity $arg1 ] -menuitemslider "Friction Multiplier: " 10 500 "$matchfriction" 10 [ matchfriction $arg1 ] +menuitemslider "Recoil Multiplier: " 50 500 "$matchrecoilmul" 10 [ matchset matchrecoilmul $arg1 ] +menuitemslider "Spread Multiplier: " 50 500 "$matchspreadmul" 10 [ matchset matchspreadmul $arg1 ] +menuitemslider "Speed Multiplier: " 50 500 "$matchspeedmul" 10 [ matchset matchspeedmul $arg1 ] +menuitemslider "Gravity Multiplier: " 50 500 "$matchgravity" 10 [ matchset matchgravity $arg1 ] +menuitemslider "Friction Multiplier: " 10 500 "$matchfriction" 10 [ matchset matchfriction $arg1 ] menuitem "" -1 -menuitemcheckbox "Enable Assists: " "$matchassist" [ matchassist $arg1 ] -menuitemcheckbox "Explosive Deaths: " "$matchdeathexplosion" [ matchdeathexplosion $arg1 ] -menuitemslider " Blast Radius %: " 10 300 "$matchdeathradius" 10 [ matchdeathradius $arg1 ] -menuitemslider " Blast Power %: " 10 500 "$matchdeathpower" 10 [ matchdeathpower $arg1 ] +menuitemcheckbox "Enable Assists: " "$matchassist" [ matchset matchassist $arg1 ] +menuitemcheckbox "Explosive Deaths: " "$matchdeathexplosion" [ matchset matchdeathexplosion $arg1 ] +menuitemslider " Blast Radius %: " 10 300 "$matchdeathradius" 10 [ matchset matchdeathradius $arg1 ] +menuitemslider " Blast Power %: " 10 500 "$matchdeathpower" 10 [ matchset matchdeathpower $arg1 ] menuitem "" -1 menuitem "Friendly Fire\i\3" [ showmenu [Match Friendly Fire] ] menuitem "" -1 @@ -183,22 +186,24 @@ menuitem "Spawn Weapons\i\3" [ showmenu [Match Spawn Weapons] ] menuitem "" -1 menuitem "\f2Start Bot Match" [ startbotmap ] menuitem "" -1 -menuitem "Reset to defaults (100%)" [ matchrecoilmul 100; matchspreadmul 100; matchspeedmul 100; matchgravity 100; matchfriction 100; matchdeathradius 100; matchdeathpower 100; matchassist 1; matchfriendlyfire 1; matchdeathexplosion 0; matchspawnprimary -1; matchspawnsecondary -1; matchspawntertiary -1; matchspawnquaternary -1; showmenu [Match Settings] ] +menuitem "Reset to defaults" [ matchsetreset; showmenu [Match Settings] ] menuitem "Back" [ showmenu Mastermode ] //// In-Game admin > Match Settings > Friendly Fire //// newmenu [Match Friendly Fire] +menuadminonly 2 menuinit [ ] -menuitem "Off (no damage)" [ matchfriendlyfire 0; showmenu [Match Settings] ] -menuitem "On (normal)" [ matchfriendlyfire 1; showmenu [Match Settings] ] -menuitem "Off + no shoot teammates" [ matchfriendlyfire 2; showmenu [Match Settings] ] +menuitem "Off (no damage)" [ matchset matchfriendlyfire 0; showmenu [Match Settings] ] +menuitem "On (normal)" [ matchset matchfriendlyfire 1; showmenu [Match Settings] ] +menuitem "Off + no shoot teammates" [ matchset matchfriendlyfire 2; showmenu [Match Settings] ] menuitem "" -1 menuitem "Back" [ showmenu [Match Settings] ] //// In-Game admin > Match Settings > Spawn Weapons //// newmenu [Match Spawn Weapons] +menuadminonly 2 menuinit [ ] menutitle "Spawn weapons (primary = what you hold, others = extra guns)" menuitem "Primary Weapon\i\3" [ showmenu [Match Spawn Primary] ] @@ -211,64 +216,68 @@ menuitem "Back" [ showmenu [Match Settings] ] //// In-Game admin > Match Settings > Spawn Weapons > Primary //// newmenu [Match Spawn Primary] +menuadminonly 2 menuinit [ ] -menuitem "Default (your own choice)" [ matchspawnprimary -1; showmenu [Match Spawn Weapons] ] -menuitem "Knife" [ matchspawnprimary 0; showmenu [Match Spawn Weapons] ] -menuitem "Pistol" [ matchspawnprimary 1; showmenu [Match Spawn Weapons] ] -menuitem "Carbine" [ matchspawnprimary 2; showmenu [Match Spawn Weapons] ] -menuitem "Shotgun" [ matchspawnprimary 3; showmenu [Match Spawn Weapons] ] -menuitem "Subgun" [ matchspawnprimary 4; showmenu [Match Spawn Weapons] ] -menuitem "Sniper" [ matchspawnprimary 5; showmenu [Match Spawn Weapons] ] -menuitem "Assault" [ matchspawnprimary 6; showmenu [Match Spawn Weapons] ] -menuitem "Grenade" [ matchspawnprimary 7; showmenu [Match Spawn Weapons] ] +menuitem "Default (your own choice)" [ matchset matchspawnprimary -1; showmenu [Match Spawn Weapons] ] +menuitem "Knife" [ matchset matchspawnprimary 0; showmenu [Match Spawn Weapons] ] +menuitem "Pistol" [ matchset matchspawnprimary 1; showmenu [Match Spawn Weapons] ] +menuitem "Carbine" [ matchset matchspawnprimary 2; showmenu [Match Spawn Weapons] ] +menuitem "Shotgun" [ matchset matchspawnprimary 3; showmenu [Match Spawn Weapons] ] +menuitem "Subgun" [ matchset matchspawnprimary 4; showmenu [Match Spawn Weapons] ] +menuitem "Sniper" [ matchset matchspawnprimary 5; showmenu [Match Spawn Weapons] ] +menuitem "Assault" [ matchset matchspawnprimary 6; showmenu [Match Spawn Weapons] ] +menuitem "Grenade" [ matchset matchspawnprimary 7; showmenu [Match Spawn Weapons] ] menuitem "" -1 menuitem "Back" [ showmenu [Match Spawn Weapons] ] //// In-Game admin > Match Settings > Spawn Weapons > Secondary //// newmenu [Match Spawn Secondary] +menuadminonly 2 menuinit [ ] -menuitem "None" [ matchspawnsecondary -1; showmenu [Match Spawn Weapons] ] -menuitem "Knife" [ matchspawnsecondary 0; showmenu [Match Spawn Weapons] ] -menuitem "Pistol" [ matchspawnsecondary 1; showmenu [Match Spawn Weapons] ] -menuitem "Carbine" [ matchspawnsecondary 2; showmenu [Match Spawn Weapons] ] -menuitem "Shotgun" [ matchspawnsecondary 3; showmenu [Match Spawn Weapons] ] -menuitem "Subgun" [ matchspawnsecondary 4; showmenu [Match Spawn Weapons] ] -menuitem "Sniper" [ matchspawnsecondary 5; showmenu [Match Spawn Weapons] ] -menuitem "Assault" [ matchspawnsecondary 6; showmenu [Match Spawn Weapons] ] -menuitem "Grenade" [ matchspawnsecondary 7; showmenu [Match Spawn Weapons] ] +menuitem "None" [ matchset matchspawnsecondary -1; showmenu [Match Spawn Weapons] ] +menuitem "Knife" [ matchset matchspawnsecondary 0; showmenu [Match Spawn Weapons] ] +menuitem "Pistol" [ matchset matchspawnsecondary 1; showmenu [Match Spawn Weapons] ] +menuitem "Carbine" [ matchset matchspawnsecondary 2; showmenu [Match Spawn Weapons] ] +menuitem "Shotgun" [ matchset matchspawnsecondary 3; showmenu [Match Spawn Weapons] ] +menuitem "Subgun" [ matchset matchspawnsecondary 4; showmenu [Match Spawn Weapons] ] +menuitem "Sniper" [ matchset matchspawnsecondary 5; showmenu [Match Spawn Weapons] ] +menuitem "Assault" [ matchset matchspawnsecondary 6; showmenu [Match Spawn Weapons] ] +menuitem "Grenade" [ matchset matchspawnsecondary 7; showmenu [Match Spawn Weapons] ] menuitem "" -1 menuitem "Back" [ showmenu [Match Spawn Weapons] ] //// In-Game admin > Match Settings > Spawn Weapons > Tertiary //// newmenu [Match Spawn Tertiary] +menuadminonly 2 menuinit [ ] -menuitem "None" [ matchspawntertiary -1; showmenu [Match Spawn Weapons] ] -menuitem "Knife" [ matchspawntertiary 0; showmenu [Match Spawn Weapons] ] -menuitem "Pistol" [ matchspawntertiary 1; showmenu [Match Spawn Weapons] ] -menuitem "Carbine" [ matchspawntertiary 2; showmenu [Match Spawn Weapons] ] -menuitem "Shotgun" [ matchspawntertiary 3; showmenu [Match Spawn Weapons] ] -menuitem "Subgun" [ matchspawntertiary 4; showmenu [Match Spawn Weapons] ] -menuitem "Sniper" [ matchspawntertiary 5; showmenu [Match Spawn Weapons] ] -menuitem "Assault" [ matchspawntertiary 6; showmenu [Match Spawn Weapons] ] -menuitem "Grenade" [ matchspawntertiary 7; showmenu [Match Spawn Weapons] ] +menuitem "None" [ matchset matchspawntertiary -1; showmenu [Match Spawn Weapons] ] +menuitem "Knife" [ matchset matchspawntertiary 0; showmenu [Match Spawn Weapons] ] +menuitem "Pistol" [ matchset matchspawntertiary 1; showmenu [Match Spawn Weapons] ] +menuitem "Carbine" [ matchset matchspawntertiary 2; showmenu [Match Spawn Weapons] ] +menuitem "Shotgun" [ matchset matchspawntertiary 3; showmenu [Match Spawn Weapons] ] +menuitem "Subgun" [ matchset matchspawntertiary 4; showmenu [Match Spawn Weapons] ] +menuitem "Sniper" [ matchset matchspawntertiary 5; showmenu [Match Spawn Weapons] ] +menuitem "Assault" [ matchset matchspawntertiary 6; showmenu [Match Spawn Weapons] ] +menuitem "Grenade" [ matchset matchspawntertiary 7; showmenu [Match Spawn Weapons] ] menuitem "" -1 menuitem "Back" [ showmenu [Match Spawn Weapons] ] //// In-Game admin > Match Settings > Spawn Weapons > Quaternary //// newmenu [Match Spawn Quaternary] +menuadminonly 2 menuinit [ ] -menuitem "None" [ matchspawnquaternary -1; showmenu [Match Spawn Weapons] ] -menuitem "Knife" [ matchspawnquaternary 0; showmenu [Match Spawn Weapons] ] -menuitem "Pistol" [ matchspawnquaternary 1; showmenu [Match Spawn Weapons] ] -menuitem "Carbine" [ matchspawnquaternary 2; showmenu [Match Spawn Weapons] ] -menuitem "Shotgun" [ matchspawnquaternary 3; showmenu [Match Spawn Weapons] ] -menuitem "Subgun" [ matchspawnquaternary 4; showmenu [Match Spawn Weapons] ] -menuitem "Sniper" [ matchspawnquaternary 5; showmenu [Match Spawn Weapons] ] -menuitem "Assault" [ matchspawnquaternary 6; showmenu [Match Spawn Weapons] ] -menuitem "Grenade" [ matchspawnquaternary 7; showmenu [Match Spawn Weapons] ] +menuitem "None" [ matchset matchspawnquaternary -1; showmenu [Match Spawn Weapons] ] +menuitem "Knife" [ matchset matchspawnquaternary 0; showmenu [Match Spawn Weapons] ] +menuitem "Pistol" [ matchset matchspawnquaternary 1; showmenu [Match Spawn Weapons] ] +menuitem "Carbine" [ matchset matchspawnquaternary 2; showmenu [Match Spawn Weapons] ] +menuitem "Shotgun" [ matchset matchspawnquaternary 3; showmenu [Match Spawn Weapons] ] +menuitem "Subgun" [ matchset matchspawnquaternary 4; showmenu [Match Spawn Weapons] ] +menuitem "Sniper" [ matchset matchspawnquaternary 5; showmenu [Match Spawn Weapons] ] +menuitem "Assault" [ matchset matchspawnquaternary 6; showmenu [Match Spawn Weapons] ] +menuitem "Grenade" [ matchset matchspawnquaternary 7; showmenu [Match Spawn Weapons] ] menuitem "" -1 menuitem "Back" [ showmenu [Match Spawn Weapons] ] diff --git a/config/servercmdline.txt b/config/servercmdline.txt index fdf24932..8c1655d7 100644 --- a/config/servercmdline.txt +++ b/config/servercmdline.txt @@ -15,7 +15,7 @@ // example MOTD: -o \f3server using example cmdline file (config/servercmdline.txt)\n\f2gassy server 🥶 - + -f 27015 // commonly used commandline switches: @@ -51,7 +51,6 @@ // -B // server IP blacklist file, default config/serverblacklist.cfg // -G // server geoip file, default config/geoip.cfg // -K // server nickname blacklist file, default config/nicknameblacklist.cfg -// -g // server forbidden words file, default config/forbidden.cfg // -P // permission string (read docs/commandline.html) // k vote to kick players // b vote to ban players (and remove bans) diff --git a/config/serverpwd.cfg b/config/serverpwd.cfg index 024cf88b..9066826b 100644 --- a/config/serverpwd.cfg +++ b/config/serverpwd.cfg @@ -6,3 +6,4 @@ // password [denyadmin] // optional: if denyadmin is set to '1', the password can only be used to connect to the server in case of ban, not to claim admin +awesomesauce diff --git a/source/codeblocks/AssaultCube.cbp b/source/codeblocks/AssaultCube.cbp index 9b57f9f8..b13a7836 100755 --- a/source/codeblocks/AssaultCube.cbp +++ b/source/codeblocks/AssaultCube.cbp @@ -185,7 +185,6 @@ - diff --git a/source/src/client.cpp b/source/src/client.cpp index 4e2e8f2e..04501116 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -184,6 +184,7 @@ void disconnect(int onlyclean, int async) kickallbots(); loopv(players) zapplayer(players[i]); clearvote(); + matchsettings_reset(); audiomgr.clearworldsounds(false); localdisconnect(); } diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 5b3255c8..edc81f82 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -59,6 +59,8 @@ bool duplicatename(playerent *d, char *name = NULL) char *colorname(playerent *d, char *name, const char *prefix) { if(!name) name = d->name; + static char unnamed[] = "unnamed"; + if(!name[0]) name = unnamed; // still joining, real name not received yet: never display a bare "(cn)" bool isdname = duplicatename(d, name); if(name[0] && !isdname && !getigraph(name + 1)) return name; static string cname[4]; diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 78325984..543dc6ee 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -749,6 +749,17 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false) break; } + case SV_MATCHSETTINGS: // the server's match settings (sent when they change, and when we join) + { + int n = getint(p); + loopi(n) + { + int v = getint(p); + if(i < NUMMATCHSETTINGS) matchsetting_apply(i, v); + } + break; + } + case SV_DEATHBLAST: // "explosive deaths": the damage comes as normal SV_DAMAGE messages, this is just the visual/sound { int cn = getint(p); diff --git a/source/src/entity.h b/source/src/entity.h index e14587c7..83ee4932 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -87,6 +87,16 @@ extern int matchdeathradius, matchdeathpower; #define DEATHBLASTRAD (EXPDAMRAD*matchdeathradius/100.0f) #define DEATHBLASTDMG (guns[GUN_GRENADE].damage*3/4*matchdeathpower/100) +// Match settings. Online they belong to the server: only an admin can change them (SV_MATCHSET), the server keeps +// the values, sends them to every client when they change and to everybody who joins (SV_MATCHSETTINGS). +// Offline/bot games have no server, so there you simply set them yourself. Defined in server.cpp (which is part of the client too). +struct matchsettingdef { const char *name; int *var; int min, def, max; }; +#define NUMMATCHSETTINGS 14 +extern matchsettingdef matchsettings[NUMMATCHSETTINGS]; +extern int matchsynced[NUMMATCHSETTINGS]; // client side: the values the server last announced +extern void matchsettings_reset(); // everything back to the defaults +extern bool matchsetting_apply(int idx, int val); // clamps and stores, true if the value changed + struct itemstat { int add, start, max, sound; }; extern itemstat ammostats[NUMGUNS]; extern itemstat powerupstats[I_ARMOUR-I_HEALTH+1]; diff --git a/source/src/main.cpp b/source/src/main.cpp index 3aae61a2..540d892a 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -1407,6 +1407,7 @@ int main(int argc, char **argv) banmenu = addmenu("ban player", NULL, true, refreshsopmenu); forceteammenu = addmenu("force team", NULL, true, refreshsopmenu); giveadminmenu = addmenu("give admin", NULL, true, refreshsopmenu); + ((gmenu *)giveadminmenu)->minrole = CR_ADMIN; docmenu = addmenu("reference", NULL, true, renderdocmenu); applymenu = addmenu("apply", "apply changes now?", true, refreshapplymenu); downloaddemomenu = addmenu("Download demo", NULL, true); diff --git a/source/src/menus.cpp b/source/src/menus.cpp index 489226d3..437e2b35 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -39,6 +39,11 @@ void showmenu(const char *name, bool top) } gmenu *m = menus.access(name); if(!m) return; + if(m->minrole > CR_DEFAULT && multiplayer(NULL) && player1->clientrole < m->minrole) + { // admin-only menu (offline/bot games have no admins, there you're the host and may open everything) + conoutf("\f3this menu is for server admins only"); + return; + } if(!top && curmenu) { if(curmenu==m) return; @@ -803,6 +808,7 @@ void *addmenu(const char *name, const char *title, bool allowinput, void (__cdec m->header = m->footer = NULL; m->menusel = 0; } + m->minrole = CR_DEFAULT; m->title = title; DELSTRING(m->mdl); m->allowinput = allowinput; @@ -904,6 +910,14 @@ void menuinit(char *initaction) } COMMAND(menuinit, "s"); +// menuadminonly [role]: the menu that was just created with newmenu can only be opened by admins online (or by players with at least the given role) +void menuadminonly(int *role) +{ + if(!lastmenu) return; + lastmenu->minrole = *role > 0 ? *role : CR_ADMIN; +} +COMMAND(menuadminonly, "i"); + void menuinitselection(int *line) { if(!lastmenu) return; diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index 25070312..d3a010e7 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -621,6 +621,7 @@ static const int msgsizes[] = // size inclusive message token, 0 f SV_PAUSEMODE, 1, SV_GETVITA, 2, SV_VITADATA, 0, SV_DEATHBLAST, 2, + SV_MATCHSET, 3, -1 }; diff --git a/source/src/protocol.h b/source/src/protocol.h index cb6de117..c4e1d4dd 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -7,7 +7,7 @@ #define CUBE_SERVINFO_PORT(serverport) (serverport+1) #define CUBE_SERVINFO_TO_SERV_PORT(servinfoport) (servinfoport-1) #define CLIENTIPCENSOR 12 // blank out the last 12 bits of client's IPs before telling them to others (compromise between "16" from AC 0.9x and "8" from AC 1.1) -#define PROTOCOL_VERSION 1302 // bump when protocol changes (use negative numbers for mods!) - FORK VERSION +#define PROTOCOL_VERSION 1303 // bump when protocol changes (use negative numbers for mods!) - FORK VERSION #define DEMO_VERSION 2 // bump when demo format changes #define DEMO_MAGIC "ASSAULTCUBE_DEMO" #define DEMO_MINTIME 10000 // don't keep demo recordings with less than 10 seconds @@ -52,6 +52,7 @@ enum SV_PAUSEMODE, SV_GETVITA, SV_VITADATA, SV_DEATHBLAST, + SV_MATCHSET, SV_MATCHSETTINGS, SV_NUM }; @@ -225,7 +226,7 @@ enum #define FTXT__MDLATTR (FTXT_NOWHITE | FTXT_ALLOWBLANKS | FTXT_TABTOBLANK | FTXT_NOCOLOR | FTXT_SAFECS) #define FTXT__DEMONAME (FTXT_NOCOLOR | FTXT_MAPNAME | FTXT_ALLOWSLASH) #define FTXT__DEMOINFO (FTXT_NOCOLOR | FTXT_NOWHITE | FTXT_ALLOWBLANKS) -#define FTXT__PLAYERNAME (FTXT_NOWHITE | FTXT_NOCOLOR) +#define FTXT__PLAYERNAME (FTXT_NOWHITE | FTXT_NOCOLOR | FTXT_UTF8) #define FTXT__SERVERINFOLINE (FTXT_ALLOWTAB | FTXT_CROPWHITE_LEAD | FTXT_CROPWHITE_TRAIL) #define FTXT__SERVERINFO (FTXT_ALLOWTAB | FTXT_ALLOWNL) #define FTXT__MOTD (FTXT_TABTOBLANK) diff --git a/source/src/protos.h b/source/src/protos.h index 6a550585..fc71c52f 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -244,8 +244,9 @@ struct gmenu int xoffs, yoffs; char *previewtexture, *previewtexturetitle; mdirlist *dirlist; + int minrole; // CR_DEFAULT = anybody; online, menus with a higher minrole can only be opened by players with at least that role - gmenu() : name(NULL), title(NULL), header(NULL), footer(NULL), menuselinit(-1), initaction(NULL), usefont(NULL), + gmenu() : name(NULL), title(NULL), header(NULL), footer(NULL), menuselinit(-1), initaction(NULL), usefont(NULL), minrole(CR_DEFAULT), allowblink(false), persistentselection(false), synctabstops(false), hasdesc(false), headfootheap(false), mdl(NULL), footlen(0), xoffs(0), yoffs(0), previewtexture(NULL), previewtexturetitle(NULL), dirlist(NULL) {} virtual ~gmenu() @@ -1177,7 +1178,7 @@ struct serverconfigfile struct servercommandline { int uprate, serverport, syslogfacility, filethres, syslogthres, maxdemos, maxclients, kickthreshold, banthreshold, verbose, incoming_limit, afk_limit, ban_time; - const char *ip, *master, *logident, *adminpasswd, *demopath, *maprotfile, *pwdfile, *blfile, *geoipfile, *nbfile, *infopath, *motdpath, *forbidden, *service, *logfilepath, *parfilepath, *ssk, *vitabasename; + const char *ip, *master, *logident, *adminpasswd, *demopath, *maprotfile, *pwdfile, *blfile, *geoipfile, *nbfile, *infopath, *motdpath, *service, *logfilepath, *parfilepath, *ssk, *vitabasename; bool logtimestamp, demo_interm, loggamestatus; string motd, servdesc_full, servdesc_pre, servdesc_suf, voteperm, mapperm; int clfilenesting; @@ -1187,7 +1188,7 @@ struct servercommandline maxclients(DEFAULTCLIENTS), kickthreshold(-5), banthreshold(-6), verbose(0), incoming_limit(10), afk_limit(45000), ban_time(20*60*1000), ip(""), master(NULL), logident(""), adminpasswd(""), demopath("demos/"), maprotfile("config/maprot.cfg"), pwdfile("config/serverpwd.cfg"), blfile("config/serverblacklist.cfg"), geoipfile("config/geoip.cfg"), nbfile("config/nicknameblacklist.cfg"), - infopath("config/serverinfo"), motdpath("config/motd"), forbidden("config/forbidden.cfg"), service(NULL), logfilepath("logs/"), parfilepath("config/serverparameters.cfg"), ssk(NULL), vitabasename("config/servervita"), + infopath("config/serverinfo"), motdpath("config/motd"), service(NULL), logfilepath("logs/"), parfilepath("config/serverparameters.cfg"), ssk(NULL), vitabasename("config/servervita"), logtimestamp(false), demo_interm(false), loggamestatus(true), clfilenesting(0) { @@ -1279,7 +1280,7 @@ struct servercommandline case 'I': infopath = a; break; case 'o': filterrichtext(motd, a); break; case 'O': motdpath = a; break; - case 'g': forbidden = a; break; + case 'g': break; // removed: server profanity filter (forbidden words file) case 'n': { char *t = servdesc_full; diff --git a/source/src/server.cpp b/source/src/server.cpp index 152fc33d..6623ede2 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -44,6 +44,60 @@ int matchdeathradius = 100; // blast radius, % of a grenade's extern int matchdeathpower; int matchdeathpower = 100; // blast damage, % of the default (75% of a grenade's damage at the centre) +// movement/weapon feel: the clients apply these, the server only hands them out +int matchrecoilmul = 100, matchspreadmul = 100, matchspeedmul = 100, matchgravity = 100, matchfriction = 100; + +// The order of this table is part of the protocol (SV_MATCHSET sends an index, SV_MATCHSETTINGS all values in this order). +matchsettingdef matchsettings[NUMMATCHSETTINGS] = +{ + { "matchrecoilmul", &matchrecoilmul, 0, 100, 500 }, + { "matchspreadmul", &matchspreadmul, 0, 100, 500 }, + { "matchspeedmul", &matchspeedmul, 0, 100, 500 }, + { "matchgravity", &matchgravity, 0, 100, 500 }, + { "matchfriction", &matchfriction, 10, 100, 500 }, + { "matchassist", &matchassist, 0, 1, 1 }, + { "matchfriendlyfire", &matchfriendlyfire, 0, 1, 2 }, + { "matchdeathexplosion", &matchdeathexplosion, 0, 0, 1 }, + { "matchdeathradius", &matchdeathradius, 10, 100, 500 }, + { "matchdeathpower", &matchdeathpower, 0, 100, 500 }, + { "matchspawnprimary", &matchspawnprimary, -1, -1, NUMGUNS-1 }, + { "matchspawnsecondary", &matchspawnsecondary, -1, -1, NUMGUNS-1 }, + { "matchspawntertiary", &matchspawntertiary, -1, -1, NUMGUNS-1 }, + { "matchspawnquaternary", &matchspawnquaternary,-1, -1, NUMGUNS-1 } +}; +int matchsynced[NUMMATCHSETTINGS]; + +void matchsettings_reset() +{ + loopi(NUMMATCHSETTINGS) matchsynced[i] = *matchsettings[i].var = matchsettings[i].def; +} + +bool matchsetting_apply(int idx, int val) +{ + if(idx < 0 || idx >= NUMMATCHSETTINGS) return false; + matchsettingdef &m = matchsettings[idx]; + val = clamp(val, m.min, m.max); + matchsynced[idx] = val; + if(*m.var == val) return false; + *m.var = val; + return true; +} +static int __matchsettingsinit = (matchsettings_reset(), 0); + +void putmatchsettings(packetbuf &p) +{ + putint(p, SV_MATCHSETTINGS); + putint(p, NUMMATCHSETTINGS); + loopi(NUMMATCHSETTINGS) putint(p, *matchsettings[i].var); +} + +void sendmatchsettings() +{ + packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + putmatchsettings(p); + sendpacket(-1, 1, p.finalize()); +} + struct servergame { int mastermode; @@ -119,7 +173,6 @@ servermaprot maprot; serveripblacklist ipblacklist; serveripcclist geoiplist; servernickblacklist nickblacklist; -serverforbiddenlist forbiddenlist; serverpasswords passwords; serverinfofile serverinfoinfo; serverinfofile serverinfomotd; @@ -2966,6 +3019,7 @@ void disconnect_client(int n, int reason) if(reason>=0) enet_peer_disconnect(c.peer, reason); clients[n]->zap(); sendf(-1, 1, "rii", SV_CDIS, n); + if(numclients() == 0) matchsettings_reset(); if(curvote) curvote->evaluate(); if(*scoresaved && sg->mastermode == MM_MATCH) senddisconnectedscores(-1); } @@ -3125,6 +3179,7 @@ void welcomepacket(packetbuf &p, int n) putint(p, sendservermode(false)); putint(p, SV_PAUSEMODE); putint(p, sg->sispaused); + putmatchsettings(p); const char *motd = scl.motd[0] ? scl.motd : serverinfomotd.getmsg(); if(*motd) { @@ -3164,7 +3219,7 @@ int checktype(int type, client *cl) SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTERESULT, SV_SETTEAM, SV_TEAMDENY, SV_SERVERMODE, SV_IPLIST, SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, - SV_CLIENT, SV_DEMOCHECKSUM, SV_PAUSEMODE, SV_DEATHBLAST }; + SV_CLIENT, SV_DEMOCHECKSUM, SV_PAUSEMODE, SV_DEATHBLAST, SV_MATCHSETTINGS }; // only allow edit messages in coop-edit mode static int edittypes[] = { SV_EDITENT, SV_EDITXY, SV_EDITARCH, SV_EDITBLOCK, SV_EDITD, SV_EDITE, SV_NEWMAP }; if(cl) @@ -3500,26 +3555,17 @@ void process(ENetPacket *packet, int sender, int chan) filtertext(text, text, FTXT__CHAT); if(*text) { - bool canspeech = forbiddenlist.canspeech(text); - if(!spamdetect(cl, text) && canspeech) // team chat + if(!spamdetect(cl, text)) // team chat { mlog(ACLOG_INFO, "[%s] %s%s says to team %s: '%s'", cl->hostname, type == SV_TEAMTEXTME ? "(me) " : "", cl->name, team_string(cl->team), text); sendteamtext(text, sender, type); } else { - mlog(ACLOG_INFO, "[%s] %s%s says to team %s: '%s', %s", cl->hostname, type == SV_TEAMTEXTME ? "(me) " : "", - cl->name, team_string(cl->team), text, canspeech ? "SPAM detected" : "Forbidden speech"); - if (canspeech) - { - sendservmsg("\f3Please do not spam; your message was not delivered.", sender); - if ( cl->spamcount > SPAMMAXREPEAT + 2 ) disconnect_client(cl->clientnum, DISC_SPAM); - } - else - { - sendservmsg("\f3Watch your language! Your message was not delivered.", sender); - kick_abuser(cl->clientnum, cl->badmillis, cl->badspeech, 3); - } + mlog(ACLOG_INFO, "[%s] %s%s says to team %s: '%s', SPAM detected", cl->hostname, type == SV_TEAMTEXTME ? "(me) " : "", + cl->name, team_string(cl->team), text); + sendservmsg("\f3Please do not spam; your message was not delivered.", sender); + if ( cl->spamcount > SPAMMAXREPEAT + 2 ) disconnect_client(cl->clientnum, DISC_SPAM); } } break; @@ -3532,8 +3578,7 @@ void process(ENetPacket *packet, int sender, int chan) filtertext(text, text, FTXT__CHAT); if(*text) { - bool canspeech = forbiddenlist.canspeech(text); - if(!spamdetect(cl, text) && canspeech) + if(!spamdetect(cl, text)) { if(sg->mastermode != MM_MATCH || !sg->matchteamsize || team_isactive(cl->team) || (cl->team == TEAM_SPECT && cl->role == CR_ADMIN)) // common chat { @@ -3551,18 +3596,10 @@ void process(ENetPacket *packet, int sender, int chan) } else { - mlog(ACLOG_INFO, "[%s] %s%s says: '%s', %s", cl->hostname, type == SV_TEXTME ? "(me) " : "", - cl->name, text, canspeech ? "SPAM detected" : "Forbidden speech"); - if (canspeech) - { - sendservmsg("\f3Please do not spam; your message was not delivered.", sender); - if ( cl->spamcount > SPAMMAXREPEAT + 2 ) disconnect_client(cl->clientnum, DISC_SPAM); - } - else - { - sendservmsg("\f3Watch your language! Your message was not delivered.", sender); - kick_abuser(cl->clientnum, cl->badmillis, cl->badspeech, 3); - } + mlog(ACLOG_INFO, "[%s] %s%s says: '%s', SPAM detected", cl->hostname, type == SV_TEXTME ? "(me) " : "", + cl->name, text); + sendservmsg("\f3Please do not spam; your message was not delivered.", sender); + if ( cl->spamcount > SPAMMAXREPEAT + 2 ) disconnect_client(cl->clientnum, DISC_SPAM); } } break; @@ -3579,8 +3616,7 @@ void process(ENetPacket *packet, int sender, int chan) if(*text) { - bool canspeech = forbiddenlist.canspeech(text); - if(!spamdetect(cl, text) && canspeech) + if(!spamdetect(cl, text)) { bool allowed = !(sg->mastermode == MM_MATCH && cl->team != target->team) && cl->role >= roleconf('t'); mlog(ACLOG_INFO, "[%s] %s says to %s: '%s' (%s)", cl->hostname, cl->name, target->name, text, allowed ? "allowed":"disallowed"); @@ -3592,17 +3628,9 @@ void process(ENetPacket *packet, int sender, int chan) } else { - mlog(ACLOG_INFO, "[%s] %s says to %s: '%s', %s", cl->hostname, cl->name, target->name, text, canspeech ? "SPAM detected" : "Forbidden speech"); - if (canspeech) - { - sendservmsg("\f3Please do not spam; your message was not delivered.", sender); - if ( cl->spamcount > SPAMMAXREPEAT + 2 ) disconnect_client(cl->clientnum, DISC_SPAM); - } - else - { - sendservmsg("\f3Watch your language! Your message was not delivered.", sender); - kick_abuser(cl->clientnum, cl->badmillis, cl->badspeech, 3); - } + mlog(ACLOG_INFO, "[%s] %s says to %s: '%s', SPAM detected", cl->hostname, cl->name, target->name, text); + sendservmsg("\f3Please do not spam; your message was not delivered.", sender); + if ( cl->spamcount > SPAMMAXREPEAT + 2 ) disconnect_client(cl->clientnum, DISC_SPAM); } } } @@ -4292,6 +4320,31 @@ void process(ENetPacket *packet, int sender, int chan) case SV_PAUSEMODE: break; + case SV_MATCHSET: // admin changes a match setting (idx -1 = reset everything to the defaults) + { + int idx = getint(p), val = getint(p); + if(!cl || cl->role < CR_ADMIN) + { + if(cl) { mlog(ACLOG_INFO, "[%s] player %s tried to change a match setting without being admin", cl->hostname, cl->name); sendservmsg("\f3only an admin can change match settings", sender); } + break; + } + defformatstring(msg)("\f2%s", cl->name); + if(idx == -1) + { + matchsettings_reset(); + concatstring(msg, " reset all match settings to the defaults"); + } + else + { + if(idx < 0 || idx >= NUMMATCHSETTINGS || !matchsetting_apply(idx, val)) break; + concatformatstring(msg, " set %s to %d", matchsettings[idx].name, *matchsettings[idx].var); + } + mlog(ACLOG_INFO, "[%s] %s", cl->hostname, msg + 2); + sendmatchsettings(); + sendservmsg(msg); + break; + } + case SV_GETVITA: { int targetcn = getint(p); @@ -5079,7 +5132,6 @@ void initserver(bool dedicated) ipblacklist.init(scl.blfile); geoiplist.init(scl.geoipfile); nickblacklist.init(scl.nbfile); - forbiddenlist.init(scl.forbidden); serverinfoinfo.init(scl.infopath, MAXTRANS / 2); serverinfomotd.init(scl.motdpath, MAXSTRLEN - 5); mlog(ACLOG_VERBOSE, "holding up to %d recorded demos in memory", demo_max_number); diff --git a/source/src/server.h b/source/src/server.h index 545d0b3a..d7cb0059 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -457,7 +457,7 @@ const char *messagenames[SV_NUM] = "SV_EXTENSION", "SV_MAPIDENT", "SV_DEMOCHECKSUM", "SV_DEMOSIGNATURE", "SV_PAUSEMODE", - "SV_GETVITA", "SV_VITADATA", "SV_DEATHBLAST" + "SV_GETVITA", "SV_VITADATA", "SV_DEATHBLAST", "SV_MATCHSET", "SV_MATCHSETTINGS" }; const char *entnames[] = diff --git a/source/src/serverfiles.h b/source/src/serverfiles.h index 088c1732..c9120bb1 100644 --- a/source/src/serverfiles.h +++ b/source/src/serverfiles.h @@ -1400,99 +1400,6 @@ struct servernickblacklist : serverconfigfile } }; -#define FORBIDDENSIZE 15 - -bool issimilar (char s, char d) -{ - s = tolower(s); d = tolower(d); - if ( s == d ) return true; - switch (d) - { - case 'a': if ( s == '@' || s == '4' ) return true; break; - case 'c': if ( s == 'k' ) return true; break; - case 'e': if ( s == '3' ) return true; break; - case 'i': if ( s == '!' || s == '1' ) return true; break; - case 'o': if ( s == '0' ) return true; break; - case 's': if ( s == '$' || s == '5' ) return true; break; - case 't': if ( s == '7' ) return true; break; - case 'u': if ( s == '#' ) return true; break; - } - return false; -} - -bool findpattern (char *s, char *d) // returns true if there is more than 80% of similarity -{ - int len, hit = 0; - if (!d || (len = strlen(d)) < 1) return false; - char *dp = d, *s_end = s + strlen(s); - while (s != s_end) - { - if ( *s == ' ' ) // spaces separate words - { - if ( !issimilar(*(s+1),*dp) ) { dp = d; hit = 0; } // d e t e c t i t - } - else if ( issimilar(*s,*dp) ) { dp++; hit++; } // hit! - else if ( hit > 0 ) // this is not a pair, but there is a previous pattern - { - if (*s == '.' || *s == *(s-1) || issimilar(*(s+1),*dp) ); // separator or typo (do nothing) - else if ( issimilar(*(s+1),*(dp+1)) || *s == '*' ) dp++; // wild card or typo - else hit--; // maybe this is nothing - } - else dp = d; // nothing here - s++; // walk on the string - if ( hit && 5 * hit > 4 * len ) return true; // found it! - } - return false; -} - -struct serverforbiddenlist : serverconfigfile -{ - int num; - char entries[100][2][FORBIDDENSIZE+1]; // 100 entries and 2 words per entry is more than enough - - void clear() - { - num = 0; - memset(entries,'\0',2*100*(FORBIDDENSIZE+1)); - } - - void addentry(char *s) - { - int len = strlen(s); - if ( len > 128 || len < 3 ) return; - int n = 0; - string s1, s2; - char *c1 = s1, *c2 = s2; - if (num < 100 && (n = sscanf(s,"%s %s",s1,s2)) > 0 ) // no warnings - { - copystring(entries[num][0], c1, FORBIDDENSIZE); - if ( n > 1 ) copystring(entries[num][1], c2, FORBIDDENSIZE); - else entries[num][1][0]='\0'; - num++; - } - } - - void read() - { - char *l, *p = buf; - mlog(ACLOG_VERBOSE,"reading forbidden list '%s'", filename); - while(p < buf + filelen) - { - l = p; p += strlen(p) + 1; - addentry(l); - } - } - - bool canspeech(char *s) - { - for (int i=0; i : change a match setting. Online this is a request to the server, which only admins may make. +void matchset(char *name, int *val) +{ + int idx = -1; + loopi(NUMMATCHSETTINGS) if(!strcmp(name, matchsettings[i].name)) { idx = i; break; } + if(idx < 0) { conoutf("unknown match setting \"%s\"", name); return; } + if(multiplayer(NULL)) + { + if(player1->clientrole < CR_ADMIN) { conoutf("\f3only the server admin can change match settings"); return; } + addmsg(SV_MATCHSET, "rii", idx, *val); + } + else matchsetting_apply(idx, *val); +} +COMMAND(matchset, "si"); + +void matchsetreset() +{ + if(multiplayer(NULL)) + { + if(player1->clientrole < CR_ADMIN) { conoutf("\f3only the server admin can change match settings"); return; } + addmsg(SV_MATCHSET, "rii", -1, 0); + } + else matchsettings_reset(); +} +COMMAND(matchsetreset, ""); // "explosive deaths": effect shown where somebody died (also used for the blast sent by the server) void deathblasteffect(const vec &pos) diff --git a/source/vcpp/assaultcube.vcxproj b/source/vcpp/assaultcube.vcxproj index e7ef1c3d..da1bf793 100644 --- a/source/vcpp/assaultcube.vcxproj +++ b/source/vcpp/assaultcube.vcxproj @@ -465,7 +465,6 @@ - diff --git a/source/vcpp/assaultcube.vcxproj.filters b/source/vcpp/assaultcube.vcxproj.filters index e01c9948..4af0af45 100644 --- a/source/vcpp/assaultcube.vcxproj.filters +++ b/source/vcpp/assaultcube.vcxproj.filters @@ -168,9 +168,6 @@ cfg - - cfg - cfg