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