1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // Auxiliary Software class library
6 //
7 // based on the work of JasonB
8 // enhanced by Dark Charlie and Albert Zeyer
9 //
10 // code under LGPL
11 //
12 /////////////////////////////////////////
13
14
15 // Command/variable parsing
16 // Created 9/4/02
17 // Jason Boettcher
18
19
20 #include <limits.h>
21 #include "LieroX.h"
22 #include "Debug.h"
23 #include "CServer.h"
24 #include "CClient.h"
25 #include "console.h"
26 #include "StringUtils.h"
27 #include "sex.h"
28 #include "CWorm.h"
29 #include "AuxLib.h"
30 #include "Version.h"
31 #include "CClientNetEngine.h"
32 #include "DeprecatedGUI/Menu.h"
33 #include "IRC.h"
34 #include "ProfileSystem.h"
35 #include "FindFile.h"
36 #include "DedicatedControl.h"
37 #include "CGameMode.h"
38 #include "Cache.h"
39 #include "CServerConnection.h"
40 #include "CServerNetEngine.h"
41 #include "CChannel.h"
42 #include "IpToCountryDB.h"
43 #include "Unicode.h"
44 #include "Autocompletion.h"
45 #include "Command.h"
46 #include "TaskManager.h"
47 #include "StringUtils.h"
48
49
stdoutCLI()50 CmdLineIntf& stdoutCLI() {
51 struct StdoutCLI : CmdLineIntf {
52 virtual void pushReturnArg(const std::string& str) {
53 notes << "Ret: " << str << endl;
54 }
55
56 virtual void finalizeReturn() {
57 notes << "Ret." << endl;
58 }
59
60 virtual void writeMsg(const std::string& msg, CmdLineMsgType type) {
61 Logger* l = ¬es;
62 switch(type) {
63 case CNC_NORMAL: break;
64 case CNC_WARNING: l = &warnings; break;
65 case CNC_ERROR: l = &errors; break;
66 case CNC_NOTIFY: l = &hints; break;
67 default: (*l) << CmdLineMsgTypeAsString(type).substr(0,1) << ": ";
68 }
69 (*l) << msg << endl;
70 }
71
72 };
73 static StdoutCLI cli;
74 return cli;
75 }
76
ParseParams_Seps(const std::string & params)77 ParamSeps ParseParams_Seps(const std::string& params) {
78 bool quote = false;
79 size_t start = 0;
80 ParamSeps res;
81 ParamSeps::iterator lastentry = res.begin();
82
83 const_string_iterator i (params);
84 for(; i.pos < params.size(); IncUtf8StringIterator(i, const_string_iterator(params, params.size()))) {
85
86 // Check delimeters
87 if(*i == ' ' || *i == ',') {
88 if(start < i.pos)
89 lastentry = res.insert(lastentry, ParamSeps::value_type(start, i.pos - start));
90 start = i.pos + 1;
91
92 continue;
93 }
94
95 // TODO: do we really want this?
96 // Check comments
97 if (i.pos + 1 < params.size()) {
98 if(*i == '/' && params[i.pos+1] == '/') {
99 if(start < i.pos)
100 lastentry = res.insert(lastentry, ParamSeps::value_type(start, i.pos - start));
101 start = params.size() + 1;
102
103 // Just end here
104 break;
105 }
106 }
107
108 // Check quotes
109 if(*i == '"') {
110 quote = true;
111 i.pos++;
112 start = i.pos;
113
114 // Read until another quote
115 for(; i.pos < params.size(); IncUtf8StringIterator(i, const_string_iterator(params, params.size()))) {
116 if(*i == '"') {
117 quote = false;
118 break;
119 }
120 }
121 if(quote) break; // if we are still in the quote, break (else we would make an addition i++ => crash)
122 lastentry = res.insert(lastentry, ParamSeps::value_type(start, i.pos - start));
123 start = i.pos + 1;
124 continue;
125 }
126
127 // check brackets (handle it like quotes but include them)
128 if(*i == '(') {
129 quote = true;
130 i.pos++;
131
132 // Read until all brackets closed
133 unsigned int bracketDepth = 1;
134 for(; i.pos < params.size(); IncUtf8StringIterator(i, const_string_iterator(params, params.size()))) {
135 if(*i == '(') bracketDepth++;
136 else if(*i == ')') { bracketDepth--; if(bracketDepth == 0) break; }
137 }
138 if(bracketDepth > 0) break; // if we are still in the bracket, break (else we would make an addition i++ => crash)
139 quote = false;
140 continue;
141 }
142 }
143
144 // Add the last token
145 if(start < params.size()) {
146 lastentry = res.insert(lastentry, ParamSeps::value_type(start, params.size() - start));
147 }
148
149 return res;
150 }
151
152
ParseParams(const std::string & params)153 std::vector<std::string> ParseParams(const std::string& params) {
154 std::vector<std::string> res;
155 ParamSeps seps = ParseParams_Seps(params);
156 for(ParamSeps::iterator i = seps.begin(); i != seps.end(); ++i)
157 res.push_back(params.substr(i->first, i->second));
158 return res;
159 }
160
161
162
163
164 ///////////////////
165 // Converts ID as string to an integer, returns -1 on fail
166 /*static int atoid(const std::string& str)
167 {
168 // Ignore any non-numerical characters before the actual number, in most cases this is #
169 std::string::const_iterator it = str.begin();
170 while (it != str.end()) {
171 if (*it >= 48 && *it <= 57)
172 break;
173
174 it++;
175 }
176
177 // Convert the string to a number
178 std::string id = std::string(it, str.end());
179 if (id.size() == 0)
180 return -1;
181
182 bool fail;
183 int res = from_string<int>(id, fail);
184
185 if (fail)
186 return -1;
187 else
188 return res;
189 }
190 */
191
192
193 /*
194 ======================================
195
196 Commands
197
198 ======================================
199 */
200
201
202
203
204
205 struct AutocompleteRequest {
206 CmdLineIntf& cli;
207 AutocompletionInfo& autocomplete;
208 AutocompletionInfo::InputState old;
209
210 std::string pretxt, posttxt;
211 std::string token;
212 size_t tokenpos;
213
emptyStartAutocompleteRequest214 static bool emptyStart(char c) {
215 return c == ' ' || c == '\"' || c == ',';
216 }
217
completeSuggestionAutocompleteRequest218 AutocompletionInfo::InputState completeSuggestion(const std::string& repl, bool isCompleteSuggestion) const {
219 AutocompletionInfo::InputState ret;
220 ret.text = pretxt;
221 ret.pos = pretxt.size() + repl.size();
222 bool needToAddPostQuotes = false;
223 bool hadPreQuotes = pretxt.size() > 0 && pretxt[pretxt.size()-1] == '\"';
224 if(repl.find_first_of(" ,\t") != std::string::npos || hadPreQuotes) { // we must have quotes
225 if(!hadPreQuotes) { // no pre-quotes
226 // we need to put pre-quotes
227 ret.text += '\"';
228 ret.pos++;
229 }
230 if(posttxt.size() == 0 || posttxt[0] != '\"') // no post-quotes
231 needToAddPostQuotes = true;
232 }
233 ret.text += repl;
234 if(needToAddPostQuotes) ret.text += "\"";
235 if(posttxt.size() > 0 && !emptyStart(posttxt[0])) {
236 ret.text += " ";
237 if(isCompleteSuggestion) { ret.pos++; if(needToAddPostQuotes) ret.pos++; }
238 }
239 else if(posttxt.size() > 0 && emptyStart(posttxt[0])) {
240 if(isCompleteSuggestion) { ret.pos++; if(needToAddPostQuotes) ret.pos++; }
241 }
242 else if(posttxt.empty() && isCompleteSuggestion) {
243 ret.text += " ";
244 ret.pos++;
245 if(needToAddPostQuotes) ret.pos++;
246 }
247 ret.text += posttxt;
248 return ret;
249 }
250
251 };
252
253
254
255
256 // TODO: Move this
CheckWorm(CmdLineIntf * caller,int id,const std::string & request)257 static CWorm* CheckWorm(CmdLineIntf* caller, int id, const std::string& request)
258 {
259 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
260 caller->writeMsg(request + " works only as server");
261 return NULL;
262 }
263
264 if(id <0 || id >= MAX_WORMS) {
265 caller->writeMsg(request + " : Faulty ID " + itoa(id));
266 return NULL;
267 }
268 CWorm *w = cServer->getWorms() + id;
269 if(!w->isUsed()) {
270 caller->writeMsg(request + " : ID " + itoa(id) + " not in use");
271 return NULL;
272 }
273 return w;
274 }
275
276
277
278 class Command;
279 typedef bool (*AutoCompleteFct) (Command*, AutocompleteRequest&);
280
281
282 class Command {
283 public:
~Command()284 virtual ~Command() {}
285
286 std::string name;
287 std::string desc;
288 std::string usage;
289 unsigned int minParams, maxParams;
290 std::map<unsigned int, AutoCompleteFct> paramCompleters;
291 bool hidden;
292
minMaxStr() const293 std::string minMaxStr() const {
294 if(minParams == maxParams) return itoa(minParams);
295 std::string s = itoa(minParams) + "-";
296 if(maxParams == UINT_MAX) s += "*";
297 else s += itoa(maxParams);
298 return s;
299 }
300
usageStr() const301 std::string usageStr() const {
302 if(usage != "") return name + " " + usage;
303 else return name;
304 }
305
printUsage(CmdLineIntf * caller)306 void printUsage(CmdLineIntf* caller) {
307 caller->writeMsg("usage: " + usageStr());
308 }
309
fullDesc() const310 std::string fullDesc() const {
311 return usageStr() + " - " + desc;
312 }
313
exec(CmdLineIntf * caller,const std::string & params)314 void exec(CmdLineIntf* caller, const std::string& params) {
315 std::vector<std::string> ps = ParseParams(params);
316
317 // if we want max 1 param but we give more, this is a small hack to dont be too strict
318 if(maxParams == 1 && ps.size() > 1) {
319 for(size_t i = 1; i < ps.size(); ++i)
320 ps[0] += " " + ps[i];
321 ps.resize(1);
322 }
323
324 if(ps.size() < minParams || ps.size() > maxParams) {
325 caller->writeMsg(minMaxStr() + " param" + ((maxParams == 1) ? "" : "s") + " needed, usage: " + usageStr(), CNC_DEV);
326 //caller->writeMsg("bad cmd: " + name + " " + params);
327 return;
328 }
329
330 exec(caller, ps);
331 }
332
completeParam(unsigned int i,AutocompleteRequest & request)333 bool completeParam(unsigned int i, AutocompleteRequest& request) {
334 if(i + 1 > maxParams) {
335 if(request.token != "")
336 request.cli.writeMsg(name + " takes " + minMaxStr() + " param" + ((maxParams == 1) ? "" : "s") + ", " + itoa(i + 1) + " given", CNC_DEV);
337 else
338 request.cli.writeMsg(name + " - " + desc, CNC_DEV);
339 return false;
340 }
341
342 std::map<unsigned int, AutoCompleteFct>::iterator p = paramCompleters.find(i);
343 if(p != paramCompleters.end()) return (*p->second)(this, request);
344
345 // no custom completer, so just write usage
346 request.cli.writeMsg(fullDesc(), CNC_DEV);
347 return false;
348 }
349
getWorm(CmdLineIntf * caller,const std::string & param)350 CWorm* getWorm(CmdLineIntf* caller, const std::string& param) {
351 bool fail = true;
352 int id = from_string<int>(param, fail);
353 if(fail) {
354 printUsage(caller);
355 return NULL;
356 }
357 return CheckWorm(caller, id, name);
358 }
359
360 protected:
361 virtual void exec(CmdLineIntf* caller, const std::vector<std::string>& params) = 0;
362
363 };
364
365 typedef std::map<std::string, Command*, stringcaseless> CommandMap;
366 static CommandMap commands;
367
368
369
370 template <typename _List>
autoCompleteForList(AutocompleteRequest & request,const std::string & unitname,const _List & unitlist)371 static bool autoCompleteForList(AutocompleteRequest& request, const std::string& unitname, const _List& unitlist) {
372 if(request.token == "") {
373 request.cli.writeMsg("please enter a " + unitname, CNC_DEV);
374 return false;
375 }
376
377 typedef typename _List::const_iterator iterator;
378 iterator it = unitlist.lower_bound(request.token);
379 if(it == unitlist.end()) {
380 request.cli.writeMsg("no such " + unitname, CNC_WARNING);
381 return false;
382 }
383
384 if(!subStrCaseEqual(it->first, request.token, request.token.size())) {
385 request.cli.writeMsg("no such " + unitname, CNC_WARNING);
386 return false;
387 }
388
389 std::list<std::string> possibilities;
390
391 for(iterator j = it; j != unitlist.end(); ++j) {
392 if(subStrCaseEqual(request.token, j->first, request.token.size()))
393 possibilities.push_back(j->first);
394 else
395 break;
396 }
397
398 if(possibilities.size() == 0) {
399 // strange though..
400 request.cli.writeMsg("unknown " + unitname, CNC_WARNING);
401 return false;
402 }
403
404 if(possibilities.size() == 1) {
405 // we have exactly one solution
406 request.autocomplete.setReplace(request.old, request.completeSuggestion(possibilities.front(), true));
407 return true;
408 }
409
410 size_t l = maxStartingCaseEqualStr(possibilities);
411 if(l > request.token.size()) {
412 // we can complete to some longer sequence
413 request.autocomplete.setReplace(request.old, request.completeSuggestion(possibilities.front().substr(0,l), false));
414 return true;
415 }
416
417 // send list of all possibilities
418 std::string possStr;
419 for(std::list<std::string>::iterator j = possibilities.begin(); j != possibilities.end(); ++j) {
420 if(possStr.size() > 0) possStr += " ";
421 possStr += *j;
422 }
423 request.cli.writeMsg(possStr);
424 return false;
425 }
426
autoCompleteCommand(Command *,AutocompleteRequest & request)427 static bool autoCompleteCommand(Command*, AutocompleteRequest& request) {
428 return autoCompleteForList(request, "command", commands);
429 }
430
autoCompleteVar(Command *,AutocompleteRequest & request)431 static bool autoCompleteVar(Command*, AutocompleteRequest& request) {
432 std::list<std::string> possibilities;
433
434 for(CScriptableVars::const_iterator it = CScriptableVars::lower_bound(request.token); it != CScriptableVars::Vars().end(); ++it) {
435 // ignore callbacks
436 if(it->second.var.type == SVT_CALLBACK) continue;
437
438 if( subStrCaseEqual(request.token, it->first, request.token.size()) ) {
439 std::string nextComplete = it->first.substr(0, request.token.size());
440 for(size_t f = request.token.size();; ++f) {
441 if(f >= it->first.size()) break;
442 nextComplete += it->first[f];
443 if(it->first[f] == '.') break;
444 }
445
446 if(possibilities.size() == 0 || *possibilities.rbegin() != nextComplete) {
447 possibilities.push_back(nextComplete);
448 }
449 }
450 else
451 break;
452 }
453
454 if(possibilities.size() == 0) {
455 request.cli.writeMsg("unknown variable", CNC_WARNING);
456 return false;
457 }
458
459 if(possibilities.size() == 1) {
460 // we have exactly one solution
461 bool isComplete = possibilities.front().size() > 0 && possibilities.front()[possibilities.front().size()-1] != '.';
462 request.autocomplete.setReplace(request.old, request.completeSuggestion(possibilities.front(), isComplete));
463 return true;
464 }
465
466 size_t l = maxStartingCaseEqualStr(possibilities);
467 if(l > request.token.size()) {
468 // we can complete to some longer sequence
469 request.autocomplete.setReplace(request.old, request.completeSuggestion(possibilities.front().substr(0, l), false));
470 return true;
471 }
472
473 // send list of all possibilities
474 std::string possStr;
475 size_t startSugPos = 0;
476 for(size_t p = 0; p < request.token.size(); ++p)
477 if(request.token[p] == '.') {
478 startSugPos = p + 1;
479 }
480 for(std::list<std::string>::iterator j = possibilities.begin(); j != possibilities.end(); ++j) {
481 if(possStr.size() > 0) possStr += " ";
482 possStr += j->substr(startSugPos);
483 }
484 request.cli.writeMsg(possStr);
485 return false;
486 }
487
autoComplete(AutocompleteRequest & request)488 bool FileListCacheIntf::autoComplete(AutocompleteRequest& request) {
489 Mutex::ScopedLock lock(mutex);
490 if(!isReady) {
491 request.cli.writeMsg("file list cache not ready yet - please try again in a moment", CNC_ERROR);
492 return false;
493 }
494 return autoCompleteForList(request, name, filelist);
495 }
496
497 template <FileListCacheIntf*& filelist>
autoCompleteForFileListCache(Command *,AutocompleteRequest & request)498 bool autoCompleteForFileListCache(Command*, AutocompleteRequest& request) {
499 return filelist->autoComplete(request);
500 }
501
502
503 ///////////////////
504 // Find a command with the same name
Cmd_GetCommand(const std::string & strName)505 Command *Cmd_GetCommand(const std::string& strName)
506 {
507 CommandMap::iterator it = commands.find(strName);
508 if(it != commands.end()) return it->second;
509 return NULL;
510 }
511
registerCommand(const std::string & name,Command * cmd)512 static void registerCommand(const std::string& name, Command* cmd) {
513 #ifdef DEBUG
514 Command* old = Cmd_GetCommand(name);
515 if(old) {
516 errors << "Command '" << cmd->fullDesc() << "' as " << name << " will overwrite command " << old->name << endl;
517 }
518 #endif
519 commands.insert( CommandMap::value_type(name, cmd) );
520 }
521
registerCommand(Command * cmd)522 static void registerCommand(Command* cmd) {
523 registerCommand(cmd->name, cmd);
524 }
525
526
527
528
529 #ifndef TOSTRING
530 #define STRINGIFY(x) #x
531 #define TOSTRING(x) STRINGIFY(x)
532 #endif
533
534 #define COMMAND_EXTRA(_name, _desc, _us, _minp, _maxp, extras) \
535 class Cmd_##_name : public Command { \
536 public: \
537 Cmd_##_name() { \
538 name = TOSTRING(_name); desc = _desc; usage = _us; minParams = _minp; maxParams = _maxp; \
539 hidden = false; \
540 { extras; } \
541 registerCommand(this); \
542 } \
543 protected: \
544 void exec(CmdLineIntf* caller, const std::vector<std::string>& params); \
545 } \
546 __cmd_##_name;
547
548 #define COMMAND(_name, _desc, _us, _minp, _maxp) \
549 COMMAND_EXTRA(_name, _desc, _us, _minp, _maxp, {})
550
551 // ------------- original console commands starting here ----------
552
553
554
555 COMMAND_EXTRA(crash, "crash the game", "", 0, 0, hidden = true);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)556 void Cmd_crash::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
557 {
558 caller->writeMsg("In a previous version, the game would crash now!", CNC_NORMAL);
559 // HINT: please don't add any code, which could make the game unstable
560 // (I myself just tested this command without knowing and BANG,
561 // I got an access violation. Perhaps the hoster of an important
562 // clan war does it...)
563 if(GetGameVersion().releasetype == Version::RT_BETA) {
564 caller->writeMsg("This version will crash too, though.", CNC_WARNING);
565 // HINT: the current simple CrashHandler does not have any problems with this, thus it can stay here for testing
566 (*(int*)0x13) = 42;
567 assert(false);
568 }
569 }
570
571 COMMAND_EXTRA(coreDump, "generate a core dump", "", 0, 0, hidden = true);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)572 void Cmd_coreDump::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
573 caller->writeMsg("Dumping core ...", CNC_NORMAL);
574 doVideoFrameInMainThread();
575 struct Dumper : Action {
576 int handle() {
577 OlxWriteCoreDump("OlxCoreDump.dmp");
578 hints << "Dumping core finished" << endl; // don't use caller here because it's not sure that it still exists
579 return 0;
580 }
581 };
582 doActionInMainThread(new Dumper());
583 }
584
585
586 COMMAND(suicide, "suicide first local human worm", "[#kills]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)587 void Cmd_suicide::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
588 {
589 if (!cClient) {
590 caller->writeMsg("client not initialised", CNC_ERROR);
591 return;
592 }
593
594 if(bDedicated) {
595 caller->writeMsg("Cannot suicide in dedicated mode!", CNC_WARNING);
596 return;
597 }
598
599 if(cClient->getStatus() != NET_PLAYING) {
600 caller->writeMsg("Cannot suicide when not playing!", CNC_WARNING);
601 return;
602 }
603
604 CWorm* w = NULL;
605 for(int i = 0; i < cClient->getNumWorms(); ++i) {
606 if( cClient->getWorm(i) && cClient->getWorm(i)->getType() == PRF_HUMAN ) {
607 w = cClient->getWorm(i);
608 break;
609 }
610 }
611 if(!w) {
612 caller->writeMsg("no local human worm found", CNC_WARNING);
613 return;
614 }
615
616 // Without arguments, just commit one suicide
617 if (params.size() == 0) {
618 if(w->isUsed())
619 cClient->getNetEngine()->SendDeath(w->getID(), w->getID());
620 }
621 // A number has been entered, suicide the specified number
622 else {
623 // Get the number
624 bool fail;
625 int number = from_string<int>(params[0], fail);
626 if (fail)
627 number = 1;
628
629 if (number > cClient->getGameLobby()->iLives+1) // Safety, not needed really (should be covered in next condition)
630 number = cClient->getGameLobby()->iLives+1;
631 if (number > w->getLives()+1)
632 number = w->getLives()+1;
633 if (number < 1)
634 number = 1;
635
636 // Suicide
637 if (w->isUsed())
638 for (int i = 0; i < number; i++)
639 cClient->getNetEngine()->SendDeath(w->getID(), w->getID());
640 }
641 }
642
643 COMMAND(unstuck, "unstuck first local human worm", "", 0, 0);
644 // Unstuck a stucked worm
exec(CmdLineIntf * caller,const std::vector<std::string> & params)645 void Cmd_unstuck::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
646 {
647 if (!cClient) {
648 caller->writeMsg("client not initialised", CNC_ERROR);
649 return;
650 }
651
652 if(bDedicated) {
653 caller->writeMsg("Cannot unstuck in dedicated mode!", CNC_WARNING);
654 return;
655 }
656
657 // Not playing
658 if(cClient->getStatus() != NET_PLAYING) {
659 caller->writeMsg("Cannot unstuck when not playing!", CNC_WARNING);
660 return;
661 }
662
663 // Unstuck
664 CWorm* w = NULL;
665 for(int i = 0; i < cClient->getNumWorms(); ++i) {
666 if( cClient->getWorm(i) && cClient->getWorm(i)->getType() == PRF_HUMAN ) {
667 w = cClient->getWorm(i);
668 break;
669 }
670 }
671 if(!w) {
672 caller->writeMsg("no local human worm found", CNC_WARNING);
673 return;
674 }
675
676 if (w->isUsed() && w->getAlive())
677 w->setPos(cClient->FindNearestSpot(w));
678 }
679
680 COMMAND(wantsJoin, "enable/disable wants to join messages", "true/false", 1, 1);
681 // Enables or disables wants to join messages
exec(CmdLineIntf * caller,const std::vector<std::string> & params)682 void Cmd_wantsJoin::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
683 {
684 const std::string arg = params[0];
685
686 if (!stringcasecmp(arg,"on") || !stringcasecmp(arg,"true") || !stringcasecmp(arg,"1") || !stringcasecmp(arg,"yes")) {
687 tLXOptions->bAllowWantsJoinMsg = true;
688 caller->writeMsg("\"Wants to join\" messages have been enabled", CNC_NORMAL);
689 }
690 else {
691 tLXOptions->bAllowWantsJoinMsg = false;
692 caller->writeMsg("\"Wants to join\" messages have been disabled", CNC_NORMAL);
693 }
694
695 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
696 caller->writeMsg("Note that this has no effect right now; it's for the case when you are hosting next time.");
697 return;
698 }
699 }
700
701 COMMAND(serverName, "rename server", "new-name", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)702 void Cmd_serverName::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
703 {
704 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
705 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
706 return;
707 }
708
709 cServer->setName(params[0]);
710 }
711
712 COMMAND_EXTRA(help, "list available commands or shows desription/usage of specific command", "[command]", 0, 1, paramCompleters[0] = &autoCompleteCommand);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)713 void Cmd_help::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
714 if(params.size() > 0) {
715 CommandMap::iterator it = commands.lower_bound(params[0]);
716 if(it != commands.end()) {
717 if(!stringcaseequal(it->first, params[0]))
718 caller->writeMsg("Help: Was that a typo? Did you mean '" + it->first + "'?", CNC_WARNING);
719 if(!stringcaseequal(it->second->name, it->first))
720 caller->pushReturnArg(it->first + " is an alias for " + it->second->name);
721 caller->pushReturnArg(it->second->usageStr());
722 caller->pushReturnArg(it->second->desc);
723 return;
724 }
725
726 caller->writeMsg("Help: command " + params[0] + " is unknown", CNC_WARNING);
727 }
728
729 caller->writeMsg("Available commands:");
730 std::string cmd_help_buf;
731 unsigned short count = 0;
732
733 for(CommandMap::iterator it = commands.begin(); it != commands.end(); ++it) {
734 if(!it->second->hidden && it->first == it->second->name) {
735 cmd_help_buf += it->first;
736 cmd_help_buf += " ";
737 count++;
738 if(count >= 5) {
739 count = 0;
740 caller->writeMsg(" " + cmd_help_buf);
741 cmd_help_buf = "";
742 }
743 }
744 }
745 if(count && cmd_help_buf != "") {
746 caller->writeMsg(" " + cmd_help_buf);
747 }
748 caller->writeMsg("Type 'longhelp' to get a list together with small description.");
749 caller->writeMsg("Type 'help <command>' to get some help about this commad.");
750 }
751
752 COMMAND(longhelp, "list available commands and description", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)753 void Cmd_longhelp::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
754 caller->writeMsg("Available commands:");
755 for(CommandMap::iterator it = commands.begin(); it != commands.end(); ++it) {
756 if(!it->second->hidden && it->first == it->second->name) {
757 caller->writeMsg(it->first + " - " + it->second->desc);
758 }
759 }
760 }
761
762 COMMAND_EXTRA(version, "print game version info", "", 0, 0, registerCommand("about", this));
exec(CmdLineIntf * caller,const std::vector<std::string> & params)763 void Cmd_version::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
764 caller->pushReturnArg(GetFullGameName());
765 }
766
767 COMMAND_EXTRA(sex, "say something messy", "", 0, 0, hidden = true; registerCommand("fuck", this));
exec(CmdLineIntf * caller,const std::vector<std::string> & params)768 void Cmd_sex::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
769 caller->pushReturnArg(sex(50));
770 }
771
772 COMMAND(disconnect, "disconnect from server or exit server", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)773 void Cmd_disconnect::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
774 if(currentGameState() == S_INACTIVE) {
775 caller->writeMsg("game is inactive, cannot disconnect anything", CNC_WARNING);
776 return;
777 }
778
779 if(cClient && cClient->getStatus() != NET_DISCONNECTED)
780 cClient->Disconnect();
781
782 if(cServer && cServer->isServerRunning()) {
783 // Tell any clients that we're leaving
784 cServer->SendDisconnect();
785
786 // Shutdown server & clients
787 cClient->Shutdown();
788 cServer->Shutdown();
789 }
790 else if(cClient && cClient->getStatus() != NET_DISCONNECTED)
791 cClient->Shutdown();
792
793 SetQuitEngineFlag("Cmd_disconnect");
794
795 if(!bDedicated && DeprecatedGUI::tMenu) {
796 DeprecatedGUI::Menu_Current_Shutdown();
797
798 DeprecatedGUI::Menu_SetSkipStart(true);
799 if(tLX->iGameType == GME_LOCAL) {
800 DeprecatedGUI::Menu_LocalInitialize();
801 }
802 else {
803 DeprecatedGUI::Menu_NetInitialize(true);
804
805 // when we leave the server
806 DeprecatedGUI::tMenu->iReturnTo = DeprecatedGUI::iNetMode;
807 }
808 }
809 }
810
811 COMMAND(volume, "set sound volume", "0-100", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)812 void Cmd_volume::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
813 if(bDedicated) {
814 caller->writeMsg(name + " cannot be used in dedicated mode");
815 return;
816 }
817
818 bool fail = false;
819 int vol = from_string<int>(params[0], fail);
820 if(fail) {
821 printUsage(caller);
822 return;
823 }
824
825 vol = MIN(vol,100);
826 vol = MAX(vol,0);
827 SetSoundVolume(vol);
828 caller->writeMsg("new sound volume: " + itoa(vol));
829 }
830
831 COMMAND(sound, "enable or disable sound", "true/false", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)832 void Cmd_sound::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
833 if(bDedicated) {
834 caller->writeMsg(name + " cannot be used in dedicated mode");
835 return;
836 }
837
838 const std::string arg = params[0];
839
840 if (!stringcasecmp(arg,"on") || !stringcasecmp(arg,"true") || !stringcasecmp(arg,"1") || !stringcasecmp(arg,"yes")) {
841 StartSoundSystem();
842 tLXOptions->bSoundOn = true;
843 caller->writeMsg("sound is now enabled, volume = " + itoa(GetSoundVolume()));
844 }
845 else {
846 StopSoundSystem();
847 tLXOptions->bSoundOn = false;
848 caller->writeMsg("sound is now disabled");
849 }
850 }
851
852 COMMAND_EXTRA(serverSideHealth, "turn on/off server-side health", "true/false", 1, 1, registerCommand("ssh", this));
exec(CmdLineIntf * caller,const std::vector<std::string> & params)853 void Cmd_serverSideHealth::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
854 if(!bDedicated) {
855 caller->writeMsg("Sorry, server side health has been removed for non-dedicated servers", CNC_WARNING);
856 return;
857 }
858
859 const std::string arg = params[0];
860
861 // Set the ssh
862 tLXOptions->tGameInfo.bServerSideHealth = stringcaseequal(arg,"on") || stringcaseequal(arg,"true") || stringcaseequal(arg,"1") || stringcaseequal(arg,"yes");
863
864 caller->writeMsg(std::string("Server-side health is now ") + (tLXOptions->tGameInfo.bServerSideHealth ? std::string("enabled.") : std::string("disabled.")));
865
866 }
867
868 COMMAND(irc, "send message to IRC chat", "text", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)869 void Cmd_irc::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
870 if (GetGlobalIRC())
871 GetGlobalIRC()->sendChat(params[0]);
872 else
873 caller->writeMsg("IRC system is not running");
874 }
875
876 COMMAND(connect, "join a server", "server[:port] [player]", 1, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)877 void Cmd_connect::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
878 if(cServer)
879 cServer->Shutdown();
880
881 if(cClient && cClient->getStatus() != NET_DISCONNECTED)
882 cClient->Disconnect();
883
884 DeprecatedGUI::Menu_Current_Shutdown();
885
886 if(!DeprecatedGUI::tMenu || !DeprecatedGUI::tMenu->bMenuRunning) { // we are in game
887 SetQuitEngineFlag("Cmd_Connect & in game");
888 }
889
890 std::string server = params[0];
891 std::string player =
892 (params.size() >= 2) ? params[1] :
893 bDedicated ? FindFirstCPUProfileName() :
894 tLXOptions->sLastSelectedPlayer;
895 if(params.size() == 1 && player == "" && GetProfiles()) player = GetProfiles()->sName;
896 if(!JoinServer(server, server, player)) return;
897
898 if(!bDedicated) {
899 // goto the joining dialog
900 DeprecatedGUI::Menu_SetSkipStart(true);
901 DeprecatedGUI::Menu_NetInitialize(false);
902 DeprecatedGUI::Menu_Net_JoinInitialize(server);
903
904 // when we leave the server
905 DeprecatedGUI::tMenu->iReturnTo = DeprecatedGUI::iNetMode;
906 }
907 }
908
909 COMMAND(wait, "Execute commands after wait", "seconds|lobby|game command [args] [ ; command2 args... ]", 2, INT_MAX);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)910 void Cmd_wait::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
911
912 if(cClient && cClient->getStatus() != NET_DISCONNECTED)
913 cClient->Disconnect();
914
915 struct WaitThread: public Action
916 {
917 std::vector<std::string> params;
918 WaitThread(const std::vector<std::string>& _params): params(_params) {};
919 int handle()
920 {
921 int seconds = atoi(params[0]);
922 if(seconds == 0)
923 seconds = -1;
924
925 while( tLX && !tLX->bQuitGame ) // TODO: put mutex here
926 {
927 if(params[0] == "game" && cClient && cClient->getStatus() == NET_PLAYING) // TODO: put mutex here
928 {
929 //stdoutCLI().writeMsg("wait: trigger: game started");
930 break;
931 }
932 if(params[0] == "lobby" && cClient && cClient->getStatus() == NET_CONNECTED) // TODO: put mutex here
933 {
934 //stdoutCLI().writeMsg("wait: trigger: lobby started");
935 break;
936 }
937 if(seconds > 0)
938 {
939 seconds--;
940 if(seconds <= 0)
941 {
942 //stdoutCLI().writeMsg("wait: trigger: timeout");
943 break;
944 }
945 }
946 SDL_Delay(1000);
947 }
948
949 if( ! (tLX && !tLX->bQuitGame) ) // TODO: put mutex here
950 return 1;
951
952 for( std::vector<std::string>::iterator it = (++params.begin()); it != params.end(); )
953 {
954 std::string cmdName = *it;
955 Command * cmd = Cmd_GetCommand(cmdName);
956 std::string params1;
957 it++;
958 for( ; it != params.end() && *it != ";"; it++ )
959 params1 += *it + " ";
960 if( it != params.end() )
961 it++;
962 if(!cmd)
963 stdoutCLI().writeMsg("wait: cannot execute command " + cmdName + " " + params1);
964 if(cmd)
965 {
966 struct ExecCmd: public Action
967 {
968 Command * cmd;
969 std::string params;
970 int handle()
971 {
972 cmd->exec( &stdoutCLI(), params );
973 return 0;
974 }
975 };
976 ExecCmd * execCmd = new ExecCmd();
977 execCmd->cmd = cmd;
978 execCmd->params = params1;
979 //stdoutCLI().writeMsg("wait: executing cmd: " + cmdName + " " + params1);
980 doActionInMainThread( execCmd );
981 }
982 }
983 return 0;
984 }
985 };
986
987 threadPool->start( new WaitThread(params), "Cmd_wait() command thread", true );
988 }
989
990 COMMAND(setViewport, "Set viewport mode", "mode=follow|cycle|freelook|actioncam [wormID] [mode2] [wormID2]", 1, 4);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)991 void Cmd_setViewport::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
992
993 if(!cClient || cClient->getStatus() != NET_PLAYING)
994 {
995 caller->writeMsg("Cannot set viewport while not playing");
996 return;
997 }
998
999 int mode1 = VW_FOLLOW, mode2 = VW_FOLLOW;
1000 int * modePtr = &mode1;
1001 for( int idx = 0; (int)params.size() > idx; idx += 2, modePtr = &mode2 )
1002 {
1003 if( params[idx] == "follow" )
1004 *modePtr = VW_FOLLOW;
1005 else if( params[idx] == "cycle" )
1006 *modePtr = VW_CYCLE;
1007 else if( params[idx] == "freelook" )
1008 *modePtr = VW_FREELOOK;
1009 else if( params[idx] == "actioncam" )
1010 *modePtr = VW_ACTIONCAM;
1011 else
1012 {
1013 caller->writeMsg("Invalid mode");
1014 return;
1015 }
1016 }
1017 CWorm *w1 = NULL;
1018 CWorm *w2 = NULL;
1019 CWorm **w = &w1;
1020 for( int idx = 1; (int)params.size() > idx; idx += 2, w = &w2 )
1021 {
1022 int id = atoi(params[idx]);
1023 if(id < 0 || id >= MAX_WORMS)
1024 {
1025 caller->writeMsg("Faulty worm ID " + itoa(id));
1026 return;
1027 }
1028 *w = cClient->getRemoteWorms() + id;
1029 if(!(*w)->isUsed())
1030 {
1031 caller->writeMsg("Worm ID " + itoa(id) + " not used");
1032 return;
1033 }
1034 }
1035
1036 cClient->SetupViewports(w1, w2, mode1, mode2);
1037 }
1038
1039
1040
1041
1042
1043
1044 // ------------- original dedicated commands starting here --------
1045
1046
1047 COMMAND_EXTRA(quit, "quit game", "", 0, 0, registerCommand("exit", this));
exec(CmdLineIntf * caller,const std::vector<std::string> &)1048 void Cmd_quit::exec(CmdLineIntf* caller, const std::vector<std::string>&) {
1049 *DeprecatedGUI::bGame = false; // this means if we were in menu => quit
1050 DeprecatedGUI::tMenu->bMenuRunning = false; // if we were in menu, quit menu
1051
1052 tLX->bQuitGame = true; // quit main-main-loop
1053 SetQuitEngineFlag("DedicatedControl::Cmd_Quit()"); // quit main-game-loop
1054 }
1055
1056 COMMAND(msg, "print message on stdout", "text", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1057 void Cmd_msg::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1058 hints << "DedicatedControl: message: " << params[0] << endl;
1059 }
1060
1061 COMMAND(script, "load extern dedicated script", "[script] [args]", 0, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1062 void Cmd_script::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1063 std::string script = (params.size() > 0) ? params[0] : "";
1064 std::string args = (params.size() > 1) ? params[1] : "";
1065
1066 if(!DedicatedControl::Get()) {
1067 caller->writeMsg("Error: can only load extern script in dedicated mode");
1068 return;
1069 }
1070
1071 if(IsAbsolutePath(script)) {
1072 caller->writeMsg("Error: absolute path names are not allowed for script command");
1073 return;
1074 }
1075
1076 if(script.find("..") != std::string::npos) {
1077 caller->writeMsg("Error: invalid script filename: " + script);
1078 return;
1079 }
1080
1081 DedicatedControl::Get()->ChangeScript(script, args);
1082 }
1083
1084 COMMAND(addHuman, "add human player to game", "[profile]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1085 void Cmd_addHuman::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1086 {
1087 if(bDedicated) {
1088 caller->writeMsg("cannot add human player in dedicated mode", CNC_WARNING);
1089 return;
1090 }
1091
1092 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1093 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1094 return;
1095 }
1096
1097 if( cClient->getNumWorms() + 1 >= MAX_WORMS ) {
1098 caller->writeMsg("Too many worms!");
1099 return;
1100 }
1101
1102 // try to find the requested worm
1103 std::string player = (params.size() > 0) ? params[0] : tLXOptions->sLastSelectedPlayer;
1104 TrimSpaces(player);
1105 StripQuotes(player);
1106 if(player == "" && GetProfiles()) player = GetProfiles()->sName;
1107
1108 profile_t *p = FindProfile(player);
1109 if(p) {
1110 if(p->iType != PRF_HUMAN->toInt()) {
1111 caller->writeMsg("worm " + player + " is not a human player", CNC_WARNING);
1112 return;
1113 }
1114 cClient->AddWorm(p);
1115 return;
1116 }
1117
1118 caller->writeMsg("cannot find a profile for worm '" + player + "'");
1119 }
1120
1121
1122 COMMAND(addBot, "add bot to game", "[botprofile] [ai-diff] [inGame:*true/false]", 0, 3);
1123 // adds a worm to the game (By string - id is way to complicated)
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1124 void Cmd_addBot::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1125 {
1126 // Note: this check only for non-dedicated; in ded, we allow it, although it is a bit experimental
1127 if(!bDedicated && (tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning())) {
1128 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1129 return;
1130 }
1131
1132 if( cClient->getNumWorms() + 1 >= MAX_WORMS ) {
1133 caller->writeMsg("Too many worms!");
1134 return;
1135 }
1136
1137 bool outOfGame = false;
1138 if(params.size() > 2) {
1139 bool fail = false;
1140 outOfGame = !from_string<bool>(params[2], fail);
1141 if(fail) { printUsage(caller); return; }
1142 }
1143
1144 int wormAiDiff = -1;
1145 if(params.size() > 1) {
1146 bool fail = false;
1147 wormAiDiff = from_string<int>(params[1], fail);
1148 if(fail) { printUsage(caller); return; }
1149 if(wormAiDiff < -1 || wormAiDiff >= 4) {
1150 caller->writeMsg("only values from 0-3 are allowed for ai-difficulty, -1 is to use profile-default", CNC_WARNING);
1151 return;
1152 }
1153 }
1154
1155 // try to find the requested worm
1156 if(params.size() > 0 && params[0] != "") {
1157 std::string localWorm = params[0];
1158 TrimSpaces(localWorm);
1159 StripQuotes(localWorm);
1160
1161 profile_t *p = FindProfile(localWorm);
1162 if(p) {
1163 if(p->iType != PRF_COMPUTER->toInt()) {
1164 caller->writeMsg("worm " + localWorm + " is not a bot", CNC_WARNING);
1165 return;
1166 }
1167 int w = cClient->AddWorm(p, outOfGame);
1168 if(w >= 0) {
1169 if(wormAiDiff >= 0) cClient->getRemoteWorms()[w].setAiDiff(wormAiDiff);
1170 caller->pushReturnArg(itoa(w));
1171 }
1172 return;
1173 }
1174
1175 caller->writeMsg("cannot find worm profile " + localWorm + ", using random instead");
1176 }
1177
1178 std::list<int> worms = cClient->AddRandomBots(1, outOfGame);
1179 for(std::list<int>::iterator i = worms.begin(); i != worms.end(); ++i) {
1180 if(wormAiDiff >= 0) cClient->getRemoteWorms()[*i].setAiDiff(wormAiDiff);
1181 caller->pushReturnArg(itoa(*i));
1182 }
1183 }
1184
1185 COMMAND(addBots, "add bots to game", "number [ai-diff]", 1, 2);
1186 // adds a worm to the game (By string - id is way to complicated)
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1187 void Cmd_addBots::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1188 {
1189 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1190 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1191 return;
1192 }
1193
1194 if( cClient->getNumWorms() + 1 >= MAX_WORMS ) {
1195 caller->writeMsg("Too many worms!");
1196 return;
1197 }
1198
1199 bool fail = false;
1200 int num = from_string<int>(params[0], fail);
1201 if(fail || num <= 0) {
1202 printUsage(caller);
1203 return;
1204 }
1205
1206 int wormAiDiff = -1;
1207 if(params.size() > 1) {
1208 wormAiDiff = from_string<int>(params[1], fail);
1209 if(fail) { printUsage(caller); return; }
1210 if(wormAiDiff < -1 || wormAiDiff >= 4) {
1211 caller->writeMsg("only values from 0-3 are allowed for ai-difficulty, -1 is to use profile-default", CNC_WARNING);
1212 return;
1213 }
1214 }
1215
1216 std::list<int> worms = cClient->AddRandomBots(num);
1217 for(std::list<int>::iterator i = worms.begin(); i != worms.end(); ++i) {
1218 if(wormAiDiff >= 0) cClient->getRemoteWorms()[*i].setAiDiff(wormAiDiff);
1219 caller->pushReturnArg(itoa(*i));
1220 }
1221 }
1222
1223 COMMAND(kickBot, "kick bot from game", "[reason]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1224 void Cmd_kickBot::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1225 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1226 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1227 return;
1228 }
1229
1230 std::string reason = (params.size() > 0) ? params[0] : "Dedicated command";
1231 int worm = cServer->getLastBot();
1232 if(worm < 0) {
1233 caller->writeMsg("there is no bot on the server");
1234 return;
1235 }
1236 cServer->kickWorm(worm, reason);
1237 }
1238
1239 COMMAND(kickBots, "kick all bots from game", "[reason]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1240 void Cmd_kickBots::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1241 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1242 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1243 return;
1244 }
1245
1246 std::string reason = (params.size() > 0) ? params[0] : "Dedicated command";
1247 std::list<int> worms;
1248 for( int f = 0; f < cClient->getNumWorms(); f++ )
1249 if( cClient->getWorm(f)->getType() == PRF_COMPUTER )
1250 worms.push_back(cClient->getWorm(f)->getID());
1251 if(worms.size() == 0)
1252 caller->writeMsg("there is no bot on the server");
1253 for(std::list<int>::iterator i = worms.begin(); i != worms.end(); ++i)
1254 cServer->kickWorm(*i, reason);
1255 }
1256
1257
1258 COMMAND(killBots, "kill all bots out of game", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1259 void Cmd_killBots::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1260 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1261 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1262 return;
1263 }
1264
1265 for( int f=0; f<cClient->getNumWorms(); f++ )
1266 if( cClient->getWorm(f)->getType() == PRF_COMPUTER )
1267 {
1268 cServer->getWorms()[cClient->getWorm(f)->getID()].setLives(0);
1269 cClient->getNetEngine()->SendDeath(cClient->getWorm(f)->getID(), cClient->getWorm(f)->getID());
1270 }
1271 }
1272
1273 COMMAND(kickWorm, "kick worm", "id [reason]", 1, 2);
1274 // Kick and ban will both function using ID
1275 // It's up to the control-program to supply the ID
1276 // - if it sends a string atoi will fail at converting it to something sensible
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1277 void Cmd_kickWorm::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1278 {
1279 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1280 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1281 return;
1282 }
1283
1284 bool fail = true;
1285 int id = from_string<int>(params[0], fail);
1286 if(fail) {
1287 printUsage(caller);
1288 return;
1289 }
1290
1291 std::string reason = "";
1292 if (params.size() >= 2)
1293 reason = params[1];
1294
1295 if(!CheckWorm(caller, id, "kickWorm"))
1296 return;
1297
1298 cServer->kickWorm(id,reason);
1299 }
1300
1301 COMMAND(banWorm, "ban worm", "id [reason]", 1, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1302 void Cmd_banWorm::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1303 {
1304 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1305 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1306 return;
1307 }
1308
1309 bool fail = true;
1310 int id = from_string<int>(params[0], fail);
1311 if(fail) {
1312 printUsage(caller);
1313 return;
1314 }
1315
1316 std::string reason = "";
1317 if (params.size() >= 2)
1318 reason = params[1];
1319
1320 if(!CheckWorm(caller, id, "banWorm"))
1321 return;
1322
1323 cServer->banWorm(id,reason);
1324 }
1325
1326 COMMAND(muteWorm, "mute worm", "id", 1, 1);
1327 // TODO: Add name muting, if wanted.
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1328 void Cmd_muteWorm::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1329 {
1330 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1331 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1332 return;
1333 }
1334
1335 bool fail = true;
1336 int id = from_string<int>(params[0], fail);
1337 if(fail) {
1338 printUsage(caller);
1339 return;
1340 }
1341
1342 if(!CheckWorm(caller, id, "muteWorm"))
1343 return;
1344
1345 cServer->muteWorm(id);
1346 }
1347
1348 COMMAND(unmuteWorm, "unmute worm", "id", 1, 1);
1349 // TODO: Add name muting, if wanted.
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1350 void Cmd_unmuteWorm::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1351 {
1352 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1353 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1354 return;
1355 }
1356
1357 bool fail = true;
1358 int id = from_string<int>(params[0], fail);
1359 if(fail) {
1360 printUsage(caller);
1361 return;
1362 }
1363
1364 if(!CheckWorm(caller, id, "unmuteWorm"))
1365 return;
1366
1367 cServer->unmuteWorm(id);
1368 }
1369
1370
1371 COMMAND(spawnWorm, "spawn worm", "id [pos]", 1, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1372 void Cmd_spawnWorm::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1373 {
1374 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1375 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1376 return;
1377 }
1378
1379 if(cServer->getState() != SVS_PLAYING) {
1380 caller->writeMsg("can only spawn worm when playing");
1381 return;
1382 }
1383
1384 bool fail = true;
1385 int id = from_string<int>(params[0], fail);
1386 if(fail) { printUsage(caller); return; }
1387 CWorm* w = CheckWorm(caller, id, "spawnWorm");
1388 if(!w) return;
1389
1390 CVec pos;
1391 bool havePos = false;
1392 if(params.size() > 1) {
1393 pos = from_string< VectorD2<int> >(params[1], fail);
1394 if(fail) { printUsage(caller); return; }
1395 havePos = true;
1396 }
1397
1398 cServer->SpawnWorm(w, havePos ? &pos : NULL);
1399 }
1400
1401
1402 COMMAND(setWormLives, "set worm lives", "id (-2: unlimited, -1: outofgame, >=0: lives)", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1403 void Cmd_setWormLives::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1404 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1405 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1406 return;
1407 }
1408
1409 bool fail = true;
1410 int id = from_string<int>(params[0], fail);
1411 if(fail) { printUsage(caller); return; }
1412 CWorm* w = CheckWorm(caller, id, "setWormLives");
1413 if(!w) return;
1414
1415 Sint16 lives = from_string<Sint16>(params[1], fail);
1416 if(fail) { printUsage(caller); return; }
1417 if(lives < -2) { printUsage(caller); return; }
1418
1419 w->setLives(lives);
1420 for(int ii = 0; ii < MAX_CLIENTS; ii++) {
1421 if(cServer->getClients()[ii].getStatus() != NET_CONNECTED) continue;
1422 if(cServer->getClients()[ii].getNetEngine() == NULL) continue;
1423 cServer->getClients()[ii].getNetEngine()->SendWormScore( w );
1424 }
1425 }
1426
1427
1428 COMMAND(setWormTeam, "set worm team", "id team", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1429 void Cmd_setWormTeam::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
1430 {
1431 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1432 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1433 return;
1434 }
1435
1436 bool fail = true;
1437 int id = from_string<int>(params[0], fail);
1438 if(fail) {
1439 printUsage(caller);
1440 return;
1441 }
1442
1443 CWorm *w = CheckWorm(caller, id,"setWormTeam");
1444 if (!w) return;
1445
1446 int team = from_string<int>(params[1], fail);
1447 if(fail) {
1448 printUsage(caller);
1449 return;
1450 }
1451
1452 if( team < 0 || team > 3 ) {
1453 caller->writeMsg("setWormTeam: invalid team number");
1454 return;
1455 }
1456
1457 w->setTeam(team);
1458 cServer->UpdateWorm(w);
1459 // TODO: SendWormLobbyUpdate: is this still needed? which information does it contain which are not in UpdateWorm?
1460 // Also, if we need it, we should at least set target=w->getClient(). Also, if it is only needed for old clients,
1461 // we also should send it only to them to save some bandwidth.
1462 //cServer->SendWormLobbyUpdate();
1463 cServer->RecheckGame();
1464 }
1465
1466 COMMAND(setWormSpeedFactor, "set worm speedfactor", "id factor", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1467 void Cmd_setWormSpeedFactor::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1468 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1469 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1470 return;
1471 }
1472
1473 bool fail = true;
1474 int id = from_string<int>(params[0], fail);
1475 if(fail) {
1476 printUsage(caller);
1477 return;
1478 }
1479
1480 if(!CheckWorm(caller, id, "setWormSpeedFactor")) return;
1481
1482 float factor = from_string<float>(params[1], fail);
1483 if(fail) {
1484 printUsage(caller);
1485 return;
1486 }
1487
1488 cServer->SetWormSpeedFactor(id, factor);
1489 }
1490
1491 COMMAND(setWormDamageFactor, "set worm damagefactor", "id factor", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1492 void Cmd_setWormDamageFactor::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1493 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1494 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1495 return;
1496 }
1497
1498 bool fail = true;
1499 int id = from_string<int>(params[0], fail);
1500 if(fail) {
1501 printUsage(caller);
1502 return;
1503 }
1504
1505 if(!CheckWorm(caller, id, "setWormDamageFactor")) return;
1506
1507 float factor = from_string<float>(params[1], fail);
1508 if(fail) {
1509 printUsage(caller);
1510 return;
1511 }
1512
1513 cServer->SetWormDamageFactor(id, factor);
1514 }
1515
1516 COMMAND(setWormShieldFactor, "set worm shieldfactor", "id factor", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1517 void Cmd_setWormShieldFactor::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1518 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1519 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1520 return;
1521 }
1522
1523 bool fail = true;
1524 int id = from_string<int>(params[0], fail);
1525 if(fail) {
1526 printUsage(caller);
1527 return;
1528 }
1529
1530 if(!CheckWorm(caller, id, "setWormShieldFactor")) return;
1531
1532 float factor = from_string<float>(params[1], fail);
1533 if(fail) {
1534 printUsage(caller);
1535 return;
1536 }
1537
1538 cServer->SetWormShieldFactor(id, factor);
1539 }
1540
1541 COMMAND(setWormCanUseNinja, "(dis)allow worm to use ninja", "id true/false", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1542 void Cmd_setWormCanUseNinja::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1543 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1544 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1545 return;
1546 }
1547
1548 bool fail = true;
1549 int id = from_string<int>(params[0], fail);
1550 if(fail) {
1551 printUsage(caller);
1552 return;
1553 }
1554
1555 if(!CheckWorm(caller, id, "setWormCanUseNinja")) return;
1556
1557 bool canUse = from_string<bool>(params[1], fail);
1558 if(fail) {
1559 printUsage(caller);
1560 return;
1561 }
1562
1563 cServer->SetWormCanUseNinja(id, canUse);
1564 }
1565
1566 COMMAND(setWormCanAirJump, "enable/disable air jump for worm", "id true/false", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1567 void Cmd_setWormCanAirJump::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1568 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1569 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1570 return;
1571 }
1572
1573 bool fail = true;
1574 int id = from_string<int>(params[0], fail);
1575 if(fail) {
1576 printUsage(caller);
1577 return;
1578 }
1579
1580 if(!CheckWorm(caller, id, name)) return;
1581
1582 bool canUse = from_string<bool>(params[1], fail);
1583 if(fail) {
1584 printUsage(caller);
1585 return;
1586 }
1587
1588 cServer->SetWormCanAirJump(id, canUse);
1589 }
1590
1591 COMMAND(setWormWeapons, "force specific weapons for a worm", "wormId weaponName1 weaponName2 weaponName3 weaponName4 weaponName5", 6, 6);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1592 void Cmd_setWormWeapons::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1593 if (tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1594 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1595 return;
1596 }
1597
1598 bool fail = true;
1599 int id = from_string<int>(params[0], fail);
1600 if (fail) {
1601 printUsage(caller);
1602 return;
1603 }
1604
1605 if (!CheckWorm(caller, id, "setWormWeapon")) return;
1606
1607 CWorm *w = &(cServer->getWorms()[id]);
1608
1609 for (int slotId = 0; slotId < 5; slotId++) {
1610 if (slotId >= w->getNumWeaponSlots()) {
1611 continue;
1612 }
1613 const weapon_t *weapon = NULL;
1614 if (params[slotId+1] != "") {
1615 weapon = cServer->getGameScript()->FindWeapon(params[slotId+1]);
1616 if (!weapon) {
1617 printUsage(caller);
1618 warnings << "Cannot find weapon " << params[slotId+1] << endl;
1619 return;
1620 }
1621 }
1622 wpnslot_t *slot = w->getWeapon(slotId);
1623 const weapon_t *oldWeapon = slot->Weapon;
1624 if (weapon) {
1625 slot->Weapon = weapon;
1626 slot->Enabled = true;
1627 slot->Charge = 1;
1628 slot->Reloading = false;
1629 } else {
1630 slot->Weapon = NULL;
1631 slot->Enabled = false;
1632 }
1633 // handle worm shoot end if needed
1634 if(oldWeapon && slot->Weapon != oldWeapon && w->getWormState()->bShoot)
1635 cServer->WormShootEnd(w, oldWeapon);
1636 }
1637
1638 cServer->SendWeapons(w);
1639 }
1640
1641 COMMAND(authorizeWorm, "authorize worm", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1642 void Cmd_authorizeWorm::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1643 if(tLX->iGameType == GME_JOIN) {
1644 caller->writeMsg(name + ": cannot do that as client", CNC_WARNING);
1645 return;
1646 }
1647
1648 bool fail = true;
1649 int id = from_string<int>(params[0], fail);
1650 if(fail) {
1651 printUsage(caller);
1652 return;
1653 }
1654
1655 if(!CheckWorm(caller, id, name)) return;
1656
1657 cServer->authorizeWorm(id);
1658 }
1659
1660 COMMAND_EXTRA(setVar, "set variable", "variable value", 2, 2, paramCompleters[0] = &autoCompleteVar);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1661 void Cmd_setVar::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1662 std::string var = params[0];
1663 std::string value = params[1];
1664
1665 RegisteredVar* varptr = CScriptableVars::GetVar(var);
1666 if( varptr == NULL )
1667 {
1668 caller->writeMsg("SetVar: no var with name " + var);
1669 notes << "Available vars:\n" << CScriptableVars::DumpVars() << endl;
1670 notes << "\nFor Python ded control script:\n" << endl;
1671 for( CScriptableVars::const_iterator it = CScriptableVars::begin(); it != CScriptableVars::end(); it++ )
1672 {
1673 notes << "setvar( \"" << it->first << "\", ";
1674 if( it->second.var.type == SVT_BOOL )
1675 notes << (int) * it->second.var.b;
1676 else if( it->second.var.type == SVT_INT )
1677 notes << * it->second.var.i;
1678 else if( it->second.var.type == SVT_FLOAT )
1679 notes << * it->second.var.f;
1680 else if( it->second.var.type == SVT_STRING )
1681 notes << "\"" << * it->second.var.s << "\"";
1682 else if( it->second.var.type == SVT_COLOR )
1683 notes << ColToHex(*it->second.var.cl);
1684 else
1685 notes << "\"\"";
1686 notes << ")" << endl;
1687 }
1688 return;
1689 }
1690
1691 if( varptr->var.type == SVT_CALLBACK ) {
1692 caller->writeMsg("SetVar: callbacks are not allowed");
1693 // If we want support for that, I would suggest a seperated command like "call ...".
1694 return;
1695 }
1696
1697 if(cServer && cServer->isServerRunning() && cServer->getState() != SVS_LOBBY) {
1698 if( varptr->var.s == &tLXOptions->tGameInfo.sMapFile ) {
1699 caller->writeMsg("SetVar: You cannot change the map in game");
1700 return;
1701 }
1702
1703 if( varptr->var.s == &tLXOptions->tGameInfo.sMapName ) {
1704 caller->writeMsg("SetVar: You cannot change the map-name in game");
1705 return;
1706 }
1707
1708 if( varptr->var.s == &tLXOptions->tGameInfo.sModDir ) {
1709 caller->writeMsg("SetVar: You cannot change the mod in game");
1710 return;
1711 }
1712
1713 if( stringcaseequal(var, "GameOptions.GameInfo.GameType") ) {
1714 caller->writeMsg("SetVar: You cannot change the gametype in game.");
1715 return;
1716 }
1717 }
1718
1719 CScriptableVars::SetVarByString(varptr->var, value);
1720 //notes << "DedicatedControl: SetVar " << var << " = " << value << endl;
1721
1722 cServer->UpdateGameLobby();
1723 }
1724
1725 COMMAND_EXTRA(getVar, "read variable", "variable", 1, 1, paramCompleters[0] = &autoCompleteVar);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1726 void Cmd_getVar::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1727 std::string var = params[0];
1728
1729 RegisteredVar* varptr = CScriptableVars::GetVar(var);
1730 if( varptr == NULL ) {
1731 caller->writeMsg("GetVar: no var with name " + var);
1732 return;
1733 }
1734
1735 if( varptr->var.type == SVT_CALLBACK ) {
1736 caller->writeMsg("GetVar: callbacks are not allowed");
1737 // If we want supoort for that, I would suggest a seperated command like "call ...".
1738 return;
1739 }
1740
1741 if( varptr->var.s == &tLXOptions->sServerPassword ) {
1742 caller->writeMsg("GetVar: this variable is restricted");
1743 // If you want to check if a worm is authorized, use another function for that.
1744 return;
1745 }
1746
1747 caller->pushReturnArg(varptr->var.toString());
1748 }
1749
1750 COMMAND(listVars, "list all variables", "[prefix]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1751 void Cmd_listVars::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1752 std::string prefix = params.size() > 0 ? params[0] : "";
1753 for (CScriptableVars::const_iterator it = (prefix == "") ? CScriptableVars::begin() : CScriptableVars::lower_bound(prefix);
1754 it != CScriptableVars::end(); it++)
1755 {
1756 if ( prefix != "" && !strStartsWith(it->first, prefix) )
1757 break;
1758 caller->pushReturnArg(it->first);
1759 }
1760 }
1761
1762 COMMAND_EXTRA(getVarHelp, "print variable description", "variable", 1, 1, paramCompleters[0] = &autoCompleteVar);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1763 void Cmd_getVarHelp::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1764 std::string var = params[0];
1765
1766 RegisteredVar* varptr = CScriptableVars::GetVar(var);
1767 if( varptr == NULL ) {
1768 caller->writeMsg("GetVarHelp: no var with name " + var);
1769 return;
1770 }
1771
1772 caller->pushReturnArg(varptr->longDesc != "" ? varptr->longDesc : varptr->shortDesc != "" ? varptr->shortDesc : var);
1773 }
1774
1775 COMMAND(getFullFileName, "get full filename", "relativefilename", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1776 void Cmd_getFullFileName::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1777 caller->pushReturnArg(Utf8ToSystemNative(GetAbsolutePath(GetFullFileName(params[0], NULL))));
1778 }
1779
1780 COMMAND(getWriteFullFileName, "get writeable full filename", "relativefilename", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1781 void Cmd_getWriteFullFileName::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1782 caller->pushReturnArg(Utf8ToSystemNative(GetAbsolutePath(GetWriteFullFileName(params[0]))));
1783 }
1784
1785 COMMAND(startLobby, "start server lobby", "[serverport]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1786 void Cmd_startLobby::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1787 if(!DeprecatedGUI::tMenu->bMenuRunning) {
1788 caller->writeMsg("we cannot start the lobby in current state", CNC_NOTIFY);
1789 caller->writeMsg("stop game if you want to restart it", CNC_NOTIFY);
1790 return; // just ignore it and stay in current state
1791 }
1792
1793 if(cServer && cServer->isServerRunning()) {
1794 caller->writeMsg("server is already running", CNC_NOTIFY);
1795 return;
1796 }
1797
1798 if(tLX->iGameType == GME_JOIN && cClient && cClient->getStatus() != NET_DISCONNECTED) {
1799 caller->writeMsg("cannot start server lobby as client", CNC_WARNING);
1800 return;
1801 }
1802
1803 if( params.size() > 0 ) {
1804 int port = atoi(params[0]);
1805 if( port ) tLXOptions->iNetworkPort = port;
1806 }
1807
1808 tLXOptions->tGameInfo.iMaxPlayers = CLAMP(tLXOptions->tGameInfo.iMaxPlayers, 2, (int)MAX_PLAYERS);
1809
1810 tLX->iGameType = GME_HOST;
1811
1812 cClient->Shutdown();
1813 cClient->Clear();
1814
1815 // Start the server
1816 if(!cServer->StartServer()) {
1817 // Crappy
1818 caller->writeMsg("ERROR: Server wouldn't start", CNC_ERROR);
1819 return;
1820 }
1821
1822 // Lets connect me to the server
1823 if(!cClient->Initialize()) {
1824 // Crappy
1825 caller->writeMsg("ERROR: Client wouldn't initialize", CNC_ERROR);
1826 return;
1827 }
1828 cClient->Connect("127.0.0.1:" + itoa(cServer->getPort()));
1829
1830 if(tLXOptions->tGameInfo.sModDir == "")
1831 tLXOptions->tGameInfo.sModDir = "MW 1.0";
1832 if(!CGameScript::CheckFile(tLXOptions->tGameInfo.sModDir, tLXOptions->tGameInfo.sModName)) {
1833 caller->writeMsg("no mod for dedicated, " + tLXOptions->tGameInfo.sModDir + " not found");
1834 // TODO..
1835 }
1836
1837 // Get the game type
1838 if(tLXOptions->tGameInfo.gameMode == NULL)
1839 tLXOptions->tGameInfo.gameMode = GameMode(GM_DEATHMATCH);
1840
1841 if(tLXOptions->tGameInfo.sMapFile == "")
1842 tLXOptions->tGameInfo.sMapFile = "CastleStrike.lxl";
1843 tLXOptions->tGameInfo.sMapName = CMap::GetLevelName(tLXOptions->tGameInfo.sMapFile);
1844
1845 if(DedicatedControl::Get())
1846 DedicatedControl::Get()->LobbyStarted_Signal();
1847
1848 // load the menu
1849 if(!bDedicated) {
1850 DeprecatedGUI::Menu_Current_Shutdown();
1851
1852 // goto the joining dialog
1853 DeprecatedGUI::Menu_SetSkipStart(true);
1854 DeprecatedGUI::Menu_NetInitialize(false);
1855 DeprecatedGUI::Menu_Net_HostGotoLobby();
1856
1857 // when we leave the server
1858 DeprecatedGUI::tMenu->iReturnTo = DeprecatedGUI::iNetMode;
1859 }
1860 }
1861
1862 COMMAND(startGame, "start game", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1863 void Cmd_startGame::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1864 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1865 caller->writeMsg("cannot start game as client");
1866 return;
1867 }
1868
1869 if(cServer->getNumPlayers() <= 1 && !tLXOptions->tGameInfo.features[FT_AllowEmptyGames]) {
1870 caller->writeMsg("cannot start game, too few players");
1871 return;
1872 }
1873
1874 if(cServer->getState() != SVS_LOBBY) {
1875 // we have already started the game -> goto lobby back first and then restart
1876 cServer->gotoLobby(false, "Cmd_startGame and we were not in lobby before");
1877 for(int i = 0; i < cClient->getNumWorms(); ++i) {
1878 if(cClient->getWorm(i) != NULL) {
1879 /*
1880 If we have host-worm-does-wpn-selection activated, we would skip the new
1881 wpn selection if we don't force it by this way.
1882 */
1883 cClient->getWorm(i)->setWeaponsReady(false);
1884 }
1885 }
1886 }
1887
1888 // Start the game
1889 std::string errMsg;
1890 if(!cServer->StartGame(&errMsg)) {
1891 caller->writeMsg("cannot start game, got error: " + errMsg);
1892 cCache.ClearExtraEntries(); // just to be sure
1893 return;
1894 }
1895
1896 // Leave the frontend
1897 *DeprecatedGUI::bGame = true;
1898 DeprecatedGUI::tMenu->bMenuRunning = false;
1899 }
1900
1901 COMMAND_EXTRA(map, "set map", "filename", 1, 1, paramCompleters[0] = &autoCompleteForFileListCache<mapList>);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1902 void Cmd_map::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1903 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1904 caller->writeMsg("cannot set map as client");
1905 return;
1906 }
1907
1908 if(cServer->getState() != SVS_LOBBY) {
1909 caller->writeMsg("can only set map in lobby");
1910 return;
1911 }
1912
1913 std::string filename = params[0];
1914 if(filename == "") {
1915 caller->writeMsg("specify map filename");
1916 return;
1917 }
1918
1919 if(filename.find(".") == std::string::npos)
1920 filename += ".lxl";
1921
1922 if(!mapList->includes(filename)) {
1923 caller->writeMsg("map '" + filename + "' not found", CNC_WARNING);
1924 return;
1925 }
1926
1927 tLXOptions->tGameInfo.sMapFile = filename;
1928 if(!bDedicated)
1929 DeprecatedGUI::Menu_Net_HostLobbySetLevel(filename);
1930 cServer->UpdateGameLobby();
1931 }
1932
1933 COMMAND_EXTRA(mod, "set mod", "filename", 1, 1, paramCompleters[0] = &autoCompleteForFileListCache<modList>);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1934 void Cmd_mod::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1935 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1936 caller->writeMsg("cannot set mod as client");
1937 return;
1938 }
1939
1940 if(cServer->getState() != SVS_LOBBY) {
1941 caller->writeMsg("can only set mod in lobby");
1942 return;
1943 }
1944
1945 std::string filename = params[0];
1946 if(filename == "") {
1947 caller->writeMsg("specify mod filename");
1948 return;
1949 }
1950
1951 if(!modList->includes(filename)) {
1952 caller->writeMsg("mod '" + filename + "' not found", CNC_WARNING);
1953 return;
1954 }
1955
1956 tLXOptions->tGameInfo.sModDir = filename;
1957 if(!bDedicated)
1958 DeprecatedGUI::Menu_Net_HostLobbySetMod(filename);
1959 cServer->UpdateGameLobby();
1960 }
1961
1962 COMMAND(listMaps, "list all available maps", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1963 void Cmd_listMaps::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1964 for(FileListCacheIntf::Iterator::Ref i = mapList->begin(); i->isValid(); i->next())
1965 caller->pushReturnArg(i->get().first);
1966 }
1967
1968 COMMAND(listMods, "list all available mods", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1969 void Cmd_listMods::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1970 for(FileListCacheIntf::Iterator::Ref i = modList->begin(); i->isValid(); i->next())
1971 caller->pushReturnArg(i->get().first);
1972 }
1973
1974 COMMAND(gotoLobby, "go to lobby", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> &)1975 void Cmd_gotoLobby::exec(CmdLineIntf* caller, const std::vector<std::string>&) {
1976 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) {
1977 caller->writeMsg("cannot goto lobby as client");
1978 return;
1979 }
1980
1981 if(tLX->iGameType == GME_LOCAL) {
1982 // for local games, we just stop the server and go to local menu
1983 hints << "Cmd_gotoLobby: we are quitting server and going to local menu" << endl;
1984 GotoLocalMenu();
1985 return;
1986 }
1987
1988 cServer->gotoLobby(true, "Cmd_gotoLobby");
1989 // The client will get the gotolobby and handle the menu stuff.
1990 }
1991
1992 COMMAND(chatMsg, "give a global chat message", "text", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)1993 void Cmd_chatMsg::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
1994 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning())
1995 {
1996 if( cClient && (cClient->getStatus() == NET_CONNECTED || cClient->getStatus() == NET_PLAYING) )
1997 cClient->getNetEngine()->SendText( params[0], (cClient->getNumWorms() > 0) ? cClient->getWorm(0)->getName() : "" );
1998 else
1999 {
2000 caller->writeMsg("We are not running server and not connected to a server, cannot send msg");
2001 //hints << "Cmd_chatMsg::exec(): cClient " << cClient << " cClient->getStatus() " << NetStateString((ClientNetState)cClient->getStatus()) << endl;
2002 }
2003 return;
2004 }
2005
2006 std::string msg = params[0];
2007 int type = TXT_NOTICE; // TODO: make variable
2008 cServer->SendGlobalText(msg, type);
2009 }
2010
2011 COMMAND(privateMsg, "give a private message to a worm", "id text", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2012 void Cmd_privateMsg::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2013 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2014
2015 if( !w->getClient() || !w->getClient()->getNetEngine() ) {
2016 caller->writeMsg("worm " + itoa(w->getID()) + " is somehow crippled");
2017 return;
2018 }
2019
2020 int type = TXT_NOTICE; // TODO: make variable
2021 w->getClient()->getNetEngine()->SendText(params[1], type);
2022 }
2023
2024 COMMAND(getWormList, "get worm list", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2025 void Cmd_getWormList::exec(CmdLineIntf* caller, const std::vector<std::string>& params)
2026 {
2027 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) { caller->writeMsg(name + " works only as server"); return; }
2028
2029 CWorm *w = cServer->getWorms();
2030 if(w == NULL) return; // just to be sure but should be handled already
2031 for(int i=0; i < MAX_WORMS; i++, w++)
2032 {
2033 if(!w->isUsed())
2034 continue;
2035
2036 caller->pushReturnArg(itoa(w->getID()));
2037 }
2038 }
2039
2040 COMMAND(getComputerWormList, "get computer worm list", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2041 void Cmd_getComputerWormList::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2042 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) { caller->writeMsg(name + " works only as server"); return; }
2043
2044 CWorm *w = cServer->getWorms();
2045 for(int i = 0; i < MAX_WORMS; i++, w++) {
2046 if(w->isUsed() && w->getType() == PRF_COMPUTER)
2047 caller->pushReturnArg(itoa(w->getID()));
2048 }
2049 }
2050
2051 COMMAND(getWormName, "get worm name", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2052 void Cmd_getWormName::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2053 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2054 caller->pushReturnArg(w->getName());
2055 }
2056
2057 COMMAND(getWormTeam, "get worm team", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2058 void Cmd_getWormTeam::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2059 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2060 caller->pushReturnArg(itoa(w->getTeam()));
2061 }
2062
2063 COMMAND(getWormScore, "get worm score", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2064 void Cmd_getWormScore::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2065 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2066 caller->pushReturnArg(itoa(w->getLives()));
2067 caller->pushReturnArg(itoa(w->getScore()));
2068 caller->pushReturnArg(ftoa(w->getDamage()));
2069 caller->pushReturnArg(w->getAlive() ? ftoa(w->getHealth()) : "-1");
2070 }
2071
2072 COMMAND(getWormIp, "get worm IP", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2073 void Cmd_getWormIp::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2074 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2075
2076 // TODO: Perhaps we can cut out the second argument for the signal- but that would lead to the signal being much larger. Is it worth it?
2077 std::string str_addr;
2078 if(w->getClient() && w->getClient()->getChannel())
2079 NetAddrToString(w->getClient()->getChannel()->getAddress(), str_addr);
2080 if (str_addr != "")
2081 caller->pushReturnArg(str_addr);
2082 else
2083 caller->writeMsg("GetWormIp: str_addr == \"\"");
2084 }
2085
2086 COMMAND(getWormLocationInfo, "get worm location info", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2087 void Cmd_getWormLocationInfo::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2088 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2089
2090 std::string str_addr;
2091 NetAddrToString(w->getClient()->getChannel()->getAddress(), str_addr);
2092 if (str_addr != "")
2093 {
2094 IpInfo info = tIpToCountryDB->GetInfoAboutIP(str_addr);
2095 caller->pushReturnArg(info.continent);
2096 caller->pushReturnArg(info.countryName);
2097 caller->pushReturnArg(info.countryCode);
2098 }
2099 else
2100 {
2101 caller->writeMsg("GetWormCountryInfo: str_addr == \"\"");
2102 }
2103 }
2104
2105
2106 COMMAND(getWormPing, "get worm ping", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2107 void Cmd_getWormPing::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2108 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2109
2110 if(!w->getClient() || !w->getClient()->getChannel()) {
2111 caller->writeMsg("worm " + itoa(w->getID()) + " has a crippled connection");
2112 return;
2113 }
2114
2115 caller->pushReturnArg(itoa(w->getClient()->getChannel()->getPing()));
2116 }
2117
2118 COMMAND(getWormSkin, "get worm skin", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2119 void Cmd_getWormSkin::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2120 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2121 caller->pushReturnArg(itoa(w->getSkin().getDefaultColor().get()));
2122 caller->pushReturnArg(w->getSkin().getFileName());
2123 }
2124
2125 COMMAND(getWormPos, "get worm position", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2126 void Cmd_getWormPos::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2127 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2128 caller->pushReturnArg(ftoa(w->getPos().x));
2129 caller->pushReturnArg(ftoa(w->getPos().y));
2130 }
2131
2132 COMMAND(getWormVelocity, "get worm velocity", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2133 void Cmd_getWormVelocity::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2134 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2135 caller->pushReturnArg(ftoa(w->velocity().x));
2136 caller->pushReturnArg(ftoa(w->velocity().y));
2137 }
2138
2139 COMMAND(getWormProps, "get worm properties", "id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2140 void Cmd_getWormProps::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2141 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2142 caller->pushReturnArg("SpeedFactor: " + ftoa(w->speedFactor()));
2143 caller->pushReturnArg("DamageFactor: " + ftoa(w->damageFactor()));
2144 caller->pushReturnArg("ShieldFactor: " + ftoa(w->shieldFactor()));
2145 caller->pushReturnArg("CanAirJump: " + to_string<bool>(w->canAirJump()));
2146 caller->pushReturnArg("CanUseNinja: " + to_string<bool>(w->canUseNinja()));
2147 }
2148
2149 COMMAND(whoIs, "get some info about a worm", "worm-id", 1, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2150 void Cmd_whoIs::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2151 CWorm* w = getWorm(caller, params[0]); if(!w) return;
2152 caller->pushReturnArg("ID/Name: " + itoa(w->getID()) + ":" + w->getName());
2153 if(cClient && cClient->getGameLobby() && cClient->getGameLobby()->gameMode && cClient->getGameLobby()->gameMode->GameTeams() > 1)
2154 caller->pushReturnArg("Team: " + itoa(w->getTeam()));
2155 if(w->getClient()) {
2156 caller->pushReturnArg("Address: " + w->getClient()->getAddrAsString() + " (" + w->getClient()->ipInfo().countryName + ")");
2157 caller->pushReturnArg("Version: " + w->getClient()->getClientVersion().asString());
2158 caller->pushReturnArg("ConnectTime: " + ftoa((tLX->currentTime - w->getClient()->getConnectTime()).seconds()) + " secs");
2159 caller->pushReturnArg("NetSpeed: " + NetworkSpeedString((NetworkSpeed)w->getClient()->getNetSpeed()));
2160 caller->pushReturnArg("Ping: " + itoa(w->getClient()->getPing()));
2161 caller->pushReturnArg("LastResponse: " + ftoa((tLX->currentTime - w->getClient()->getLastReceived()).seconds()) + " secs ago");
2162 if(cServer->getState() != SVS_LOBBY) {
2163 caller->pushReturnArg("IsReady: " + to_string(w->getClient()->getGameReady()));
2164 }
2165 } else
2166 caller->pushReturnArg("Client is INVALID");
2167
2168 if(cServer->getState() != SVS_LOBBY) {
2169 caller->pushReturnArg("IsAlive: " + to_string(w->getAlive()));
2170 }
2171 }
2172
2173
2174
getCurrentMap()2175 static CMap* getCurrentMap() {
2176 CMap* m = cServer ? cServer->getMap() : NULL;
2177 if(!m && cClient) m = cClient->getMap();
2178 return m;
2179 }
2180
2181 COMMAND(mapInfo, "get map info", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2182 void Cmd_mapInfo::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2183 CMap* m = getCurrentMap();
2184 if(!m)
2185 caller->writeMsg("map not loaded", CNC_ERROR);
2186 else {
2187 caller->pushReturnArg(m->getName());
2188 caller->pushReturnArg(m->getFilename());
2189 caller->pushReturnArg(itoa(m->GetWidth()));
2190 caller->pushReturnArg(itoa(m->GetHeight()));
2191 }
2192 }
2193
2194 COMMAND(findSpot, "find randm free spot in map (close to pos)", "[(x,y)]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2195 void Cmd_findSpot::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2196 if(tLX->iGameType == GME_JOIN || !cServer || !cServer->isServerRunning()) { caller->writeMsg(name + " works only as server"); return; }
2197 if(cServer->getMap() == NULL) {
2198 caller->writeMsg("map not loaded", CNC_ERROR);
2199 return;
2200 }
2201
2202 VectorD2<int> v;
2203 if(params.size() == 0)
2204 v = cServer->FindSpot();
2205 else {
2206 bool fail = false;
2207 VectorD2<int> closev = from_string< VectorD2<int> >(params[0], fail);
2208 if(fail) {
2209 printUsage(caller);
2210 return;
2211 }
2212 v = cServer->FindSpotCloseToPos(closev);
2213 }
2214
2215 caller->pushReturnArg(itoa(v.x));
2216 caller->pushReturnArg(itoa(v.y));
2217 }
2218
2219
2220
2221 COMMAND(dumpGameState, "dump game state", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2222 void Cmd_dumpGameState::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2223 GameState state = currentGameState();
2224 caller->writeMsg("GameState: " + GameStateAsString(state), CNC_DEV);
2225 if(state == S_INACTIVE) return;
2226 if(cServer && cServer->isServerRunning()) cServer->DumpGameState(caller);
2227 else if(cClient) cClient->DumpGameState(caller);
2228 else caller->writeMsg("server nor client correctly initialised", CNC_ERROR);
2229 }
2230
2231 COMMAND(dumpSysState, "dump system state", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2232 void Cmd_dumpSysState::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2233 hints << "Threads:" << endl;
2234 threadPool->dumpState(stdoutCLI());
2235 hints << "Tasks:" << endl;
2236 taskManager->dumpState(stdoutCLI());
2237 hints << "Free system memory: " << (GetFreeSysMemory() / 1024) << " KB" << endl;
2238 hints << "Cache size: " << (cCache.GetCacheSize() / 1024) << " KB" << endl;
2239 hints << "Current time: " << GetDateTimeText() << endl;
2240 }
2241
2242 COMMAND(dumpConnections, "dump connections of server", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2243 void Cmd_dumpConnections::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2244 if(cServer) cServer->DumpConnections();
2245 else caller->writeMsg("server not initialised");
2246 }
2247
2248 #ifdef MEMSTATS
2249 COMMAND(printMemStats, "print memory stats", "", 0, 0);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2250 void Cmd_printMemStats::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2251 printMemStats();
2252 }
2253 #endif
2254
2255
2256 COMMAND(saveConfig, "save current config", "[filename]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2257 void Cmd_saveConfig::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2258 if(tLXOptions) {
2259 if(params.size() == 0)
2260 tLXOptions->SaveToDisc();
2261 else
2262 tLXOptions->SaveToDisc("cfg/" + params[0]);
2263 }
2264 else
2265 caller->writeMsg("options structure not initialised", CNC_ERROR);
2266 }
2267
2268 COMMAND(saveConfigSection, "save a specific config section", "section filename", 2, 2);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2269 void Cmd_saveConfigSection::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2270 if(tLXOptions) {
2271 std::string section = params[0];
2272 if(section == "") {
2273 printUsage(caller);
2274 return;
2275 }
2276 if(section[section.size()-1] == '.') {
2277 caller->writeMsg("section must not end with a '.'");
2278 return;
2279 }
2280 if(!CScriptableVars::haveSomethingWith(section + ".")) {
2281 caller->writeMsg("section '" + section + "' is unknown");
2282 return;
2283 }
2284 tLXOptions->SaveSectionToDisc(section, "cfg/" + params[1]);
2285 }
2286 else
2287 caller->writeMsg("options structure not initialised", CNC_ERROR);
2288 }
2289
2290 COMMAND(loadConfig, "load config file", "[filename]", 0, 1);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2291 void Cmd_loadConfig::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2292 if(tLXOptions) {
2293 if(params.size() == 0)
2294 tLXOptions->LoadFromDisc();
2295 else
2296 tLXOptions->LoadFromDisc("cfg/" + params[0]);
2297 }
2298 else
2299 caller->writeMsg("options structure not initialised", CNC_ERROR);
2300 }
2301
2302 // Note: It doesn't make sense to send custom signal without args, so force at least 1 arg.
2303 COMMAND(signal, "send custom signal to dedicated script", "<args>", 1, UINT_MAX);
exec(CmdLineIntf * caller,const std::vector<std::string> & params)2304 void Cmd_signal::exec(CmdLineIntf* caller, const std::vector<std::string>& params) {
2305 if(!DedicatedControl::Get()) {
2306 caller->writeMsg("dedicated control not available", CNC_ERROR);
2307 return;
2308 }
2309
2310 DedicatedControl::Get()->Custom_Signal( std::list<std::string>(params.begin(), params.end()) );
2311 }
2312
2313
HandleCommand(const CmdLineIntf::Command & command)2314 static void HandleCommand(const CmdLineIntf::Command& command) {
2315 std::string cmdstr = command.cmd; TrimSpaces(cmdstr);
2316 std::string params;
2317 size_t f = cmdstr.find(' ');
2318 if(f != std::string::npos) {
2319 params = cmdstr.substr(f + 1);
2320 TrimSpaces(params);
2321 cmdstr = cmdstr.substr(0, f);
2322 }
2323 if(cmdstr == "") return;
2324
2325 CommandMap::iterator cmd = commands.find(cmdstr);
2326
2327 if(cmd != commands.end()) {
2328 cmd->second->exec(command.sender, params);
2329 }
2330 // we must handle this command seperate
2331 else if( stringcaseequal(cmdstr, "nextsignal") ) {
2332 if(DedicatedControl::Get()) {
2333 if(!DedicatedControl::Get()->GetNextSignal(command.sender)) return;
2334 }
2335 else
2336 command.sender->writeMsg("nextsignal is only available in dedicated mode");
2337 }
2338 else {
2339 command.sender->writeMsg("unknown command: " + cmdstr + " " + params);
2340 }
2341
2342 command.sender->finalizeReturn();
2343 }
2344
2345
2346 struct CmdQueue {
2347
2348 SDL_mutex* pendingCommandsMutex;
2349 std::list<CmdLineIntf::Command> pendingCommands;
2350
CmdQueueCmdQueue2351 CmdQueue() {
2352 pendingCommandsMutex = SDL_CreateMutex();
2353 }
2354
~CmdQueueCmdQueue2355 ~CmdQueue() {
2356 SDL_DestroyMutex(pendingCommandsMutex);
2357 pendingCommandsMutex = NULL;
2358 }
2359
2360 };
2361
2362 static CmdQueue cmdQueue;
2363
2364
Execute(const CmdLineIntf::Command & command)2365 void Execute(const CmdLineIntf::Command& command) {
2366 ScopedLock lock(cmdQueue.pendingCommandsMutex);
2367 cmdQueue.pendingCommands.push_back(command);
2368 }
2369
HandlePendingCommands()2370 void HandlePendingCommands() {
2371 std::list<CmdLineIntf::Command> cmds;
2372 {
2373 ScopedLock lock(cmdQueue.pendingCommandsMutex);
2374 cmds.swap(cmdQueue.pendingCommands);
2375 }
2376
2377 while( cmds.size() > 0 ) {
2378 CmdLineIntf::Command command = cmds.front();
2379 cmds.pop_front();
2380
2381 HandleCommand(command);
2382 command.sender->finishedCommand(command.cmd);
2383 }
2384 }
2385
2386
2387 ///////////////////
2388 // Auto complete a command
AutoComplete(const std::string & text,size_t pos,CmdLineIntf & cli,AutocompletionInfo & autocomplete)2389 bool AutoComplete(const std::string& text, size_t pos, CmdLineIntf& cli, AutocompletionInfo& autocomplete)
2390 {
2391 struct Finalizer {
2392 AutocompletionInfo& autocomplete;
2393 Finalizer(AutocompletionInfo& i) : autocomplete(i) {}
2394 ~Finalizer() { autocomplete.finalize(); }
2395 }
2396 finalizer(autocomplete);
2397
2398 ParamSeps seps = ParseParams_Seps(text);
2399 if(seps.size() == 0) {
2400 // no text at all
2401 return false;
2402 }
2403
2404 ParamSeps::value_type firstSep = *seps.begin();
2405 if(pos < firstSep.first) {
2406 // before any txt
2407 return false;
2408 }
2409
2410 ParamSeps::iterator it = seps.lower_bound(pos);
2411 if(it == seps.end() || pos != it->first) --it;
2412
2413 Command* cmd = NULL;
2414 if(it != seps.begin() || pos > it->first + it->second) {
2415 // it means that the command is already complete, or at least should be
2416 std::string cmdStr = text.substr(firstSep.first, firstSep.second);
2417 cmd = Cmd_GetCommand(cmdStr);
2418 if(!cmd) {
2419 cli.writeMsg("command unknown", CNC_WARNING);
2420 return false;
2421 }
2422 }
2423
2424 unsigned int paramIndex = 0;
2425 for(ParamSeps::iterator j = seps.begin(); j != it; ++j, ++paramIndex) {}
2426
2427 if(pos > it->first + it->second) {
2428 // we are between two parameters or at the very end
2429 if(cmd) {
2430 // it is the correct corresponding param to pos
2431 AutocompleteRequest request = {
2432 cli, autocomplete, AutocompletionInfo::InputState(text, pos),
2433 text.substr(0, pos), text.substr(pos),
2434 "", 0
2435 };
2436
2437 // Note: just use paramIndex as it is because we want to point to next param
2438 return cmd->completeParam(paramIndex, request);
2439 }
2440 else
2441 cli.writeMsg("command unknown", CNC_WARNING);
2442
2443 return false;
2444 }
2445
2446 // it is the correct corresponding param to pos
2447 AutocompleteRequest request = {
2448 cli, autocomplete, AutocompletionInfo::InputState(text, pos),
2449 text.substr(0, it->first), text.substr(it->first + it->second),
2450 text.substr(it->first, it->second), pos - it->first
2451 };
2452
2453 if(it == seps.begin())
2454 return autoCompleteCommand(NULL, request);
2455
2456 if(!cmd) {
2457 cli.writeMsg("command unknown", CNC_WARNING);
2458 return false;
2459 }
2460
2461 if(paramIndex > 0) paramIndex--;
2462 return cmd->completeParam(paramIndex, request);
2463 }
2464
2465