2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file game_core.cpp Implementation of Game. */
10 #include "../stdafx.h"
11 #include "../core/backup_type.hpp"
12 #include "../company_base.h"
13 #include "../company_func.h"
14 #include "../network/network.h"
15 #include "../window_func.h"
16 #include "../framerate_type.h"
18 #include "game_scanner.hpp"
19 #include "game_config.hpp"
20 #include "game_instance.hpp"
21 #include "game_info.hpp"
23 #include "../safeguards.h"
25 /* static */ uint
Game::frame_counter
= 0;
26 /* static */ GameInfo
*Game::info
= nullptr;
27 /* static */ GameInstance
*Game::instance
= nullptr;
28 /* static */ GameScannerInfo
*Game::scanner_info
= nullptr;
29 /* static */ GameScannerLibrary
*Game::scanner_library
= nullptr;
31 /* static */ void Game::GameLoop()
33 if (_networking
&& !_network_server
) {
34 PerformanceMeasurer::SetInactive(PFE_GAMESCRIPT
);
37 if (Game::instance
== nullptr) {
38 PerformanceMeasurer::SetInactive(PFE_GAMESCRIPT
);
42 PerformanceMeasurer
framerate(PFE_GAMESCRIPT
);
44 Game::frame_counter
++;
46 Backup
<CompanyID
> cur_company(_current_company
);
47 cur_company
.Change(OWNER_DEITY
);
48 Game::instance
->GameLoop();
49 cur_company
.Restore();
51 /* Occasionally collect garbage */
52 if ((Game::frame_counter
& 255) == 0) {
53 Game::instance
->CollectGarbage();
57 /* static */ void Game::Initialize()
59 if (Game::instance
!= nullptr) Game::Uninitialize(true);
61 Game::frame_counter
= 0;
63 if (Game::scanner_info
== nullptr) {
64 TarScanner::DoScan(TarScanner::GAME
);
65 Game::scanner_info
= new GameScannerInfo();
66 Game::scanner_info
->Initialize();
67 Game::scanner_library
= new GameScannerLibrary();
68 Game::scanner_library
->Initialize();
72 /* static */ void Game::StartNew()
74 if (Game::instance
!= nullptr) return;
76 /* Don't start GameScripts in intro */
77 if (_game_mode
== GM_MENU
) return;
79 /* Clients shouldn't start GameScripts */
80 if (_networking
&& !_network_server
) return;
82 GameConfig
*config
= GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME
);
83 GameInfo
*info
= config
->GetInfo();
84 if (info
== nullptr) return;
86 config
->AnchorUnchangeableSettings();
88 Backup
<CompanyID
> cur_company(_current_company
);
89 cur_company
.Change(OWNER_DEITY
);
92 Game::instance
= new GameInstance();
93 Game::instance
->Initialize(info
);
94 Game::instance
->LoadOnStack(config
->GetToLoadData());
95 config
->SetToLoadData(nullptr);
97 cur_company
.Restore();
99 InvalidateWindowClassesData(WC_SCRIPT_DEBUG
, -1);
102 /* static */ void Game::Uninitialize(bool keepConfig
)
104 Backup
<CompanyID
> cur_company(_current_company
);
106 delete Game::instance
;
107 Game::instance
= nullptr;
108 Game::info
= nullptr;
110 cur_company
.Restore();
115 delete Game::scanner_info
;
116 delete Game::scanner_library
;
117 Game::scanner_info
= nullptr;
118 Game::scanner_library
= nullptr;
120 if (_settings_game
.game_config
!= nullptr) {
121 delete _settings_game
.game_config
;
122 _settings_game
.game_config
= nullptr;
124 if (_settings_newgame
.game_config
!= nullptr) {
125 delete _settings_newgame
.game_config
;
126 _settings_newgame
.game_config
= nullptr;
131 /* static */ void Game::Pause()
133 if (Game::instance
!= nullptr) Game::instance
->Pause();
136 /* static */ void Game::Unpause()
138 if (Game::instance
!= nullptr) Game::instance
->Unpause();
141 /* static */ bool Game::IsPaused()
143 return Game::instance
!= nullptr? Game::instance
->IsPaused() : false;
146 /* static */ void Game::NewEvent(ScriptEvent
*event
)
148 /* AddRef() and Release() need to be called at least once, so do it here */
151 /* Clients should ignore events */
152 if (_networking
&& !_network_server
) {
157 /* Check if Game instance is alive */
158 if (Game::instance
== nullptr) {
163 /* Queue the event */
164 Backup
<CompanyID
> cur_company(_current_company
, OWNER_DEITY
);
165 Game::instance
->InsertEvent(event
);
166 cur_company
.Restore();
171 /* static */ void Game::ResetConfig()
173 /* Check for both newgame as current game if we can reload the GameInfo inside
174 * the GameConfig. If not, remove the Game from the list. */
175 if (_settings_game
.game_config
!= nullptr && _settings_game
.game_config
->HasScript()) {
176 if (!_settings_game
.game_config
->ResetInfo(true)) {
177 Debug(script
, 0, "After a reload, the GameScript by the name '{}' was no longer found, and removed from the list.", _settings_game
.game_config
->GetName());
178 _settings_game
.game_config
->Change(std::nullopt
);
179 if (Game::instance
!= nullptr) {
180 delete Game::instance
;
181 Game::instance
= nullptr;
182 Game::info
= nullptr;
184 } else if (Game::instance
!= nullptr) {
185 Game::info
= _settings_game
.game_config
->GetInfo();
188 if (_settings_newgame
.game_config
!= nullptr && _settings_newgame
.game_config
->HasScript()) {
189 if (!_settings_newgame
.game_config
->ResetInfo(false)) {
190 Debug(script
, 0, "After a reload, the GameScript by the name '{}' was no longer found, and removed from the list.", _settings_newgame
.game_config
->GetName());
191 _settings_newgame
.game_config
->Change(std::nullopt
);
196 /* static */ void Game::Rescan()
198 TarScanner::DoScan(TarScanner::GAME
);
200 Game::scanner_info
->RescanDir();
201 Game::scanner_library
->RescanDir();
204 InvalidateWindowData(WC_SCRIPT_LIST
, 0, 1);
205 SetWindowClassesDirty(WC_SCRIPT_DEBUG
);
206 InvalidateWindowClassesData(WC_SCRIPT_SETTINGS
);
207 InvalidateWindowClassesData(WC_GAME_OPTIONS
);
211 /* static */ void Game::Save()
213 if (Game::instance
!= nullptr && (!_networking
|| _network_server
)) {
214 Backup
<CompanyID
> cur_company(_current_company
, OWNER_DEITY
);
215 Game::instance
->Save();
216 cur_company
.Restore();
218 GameInstance::SaveEmpty();
222 /* static */ void Game::GetConsoleList(std::back_insert_iterator
<std::string
> &output_iterator
, bool newest_only
)
224 Game::scanner_info
->GetConsoleList(output_iterator
, newest_only
);
227 /* static */ void Game::GetConsoleLibraryList(std::back_insert_iterator
<std::string
> &output_iterator
)
229 Game::scanner_library
->GetConsoleList(output_iterator
, true);
232 /* static */ const ScriptInfoList
*Game::GetInfoList()
234 return Game::scanner_info
->GetInfoList();
237 /* static */ const ScriptInfoList
*Game::GetUniqueInfoList()
239 return Game::scanner_info
->GetUniqueInfoList();
242 /* static */ GameInfo
*Game::FindInfo(const std::string
&name
, int version
, bool force_exact_match
)
244 return Game::scanner_info
->FindInfo(name
, version
, force_exact_match
);
247 /* static */ GameLibrary
*Game::FindLibrary(const std::string
&library
, int version
)
249 return Game::scanner_library
->FindLibrary(library
, version
);
253 * Check whether we have an Game (library) with the exact characteristics as ci.
254 * @param ci the characteristics to search on (shortname and md5sum)
255 * @param md5sum whether to check the MD5 checksum
256 * @return true iff we have an Game (library) matching.
258 /* static */ bool Game::HasGame(const ContentInfo
*ci
, bool md5sum
)
260 return Game::scanner_info
->HasScript(ci
, md5sum
);
263 /* static */ bool Game::HasGameLibrary(const ContentInfo
*ci
, bool md5sum
)
265 return Game::scanner_library
->HasScript(ci
, md5sum
);
268 /* static */ GameScannerInfo
*Game::GetScannerInfo()
270 return Game::scanner_info
;
272 /* static */ GameScannerLibrary
*Game::GetScannerLibrary()
274 return Game::scanner_library
;