]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/clientcommands.qc
renamed and moved all gamec/*.[ch] files to *.q[ch]
[divverent/nexuiz.git] / data / qcsrc / server / clientcommands.qc
1 void ReadyCount();
2 float ValidateMap(string vote);
3
4 void SV_ParseClientCommand(string s) {
5         local float index;
6
7         tokenize(s);
8
9         if(argv(0) == "clogin") {
10                 if(cvar("sv_clientcommands")) {
11                         if(self.adminstatus < -5) {
12                                 sprint(self, "Too many unsuccessful tries.\n");
13                         } else if(argv(1) == cvar_string("sv_clientcommands_password")) {
14                                 self.adminstatus = 1;
15                                 sprint(self, "You now have remote admin status.\n");
16                                 ServerConsoleEcho(strcat("ClientCommands: ", self.netname, " received admin status"), TRUE);
17                         } else {
18                                 sprint(self, "Wrong password.\n");
19                                 // use of -- produces compiler warning in the if() line???
20                                 self.adminstatus = self.adminstatus - 1;
21                                 if(self.adminstatus == 0)
22                                 {
23                                         sprint(self, "You lost remote admin status.\n");
24                                         ServerConsoleEcho(strcat("ClientCommands: ", self.netname, " lost admin status"), TRUE);
25                                 }
26                         }
27                 } else {
28                         sprint(self, "Clientside commands NOT allowed.\n");
29                 }
30         } else if(argv(0) == "ccmd") {
31                 if(cvar("sv_clientcommands")) {
32                         if(self.adminstatus > 0) {
33                                 local string command;
34                                 command = argv(1);
35                                 index = 2;
36                                 while(argv(index) != "") {
37                                         command = strcat(command, " ", argv(index));
38                                         index++;
39                                 }
40                                 ServerConsoleEcho(strcat("ClientCommands: ", self.netname, " issued command '", command, "'"), TRUE);
41                                 localcmd(strcat(command, "\n"));
42                         } else
43                                 sprint(self, "You don't have remote admin status.\n");
44                 } else {
45                         sprint(self, "Clientside commands NOT allowed.\n");
46                 }
47         } else if(argv(0) == "vote") {
48                 if(argv(1) == "help") {
49                         local string vmasterdis;
50                         if(!cvar("sv_vote_master")) {
51                                 vmasterdis = " ^1(disabled)";
52                         }
53                         local string vcalldis;
54                         if(!cvar("sv_vote_call")) {
55                                 vcalldis = " ^1(disabled)";
56                         }
57                         sprint(self, "^7You can use voting with \"^2cmd vote help^7\" \"^2cmd vote status^7\" \"^2cmd vote call ^3COMMAND ARGUMENTS^7\" \"^2cmd vote stop^7\" \"^2cmd vote master^7\" \"^2cmd vote do ^3COMMAND ARGUMENTS^7\" \"^2cmd vote yes^7\" \"^2cmd vote no^7\".\n");
58                         sprint(self, "^7Or if your version is up to date you can use these aliases \"^2vhelp^7\" \"^2vstatus^7\" \"^2vcall ^3COMMAND ARGUMENTS^7\" \"^2vstop^7\" \"^2vmaster^7\" \"^2vdo ^3COMMAND ARGUMENTS^7\" \"^2vyes^7\" \"^2vno^7\".\n");
59                         sprint(self, "^7\"^2help^7\" shows this info.\n");
60                         sprint(self, "^7\"^2status^7\" shows if there is a vote called and who called it.\n");
61                         sprint(self, strcat("^7\"^2call^7\" is used to call a vote. See the list of allowed commands.", vcalldis, "^7\n"));
62                         sprint(self, "^7\"^2stop^7\" can be used by the vote caller or an admin to stop a vote and maybe correct it.\n");
63                         sprint(self, strcat("^7\"^2master^7\" is used to call a vote to become a master.", vmasterdis, "^7\n"));
64                         sprint(self, "^7\"^2do^7\" If you are a master you can execute a command without a vote. See the list of allowed commands.\n");
65                         sprint(self, "^7\"^2yes^7\" and \"^2no^7\" to make your vote.\n");
66                         sprint(self, "^7If more then 50% of the players vote yes the vote is accepted.\n");
67                         sprint(self, "^7If more then 50% of the players vote no the vote is rejected.\n");
68                         sprint(self, strcat("^7The vote will end after ", cvar_string("sv_vote_timeout"), "^7 seconds.\n"));
69                         sprint(self, "^7You can call a vote for or execute these commands:\n");
70                         sprint(self, strcat("^3", cvar_string("sv_vote_commands"), "^7 and maybe further ^3arguments^7\n"));
71                 } else if(argv(1) == "status") {
72                         if(votecalled) {
73                                 sprint(self, strcat("^7Vote for \"^1", votecalledvote, "^7\" called by \"^7", votecaller.netname, "^7\".\n"));
74                         } else {
75                                 sprint(self, "^1No vote called.\n");
76                         }
77                 } else if(argv(1) == "call") {
78                         if(cvar("sv_vote_call")) {
79                                 if(votecalled) {
80                                         sprint(self, "^1There is already a vote called.\n");
81                                 } else {
82                                         local string vote;
83                                         vote = VoteParse();
84                                         if(vote == "") {
85                                                 sprint(self, "^1Your vote is empty. See help for more info.\n");
86                                         } else if(time < self.vote_next) {
87                                                 sprint(self, strcat("^1You have to wait ^2", ftos(self.vote_next - time), "^1 seconds before you can again call a vote.\n"));
88                                         } else if(VoteAllowed(strcat(argv(2)))) { // strcat seems to be necessary
89                                                 if(!ValidateMap(vote))
90                                                         return;
91                                                 votecalled = TRUE;
92                                                 votecalledmaster = FALSE;
93                                                 votecalledvote = strzone(vote);
94                                                 votecaller = self; // remember who called the vote
95                                                 votefinished = time + cvar("sv_vote_timeout");
96                                                 votecaller.vote_vote = 1; // of course you vote yes
97                                                 votecaller.vote_next = time + cvar("sv_vote_wait");
98                                                 bprint(strcat("^3Vote for \"^1", votecalledvote, "^3\" called by \"^7", votecaller.netname, "^3\".\n"));
99                                                 VoteCount(); // needed if you are the only one
100                                         } else {
101                                                 sprint(self, "^1This vote is not ok. See help for more info.\n");
102                                         }
103                                 }
104                         } else {
105                                 sprint(self, "^1Vote calling is NOT allowed.\n");
106                         }
107                 } else if(argv(1) == "stop") {
108                         if(!votecalled) {
109                                 sprint(self, "^1No vote called.\n");
110                         } else if(self == votecaller
111                                   || self.adminstatus > 0) { // the votecaller and admins can stop a vote
112                                 VoteStop(self);
113                         } else {
114                                 sprint(self, "^1You are not allowed to stop that Vote.\n");
115                         }
116                 } else if(argv(1) == "master") {
117                         if(cvar("sv_vote_master")) {
118                                 if(votecalled) {
119                                         sprint(self, "^1There is already a vote called.\n");
120                                 } else {
121                                         votecalled = TRUE;
122                                         votecalledmaster = TRUE;
123                                         votecalledvote = strzone("^3master");
124                                         votecaller = self; // remember who called the vote
125                                         votefinished = time + cvar("sv_vote_timeout");
126                                         votecaller.vote_vote = 1; // of course you vote yes
127                                         votecaller.vote_next = time + cvar("sv_vote_wait");
128                                         bprint(strcat("\"^3", votecaller.netname, "^3\" called a vote to become ^3master^3.\n"));
129                                         VoteCount(); // needed if you are the only one
130                                 }
131                         } else {
132                                 sprint(self, "^1Vote to become master is NOT allowed.\n");
133                         }
134                 } else if(argv(1) == "do") {
135                         if(self.vote_master) {
136                                 local string dovote;
137                                 dovote = VoteParse();
138                                 if(dovote == "") {
139                                         sprint(self, "^1Your command was empty. See help for more info.\n");
140                                 } else if(VoteAllowed(strcat(argv(2)))) { // strcat seems to be necessary
141                                         bprint("\"^7", strcat(self.netname, "^2 used his ^3master^2 status to do \"^2", dovote, "^2\".\n"));
142                                         localcmd(strcat(dovote, "\n"));
143                                 } else {
144                                         sprint(self, "^1This command is not ok. See help for more info.\n");
145                                 }
146                         } else {
147                                 sprint(self, "^1You are NOT a master.\n");
148                         }
149                 } else if(argv(1) == "yes") {
150                         if(!votecalled) {
151                                 sprint(self, "^1No vote called.\n");
152                         } else if(self.vote_vote == 0
153                                   || cvar("sv_vote_change")) {
154                                 sprint(self, "^1You accepted the vote.\n");
155                                 self.vote_vote = 1;
156                                 if(!cvar("sv_vote_singlecount")) {
157                                         VoteCount();
158                                 }
159                         } else {
160                                 sprint(self, "^1You have already voted.\n");
161                         }
162                 } else if(argv(1) == "no") {
163                         if(!votecalled) {
164                                 sprint(self, "^1No vote called.\n");
165                         } else if(self.vote_vote == 0
166                                   || cvar("sv_vote_change")) {
167                                 sprint(self, "^1You rejected the vote.\n");
168                                 self.vote_vote = -1;
169                                 if(!cvar("sv_vote_singlecount")) {
170                                         VoteCount();
171                                 }
172                         } else {
173                                 sprint(self, "^1You have already voted.\n");
174                         }
175                 } else {
176                         // ignore this?
177                         sprint(self, "^1Unknown vote command.\n");
178                 }
179         } else if(argv(0) == "autoswitch") {
180                 // be backwards compatible with older clients (enabled)
181                 self.autoswitch = ("0" != argv(1));
182                 local string autoswitchmsg;
183                 if (self.autoswitch) {
184                         autoswitchmsg = "on";
185                 } else {
186                         autoswitchmsg = "off";
187                 }
188                 sprint(self, strcat("^1autoswitch turned ", autoswitchmsg, "\n"));
189         } else if(argv(0) == "clientversion") {
190                 if (argv(1) == "$gameversion") {
191                         //versionmsg = "^1client is too old to get versioninfo.\nUPDATE!!! (http://www.nexuiz.com)^8";
192                         // either that or someone wants to be funny
193                         self.version = 1;
194                 } else {
195                         self.version = stof(argv(1));
196                 }
197                 if(self.version != cvar("gameversion")) 
198                 {
199                         self.classname = "observer";
200                         self.frags = -2;
201                         PutClientInServer();
202                 } else if(cvar("g_campaign") || cvar("g_balance_teams")) {
203                         //JoinBestTeam(self, 0);
204                 } else if(cvar("teamplay") && !cvar("sv_spectate")) {
205                         self.classname = "observer";
206                         stuffcmd(self,"menu_showteamselect\n");
207                 }
208         } else if(argv(0) == "reportcvar") {
209                 GetCvars(1);
210         } else if(argv(0) == "spectate") {
211                 if(cvar("g_lms") || cvar("g_arena"))
212                         return; // don't allow spectating in lms, unless player runs out of lives
213                 if(self.classname == "player" && cvar("sv_spectate") == 1) {
214                         self.classname = "observer";
215                         PutClientInServer();
216                 }
217         } else if(argv(0) == "join") {
218                 if(!cvar("g_arena"))
219                 if (self.classname != "player")
220                 {
221                         self.classname = "player";
222                         self.frags = 0;
223                         bprint (strcat("^4", self.netname, "^4 is playing now\n"));
224                         PutClientInServer();
225                 }
226         } else if( argv(0) == "selectteam" ) {
227                 if( !cvar("teamplay") ) {
228                         sprint( self, "selecteam can only be used in teamgames\n");
229                 } else if(cvar("g_campaign")) {
230                         //JoinBestTeam(self, 0);
231                 } else if( argv(1) == "none" ) {
232                         SV_ChangeTeam( 0 );
233                 } else if( argv(1) == "red" ) {
234                         SV_ChangeTeam( COLOR_TEAM1 - 1 );
235                 } else if( argv(1) == "blue" ) {
236                         SV_ChangeTeam( COLOR_TEAM2 - 1 );
237                 } else if( argv(1) == "pink" ) {
238                         SV_ChangeTeam( COLOR_TEAM3 - 1 );
239                 } else if( argv(1) == "yellow" ) {
240                         SV_ChangeTeam( COLOR_TEAM4 - 1 );
241                 } else if( argv(1) == "auto" ) {
242                         self.team = -1;
243                         JoinBestTeam( self, 0 );
244                 } else {
245                         sprint( self, strcat( "selectteam none/red/blue/pink/yellow/auto - \"", argv(1), "\" not recognised\n" ) );
246                 }
247         } else if(argv(0) == "ready") {
248                 if(cvar("sv_ready_restart"))
249                 {
250                         self.ready = TRUE;
251                         bprint(self.netname, "^2 is ready\n");
252                         ReadyCount();
253                 }
254         } else if(argv(0) == "say") {
255                 clientcommand(self, formatmessage(s));
256         } else if(argv(0) == "say_team") {
257                 clientcommand(self, formatmessage(s));
258         } else {
259                 clientcommand(self,s);
260         }
261 }
262
263 float ValidateMap(string vote)
264 {
265         string ext;
266         
267         tokenize(vote);
268         if(argv(0) == "map" || argv(0) == "changelevel")
269                 ext = ".bsp";
270         else if(argv(0) == "chmap")
271                 ext = ".mapcfg";
272         else
273                 return TRUE;
274
275         if(!TryFile(strcat("maps/", argv(1), ext)))
276         {
277                 sprint(self, strcat("^1Invalid mapname, \"^3", argv(1), "^1\" does not exist on this server.\n"));
278                 return FALSE;
279         }
280         return TRUE;
281 }
282
283
284 void VoteThink() {
285         if(votefinished > 0 // a vote was called
286             && time > votefinished) // time is up
287         {
288                 VoteCount();
289         }
290 }
291
292 string VoteParse() {
293         local float index;
294         index = 3;
295         local string vote;
296         vote = argv(2);
297         while(argv(index) != "") {
298                 vote = strcat(vote, " ", argv(index));
299                 index++;
300         }
301
302         // necessary for some of the string operations
303         vote = strzone(vote);
304
305         // now we remove some things that could be misused
306         index = 0;
307         local float found;
308         found = FALSE;
309         local float votelength;
310         votelength = strlen(vote);
311         while(!found && index < votelength)
312         {
313                 local string badchar;
314                 badchar = substring(vote, index, 1);
315                 if(badchar == ";"
316                    || badchar == "\r"
317                    || badchar == "\n")
318                 {
319                         found = TRUE;
320                 } else {
321                         index++;
322                 }
323         }
324         return substring(vote, 0, index);
325 }
326
327 float VoteAllowed(string votecommand) {
328         tokenize(cvar_string("sv_vote_commands"));
329         local float index;
330         index = 0;
331         while(argv(index) != "") {
332                 local string allowed;
333                 allowed = argv(index);
334                 if(votecommand == allowed) {
335                         return TRUE;
336                 }
337                 index++;
338         }
339         return FALSE;
340 }
341
342 void VoteReset() {
343         local string searchclass;
344         searchclass = "player";
345
346         while (TRUE)
347         {
348                 local entity player;
349                 player = find(player, classname, searchclass);
350                 while(player)
351                 {
352                         player.vote_vote = 0;
353                         player = find(player, classname, searchclass);
354                 }
355
356                 if("player" == searchclass) {
357                         searchclass = "observer";
358                 } else if("observer" == searchclass) {
359                         searchclass = "spectator";
360                 } else {
361                         break;
362                 }
363         }
364
365         votecalled = FALSE;
366         votecalledmaster = FALSE;
367         votefinished = 0;
368 }
369
370 void VoteAccept() {
371         bprint(strcat("^2The vote for \"^1", votecalledvote, "^2\" from \"^7", votecaller.netname, "^2\" was accepted.\n"));
372         if(votecalledmaster)
373         {
374                 votecaller.vote_master = 1;
375         } else {
376                 localcmd(strcat(votecalledvote, "\n"));
377         }
378         votecaller.vote_next = 0; // people like your votes, no wait for next vote
379         VoteReset();
380 }
381
382 void VoteReject() {
383         bprint(strcat("^2The vote for \"^1", votecalledvote, "^2\" from \"^7", votecaller.netname, "^2\" was rejected.\n"));
384         VoteReset();
385 }
386
387 void VoteTimeout() {
388         bprint(strcat("^5The vote for \"^1", votecalledvote, "^5\" from \"^7", votecaller.netname, "^5\" did timeout.\n"));
389         VoteReset();
390 }
391
392 void VoteStop(entity stopper) {
393         bprint(strcat("^5The vote for \"^1", votecalledvote, "^5\" from \"^7", votecaller.netname, "^5\" was stopped by \"^5", stopper.netname, "^5\".\n"));
394         if(stopper == votecaller) {
395                 // no wait for next vote so you can correct your vote
396                 votecaller.vote_next = 0;
397         }
398         VoteReset();
399 }
400
401 void VoteCount() {
402         local float playercount;
403         playercount = 0;
404         local float yescount;
405         yescount = 0;
406         local float nocount;
407         nocount = 0;
408         local string searchclass;
409         searchclass = "player";
410
411         while (TRUE)
412         {
413                 local entity player;
414                 player = find(player, classname, searchclass);
415
416                 while(player)
417                 {
418                         if(clienttype(player) != CLIENTTYPE_BOT) {
419                                 if(player.vote_vote < 0) {
420                                         nocount++;
421                                 } else if(player.vote_vote > 0) {
422                                         yescount++;
423                                 }
424                                 playercount++;
425                         }
426                         player = find(player, classname, searchclass);
427                 }
428
429                 if("player" == searchclass) {
430                         searchclass = "observer";
431                 } else if("observer" == searchclass) {
432                         searchclass = "specator";
433                 } else {
434                         break;
435                 }
436         }
437
438         if((playercount == 1) && votecalledmaster) {
439                 // if only one player is on the server becoming vote
440                 // master is not allowed.  This could be used for
441                 // trolling or worse. 'self' is the user who has
442                 // called the vote because this function is called
443                 // by SV_ParseClientCommand. Maybe all voting should
444                 // be disabled for a single player?
445                 sprint(self, "^1You are the only player on this server so you can not become vote master.\n");
446                 votecaller.vote_next = 0;
447                 VoteReset();
448         } else if((playercount / 2) < yescount) { // vote accepted
449                 VoteAccept();
450         } else if((playercount / 2) < nocount) { // vote rejected
451                 VoteReject();
452         } else if(time > votefinished) { // vote timedout
453                 VoteTimeout();
454         } // else still running
455 }
456
457
458 void ReadyCount()
459 {
460         local entity e;
461         local float r, p;
462
463         e = find(world, classname, "player");
464
465         while(e)
466         {
467                 if(clienttype(e) == CLIENTTYPE_REAL)
468                 {
469                         p += 1;
470                         if(e.ready) r += 1;
471                 }
472                 e = find(e, classname, "player");
473         }
474
475         if(p && r == p)
476         {
477                 bprint("^1Server is restarting...\n");
478                 localcmd("restart\n");
479         }
480 }