1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 /* control management */
17 
18 #include "C4Include.h"
19 #include "control/C4GameControl.h"
20 
21 #include "control/C4Record.h"
22 #include "game/C4Application.h"
23 #include "gui/C4GameOverDlg.h"
24 #include "gui/C4MouseControl.h"
25 #include "network/C4Network2Stats.h"
26 #include "platform/C4GamePadCon.h"
27 #include "player/C4PlayerList.h"
28 #include "player/C4Player.h"
29 
30 #ifdef _MSC_VER
31 #pragma warning (disable: 4355)
32 #endif
33 
34 // *** C4GameControl
35 
C4GameControl()36 C4GameControl::C4GameControl()
37 		: Network(this)
38 {
39 	Default();
40 }
41 
~C4GameControl()42 C4GameControl::~C4GameControl()
43 {
44 	Clear();
45 }
46 
InitLocal(C4Client * pLocal)47 bool C4GameControl::InitLocal(C4Client *pLocal)
48 {
49 	eMode = CM_Local; fInitComplete = true;
50 	fHost = true; iClientID = pLocal->getID();
51 	ControlRate = 1;
52 	// ok
53 	return true;
54 }
55 
InitNetwork(C4Client * pLocal)56 bool C4GameControl::InitNetwork(C4Client *pLocal)
57 {
58 	// network should already be initialized (by C4Network2)
59 	if (!Network.IsEnabled())
60 		return false;
61 	// set mode
62 	eMode = CM_Network; fInitComplete = true;
63 	fHost = pLocal->isHost(); iClientID = pLocal->getID();
64 	// control rate by parameters
65 	ControlRate = Game.Parameters.ControlRate;
66 	// ok
67 	return true;
68 }
69 
InitReplay(C4Group & rGroup)70 bool C4GameControl::InitReplay(C4Group &rGroup)
71 {
72 	// open replay
73 	pPlayback = new C4Playback();
74 	if (!pPlayback->Open(rGroup))
75 	{
76 		LogFatal(LoadResStr("IDS_ERR_REPLAYREAD"));
77 		delete pPlayback; pPlayback = nullptr;
78 		return false;
79 	}
80 	// set mode
81 	eMode = CM_Replay; fInitComplete = true;
82 	fHost = false; iClientID = C4ClientIDUnknown;
83 	// control rate by parameters
84 	ControlRate = Game.Parameters.ControlRate;
85 	// just in case
86 	StopRecord();
87 	// ok
88 	return true;
89 }
90 
ChangeToLocal()91 void C4GameControl::ChangeToLocal()
92 {
93 	// changes from any given mode to local
94 	// (emergency - think of network disconnect)
95 
96 	// remove all non-local clients
97 	Game.Clients.RemoveRemote();
98 	// activate local client
99 	if (Game.Clients.getLocal())
100 		Game.Clients.getLocal()->SetActivated(true);
101 
102 	// network: clear network
103 	if (eMode == CM_Network)
104 	{
105 		Network.Clear();
106 		if (::Network.isEnabled())
107 			::Network.Clear();
108 	}
109 	// replay: close playback
110 	else if (eMode == CM_Replay)
111 		{ delete pPlayback; pPlayback = nullptr; }
112 
113 	// we're now managing our own player info list; make sure counter works
114 	Game.PlayerInfos.FixIDCounter();
115 
116 	// start the game, if we're not in the game over dialog
117 	// (otherwise, clients start game when host disconnected!)
118 	if (!C4GameOverDlg::IsShown()) Game.HaltCount = 0;
119 
120 	// set status
121 	eMode = CM_Local; fHost = true;
122 	ControlRate = 1;
123 }
124 
OnGameSynchronizing()125 void C4GameControl::OnGameSynchronizing()
126 {
127 	// start record if desired
128 	if (fRecordNeeded)
129 	{
130 		fRecordNeeded = false;
131 		StartRecord(false, false);
132 	}
133 }
134 
StartRecord(bool fInitial,bool fStreaming)135 bool C4GameControl::StartRecord(bool fInitial, bool fStreaming)
136 {
137 	assert(fInitComplete);
138 	// already recording?
139 	if (pRecord) StopRecord();
140 	// start
141 	pRecord = new C4Record();
142 	if (!pRecord->Start(fInitial))
143 	{
144 		delete pRecord; pRecord = nullptr;
145 		return false;
146 	}
147 	// streaming
148 	if (fStreaming)
149 	{
150 		if (!pRecord->StartStreaming(fInitial) ||
151 		    !::Network.StartStreaming(pRecord))
152 		{
153 			delete pRecord; pRecord = nullptr;
154 			return false;
155 		}
156 	}
157 	// runtime records executed through queue: Must record initial control
158 	if (pExecutingControl)
159 		pRecord->Rec(*pExecutingControl, Game.FrameCounter);
160 	// ok
161 	return true;
162 }
163 
StopRecord(StdStrBuf * pRecordName,BYTE * pRecordSHA1)164 void C4GameControl::StopRecord(StdStrBuf *pRecordName, BYTE *pRecordSHA1)
165 {
166 	if (pRecord)
167 	{
168 		::Network.FinishStreaming();
169 		pRecord->Stop(pRecordName, pRecordSHA1);
170 		// just delete
171 		delete pRecord; pRecord = nullptr;
172 	}
173 }
174 
RequestRuntimeRecord()175 void C4GameControl::RequestRuntimeRecord()
176 {
177 	if (!IsRuntimeRecordPossible()) return; // cannot record
178 	fRecordNeeded = true;
179 	// request through a synchronize-call
180 	// currnetly do not request, but start record with next gamesync, so network runtime join can be debugged
181 	if (Config.General.DebugRec)
182 		::Control.DoInput(CID_Synchronize, new C4ControlSynchronize(false, true), CDT_Queue);
183 }
184 
IsRuntimeRecordPossible() const185 bool C4GameControl::IsRuntimeRecordPossible() const
186 {
187 	// already requested?
188 	if (fRecordNeeded) return false;
189 	// not from replay
190 	if (isReplay()) return false;
191 	// not if recording already
192 	if (isRecord()) return false;
193 	// Record OK
194 	return true;
195 }
196 
RecAddFile(const char * szLocalFilename,const char * szAddAs)197 bool C4GameControl::RecAddFile(const char *szLocalFilename, const char *szAddAs)
198 {
199 	if (!isRecord() || !pRecord) return false;
200 	return pRecord->AddFile(szLocalFilename, szAddAs);
201 }
202 
Clear()203 void C4GameControl::Clear()
204 {
205 	StopRecord();
206 	ChangeToLocal();
207 	Default();
208 }
209 
Default()210 void C4GameControl::Default()
211 {
212 	Input.Clear();
213 	Network.Clear();
214 	eMode = CM_None;
215 	fHost = fInitComplete = false;
216 	iClientID = C4ClientIDUnknown;
217 	pRecord = nullptr;
218 	pPlayback = nullptr;
219 	SyncChecks.Clear();
220 	ControlRate = Clamp<int>(Config.Network.ControlRate, 1, C4MaxControlRate);
221 	ControlTick = 0;
222 	SyncRate = C4SyncCheckRate;
223 	DoSync = false;
224 	fRecordNeeded = false;
225 	pExecutingControl = nullptr;
226 }
227 
Prepare()228 bool C4GameControl::Prepare()
229 {
230 	assert(fInitComplete);
231 
232 	// Prepare control, return true if everything is ready for GameGo.
233 	bool is_input_prepared = false;
234 
235 	switch (eMode)
236 	{
237 		// full steam ahead
238 	case CM_Local: case CM_Replay:
239 		return true;
240 
241 	case CM_Network:
242 
243 		Network.Execute();
244 
245 		// deactivated and got control: request activate
246 		if (Input.firstPkt() && !Game.Clients.getLocal()->isActivated())
247 			::Network.RequestActivate();
248 
249 		// needs input?
250 		while (Network.CtrlNeeded(Game.FrameCounter))
251 		{
252 			if (!is_input_prepared && Game.Clients.getLocal()->isActivated())
253 			{
254 				// add per-controlframe input
255 				PrepareInput();
256 				is_input_prepared = true;
257 			}
258 			Network.DoInput(Input);
259 			Input.Clear();
260 		}
261 
262 		// control tick?
263 		if (Game.FrameCounter % ControlRate)
264 			return true;
265 
266 		// check GameGo
267 		return Network.CtrlReady(ControlTick);
268 	default:
269 		return false;
270 	}
271 }
272 
Execute()273 void C4GameControl::Execute()
274 {
275 	// Execute all available control
276 
277 	assert(fInitComplete);
278 
279 	// control tick? replay must always be executed.
280 	if (!isReplay() && Game.FrameCounter % ControlRate)
281 		return;
282 
283 	// Get control
284 	C4Control Control;
285 	if (eMode == CM_Local)
286 	{
287 		// control = input
288 		PrepareInput(); // add per-controlframe input
289 		Control.Take(Input);
290 	}
291 	if (eMode == CM_Network)
292 	{
293 		// control = network input
294 		if (!Network.GetControl(&Control, ControlTick))
295 		{
296 			LogFatal("Network: could not retrieve control from C4GameControlNetwork!");
297 			return;
298 		}
299 	}
300 	if (eMode == CM_Replay)
301 	{
302 		if (!pPlayback) { ChangeToLocal(); return; }
303 		// control = replay data
304 		pPlayback->ExecuteControl(&Control, Game.FrameCounter);
305 	}
306 
307 	// Record: Save ctrl
308 	if (pRecord)
309 		pRecord->Rec(Control, Game.FrameCounter);
310 
311 	// debug: recheck PreExecute
312 	assert(Control.PreExecute());
313 
314 	// execute
315 	pExecutingControl = &Control;
316 	Control.Execute();
317 	Control.Clear();
318 	pExecutingControl = nullptr;
319 
320 	// statistics record
321 	if (Game.pNetworkStatistics) Game.pNetworkStatistics->ExecuteControlFrame();
322 
323 }
324 
Ticks()325 void C4GameControl::Ticks()
326 {
327 	assert(fInitComplete);
328 
329 	if (!(Game.FrameCounter % ControlRate))
330 		ControlTick++;
331 	if (!(Game.FrameCounter % SyncRate))
332 		DoSync = true;
333 
334 	// calc next tick without waiting for timer? (catchup cases)
335 	if (eMode == CM_Network)
336 		if (Network.CtrlOverflow(ControlTick))
337 		{
338 			Game.GameGo = true;
339 			if (Network.GetBehind(ControlTick) >= 25)
340 				if (Game.FrameCounter % ((Network.GetBehind(ControlTick) + 15) / 20))
341 					Game.DoSkipFrame = true;
342 		}
343 }
344 
CtrlTickReached(int32_t iTick)345 bool C4GameControl::CtrlTickReached(int32_t iTick)
346 {
347 	// 1. control tick reached?
348 	if (ControlTick < iTick) return false;
349 	// 2. control tick?
350 	if (Game.FrameCounter % ControlRate) return false;
351 	// ok then
352 	return true;
353 }
354 
getCtrlTick(int32_t iFrame) const355 int32_t C4GameControl::getCtrlTick(int32_t iFrame) const
356 {
357 	// returns control tick by frame. Note this is a guess, as the control rate
358 	// can always change, so don't rely on the return value too much.
359 
360 	return iFrame / ControlRate + ControlTick - Game.FrameCounter / ControlRate;
361 }
362 
getNextControlTick() const363 int32_t C4GameControl::getNextControlTick() const
364 {
365 	return ControlTick + (Game.FrameCounter % ControlRate ? 1 : 0);
366 }
367 
AdjustControlRate(int32_t iBy)368 void C4GameControl::AdjustControlRate(int32_t iBy)
369 {
370 	// control host only
371 	if (isCtrlHost())
372 		::Control.DoInput(CID_Set, new C4ControlSet(C4CVT_ControlRate, iBy), CDT_Decide);
373 }
374 
SetActivated(bool fnActivated)375 void C4GameControl::SetActivated(bool fnActivated)
376 {
377 	fActivated = fnActivated;
378 	if (eMode == CM_Network)
379 		Network.SetActivated(fnActivated);
380 }
381 
DoInput(C4PacketType eCtrlType,C4ControlPacket * pPkt,C4ControlDeliveryType eDelivery)382 void C4GameControl::DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
383 {
384 	assert(fInitComplete || pPkt->Lobby());
385 
386 	// check if the control can be executed
387 	if (eDelivery == CDT_Direct || eDelivery == CDT_Private)
388 		assert(!pPkt->Sync());
389 
390 	// decide control type
391 	if (eDelivery == CDT_Decide)
392 		eDelivery = DecideControlDelivery();
393 
394 	// queue?
395 	if (eDelivery == CDT_Queue)
396 	{
397 		// add, will be executed/sent later
398 		Input.Add(eCtrlType, pPkt);
399 		return;
400 	}
401 
402 	// Network?
403 	if (isNetwork())
404 	{
405 		Network.DoInput(eCtrlType, pPkt, eDelivery);
406 	}
407 	else
408 	{
409 		// Local mode: execute at once
410 		ExecControlPacket(eCtrlType, pPkt);
411 		delete pPkt;
412 	}
413 
414 }
415 
DbgRec(C4RecordChunkType eType,const uint8_t * pData,size_t iSize)416 void C4GameControl::DbgRec(C4RecordChunkType eType, const uint8_t *pData, size_t iSize)
417 {
418 	if (Config.General.DebugRec)
419 	{
420 		if (DoNoDebugRec>0) return;
421 		// record data
422 		if (pRecord)
423 		{
424 			C4PktDebugRec dr(eType, StdBuf(pData, iSize));
425 			pRecord->Rec(Game.FrameCounter, DecompileToBuf<StdCompilerBinWrite>(dr), eType);
426 		}
427 		// check against playback
428 		if (pPlayback)
429 			pPlayback->Check(eType, pData, iSize);
430 	}
431 }
432 
DecideControlDelivery()433 C4ControlDeliveryType C4GameControl::DecideControlDelivery()
434 {
435 	// network
436 	if (eMode == CM_Network)
437 		return Network.DecideControlDelivery();
438 	// use direct
439 	return CDT_Direct;
440 }
441 
DoSyncCheck()442 void C4GameControl::DoSyncCheck()
443 {
444 	// only once
445 	if (!DoSync) return;
446 	DoSync = false;
447 	// create sync check
448 	C4ControlSyncCheck *pSyncCheck = new C4ControlSyncCheck();
449 	pSyncCheck->Set();
450 	// host?
451 	if (fHost)
452 		// add sync check to control queue or send it directly if the queue isn't active
453 		DoInput(CID_SyncCheck, pSyncCheck, fActivated ? CDT_Queue : CDT_Direct);
454 	else
455 	{
456 		// already have sync check?
457 		C4ControlSyncCheck* pSyncCheck2 = GetSyncCheck(Game.FrameCounter);
458 		if (!pSyncCheck2)
459 			// add to sync check array
460 			SyncChecks.Add(CID_SyncCheck, pSyncCheck);
461 		else
462 		{
463 			// check
464 			pSyncCheck->Execute();
465 			delete pSyncCheck;
466 		}
467 	}
468 	// remove old
469 	RemoveOldSyncChecks();
470 }
471 
ExecControl(const C4Control & rCtrl)472 void C4GameControl::ExecControl(const C4Control &rCtrl)
473 {
474 	// nothing to do?
475 	if (!rCtrl.firstPkt()) return;
476 	// execute it
477 	if (!rCtrl.PreExecute()) Log("Control: PreExecute failed for sync control!");
478 	rCtrl.Execute();
479 	// record
480 	if (pRecord)
481 		pRecord->Rec(rCtrl, Game.FrameCounter);
482 }
483 
ExecControlPacket(C4PacketType eCtrlType,C4ControlPacket * pPkt)484 void C4GameControl::ExecControlPacket(C4PacketType eCtrlType, C4ControlPacket *pPkt)
485 {
486 	// execute it
487 	if (!pPkt->PreExecute()) Log("Control: PreExecute failed for direct control!");
488 	pPkt->Execute();
489 	// record it
490 	if (pRecord)
491 		pRecord->Rec(eCtrlType, pPkt, Game.FrameCounter);
492 }
493 
GetSyncCheck(int32_t iTick)494 C4ControlSyncCheck *C4GameControl::GetSyncCheck(int32_t iTick)
495 {
496 	for (C4IDPacket *pPkt = SyncChecks.firstPkt(); pPkt; pPkt = SyncChecks.nextPkt(pPkt))
497 	{
498 		// should be a sync check
499 		if (pPkt->getPktType() != CID_SyncCheck) continue;
500 		// get sync check
501 		C4ControlSyncCheck *pCheck = static_cast<C4ControlSyncCheck *>(pPkt->getPkt());
502 		// packet that's searched for?
503 		if (pCheck->getFrame() == iTick)
504 			return pCheck;
505 	}
506 	return nullptr;
507 }
508 
RemoveOldSyncChecks()509 void C4GameControl::RemoveOldSyncChecks()
510 {
511 	C4IDPacket *pNext;
512 	for (C4IDPacket *pPkt = SyncChecks.firstPkt(); pPkt; pPkt = pNext)
513 	{
514 		pNext = SyncChecks.nextPkt(pPkt);
515 		// should be a sync check
516 		if (pPkt->getPktType() != CID_SyncCheck) continue;
517 		// remove?
518 		C4ControlSyncCheck *pCheck = static_cast<C4ControlSyncCheck *>(pPkt->getPkt());
519 		if (pCheck->getFrame() < Game.FrameCounter - C4SyncCheckMaxKeep)
520 			SyncChecks.Delete(pPkt);
521 	}
522 }
523 
PrepareInput()524 void C4GameControl::PrepareInput()
525 {
526 	// add per-controlframe input
527 	::MouseControl.DoMoveInput();
528 	if (Application.pGamePadControl) Application.pGamePadControl->DoAxisInput();
529 	// per-player input
530 	C4Player *plr; int32_t i=0;
531 	while ((plr = ::Players.GetLocalByIndex(i++)))
532 		plr->Control.PrepareInput();
533 }
534 
535 C4GameControl Control;
536