1 /*
2  *  DedicatedControl.cpp
3  *  OpenLieroX
4  *
5  *  Created by Albert Zeyer on 11.01.08.
6  *  code under LGPL
7  *
8  */
9 
10 // define HAVE_BOOST if you want to compile dedicated server for Win32 and have Boost headers installed.
11 
12 // A workaround for a bug in boost - doesn't compile with DEBUG_NEW in MSVC 2005
13 #if defined(_MSC_VER) && _MSC_VER == 1400
14 #undef new
15 #endif
16 
17 #include <string>
18 #include <sstream>
19 #include <stdexcept>
20 #include "ThreadPool.h"
21 #include <fcntl.h>
22 #include <iostream>
23 
24 #include "Debug.h"
25 #include "LieroX.h"
26 #include "IpToCountryDB.h"
27 #include "DedicatedControl.h"
28 #include "FindFile.h"
29 #include "StringUtils.h"
30 #include "CMap.h"
31 #include "ProfileSystem.h"
32 #include "CClient.h"
33 #include "CServer.h"
34 #include "CWorm.h"
35 #include "CGameScript.h"
36 #include "Unicode.h"
37 #include "Protocol.h"
38 #include "CScriptableVars.h"
39 #include "CClientNetEngine.h"
40 #include "CChannel.h"
41 #include "CServerConnection.h"
42 #include "CServerNetEngine.h"
43 #include "Command.h"
44 #include "Process.h"
45 #include "Event.h"
46 #include "CGameMode.h"
47 #include "Cache.h"
48 #include "AuxLib.h"
49 #include "DeprecatedGUI/Menu.h"
50 
51 #ifdef _MSC_VER
52 #pragma warning(disable: 4996)
53 #endif
54 
55 
56 using std::endl;
57 
58 
59 
60 static DedicatedControl* dedicatedControlInstance = NULL;
61 
Get()62 DedicatedControl* DedicatedControl::Get()
63 {
64 	return dedicatedControlInstance;
65 }
66 
Init()67 bool DedicatedControl::Init() {
68 	dedicatedControlInstance = new DedicatedControl();
69 	return dedicatedControlInstance->Init_priv();
70 }
71 
Uninit()72 void DedicatedControl::Uninit() {
73 	delete dedicatedControlInstance;
74 	dedicatedControlInstance = NULL;
75 }
76 
77 
78 
79 struct ScriptCmdLineIntf : CmdLineIntf {
80 	Process pipe;
81 	ThreadPoolItem* thread;
82 
83 
ScriptCmdLineIntfScriptCmdLineIntf84 	ScriptCmdLineIntf() : thread(NULL) {
85 	}
86 
~ScriptCmdLineIntfScriptCmdLineIntf87 	~ScriptCmdLineIntf() {
88 		breakCurrentScript();
89 	}
90 
91 
pushReturnArgScriptCmdLineIntf92 	void pushReturnArg(const std::string& str) {
93 		pipeOut() << ":" << str << endl;
94 	}
95 
finalizeReturnScriptCmdLineIntf96 	void finalizeReturn() {
97 		pipeOut() << "." << endl;
98 	}
99 
writeMsgScriptCmdLineIntf100 	void writeMsg(const std::string& str, CmdLineMsgType type) {
101 		// TODO: handle type
102 		hints << "Script Dedicated: " << str << endl;
103 	}
104 
105 
pipeOutScriptCmdLineIntf106 	std::ostream& pipeOut() {
107 		return pipe.in();
108 	}
109 
closePipeScriptCmdLineIntf110 	void closePipe() {
111 		pipe.close();
112 	}
113 
havePipeScriptCmdLineIntf114 	bool havePipe() {
115 		return thread != NULL;
116 	}
117 
118 
119 
breakCurrentScriptScriptCmdLineIntf120 	bool breakCurrentScript() {
121 		if(thread) {
122 			notes << "waiting for pipeThread ..." << endl;
123 			pipe.close();
124 			threadPool->wait(thread, NULL);
125 		}
126 		thread = NULL;
127 
128 		return true;
129 	}
130 
loadScriptScriptCmdLineIntf131 	bool loadScript(const std::string& script, const std::string& scriptArgs)
132 	{
133 		breakCurrentScript();
134 		return loadScript_Pipe(script, scriptArgs);
135 	}
136 
137 
loadScript_PipeScriptCmdLineIntf138 	bool loadScript_Pipe(const std::string& script, const std::string& scriptArgs) {
139 		breakCurrentScript();
140 
141 		std::string scriptfn = GetAbsolutePath(GetFullFileName(script));
142 		if(script != "/dev/null") {
143 			if(!IsFileAvailable(scriptfn, true)) {
144 				errors << "Dedicated: " << scriptfn << " not found" << endl;
145 				return false;
146 			}
147 
148 			notes << "Dedicated server: running script \"" << scriptfn << "\" args \"" << scriptArgs << "\"" << endl;
149 			// HINT: If a script need this change in his own directory, it is a bug in the script.
150 			// If we change into script directory, ded function "getfullfilename" won't work anymore
151 			std::vector<std::string> args = explode(script + " " + scriptArgs, " ");
152 			if(!pipe.open(scriptfn, args, ExtractDirectory(scriptfn) )) {
153 				errors << "cannot start dedicated server - cannot run script " << scriptfn << endl;
154 				return false;
155 			}
156 
157 			thread = threadPool->start(&ScriptCmdLineIntf::pipeThreadFunc, this, "Ded pipe watcher");
158 		}
159 		else
160 			notes << "Dedicated server: not running any script" << endl;
161 
162 		return true;
163 	}
164 
ScriptSignalHandlerScriptCmdLineIntf165 	void ScriptSignalHandler() {} // stub
166 
167 
168 
169 	// Pipe functions
170 
171 	// reading lines from pipe-out and put them to pipeOutput
pipeThreadFuncScriptCmdLineIntf172 	static int pipeThreadFunc(void* o) {
173 		ScriptCmdLineIntf* owner = (ScriptCmdLineIntf*)o;
174 
175 		while(!owner->pipe.out().eof()) {
176 			std::string buf;
177 			getline(owner->pipe.out(), buf);
178 
179 			Execute( owner, buf );
180 		}
181 		return 0;
182 	}
183 
184 };
185 
186 struct StdinCmdLineIntf : CmdLineIntf {
187 	ThreadPoolItem* thread;
188 
StdinCmdLineIntfStdinCmdLineIntf189 	StdinCmdLineIntf() {
190 		thread = threadPool->start(&StdinCmdLineIntf::stdinThreadFunc, this, "Ded stdin watcher");
191 	}
192 
~StdinCmdLineIntfStdinCmdLineIntf193 	~StdinCmdLineIntf() {
194 		notes << "waiting for stdinThread ..." << endl;
195 		threadPool->wait(thread, NULL);
196 		thread = NULL;
197 	}
198 
pushReturnArgStdinCmdLineIntf199 	void pushReturnArg(const std::string& str) {
200 		notes << "Dedicated return: " << str << endl;
201 	}
202 
finalizeReturnStdinCmdLineIntf203 	void finalizeReturn() {
204 		notes << "Dedicated return." << endl;
205 	}
206 
writeMsgStdinCmdLineIntf207 	void writeMsg(const std::string& str, CmdLineMsgType type) {
208 		// TODO: handle type
209 		hints << "STDIN Dedicated: " << str << endl;
210 	}
211 
212 
213 	// reading lines from stdin and put them to pipeOutput
stdinThreadFuncStdinCmdLineIntf214 	static int stdinThreadFunc(void* o) {
215 		StdinCmdLineIntf* owner = (StdinCmdLineIntf*)o;
216 
217 #ifndef WIN32
218 		// TODO: there's no fcntl for Windows!
219 		if(fcntl(0, F_SETFL, O_NONBLOCK) == -1)
220 #endif
221 			warnings << "ERROR setting standard input into non-blocking mode" << endl;
222 
223 		while(true) {
224 			std::string buf;
225 			while(true) {
226 				SDL_Delay(10); // TODO: select() here
227 				if(tLX->bQuitGame) return 0;
228 
229 				char c;
230 
231 				if(read(0, &c, 1) >= 0) {
232 					if(c == '\n') break;
233 					// TODO: why is this needed? is that WIN32 only?
234 					if(c == -52) return 0;  // CTRL-C
235 					buf += c;
236 				}
237 			}
238 
239 			Execute( owner, buf );
240 		}
241 		return 0;
242 	}
243 };
244 
245 
246 
247 
248 struct DedIntern {
249 
GetDedIntern250 	static DedIntern* Get() { return (DedIntern*)dedicatedControlInstance->internData; }
251 
252 	ScriptCmdLineIntf* scriptInterface;
253 	StdinCmdLineIntf* stdinInterface;
254 
255 	bool quitSignal;
256 	SDL_mutex* pendingSignalsMutex;
257 	bool waitingForNextSignal;
258 	struct Signal {
259 		std::string name;
260 		std::list<std::string> args;
SignalDedIntern::Signal261 		Signal(const std::string& n, const std::list<std::string>& a) : name(n), args(a) {}
262 	};
263 	std::list<Signal> pendingSignals;
264 
265 
266 
DedInternDedIntern267 	DedIntern() :
268 		scriptInterface(NULL), stdinInterface(NULL),
269 		quitSignal(false),
270 		pendingSignalsMutex(NULL), waitingForNextSignal(false)
271 	{
272 		dedicatedControlInstance->internData = this;
273 		pendingSignalsMutex = SDL_CreateMutex();
274 
275 		scriptInterface = new ScriptCmdLineIntf();
276 		stdinInterface = new StdinCmdLineIntf();
277 	}
278 
~DedInternDedIntern279 	~DedIntern() {
280 		Sig_Quit();
281 		quitSignal = true;
282 
283 		delete stdinInterface; stdinInterface = NULL;
284 		delete scriptInterface; scriptInterface = NULL;
285 
286 		SDL_DestroyMutex(pendingSignalsMutex);
287 
288 		notes << "DedicatedControl destroyed" << endl;
289 		dedicatedControlInstance->internData = NULL;
290 	}
291 
292 
pushSignalDedIntern293 	void pushSignal(const std::string& name, const std::list<std::string>& args = std::list<std::string>()) {
294 		if(!scriptInterface->havePipe()) return;
295 		ScopedLock lock(pendingSignalsMutex);
296 		if(waitingForNextSignal) {
297 			if(pendingSignals.size() > 0)
298 				errors << "Dedicated pending signals queue should be empty when the script is waiting for next signal" << endl;
299 			scriptInterface->pushReturnArg(name);
300 			for(std::list<std::string>::const_iterator i = args.begin(); i != args.end(); ++i)
301 				scriptInterface->pushReturnArg(*i);
302 			scriptInterface->finalizeReturn();
303 			waitingForNextSignal = false;
304 		}
305 		else {
306 			pendingSignals.push_back(Signal(name, args));
307 			if(pendingSignals.size() > 1000) {
308 				warnings << "Dedicated pending signals queue got too full!" << endl;
309 				pendingSignals.pop_front();
310 			}
311 		}
312 	}
313 
pushSignalDedIntern314 	void pushSignal(const std::string& name, const std::string& arg1) {
315 		std::list<std::string> args; args.push_back(arg1);
316 		pushSignal(name, args);
317 	}
318 
pushSignalDedIntern319 	void pushSignal(const std::string& name, const std::string& arg1, const std::string& arg2) {
320 		std::list<std::string> args; args.push_back(arg1); args.push_back(arg2);
321 		pushSignal(name, args);
322 	}
323 
pushSignalDedIntern324 	void pushSignal(const std::string& name, const std::string& arg1, const std::string& arg2, const std::string& arg3) {
325 		std::list<std::string> args; args.push_back(arg1); args.push_back(arg2); args.push_back(arg3);
326 		pushSignal(name, args);
327 	}
328 
329 
330 	// ----------------------------------
331 	// ----------- signals --------------
332 
Sig_LobbyStartedDedIntern333 	void Sig_LobbyStarted() { pushSignal("lobbystarted"); }
Sig_GameLoopStartDedIntern334 	void Sig_GameLoopStart() { pushSignal("gameloopstart"); }
Sig_GameLoopEndDedIntern335 	void Sig_GameLoopEnd() {
336 		pushSignal("gameloopend");
337 		if(currentGameState() != S_SVRLOBBY && currentGameState() != S_CLILOBBY)
338 			// This is because of the current game logic: It will end the game
339 			// loop and then return to the lobby but only in the case if we got a
340 			// BackToLobby-signal before; if we didn't get such a signal and
341 			// the gameloop was ended, that means that the game was stopped
342 			// completely.
343 			notes << "gameloopend without backtolobby -> stop" << endl;
344 	}
Sig_WeaponSelectionsDedIntern345 	void Sig_WeaponSelections() { pushSignal("weaponselections"); }
Sig_GameStartedDedIntern346 	void Sig_GameStarted() { pushSignal("gamestarted"); }
Sig_BackToLobbyDedIntern347 	void Sig_BackToLobby() { pushSignal("backtolobby"); }
Sig_QuitDedIntern348 	void Sig_Quit() { pushSignal("quit"); scriptInterface->closePipe(); }
349 
Sig_ConnectingDedIntern350 	void Sig_Connecting(const std::string& addr) { pushSignal("connecting", addr); }
Sig_ConnectErrorDedIntern351 	void Sig_ConnectError(const std::string& err) { pushSignal("connecterror", err); }
Sig_ConnectedDedIntern352 	void Sig_Connected() { pushSignal("connected"); }
Sig_ClientErrorDedIntern353 	void Sig_ClientError() { pushSignal("clienterror"); }
Sig_ClientConnectionErrorDedIntern354 	void Sig_ClientConnectionError(const std::string& err) { pushSignal("connectionerror", err); }
Sig_ClientGameStartedDedIntern355 	void Sig_ClientGameStarted() { pushSignal("clientgamestarted"); }
Sig_ClientGotoLobbyDedIntern356 	void Sig_ClientGotoLobby() { pushSignal("clientbacktolobby"); }
357 
Sig_NewWormDedIntern358 	void Sig_NewWorm(CWorm* w) { pushSignal("newworm", itoa(w->getID()), w->getName()); }
Sig_WormLeftDedIntern359 	void Sig_WormLeft(CWorm* w) { pushSignal("wormleft", itoa(w->getID()), w->getName()); }
Sig_ChatMessageDedIntern360 	void Sig_ChatMessage(CWorm* w, const std::string& message) { pushSignal("chatmessage", itoa(w->getID()), message); }
Sig_PrivateMessageDedIntern361 	void Sig_PrivateMessage(CWorm* w, CWorm* to, const std::string& message) { pushSignal("privatemessage", itoa(w->getID()), itoa(to->getID()), message); }
Sig_WormDiedDedIntern362 	void Sig_WormDied(int died, int killer) { pushSignal("wormdied", itoa(died), itoa(killer)); }
Sig_WormSpawnedDedIntern363 	void Sig_WormSpawned(CWorm* worm) { pushSignal("wormspawned", itoa(worm->getID())); }
Sig_WormGotAdminDedIntern364 	void Sig_WormGotAdmin(CWorm* w) { pushSignal("wormgotadmin", itoa(w->getID())); }
Sig_WormAuthorizedDedIntern365 	void Sig_WormAuthorized(CWorm* w) { pushSignal("wormauthorized", itoa(w->getID())); }
366 
Sig_TimerDedIntern367 	void Sig_Timer(const std::string& name) { pushSignal("timer", name); }
368 
369 
370 	// ----------------------------------
371 	// ---------- frame handlers --------
372 
Frame_ServerLobbyDedIntern373 	void Frame_ServerLobby() {
374 		// Process the server & client frames
375 		cServer->Frame();
376 		cClient->Frame();
377 	}
378 
Frame_PlayingDedIntern379 	void Frame_Playing() {
380 		// we don't have to process server/client frames here as it is done already by the main loop
381 	}
382 
Frame_ClientConnectingDedIntern383 	void Frame_ClientConnecting() {
384 		cClient->Frame();
385 
386 		// are we connected?
387 		if(cClient->getStatus() == NET_CONNECTED) {
388 			Sig_Connected();
389 			return;
390 		}
391 
392 		// error?
393 		if(cClient->getBadConnection()) {
394 			warnings << "Bad connection: " << cClient->getBadConnectionMsg() << endl;
395 			Sig_ConnectError(cClient->getBadConnectionMsg());
396 			cClient->Shutdown();
397 			return;
398 		}
399 	}
400 
Frame_ClientLobbyDedIntern401 	void Frame_ClientLobby() {
402 		// Process the client
403 		cClient->Frame();
404 
405 		// If there is a client error, leave
406 		if(cClient->getClientError()) {
407 			Sig_ClientError();
408 			return;
409 		}
410 
411 		// If we have started, leave the frontend
412 		if(cClient->getGameReady()) {
413 			// Leave the frontend
414 			*DeprecatedGUI::bGame = true;
415 			DeprecatedGUI::tMenu->bMenuRunning = false;
416 			tLX->iGameType = GME_JOIN;
417 			Sig_ClientGameStarted();
418 			return;
419 		}
420 
421 
422 		// Check if the communication link between us & server is still ok
423 		if(cClient->getServerError()) {
424 			warnings << "Client connection error: " << cClient->getServerErrorMsg() << endl;
425 			Sig_ClientConnectionError(cClient->getServerErrorMsg());
426 			cClient->Disconnect();
427 			cClient->Shutdown();
428 			SetQuitEngineFlag("Frame_ClientLobby: connection error");
429 			return;
430 		}
431 	}
432 
Frame_BasicDedIntern433 	void Frame_Basic() {
434 
435 		// TODO: make this clean!
436 		// TODO: no static var here
437 		// TODO: not a single timer, the script should register as much as it want
438 		// TODO: each timer should be configurable
439 		static AbsTime lastTimeHandlerCalled = tLX->currentTime;
440 		if( tLX->currentTime > lastTimeHandlerCalled + 1.0f ) // Call once per second, when no signals pending, TODO: configurable from ded script
441 		{
442 			Sig_Timer("second-ticker"); // TODO ...
443 			lastTimeHandlerCalled = tLX->currentTime;
444 		}
445 
446 
447 		// Some debugging stuff
448 		// TODO: This really much spams my logs. There should be a better way to inform about this.
449 #if 0
450 //#if DEBUG
451 		int fps = GetFPS();
452 		static AbsTime lastFpsPrint = tLX->currentTime;
453 		if (tLX->currentTime - lastFpsPrint >= 20.0f)  {
454 			notes << "Current FPS: " << fps << endl;
455 			lastFpsPrint = tLX->currentTime;
456 		}
457 
458 		static AbsTime lastBandwidthPrint = tLX->currentTime;
459 		if (tLX->currentTime - lastBandwidthPrint >= 20.0f)  {
460 			// Upload and download rates
461 			float up = 0;
462 			float down = 0;
463 
464 			// Get the rates
465 			if( tLX->iGameType == GME_JOIN ) {
466 				if(cClient->getChannel()) {
467 					down = cClient->getChannel()->getIncomingRate() / 1024.0f;
468 					up = cClient->getChannel()->getOutgoingRate() / 1024.0f;
469 				}
470 			}
471 			else if( tLX->iGameType == GME_HOST ) {
472 				down = cServer->GetDownload() / 1024.0f;
473 				up = cServer->GetUpload() / 1024.0f;
474 			}
475 
476 			notes << "Current upload rate: " << up << " kB/s" << endl;
477 			notes << "Current download rate: " << down << " kB/s" << endl;
478 			lastBandwidthPrint = tLX->currentTime;
479 		}
480 #endif
481 
482 
483 		switch(currentGameState()) {
484 			case S_INACTIVE: break;
485 			case S_SVRLOBBY: Frame_ServerLobby(); break;
486 			case S_SVRWEAPONS: Frame_Playing(); break;
487 			case S_SVRPLAYING: Frame_Playing(); break;
488 			case S_CLICONNECTING: Frame_ClientConnecting(); break;
489 			case S_CLILOBBY: Frame_ClientLobby(); break;
490 			case S_CLIPLAYING: Frame_Playing(); break;
491 			case S_CLIWEAPONS: Frame_Playing(); break;
492 		}
493 	}
494 };
495 
496 
DedicatedControl()497 DedicatedControl::DedicatedControl() : internData(NULL) {}
~DedicatedControl()498 DedicatedControl::~DedicatedControl() { if(internData) delete internData; internData = NULL; }
499 
500 // gets called from static DedicatedControl::Init()
Init_priv()501 bool DedicatedControl::Init_priv() {
502 	DedIntern* dedIntern = new DedIntern; // constr will assign this->internData to this obj
503 	if(tLXOptions->sDedicatedScript != "" && tLXOptions->sDedicatedScript != "/dev/null") {
504 		if(IsAbsolutePath(tLXOptions->sDedicatedScript)) {
505 			dedIntern->scriptInterface->loadScript(tLXOptions->sDedicatedScript, tLXOptions->sDedicatedScriptArgs);
506 			return true;
507 		}
508 
509 		if(strStartsWith(tLXOptions->sDedicatedScript, "scripts/")) { // old clients will use it like that
510 			return dedIntern->scriptInterface->loadScript(tLXOptions->sDedicatedScript, tLXOptions->sDedicatedScriptArgs);
511 			return true;
512 		}
513 
514 		dedIntern->scriptInterface->loadScript("scripts/" + tLXOptions->sDedicatedScript, tLXOptions->sDedicatedScriptArgs);
515 	}
516 	else
517 		dedIntern->scriptInterface->loadScript("/dev/null", "");
518 
519 	return true;
520 }
521 
522 
523 // This is the main game loop, the one that do all the simulation etc.
GameLoopStart_Signal()524 void DedicatedControl::GameLoopStart_Signal() { internData->Sig_GameLoopStart(); }
GameLoopEnd_Signal()525 void DedicatedControl::GameLoopEnd_Signal() { internData->Sig_GameLoopEnd(); }
LobbyStarted_Signal()526 void DedicatedControl::LobbyStarted_Signal() { internData->Sig_LobbyStarted(); }
BackToServerLobby_Signal()527 void DedicatedControl::BackToServerLobby_Signal() { internData->Sig_BackToLobby(); }
BackToClientLobby_Signal()528 void DedicatedControl::BackToClientLobby_Signal() { internData->Sig_ClientGotoLobby(); }
WeaponSelections_Signal()529 void DedicatedControl::WeaponSelections_Signal() { internData->Sig_WeaponSelections(); }
GameStarted_Signal()530 void DedicatedControl::GameStarted_Signal() { internData->Sig_GameStarted(); }
Connecting_Signal(const std::string & addr)531 void DedicatedControl::Connecting_Signal(const std::string& addr) { internData->Sig_Connecting(addr); }
NewWorm_Signal(CWorm * w)532 void DedicatedControl::NewWorm_Signal(CWorm* w) { internData->Sig_NewWorm(w); }
WormLeft_Signal(CWorm * w)533 void DedicatedControl::WormLeft_Signal(CWorm* w) { internData->Sig_WormLeft(w); }
ChatMessage_Signal(CWorm * w,const std::string & message)534 void DedicatedControl::ChatMessage_Signal(CWorm* w, const std::string& message) { internData->Sig_ChatMessage(w,message); }
PrivateMessage_Signal(CWorm * w,CWorm * to,const std::string & message)535 void DedicatedControl::PrivateMessage_Signal(CWorm* w, CWorm* to, const std::string& message) { internData->Sig_PrivateMessage(w,to,message); }
WormDied_Signal(CWorm * worm,CWorm * killer)536 void DedicatedControl::WormDied_Signal(CWorm* worm, CWorm* killer) { internData->Sig_WormDied(worm->getID(), killer ? killer->getID() : -1); }
WormSpawned_Signal(CWorm * worm)537 void DedicatedControl::WormSpawned_Signal(CWorm* worm){ internData->Sig_WormSpawned(worm); };
WormGotAdmin_Signal(CWorm * worm)538 void DedicatedControl::WormGotAdmin_Signal(CWorm* worm){ internData->Sig_WormGotAdmin(worm); };
WormAuthorized_Signal(CWorm * worm)539 void DedicatedControl::WormAuthorized_Signal(CWorm* worm){ internData->Sig_WormAuthorized(worm); };
Custom_Signal(const std::list<std::string> & args)540 void DedicatedControl::Custom_Signal(const std::list<std::string>& args) { internData->pushSignal("custom", args); }
541 
Menu_Frame()542 void DedicatedControl::Menu_Frame() { internData->Frame_Basic(); }
GameLoop_Frame()543 void DedicatedControl::GameLoop_Frame() { internData->Frame_Basic(); }
544 
ChangeScript(const std::string & filename,const std::string & args)545 void DedicatedControl::ChangeScript(const std::string& filename, const std::string& args) {
546 	{
547 		ScopedLock lock(internData->pendingSignalsMutex);
548 		internData->waitingForNextSignal = false;
549 		internData->pendingSignals.clear();
550 	}
551 
552 	if(filename == "" || filename == "/dev/null")
553 		internData->scriptInterface->loadScript("/dev/null", "");
554 	else
555 		internData->scriptInterface->loadScript("scripts/" + filename, args);
556 }
557 
GetNextSignal(CmdLineIntf * sender)558 bool DedicatedControl::GetNextSignal(CmdLineIntf* sender) {
559 	if(sender != internData->scriptInterface) {
560 		sender->writeMsg("nextsignal can only be called from the dedicated script");
561 		return true; // true means that finalizeReturn() should be called from caller of this func
562 	}
563 
564 	ScopedLock lock(internData->pendingSignalsMutex);
565 	if(internData->pendingSignals.size() > 0) {
566 		DedIntern::Signal sig = internData->pendingSignals.front();
567 		internData->pendingSignals.pop_front();
568 		sender->pushReturnArg(sig.name);
569 		for(std::list<std::string>::iterator i = sig.args.begin(); i != sig.args.end(); ++i)
570 			sender->pushReturnArg(*i);
571 		return true;
572 	}
573 	else {
574 		// do nothing, we will send the next signal when it arrives
575 		internData->waitingForNextSignal = true;
576 		return false;
577 	}
578 }
579