1 /***************************************************************************
2                           gamecorethread.cpp  -  The thread of the simulation
3                              -------------------
4     begin                : wo jan 12 2005
5     copyright            : (C) 2005 by CJP
6     email                : cornware-cjp@users.sourceforge.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include <unistd.h>
19 #include <cstdio>
20 
21 #include <libintl.h>
22 #define _(String) gettext (String)
23 #define N_(String1, String2, n) ngettext ((String1), (String2), (n))
24 
25 #include "gamecorethread.h"
26 #include "main.h"
27 #include "movobjinput.h"
28 #include "timer.h"
29 #include "netmessages.h"
30 
CGamecoreThread()31 CGamecoreThread::CGamecoreThread()
32 {
33 	m_SaveHiscore = true;
34 	m_Trackfile = "tracks/default.track";
35 
36 	m_GO_minPlayers = false;
37 	m_GO_startCommand = false;
38 	m_GameStatus = "Error: uninitialised status"; //should never occur
39 }
40 
~CGamecoreThread()41 CGamecoreThread::~CGamecoreThread(){
42 }
43 
GO_minPlayers()44 void CGamecoreThread::GO_minPlayers()
45 {
46 	consolethread.write("Minimum number of players reached");
47 	m_GO_minPlayers = true;
48 
49 	if(m_GO_minPlayers && m_GO_startCommand)
50 		start(); //start the thread
51 }
52 
GO_startCommand()53 void CGamecoreThread::GO_startCommand()
54 {
55 	consolethread.write("Start command given");
56 	m_GO_startCommand = true;
57 
58 	if(m_GO_minPlayers && m_GO_startCommand)
59 		start(); //start the thread
60 }
61 
getGameStatus()62 CString CGamecoreThread::getGameStatus()
63 {
64 	if(isRunning())
65 	{
66 		return m_GameStatus; //loading, waiting, running etc.
67 	}
68 	else if(m_GO_minPlayers)
69 	{
70 		return _("Waiting for the server to start");
71 	}
72 	else
73 	{
74 		Clients.enter();
75 		unsigned int players = Clients.playerRequests.size() + Clients.AIrequests;
76 		unsigned int minimum = Clients.minPlayers;
77 		Clients.leave();
78 
79 		CString status;
80 		status.format(
81 			N_("Waiting for %d remaining player to join", "Waiting for %d remaining players to join",
82 			minimum - players), 80, minimum - players);
83 		return status;
84 	}
85 }
86 
processInput(const CMessageBuffer & b)87 void CGamecoreThread::processInput(const CMessageBuffer &b)
88 {
89 	m_InputQueue.enter();
90 	m_InputQueue.push_back(b);
91 	m_InputQueue.leave();
92 }
93 
threadFunc()94 void CGamecoreThread::threadFunc()
95 {
96 	startGame();
97 
98 	m_GameStatus = _("Running a game");
99 
100 	float t = CTimer().getTime();
101 
102 	//keep running until the game has ended
103 	//or a stop command is given
104 	while( m_GameCore->update() && (!m_terminate) )
105 	{
106 		processInputQueue();
107 
108 		//send object data to the clients:
109 		float tnow = CTimer().getTime();
110 		if(tnow - t > 0.04) //25 fps network updates
111 		{
112 			t = tnow;
113 
114 			vector<CDataObject *> objs = theWorld->getObjectArray(CDataObject::eMovingObject);
115 			for(unsigned int i=0; i < objs.size(); i++)
116 			{
117 				//send object state
118 				CMovingObject *mo = (CMovingObject *)objs[i];
119 				networkthread.sendToAll(mo->getBuffer());
120 
121 				//send messages
122 				for(unsigned int j=0; j < mo->m_IncomingMessages.size(); j++)
123 				{
124 					CChatMessage &msg = mo->m_IncomingMessages[j];
125 					msg.m_ToMovingObject = mo->getMovObjID(); //prevents multiplication of "to all" messages
126 					CMessageBuffer b = msg.getBuffer();
127 					b.setAC(1); //reliable
128 					networkthread.sendToAll(b);
129 				}
130 				mo->m_IncomingMessages.clear();
131 			}
132 		}
133 
134 		//sleep for some time (give some CPU time to e.g. a client)
135 		usleep(10000); //< 100 fps
136 	}
137 
138 	m_GameStatus = _("Stopping the game");
139 
140 	m_GameCore->stopGame(m_SaveHiscore);
141 
142 	//now the game has ended, send the hiscore
143 	CMessageBuffer b = m_GameCore->getHiscore(true).getBuffer(); //true: only hiscore entries of this game
144 	b.setAC(1);
145 	networkthread.sendToAll(b);
146 
147 	clearPlayerLists();
148 	m_GO_minPlayers = false;
149 	m_GO_startCommand = false;
150 }
151 
processInputQueue()152 void CGamecoreThread::processInputQueue()
153 {
154 	m_InputQueue.enter();
155 	if(m_InputQueue.size() > 0)
156 	{
157 		//the moving objects:
158 		vector<CDataObject *> objs = theWorld->getObjectArray(CDataObject::eMovingObject);
159 
160 		for(unsigned int i=0; i < m_InputQueue.size(); i++)
161 		{
162 			CMessageBuffer &buffer = m_InputQueue[i];
163 
164 			CBinBuffer b = buffer.getData();
165 			unsigned int pos=0;
166 			Uint8 ID = b.getUint8(pos);
167 
168 
169 			//find the corresponding moving object
170 			CMovingObject *mo = NULL;
171 			//first candidate
172 			if(ID < objs.size())
173 			{
174 				CMovingObject *tmp = (CMovingObject *)objs[ID];
175 				if(tmp->getMovObjID() == ID)
176 					mo = tmp;
177 			}
178 			//all other objects
179 			if(mo == NULL)
180 				for(unsigned int j=0; j < objs.size(); j++)
181 				{
182 					CMovingObject *tmp = (CMovingObject *)objs[j];
183 					if(tmp->getMovObjID() == ID)
184 						mo = tmp;
185 				}
186 
187 			if(mo == NULL)
188 			{
189 				consolethread.write("Unidentified Input Message (UIM)");
190 				continue; //not found
191 			}
192 
193 			mo->m_InputData->setBuffer(buffer);
194 
195 			//consolethread.write(CString("Steering input ") + ID + ":" + mo->m_InputData->m_Right);
196 		}
197 		m_InputQueue.clear();
198 
199 	}
200 	m_InputQueue.leave();
201 }
202 
unReadyPlayers()203 void CGamecoreThread::unReadyPlayers()
204 {
205 	Clients.enter();
206 	for(unsigned int i=0; i < Clients.size(); i++)
207 		Clients[i].ready = false;
208 	Clients.leave();
209 }
210 
wait4ReadyPlayers()211 void CGamecoreThread::wait4ReadyPlayers()
212 {
213 	printf("waiting until all clients are ready\n");
214 	while(true)
215 	{
216 		bool ready = true;
217 
218 		Clients.enter();
219 		for(unsigned int i=0; i < Clients.size(); i++)
220 			if(!Clients[i].ready)
221 				{ready = false; break;}
222 		Clients.leave();
223 
224 		if(ready) break;
225 
226 		sleep(1);
227 	}
228 	printf("OK, they are ready\n");
229 }
230 
clearPlayerLists()231 void CGamecoreThread::clearPlayerLists()
232 {
233 	printf("\n---Players\n");
234 	for(unsigned int i=0; i < m_AIPlayers.size(); i++)
235 		delete m_AIPlayers[i];
236 	m_AIPlayers.clear();
237 	for(unsigned int i=0; i < m_RemotePlayers.size(); i++)
238 		delete m_RemotePlayers[i];
239 	m_RemotePlayers.clear();
240 
241 	Clients.enter();
242 	for(unsigned int i=0; i < Clients.size(); i++)
243 	{
244 		Clients[i].players.clear();
245 		Clients[i].ready = false;
246 	}
247 	Clients.AIrequests = 0;
248 	Clients.leave();
249 
250 	ObjectChoices.enter();
251 	ObjectChoices.clear();
252 	ObjectChoices.leave();
253 }
254 
addRemotePlayers()255 void CGamecoreThread::addRemotePlayers()
256 {
257 	Clients.enter();
258 
259 	//add them
260 	for(unsigned int i=0; i < Clients.playerRequests.size(); i++)
261 	{
262 		m_RemotePlayers.push_back(new CPlayer);
263 		CPlayer *p = m_RemotePlayers.back();
264 
265 		CObjectChoice &choice = Clients.playerRequests[i];
266 
267 		if(!gamecorethread.m_GameCore->addPlayer(p, choice))
268 		{
269 			printf("Sim doesn't accept remote player %s\n", choice.m_PlayerName.c_str());
270 			delete p;
271 			m_RemotePlayers.erase(m_RemotePlayers.end());
272 			continue;
273 		}
274 
275 		//add to the objecechoice list
276 		ObjectChoices.enter();
277 		ObjectChoices.push_back(choice);
278 		ObjectChoices.leave();
279 	}
280 
281 	//remove player requests:
282 	Clients.playerRequests.clear();
283 
284 	//Make references in the client info
285 	for(unsigned int i=0; i < Clients.size(); i++)
286 	{
287 		Clients[i].players = Clients[i].playerRequests;
288 		Clients[i].playerRequests.clear();
289 		Clients[i].ready = false;
290 	}
291 
292 	Clients.leave();
293 }
294 
addAI(SAIDescr ai)295 void CGamecoreThread::addAI(SAIDescr ai)
296 {
297 	m_AIDescriptions.push_back(ai);
298 	Clients.enter();
299 	Clients.AIrequests++;
300 	Clients.leave();
301 
302 	if(Clients.reachedMinimum_safe()) GO_minPlayers();
303 }
304 
clearAI()305 void CGamecoreThread::clearAI()
306 {
307 	m_AIDescriptions.clear();
308 	Clients.enter();
309 	Clients.AIrequests = 0;
310 	Clients.leave();
311 }
312 
addAIPlayers()313 void CGamecoreThread::addAIPlayers()
314 {
315 	for(unsigned int i=0; i<m_AIDescriptions.size(); i++)
316 	{
317 		m_AIPlayers.push_back(new CAIPlayerCar);
318 		CAIPlayerCar *p = m_AIPlayers.back();
319 
320 		CString name = m_AIDescriptions[i].name;
321 		CString car = m_AIDescriptions[i].carFile;
322 
323 		CObjectChoice choice;
324 		choice.m_Filename = car;
325 		choice.m_PlayerName = name;
326 
327 		if(!gamecorethread.m_GameCore->addPlayer(p, choice))
328 		{
329 			printf("Sim doesn't accept AI player %s\n", name.c_str());
330 			delete p;
331 			m_AIPlayers.erase(m_AIPlayers.end());
332 			continue;
333 		}
334 
335 		//add to the objecechoice list
336 		ObjectChoices.enter();
337 		ObjectChoices.push_back(choice);
338 		ObjectChoices.leave();
339 	}
340 }
341 
startGame()342 void CGamecoreThread::startGame()
343 {
344 	consolethread.write(_("Game is being started"));
345 
346 	m_GameStatus = _("Game is being started");
347 
348 	//TODO: super-server setup
349 	m_GameCore->initLocalGame(m_Trackfile);
350 
351 	//wait until all players are ready
352 	m_GameStatus = _("Waiting for client processes");
353 	wait4ReadyPlayers();
354 	unReadyPlayers();
355 
356 	//now send the track info to clients, so that they can load the track
357 	networkthread.sendToAll(USNET_TRACK + m_Trackfile);
358 
359 	consolethread.write(_("Loading"));
360 	clearPlayerLists();
361 	addRemotePlayers(); //add remote human and AI players
362 	addAIPlayers(); //add local AI players
363 
364 	//load the track and moving objects
365 	m_GameCore->readyAndLoad();
366 
367 	//wait a second time for a ready signal
368 	m_GameStatus = _("Waiting for client processes");
369 	wait4ReadyPlayers();
370 	unReadyPlayers();
371 
372 	//Then tell everybody to start the game, and start it here at the same time
373 	networkthread.sendToAll(USNET_READY);
374 	m_GameCore->setStartTime();
375 }
376