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 = &notes;
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