Project Alice
Loading...
Searching...
No Matches
entry_point_win.cpp
Go to the documentation of this file.
1#include "system_state.hpp"
2#include "serialization.hpp"
3
4#ifndef UNICODE
5#define UNICODE
6#endif
7#define NOMINMAX
8#define WIN32_LEAN_AND_MEAN
9
10#include <Windows.h>
11#include <shellapi.h>
12#include "Objbase.h"
13#include "window.hpp"
14
15#include "network/webui.hpp"
16
17#pragma comment(lib, "Ole32.lib")
18#pragma comment(lib, "Shell32.lib")
19#pragma comment(lib, "icu.lib")
20
21static sys::state game_state; // too big for the stack
22static CRITICAL_SECTION guard_abort_handler;
23
25 static bool run_once = false;
26
27 EnterCriticalSection(&guard_abort_handler);
28 if(run_once == false) {
29 run_once = true;
30
31 STARTUPINFO si;
32 ZeroMemory(&si, sizeof(si));
33 si.cb = sizeof(si);
34 PROCESS_INFORMATION pi;
35 ZeroMemory(&pi, sizeof(pi));
36 // Start the child process.
37 if(CreateProcessW(
38 L"dbg_alice.exe", // Module name
39 NULL, // Command line
40 NULL, // Process handle not inheritable
41 NULL, // Thread handle not inheritable
42 FALSE, // Set handle inheritance to FALSE
43 0, // No creation flags
44 NULL, // Use parent's environment block
45 NULL, // Use parent's starting directory
46 &si, // Pointer to STARTUPINFO structure
47 &pi) == 0) {
48
49 // create process failed
50 LeaveCriticalSection(&guard_abort_handler);
51 return;
52
53 }
54 // Wait until child process exits.
55 WaitForSingleObject(pi.hProcess, INFINITE);
56 // Close process and thread handles.
57 CloseHandle(pi.hProcess);
58 CloseHandle(pi.hThread);
59 }
60 LeaveCriticalSection(&guard_abort_handler);
61}
62
63LONG WINAPI uef_wrapper(struct _EXCEPTION_POINTERS* lpTopLevelExceptionFilter) {
65 return EXCEPTION_CONTINUE_SEARCH;
66}
67
70}
72 const wchar_t* expression,
73 const wchar_t* function,
74 const wchar_t* file,
75 unsigned int line,
76 uintptr_t pReserved
77) {
79}
80
82 typedef BOOL(WINAPI* tGetPolicy)(LPDWORD lpFlags);
83 typedef BOOL(WINAPI* tSetPolicy)(DWORD dwFlags);
84 const DWORD EXCEPTION_SWALLOWING = 0x1;
85
86 HMODULE kernel32 = LoadLibraryA("kernel32.dll");
87 if(kernel32) {
88 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
89 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
90 if(pGetPolicy && pSetPolicy) {
91 DWORD dwFlags;
92 if(pGetPolicy(&dwFlags)) {
93 // Turn off the filter
94 pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
95 }
96 }
97 }
98 BOOL insanity = FALSE;
99 SetUserObjectInformationA(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, &insanity, sizeof(insanity));
100}
101
102int WINAPI wWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPWSTR /*commandline*/, int /*nCmdShow*/
103) {
104
105#ifdef _DEBUG
106 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
107#endif
108
109 InitializeCriticalSection(&guard_abort_handler);
110
111 if(!IsDebuggerPresent()) {
113 _set_purecall_handler(generic_wrapper);
114 _set_invalid_parameter_handler(invalid_parameter_wrapper);
115 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
116 SetUnhandledExceptionFilter(uef_wrapper);
117 signal(SIGABRT, signal_abort_handler);
118 }
119
120 SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
121
122 if(SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) {
123 // do everything here: create a window, read messages
124
125 int num_params = 0;
126 auto parsed_cmd = CommandLineToArgvW(GetCommandLineW(), &num_params);
127
128 int headless_speed = 6;
129 bool headless_repeat = false;
130 bool headless = false;
131
132 network::port_forwarder forwarding_apparatus;
133
134 if(num_params < 2) {
135#ifdef NDEBUG
136 auto msg = std::string("Start Alice.exe using the launcher");
138 return 0;
139#else
140
141 add_root(game_state.common_fs, NATIVE(".")); // for the moment this lets us find the shader files
142
143
144 if(!sys::try_read_scenario_and_save_file(game_state, NATIVE("development_test_file.bin"))) {
145 // scenario making functions
146 parsers::error_handler err{ "" };
147
149 simple_fs::add_root(fs_root, L".");
150
151 auto root = get_root(fs_root);
152 auto common = open_directory(root, NATIVE("common"));
153
154 parsers::bookmark_context bookmark_context;
155 if(auto f = open_file(common, NATIVE("bookmarks.txt")); f) {
156 auto bookmark_content = simple_fs::view_contents(*f);
157 err.file_name = "bookmarks.txt";
158 parsers::token_generator gen(bookmark_content.data, bookmark_content.data + bookmark_content.file_size);
159 parsers::parse_bookmark_file(gen, err, bookmark_context);
160 assert(!bookmark_context.bookmark_dates.empty());
161 } else {
162 err.accumulated_errors += "File common/bookmarks.txt could not be opened\n";
163 }
164
165 auto to_hex = [](uint64_t v) {
166 native_string ret;
167 constexpr native_char digits[] = NATIVE("0123456789ABCDEF");
168 do {
169 ret += digits[v & 0x0F];
170 v = v >> 4;
171 } while(v != 0);
172 return ret;
173 };
174
175 sys::checksum_key scenario_key;
176 for(uint32_t date_index = 0; date_index < uint32_t(bookmark_context.bookmark_dates.size()); date_index++) {
177 err.accumulated_errors.clear();
178 err.accumulated_warnings.clear();
179 //
180 auto inner_game_state = std::make_unique<sys::state>();
181 simple_fs::add_root(inner_game_state->common_fs, L".");
182
183 inner_game_state->load_scenario_data(err, bookmark_context.bookmark_dates[date_index].date_);
184 if(err.fatal)
185 break;
186 if(date_index == 0) {
188 int32_t append = 0;
189 auto time_stamp = uint64_t(std::time(0));
190 auto selected_scenario_file = native_string(NATIVE("development_test_file.bin"));
191 sys::write_scenario_file(*inner_game_state, selected_scenario_file, 0);
192 if(auto of = simple_fs::open_file(sdir, selected_scenario_file); of) {
193 auto content = view_contents(*of);
194 auto desc = sys::extract_mod_information(reinterpret_cast<uint8_t const*>(content.data), content.file_size);
195 }
196 scenario_key = inner_game_state->scenario_checksum;
197 } else {
198 inner_game_state->scenario_checksum = scenario_key;
199 sys::write_save_file(*inner_game_state, sys::save_type::bookmark, bookmark_context.bookmark_dates[date_index].name_);
200 }
201 }
202
203 if(!err.accumulated_errors.empty()) {
204 auto assembled_file = std::string("You can still play the mod, but it might be unstable\r\nThe following problems were encountered while creating the scenario:\r\n\r\nErrors:\r\n") + err.accumulated_errors + "\r\n\r\nWarnings:\r\n" + err.accumulated_warnings;
206 simple_fs::write_file(pdir, NATIVE("scenario_errors.txt"), assembled_file.data(), uint32_t(assembled_file.length()));
207
208 auto fname = simple_fs::get_full_name(pdir) + NATIVE("\\scenario_errors.txt");
209 ShellExecuteW(
210 nullptr,
211 L"open",
212 fname.c_str(),
213 nullptr,
214 nullptr,
215 SW_NORMAL
216 );
217
218 std::abort();
219 }
220 if(!sys::try_read_scenario_and_save_file(game_state, NATIVE("development_test_file.bin")))
221 std::abort();
222 }
223
224 game_state.fill_unsaved_data();
225#endif
226 } else {
227 for(int i = 1; i < num_params; ++i) {
228 if(native_string(parsed_cmd[i]) == NATIVE("-host")) {
229 forwarding_apparatus.start_forwarding();
231 } else if(native_string(parsed_cmd[i]) == NATIVE("-join")) {
233 game_state.network_state.ip_address = "127.0.0.1";
234 if(i + 1 < num_params) {
235 game_state.network_state.ip_address = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
236 i++;
237 }
238 } else if(native_string(parsed_cmd[i]) == NATIVE("-name")) {
239 if(i + 1 < num_params) {
240 std::string nickname = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
241 memcpy(&game_state.network_state.nickname.data, nickname.c_str(), std::min<size_t>(nickname.length(), 8));
242 i++;
243 }
244 } else if(native_string(parsed_cmd[i]) == NATIVE("-password")) {
245 if(i + 1 < num_params) {
246 auto str = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
247 std::memset(game_state.network_state.lobby_password, '\0', sizeof(game_state.network_state.lobby_password));
248 std::memcpy(game_state.network_state.lobby_password, str.c_str(), std::min(sizeof(game_state.network_state.lobby_password), str.length()));
249 i++;
250 }
251 }
252 else if (native_string(parsed_cmd[i]) == NATIVE("-player_password")) {
253 if (i + 1 < num_params) {
254 std::string password = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
255 memcpy(&game_state.network_state.player_password.data, password.c_str(), std::min<size_t>(password.length(), 8));
256 i++;
257 }
258 }
259 else if(native_string(parsed_cmd[i]) == NATIVE("-v6")) {
260 game_state.network_state.as_v6 = true;
261 } else if(native_string(parsed_cmd[i]) == NATIVE("-v4")) {
262 game_state.network_state.as_v6 = false;
263 } else if(native_string(parsed_cmd[i]) == NATIVE("-headless")) {
264 headless = true;
265 } else if(native_string(parsed_cmd[i]) == NATIVE("-repeat")) {
266 headless_repeat = true;
267 } else if(native_string(parsed_cmd[i]) == NATIVE("-speed")) {
268 if(i + 1 < num_params) {
269 auto str = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
270 headless_speed = std::atoi(str.c_str());
271 i++;
272 }
273 }
274 }
275
276 if(sys::try_read_scenario_and_save_file(game_state, parsed_cmd[1])) {
277 game_state.fill_unsaved_data();
278 game_state.console_log("Read scenario file " + simple_fs::native_to_utf8(parsed_cmd[1]));
279 } else {
280 auto msg = std::string("Scenario file ") + simple_fs::native_to_utf8(parsed_cmd[1]) + " could not be read";
282 return 0;
283 }
284 }
285 LocalFree(parsed_cmd);
286
287 // scenario loading functions (would have to run these even when scenario is pre-built)
288 game_state.load_user_settings();
290
292 network::save_host_settings(game_state);
293 network::load_host_settings(game_state);
294
295 if(game_state.host_settings.alice_expose_webui != 0) {
296 std::thread web_thread([&]() { webui::init(game_state); });
297 web_thread.detach();
298 }
299 }
300
301 network::init(game_state);
302
303 if(headless) {
304 game_state.actual_game_speed = headless_speed;
305 game_state.ui_pause.store(false, std::memory_order::release);
307 game_state.local_player_nation = dcon::nation_id{};
308 if(headless_repeat) {
309 std::thread update_thread([&]() { game_state.game_loop(); });
310 while(!game_state.quit_signaled.load(std::memory_order::acquire)) {
311 while(game_state.current_scene.game_in_progress) {
312 std::this_thread::sleep_for(std::chrono::milliseconds(15));
313 }
314 //Reload savefile
315 network::finish(game_state, true);
316 game_state.actual_game_speed = 0;
317 //
318 if(sys::try_read_scenario_and_save_file(game_state, parsed_cmd[1])) {
319 game_state.fill_unsaved_data();
320 } else {
321 auto msg = std::string("Scenario file ") + simple_fs::native_to_utf8(parsed_cmd[1]) + " could not be read";
323 return 0;
324 }
325 //
326 network::init(game_state);
328 game_state.actual_game_speed = headless_speed;
329 };
330 update_thread.join();
331 } else {
332 game_state.game_loop();
333 }
334 } else {
335 std::thread update_thread([&]() { game_state.game_loop(); });
336 // entire game runs during this line
338 game_state.quit_signaled.store(true, std::memory_order_release);
339 update_thread.join();
340 }
341
342 network::finish(game_state, true);
343 webui::svr.stop();
344 CoUninitialize();
345 }
346 return 0;
347}
#define assert(condition)
Definition: debug.h:74
native_string to_hex(uint64_t v)
void EnableCrashingOnCrashes()
void signal_abort_handler(int)
LONG WINAPI uef_wrapper(struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
void generic_wrapper()
void invalid_parameter_wrapper(const wchar_t *expression, const wchar_t *function, const wchar_t *file, unsigned int line, uintptr_t pReserved)
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
void switch_scene(sys::state &state, scene_id ui_scene)
Definition: game_scene.cpp:13
void load_host_settings(sys::state &state)
Definition: network.cpp:1686
void finish(sys::state &state, bool notify_host)
Definition: network.cpp:1568
void save_host_settings(sys::state &state)
Definition: network.cpp:1710
void init(sys::state &state)
Definition: network.cpp:832
void add_root(file_system &fs, native_string_view root_path)
void write_file(directory const &dir, native_string_view file_name, char const *file_data, uint32_t file_size)
directory get_or_create_settings_directory()
native_string get_full_name(directory const &f)
std::optional< file > open_file(directory const &dir, native_string_view file_name)
std::string native_to_utf8(native_string_view data_in)
file_contents view_contents(file const &f)
directory get_or_create_scenario_directory()
mod_identifier extract_mod_information(uint8_t const *ptr_in, uint64_t file_size)
@ bookmark
Definition: constants.hpp:568
void write_scenario_file(sys::state &state, native_string_view name, uint32_t count)
void write_save_file(sys::state &state, save_type type, std::string const &name)
bool try_read_scenario_and_save_file(sys::state &state, native_string_view name)
void populate_definitions_map(sys::state &state)
void init(sys::state &state) noexcept
Definition: webui.hpp:98
void emit_error_message(std::string const &content, bool fatal)
Definition: window_nix.cpp:355
void create_window(sys::state &game_state, creation_parameters const &params)
Definition: window_nix.cpp:278
char native_char
#define NATIVE(X)
std::string native_string
uint uint32_t
ulong uint64_t
uchar uint8_t
uint8_t lobby_password[16]
Definition: network.hpp:94
sys::player_password_raw player_password
Definition: network.hpp:77
std::string ip_address
Definition: network.hpp:84
sys::player_name nickname
Definition: network.hpp:76
std::vector< bookmark_definition > bookmark_dates
std::array< uint8_t, _Size > data
Holds important data about the game world, state, and other data regarding windowing,...
network_mode_type network_mode
user_settings_s user_settings
std::atomic< bool > ui_pause
std::atomic< int32_t > actual_game_speed
dcon::nation_id local_player_nation
simple_fs::file_system common_fs
game_scene::scene_properties current_scene
std::atomic< bool > quit_signaled
host_settings_s host_settings
void load_user_settings()
void game_loop()
network::network_state network_state
void fill_unsaved_data()
void console_log(std::string_view message)