Added Steam support for Windows

This commit is contained in:
Vladislav Khorev committed 2026-07-14 19:41:36 +03:00
1 parent 9da0ae1016
commit 76287dda46
2 files changed
+412 -306

No files matched your search

+265 -226
View File
@@ -11,133 +11,127 @@
#include <emscripten/html5.h>
#endif
// For Emscripten the game is heap-allocated so it can be destroyed and
// re-created when the WebGL context is lost and restored (e.g. fullscreen).
// For Android and Desktop a plain global value is used (no context loss).
#ifdef EMSCRIPTEN
ZL::Game* g_game = nullptr;
//static SDL_Window* win_x = nullptr;
static SDL_GLContext glContext_x; // Не указатель, а сам объект (который суть void*)
#else
ZL::Game game;
#ifdef STEAMSDK
#include "steam_api.h"
#endif
#ifdef EMSCRIPTEN
#if defined(EMSCRIPTEN)
static SDL_GLContext glContext_x;
#endif
// Единый указатель на объект игры для всех платформ
ZL::Game* g_game = nullptr;
void MainLoop() {
g_game->update();
}
#else
void MainLoop() {
game.update();
}
#endif
#ifdef EMSCRIPTEN
EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
return EM_TRUE;
return EM_TRUE;
}
EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
return EM_TRUE;
return EM_TRUE;
}
static void applyResize(int logicalW, int logicalH) {
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
double dpr = emscripten_get_device_pixel_ratio();
//double dpr = 1; // low quality
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
double dpr = emscripten_get_device_pixel_ratio();
//double dpr = 1; // low quality
// Вычисляем реальные физические пиксели
int physicalW = static_cast<int>(logicalW * dpr);
int physicalH = static_cast<int>(logicalH * dpr);
// Вычисляем реальные физические пиксели
int physicalW = static_cast<int>(logicalW * dpr);
int physicalH = static_cast<int>(logicalH * dpr);
// Устанавливаем размер внутреннего буфера канваса
emscripten_set_canvas_element_size("#canvas", physicalW, physicalH);
// Устанавливаем размер внутреннего буфера канваса
emscripten_set_canvas_element_size("#canvas", physicalW, physicalH);
// Сообщаем SDL о новом размере.
// ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели
// для корректной работы последующих вызовов glViewport.
if (ZL::Environment::window) {
SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH);
}
// Сообщаем SDL о новом размере.
// ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели
// для корректной работы последующих вызовов glViewport.
if (ZL::Environment::window) {
SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH);
}
// Пушим событие, чтобы движок пересчитал матрицы проекции
SDL_Event e = {};
e.type = SDL_WINDOWEVENT;
e.window.event = SDL_WINDOWEVENT_RESIZED;
e.window.data1 = physicalW;
e.window.data2 = physicalH;
SDL_PushEvent(&e);
// Пушим событие, чтобы движок пересчитал матрицы проекции
SDL_Event e = {};
e.type = SDL_WINDOWEVENT;
e.window.event = SDL_WINDOWEVENT_RESIZED;
e.window.data1 = physicalW;
e.window.data2 = physicalH;
SDL_PushEvent(&e);
std::cout << "Resized, new size: " << logicalW << "x" << logicalH
<< " (physical: " << physicalW << "x" << physicalH
std::cout << "Resized, new size: " << logicalW << "x" << logicalH
<< " (physical: " << physicalW << "x" << physicalH
<< ", DPR: " << dpr << ")" << std::endl;
}
EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*userData*/) {
// Use the event's window dimensions — querying the canvas element would
// return its old fixed size (e.g. 1280x720) before it has been resized.
applyResize(e->windowInnerWidth, e->windowInnerHeight);
return EM_FALSE;
// Use the event's window dimensions — querying the canvas element would
// return its old fixed size (e.g. 1280x720) before it has been resized.
applyResize(e->windowInnerWidth, e->windowInnerHeight);
return EM_FALSE;
}
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
ZL::Environment::isFullscreen = e->isFullscreen;
ZL::Environment::isFullscreen = e->isFullscreen;
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
// так как после перехода в FS именно он растягивается на весь экран.
double clientW, clientH;
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
applyResize(clientW, clientH);
return EM_FALSE;
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
// так как после перехода в FS именно он растягивается на весь экран.
double clientW, clientH;
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
applyResize(clientW, clientH);
return EM_FALSE;
}
int main(int argc, char* argv[]) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); // Для WebGL 2.0
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); // Для WebGL 2.0
ZL::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
glContext_x = SDL_GL_CreateContext(ZL::Environment::window);
ZL::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
glContext_x = SDL_GL_CreateContext(ZL::Environment::window);
SDL_GL_MakeCurrent(ZL::Environment::window, glContext_x);
SDL_GL_MakeCurrent(ZL::Environment::window, glContext_x);
g_game = new ZL::Game();
g_game->setup();
emscripten_set_webglcontextlost_callback("#canvas", nullptr, EM_TRUE, onWebGLContextLost);
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
// Keep Environment::width/height in sync when the canvas is resized.
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
// 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
// Получаем реальные размеры окна браузера на момент запуска
int canvasW = EM_ASM_INT({ return window.innerWidth; });
int canvasH = EM_ASM_INT({ return window.innerHeight; });
// Keep Environment::width/height in sync when the canvas is resized.
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
// 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
// Получаем реальные размеры окна браузера на момент запуска
int canvasW = EM_ASM_INT({ return window.innerWidth; });
int canvasH = EM_ASM_INT({ return window.innerHeight; });
// Вызываем вашу функцию — она сама применит DPR, выставит физический размер
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
applyResize(canvasW, canvasH);
// Вызываем вашу функцию — она сама применит DPR, выставит физический размер
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
applyResize(canvasW, canvasH);
// Prevent mouse clicks from generating fake SDL_FINGERDOWN events (desktop browser)
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
// Prevent touch events from generating fake SDL_MOUSEBUTTONDOWN events (mobile browser),
// since we now handle SDL_FINGERDOWN directly for multi-touch support.
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
ZL::emscriptenInitFileSystem();
ZL::emscriptenInitFileSystem();
emscripten_set_main_loop(MainLoop, 0, 1);
return 0;
emscripten_set_main_loop(MainLoop, 0, 1);
return 0;
}
#endif
@@ -147,82 +141,83 @@ int main(int argc, char* argv[]) {
extern "C" int SDL_main(int argc, char* argv[]) {
// Инициализация SDL перед получением информации о дисплее
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL init failed: %s", SDL_GetError());
return 1;
}
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL init failed: %s", SDL_GetError());
return 1;
}
SDL_DisplayMode displayMode;
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
SDL_Quit();
return 1;
}
ZL::Environment::width = displayMode.w;
ZL::Environment::height = displayMode.h;
SDL_DisplayMode displayMode;
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
SDL_Quit();
return 1;
}
ZL::Environment::width = displayMode.w;
ZL::Environment::height = displayMode.h;
__android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d",
ZL::Environment::width, ZL::Environment::height);
__android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d",
ZL::Environment::width, ZL::Environment::height);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
ZL::Environment::window = SDL_CreateWindow(
"Shadow Over Bishkek",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
ZL::Environment::width, ZL::Environment::height,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
);
ZL::Environment::window = SDL_CreateWindow(
"Shadow Over Bishkek",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
ZL::Environment::width, ZL::Environment::height,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
);
if (!ZL::Environment::window) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "Failed to create window: %s", SDL_GetError());
SDL_Quit();
return 1;
}
__android_log_print(ANDROID_LOG_ERROR, "Game", "Failed to create window: %s", SDL_GetError());
SDL_Quit();
return 1;
}
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
if (!ctx) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_CreateContext failed: %s", SDL_GetError());
SDL_DestroyWindow(ZL::Environment::window);
SDL_Quit();
return 1;
}
if (!ctx) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_CreateContext failed: %s", SDL_GetError());
SDL_DestroyWindow(ZL::Environment::window);
SDL_Quit();
return 1;
}
if (SDL_GL_MakeCurrent(ZL::Environment::window, ctx) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_MakeCurrent failed: %s", SDL_GetError());
SDL_GL_DeleteContext(ctx);
SDL_DestroyWindow(ZL::Environment::window);
SDL_Quit();
return 1;
}
if (SDL_GL_MakeCurrent(ZL::Environment::window, ctx) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_MakeCurrent failed: %s", SDL_GetError());
SDL_GL_DeleteContext(ctx);
SDL_DestroyWindow(ZL::Environment::window);
SDL_Quit();
return 1;
}
// Настройка VSync
if (SDL_GL_SetSwapInterval(1) < 0) {
__android_log_print(ANDROID_LOG_WARN, "Game", "Unable to set VSync: %s", SDL_GetError());
}
// Настройка VSync
if (SDL_GL_SetSwapInterval(1) < 0) {
__android_log_print(ANDROID_LOG_WARN, "Game", "Unable to set VSync: %s", SDL_GetError());
}
// Настройка игры
try {
game.setup();
} catch (const std::exception &e) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "Game setup failed: %s", e.what());
return 1;
}
// Настройка игры
try {
game.setup();
}
catch (const std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "Game setup failed: %s", e.what());
return 1;
}
while (!game.shouldExit()) {
game.update();
SDL_Delay(16);
}
while (!game.shouldExit()) {
game.update();
SDL_Delay(16);
}
return 0;
return 0;
}
@@ -231,113 +226,157 @@ extern "C" int SDL_main(int argc, char* argv[]) {
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__linux__)
int main(int argc, char *argv[]) {
try
{
int main(int argc, char* argv[]) {
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2");
//SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "1");
SDL_GLContext ctx = nullptr;
try
{
#ifdef STEAMSDK
// Передаем явный App ID демо-версии
if (SteamAPI_RestartAppIfNecessary(4945840)) {
return 0;
}
constexpr int CONST_WIDTH = ZL::Environment::CONST_DEFAULT_WIDTH;
constexpr int CONST_HEIGHT = ZL::Environment::CONST_DEFAULT_HEIGHT;
if (!SteamAPI_Init()) {
std::cerr << "SteamAPI_Init() failed! Steam client might not be running.\n";
return 1;
}
#endif
ZL::Environment::width = CONST_WIDTH;
ZL::Environment::height = CONST_HEIGHT;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
SDL_Log("SDL init failed: %s", SDL_GetError());
return 1;
}
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
constexpr int CONST_WIDTH = ZL::Environment::CONST_DEFAULT_WIDTH;
constexpr int CONST_HEIGHT = ZL::Environment::CONST_DEFAULT_HEIGHT;
// Read the persisted fullscreen preference (if any) before creating the
// window, so it opens directly in the right mode instead of flashing
// windowed -> fullscreen once settings are loaded later during setupPart2().
{
const std::string settingsContent = ZL::readSavedTextFile("settings.json");
if (!settingsContent.empty()) {
try {
const nlohmann::json settingsRoot = nlohmann::json::parse(settingsContent);
if (settingsRoot.contains("fullscreen")) {
ZL::Environment::isFullscreen = settingsRoot["fullscreen"].get<bool>();
}
if (settingsRoot.contains("isHighDPIEnabled")) {
ZL::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get<bool>();
}
} catch (const std::exception& e) {
std::cerr << "[settings] Failed to parse settings.json: " << e.what() << std::endl;
}
}
}
ZL::Environment::width = CONST_WIDTH;
ZL::Environment::height = CONST_HEIGHT;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
SDL_Log("SDL init failed: %s", SDL_GetError());
return 1;
}
Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
if (ZL::Environment::isFullscreen) {
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
//ZL::Environment::isFullscreen = false;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
// Read the persisted fullscreen preference (if any) before creating the
// window, so it opens directly in the right mode instead of flashing
// windowed -> fullscreen once settings are loaded later during setupPart2().
{
const std::string settingsContent = ZL::readSavedTextFile("settings.json");
if (!settingsContent.empty()) {
try {
const nlohmann::json settingsRoot = nlohmann::json::parse(settingsContent);
if (settingsRoot.contains("fullscreen")) {
ZL::Environment::isFullscreen = settingsRoot["fullscreen"].get<bool>();
}
if (settingsRoot.contains("isHighDPIEnabled")) {
ZL::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get<bool>();
}
}
catch (const std::exception& e) {
std::cerr << "[settings] Failed to parse settings.json: " << e.what() << std::endl;
}
}
}
Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
if (ZL::Environment::isFullscreen) {
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
SDL_DisplayMode dm;
if (SDL_GetCurrentDisplayMode(0, &dm) == 0) {
std::cout << "Desktop resolution: " << dm.w << "x" << dm.h << " @ " << dm.refresh_rate << "Hz\n";
}
else {
SDL_Log("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
}
SDL_DisplayMode dm;
if (SDL_GetCurrentDisplayMode(0, &dm) == 0) {
std::cout << "Desktop resolution: " << dm.w << "x" << dm.h << " @ " << dm.refresh_rate << "Hz\n";
}
else {
SDL_Log("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
}
int windowWidth = ZL::Environment::width;
int windowHeight = ZL::Environment::height;
//In case if the game is launched on a small screen
if (dm.w <= 1280 && dm.h <= 720) {
windowWidth = round(dm.w / 1.5);
windowWidth = round(dm.w / 1.5);
windowHeight = round(dm.h / 1.5);
}
ZL::Environment::window = SDL_CreateWindow(
"Shadow Over Bishkek",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
windowWidth, windowHeight,
windowFlags
);
ZL::Environment::window = SDL_CreateWindow(
"Shadow Over Bishkek Demo",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
windowWidth, windowHeight,
windowFlags
);
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
SDL_GL_MakeCurrent(ZL::Environment::window, ctx);
ctx = SDL_GL_CreateContext(ZL::Environment::window);
SDL_GL_MakeCurrent(ZL::Environment::window, ctx);
// On HiDPI displays the drawable (physical) size is larger than the
// logical window size. Use the drawable size so the GL framebuffer
// covers every physical pixel and the image stays sharp.
if (ZL::Environment::isHighDPIEnabled)
{
int drawW, drawH;
SDL_GL_GetDrawableSize(ZL::Environment::window, &drawW, &drawH);
ZL::Environment::width = drawW;
ZL::Environment::height = drawH;
std::cout << "HiDPI enabled, drawable size: " << drawW << "x" << drawH << std::endl;
}
else
{
if (ZL::Environment::isHighDPIEnabled) {
int drawW, drawH;
SDL_GL_GetDrawableSize(ZL::Environment::window, &drawW, &drawH);
ZL::Environment::width = drawW;
ZL::Environment::height = drawH;
std::cout << "HiDPI enabled, drawable size: " << drawW << "x" << drawH << std::endl;
}
else {
int winW, winH;
SDL_GetWindowSize(ZL::Environment::window, &winW, &winH);
ZL::Environment::width = winW;
ZL::Environment::height = winH;
SDL_GetWindowSize(ZL::Environment::window, &winW, &winH);
ZL::Environment::width = winW;
ZL::Environment::height = winH;
std::cout << "HiDPI disabled, window size: " << winW << "x" << winH << std::endl;
}
}
game.setup();
// Динамическое создание объекта игры
g_game = new ZL::Game();
g_game->setup();
while (!game.shouldExit()) {
game.update();
SDL_Delay(2);
}
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
while (!g_game->shouldExit()) {
g_game->update();
SDL_Delay(2);
return 0;
#ifdef STEAMSDK
SteamAPI_RunCallbacks();
#endif
}
// Явное уничтожение объекта игры ДО завершения работы Steamworks
delete g_game;
g_game = nullptr;
}
catch (const std::exception& e)
{
std::cerr << "Exception caught: " << e.what() << std::endl;
if (g_game) {
delete g_game;
g_game = nullptr;
}
}
catch (...)
{
std::cerr << "Unknown exception happened: " << std::endl;
if (g_game) {
delete g_game;
g_game = nullptr;
}
}
#ifdef STEAMSDK
// Деинициализация Steamworks происходит только тогда, когда все объекты игры гарантированно уничтожены
SteamAPI_Shutdown();
#endif
// Очистка ресурсов SDL
if (ctx) {
SDL_GL_DeleteContext(ctx);
}
if (ZL::Environment::window) {
SDL_DestroyWindow(ZL::Environment::window);
}
SDL_Quit();
return 0;
}
#endif