diff --git a/CMakeLists.txt b/CMakeLists.txt index cdd552c1c..eb09309e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,6 @@ OPTION(OPTION_FINAL_VERSION "Generate Final version" ON) OPTION(OPTION_STATIC_BUILD "Generate static build" OFF) OPTION(OPTION_STATIC_LIB_STD "Statically link to libstd++" ON) OPTION(OPTION_CHECK_SCRIPTS "Check game scripts with XPrm" OFF) -OPTION(OPTION_PROCESS_SCRIPTS "Re-Process game scripts with XPrm" OFF) OPTION(OPTION_DISABLE_STACKTRACE "Disable stacktrace support" OFF) OPTION(OPTION_LINKER "Use specified linker instead of default" "") OPTION(OPTION_DEBUG_ASSERT "Enable debug assertions" OFF) @@ -28,10 +27,7 @@ OPTION(OPTION_GPERFTOOLS "Link executable with gperftools for debugging" OFF) OPTION(OPTION_D3D9 "Enable D3D9 renderer using dxvk in *nix or DirectX in Windows" ON) OPTION(OPTION_LINK_LIBS "Link executables with provided libraries, placed before the rest" "") OPTION(OPTION_LINK_LIBS_POST "Link executables with provided libraries, placed after the rest" "") -OPTION(OPTION_DXVK_1 "Use old DXVK-native (DXVK 1.x) instead of DXVK 2.x" OFF) -OPTION(OPTION_DXVK_SOURCE_DIR "Path for predownloaded DXVK source code") OPTION(OPTION_SOKOL "Enable Sokol GFX based renderer" ON) -OPTION(OPTION_PROCESS_SHADERS "Re-Process game shaders" OFF) OPTION(OPTION_FFMPEG_MOVIES "Use FFMPEG to decode ingame movies" ON) OPTION(OPTION_ASAN "Enable AddressSanitizer" OFF) OPTION(OPTION_O0 "Disable optimizations" OFF) diff --git a/Source/Game/CMakeLists.txt b/Source/Game/CMakeLists.txt index 04bd7e164..d44058b3f 100644 --- a/Source/Game/CMakeLists.txt +++ b/Source/Game/CMakeLists.txt @@ -1,3 +1,6 @@ + +OPTION(OPTION_PROCESS_SCRIPTS "Re-Process game scripts with XPrm" OFF) + target_sources(perimeter PRIVATE CameraManager.cpp MonkManager.cpp @@ -143,7 +146,7 @@ SET(perimeter_LINK_LIBS IF(PERIMETER_WINDOWS) SET(perimeter_LINK_LIBS ${perimeter_LINK_LIBS} ${SDL2MAIN_LIBRARY}) ENDIF() - +message(${perimeter_LINK_LIBS}) target_link_libraries(perimeter PRIVATE ${perimeter_LINK_LIBS}) #Install rules diff --git a/Source/Game/GameContent.cpp b/Source/Game/GameContent.cpp index 030651893..00492dc34 100644 --- a/Source/Game/GameContent.cpp +++ b/Source/Game/GameContent.cpp @@ -23,11 +23,8 @@ int firstMissionNumber = 0; static std::map gameMods; -///The identified content at the root of game content, this only can be one thing GAME_CONTENT terGameContentBase = CONTENT_NONE; -///All available contents in this installation (base + addons) GAME_CONTENT terGameContentAvailable = CONTENT_NONE; -///Current selected content, can be several or only one in available content (when user chooses one) GAME_CONTENT terGameContentSelect = CONTENT_NONE; std::map& getGameMods() { diff --git a/Source/Game/GameContent.h b/Source/Game/GameContent.h index 84069b45c..50eb5ad71 100644 --- a/Source/Game/GameContent.h +++ b/Source/Game/GameContent.h @@ -1,8 +1,11 @@ #ifndef PERIMETER_GAMECONTENT_H #define PERIMETER_GAMECONTENT_H +///The identified content at the root of game content, this only can be one thing extern GAME_CONTENT terGameContentBase; +///All available contents in this installation (base + addons) extern GAME_CONTENT terGameContentAvailable; +///Current selected content, can be several or only one in available content (when user chooses one) extern GAME_CONTENT terGameContentSelect; /** diff --git a/Source/Game/Runtime.cpp b/Source/Game/Runtime.cpp index 6f562c74e..4776c20a6 100644 --- a/Source/Game/Runtime.cpp +++ b/Source/Game/Runtime.cpp @@ -864,7 +864,8 @@ void show_help() { "\n" "Multiplayer:\n" " server=IP:PORT - Opens game in server mode and binds to address\n" - " connect=IP:PORT - Connects to provided server address\n" + " connect=IP:PORT - Connects as client to provided server address\n" + " connect_room=ROOMID - Connects as client to provided room id\n" " password=Password - Password to use as server or connecting to server\n" " name=Name - Player name to use as server or connecting to server\n" " save=savegame - Multiplayer save name to use to resume as server\n" diff --git a/Source/Game/Scripts/InterfaceScriptExport.cppi b/Source/Game/Scripts/InterfaceScriptExport.cppi index 06f91721b..06733e324 100644 --- a/Source/Game/Scripts/InterfaceScriptExport.cppi +++ b/Source/Game/Scripts/InterfaceScriptExport.cppi @@ -187,10 +187,10 @@ struct sqshTabSheet { }; #pragma pack( pop, __XScript_struct_pack__) #endif // __XScript_struct_sqshTabSheet__ -static BGObj _bgObjects_59[] = { { "group battle" +static BGObj _bgObjects_60[] = { { "group battle" , "battle" }, { "group back2", "back2" }, { "group skip", "skip" }, { "group background" , "background off" } }; -static sqshControl _controls_58[] = { { SQSH_CREDITS_TEXT_TYPE +static sqshControl _controls_59[] = { { SQSH_CREDITS_TEXT_TYPE , SQSH_MM_CREDITS_TXT, 2, 3, 0.17480469f, 0.11067708f, 0.63671875f, 0.70833333f, 0.f , 0.f, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 1024.f, 1024.f, 0.33105469f , 0.20442708f, 0.125f, 0.16666667f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f @@ -251,7 +251,7 @@ static sqshControl _controls_58[] = { { SQSH_CREDITS_TEXT_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE +static sqshControl _controls_58[] = { { SQSH_MOVE_BUTTON_TYPE , SQSH_MM_RAMKA, 2, 3, 0.55761719f, 0.f, 0.41503906f, 0.84375f, 359.f, -648.f, { "resource\\icons\\MainMenu\\new_menu.tga" , 599.f, 376.f, 425.f, 648.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -294,7 +294,7 @@ static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 477, -1, 0, 0, 0, 0, { "", "" }, "SOUND EFFECTS", 0.f + }, "mainmenu_button", "", "", 483, -1, 0, 0, 0, 0, { "", "" }, "SOUND EFFECTS", 0.f , 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -306,7 +306,7 @@ static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 476, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 482, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -342,7 +342,7 @@ static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press" - , "", "", 478, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", "", 484, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -365,7 +365,7 @@ static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" - , "", "", 481, -1, 0, 0, 0, 0, { "", "" }, "MUSIC", 0.f, 0.02083333f, SHELL_ALIGN_LEFT + , "", "", 487, -1, 0, 0, 0, 0, { "", "" }, "MUSIC", 0.f, 0.02083333f, SHELL_ALIGN_LEFT , SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -376,7 +376,7 @@ static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f - , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 480, -1 + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 486, -1 , 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255 , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -412,7 +412,7 @@ static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE , 447.f, 456.f, 16.f, 27.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 482, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 488, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -442,7 +442,7 @@ static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT , 0 } }; -static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA +static sqshControl _controls_57[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 2, 3, 0.f, 0.f, 1.f, 1.f, 521.f, -768.f, { "resource\\icons\\MainMenu\\New_statistik.tga" , 0.f, 0.f, 1024.f, 768.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -473,7 +473,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" - , "", "", 395, -1, 0, 0, 0, 0, { "", "" }, "LANDSCAPE DETAILS", 0.f, 0.02083333f, SHELL_ALIGN_LEFT + , "", "", 401, -1, 0, 0, 0, 0, { "", "" }, "LANDSCAPE DETAILS", 0.f, 0.02083333f, SHELL_ALIGN_LEFT , SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -484,7 +484,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f - , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 394, -1 + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 400, -1 , 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255 , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -508,7 +508,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 475, -1, 0, 0, 0, 0, { "", "" }, "REFLECTIONS", 0.f + }, "mainmenu_button", "", "", 481, -1, 0, 0, 0, 0, { "", "" }, "REFLECTIONS", 0.f , 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -520,7 +520,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 474, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 480, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -543,7 +543,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 399, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 405, -1, 0, 0, 0, 0, { "", "" }, "OCCLUSION", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0 , 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" @@ -555,7 +555,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 398, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 404, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -578,7 +578,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 401, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 407, -1, 0, 0, 0, 0, { "", "" }, "POINT LIGHT", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -590,7 +590,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 400, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 406, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -613,7 +613,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 473, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 479, -1, 0, 0, 0, 0, { "", "" }, "SHADOW TYPE", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -625,7 +625,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 472, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 478, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -648,7 +648,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 471, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 477, -1, 0, 0, 0, 0, { "", "" }, "SHADOW QUALITY", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255 , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -660,7 +660,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 470, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 476, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -683,7 +683,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f - , 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 403, -1, 0, 0, 0, 0, { "" + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 409, -1, 0, 0, 0, 0, { "" , "" }, "SHADOW SAMPLES", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2 , 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -695,7 +695,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 402, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 408, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -718,7 +718,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 405, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 411, -1, 0, 0, 0, 0, { "", "" }, "BUMP MAPPING", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -730,7 +730,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 404, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 410, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -753,7 +753,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 407, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 413, -1, 0, 0, 0, 0, { "", "" }, "BUMP CHAOS", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -765,7 +765,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 406, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 412, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -801,7 +801,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press" - , "", "", 396, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", "", 402, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -824,7 +824,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 409, -1, 0, 0, 0, 0, { "", "" }, "COMPRESS TEXTURES" + }, "mainmenu_button", "", "", 415, -1, 0, 0, 0, 0, { "", "" }, "COMPRESS TEXTURES" , 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0 , 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -836,7 +836,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 408, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 414, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -865,7 +865,7 @@ static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT , 0 } }; -static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA +static sqshControl _controls_56[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 2, 3, 0.f, 0.f, 1.f, 1.f, 521.f, -768.f, { "resource\\icons\\MainMenu\\New_statistik.tga" , 0.f, 0.f, 1024.f, 768.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -908,7 +908,7 @@ static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 467, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 473, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -931,7 +931,7 @@ static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, "mainmenu_button", "", "", 413, -1, 0, 0, 0, 0, { "", "" }, "SETTINGS", 0.f + , 0 }, "mainmenu_button", "", "", 419, -1, 0, 0, 0, 0, { "", "" }, "SETTINGS", 0.f , 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -943,7 +943,7 @@ static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 412, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 418, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -966,7 +966,7 @@ static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 456, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 462, -1, 0, 0, 0, 0, { "", "" }, "UI POSITION", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -978,7 +978,7 @@ static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 455, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 461, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1001,7 +1001,7 @@ static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 458, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 464, -1, 0, 0, 0, 0, { "", "" }, "GRAB INPUT", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1013,7 +1013,7 @@ static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 457, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 463, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1090,7 +1090,7 @@ static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE, SQSH_MM_RAMKA , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE +static sqshControl _controls_55[] = { { SQSH_MOVE_BUTTON_TYPE , SQSH_MM_RAMKA, 2, 3, 0.f, 0.f, 1.f, 1.f, 521.f, -768.f, { "resource\\icons\\MainMenu\\New_statistik.tga" , 0.f, 0.f, 1024.f, 768.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -1133,7 +1133,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 447.f, 456.f, 16.f, 27.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 441, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 447, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1169,7 +1169,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 443, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 449, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1192,7 +1192,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 448, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 454, -1, 0, 0, 0, 0, { "", "" }, "TOOL TIPS", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0 , 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" @@ -1204,7 +1204,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 447, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 453, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1227,7 +1227,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 450, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 456, -1, 0, 0, 0, 0, { "", "" }, "RUN IN BACKGROUND", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255 , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1239,7 +1239,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 449, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 455, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1262,7 +1262,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 454, -1, 0, 0, 0, 0, { "", "" + , 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 460, -1, 0, 0, 0, 0, { "", "" }, "CAMERA MODE", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1274,7 +1274,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 453, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 459, -1, 0, 0, 0, 0, { "", "" }, "" , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1303,7 +1303,7 @@ static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static sqshControl _controls_53[] = { { SQSH_MOVE_BUTTON_TYPE +static sqshControl _controls_54[] = { { SQSH_MOVE_BUTTON_TYPE , SQSH_MM_RAMKA, 2, 3, 0.33105469f, 0.f, 0.32226562f, 0.68619792f, 359.f, -768.f, { "resource\\icons\\MainMenu\\new_menu.tga" , 0.f, 0.f, 330.f, 527.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -1411,7 +1411,7 @@ static sqshControl _controls_53[] = { { SQSH_MOVE_BUTTON_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static sqshControl _controls_52[] = { { SQSH_MOVE_BUTTON_TYPE +static sqshControl _controls_53[] = { { SQSH_MOVE_BUTTON_TYPE , SQSH_MM_RAMKA, 2, 3, 0.33105469f, 0.f, 0.32226562f, 0.68619792f, 359.f, -768.f, { "resource\\icons\\MainMenu\\new_menu.tga" , 0.f, 0.f, 330.f, 527.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -1506,10 +1506,10 @@ static sqshControl _controls_52[] = { { SQSH_MOVE_BUTTON_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_51[] = { { "group start" +static BGObj _bgObjects_52[] = { { "group start" , "start screen" }, { "group back", "back" }, { "group next", "next" }, { "group background" , "background off" } }; -static sqshControl _controls_50[] = { { SQSH_SCALE_BUTTON_TYPE +static sqshControl _controls_51[] = { { SQSH_SCALE_BUTTON_TYPE , SQSH_MM_RAMKA, 2, 3, 0.33300781f, 0.20963542f, 0.33105469f, 0.60677083f, 0.f, 0.60677083f , { "resource\\icons\\MainMenu\\Main_menu.tga", 0.f, 518.f, 339.f, 466.f, 0.f, 0.f , 0.f, 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 60.f, 120.f, 0.f @@ -1609,10 +1609,10 @@ static sqshControl _controls_50[] = { { SQSH_SCALE_BUTTON_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_49[] = { { "group start" +static BGObj _bgObjects_50[] = { { "group start" , "start screen" }, { "group back", "back" }, { "group background", "background off" } }; -static sqshControl _controls_48[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA +static sqshControl _controls_49[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA , 2, 3, 0.39941406f, 0.45703125f, 0.1953125f, 0.00390625f, 0.f, 0.f, { "resource\\icons\\MainMenu\\Main_menu.tga" , 2.f, 987.f, 200.f, 3.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -1829,10 +1829,10 @@ static sqshControl _controls_48[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT , 0 } }; -static BGObj _bgObjects_47[] = { { "group battle", "battle" }, { "group back2" +static BGObj _bgObjects_48[] = { { "group battle", "battle" }, { "group back2" , "back2" }, { "group next2", "next2" }, { "group skip", "skip" }, { "group map", "map" }, { "group background", "background off" } }; -static sqshControl _controls_46[] = { { SQSH_SCALE_BUTTON_TYPE +static sqshControl _controls_47[] = { { SQSH_SCALE_BUTTON_TYPE , SQSH_MM_RAMKA, 2, 3, 0.18164062f, 0.12239583f, 0.37109375f, 0.67057292f, 0.f, 0.67057292f , { "resource\\icons\\MainMenu\\Main_menu.tga", 0.f, 0.f, 300.f, 515.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 60.f, 120.f, 0.f, 0.f @@ -1928,7 +1928,7 @@ static sqshControl _controls_46[] = { { SQSH_SCALE_BUTTON_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 499, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_LEFT + }, "button_press", "", "", 505, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_LEFT , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -1940,7 +1940,7 @@ static sqshControl _controls_46[] = { { SQSH_SCALE_BUTTON_TYPE , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, "mainmenu_button", "", "", 497, -1, 0, 0, 0, 0, { "", "" }, "MOD ENABLE STATE" + , 0 }, "mainmenu_button", "", "", 503, -1, 0, 0, 0, 0, { "", "" }, "MOD ENABLE STATE" , 0.f, 0.00911458f, SHELL_ALIGN_RIGHT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0 , 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2017,10 +2017,10 @@ static sqshControl _controls_46[] = { { SQSH_SCALE_BUTTON_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_45[] = { { "group battle" +static BGObj _bgObjects_46[] = { { "group battle" , "battle" }, { "group back2", "back2" }, { "group background", "background off" } }; -static sqshControl _controls_44[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 +static sqshControl _controls_45[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 3, 0.24414062f, 0.34375f, 0.48828125f, 0.00390625f, 0.f, 0.f, { "resource\\icons\\MainMenu\\Main_menu.tga" , 2.f, 987.f, 500.f, 3.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -2075,7 +2075,7 @@ static sqshControl _controls_44[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 477, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 483, -1, 0 , 0, 0, 0, { "", "" }, "SOUND EFFECTS", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2088,7 +2088,7 @@ static sqshControl _controls_44[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 476, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 482, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2125,7 +2125,7 @@ static sqshControl _controls_44[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 478, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 484, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -2148,7 +2148,7 @@ static sqshControl _controls_44[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 481, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 487, -1, 0 , 0, 0, 0, { "", "" }, "MUSIC", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2161,7 +2161,7 @@ static sqshControl _controls_44[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 480, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 486, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -2197,7 +2197,7 @@ static sqshControl _controls_44[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 482, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 488, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -2226,9 +2226,9 @@ static sqshControl _controls_44[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT , 0 } }; -static BGObj _bgObjects_43[] = { { "group battle", "battle" }, { "group back2" +static BGObj _bgObjects_44[] = { { "group battle", "battle" }, { "group back2" , "back2" }, { "group background", "background off" } }; -static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE +static sqshControl _controls_43[] = { { SQSH_GENERAL_WND_TYPE , SQSH_MM_RAMKA, 2, 3, 0.24414062f, 0.17317708f, 0.48828125f, 0.00390625f, 0.f, 0.f , { "resource\\icons\\MainMenu\\Main_menu.tga", 2.f, 987.f, 500.f, 3.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f @@ -2368,7 +2368,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 395, -1, 0, 0, 0, 0, { "", "" }, "LANDSCAPE DETAILS" + }, "mainmenu_button", "", "", 401, -1, 0, 0, 0, 0, { "", "" }, "LANDSCAPE DETAILS" , 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0 , 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2380,7 +2380,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 394, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 400, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2404,7 +2404,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 475, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 481, -1, 0 , 0, 0, 0, { "", "" }, "REFLECTIONS", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2417,7 +2417,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 474, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 480, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2441,7 +2441,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 399, -1, 0, 0, 0, 0, { "", "" }, "OCCLUSION", 0.f, 0.02083333f + }, "mainmenu_button", "", "", 405, -1, 0, 0, 0, 0, { "", "" }, "OCCLUSION", 0.f, 0.02083333f , SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0 , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" @@ -2453,7 +2453,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 398, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 404, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2477,7 +2477,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 401, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 407, -1, 0 , 0, 0, 0, { "", "" }, "POINT LIGHT", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2490,7 +2490,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 400, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 406, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2514,7 +2514,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 473, -1, 0, 0, 0, 0, { "", "" }, "SHADOW TYPE", 0.f + }, "mainmenu_button", "", "", 479, -1, 0, 0, 0, 0, { "", "" }, "SHADOW TYPE", 0.f , 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2526,7 +2526,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 472, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 478, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2550,7 +2550,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 471, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 477, -1, 0 , 0, 0, 0, { "", "" }, "SHADOW QUALITY", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2563,7 +2563,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 470, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 476, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2587,7 +2587,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, "mainmenu_button", "", "", 403, -1, 0, 0, 0, 0, { "", "" }, "SHADOW SAMPLES" + , 0 }, "mainmenu_button", "", "", 409, -1, 0, 0, 0, 0, { "", "" }, "SHADOW SAMPLES" , 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0 , 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2600,7 +2600,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 402, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 408, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -2623,7 +2623,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 405, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 411, -1, 0 , 0, 0, 0, { "", "" }, "BUMP MAPPING", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2636,7 +2636,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 404, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 410, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2660,7 +2660,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 407, -1, 0, 0, 0, 0, { "", "" }, "BUMP CHAOS", 0.f + }, "mainmenu_button", "", "", 413, -1, 0, 0, 0, 0, { "", "" }, "BUMP CHAOS", 0.f , 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2672,7 +2672,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 406, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 412, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2709,7 +2709,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 396, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 402, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -2732,7 +2732,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 409, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 415, -1, 0 , 0, 0, 0, { "", "" }, "COMPRESS TEXTURES", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2745,7 +2745,7 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 408, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 414, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2774,10 +2774,10 @@ static sqshControl _controls_42[] = { { SQSH_GENERAL_WND_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_41[] = { { "group battle" +static BGObj _bgObjects_42[] = { { "group battle" , "battle" }, { "group back2", "back2" }, { "group skip", "skip" }, { "group next2" , "next2" }, { "group background", "background off" } }; -static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE +static sqshControl _controls_41[] = { { SQSH_GENERAL_WND_TYPE , SQSH_MM_RAMKA, 2, 3, 0.24414062f, 0.25260417f, 0.48828125f, 0.00390625f, 0.f, 0.f , { "resource\\icons\\MainMenu\\Main_menu.tga", 2.f, 987.f, 500.f, 3.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f @@ -2882,7 +2882,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 467, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 473, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -2905,7 +2905,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 413, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 419, -1, 0 , 0, 0, 0, { "", "" }, "SETTINGS", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2918,7 +2918,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 412, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 418, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -2941,7 +2941,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 462, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 468, -1, 0 , 0, 0, 0, { "", "" }, "RESOLUTION", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2954,7 +2954,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 461, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 467, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_RIGHT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2978,7 +2978,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 464, -1, 0, 0, 0, 0, { "", "" }, "COLOR DEPTH", 0.f + }, "mainmenu_button", "", "", 470, -1, 0, 0, 0, 0, { "", "" }, "COLOR DEPTH", 0.f , 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -2990,7 +2990,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 463, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 469, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3014,7 +3014,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 456, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 462, -1, 0 , 0, 0, 0, { "", "" }, "UI POSITION", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3027,7 +3027,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 455, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 461, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3051,7 +3051,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 458, -1, 0, 0, 0, 0, { "", "" }, "GRAB INPUT", 0.f + }, "mainmenu_button", "", "", 464, -1, 0, 0, 0, 0, { "", "" }, "GRAB INPUT", 0.f , 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3063,7 +3063,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 457, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 463, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3087,7 +3087,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 460, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 466, -1, 0 , 0, 0, 0, { "", "" }, "FOG", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3100,7 +3100,7 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 459, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 465, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3177,10 +3177,10 @@ static sqshControl _controls_40[] = { { SQSH_GENERAL_WND_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_39[] = { { "group battle" +static BGObj _bgObjects_40[] = { { "group battle" , "battle" }, { "group back2", "back2" }, { "group background", "background off" } }; -static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 +static sqshControl _controls_39[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 3, 0.24414062f, 0.24739583f, 0.48828125f, 0.00390625f, 0.f, 0.f, { "resource\\icons\\MainMenu\\Main_menu.tga" , 2.f, 987.f, 500.f, 3.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -3272,7 +3272,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 441, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 447, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -3308,7 +3308,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" - , "", 443, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , "", 449, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT @@ -3331,7 +3331,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 448, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 454, -1, 0 , 0, 0, 0, { "", "" }, "TOOL TIPS", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3344,7 +3344,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 447, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 453, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3368,7 +3368,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 450, -1, 0, 0, 0, 0, { "", "" }, "RUN IN BACKGROUND" + }, "mainmenu_button", "", "", 456, -1, 0, 0, 0, 0, { "", "" }, "RUN IN BACKGROUND" , 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0 , 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3380,7 +3380,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 449, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 455, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3404,7 +3404,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 452, -1, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", 458, -1, 0 , 0, 0, 0, { "", "" }, "START SPLASH", 0.f, 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT , 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3417,7 +3417,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "button_press", "", "", 451, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + }, "button_press", "", "", 457, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3441,7 +3441,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 454, -1, 0, 0, 0, 0, { "", "" }, "CAMERA MODE", 0.f + }, "mainmenu_button", "", "", 460, -1, 0, 0, 0, 0, { "", "" }, "CAMERA MODE", 0.f , 0.02083333f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3453,7 +3453,7 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 453, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 459, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -3483,9 +3483,9 @@ static sqshControl _controls_38[] = { { SQSH_GENERAL_WND_TYPE, SQSH_MM_RAMKA, 2 , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT , 0 } }; -static BGObj _bgObjects_37[] = { { "group start", "start screen" }, { "group back" +static BGObj _bgObjects_38[] = { { "group start", "start screen" }, { "group back" , "back" }, { "group background", "background off" } }; -static sqshControl _controls_36[] = { { SQSH_GENERAL_WND_TYPE +static sqshControl _controls_37[] = { { SQSH_GENERAL_WND_TYPE , SQSH_MM_RAMKA, 2, 3, 0.44824219f, 0.51432292f, 0.09765625f, 0.00390625f, 0.f, 0.f , { "resource\\icons\\MainMenu\\Main_menu.tga", 2.f, 987.f, 100.f, 3.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f @@ -3630,10 +3630,10 @@ static sqshControl _controls_36[] = { { SQSH_GENERAL_WND_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_35[] = { { "group battle" +static BGObj _bgObjects_36[] = { { "group battle" , "battle" }, { "group back2", "back2" }, { "group next2", "next2" }, { "group map" , "map" }, { "group background", "background off" } }; -static sqshControl _controls_34[] = { { SQSH_SCALE_BUTTON_TYPE +static sqshControl _controls_35[] = { { SQSH_SCALE_BUTTON_TYPE , SQSH_MM_RAMKA, 2, 3, 0.55078125f, 0.58203125f, 0.02441406f, 0.09635417f, 0.f, 0.09635417f , { "resource\\icons\\MainMenu\\Main_menu.tga", 346.f, 522.f, 25.f, 74.f, 0.f, 0.f , 0.f, 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 10.f, 120.f, 0.f @@ -4369,10 +4369,10 @@ static sqshControl _controls_34[] = { { SQSH_SCALE_BUTTON_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_33[] = { { "group battle" +static BGObj _bgObjects_34[] = { { "group battle" , "battle" }, { "group back2", "back2" }, { "group next2", "next2" }, { "group map" , "map" }, { "group background", "background off" } }; -static sqshControl _controls_32[] = { { SQSH_SCALE_BUTTON_TYPE +static sqshControl _controls_33[] = { { SQSH_SCALE_BUTTON_TYPE , SQSH_MM_RAMKA, 2, 3, 0.18164062f, 0.12239583f, 0.38085938f, 0.67057292f, 0.f, 0.67057292f , { "resource\\icons\\MainMenu\\Main_menu.tga", 0.f, 0.f, 300.f, 515.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 60.f, 120.f, 0.f, 0.f @@ -4393,7 +4393,7 @@ static sqshControl _controls_32[] = { { SQSH_SCALE_BUTTON_TYPE , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" - , "", "", 333, -1, 0, 0, 0, 0, { "", "" }, "Name", 0.f, 0.00911458f, SHELL_ALIGN_RIGHT + , "", "", 339, -1, 0, 0, 0, 0, { "", "" }, "Name", 0.f, 0.00911458f, SHELL_ALIGN_RIGHT , SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -4430,7 +4430,7 @@ static sqshControl _controls_32[] = { { SQSH_SCALE_BUTTON_TYPE , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" - , "", "", 335, -1, 0, 0, 0, 0, { "", "" }, "Password", 0.f, 0.00911458f, SHELL_ALIGN_RIGHT + , "", "", 341, -1, 0, 0, 0, 0, { "", "" }, "Password", 0.f, 0.00911458f, SHELL_ALIGN_RIGHT , SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -4467,7 +4467,7 @@ static sqshControl _controls_32[] = { { SQSH_SCALE_BUTTON_TYPE , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" - , "", "", 334, -1, 0, 0, 0, 0, { "", "" }, "Port", 0.f, 0.00911458f, SHELL_ALIGN_RIGHT + , "", "", 340, -1, 0, 0, 0, 0, { "", "" }, "Port", 0.f, 0.00911458f, SHELL_ALIGN_RIGHT , SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -4504,7 +4504,7 @@ static sqshControl _controls_32[] = { { SQSH_SCALE_BUTTON_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 332, -1, 0, 0, 0, 0, { "", "" }, "SERVER TYPE", 0.f + }, "mainmenu_button", "", "", 338, -1, 0, 0, 0, 0, { "", "" }, "SERVER TYPE", 0.f , 0.00911458f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255 , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -4517,7 +4517,7 @@ static sqshControl _controls_32[] = { { SQSH_SCALE_BUTTON_TYPE , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" - , "", "", 331, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.01171875f, SHELL_ALIGN_LEFT + , "", "", 337, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.01171875f, SHELL_ALIGN_LEFT , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -4690,22 +4690,47 @@ static sqshControl _controls_32[] = { { SQSH_SCALE_BUTTON_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_31[] = { { "group back2" - , "back2" }, { "group skip", "skip" }, { "group next2", "next2" }, { "group text" - , "text" }, { "group background", "background off" } }; -static sqshControl _controls_30[] = { { SQSH_SCALE_BUTTON_TYPE - , SQSH_MM_RAMKA, 2, 3, 0.19238281f, 0.73567708f, 0.07324219f, 0.0546875f, 0.f, 0.08333333f - , { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f - , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f - , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f +static BGObj _bgObjects_32[] = { { "group battle" + , "battle" }, { "group back2", "back2" }, { "group skip", "skip" }, { "group next2" + , "next2" }, { "group map", "map" }, { "group background", "background off" } }; +static sqshControl _controls_31[] = { { SQSH_SCALE_BUTTON_TYPE + , SQSH_MM_RAMKA, 2, 3, 0.18164062f, 0.12239583f, 0.38085938f, 0.67057292f, 0.f, 0.67057292f + , { "resource\\icons\\MainMenu\\Main_menu.tga", 0.f, 0.f, 300.f, 515.f, 0.f, 0.f, 0.f + , 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 60.f, 120.f, 0.f, 0.f + , 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "" + , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", -1, -1, 0, 0 + , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 + , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } + , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE + , 0 }, { SQSH_SCALE_BUTTON_TYPE, SQSH_MM_RAMKA, 2, 3, 0.53222656f, 0.18489583f, 0.0234375f + , 0.59635417f, 0.f, 0.59635417f, { "resource\\icons\\MainMenu\\Main_menu.tga", 302.f + , 33.f, 24.f, 458.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\tv.avi" + , 0.f, 0.f, 10.f, 120.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, "button_press", "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" + , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" } }, 0, 0, 1, HITTEST_NONE, 0 }, { SQSH_SCALE_BUTTON_TYPE, SQSH_MM_RAMKA, 2 + , 3, 0.19238281f, 0.12760417f, 0.07324219f, 0.0546875f, 0.f, 0.08333333f, { "", 0.f , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press" - , "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" + , "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE - , 0 }, { SQSH_PUSH_BUTTON_TYPE, SQSH_MM_RAMKA, 2, 3, 0.19238281f, 0.73567708f, 0.07324219f + , 0 }, { SQSH_PUSH_BUTTON_TYPE, SQSH_MM_RAMKA, 2, 3, 0.19238281f, 0.12760417f, 0.07324219f , 0.0546875f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 1024.f, 1024.f , 0.33105469f, 0.20442708f, 0.125f, 0.16666667f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 @@ -4713,12 +4738,12 @@ static sqshControl _controls_30[] = { { SQSH_SCALE_BUTTON_TYPE , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" - , "", "", 319, -1, 0, 0, 0, 0, { "", "" }, "Name", 0.f, 0.00911458f, SHELL_ALIGN_CENTER + , "", "", 325, -1, 0, 0, 0, 0, { "", "" }, "Name", 0.f, 0.00911458f, SHELL_ALIGN_CENTER , SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 }, { SQSH_EDIT_BOX_TYPE, SQSH_MM_MULTIPLAYER_NAME_INPUT - , 2, 3, 0.27050781f, 0.73567708f, 0.1953125f, 0.04166667f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi" + , 2, 3, 0.265625f, 0.12760417f, 0.1953125f, 0.04166667f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi" , 0.f, 0.f, 1024.f, 1024.f, 0.33105469f, 0.20442708f, 0.125f, 0.16666667f, 0, 0 } , { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f @@ -4729,20 +4754,116 @@ static sqshControl _controls_30[] = { { SQSH_SCALE_BUTTON_TYPE , SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0 , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" - }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 0, HITTEST_DEFAULT, 0 }, { SQSH_SCALE_BUTTON_TYPE - , SQSH_MM_RAMKA, 3, 3, 0.76464844f, 0.93619792f, 0.11425781f, 0.04817708f, 0.f, 0.f - , { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f - , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f + }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 0, HITTEST_DEFAULT, 0 }, { SQSH_GENERAL_WND_TYPE + , SQSH_MM_RAMKA, 2, 3, 0.1953125f, 0.17447917f, 0.35351562f, 0.00390625f, 0.f, 0.f + , { "resource\\icons\\MainMenu\\Main_menu.tga", 2.f, 987.f, 272.f, 3.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.3f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 + , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 + , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 }, { SQSH_LIST_BOX_TYPE + , SQSH_MM_MULTIPLAYER_LIST_GAME_LIST, 3, 3, 0.18457031f, 0.18489583f, 0.37109375f + , 0.59635417f, 0.0234375f, 0.04557292f, { "resource\\icons\\MainMenu\\Main_menu.tga" + , 0.f, 992.f, 226.f, 26.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\Main_menu.tga" + , 328.f, 427.f, 24.f, 30.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\tv.avi" + , 0.f, 0.f, 24.f, 24.f, 0.33105469f, 0.20442708f, 0.125f, 0.16666667f, 0, 0 }, { "" + , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", -1, -1, 0, 0 + , 0, 0, { "", "" }, "", 0.01302083f, 0.00911458f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT + , 0 }, { SQSH_SCALE_BUTTON_TYPE, SQSH_MM_RAMKA, 3, 3, 0.63085938f, 0.07161458f, 0.25f + , 0.33333333f, 0.f, 0.33333333f, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f + , 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f + , 0.f, 1, 0 }, "button_press", "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f + , SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0 + , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" + , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE, 0 }, { SQSH_MAPWINDOW, SQSH_MM_MULTIPLAYER_LIST_MAP + , 3, 3, 0.63085938f, 0.07161458f, 0.25f, 0.33333333f, 0.f, 0.f, { "", 0.f, 0.f, 1.f + , 1.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f + , 120.f, 120.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f + , 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "" + , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press" , "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT + , 0 }, { SQSH_SCALE_BUTTON_TYPE, SQSH_MM_RAMKA, 3, 3, 0.61132812f, 0.40755208f, 0.29296875f + , 0.19140625f, 0.f, 0.38541667f, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f + , 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f + , 0.f, 1, 0 }, "button_press", "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f + , SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0 + , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" + , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE, 0 }, { SQSH_TEXT_WINDOW_TYPE + , SQSH_MM_MULTIPLAYER_LIST_MAP_DESCR_TXT, 3, 3, 0.61132812f, 0.40755208f, 0.29296875f + , 0.19140625f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 1024.f, 1024.f + , 0.33105469f, 0.20442708f, 0.125f, 0.16666667f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" + , "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.7f, 0.f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT + , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT + , 0 }, { SQSH_SCALE_BUTTON_TYPE, SQSH_MM_RAMKA, 3, 3, 0.58105469f, 0.73567708f, 0.21484375f + , 0.05859375f, 0.f, 0.05859375f, { "resource\\icons\\MainMenu\\new_menu.tga", 340.f + , 234.f, 194.f, 45.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f + , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", -1, -1 + , 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255 + , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE - , 0 }, { SQSH_PUSH_BUTTON_TYPE, SQSH_MM_MULTIPLAYER_LIST_CREATE_BTN, 2, 3, 0.76464844f - , 0.93619792f, 0.11425781f, 0.04817708f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi" + , 0 }, { SQSH_PUSH_BUTTON_TYPE, SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN, 2, 3, 0.58789062f + , 0.73828125f, 0.20117188f, 0.05338542f, 0.f, 0.f, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", -1, -1, 0, 0, 0, 0 + , { "", "" }, "JOIN", 81.f, 11.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 2, 255, 0 + , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } + , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT + , 0 }, { SQSH_SCALE_BUTTON_TYPE, SQSH_MM_RAMKA, 3, 3, 0.76464844f, 0.93619792f, 0.11425781f + , 0.04817708f, 0.f, 0.f, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f + , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, "button_press", "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER + , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" + , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" } }, 0, 0, 1, HITTEST_NONE, 0 }, { SQSH_PUSH_BUTTON_TYPE, SQSH_MM_MULTIPLAYER_LIST_CREATE_BTN + , 2, 3, 0.76464844f, 0.93619792f, 0.11425781f, 0.04817708f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi" , 0.f, 0.f, 1024.f, 1024.f, 0.33105469f, 0.20442708f, 0.125f, 0.16666667f, 0, 0 } , { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f @@ -4802,18 +4923,128 @@ static sqshControl _controls_30[] = { { SQSH_SCALE_BUTTON_TYPE , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 } }; -static BGObj _bgObjects_29[] = { { "group text" - , "text" }, { "group back2", "back2" }, { "group next2", "next2" }, { "group background" - , "background off" } }; -static sqshControl _controls_28[] = { { SQSH_SCALE_BUTTON_TYPE - , SQSH_MM_RAMKA, 2, 3, 0.16503906f, 0.6875f, 0.13476562f, 0.0546875f, 0.f, 0.08333333f +static sqshControl _controls_30[] = { { SQSH_MOVE_BUTTON_TYPE + , SQSH_MM_RAMKA, 2, 3, 0.33105469f, 0.f, 0.32226562f, 0.68619792f, 359.f, -768.f, { "resource\\icons\\MainMenu\\new_menu.tga" + , 0.f, 0.f, 330.f, 527.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" + , "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT + , 0 }, { SQSH_TEXT_WINDOW_TYPE, SQSH_MM_MULTIPLAYER_PASSWORD_NAME_TXT, 3, 3, 0.39550781f + , 0.20833333f, 0.20019531f, 0.0546875f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi" + , 0.f, 0.f, 1024.f, 1024.f, 0.33105469f, 0.20442708f, 0.125f, 0.16666667f, 0, 0 } , { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press" - , "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT + , 0 }, "button_press", "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.7f, 0.f, SHELL_ALIGN_CENTER + , SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" + , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 }, { SQSH_PUSH_BUTTON_TYPE, SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_BTN + , 2, 3, 0.390625f, 0.3125f, 0.20019531f, 0.0546875f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi" + , 0.f, 0.f, 1024.f, 1024.f, 0.33105469f, 0.20442708f, 0.125f, 0.16666667f, 0, 0 } + , { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f + , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, "mainmenu_button", "", "", 321, -1, 0, 0, 0, 0, { "", "" }, "Password", 0.f + , 0.00911458f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0 + , 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT, 0 }, { SQSH_SCALE_BUTTON_TYPE + , SQSH_MM_RAMKA, 3, 3, 0.390625f, 0.36458333f, 0.20019531f, 0.05859375f, 0.f, 0.05859375f + , { "resource\\icons\\MainMenu\\new_menu.tga", 340.f, 291.f, 145.f, 45.f, 0.f, 0.f + , 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "" + , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "" + , 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0, 0, 255, 255, 0, 0, 255 + , 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0 + , "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE, 0 }, { SQSH_EDIT_BOX_TYPE + , SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_INPUT, 2, 3, 0.39746094f, 0.3671875f, 0.18652344f + , 0.05338542f, 0.f, 0.f, { "resource\\icons\\MainMenu\\tv.avi", 0.f, 0.f, 1024.f, 1024.f + , 0.33105469f, 0.20442708f, 0.125f, 0.16666667f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" + , "", "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.01171875f, SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT + , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 0, HITTEST_DEFAULT + , 0 }, { SQSH_SCALE_BUTTON_TYPE, SQSH_STATIC_ID, 3, 3, 0.38183594f, 0.56119792f, 0.09375f + , 0.0546875f, 0.f, 0.0546875f, { "resource\\icons\\MainMenu\\new_menu.tga", 340.f + , 170.f, 96.f, 42.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f + , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", -1, -1 + , 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255 + , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE + , 0 }, { SQSH_PUSH_BUTTON_TYPE, SQSH_MM_BACK_FROM_MULTIPLAYER_PASSWORD_BTN, 2, 3, 0.38769531f + , 0.56380208f, 0.08203125f, 0.04947917f, 0.f, 0.f, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", -1, -1, 0, 0, 0, 0 + , { "", "" }, "BACK", 0.f, 0.01692708f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 2, 255 + , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT + , 0 }, { SQSH_SCALE_BUTTON_TYPE, SQSH_STATIC_ID, 3, 3, 0.50683594f, 0.56119792f, 0.09375f + , 0.0546875f, 0.f, 0.0546875f, { "resource\\icons\\MainMenu\\new_menu.tga", 340.f + , 170.f, 96.f, 42.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f + , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", -1, -1 + , 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255 + , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE + , 0 }, { SQSH_PUSH_BUTTON_TYPE, SQSH_MM_MULTIPLAYER_PASSWORD_NEXT_BTN, 2, 3, 0.51269531f + , 0.56380208f, 0.08203125f, 0.04947917f, 0.f, 0.f, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button", "", "", -1, -1, 0, 0, 0, 0 + , { "", "" }, "JOIN", 0.f, 0.01692708f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 2, 255 + , 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" + }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED + , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_DEFAULT + , 0 } }; +static BGObj _bgObjects_29[] = { { "group text", "text" }, { "group back2" + , "back2" }, { "group next2", "next2" }, { "group background", "background off" } + }; +static sqshControl _controls_28[] = { { SQSH_SCALE_BUTTON_TYPE, SQSH_MM_RAMKA + , 2, 3, 0.16503906f, 0.6875f, 0.13476562f, 0.0546875f, 0.f, 0.08333333f, { "", 0.f + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 + }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "" + , "", -1, -1, 0, 0, 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT , 1, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" } }, 0, 0, 1, HITTEST_NONE @@ -4825,7 +5056,7 @@ static sqshControl _controls_28[] = { { SQSH_SCALE_BUTTON_TYPE , 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, "mainmenu_button", "", "", 314, -1, 0, 0, 0, 0, { "", "" }, "IP", 0.f, 0.00911458f + , 0 }, "mainmenu_button", "", "", 315, -1, 0, 0, 0, 0, { "", "" }, "IP", 0.f, 0.00911458f , SHELL_ALIGN_LEFT, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0 , 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" @@ -4874,7 +5105,7 @@ static sqshControl _controls_28[] = { { SQSH_SCALE_BUTTON_TYPE , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "mainmenu_button" - , "", "", 316, -1, 0, 0, 0, 0, { "", "" }, "Password", 0.f, 0.00911458f, SHELL_ALIGN_LEFT + , "", "", 317, -1, 0, 0, 0, 0, { "", "" }, "Password", 0.f, 0.00911458f, SHELL_ALIGN_LEFT , SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "" , EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -7406,7 +7637,7 @@ static sqshControl _controls_9[] = { { SQSH_SCALE_BUTTON_TYPE , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 192, -1, 0, 0 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, "button_press", "", "", 193, -1, 0, 0 , 0, 0, { "", "" }, "", 0.f, 0.f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 1, 255, 0 , 0, 255, 255, 0, 0, 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" } , { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -7419,7 +7650,7 @@ static sqshControl _controls_9[] = { { SQSH_SCALE_BUTTON_TYPE , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 - }, "mainmenu_button", "", "", 191, -1, 0, 0, 0, 0, { "", "" }, "DIFFICULTY", 0.f + }, "mainmenu_button", "", "", 192, -1, 0, 0, 0, 0, { "", "" }, "DIFFICULTY", 0.f , 0.00911458f, SHELL_ALIGN_CENTER, SHELL_ALIGN_LEFT, 2, 255, 0, 0, 255, 255, 0, 0 , 255, 900.f, 0, 255, 0, 64, 6, { { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED , 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED, 0, "" }, { "", EVENT_PRESSED @@ -9944,7 +10175,7 @@ int startUp = 283; int startStep = 70; int optionsUp = 185; int optionsStep = 122; -int _sqsh_control_count = 41; +int _sqsh_control_count = 42; sqshControlContainer _sqsh_controls[] = { { SQSH_GENERAL_TYPE , 1, 65, 1, 0, 0, 1, 1, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 } , { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 135, _controls_2, 0 @@ -9971,21 +10202,21 @@ sqshControlContainer _sqsh_controls[] = { { SQSH_GENERAL_TYPE }, { SQSH_MULTITEX_WINDOW, 143, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, 39, _controls_15, 5, _bgObjects_16, SQSH_MM_BATTLE_GO_BTN, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_BATTLE_BTN - }, { SQSH_MULTITEX_WINDOW, 158, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f + }, { SQSH_MULTITEX_WINDOW, 159, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, 14, _controls_17, 5, _bgObjects_18, SQSH_MM_LOAD_GO_BTN, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_LOAD_BTN - }, { SQSH_MULTITEX_WINDOW, 163, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f + }, { SQSH_MULTITEX_WINDOW, 164, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 , 0 }, 14, _controls_19, 5, _bgObjects_20, SQSH_MM_LOAD_REPLAY_GO_BTN, SQSH_STATIC_ID - , SQSH_MM_BACK_FROM_LOAD_REPLAY_BTN }, { SQSH_MULTITEX_WINDOW, 159, 0, 1, 0, 0, 1024 + , SQSH_MM_BACK_FROM_LOAD_REPLAY_BTN }, { SQSH_MULTITEX_WINDOW, 160, 0, 1, 0, 0, 1024 , 768, { "resource\\icons\\MainMenu\\t.avi", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, 16, _controls_21, 0, 0, SQSH_MM_LOAD_IN_GAME_GO_BTN, SQSH_STATIC_ID - , SQSH_MM_BACK_FROM_LOAD_IN_GAME_BTN }, { SQSH_MULTITEX_WINDOW, 161, 0, 1, 0, 0, 1024 + , SQSH_MM_BACK_FROM_LOAD_IN_GAME_BTN }, { SQSH_MULTITEX_WINDOW, 162, 0, 1, 0, 0, 1024 , 768, { "resource\\icons\\MainMenu\\t.avi", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, 18, _controls_22, 0, 0, SQSH_MM_SAVE_REPLAY_GO_BTN, SQSH_STATIC_ID - , SQSH_MM_BACK_FROM_SAVE_REPLAY_BTN }, { SQSH_MULTITEX_WINDOW, 160, 0, 1, 0, 0, 1024 + , SQSH_MM_BACK_FROM_SAVE_REPLAY_BTN }, { SQSH_MULTITEX_WINDOW, 161, 0, 1, 0, 0, 1024 , 768, { "resource\\icons\\MainMenu\\t.avi", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 1, 0 }, 18, _controls_23, 0, 0, SQSH_MM_SAVE_GAME_GO_BTN, SQSH_STATIC_ID @@ -10008,79 +10239,83 @@ sqshControlContainer _sqsh_controls[] = { { SQSH_GENERAL_TYPE , 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "" , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 11, _controls_28, 4, _bgObjects_29 , SQSH_MM_MULTIPLAYER_JOIN_NEXT_BTN, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_MULTIPLAYER_JOIN_BTN - }, { SQSH_MULTITEX_WINDOW, 145, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f + }, { SQSH_MULTITEX_WINDOW, 147, 0, 2, 0, 0, 1024, 768, { "resource\\icons\\MainMenu\\t.avi" + , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga" + , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 9, _controls_30, 0, 0, SQSH_MM_MULTIPLAYER_PASSWORD_NEXT_BTN + , SQSH_STATIC_ID, SQSH_MM_BACK_FROM_MULTIPLAYER_PASSWORD_BTN }, { SQSH_MULTITEX_WINDOW + , 145, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 19, _controls_31 + , 6, _bgObjects_32, SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_MULTIPLAYER_LIST_BTN + }, { SQSH_MULTITEX_WINDOW, 155, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, 9, _controls_30, 5, _bgObjects_31, SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN, SQSH_MM_MULTIPLAYER_LIST_CREATE_BTN - , SQSH_MM_BACK_FROM_MULTIPLAYER_LIST_BTN }, { SQSH_MULTITEX_WINDOW, 154, 0, 2, 0, 0 + , 0 }, 26, _controls_33, 5, _bgObjects_34, SQSH_MM_MULTIPLAYER_HOST_NEXT_BTN, SQSH_STATIC_ID + , SQSH_MM_BACK_FROM_MULTIPLAYER_HOST_BTN }, { SQSH_MULTITEX_WINDOW, 148, 0, 2, 0, 0 , 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f - , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 26, _controls_32, 5, _bgObjects_33 - , SQSH_MM_MULTIPLAYER_HOST_NEXT_BTN, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_MULTIPLAYER_HOST_BTN - }, { SQSH_MULTITEX_WINDOW, 147, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f - , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, 61, _controls_34, 5, _bgObjects_35, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_LOBBY_BTN - }, { SQSH_MULTITEX_WINDOW, 148, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f - , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, 12, _controls_36, 3, _bgObjects_37, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_OPTIONS_BTN - }, { SQSH_MULTITEX_WINDOW, 152, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f - , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, 25, _controls_38, 3, _bgObjects_39, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_GAME_BTN - }, { SQSH_MULTITEX_WINDOW, 149, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f - , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, 33, _controls_40, 5, _bgObjects_41, SQSH_MM_APPLY_BTN, SQSH_MM_CUSTOM_GRAPHICS_BTN - , SQSH_MM_BACK_FROM_GRAPHICS_BTN }, { SQSH_MULTITEX_WINDOW, 150, 0, 2, 0, 0, 1024 + , 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 61, _controls_35, 5, _bgObjects_36 + , SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_LOBBY_BTN }, { SQSH_MULTITEX_WINDOW + , 149, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 12, _controls_37 + , 3, _bgObjects_38, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_OPTIONS_BTN + }, { SQSH_MULTITEX_WINDOW, 153, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, 25, _controls_39, 3, _bgObjects_40, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_GAME_BTN + }, { SQSH_MULTITEX_WINDOW, 150, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, 33, _controls_41, 5, _bgObjects_42, SQSH_MM_APPLY_BTN, SQSH_MM_CUSTOM_GRAPHICS_BTN + , SQSH_MM_BACK_FROM_GRAPHICS_BTN }, { SQSH_MULTITEX_WINDOW, 151, 0, 2, 0, 0, 1024 , 768, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f - , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 45, _controls_42, 3, _bgObjects_43, SQSH_STATIC_ID - , SQSH_STATIC_ID, SQSH_MM_BACK_FROM_CUSTOM_BTN }, { SQSH_MULTITEX_WINDOW, 153, 0, 2 + , 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 45, _controls_43, 3, _bgObjects_44, SQSH_STATIC_ID + , SQSH_STATIC_ID, SQSH_MM_BACK_FROM_CUSTOM_BTN }, { SQSH_MULTITEX_WINDOW, 154, 0, 2 , 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "" - , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 17, _controls_44, 3, _bgObjects_45 + , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 17, _controls_45, 3, _bgObjects_46 , SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_SOUND_BTN }, { SQSH_MULTITEX_WINDOW - , 156, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 15, _controls_46 - , 6, _bgObjects_47, SQSH_MM_ADDONS_ENABLE_BTN, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_ADDONS_BTN - }, { SQSH_MULTITEX_WINDOW, 157, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f + , 157, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 + , 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 15, _controls_47 + , 6, _bgObjects_48, SQSH_MM_ADDONS_ENABLE_BTN, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_ADDONS_BTN + }, { SQSH_MULTITEX_WINDOW, 158, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, 18, _controls_48, 3, _bgObjects_49, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_COMMUNITY_BTN - }, { SQSH_MULTITEX_WINDOW, 155, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f + , 0 }, 18, _controls_49, 3, _bgObjects_50, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_COMMUNITY_BTN + }, { SQSH_MULTITEX_WINDOW, 156, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, 8, _controls_50, 4, _bgObjects_51, SQSH_MM_CONTENT_CHOOSER_SELECT_BTN, SQSH_STATIC_ID - , SQSH_MM_BACK_FROM_CONTENT_CHOOSER_BTN }, { SQSH_MULTITEX_WINDOW, 162, 0, 3, 0, 0 + , 0 }, 8, _controls_51, 4, _bgObjects_52, SQSH_MM_CONTENT_CHOOSER_SELECT_BTN, SQSH_STATIC_ID + , SQSH_MM_BACK_FROM_CONTENT_CHOOSER_BTN }, { SQSH_MULTITEX_WINDOW, 163, 0, 3, 0, 0 , 1024, 768, { "resource\\icons\\MainMenu\\t.avi", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f , 0.f, 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga", 0.f, 0.f, 1024.f, 1024.f - , 0.f, 0.f, 0.f, 0.f, 1, 0 }, 8, _controls_52, 0, 0, SQSH_STATIC_ID, SQSH_MM_SUBMIT_YES_BTN - , SQSH_MM_SUBMIT_NO_BTN }, { SQSH_MULTITEX_WINDOW, 165, 0, 1, 0, 0, 1024, 768, { "resource\\icons\\MainMenu\\t.avi" + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, 8, _controls_53, 0, 0, SQSH_STATIC_ID, SQSH_MM_SUBMIT_YES_BTN + , SQSH_MM_SUBMIT_NO_BTN }, { SQSH_MULTITEX_WINDOW, 166, 0, 1, 0, 0, 1024, 768, { "resource\\icons\\MainMenu\\t.avi" , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga" - , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 9, _controls_53, 0, 0, SQSH_STATIC_ID - , SQSH_STATIC_ID, SQSH_MM_BACK_FROM_INGAME_OPTIONS }, { SQSH_MULTITEX_WINDOW, 166 + , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 9, _controls_54, 0, 0, SQSH_STATIC_ID + , SQSH_STATIC_ID, SQSH_MM_BACK_FROM_INGAME_OPTIONS }, { SQSH_MULTITEX_WINDOW, 167 , 0, 1, 0, 0, 1024, 768, { "resource\\icons\\MainMenu\\t.avi", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga", 0.f, 0.f, 1024.f - , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 18, _controls_54, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID - , SQSH_MM_BACK_FROM_INGAME_GAME_OPTIONS }, { SQSH_MULTITEX_WINDOW, 167, 0, 1, 0, 0 + , 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 18, _controls_55, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID + , SQSH_MM_BACK_FROM_INGAME_GAME_OPTIONS }, { SQSH_MULTITEX_WINDOW, 168, 0, 1, 0, 0 , 1024, 768, { "resource\\icons\\MainMenu\\t.avi", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f , 0.f, 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga", 0.f, 0.f, 1024.f, 1024.f - , 0.f, 0.f, 0.f, 0.f, 1, 0 }, 19, _controls_55, 0, 0, SQSH_MM_INGAME_APPLY_BTN, SQSH_MM_CUSTOM_INGAME_GRAPHICS_BTN - , SQSH_MM_BACK_FROM_INGAME_GRAPHICS_BTN }, { SQSH_MULTITEX_WINDOW, 151, 0, 1, 0, 0 + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, 19, _controls_56, 0, 0, SQSH_MM_INGAME_APPLY_BTN, SQSH_MM_CUSTOM_INGAME_GRAPHICS_BTN + , SQSH_MM_BACK_FROM_INGAME_GRAPHICS_BTN }, { SQSH_MULTITEX_WINDOW, 152, 0, 1, 0, 0 , 1024, 768, { "resource\\icons\\MainMenu\\t.avi", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f , 0.f, 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga", 0.f, 0.f, 1024.f, 1024.f - , 0.f, 0.f, 0.f, 0.f, 1, 0 }, 36, _controls_56, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID - , SQSH_MM_BACK_FROM_INGAME_CUSTOM }, { SQSH_MULTITEX_WINDOW, 168, 0, 1, 0, 0, 1024 + , 0.f, 0.f, 0.f, 0.f, 1, 0 }, 36, _controls_57, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID + , SQSH_MM_BACK_FROM_INGAME_CUSTOM }, { SQSH_MULTITEX_WINDOW, 169, 0, 1, 0, 0, 1024 , 768, { "resource\\icons\\MainMenu\\t.avi", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f , 0.f, 0, 0 }, { "resource\\icons\\MainMenu\\il.tga", 0.f, 0.f, 1024.f, 1024.f, 0.f - , 0.f, 0.f, 0.f, 1, 0 }, 16, _controls_57, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_INGAME_SOUND - }, { SQSH_MULTITEX_WINDOW, 164, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f + , 0.f, 0.f, 0.f, 1, 0 }, 16, _controls_58, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_FROM_INGAME_SOUND + }, { SQSH_MULTITEX_WINDOW, 165, 0, 2, 0, 0, 1024, 768, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1 - , 0 }, 5, _controls_58, 4, _bgObjects_59, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_CREDITS_BTN - }, { SQSH_SPLASHSCREEN, 485, 2, 2, 0, 0, 1, 1, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + , 0 }, 5, _controls_59, 4, _bgObjects_60, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_MM_BACK_CREDITS_BTN + }, { SQSH_SPLASHSCREEN, 491, 2, 2, 0, 0, 1, 1, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 0, 0, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_STATIC_ID }, { SQSH_SPLASHSCREEN - , 486, 2, 2, 0, 0, 1, 1, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 0, 0 + , 492, 2, 2, 0, 0, 1, 1, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 0, 0, 0, 0, SQSH_STATIC_ID - , SQSH_STATIC_ID, SQSH_STATIC_ID }, { SQSH_SPLASHSCREEN, 487, 2, 2, 0, 0, 1, 1, { "" + , SQSH_STATIC_ID, SQSH_STATIC_ID }, { SQSH_SPLASHSCREEN, 493, 2, 2, 0, 0, 1, 1, { "" , 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f , 0.f, 0.f, 0.f, 0.f, 1, 0 }, 0, 0, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_STATIC_ID - }, { SQSH_SPLASHSCREEN, 488, 2, 2, 0, 0, 1, 1, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f + }, { SQSH_SPLASHSCREEN, 494, 2, 2, 0, 0, 1, 1, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f , 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 0, 0, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_STATIC_ID }, { SQSH_SPLASHSCREEN - , 489, 2, 2, 0, 0, 1, 1, { "resource\\icons\\logo.tga", 0.f, 0.f, 1024.f, 768.f, 0.f + , 495, 2, 2, 0, 0, 1, 1, { "resource\\icons\\logo.tga", 0.f, 0.f, 1024.f, 768.f, 0.f , 0.f, 0.f, 0.f, 0, 0 }, { "", 0.f, 0.f, 1024.f, 1024.f, 0.f, 0.f, 0.f, 0.f, 1, 0 }, 0, 0, 0, 0, SQSH_STATIC_ID, SQSH_STATIC_ID, SQSH_STATIC_ID } }; int _sqsh_sheet_count = 2; @@ -10338,37 +10573,38 @@ struct InterfaceScriptExportPrm_ParameterSection : ParameterSection add_dependency(".\\Scripts\\InterfaceScriptExport.prm"); add_dependency(".\\Scripts\\SquadShellEnums.inl"); add_dependency(".\\Scripts\\UnitAttribute.inl"); - description = 2674885503; + description = 2482013865; reserve(37); - add(&_bgObjects_59, "_bgObjects_59", 2078950179); - add(&_controls_58, "_controls_58", 1864733084); - add(&_controls_57, "_controls_57", 3395934282); - add(&_controls_56, "_controls_56", 4062828620); - add(&_controls_55, "_controls_55", 977388503); - add(&_controls_54, "_controls_54", 1452085606); - add(&_controls_53, "_controls_53", 3622144752); - add(&_controls_52, "_controls_52", 992598089); - add(&_bgObjects_51, "_bgObjects_51", 4226433826); - add(&_controls_50, "_controls_50", 464115785); - add(&_bgObjects_49, "_bgObjects_49", 384450559); - add(&_controls_48, "_controls_48", 2244284775); - add(&_bgObjects_47, "_bgObjects_47", 1234994839); - add(&_controls_46, "_controls_46", 2026459832); - add(&_bgObjects_45, "_bgObjects_45", 3622711294); - add(&_controls_44, "_controls_44", 2624396191); - add(&_bgObjects_43, "_bgObjects_43", 3060283390); - add(&_controls_42, "_controls_42", 3589114832); - add(&_bgObjects_41, "_bgObjects_41", 2680611221); - add(&_controls_40, "_controls_40", 3178076569); - add(&_bgObjects_39, "_bgObjects_39", 1763033215); - add(&_controls_38, "_controls_38", 1788150686); - add(&_bgObjects_37, "_bgObjects_37", 2291390590); - add(&_controls_36, "_controls_36", 1603917897); - add(&_bgObjects_35, "_bgObjects_35", 557649451); - add(&_controls_34, "_controls_34", 2744451995); - add(&_bgObjects_33, "_bgObjects_33", 2308932611); - add(&_controls_32, "_controls_32", 3669823847); - add(&_bgObjects_31, "_bgObjects_31", 3761839722); + add(&_bgObjects_60, "_bgObjects_60", 1433555463); + add(&_controls_59, "_controls_59", 1063622840); + add(&_controls_58, "_controls_58", 1000986699); + add(&_controls_57, "_controls_57", 3798587468); + add(&_controls_56, "_controls_56", 3397507259); + add(&_controls_55, "_controls_55", 1187844454); + add(&_controls_54, "_controls_54", 1713738140); + add(&_controls_53, "_controls_53", 728356937); + add(&_bgObjects_52, "_bgObjects_52", 187324494); + add(&_controls_51, "_controls_51", 199874633); + add(&_bgObjects_50, "_bgObjects_50", 2497847166); + add(&_controls_49, "_controls_49", 2508525927); + add(&_bgObjects_48, "_bgObjects_48", 1415349991); + add(&_controls_47, "_controls_47", 680090524); + add(&_bgObjects_46, "_bgObjects_46", 3878495230); + add(&_controls_45, "_controls_45", 3425505979); + add(&_bgObjects_44, "_bgObjects_44", 3350146046); + add(&_controls_43, "_controls_43", 2242740980); + add(&_bgObjects_42, "_bgObjects_42", 3266524872); + add(&_controls_41, "_controls_41", 3979186365); + add(&_bgObjects_40, "_bgObjects_40", 2259368958); + add(&_controls_39, "_controls_39", 987040442); + add(&_bgObjects_38, "_bgObjects_38", 2035598463); + add(&_controls_37, "_controls_37", 1339676745); + add(&_bgObjects_36, "_bgObjects_36", 2085149046); + add(&_controls_35, "_controls_35", 4090825407); + add(&_bgObjects_34, "_bgObjects_34", 367936287); + add(&_controls_33, "_controls_33", 3405582695); + add(&_bgObjects_32, "_bgObjects_32", 3265562297); + add(&_controls_31, "_controls_31", 2532430806); add(&_controls_30, "_controls_30", 3398795677); add(&_bgObjects_29, "_bgObjects_29", 3423755042); add(&_controls_28, "_controls_28", 3170455792); @@ -10430,7 +10666,7 @@ struct InterfaceScriptExportPrm_ParameterSection : ParameterSection add(&optionsUp, "optionsUp", 1199352564); add(&optionsStep, "optionsStep", 2147965020); add(&_sqsh_control_count, "_sqsh_control_count", 1611044955); - add(&_sqsh_controls, "_sqsh_controls", 2188844968); + add(&_sqsh_controls, "_sqsh_controls", 2069222676); add(&_sqsh_sheet_count, "_sqsh_sheet_count", 878183638); add(&_sqsh_sheets, "_sqsh_sheets", 4086170788); add(&squad_icon_count, "squad_icon_count", 3142965791); diff --git a/Source/Game/Texts.cpp b/Source/Game/Texts.cpp index 3b697d4d0..aa76af63e 100644 --- a/Source/Game/Texts.cpp +++ b/Source/Game/Texts.cpp @@ -820,6 +820,13 @@ void qdTextDB::load_supplementary_texts(const std::string& locale) { "Interface.Menu.ComboItems.Classic=Классический", "Interface.Menu.Multiplayer.StartNewGame=Начать новую игру", "Interface.Menu.Multiplayer.Server=сервер", + "Interface.Tips.Multiplayer.HasPassword=Имеет пароль", + "Interface.Tips.Multiplayer.GameStarted=Игра уже началась", + "Interface.Tips.Multiplayer.Game=Игра", + "Interface.Tips.Multiplayer.Map=Карта", + "Interface.Tips.Multiplayer.CurrentPlayers=Текущие игроки", + "Interface.Tips.Multiplayer.MaxPlayers=Макс игроков", + "Interface.Tips.Multiplayer.Ping=Пинг", "Interface.Menu.Messages.WrongIPPort=Этот IP-адрес недоступен", "Interface.Menu.Messages.Multiplayer.IncorrectContent=Сервер содержит другие игровые ресурсы", "Interface.Menu.Messages.Multiplayer.IncorrectArch=Сервер имеет другую битность или архитектуру ЦПУ, другой тип билда (Debug/Release), операционную систему или использован другой компилятор (MSVC/Clang/GCC), пожалуйста, убедитесь, что они совпадают", @@ -887,6 +894,13 @@ void qdTextDB::load_supplementary_texts(const std::string& locale) { "Interface.Menu.ComboItems.Classic=Classic", "Interface.Menu.Multiplayer.StartNewGame=Start a new game", "Interface.Menu.Multiplayer.Server=Server", + "Interface.Tips.Multiplayer.HasPassword=Has password", + "Interface.Tips.Multiplayer.GameStarted=Game has already started", + "Interface.Tips.Multiplayer.Game=Game", + "Interface.Tips.Multiplayer.Map=Map", + "Interface.Tips.Multiplayer.CurrentPlayers=Current players", + "Interface.Tips.Multiplayer.MaxPlayers=Max players", + "Interface.Tips.Multiplayer.Ping=Ping", "Interface.Menu.Messages.WrongIPPort=IP port is wrong", "Interface.Menu.Messages.Multiplayer.IncorrectContent=Server has different game content", "Interface.Menu.Messages.Multiplayer.IncorrectArch=Server has different bits or CPU architecture, different build type (Debug/Release), Operating System or used a different compiler (MSVC/Clang/GCC), please ensure they match", diff --git a/Source/Network/CMakeLists.txt b/Source/Network/CMakeLists.txt index 061f413c6..ccb24e8f6 100644 --- a/Source/Network/CMakeLists.txt +++ b/Source/Network/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(Network STATIC P2P_interfaceAnyTh.cpp NetConnection.cpp NetConnectionHandler.cpp + NetRelay.cpp ) target_include_directories(Network PRIVATE diff --git a/Source/Network/CommonEvents.h b/Source/Network/CommonEvents.h index 4334fb6db..da117237e 100644 --- a/Source/Network/CommonEvents.h +++ b/Source/Network/CommonEvents.h @@ -66,8 +66,6 @@ enum terEventID NETCOM_ID_NEXT_QUANT, ////--- - NETCOMC_ID_JOIN_REQUEST, - NETCOM_4C_ID_JOIN_RESPONSE, NETCOM_4H_ID_REJOIN_REQUEST, NETCOM_4C_ID_REJOIN_RESPONCE, @@ -739,7 +737,7 @@ struct netCommand4C_ReJoinResponse : netCommandGeneral { NETID playerNETID_; NETID groupNETID_; - netCommand4C_JoinResponse(NETID playerNETID, NETID groupNETID) : netCommandGeneral(NETCOM_4C_ID_REJOIN_RESPONSE) { + netCommand4C_ReJoinResponse(NETID playerNETID, NETID groupNETID) : netCommandGeneral(NETCOM_4C_ID_REJOIN_RESPONSE) { playerNETID_=playerNETID; groupNETID_=groupNETID; } diff --git a/Source/Network/HyperSpace.cpp b/Source/Network/HyperSpace.cpp index fd6f7741a..cb92973a3 100644 --- a/Source/Network/HyperSpace.cpp +++ b/Source/Network/HyperSpace.cpp @@ -222,36 +222,53 @@ void terHyperSpace::deserializeGameCommands(XBuffer& in, size_t len) { xassert(len >= sizeof(endQuant_inReplayListGameCommands)); len -= in.read(&endQuant_inReplayListGameCommands, sizeof(endQuant_inReplayListGameCommands)); - InOutNetComBuffer in_buffer(len, true); //проверить необходимость автоувелечения! - in_buffer.putBufferPacket(in.address()+in.tell(), len); + InOutNetComBuffer in_buffer(len, false); + char* data_ptr = in.address() + in.tell(); + uint32_t packet_id = *(reinterpret_cast(data_ptr)); + if (packet_id != NETCOM_BUFFER_PACKET_ID) { + in_buffer.automatic_realloc = true; + event_size_t event_len = 0; + size_t event_start = 0; + while (in.tell() + sizeof(event_len) < in.length()) { + event_start = in.tell(); + in.read(&event_len, sizeof(event_len)); + if (event_len == 0 || (in.tell() + event_len) > in.length()) { + break; + } + in_buffer.write(&NETCOM_BUFFER_PACKET_ID, sizeof(NETCOM_BUFFER_PACKET_ID)); + in_buffer.write(&event_len, sizeof(event_len)); + in_buffer.write(data_ptr + event_start, event_len); + in.set(event_start + event_len); + } + in_buffer.filled_size = in_buffer.tell(); + in_buffer.set(0); + } else { + in_buffer.putBufferPacket(data_ptr, len); + } - while(in_buffer.currentNetCommandID()!=NETCOM_ID_NONE) { + while (in_buffer.nextNetCommand() != NETCOM_ID_NONE) { terEventID event = (terEventID)in_buffer.currentNetCommandID(); switch(event){ - case NETCOM_4G_ID_UNIT_COMMAND: - { + case NETCOM_4G_ID_UNIT_COMMAND: { netCommand4G_UnitCommand* pnc= new netCommand4G_UnitCommand(in_buffer); replayListGameCommands.push_back(pnc); - } break; - case NETCOM_4G_ID_REGION: - { + } + case NETCOM_4G_ID_REGION: { netCommand4G_Region* pnc= new netCommand4G_Region(in_buffer); replayListGameCommands.push_back(pnc); - } break; - case NETCOM_4G_ID_FORCED_DEFEAT: - { + } + case NETCOM_4G_ID_FORCED_DEFEAT: { netCommand4G_ForcedDefeat* pnc=new netCommand4G_ForcedDefeat(in_buffer); replayListGameCommands.push_back(pnc); break; } default: - xassert(0&&"Incorrect commanf in playReel file!"); + xassert(0&&"Incorrect command in playReel file!"); break; } - in_buffer.nextNetCommand(); } } diff --git a/Source/Network/NetComEventBuffer.cpp b/Source/Network/NetComEventBuffer.cpp index 228b9ddf3..106eae63a 100644 --- a/Source/Network/NetComEventBuffer.cpp +++ b/Source/Network/NetComEventBuffer.cpp @@ -52,12 +52,11 @@ void InOutNetComBuffer::putNetCommand(const netCommandGeneral* event) { clearBufferOfTheProcessedCommands(); set(filled_size); - unsigned int pointer_to_size_of_event; - //unsigned int size_of_event; - pointer_to_size_of_event = offset; - size_of_event=0; - event_ID=event->EventID; + write(&NETCOM_BUFFER_PACKET_ID, sizeof(NETCOM_BUFFER_PACKET_ID)); + unsigned int pointer_to_size_of_event = offset; + event_size_t size_of_event = 0; write(&size_of_event, sizeof(size_of_event)); + event_ID=event->EventID; write(&event_ID, sizeof(event_ID)); event->Write(*this); @@ -71,12 +70,10 @@ void InOutNetComBuffer::putNetCommand(const netCommandGeneral* event) set(0); //для нормального next event event_ID = NETCOM_ID_NONE; - size_of_event=0; } //in -void InOutNetComBuffer::clearBufferOfTheProcessedCommands(void) -{ +void InOutNetComBuffer::clearBufferOfTheProcessedCommands() { if(next_event_pointer){ if(filled_size != next_event_pointer) memmove(address(),address() + next_event_pointer, filled_size - next_event_pointer); @@ -87,16 +84,28 @@ void InOutNetComBuffer::clearBufferOfTheProcessedCommands(void) bool InOutNetComBuffer::putBufferPacket(char* buf, unsigned int size) { + if (size < SIZE_HEAD_PACKET) { + fprintf(stderr, "Buffer packet is too small\n"); + xassert(0); + return false; + } + uint32_t packet_id = *(reinterpret_cast(buf)); + if (packet_id != NETCOM_BUFFER_PACKET_ID) { + fprintf(stderr, "Buffer packet is not a netcom buffer\n"); + xassert(0); + return false; + } clearBufferOfTheProcessedCommands(); if(length()-filled_size < size) { - xassert(0 && "Net input buffer is small."); - return 0; + fprintf(stderr, "Net input buffer is small\n"); + xassert(0); + return false; } memcpy(address() + filled_size, buf, size); byte_receive+=size; filled_size +=size; //nextNetCommand(); - return 1; + return true; } int InOutNetComBuffer::currentNetCommandID() @@ -110,8 +119,8 @@ int InOutNetComBuffer::currentNetCommandID() terEventID InOutNetComBuffer::nextNetCommand() { - if(event_ID != NETCOM_ID_NONE){ - if(next_event_pointer /*+ sizeof(size_of_event)*/ > filled_size){ + if (event_ID != NETCOM_ID_NONE) { + if(next_event_pointer > filled_size){ xassert(0&&"Incomplete packet ?!"); init(); reset(); @@ -130,9 +139,15 @@ terEventID InOutNetComBuffer::nextNetCommand() event_ID = NETCOM_ID_NONE; - if(filled_size-tell() > (sizeof(size_of_event) + sizeof(event_ID)) ) { - read(&size_of_event, sizeof(size_of_event)); //get_short(); - unsigned int new_pointer = next_event_pointer + size_of_event + sizeof(size_of_event); + if (filled_size-tell() > SIZE_HEAD_PACKET) { + uint32_t packet_id = 0; + read(&packet_id, sizeof(NETCOM_BUFFER_PACKET_ID)); + if (packet_id != NETCOM_BUFFER_PACKET_ID) { + ErrH.Abort("Couldn't match packet header ID, wrong data in input buffer?"); + } + event_size_t size_of_event = 0; + read(&size_of_event, sizeof(size_of_event)); + unsigned int new_pointer = next_event_pointer + size_of_event + sizeof(size_of_event) + sizeof(packet_id); if(new_pointer > filled_size){ xassert(0&&"Incomplete packet ?!"); set(next_event_pointer); @@ -156,7 +171,6 @@ void InOutNetComBuffer::ignoreNetCommand() void InOutNetComBuffer::backNetCommand() { - const unsigned int SIZE_HEAD_PACKET=sizeof(event_ID)+sizeof(size_of_event); if(offset>=SIZE_HEAD_PACKET){ event_ID = NETCOM_ID_NONE; offset-=SIZE_HEAD_PACKET; diff --git a/Source/Network/NetComEventBuffer.h b/Source/Network/NetComEventBuffer.h index cc4c09354..3e18d919b 100644 --- a/Source/Network/NetComEventBuffer.h +++ b/Source/Network/NetComEventBuffer.h @@ -4,6 +4,8 @@ #include "CommonEvents.h" typedef uint32_t event_size_t; +const uint32_t NETCOM_BUFFER_PACKET_ID = 0xB1FFE90D; +const unsigned int SIZE_HEAD_PACKET = sizeof(NETCOM_BUFFER_PACKET_ID) + sizeof(event_size_t) + sizeof(terEventID); class PNetCenter; class InOutNetComBuffer : public XBuffer @@ -14,7 +16,6 @@ class InOutNetComBuffer : public XBuffer size_t byte_sending;//out terEventID event_ID;//in - event_size_t size_of_event;//in size_t next_event_pointer;//in size_t filled_size;//in InOutNetComBuffer(unsigned int size, bool autoRealloc); diff --git a/Source/Network/NetConnection.cpp b/Source/Network/NetConnection.cpp index e5641eb02..f524c6851 100644 --- a/Source/Network/NetConnection.cpp +++ b/Source/Network/NetConnection.cpp @@ -6,11 +6,13 @@ ///////// NetAddress ////////////// -NetAddress::NetAddress(): addr() { - addr.host = INADDR_NONE; - addr.port = 0; +NetAddress::NetAddress(uint32_t host, uint16_t port): addr() { + addr.host = host; + setPort(port); } +NetAddress::NetAddress(): NetAddress(INADDR_NONE, 0) {} + NetAddress::~NetAddress() = default; uint32_t NetAddress::crc() const { @@ -20,12 +22,11 @@ uint32_t NetAddress::crc() const { } uint16_t NetAddress::port() const { -#if SDL_DATA_ALIGNED - //In SDL2_net When SDL_DATA_ALIGNED is set it uses SDLNet_Read16 without const, probably a mistake - return SDLNet_Read16(const_cast(&addr.port)); -#else return SDLNet_Read16(&addr.port); -#endif +} + +void NetAddress::setPort(uint16_t port) { + SDLNet_Write16(port, &addr.port); } std::string NetAddress::getString() const { @@ -45,7 +46,7 @@ std::string NetAddress::getString() const { std::string NetAddress::getStringIP() const { std::string text; - for (int i = 0; i < 4; ++i) { + for (size_t i = 0; i < 4; ++i) { if (i > 0) text += "."; uint8_t v = (addr.host >> (8 * i)) & 0xff; text += std::to_string(v); @@ -53,21 +54,24 @@ std::string NetAddress::getStringIP() const { return text; } -bool NetAddress::resolve(NetAddress& address, const std::string& host) { +bool NetAddress::resolve(NetAddress& address, const std::string& host, uint16_t default_port) { std::string ip; uint16_t port; + if (default_port == 0) { + default_port = PERIMETER_IP_PORT_DEFAULT; + } size_t pos = host.find(':'); if (pos == std::string::npos) { ip = host; - port = PERIMETER_IP_PORT_DEFAULT; + port = default_port; } else { ip = host.substr(0, pos); std::string port_str = host.substr(pos + 1); char* end; port = static_cast(strtol(port_str.c_str(), &end, 10)); - if (!port) port = PERIMETER_IP_PORT_DEFAULT; + if (!port) port = default_port; } - int ret = SDLNet_ResolveHost(&address.addr, ip.c_str(), port) == 0; + int32_t ret = SDLNet_ResolveHost(&address.addr, ip.c_str(), port) == 0; if (ret < 0 || address.addr.host == INADDR_NONE) { fprintf(stderr, "Error resolving host %s: %s\n", host.c_str(), SDLNet_GetError()); return false; @@ -75,33 +79,173 @@ bool NetAddress::resolve(NetAddress& address, const std::string& host) { return true; } +TCPsocket NetAddress::openTCP() const { + TCPsocket socket = SDLNet_TCP_Open(const_cast(&addr)); + if (socket == nullptr) { + fprintf(stderr, "NetAddress::openTCP failed address %s error %s\n", getString().c_str(), SDLNet_GetError()); + } + return socket; +} + +///////// NetTransport ////////////// + +int32_t NetTransport::send(const void* buffer, uint32_t len, int32_t timeout) { + if (is_closed()) { + return NT_STATUS_CLOSED; + } + if (buffer == nullptr) { + ErrH.Abort("NetTransport::send got null buffer"); + } + + int32_t sent = 0; + int32_t start_time = clocki(); + while (sent < len) { + if (0 < timeout && start_time + timeout < clocki()) { + return NT_STATUS_TIMEOUT; + } + int32_t amount = send_raw(static_cast(buffer) + sent, static_cast(len) - sent, timeout); + if (amount < 0) { + if (amount != NT_STATUS_TIMEOUT) { + fprintf(stderr, "NetTransport::send data failed result %d sent %d len %d\n", amount, sent, len); + } + return amount; + } + sent += amount; + } + + return sent; +} + +int32_t NetTransport::receive(void* buffer, uint32_t minlen, uint32_t maxlen, int32_t timeout) { + if (is_closed()) { + return NT_STATUS_CLOSED; + } + if (buffer == nullptr) { + ErrH.Abort("NetTransport::receive got null buffer"); + } + + int32_t received = 0; + int32_t start_time = clocki(); + bool has_timeout = 0 < timeout; + while (received < maxlen + && (0 == minlen || received < minlen)) { + if (has_timeout && start_time + timeout < clocki()) { + return NT_STATUS_TIMEOUT; + } + int32_t amount = receive_raw(static_cast(buffer) + received, static_cast(maxlen) - received, timeout); + if (has_timeout && amount == NT_STATUS_NO_DATA) { + //Keep waiting + continue; + } + if (amount < 0) { + if (amount != NT_STATUS_TIMEOUT && amount != NT_STATUS_NO_DATA) { + fprintf(stderr, "NetTransport::receive failed amount %" PRIi32 + " minlen %" PRIu32 " maxlen %" PRIu32 "\n", amount, minlen, maxlen); + close(); + } + return amount; + } + received += amount; + } + if (received < minlen || maxlen < received) { + fprintf(stderr, "NetTransport::receive length mismatch received %" PRIi32 + " minlen %" PRIu32 " maxlen %" PRIu32 "\n", received, minlen, maxlen); + close(); + return NT_STATUS_ERROR; + } + + return received; +} + +///////// NetTransportTCP ////////////// + +NetTransportTCP::NetTransportTCP(TCPsocket socket_) { + socket = socket_; + socket_set = SDLNet_AllocSocketSet(1); + SDLNet_TCP_AddSocket(socket_set, socket); +} + +void NetTransportTCP::close() { + if (socket_set) { + if (socket) { + SDLNet_TCP_DelSocket(socket_set, socket); + } + SDLNet_FreeSocketSet(socket_set); + socket_set = nullptr; + } + if (socket) { + SDLNet_TCP_Close(socket); + socket = nullptr; + } +} + +int32_t NetTransportTCP::send_raw(const uint8_t* buffer, uint32_t len, int32_t _timeout) { + //May return 0 if closed + int32_t amount = SDLNet_TCP_Send(socket, buffer, static_cast(len)); + if (amount == 0) { + return NT_STATUS_CLOSED; + } else if (amount <= 0) { + fprintf(stderr, "NetTransportTCP::send data failed result %" PRIi32 " len %" PRIu32 " %s\n", amount, len, SDLNet_GetError()); + return NT_STATUS_ERROR; + } + return amount; +} + +int32_t NetTransportTCP::receive_raw(uint8_t* buffer, uint32_t len, int32_t _timeout) { + int32_t n = SDLNet_CheckSockets(socket_set, 0); + if (n == -1) { + fprintf(stderr, "CheckSockets error: %s\n", SDLNet_GetError()); + // most of the time this is a system error, where perror might help you. + perror("SDLNet_CheckSockets"); + } else if (n == 0) { + return NT_STATUS_NO_DATA; + } + if (SDLNet_SocketReady(socket) == 0) { + return NT_STATUS_NO_DATA; + } + + //May return 0 if closed + int32_t amount = SDLNet_TCP_Recv(socket, buffer, static_cast(len)); + if (amount == 0) { + return NT_STATUS_CLOSED; + } else if (amount <= 0) { + fprintf(stderr, "NetTransportTCP::receive failed amount %" PRIi32 " len %" PRIu32 " %s\n", amount, len, SDLNet_GetError()); + return NT_STATUS_ERROR; + } + return amount; +} + ///////// NetConnection ////////////// const uint64_t NC_HEADER_MAGIC = 0xDE000000000000CA; const uint64_t NC_HEADER_MASK = 0xFF000000000000FF; -NetConnection::NetConnection(TCPsocket socket) { - set_socket(socket); +NetConnection::NetConnection(NetTransport* _transport, NETID _netid) { + set_transport(_transport, _netid); } NetConnection::~NetConnection() { close(); } -void NetConnection::set_socket(TCPsocket socket_) { +void NetConnection::set_transport(NetTransport* _transport, NETID _netid) { + //Call close to remove any existing transport and reset connectionstate close(); - state = NC_STATE_SETUP; - socket_set = SDLNet_AllocSocketSet(1); - socket = socket_; - SDLNet_TCP_AddSocket(socket_set, socket); + + //Set new transport + netid = _netid; + transport = _transport; + if (hasTransport()) { + state = NC_STATE_HAS_TRANSPORT; + } } void NetConnection::close(bool error) { switch (state) { - case NC_STATE_SETUP: + case NC_STATE_HAS_TRANSPORT: state = error ? NC_STATE_ERROR : NC_STATE_CLOSED; break; - case NC_STATE_ACTIVE: + case NC_STATE_HAS_CLIENT: state = error ? NC_STATE_ERROR_PENDING : NC_STATE_CLOSE_PENDING; break; case NC_STATE_ERROR: @@ -114,204 +258,189 @@ void NetConnection::close(bool error) { case NC_STATE_CLOSED: break; } - if (socket) { - //printf("TCP close 0x%" PRIX64 "\n", id); - SDLNet_TCP_DelSocket(socket_set, socket); - SDLNet_TCP_Close(socket); - socket = nullptr; - } - if (socket_set) { - SDLNet_FreeSocketSet(socket_set); - socket_set = nullptr; + if (transport) { + transport->close(); + delete transport; + transport = nullptr; } } -int NetConnection::send_raw(const void* buffer, uint32_t len) { - if (!has_socket()) { +int32_t NetConnection::send(const XBuffer* data, NETID source, NETID destination, int32_t timeout) { + if (!hasTransport()) { return -1; } - if (buffer == nullptr) { - ErrH.Abort("Got null buffer in send_raw"); + if (data == nullptr) { + fprintf(stderr, "NetConnection::send NETID %" PRIX64 " null buffer\n", netid); + ErrH.Abort("Got null buffer in send"); } - - int sent = 0; - while (sent < len) { - int amount = SDLNet_TCP_Send(socket, static_cast(buffer) + sent, static_cast(len) - sent); - if (amount < 0) { - fprintf(stderr, "TCP send data failed result %d sent %d len %d %s\n", amount, sent, len, SDLNet_GetError()); - return -3; - } - sent += amount; + if (data->tell() == 0) { + xassert(0); + fprintf(stderr, "NetConnection::send NETID %" PRIX64 " data to sent is empty!\n", netid); + return -2; } - - return sent; -} - -int NetConnection::send(const XBuffer& data) { - return send(reinterpret_cast(data.buf), data.length()); -} - -int NetConnection::send(const uint8_t* buffer, uint32_t len) { - if (buffer == nullptr) { - ErrH.Abort("Got null buffer in send"); + if (destination == NETID_NONE) { + destination = this->netid; } + xassert(destination != NETID_NONE); + //Pinky promise that sending_buffer won't modify the data ptr + XBuffer sending_buffer(const_cast(data->address()), data->tell()); + sending_buffer.set(data->tell()); uint16_t flags = 0; - XBuffer sending_buffer(const_cast(buffer), len); - sending_buffer.set(len); //Compression, first thing to do to calculate actual length - if (len > PERIMETER_MESSAGE_COMPRESSION_SIZE) { - XBuffer compress_buffer(len, true); - if (sending_buffer.compress(compress_buffer) == 0) { + if (sending_buffer.tell() > PERIMETER_MESSAGE_COMPRESSION_SIZE) { + XBuffer compress_buffer(sending_buffer.tell(), true); + if (sending_buffer.compress(compress_buffer) == 0 + && sending_buffer.tell() > compress_buffer.tell()) { sending_buffer = compress_buffer; - len = sending_buffer.tell(); flags |= PERIMETER_MESSAGE_FLAG_COMPRESSED; } } + + //Header size, not accounted in length that goes inside + uint32_t header_len = sizeof(NC_HEADER_MAGIC); + + //Body size, the length of message + + //Source + destination NETID info + uint32_t body_len = sending_buffer.tell() + sizeof(NETID) * 2; //Calculate message size - int msg_size = static_cast(len + sizeof(NC_HEADER_MAGIC)); + int32_t msg_size = static_cast(body_len + header_len); if (msg_size > PERIMETER_MESSAGE_MAX_SIZE) { xassert(0); - fprintf(stderr, "TCP send data too big len %d\n", msg_size); + fprintf(stderr, "NetConnection::send NETID %" PRIX64 " data too big len %d\n", netid, msg_size); return -2; } //Assemble header and data + uint64_t header = NC_HEADER_MAGIC; + header |= (static_cast(flags & 0xFFFF) << 8); + header |= (static_cast(body_len & 0xFFFFFFFF) << 24); XBuffer xbuf(msg_size); - xbuf < NC_HEADER_MAGIC; - xbuf.set(1); - xbuf < flags; - xbuf.set(3); - xbuf < len; - xbuf.set(8); - xbuf.write(sending_buffer, len); - - int sent = send_raw(xbuf.buf, msg_size); - - if (sent != msg_size) { - fprintf(stderr, "TCP send length mismatch sent %d msg %d len %d %s\n", sent, msg_size, len, SDLNet_GetError()); + xbuf < SDL_SwapBE64(header); + xbuf < SDL_SwapBE64(source); + xbuf < SDL_SwapBE64(destination); + xbuf.write(sending_buffer, sending_buffer.tell()); + +#ifdef PERIMETER_DEBUG + if (xbuf.tell() != msg_size) { + fprintf(stderr, "NetConnection::send NETID %" PRIX64 " written buffer mismatch buf %" PRIsize " msg %" PRIi32 " len %" PRIsize " %s\n", + netid, xbuf.tell(), msg_size, sending_buffer.tell(), SDLNet_GetError()); close_error(); return -4; } - - return sent; -} - -int NetConnection::receive_raw(void* buffer, uint32_t maxlen, int timeout) { - //Internal receive - if (!has_socket()) { - return -1; - } - if (buffer == nullptr) { - ErrH.Abort("Got null buffer in receive_raw"); - } - - if (0 <= timeout) { - int n = SDLNet_CheckSockets(socket_set, timeout); - if (n == -1) { - fprintf(stderr, "CheckSockets error: %s\n", SDLNet_GetError()); - // most of the time this is a system error, where perror might help you. - perror("SDLNet_CheckSockets"); - } else if (n == 0) { - return 0; - } - - /* - if (!SDLNet_SocketReady(socket)) { - return 0; - } - */ - } +#endif + int32_t sent = transport->send(xbuf.buf, xbuf.tell(), timeout); - int received = 0; - while (received < maxlen) { - int amount = SDLNet_TCP_Recv(socket, static_cast(buffer) + received, static_cast(maxlen) - received); - if (amount <= 0) { - fprintf(stderr, "TCP recv failed amount %d maxlen %d %s\n", amount, maxlen, SDLNet_GetError()); - close_error(); - return -2; - } - received += amount; - } - if (received != maxlen) { - fprintf(stderr, "TCP recv length mismatch received %d maxlen %d %s\n", received, maxlen, SDLNet_GetError()); + if (sent != msg_size) { + fprintf(stderr, "NetConnection::send NETID %" PRIX64 " length mismatch sent %" PRIi32 " msg %" PRIi32 " len %" PRIsize " %s\n", + netid, sent, msg_size, sending_buffer.tell(), SDLNet_GetError()); close_error(); return -4; } - return received; + return sent; } -int NetConnection::receive(XBuffer& buffer, int timeout) { - if (!has_socket()) { +int32_t NetConnection::receive(NetConnectionMessage** packet_ptr, int32_t timeout) { + if (!hasTransport()) { return -1; } //Get header first uint64_t header; - int amount = receive_raw(&header, sizeof(header), timeout); - if (amount <= 0) { + int32_t amount = transport->receive(&header, 0, sizeof(header), timeout); + if (amount == NetTransport::NT_STATUS_NO_DATA) { + return 0; + } + if (amount < 0) { + close_error(); return amount; } if (amount != sizeof(header)) { - fprintf(stderr, "TCP recv header failed amount %d %s\n", amount, SDLNet_GetError()); + fprintf(stderr, "NetConnection::receive NETID %" PRIX64 " header failed amount %d %s\n", netid, amount, SDLNet_GetError()); return -2; } + header = SDL_SwapBE64(header); //Check magic if ((header & NC_HEADER_MASK) != NC_HEADER_MAGIC) { - fprintf(stderr, "TCP recv header failed magic mismatch 0x%" PRIX64 " %s\n", header, SDLNet_GetError()); + fprintf(stderr, "NetConnection::receive NETID %" PRIX64 " header failed magic mismatch 0x%" PRIX64 " %s\n", netid, header, SDLNet_GetError()); return -2; } //Extract header stuff uint16_t flags = (header >> 8) & 0xFFFF; - uint32_t data_size = (header >> 24) & 0xFFFFFFFF; + amount = static_cast((header >> 24) & 0xFFFFFFFF); //Ensure is not too big - if ((data_size + sizeof(NC_HEADER_MAGIC)) >= PERIMETER_MESSAGE_MAX_SIZE) { + if (amount >= PERIMETER_MESSAGE_MAX_SIZE) { xassert(0); - fprintf(stderr, "TCP recv header failed too long 0x%" PRIX64 " len %" PRIu32 "\n", header, data_size); + fprintf(stderr, "NetConnection::receive NETID %" PRIX64 " header failed too long 0x%" PRIX64 " len %" PRIu32 "\n", netid, header, amount); return -2; } - //(re)allocate it to fit our data - buffer.alloc(data_size); - //Read data until all is received - amount = 0; - while (amount < data_size) { - uint32_t left = data_size - amount; - int received = receive_raw(buffer.buf + buffer.tell(), left, std::max(timeout, 0) + RECV_DATA_AFTER_HEADER_TIMEOUT); - if (received == 0) { - fprintf(stderr, "TCP recv data chunk failed left %d amount %d size %d %s\n", left, amount, data_size, SDLNet_GetError()); - amount = -5; - break; - } - buffer.set(received, XB_CUR); - amount += received; + NetConnectionMessage* packet = *packet_ptr; + if (packet == nullptr) { + //Create new packet + packet = new NetConnectionMessage(amount, this->netid, NETID_NONE); + } else { + //(re)allocate it to fit our data + packet->alloc(amount); } - - if (amount != data_size) { - fprintf(stderr, "TCP recv data failed amount %d size %d %s\n", amount, data_size, SDLNet_GetError()); - if (amount > 0) { - amount = -6; - } + int32_t received = transport->receive( + packet->address() + packet->tell(), amount, amount, + std::max(timeout, 0) + RECV_DATA_AFTER_HEADER_TIMEOUT + ); + if (received <= 0) { + fprintf(stderr, "NetConnection::receive NETID %" PRIX64 " data chunk failed amount %d received %d %s\n", netid, amount, received, SDLNet_GetError()); + amount = -5; + } else if (amount != received) { + fprintf(stderr, "NetConnection::receive NETID %" PRIX64 " data failed amount %d received %d %s\n", netid, amount, received, SDLNet_GetError()); + amount = -6; + } + + //Extract source and destination netids that is prepended before actual message data + //If amount goes negative or 0 is fine because it would mark this packet as invalid + amount -= sizeof(NETID) * 2; + if (amount < 0) { + xassert(0); + fprintf(stderr, "NetConnection::receive NETID %" PRIX64 " header without message 0x%" PRIX64 " len %" PRIu32 "\n", netid, header, amount); + amount = -7; + } else if (amount == 0) { + fprintf(stderr, "NetConnection::receive NETID %" PRIX64 " message is empty 0x%" PRIX64 "\n", netid, header); + } else { + *packet > packet->source; + *packet > packet->destination; + packet->source = SDL_SwapBE64(packet->source); + packet->destination = SDL_SwapBE64(packet->destination); } //Decompression if (0 < amount && flags & PERIMETER_MESSAGE_FLAG_COMPRESSED) { - buffer.set(0); - XBuffer output(data_size, true); - int ret = buffer.uncompress(output); + XBuffer output(amount, true); + int32_t ret = packet->uncompress(output); if (ret != 0) { - return -7; + amount = -9; + } else { + amount = static_cast(output.tell()); + std::swap(*static_cast(packet), output); + } + } else { + //Move message content that is after source/destination etc to start + memmove(packet->address(), packet->address() + packet->tell(), amount); + packet->set(amount); + } + + //Set packet ptr or delete if we created the packet in this function + if (*packet_ptr == nullptr) { + if (0 < amount) { + *packet_ptr = packet; + } else { + delete packet; + close_error(); } - amount = static_cast(output.tell()); - std::swap(buffer, output); } - - buffer.set(amount); return amount; } diff --git a/Source/Network/NetConnection.h b/Source/Network/NetConnection.h index 305be4022..30377ca0d 100644 --- a/Source/Network/NetConnection.h +++ b/Source/Network/NetConnection.h @@ -4,32 +4,54 @@ #define PERIMETER_IP_PORT_DEFAULT 12987 #define PERIMETER_IP_HOST_DEFAULT "127.0.0.1" const uint32_t PERIMETER_MESSAGE_MAX_SIZE = 32 * 1024 * 1024; -const uint32_t PERIMETER_MESSAGE_COMPRESSION_SIZE = 128*1024; +const uint32_t PERIMETER_MESSAGE_COMPRESSION_SIZE = 128 * 1024; ///Specifies this message contains compressed payload const uint16_t PERIMETER_MESSAGE_FLAG_COMPRESSED = 1 << 0; ///How many milliseconds extra to wait for the data part once getting header -const int RECV_DATA_AFTER_HEADER_TIMEOUT = 10000; +const int32_t RECV_DATA_AFTER_HEADER_TIMEOUT = 10000; ///How many milliseconds to wait for handshake to be sent/recv -const int CONNECTION_HANDSHAKE_TIMEOUT = 10000; +const int32_t CONNECTION_HANDSHAKE_TIMEOUT = 10000; +///How many milliseconds to wait for relay connection +const int32_t CONNECTION_RELAY_TIMEOUT = 10000; +///How many milliseconds to wait for active connection +const int32_t CONNECTION_ACTIVE_TIMEOUT = 10000; #include -//Special IDs -const uint64_t NETID_NONE = 0; -const uint64_t NETID_HOST = 1; -const uint64_t NETID_ALL = -1; - //Used to identify player connection typedef uint64_t NETID; +//Used to identify a room in server list +typedef uint64_t NetRoomID; + +//Special IDs +const NETID NETID_NONE = 0; +const NETID NETID_HOST = 1; +const NETID NETID_RELAY = 2; +const NETID NETID_ALL = 3; +const NETID NETID_CLIENTS_START = 100; + +/** + * NetConnectionMessage, holds message content and connection sending and intended destination + */ +struct NetConnectionMessage : public XBuffer { +public: + NETID source; + NETID destination; + + explicit NetConnectionMessage(size_t _size, NETID _source, NETID _destination) + : XBuffer(_size), source(_source), destination(_destination) { + } +}; + /** * Contains remote address data for connection */ class NetAddress { private: -public: IPaddress addr; - + +public: NetAddress& operator=(const NetAddress& other) { this->addr.host = other.addr.host; this->addr.port = other.addr.port; @@ -37,91 +59,148 @@ class NetAddress { } NetAddress(); + NetAddress(uint32_t host, uint16_t port); ~NetAddress(); + + void reset() { + addr.host = 0; + addr.port = 0; + } - static bool resolve(NetAddress& address, const std::string& host); + static bool resolve(NetAddress& address, const std::string& host, uint16_t default_port = 0); uint32_t crc() const; uint16_t port() const; + void setPort(uint16_t port); std::string getString() const; std::string getStringIP() const; + TCPsocket openTCP() const; }; /** - * Connection states + * Generic transport */ -enum NetConnectionState { - NC_STATE_SETUP, - NC_STATE_ACTIVE, +class NetTransport { +protected: + virtual int32_t send_raw(const uint8_t* buffer, uint32_t len, int32_t timeout) = 0; + virtual int32_t receive_raw(uint8_t* buffer, uint32_t len, int32_t timeout) = 0; - //Socket deleted - NC_STATE_ERROR, //Error occurred - NC_STATE_ERROR_PENDING, //Error occurred and pending for calling DeleteClient - NC_STATE_CLOSE_PENDING, //Closed and pending for calling DeleteClient - NC_STATE_CLOSED //Ready to be reused by another socket -}; - -/** - * Encapsulates a socket - */ -class NetConnection { -private: - SDLNet_SocketSet socket_set = nullptr; - TCPsocket socket = nullptr; +public: + static const int32_t NT_STATUS_NO_DATA = -1; + static const int32_t NT_STATUS_TIMEOUT = -2; + static const int32_t NT_STATUS_CLOSED = -3; + static const int32_t NT_STATUS_ERROR = -4; + + NetTransport() = default; + virtual ~NetTransport() { + close(); + }; + + /** Close the transport */ + virtual void close() {}; + + /** @return true if transport is closed */ + virtual bool is_closed() const { return true; } /** - * Sends TCP data into connection + * Sends data using internal send_raw * Closes connection upon error * * @param buffer pointer of data to send over wire * @param len amount of data to read from pointer + * @param timeout 0 to wait indefinitelly, amount of ms allow before giving up sending data * @return amount of bytes sent, 0 if none, <0 if error or closed */ - int send_raw(const void* buffer, uint32_t len); + int32_t send(const void* buffer, uint32_t len, int32_t timeout); /** - * Receives TCP data from connection if any + * Receives data from internal receive_raw * Closes connection upon error * * @param buffer pointer of data to write the received data - * @param maxlen max amount of data expected to read into buffer + * @param maxlen min amount of data expected to read into buffer or 0 to get what's available + * @param maxlen max amount of data expected to read into buffer or buffer max length * @param timeout 0 for no wait, amount of ms to wait until data is available * @return amount of bytes received, 0 if none, <0 if error or closed */ - int receive_raw(void* buffer, uint32_t maxlen, int timeout); + int32_t receive(void* buffer, uint32_t minlen, uint32_t maxlen, int32_t timeout); +}; + +/** + * Encapsulates TCP transport + */ +class NetTransportTCP: public NetTransport{ +private: + SDLNet_SocketSet socket_set = nullptr; + TCPsocket socket = nullptr; + +protected: + int32_t send_raw(const uint8_t* buffer, uint32_t len, int32_t timeout) override; + int32_t receive_raw(uint8_t* buffer, uint32_t len, int32_t timeout) override; public: - NetConnectionState state = NC_STATE_SETUP; - NETID netid = NETID_NONE; + explicit NetTransportTCP(TCPsocket socket); - explicit NetConnection(TCPsocket socket); - ~NetConnection(); + void close() override; - /** @return true if connection has errored */ - FORCEINLINE bool is_error() const { - return state == NC_STATE_ERROR || state == NC_STATE_ERROR_PENDING; + bool is_closed() const override { + return socket == nullptr; } +}; - /** @return true if connection is active */ - FORCEINLINE bool is_active() const { - return state == NC_STATE_ACTIVE; - } +/** + * Connection states + */ +enum NetConnectionState { + NC_STATE_HAS_TRANSPORT, //Connection transport is active + NC_STATE_HAS_CLIENT, //Connection has transport and is associated with a PNet Client + + //Socket deleted + NC_STATE_ERROR, //Error occurred + NC_STATE_ERROR_PENDING, //Error occurred and pending for calling DeleteClient + NC_STATE_CLOSE_PENDING, //Closed and pending for calling DeleteClient + NC_STATE_CLOSED //Ready to be reused by another socket +}; + +/** + * Encapsulates a game connection + */ +class NetConnection { +private: + friend class NetConnectionHandler; + NetTransport* transport = nullptr; + NETID netid = NETID_NONE; + uint64_t time_contact = 0; + NetConnectionState state = NC_STATE_CLOSED; + bool is_relay = false; + +public: + explicit NetConnection(NetTransport* transport, NETID netid); + ~NetConnection(); /** @return true if connection is closed */ - FORCEINLINE bool is_closed() const { + FORCEINLINE bool isClosed() const { return state == NC_STATE_CLOSED; } /** @return true if socket is closed */ - FORCEINLINE bool has_socket() const { - return socket != nullptr; + FORCEINLINE bool hasTransport() const { + return transport != nullptr && !transport->is_closed(); + } + + FORCEINLINE NETID getNETID() const { + return netid; + } + + FORCEINLINE bool isRelay() const { + return is_relay; } /** - * Sets the socket for this connection - * If one is already set the connection is closed before setting new socket + * Sets the transport for this connection + * If one is already set the transport is closed before setting new transport */ - void set_socket(TCPsocket socket); + void set_transport(NetTransport* transport, NETID netid); /** Closes the connection with error state */ void close_error() { @@ -139,35 +218,39 @@ class NetConnection { * Closes connection upon error * * @param buffer data to send into connection + * @param source source NETID that has sent this data + * @param destination destination NETID that will receive this data, can be NETID_NONE to assign connection's NETID * @return amount of bytes sent, <0 if error or closed */ - int send(const XBuffer& data); - - /** - * Writes data to connection - * Closes connection upon error - * - * @param buffer pointer of data to send into connection - * @param len amount of data to send - * @return amount of bytes sent, <0 if error or closed - */ - int send(const uint8_t* buffer, uint32_t len); + int32_t send(const XBuffer* data, NETID source, NETID destination, int32_t timeout); /** * Receives data from connection if any * Closes connection upon error * If no bytes were received the buffer is not allocated * - * @param buffer ref of data buffer to store the received data + * @param packet_ptr pointer for input packet ptr to store the incoming data * @param timeout 0 for no wait, amount of ms to wait until data is available * @param expectedlen expected data length, if data doesn't match then is discarded, 0< for no restriction * @return amount of bytes received, 0 if none, <0 if error or closed */ - int receive(XBuffer& buffer, int timeout = 0); + int32_t receive(NetConnectionMessage** packet_ptr, int32_t timeout); }; class PNetCenter; +struct NetRelayPeerInfo { + ///Has a client assigned in NetCenter? + bool has_client = false; + ///If host: this peer has been rejected already and is pending removal by relay? + ///If client: simply ignore the peer as we can't remove it + bool rejected = false; + ///The time this peer was first seen + uint64_t time_contact = 0; + ///The relay this peer belongs to + NETID relay_netid = NETID_NONE; +}; + /** * Bundles several connections and also handles incoming connections in accept socket */ @@ -177,10 +260,27 @@ class NetConnectionHandler { PNetCenter* net_center = nullptr; TCPsocket accept_socket = nullptr; std::unordered_map connections; + std::unordered_map relayPeers; + bool has_relay_connection = false; void stopListening(); + void stopRelay(); void stopConnections(); - NetConnection* newConnectionFromSocket(TCPsocket socket, bool host); + void tellPNetRelayDisconnected(); + + ///Creates a new NetConnection instance from socket and assign's to netid + ///If connection couldn't be allocated the connection->getNETID() will return NETID_NONE + ///and must be free'd + NetConnection* newConnectionFromSocket(TCPsocket socket, NETID netid); + + /// Reads messages from connection until empty or max_packets reached + void readConnectionMessages(NetConnection* connection, size_t max_packets); + + ///Creates a new room in relay for allowing clients to interact with game host + bool startRelayRoom(); + + ///Called to process a relay message + void receivedRelayMessage(NetConnection* connection, NetConnectionMessage* msg); public: explicit NetConnectionHandler(PNetCenter* center); @@ -189,29 +289,63 @@ class NetConnectionHandler { /** Resets all state and closes everything */ void reset(); - /** - * Checks if there is a new connection to accept - * @return new connection id that was accepted or NETID_NONE - */ - NETID acceptConnection(); + //Connection stuff /** Polls the connections */ void pollConnections(); - + /** * Sends buffer data to connection by NETID * @return amount of data sent in total, this can be several times if is NETID_ALL */ - size_t sendToNETID(const uint8_t* buffer, size_t size, NETID destination); + size_t sendToNETID(const XBuffer* buffer, NETID source, NETID destination); + + /** + * Changes the NETID of connection + */ + bool reassignConnectionNETID(NetConnection* connection, NETID netid); - //Connection stuff + /** Obtain a connection from NETID */ NetConnection* getConnection(NETID netid) const; + + /** Get list of relay peers */ + const std::unordered_map& getRelayPeers() const { return relayPeers; } + + /** + * Terminates any connection with provided NETID + * If peer is behind relay the relay is notified + */ + void terminateNETID(NETID netid); + + /** + * @return if there is at least one relay connection + */ + bool hasRelayConnection() const { + return has_relay_connection; + }; //Host related functions - bool startListening(uint16_t port); - //Single client connection - NetConnection* startConnection(NetAddress* address); + ///Game opens a listening port directly on the system that is running on and also a public room in relay + bool startHost(uint16_t listen_port, bool start_public_room); + + /** + * Checks if there is a new connection to accept from listening port or relay + * @return new connection id that was accepted or NETID_NONE + */ + void acceptConnection(); + + ///Sets connection has having client + void setHasClient(NETID netid, bool state); + + //Client related functions + + ///Connects to a game acting as host directly + NetConnection* startDirectConnection(const NetAddress& address); + + ///Connects to a game using relay server as intermediary + ///the relay address and room for the desired game from server list must be provided + NetConnection* startRelayRoomConnection(const NetAddress& address, NetRoomID room_id); }; #endif //PERIMETER_NETCONNECTION_H diff --git a/Source/Network/NetConnectionAux.h b/Source/Network/NetConnectionAux.h index edcfd4f0d..6cc686e7f 100644 --- a/Source/Network/NetConnectionAux.h +++ b/Source/Network/NetConnectionAux.h @@ -10,12 +10,13 @@ extern const char* currentShortVersion; #define ATTRIBUTES_CRC_ARCHIVE BinaryOArchive ///First packet sent upon connection -typedef uint64_t arch_flags; const uint32_t NC_INFO_ID = 0xF8C20001; class NetConnectionInfo { private: //Header + ///Must be NC_INFO_ID to identify this uint32_t id = 0; + ///CRC of version string uint32_t versionCRC = 0; //Content arch_flags arch = 0; @@ -88,7 +89,7 @@ class NetConnectionInfo { //Arch - 60-61 bits (0 = under 32, 1 = 32, 2 = 64, 3 = above 64) cpu |= (sizeof(void*) / 4) & 3; //CPU endianness - 63 bit -#ifdef SDL_BIG_ENDIAN +#if SDL_BYTEORDER == SDL_LIL_ENDIAN cpu |= 1<<3; #endif xassert(cpu <= 0xF); @@ -182,19 +183,6 @@ class NetConnectionInfo { ///Reply from sConnectInfo const uint32_t NC_INFO_REPLY_ID = 0x47C3FE65; struct NetConnectionInfoResponse { - enum e_ConnectResult{ - CR_NONE, - CR_OK, - CR_ERR_INCORRECT_SIGNATURE, - CR_ERR_INCORRECT_ARCH, - CR_ERR_INCORRECT_VERSION, - CR_ERR_INCORRECT_CONTENT, - CR_ERR_INCORRECT_CONTENT_FILES, - CR_ERR_INCORRECT_PASWORD, - CR_ERR_GAME_STARTED, - CR_ERR_GAME_FULL - }; - uint32_t id = 0; e_ConnectResult connectResult = CR_NONE; NETID clientID = 0; @@ -217,6 +205,9 @@ struct NetConnectionInfoResponse { void read(XBuffer& in) { in.read(id); + if (id != NC_INFO_REPLY_ID) { + return; + } in.read(connectResult); in.read(clientID); in.read(hostID); @@ -268,26 +259,15 @@ struct NetConnectionInfoResponse { crc=calcOwnCRC(); } - bool checkOwnCorrect(){ - return ( (crc==calcOwnCRC()) && (id==NC_INFO_REPLY_ID) ); + int32_t send_to_connection(NetConnectionHandler& connection, NETID source, NETID destination) const { + XBuffer responseBuffer(sizeof(NetConnectionInfoResponse) + gameName.length() + 1024, true); + write(responseBuffer); + return connection.sendToNETID(&responseBuffer, source, destination); } -}; - - -//////////////////////////////////////////////////////////////////////// - -/** - * InputPacket, holds buffer and connection sending it - */ -struct InputPacket : public XBuffer { -public: - NETID netid; - - explicit InputPacket(NETID _netid) : XBuffer(0), netid(_netid) { + bool checkOwnCorrect(){ + return ( (crc==calcOwnCRC()) && (id==NC_INFO_REPLY_ID) ); } }; #endif //__P2P_INTERFACEAUX_H__ - - diff --git a/Source/Network/NetConnectionHandler.cpp b/Source/Network/NetConnectionHandler.cpp index e62188b80..2662c9ac2 100644 --- a/Source/Network/NetConnectionHandler.cpp +++ b/Source/Network/NetConnectionHandler.cpp @@ -3,6 +3,8 @@ #include "crc.h" #include "P2P_interface.h" #include "NetConnectionAux.h" +#include "NetRelay.h" +#include "codepages/codepages.h" NetConnectionHandler::NetConnectionHandler(PNetCenter* center): net_center(center) { stopConnections(); @@ -15,11 +17,35 @@ NetConnectionHandler::~NetConnectionHandler() { void NetConnectionHandler::reset() { stopListening(); + stopRelay(); stopConnections(); } -NETID NetConnectionHandler::acceptConnection() { - NETID netid = NETID_NONE; +void NetConnectionHandler::readConnectionMessages(NetConnection* connection, size_t max_packets) { + size_t total_recv = 0; + size_t i = 0; + while (total_recv < PERIMETER_MESSAGE_MAX_SIZE * max_packets && i < max_packets) { + i += 1; + //Packet ptr will be set if received one successfully + NetConnectionMessage* packet = nullptr; + int len = connection->receive(&packet, 0); + if (!packet) { + break; + } + total_recv += len; + if (0 < len) { + if (connection->is_relay && connection->netid == packet->source) { + this->receivedRelayMessage(connection, packet); + } else { + net_center->ReceivedNetConnectionMessage(connection, packet); + } + } else { + delete packet; + } + } +} + +void NetConnectionHandler::acceptConnection() { if (accept_socket) { TCPsocket incoming_socket = SDLNet_TCP_Accept(accept_socket); if(!incoming_socket) { @@ -28,61 +54,205 @@ NETID NetConnectionHandler::acceptConnection() { NetConnection* incoming = nullptr; //Find any closed connection in array - bool reused = false; for (auto& entry : connections) { - if (entry.second->is_closed()) { + if (entry.second->isClosed()) { incoming = entry.second; - incoming->set_socket(incoming_socket); - reused = true; + NetTransportTCP* transport = new NetTransportTCP(incoming_socket); + incoming->set_transport(transport, entry.first); break; } } - //Check if we can add it - if (!reused) { - incoming = newConnectionFromSocket(incoming_socket, false); + //Couldn't find any connection to reuse, create new + if (incoming == nullptr) { + incoming = newConnectionFromSocket( + incoming_socket, + NETID_CLIENTS_START + connections.size() + ); } - //incoming may be deallocated if index is not available, so get it before - netid = incoming->netid; - - net_center->handleIncomingClientConnection(incoming); + if (incoming) { + if (incoming->getNETID() == NETID_NONE) { + //If netid is NONE then it wasnt allocated into pool + fprintf(stderr, "Incoming connection couldn't be allocated\n"); + //Send the reply that game is full and close it without reading connection info + net_center->SendClientHandshakeResponse(incoming, incoming->getNETID(), + e_ConnectResult::CR_ERR_GAME_FULL); + //Delete connection since is not stored anywhere + delete incoming; + } else { + //Set initial time of contact + incoming->time_contact = clock_us(); + } + } + } + } +} + +void NetConnectionHandler::setHasClient(NETID netid, bool state) { + if (netid == NETID_NONE || netid == NETID_ALL) { + xassert(0); + return; + } + + //Check relay + if (relayPeers.count(netid)) { + NetRelayPeerInfo& info = relayPeers.at(netid); + xassert(!(state && info.has_client)); + info.has_client = state; + return; + } + + //Check local + NetConnection* connection = getConnection(netid); + if (connection) { + if (connection->is_relay) { + xassert(0); + return; + } + if (state) { + switch (connection->state) { + case NC_STATE_HAS_TRANSPORT: + connection->state = NC_STATE_HAS_CLIENT; + break; + case NC_STATE_HAS_CLIENT: + case NC_STATE_ERROR_PENDING: + case NC_STATE_CLOSE_PENDING: + case NC_STATE_ERROR: + case NC_STATE_CLOSED: + xassert(0); + break; + } + } else { + switch (connection->state) { + case NC_STATE_HAS_CLIENT: + connection->state = NC_STATE_HAS_TRANSPORT; + break; + case NC_STATE_ERROR_PENDING: + case NC_STATE_CLOSE_PENDING: + case NC_STATE_HAS_TRANSPORT: + case NC_STATE_ERROR: + case NC_STATE_CLOSED: + break; + } } } +} + +void NetConnectionHandler::terminateNETID(NETID netid) { + if (netid == NETID_NONE || netid == NETID_ALL) { + xassert(0); + return; + } + LogMsg("terminateNETID 0x%" PRIX64 "\n", netid); - return netid; + //Check relay + if (relayPeers.count(netid)) { + NetRelayPeerInfo& info = relayPeers.at(netid); + if (info.rejected) { + return; + } + info.rejected = true; + if (net_center->isHost()) { + //Notify relay to remove this peer connection, + //relay will tell back when happens so the game can remove from relayPeers + static NetRelayMessage_PeerClosePeer msg; + msg.netid = netid; + NetConnection* relay = getConnection(info.relay_netid); + xassert(relay); + if (relay) { + sendNetRelayMessage(relay, &msg, net_center->m_localNETID); + } + } else { + //Client, just remove here + if (info.has_client) { + net_center->DeleteClient(netid, false); + } else if (netid == net_center->m_hostNETID) { + //If is the host and is not yet client (we are still in handshake) just stop relay + if (has_relay_connection) { + tellPNetRelayDisconnected(); + } + stopRelay(); + } + } + return; + } + + //Check local + NetConnection* conn = getConnection(netid); + if (conn && !conn->isClosed()) { + conn->close(); + } } void NetConnectionHandler::pollConnections() { + uint64_t now = clock_us(); for (auto& entry : connections) { NetConnection* connection = entry.second; - switch (connection->state) { - case NC_STATE_ACTIVE: { - size_t total_recv = 0; - while (total_recv < PERIMETER_MESSAGE_MAX_SIZE * 10) { - InputPacket* packet = new InputPacket(connection->netid); - int len = connection->receive(*packet); - if (0 < len) { - net_center->m_InputPacketList.push_back(packet); - total_recv += len; - } else { - delete packet; - break; + if (connection->is_relay) { + switch (connection->state) { + case NC_STATE_HAS_TRANSPORT: { + readConnectionMessages(connection, 10); + break; + } + default: + LogMsg("Relay connection unknown state %d\n", connection->state); + [[fallthrough]]; + case NC_STATE_CLOSED: + case NC_STATE_ERROR: { + //Mark it as closed, relay is in non-functional state + if (has_relay_connection) { + tellPNetRelayDisconnected(); } + stopRelay(); + break; } - break; } - case NC_STATE_ERROR_PENDING: - case NC_STATE_CLOSE_PENDING: { - net_center->DeleteClient(connection->netid, false); - //Mark it as closed, since we processed the client - connection->state = NC_STATE_CLOSED; - break; + } else { + switch (connection->state) { + case NC_STATE_HAS_TRANSPORT: + //Check if stayed too long without having a client assigned + if ((now - connection->time_contact) > (CONNECTION_HANDSHAKE_TIMEOUT * 1000)) { + fprintf(stderr, "Connection without client for too long! 0x%" PRIX64 "\n", connection->netid); + connection->close(); + break; + } + [[fallthrough]]; + case NC_STATE_HAS_CLIENT: { + readConnectionMessages(connection, 10); + break; + } + case NC_STATE_ERROR_PENDING: + case NC_STATE_CLOSE_PENDING: { + net_center->DeleteClient(connection->netid, false); + //Mark it as closed, since we processed the client + [[fallthrough]]; + } + case NC_STATE_ERROR: { + connection->state = NC_STATE_CLOSED; + connection->close(); + break; + } + case NC_STATE_CLOSED: + //We ignore closed ones as they will be reused + break; } - default: - break; } } + + std::vector relayPeerTimeouts; + for (auto& pair : this->relayPeers) { + if (pair.second.has_client || pair.second.rejected) { + continue; + } + //Reject peers that spent too much time to get a client + if ((clock_us() - pair.second.time_contact) > (CONNECTION_HANDSHAKE_TIMEOUT * 1000)) { + relayPeerTimeouts.push_back(pair.first); + } + } + for (NETID netid : relayPeerTimeouts) { + terminateNETID(netid); + } } void NetConnectionHandler::stopConnections() { @@ -95,37 +265,47 @@ void NetConnectionHandler::stopConnections() { } NetConnection* NetConnectionHandler::getConnection(NETID netid) const { + if (netid == NETID_NONE || netid == NETID_ALL) { + xassert(0); + return nullptr; + } NetConnection* conn = nullptr; if (connections.count(netid)) { NetConnection* candidate = connections.at(netid); - if (!candidate->is_closed()) { + if (!candidate->isClosed()) { conn = candidate; } } return conn; } -bool NetConnectionHandler::startListening(uint16_t port) { +bool NetConnectionHandler::startHost(uint16_t listen_port, bool start_public_room) { reset(); - if (gb_RenderDevice->GetRenderSelection() == DEVICE_HEADLESS) { - max_connections = NETWORK_PLAYERS_MAX; - } else { - //Remove one since host is player too - max_connections = NETWORK_PLAYERS_MAX - 1; + //Remove one since host is player too + max_connections = NETWORK_PLAYERS_MAX - 1; + + bool ok = true; + if (start_public_room) { + if (!startRelayRoom()) { + ok = false; + } } - IPaddress addr; - addr.host = INADDR_ANY; - SDLNet_Write16(port, &addr.port); + if (ok && 0 < listen_port) { + NetAddress addr(INADDR_ANY, listen_port); + accept_socket = addr.openTCP(); + if (accept_socket == nullptr) { + ok = false; + } else { + LogMsg("TCP listening on port %d\n", listen_port); + } + } - accept_socket = SDLNet_TCP_Open(&addr); - if (accept_socket == nullptr) { - fprintf(stderr, "TCP listen failed on port %d error %s\n", port, SDLNet_GetError()); - return false; - } else { - LogMsg("TCP listening on port %d\n", port); + if (!ok && start_public_room) { + stopRelay(); } + return true; } @@ -137,75 +317,392 @@ void NetConnectionHandler::stopListening() { } } -NetConnection* NetConnectionHandler::startConnection(NetAddress* address) { - reset(); +bool NetConnectionHandler::startRelayRoom() { + //Create relay connection + NetAddress conn; + if (!getNetRelayAddress(conn)) { + return false; + } + TCPsocket socket = conn.openTCP(); + if (!socket) { + return false; + } + NetConnection* connection = newConnectionFromSocket(socket, NETID_RELAY); + if (!connection || connection->netid == NETID_NONE) { + delete connection; + return false; + } + + connection->is_relay = true; + has_relay_connection = true; + + //Send our room info to create it + const NetRelayMessage_PeerSetupRoom& msg = net_center->GenerateRelaySetupRoom(); + bool ok = sendNetRelayMessage(connection, &msg, NETID_HOST); + + //Receive list of peers and our own netid + if (ok) { + NetConnectionMessage* msg_response = nullptr; + connection->receive(&msg_response, CONNECTION_RELAY_TIMEOUT); + if (!msg_response) { + ok = false; + } else { + receivedRelayMessage(connection, msg_response); + ok = net_center->m_localNETID != NETID_NONE; + } + } + + return ok; +} + +NetConnection* NetConnectionHandler::startRelayRoomConnection(const NetAddress& address, NetRoomID room_id) { + reset(); + max_connections = 1; - TCPsocket socket = SDLNet_TCP_Open(&address->addr); + //Start relay connection and assign as host since it will act as one after sending join room + TCPsocket socket = address.openTCP(); + if (!socket) { + return nullptr; + } + NetConnection* connection = newConnectionFromSocket(socket, NETID_RELAY); + if (!connection || connection->netid == NETID_NONE) { + delete connection; + stopRelay(); + return nullptr; + } + + connection->is_relay = true; + has_relay_connection = true; + + //Send the request to join room + static XBuffer buf(1024, true); + buf.init(); + static NetRelayMessage_PeerJoinRoom msg; + msg.room = room_id; + msg.write(buf); + + bool ok = sendNetRelayMessage(connection, &msg, NETID_NONE); + + //Receive list of peers and our own netid + if (ok) { + NetConnectionMessage* msg_response = nullptr; + connection->receive(&msg_response, CONNECTION_RELAY_TIMEOUT); + if (!msg_response) { + ok = false; + } else { + receivedRelayMessage(connection, msg_response); + ok = net_center->m_localNETID != NETID_NONE; + } + } + + if (!ok) { + stopRelay(); + } + + //Now that we are in room we can start talking to game host + return connection; +} + +void NetConnectionHandler::stopRelay() { + has_relay_connection = false; + relayPeers.clear(); + for (auto& pair : connections) { + NetConnection* connection = pair.second; + if (connection && connection->is_relay && !connection->isClosed()) { + LogMsg("Relay connection 0x%" PRIX64 " closed\n", connection->netid); + connection->close(); + } + } +} + +void NetConnectionHandler::tellPNetRelayDisconnected() { + if (net_center && net_center->isConnected()) { + net_center->ExecuteInternalCommand(PNC_COMMAND__END_GAME, false); + if (net_center->isHost()) { + net_center->ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECTION_FAILED); + } else { + net_center->ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_HOST_TERMINATED_GAME); + } + } +} + +NetConnection* NetConnectionHandler::startDirectConnection(const NetAddress& address) { + max_connections = 1; + + TCPsocket socket = address.openTCP(); if (!socket) { - fprintf(stderr, "TCP socket open failed address %s error %s\n", address->getString().c_str(), SDLNet_GetError()); return nullptr; } - NetConnection* connection = newConnectionFromSocket(socket, true); - if (connection->netid != NETID_NONE) { + NetConnection* connection = newConnectionFromSocket(socket, NETID_HOST); + if (connection && connection->netid != NETID_NONE) { return connection; } else { fprintf(stderr, "Error allocating new connection\n"); - connection->close(); delete connection; return nullptr; } } -NetConnection* NetConnectionHandler::newConnectionFromSocket(TCPsocket socket, bool host) { - NetConnection* connection = new NetConnection(socket); +NetConnection* NetConnectionHandler::newConnectionFromSocket(TCPsocket socket, NETID netid) { + xassert(socket); + NetConnection* connection = nullptr; if (connections.size() < max_connections) { - NETID netid; - if (host) { - netid = NETID_HOST; + if (connections.count(netid)) { + //Already exists, reuse + connection = connections.at(netid); + if (!connection->isClosed()) { + fprintf(stderr, "Connection to " PRIX64 " already allocated!\n"); + xassert(0); + } } else { - netid = NETID_HOST + connections.size() + 1; + connection = new NetConnection(nullptr, netid); + auto result = connections.try_emplace(netid, connection); + if (!result.second) { + xassert(0); + delete connection; + connection = nullptr; + } + } + if (connection && connection->getNETID() != netid) { + xassert(0); + connection->netid = netid; } - connection->netid = netid; - connections.insert_or_assign(netid, connection); } + if (!connection) { + //Connection couldn't be assigned, return empty one so we can put socket on it + connection = new NetConnection(nullptr, NETID_NONE); + } + NetTransportTCP* transport = new NetTransportTCP(socket); + connection->set_transport(transport, netid); return connection; } - -size_t SendBufferToConnection(const uint8_t* buffer, size_t size, NetConnection* connection) { +size_t SendBufferToConnection( + const XBuffer* buffer, + NetConnection* connection, + NETID source, NETID destination, + int32_t timeout +) { int retries = 5; - if (!connection || !connection->is_active()) { + if (!connection || !connection->hasTransport()) { return 0; } while (0 < retries) { - int sent = connection->send(buffer, size); + int sent = connection->send(buffer, source, destination, timeout); if (0 < sent) { - return size; - } else if (!connection->is_active()) { - fprintf(stderr, "SendBufferToConnection error sending %" PRIsize " sent %d to 0x%" PRIX64 " closed\n", size, sent, connection->netid); - break; + return buffer->tell(); } else { - fprintf(stderr, "SendBufferToConnection error sending %" PRIsize " sent %d to 0x%" PRIX64 " retry %d\n", size, sent, connection->netid, retries); + fprintf(stderr, "SendBufferToConnection error sending %" PRIsize " sent %d to 0x%" PRIX64 " retry %d\n", buffer->tell(), sent, connection->getNETID(), retries); retries--; } } return 0; } -size_t NetConnectionHandler::sendToNETID(const uint8_t* buffer, size_t size, NETID destination) { - size_t sent = 0; +size_t NetConnectionHandler::sendToNETID(const XBuffer* buffer, NETID source, NETID destination) { if (destination == NETID_NONE) { - fprintf(stderr, "Discarding sending to NETID_NONE\n"); - } else if (destination == NETID_ALL) { + xassert(0); + fprintf(stderr, "Discarding sending from 0x%" PRIX64 " to NETID_NONE\n", source); + return 0; + } + size_t sent = 0; + if (destination == NETID_ALL) { + //Send both to relay if any and to each peer for (auto& conn : connections) { - sent += SendBufferToConnection(buffer, size, conn.second); + sent += SendBufferToConnection( + buffer, conn.second, + source, conn.second->is_relay ? destination : NETID_NONE, + CONNECTION_ACTIVE_TIMEOUT + ); + } + } else if (has_relay_connection && 0 != relayPeers.count(destination)) { + //Message is for relay or a peer behind relay + NetRelayPeerInfo& info = relayPeers.at(destination); + if (!info.rejected) { + sent = SendBufferToConnection( + buffer, getConnection(info.relay_netid), + source, destination, + CONNECTION_RELAY_TIMEOUT + ); } } else { - sent = SendBufferToConnection(buffer, size, getConnection(destination)); + //Message is for connection + sent = SendBufferToConnection( + buffer, getConnection(destination), + source, NETID_NONE, + CONNECTION_ACTIVE_TIMEOUT + ); } return sent; } + +bool NetConnectionHandler::reassignConnectionNETID(NetConnection* connection, NETID netid) { + if (connection->netid != netid) { + auto result = connections.try_emplace(netid, connection); + if (result.second) { + //Inserted, remove from current position and set new netid to connection + auto it = connections.begin(); + auto end = connections.end(); + for (; it != end; ++it) { + if ((*it).first == netid) { + connections.erase(it); + break; + } + } + connection->netid = netid; + } + } + + if (connection->netid == netid) { + return true; + } + + //Couldn't be reassigned + xassert(0); + fprintf(stderr, "Couldn't reassign connection %" PRIX64 " to %" PRIX64 "\n", connection->netid, netid); + return false; +} + +void NetConnectionHandler::receivedRelayMessage(NetConnection* connection, NetConnectionMessage* msg) { + NETID localNETID = net_center->m_localNETID; + bool is_host = net_center->isHost(); + //Parse header to know what message was sent + msg->set(0); + static NetRelayMessage msg_header(RELAY_MSG_UNKNOWN); + msg_header.read_relay_header(*msg); + bool terminate = false; + if (msg_header.protocol_version != NET_RELAY_PROTOCOL_VERSION) { + xassert(0); + fprintf(stderr, "[Relay] Unexpected protocol %" PRIX32 "\n", msg_header.protocol_version); + terminate = true; + } else { + bool wrong_destination; + switch (msg_header.msg_type) { + case RELAY_MSG_CLOSE: + wrong_destination = msg->destination != NETID_NONE; + break; + default: + wrong_destination = msg->destination != localNETID; + break; + case RELAY_MSG_RELAY_LIST_PEERS: + wrong_destination = msg->destination == NETID_NONE; + break; + } + if (wrong_destination) { + fprintf(stderr, "[Relay] Received msg for someone else! for 0x%" PRIX64 "\n", + msg->destination); + delete msg; + return; + } + XBuffer buffer(128, true); + switch (msg_header.msg_type) { + default: { +#ifdef PERIMETER_DEBUG + xassert(0); + fprintf(stderr, "[Relay] connection sent unknown msg type: %" PRIX32 "\n", msg_header.msg_type); +#endif + break; + } + case RELAY_MSG_CLOSE: { + terminate = true; + printf("[Relay] connection sent close msg\n"); + break; + } + case RELAY_MSG_RELAY_LIST_LOBBIES: { +#ifdef PERIMETER_DEBUG + printf("[Relay] connection sent result msg, discarding\n"); +#endif + break; + } + case RELAY_MSG_RELAY_PING: { + static NetRelayMessage_RelayPing message; + message.read(*msg); + static NetRelayMessage_PeerPingResponse response; + response.secs = message.secs; + response.subsecs = message.subsecs; + sendNetRelayMessage(connection, &response, localNETID); + break; + } + case RELAY_MSG_RELAY_LIST_PEERS: { + static NetRelayMessage_RelayListPeers message; + message.read(*msg); + if (is_host) { + net_center->m_hostNETID = localNETID = msg->destination; + } else { + localNETID = msg->destination; + if (message.peers.empty()) { + net_center->m_hostNETID = NETID_NONE; + } else { + net_center->m_hostNETID = message.peers[0]; + } + } + net_center->m_localNETID = localNETID; + + //Register any peer that isn't already present + for (auto& netid : message.peers) { + if (0 == relayPeers.count(netid)) { + NetRelayPeerInfo info; + info.relay_netid = msg->source; + info.time_contact = clock_us(); + relayPeers.try_emplace(netid, info); + } + } + +#ifdef PERIMETER_DEBUG + printf("[Relay] room list destination %" PRIX64 " peers: %" PRIsize " [", + msg->destination, message.peers.size()); + for (NETID netid : message.peers) { + printf(" %" PRIX64, netid); + } + printf(" ]\nNETIDs local: %" PRIX64 " host: %" PRIX64 "\n", net_center->m_localNETID, net_center->m_hostNETID); +#endif + break; + } + case RELAY_MSG_RELAY_ADD_PEER: { + static NetRelayMessage_RelayAddPeer message; + message.read(*msg); +#ifdef PERIMETER_DEBUG + printf("[Relay] room add peer %" PRIX64 "\n", message.netid); +#endif + NetRelayPeerInfo info; + info.relay_netid = msg->source; + info.time_contact = clock_us(); + relayPeers.try_emplace(message.netid, info); + break; + } + case RELAY_MSG_RELAY_REMOVE_PEER: { + static NetRelayMessage_RelayRemovePeer message; + message.read(*msg); +#ifdef PERIMETER_DEBUG + printf("[Relay] room remove peer %" PRIX64 "\n", message.netid); +#endif + auto it = relayPeers.begin(); + auto end = relayPeers.end(); + for (; it != end; ++it) { + if ((*it).first == message.netid) { + NetRelayPeerInfo& info = (*it).second; + if (info.has_client) { + net_center->DeleteClient(message.netid, false); + } + relayPeers.erase(it); + break; + } + } + break; + } + } + } + + //Cleanup + delete msg; + if (terminate) { + if (has_relay_connection) { + tellPNetRelayDisconnected(); + } + stopRelay(); + } +} diff --git a/Source/Network/NetPlayer.h b/Source/Network/NetPlayer.h index b037a495b..0c36f0c91 100644 --- a/Source/Network/NetPlayer.h +++ b/Source/Network/NetPlayer.h @@ -127,7 +127,7 @@ class MissionDescription int playersAmountScenarioMax() const { return playerAmountScenarioMax; } int playersAmount() const; - int playersMaxEasily() const; + int playerSlotsAvailable() const; void packPlayerIDs(); diff --git a/Source/Network/NetRelay.cpp b/Source/Network/NetRelay.cpp new file mode 100644 index 000000000..61d7fb503 --- /dev/null +++ b/Source/Network/NetRelay.cpp @@ -0,0 +1,238 @@ +#include "NetIncludes.h" +#include "NetRelay.h" + +bool getNetRelayAddress(NetAddress& addr) { + const char* cmdline_relay = check_command_line("netrelay"); + return NetAddress::resolve( + addr, + cmdline_relay ? cmdline_relay : NET_RELAY_DEFAULT_ADDRESS, + NET_RELAY_DEFAULT_PORT + ); +} + +bool receiveNetRelayMessage( + NetConnection* relay, + NETID source, + NetRelayMessage* result, + NetRelayMessageType result_type_expected +) { + if (!relay) { + xassert(0); + return false; + } + + //Receive packet from relay + NetConnectionMessage* packet = nullptr; + int32_t ret = relay->receive(&packet, CONNECTION_HANDSHAKE_TIMEOUT); + if (0 < ret) { + if (packet == nullptr) { + xassert(0); + fprintf(stderr, "Missing packet?\n"); + ret = -1; + } else if (packet->source != relay->getNETID()) { + fprintf(stderr, "Unexpected relay message source %" PRIX64 " expected %" PRIX64 "\n", packet->source, relay->getNETID()); + ret = -1; + } else if (packet->destination != source) { + fprintf(stderr, "Unexpected relay message destination %" PRIX64 " expected %" PRIX64 "\n", packet->destination, source); + ret = -1; + } + } + + //Parse header first + if (0 < ret) { + packet->set(0); + result->read_relay_header(*packet); + if (result->protocol_version != NET_RELAY_PROTOCOL_VERSION) { + xassert(0); + fprintf(stderr, "Unexpected relay protocol %" PRIX32 "\n", result->protocol_version); + ret = -1; + } else if (result->msg_type == RELAY_MSG_CLOSE) { + fprintf(stderr, "Relay connection sent close msg\n"); + relay->close(); + ret = -1; + } else if (result_type_expected != RELAY_MSG_UNKNOWN && result->msg_type != result_type_expected) { + xassert(0); + fprintf(stderr, "Unexpected relay message type %" PRIX32 " expected %" PRIX32 "\n", result->msg_type, result_type_expected); + ret = -1; + } + } + + //Read rest of content if header is OK + if (0 < ret) { + result->read(*packet); + } + + //Error occurred + if (packet) { + delete packet; + packet = nullptr; + } + if (ret == 0) { + fprintf(stderr, "Timeout receiving relay response\n"); + } else if (ret < 0) { + fprintf(stderr, "Error receiving relay response: %" PRIi32"\n", ret); + } + return 0 < ret; +} + +bool sendNetRelayMessage( + NetConnection* relay, + const NetRelayMessage* msg, + NETID source +) { + //Assemble buffer and send it + static XBuffer buf(1024, true); + buf.init(); + msg->write_relay_header(buf); + msg->write(buf); + int ret = relay->send(&buf, source, relay->getNETID(), CONNECTION_RELAY_TIMEOUT); + if (ret <= 0) { + return false; + } + + //Avoid excess grow + if (buf.length() > 10240) { + buf.realloc(10240); + } + + return true; +} + +void closeNetRelay(NetConnection* relay) { + if (relay->hasTransport()) { + static NetRelayMessage_Close msg; + sendNetRelayMessage(relay, &msg, NETID_NONE); + } + relay->close(); +} + +void NetRelayMessage::read_relay_header(XBuffer& in) { + uint32_t tmp; + in > tmp; + protocol_version = (tmp >> 24) & 0xFF; + msg_type = static_cast(tmp & 0xFFFFFF); +} + +void NetRelayMessage::write_relay_header(XBuffer& out) const { + uint32_t tmp = static_cast(msg_type) & 0xFFFFFF; + tmp |= static_cast(protocol_version & 0xFF) << 24; + out < tmp; +} + +void NetRelayMessage::write_string(XBuffer& out, const std::string& str, uint16_t max_len) { + uint16_t len = static_cast(str.length()); + max_len = min(max_len, NET_RELAY_MAX_STRING_LENGTH); + if (len > max_len) { + xassert(0); + len = max_len; + } + out < len; + out.write(str.c_str(), len, true); +} + +void NetRelayMessage::write_list(XBuffer& out, const std::vector& list, uint16_t max_elements, uint16_t max_element_len) { + uint16_t len = static_cast(list.size()); + max_elements = min(max_elements, NET_RELAY_MAX_LIST_ELEMENTS); + if (len > max_elements) { + xassert(0); + len = max_elements; + } + out < len; + for (int i = 0; i < len; ++i) { + write_string(out, list[i], max_element_len); + } +} + +void NetRelayMessage::write_map(XBuffer& out, const std::unordered_map& map, uint16_t max_elements, uint16_t max_key_len, uint16_t max_value_len) { + uint16_t len = static_cast(map.size()); + max_elements = min(max_elements, NET_RELAY_MAX_MAP_ELEMENTS); + if (len > max_elements) { + xassert(0); + len = max_elements; + } + out < len; + for (auto& pair : map) { + write_string(out, pair.first, max_key_len); + write_string(out, pair.second, max_value_len); + } +} + +void NetRelayMessage_Close::read(XBuffer &in) { + in > code; +} + +void NetRelayMessage_Close::write(XBuffer& out) const { + out < code; +} + +void NetRelayMessage_PeerListRooms::write(XBuffer& out) const { + //Game identifier + write_string(out, "perimeter", 32); + //Send in XPrm format + out < FORMAT_XPRM; +} + +void NetRelayMessage_PeerSetupRoom::write(XBuffer& out) const { + //Game identifier + write_string(out, "perimeter", 32); + //Game version required + write_string(out, PERIMETER_VERSION, 32); + write_string(out, roomName, 32); + out < static_cast(hasPasword); + out < static_cast(isClosed); + out < static_cast(gameStarted); + out < playersCount; + out < playersMax; + out < RELAY_TOPOLOGY_SERVER_CLIENT; + //Game specific data for this room + //In this case the scenario name + write_map(out, { + { "scenario", scenarioName }, + { "game_content", std::to_string(gameContent) }, + }, 32, 64, 128); +} + +void NetRelayMessage_PeerJoinRoom::write(XBuffer& out) const { + //Game identifier + write_string(out, "perimeter", 32); + //Game version required + write_string(out, PERIMETER_VERSION, 32); + //Desired room to join + out < room; +} + +void NetRelayMessage_PeerClosePeer::write(XBuffer& out) const { + out < netid; +} + +void NetRelayMessage_PeerPingResponse::write(XBuffer& out) const { + out < secs; + out < subsecs; +} + +void NetRelayMessage_RelayListLobbies::read(XBuffer& in) { + data = in; +} + +void NetRelayMessage_RelayPing::read(XBuffer& in) { + in > secs; + in > subsecs; +} + +void NetRelayMessage_RelayListPeers::read(XBuffer& in) { + uint32_t len = 0; + NETID netid = 0; + in > len; + for (uint32_t i = 0; i < len; ++i) { + in > netid; + this->peers.push_back(netid); + } +} + +void NetRelayMessage_RelayAddPeer::read(XBuffer& in) { + in > netid; +} + +void NetRelayMessage_RelayRemovePeer::read(XBuffer& in) { + in > netid; +} diff --git a/Source/Network/NetRelay.h b/Source/Network/NetRelay.h new file mode 100644 index 000000000..af169eb34 --- /dev/null +++ b/Source/Network/NetRelay.h @@ -0,0 +1,219 @@ +#ifndef PERIMETER_NETRELAY_H +#define PERIMETER_NETRELAY_H + +static const char* NET_RELAY_DEFAULT_ADDRESS = "relay.kdlab.com"; +static const uint16_t NET_RELAY_DEFAULT_PORT = 11654; +static const uint16_t NET_RELAY_MAX_STRING_LENGTH = 256; +static const uint16_t NET_RELAY_MAX_LIST_ELEMENTS = 128; +static const uint16_t NET_RELAY_MAX_MAP_ELEMENTS = 64; +static const uint8_t NET_RELAY_PROTOCOL_VERSION = 1; + +///IDs for messages from/to relay, keep it in sync with relay +enum NetRelayMessageType { + //Common + RELAY_MSG_UNKNOWN = 0, + RELAY_MSG_CLOSE, + + //Sent by peer to relay + RELAY_MSG_PEER_START [[maybe_unused]] = 0x10000, + RELAY_MSG_PEER_LIST_LOBBIES, + RELAY_MSG_PEER_SETUP_ROOM, + RELAY_MSG_PEER_JOIN_ROOM, + RELAY_MSG_PEER_LEAVE_ROOM [[maybe_unused]], + RELAY_MSG_PEER_PING_RESPONSE, + RELAY_MSG_PEER_CLOSE_PEER, + + //Sent by relay to peer + RELAY_MSG_RELAY_START [[maybe_unused]] = 0x20000, + RELAY_MSG_RELAY_LIST_LOBBIES, + RELAY_MSG_RELAY_PING, + RELAY_MSG_RELAY_LIST_PEERS, + RELAY_MSG_RELAY_ADD_PEER, + RELAY_MSG_RELAY_REMOVE_PEER, +}; + +struct NetRelayMessage { + uint8_t protocol_version; + NetRelayMessageType msg_type; + + explicit NetRelayMessage(NetRelayMessageType msg_type) + : protocol_version(NET_RELAY_PROTOCOL_VERSION), msg_type(msg_type) {}; + + virtual ~NetRelayMessage() = default; + + NO_COPY_CONSTRUCTOR(NetRelayMessage); + + virtual void read(XBuffer& in) {}; + virtual void write(XBuffer& out) const {}; + + void read_relay_header(XBuffer& in); + void write_relay_header(XBuffer& out) const; + +protected: + static void write_string(XBuffer& out, const std::string& str, uint16_t max_len); + static void write_list(XBuffer& out, const std::vector& list, uint16_t max_elements, uint16_t max_element_len); + static void write_map(XBuffer& out, const std::unordered_map& list, uint16_t max_elements, uint16_t max_key_len, uint16_t max_value_len); +}; + +///////// Common ///////// + +struct NetRelayMessage_Close : NetRelayMessage { + uint32_t code = 0; + + NetRelayMessage_Close() + : NetRelayMessage(RELAY_MSG_CLOSE) { + } + + void read(XBuffer& in) override; + void write(XBuffer& out) const override; +}; + +///////// Peer to Relay ///////// + +struct NetRelayMessage_PeerListRooms : NetRelayMessage { + static const uint16_t FORMAT_XPRM = 1; + + NetRelayMessage_PeerListRooms() + : NetRelayMessage(RELAY_MSG_PEER_LIST_LOBBIES) { + } + + void write(XBuffer& out) const override; +}; + +struct NetRelayMessage_PeerSetupRoom : NetRelayMessage { + static const uint16_t RELAY_TOPOLOGY_SERVER_CLIENT = 1; + + ///Public name of room to display in list + std::string roomName = {}; + ///Is this room password protected? + bool hasPasword = false; + ///Is this room closed? + bool isClosed = false; + ///Is this game already started? + bool gameStarted = false; + ///Current amount of players this room has + uint16_t playersCount = 0; + ///Max amount of players this room can hold + uint16_t playersMax = 0; + ///Name of mission/scenario to play + std::string scenarioName = {}; + ///Game contents used + GAME_CONTENT gameContent = GAME_CONTENT::CONTENT_NONE; + + NetRelayMessage_PeerSetupRoom() + : NetRelayMessage(RELAY_MSG_PEER_SETUP_ROOM) { + } + + void write(XBuffer& out) const override; +}; + +struct NetRelayMessage_PeerJoinRoom : NetRelayMessage { + NetRoomID room = 0; + + NetRelayMessage_PeerJoinRoom() + : NetRelayMessage(RELAY_MSG_PEER_JOIN_ROOM) { + } + + void write(XBuffer& out) const override; +}; + +struct NetRelayMessage_PeerClosePeer : NetRelayMessage { + NETID netid = 0; + + NetRelayMessage_PeerClosePeer() + : NetRelayMessage(RELAY_MSG_PEER_CLOSE_PEER) { + } + + void write(XBuffer& out) const override; +}; + +struct NetRelayMessage_PeerPingResponse : NetRelayMessage { + uint64_t secs = 0; + uint32_t subsecs = 0; + + NetRelayMessage_PeerPingResponse() + : NetRelayMessage(RELAY_MSG_PEER_PING_RESPONSE) { + } + + void write(XBuffer& out) const override; +}; + +///////// Relay to Peer ///////// + +struct NetRelayMessage_RelayListLobbies : NetRelayMessage { + XBuffer data = XBuffer(0, false); + + NetRelayMessage_RelayListLobbies() + : NetRelayMessage(RELAY_MSG_RELAY_LIST_LOBBIES) { + } + + void read(XBuffer& in) override; +}; + +struct NetRelayMessage_RelayPing : NetRelayMessage { + uint64_t secs = 0; + uint32_t subsecs = 0; + + NetRelayMessage_RelayPing() + : NetRelayMessage(RELAY_MSG_RELAY_PING) { + } + + void read(XBuffer& in) override; +}; + +struct NetRelayMessage_RelayListPeers : NetRelayMessage { + std::vector peers = {}; + + NetRelayMessage_RelayListPeers() + : NetRelayMessage(RELAY_MSG_RELAY_LIST_PEERS) { + } + + void read(XBuffer& in) override; +}; + +struct NetRelayMessage_RelayAddPeer : NetRelayMessage { + NETID netid = 0; + + NetRelayMessage_RelayAddPeer() + : NetRelayMessage(RELAY_MSG_RELAY_ADD_PEER) { + } + + void read(XBuffer& in) override; +}; + +struct NetRelayMessage_RelayRemovePeer : NetRelayMessage { + NETID netid = 0; + + NetRelayMessage_RelayRemovePeer() + : NetRelayMessage(RELAY_MSG_RELAY_ADD_PEER) { + } + + void read(XBuffer& in) override; +}; + +bool getNetRelayAddress(NetAddress& addr); + +bool receiveNetRelayMessage( + NetConnection* relay, + NETID source, + NetRelayMessage* result, + NetRelayMessageType result_type_expected +); + +/** + * Sends a message to relay, with optional result that will be filled with response + * + * @param relay the coneection of relay + * @param msg the message to send + * @param source the source NETID to set the message + * @return + */ +bool sendNetRelayMessage( + NetConnection* relay, + const NetRelayMessage* msg, + NETID source +); + +void closeNetRelay(NetConnection* relay); + +#endif //PERIMETER_NETRELAY_H diff --git a/Source/Network/P2P_interface.h b/Source/Network/P2P_interface.h index bcc73118a..a3638cfdd 100644 --- a/Source/Network/P2P_interface.h +++ b/Source/Network/P2P_interface.h @@ -5,7 +5,7 @@ #include "NetIncludes.h" #include "NetComEventBuffer.h" #include "CommonEvents.h" -#include "ServerList.h" +#include "NetRelay.h" /** @@ -243,11 +243,13 @@ struct sPNCInterfaceCommand { }; enum e_PNCInternalCommand{ - PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST, + //Host modes + PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST, + PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST, - //PNCC_START_FIND_HOST, + //Client modes PNC_COMMAND__CONNECT_2_HOST_AND_STOP_FIND_HOST, - PNC_COMMAND__DISCONNECT_AND_ABORT_GAME_AND_END_START_FIND_HOST, + PNC_COMMAND__CONNECT_2_RELAY_ROOM_AND_STOP_FIND_HOST, //Client back commands PNC_COMMAND__CLIENT_STARTING_LOAD_GAME, @@ -270,9 +272,9 @@ enum e_PNCInternalCommand{ enum e_PNCState{ PNC_STATE__NONE=0, + //Client is polling for server list PNC_STATE__CLIENT_FIND_HOST=1, - PNC_STATE__CONNECTION=2, PNC_STATE__CLIENT_TUNING_GAME=4, PNC_STATE__CLIENT_LOADING_GAME=PNC_State_GameRun|5, PNC_STATE__CLIENT_GAME=PNC_State_GameRun|6, @@ -337,12 +339,30 @@ enum e_PNCStateHost{ PNC_STATE_NEWHOST__WAIT_GAME_DATA=PNC_State_Host|6 }; -struct InputPacket; +enum e_ConnectResult{ + CR_NONE, + CR_OK, + CR_ERR_INCORRECT_SIGNATURE, + CR_ERR_INCORRECT_ARCH, + CR_ERR_INCORRECT_VERSION, + CR_ERR_INCORRECT_CONTENT, + CR_ERR_INCORRECT_CONTENT_FILES, + CR_ERR_INCORRECT_PASWORD, + CR_ERR_GAME_STARTED, + CR_ERR_GAME_FULL +}; + +struct NetConnectionMessage; + +typedef uint64_t arch_flags; class PNetCenter { private: - ServerList serverList; + class ServerList* serverList = nullptr; NetConnectionHandler connectionHandler; + + arch_flags server_arch_mask = 0; + uint32_t server_content_crc = 0; public: std::list internalCommandList; @@ -359,9 +379,6 @@ class PNetCenter { //int m_quantPeriod; size_t m_nextQuantTime; - //Use server listing to get servers or broadcast server to listing - bool publicServerHost; - //bool flag_missionDescriptionUpdate; MissionDescription* hostMissionDescription = nullptr; @@ -394,12 +411,9 @@ class PNetCenter { PNetCenter(); ~PNetCenter(); - void DisconnectAndStartFindHost(); - void StopServerAndStartFindHost(); - - void UpdateBattleData(); void SendBattleData(); + void UpdateCurrentMissionOnRelayRoom(); void UpdateCurrentMissionDescription4C(); void CheckClients(); void DumpClients(); @@ -436,11 +450,15 @@ class PNetCenter { bool ExecuteInternalCommand(e_PNCInternalCommand ic, bool waitExecution); bool ExecuteInterfaceCommand(e_PNCInterfaceCommands ic, std::unique_ptr text = nullptr); - void CreateGame(const NetAddress& connection, const std::string& gameName, MissionDescription* mission, const std::string& playerName, const std::string& password=""); + static const char* getRelayHost(); + void CreateGame(bool isPublicGame, const NetAddress& connection, + const std::string& gameName, MissionDescription* mission, + const std::string& playerName, const std::string& password); - void JoinGame(const NetAddress& connection, const std::string& playerName, const std::string& password=""); + void JoinDirectGame(const NetAddress& connection, const std::string& playerName, const std::string& password); + void JoinPublicRoomGame(const NetAddress& connection, NetRoomID room_id, const std::string& playerName, const std::string& password); - std::vector& getGameHostList(); + const std::vector& getGameList() const; void SendEvent(const netCommandGeneral* event); void SendEventSync(const netCommandGeneral* event); @@ -488,6 +506,7 @@ class PNetCenter { unsigned int m_numberGameQuant; //Кванты на хосте Кванты считаются с 1-цы! void HostReceiveQuant(); + void hostProcessPlayerClientPackets(PClientData* pData); void ClientPredReceiveQuant(); uint64_t last_latency_status = 0; @@ -502,14 +521,8 @@ class PNetCenter { unsigned int TIMEOUT_DISCONNECT; unsigned int MAX_TIME_PAUSE_GAME; - bool isHost(void){ - if(m_state&PNC_State_Host) return 1; - else return 0; - } - bool isGameRun(void){ - if(m_state&PNC_State_GameRun) return 1; - else return 0; - } + bool isHost() { return (m_state&PNC_State_Host) != 0; } + bool isGameRun() { return (m_state&PNC_State_GameRun) != 0; } bool isSaveGame() { return lobbyMissionDescription.gameType_ == GT_MULTI_PLAYER_LOAD; @@ -523,12 +536,10 @@ class PNetCenter { void StartFindHost(); bool Init(); - bool ServerStart(); void SetConnectionTimeout(int ms); void RemovePlayer(NETID netid); void Close(bool flag_immediate=1); - bool Connect(); bool isConnected() const; size_t SendNetBuffer(InOutNetComBuffer* netbuffer, NETID destination); @@ -536,12 +547,18 @@ class PNetCenter { unsigned int flag_LockIputPacket; void LockInputPacket(); void UnLockInputPacket(); + + void ReceivedNetConnectionMessage(NetConnection* connection, NetConnectionMessage* msg); void ClearInputPacketList(); - std::list m_InputPacketList; + std::list m_InputPacketList; bool PutInputPacket2NetBuffer(InOutNetComBuffer& netBuf, NETID& returnNETID); + //Contains both the address to bind when in port listening mode or when connecting to remote host directly NetAddress hostConnection; + + //Client room id to join when connecting to relay + NetRoomID clientRoom = 0; //Host Date size_t hostGeneralCommandCounter; @@ -549,16 +566,6 @@ class PNetCenter { NETID netidClientWhichWeWait; //netid игрока которому хост при миграции посылает команду прислать игровые комманды; нужен чтобы в случае выхода переслать комманду другому - //Host info //TODO originally for gamespy, we should use this for public listed hosts in future - const char* getMissionName(); - const char* getGameName(); - const char* getGameVer(); - int getNumPlayers(); - int getMaxPlayers(); - int getHostPort(); - bool hasPassword() { - return !gamePassword.empty(); - } enum e_perimeterGameState{ PGS_OPEN_WAITING, PGS_CLOSE_WAITING, @@ -592,9 +599,48 @@ class PNetCenter { //Chat void chatMessage(bool clanOnly, const std::string& text, const std::string& locale); - - //NetConnection stuff - void handleIncomingClientConnection(NetConnection* connection); + + //Client handshake + + /** + * Send the connection info to server, this provides the client data and other connection info to server + * to initiate handshake + * + * @param connection the connection to send it to + * @return true if was sent + */ + bool SendClientHandshake(NetConnection* connection) const; + + /** + * Reads the incoming client handshake and adds the player if OK, else is rejected + * + * @param connection the connection where handshake came from + * @param msg contain the data about the client + */ + void ProcessClientHandshake(NetConnection* connection, NetConnectionMessage* msg); + + /** + * Sends the handshake response to client trying to connect + * + * @param connection the connection where handshake came from + * @param netid the netid of client + * @param result the result to send to client + */ + void SendClientHandshakeResponse(NetConnection* connection, NETID netid, e_ConnectResult result); + + /** + * Processes the reply of client handshake that server sent + * + * @param connection + * @param msg + */ + void ProcessClientHandshakeResponse(NetConnection* connection, NetConnectionMessage* msg); + + //Relay + + ///Generates a relay setup room message + ///The returned struct is always same static allocated one so beware of storing it + const NetRelayMessage_PeerSetupRoom& GenerateRelaySetupRoom() const; }; diff --git a/Source/Network/P2P_interface1Th.cpp b/Source/Network/P2P_interface1Th.cpp index 9cd020512..cd11f0f93 100644 --- a/Source/Network/P2P_interface1Th.cpp +++ b/Source/Network/P2P_interface1Th.cpp @@ -1,6 +1,7 @@ #include "NetIncludes.h" #include "P2P_interface.h" +#include "ServerList.h" #include @@ -29,8 +30,6 @@ const char* PNetCenter::getStrState() const return("PNC_STATE__NONE"); case PNC_STATE__CLIENT_FIND_HOST: return("PNC_STATE__CLIENT_FIND_HOST"); - case PNC_STATE__CONNECTION: - return("PNC_STATE__CONNECTION"); case PNC_STATE__CLIENT_TUNING_GAME: return("PNC_STATE__CLIENT_TUNING_GAME"); case PNC_STATE__CLIENT_LOADING_GAME: @@ -76,7 +75,6 @@ in_ClientBuf(PNETCENTER_BUFFER_SIZE, true), out_ClientBuf(PNETCENTER_BUFFER_SIZE in_HostBuf(PNETCENTER_BUFFER_SIZE, true), out_HostBuf(PNETCENTER_BUFFER_SIZE, true), connectionHandler(this) { - publicServerHost=false; hostConnection=NetAddress(); m_hostNETID = m_localNETID = NETID_NONE; @@ -96,6 +94,8 @@ connectionHandler(this) m_clients.reserve(NETWORK_PLAYERS_MAX); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + serverList = new ServerList(); flag_LockIputPacket=0; @@ -199,51 +199,28 @@ PNetCenter::~PNetCenter() for (auto cmd : interfaceCommandList) { delete cmd; } + + if (serverList) { + delete serverList; + serverList = nullptr; + } LogMsg("Destroyed PNetCenter\n"); } -////////////////////////////////////////////// -/// Current game info getters - -const char* PNetCenter::getMissionName() { - if (!isHost()) return ""; - return hostMissionDescription->missionName().c_str(); -} - -const char* PNetCenter::getGameName() { - if (!isHost()) return ""; - return m_GameName.c_str(); -} - -const char* PNetCenter::getGameVer() { - return currentShortVersion; -} - -int PNetCenter::getNumPlayers() { - if(!isHost()) return 0; - return hostMissionDescription->playersAmount(); -} - -int PNetCenter::getMaxPlayers() { - if(!isHost()) return 0; - return hostMissionDescription->playersMaxEasily();//playersAmountScenarioMax(); -} - -int PNetCenter::getHostPort() { - if(!isHost()) return 0; - return hostConnection.addr.port; -} - ////////////////////////////////////////////// /// New network game creation or joining -void PNetCenter::CreateGame(const NetAddress& connection, const std::string& gameName, MissionDescription* mission, const std::string& playerName, const std::string& password) -{ +void PNetCenter::CreateGame( + bool isPublicGame, const NetAddress& connection, + const std::string& gameName, MissionDescription* mission, + const std::string& playerName, const std::string& password +) { LogMsg("Create Game\n"); clientPause=false; clientInPacketPause=false; hostConnection = connection; + clientRoom = 0; gamePassword = password; m_quantInterval=NORMAL_QUANT_INTERVAL; //xm::round((float)NORMAL_QUANT_INTERVAL/gameSpeed); @@ -252,27 +229,47 @@ void PNetCenter::CreateGame(const NetAddress& connection, const std::string& gam xassert(mission); hostMissionDescription = mission; - //Argument PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST + //Arguments for: + // PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST + // PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST m_GameName = gameName; m_PlayerName = playerName; - ExecuteInternalCommand(PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST, true); + ExecuteInternalCommand( + isPublicGame ? PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST + : PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST, + true + ); gameShell->callBack_CreateGameReturnCode(isConnected() ? GameShell::CG_RC_OK : GameShell::CG_RC_CREATE_HOST_ERR); } -void PNetCenter::JoinGame(const NetAddress& connection, const std::string& playerName, const std::string& password) +void PNetCenter::JoinDirectGame(const NetAddress& connection, const std::string& playerName, const std::string& password) { - LogMsg("Join Game\n"); - clientPause = false; - clientInPacketPause=false; + LogMsg("Join Direct Game\n"); + clientPause = false; + clientInPacketPause=false; hostConnection = connection; + clientRoom = 0; gamePassword = password; - //Argument PNC_COMMAND__CONNECT_2_HOST_AND_STOP_FIND_HOST - m_PlayerName = playerName; - //internalIP установлен ранее - ExecuteInternalCommand(PNC_COMMAND__CONNECT_2_HOST_AND_STOP_FIND_HOST, false); + //Argument PNC_COMMAND__CONNECT_2_HOST_AND_STOP_FIND_HOST + m_PlayerName = playerName; + //internalIP установлен ранее + ExecuteInternalCommand(PNC_COMMAND__CONNECT_2_HOST_AND_STOP_FIND_HOST, false); +} + +void PNetCenter::JoinPublicRoomGame(const NetAddress& connection, NetRoomID room_id, const std::string& playerName, const std::string& password) { + LogMsg("Join Public Room Game\n"); + clientPause = false; + clientInPacketPause=false; + hostConnection = connection; + clientRoom = room_id; + gamePassword = password; + + //Argument PNC_COMMAND__CONNECT_2_RELAY_ROOM_AND_STOP_FIND_HOST + m_PlayerName = playerName; + ExecuteInternalCommand(PNC_COMMAND__CONNECT_2_RELAY_ROOM_AND_STOP_FIND_HOST, false); } ////////////////////////////////////////////// @@ -315,12 +312,6 @@ void PNetCenter::HandlerInputNetCommand() } } break; - case NETCOM_4C_ID_JOIN_RESPONSE: - { - //netCommand4C_JoinResponse ncjr(in_ClientBuf); - //m_playerNETID=ncjr.playerNETID_; - } - break; case NETCOM_4C_ID_START_LOAD_GAME: { netCommand4C_StartLoadGame nc4c_sl(in_ClientBuf); @@ -551,7 +542,7 @@ void PNetCenter::P2PIQuant() delete cmd; } - serverList.refreshHostInfoList(); + serverList->refreshHostInfoList(); HandlerInputNetCommand(); } @@ -729,9 +720,8 @@ void PNetCenter::GameIsReady() SendEventSync(&event2); } -std::vector& PNetCenter::getGameHostList() -{ - return serverList.gameHostInfoList; +const std::vector& PNetCenter::getGameList() const { + return serverList->getList(); } ///////////////////////////////////////////////////////////////// @@ -744,4 +734,3 @@ void PNetCenter::StartFindHost() { ExecuteInternalCommand(PNC_COMMAND__START_FIND_HOST, true); } - diff --git a/Source/Network/P2P_interface2Th.cpp b/Source/Network/P2P_interface2Th.cpp index 16f07cb0a..d59b6b24d 100644 --- a/Source/Network/P2P_interface2Th.cpp +++ b/Source/Network/P2P_interface2Th.cpp @@ -2,6 +2,7 @@ #include "Runtime.h" #include "P2P_interface.h" +#include "ServerList.h" #include "NetConnectionAux.h" #include "Universe.h" @@ -15,6 +16,7 @@ #include #endif +const int PNC_MIN_SLEEP_TIME = 10; //millis const unsigned int MAX_TIME_WAIT_RESTORE_GAME_AFTER_MIGRATE_HOST=10000;//10sec const int PNC_DESYNC_RESTORE_ATTEMPTS = 8; const int PNC_DESYNC_RESTORE_MODE_PARTIAL = 0; //2; TODO set back once partial load is finished @@ -122,19 +124,36 @@ bool PNetCenter::SecondThread() break; case PNC_COMMAND__CONNECT_2_HOST_AND_STOP_FIND_HOST: + case PNC_COMMAND__CONNECT_2_RELAY_ROOM_AND_STOP_FIND_HOST: { flag_LockIputPacket=0; flag_SkipProcessingGameCommand=0; ClearInputPacketList(); clearInOutClientHostBuffers(); m_bStarted = false; - if(m_state==PNC_STATE__CLIENT_FIND_HOST){ - m_state=PNC_STATE__CONNECTION; - } - else xassert(0&&"Connecting: command order error(not find host state)"); + m_hostNETID = m_localNETID = NETID_NONE; + NetConnection* connection = nullptr; + if (curInternalCommand == PNC_COMMAND__CONNECT_2_RELAY_ROOM_AND_STOP_FIND_HOST) { + LogMsg("SendClientHandshake Room to: %s room %" PRIX64 "\n", hostConnection.getString().c_str(), clientRoom); + connection = connectionHandler.startRelayRoomConnection(hostConnection, clientRoom); + } else { + LogMsg("SendClientHandshake Direct to: %s\n", hostConnection.getString().c_str()); + connection = connectionHandler.startDirectConnection(hostConnection); + m_hostNETID = NETID_HOST; + m_localNETID = NETID_NONE; + } + flag_connected = SendClientHandshake(connection); + if (isConnected()) { + m_state = PNC_STATE__CLIENT_TUNING_GAME; + } else { + Close(); + } + SetEvent(hCommandExecuted); + break; } break; case PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST: + case PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST: { flag_LockIputPacket=0; flag_SkipProcessingGameCommand=0; @@ -142,38 +161,50 @@ bool PNetCenter::SecondThread() clearInOutClientHostBuffers(); m_bStarted = false; - ////if(WaitForSingleObject(hStartServer, INFINITE) != WAIT_OBJECT_0) xassert(0&&"Network server: run error."); - m_state=PNC_STATE__HOST_TUNING_GAME; //Необходимо для DPN_MSGID_ENUM_HOSTS_QUERY чтоб сразу выдавал правильную инфу - serverList.stopHostFind(); + m_state=PNC_STATE__HOST_TUNING_GAME; - LogMsg("starting server...\n"); - if(!isConnected()) { - //m_pConnection->Init(); - if (ServerStart()) { - LogMsg("...started OK\n"); - } + if(isConnected()) { + Close(); } - // pNewGame->AddClient(nccg.createPlayerData_, 0/*netid*/, nccg.computerName_); - // pNewGame->StartGame(); - - ///hostGUIDInstance=getHostGUIDInstance(); - ///m_netidGroupGame = m_pConnection->CreateGroup(); + LogMsg("New game <%s> for start...\n", m_GameName.c_str()); + + LogMsg("Starting server...\n"); + bool isPublic = curInternalCommand == PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST; + m_hostNETID = m_localNETID = NETID_NONE; + flag_connected = connectionHandler.startHost(hostConnection.port(), isPublic); - ////SetEvent(hServerReady); - - ClearClients(); - PlayerData pd; - pd.set(m_PlayerName, m_localNETID); - if(AddClient(pd)==-1){ - ErrH.Abort("Network: Couldnt add host player to mission"); - } - - LogMsg("New game <%s> for start...\n", m_GameName.c_str()); + if (!isConnected()) { + fprintf(stderr, "...error!\n"); + } else { + if (!isPublic) { + //If not public we assign NETIDs ourselves + m_hostNETID = m_localNETID = NETID_HOST; + } + serverList->stopFind(); + LogMsg("...started OK\n"); + + ClearClients(); + PlayerData pd; + pd.set(m_PlayerName, m_localNETID); + if (AddClient(pd) == -1) { + if (hostMissionDescription->gameType_ == GT_MULTI_PLAYER_LOAD) { + //Try forcing first player as open to allocate host + if (0 < hostMissionDescription->playersAmountScenarioMax()) { + hostMissionDescription->playersData[0].realPlayerType = REAL_PLAYER_TYPE_OPEN; + } + if (AddClient(pd) == -1) { + ErrH.Abort("Network: Couldnt add host player to saved scenario"); + } + } else { + ErrH.Abort("Network: Couldnt add host player to scenario"); + } + } - hostMissionDescription->clearAllPlayerGameReady(); - hostMissionDescription->setChanged(); - } + hostMissionDescription->clearAllPlayerGameReady(); + hostMissionDescription->setChanged(); + } + } SetEvent(hCommandExecuted); break; case PNC_COMMAND__DESYNC: @@ -189,8 +220,6 @@ bool PNetCenter::SecondThread() break; case PNC_COMMAND__END_GAME: { - m_state=PNC_STATE__ENDING_GAME; - m_bStarted = false; if(isConnected()) { if(isHost()){ //Гарантированная отсылка последнего кванта @@ -202,28 +231,17 @@ bool PNetCenter::SecondThread() netCommand4G_Exit ex(m_localNETID); SendEvent(ex, NETID_ALL); } + m_state=PNC_STATE__ENDING_GAME; + m_bStarted = false; } break; case PNC_COMMAND__START_FIND_HOST: { - serverList.startHostFind(); + serverList->startFind(); m_state=PNC_STATE__CLIENT_FIND_HOST; } SetEvent(hCommandExecuted); break; - case PNC_COMMAND__DISCONNECT_AND_ABORT_GAME_AND_END_START_FIND_HOST: { - m_bStarted = false; - if(isConnected()) { - Close(); - } - if (m_state == PNC_STATE__CONNECTION) { - //Avoid calling connect twice until PNC_COMMAND__START_FIND_HOST is processed - m_state = PNC_STATE__CLIENT_FIND_HOST; - } - ExecuteInternalCommand(PNC_COMMAND__START_FIND_HOST, false); - SetEvent(hCommandExecuted); - break; - } case PNC_COMMAND__END: { flag_end=true; @@ -255,20 +273,18 @@ bool PNetCenter::SecondThread() //Host/Client quant if (isHost()) { + connectionHandler.acceptConnection(); HostReceiveQuant(); } else { ClientPredReceiveQuant(); } + } else { + serverList->fetchRelayHostInfoList(); } delete _pLock; if(flag_end) break; //для быстрого выхода - - //Accept any new incoming connections - if (flag_connected && isHost()) { - connectionHandler.acceptConnection(); - } //Logic quant @@ -284,13 +300,14 @@ bool PNetCenter::SecondThread() curTime=clocki(); - if(minWakingTime > curTime){ - Sleep(minWakingTime-curTime); + uint32_t sleepTime = minWakingTime > curTime ? minWakingTime - curTime : 0; + if (0 < sleepTime) { + Sleep(min(sleepTime, PNC_MIN_SLEEP_TIME)); } //end logic quant } - serverList.stopHostFind(); + serverList->stopFind(); SetConnectionTimeout(1);//Для быстрого завершения //if(m_pConnection->Connected()) m_pConnection->Close(); @@ -303,6 +320,36 @@ bool PNetCenter::SecondThread() return false; } +bool PNetCenter::Init() +{ + connectionHandler.reset(); + + SetConnectionTimeout(TIMEOUT_DISCONNECT); + + m_hostNETID = m_localNETID = NETID_NONE; + + server_arch_mask = 0xFFFE; //Allow different OSes and compilers + const char* server_arch_mask_str = check_command_line("ServerArchMask"); + if (server_arch_mask_str) { + server_arch_mask = strtoull(server_arch_mask_str, nullptr, 16); + } + + //Reset our attributes in case we played before + loadUnitAttributes(false, nullptr); + + server_content_crc = get_content_crc(); + +#if defined(PERIMETER_DEBUG) && defined(PERIMETER_EXODUS) && 0 + //Dump current attrs + XPrmOArchive ar("/tmp/test"); + XBuffer& buf = ar.buffer(); + ar << makeObjectWrapper(rigidBodyPrmLibrary(), nullptr, nullptr); + ar.close(); +#endif + + return true; +} + void PNetCenter::SendEvent(netCommandGeneral& event, NETID destination) { if (destination != m_localNETID ) { @@ -336,16 +383,6 @@ void PNetCenter::LLogicQuant() switch(m_state) { - case PNC_STATE__CONNECTION: - if(!Connect()) { - ExecuteInternalCommand(PNC_COMMAND__DISCONNECT_AND_ABORT_GAME_AND_END_START_FIND_HOST, false); - //ErrH.Abort("Unable to find multiplayer server"); - } else { - serverList.stopHostFind(); - m_state = PNC_STATE__CLIENT_TUNING_GAME; - SetEvent(hCommandExecuted); - } - break; case PNC_STATE__HOST_TUNING_GAME: { CAutoLock _lock(m_GeneralLock); //! Lock @@ -411,7 +448,7 @@ void PNetCenter::LLogicQuant() if (flag_ready) { CheckClients(); DumpClients(); - + UpdateCurrentMissionOnRelayRoom(); ///terEventBeginCommand ev_begin(m_nQuantDelay/100, 0); ///SendEvent(ev_begin, m_netidGroupGame); @@ -467,7 +504,7 @@ void PNetCenter::LLogicQuant() if (max_quant < quant) { max_quant = quant; } - if (client->netidPlayer == NETID_HOST) { + if (client->netidPlayer == m_localNETID) { ev.info.quant = quant; } } @@ -497,7 +534,7 @@ void PNetCenter::LLogicQuant() //This forces desync every 20 quants, don't enable unless debugging desyncs if (quantConfirmation > 20) { client_desync = true; - if (client->netidPlayer != m_hostNETID) { + if (client->netidPlayer != m_localNETID) { (*secondList.begin()).signature_ = 0; } } @@ -651,7 +688,10 @@ end_while_01:; //Check if any client should be removed, only one per quant! for (auto& client : m_clients) { if ((client->requestPause && ((clocki()-client->timeRequestPause) > MAX_TIME_PAUSE_GAME)) - || (!hostPause && client->netidPlayer != NETID_HOST && (client->last_time_latency_response + (TIMEOUT_DISCONNECT * 1000) < clock_us()))) { + || (!hostPause && client->netidPlayer != m_hostNETID + && (client->last_time_latency_response + (TIMEOUT_DISCONNECT * 1000) < clock_us()) + ) + ) { fprintf(stdout, "Removing non responding player: 0x%" PRIX64 " %s\n", client->netidPlayer, client->playerName); RemovePlayer(client->netidPlayer); @@ -675,7 +715,7 @@ end_while_01:; //перенесение всех команд удаления в список комманд на выполнение for(auto p=m_QueuedGameCommands.begin(); p != m_QueuedGameCommands.end(); p++){ - PutGameCommand2Queue_andAutoDelete(NETID_HOST, *p); + PutGameCommand2Queue_andAutoDelete(m_hostNETID, *p); } m_QueuedGameCommands.clear(); @@ -1106,7 +1146,7 @@ end_while_01:; break; case PNC_STATE__ENDING_GAME: { - serverList.stopHostFind(); + serverList->stopFind(); Close(false); CAutoLock _lock(m_GeneralLock); //! Lock ClearClients(); @@ -1133,34 +1173,29 @@ void PNetCenter::ClientPredReceiveQuant() if(flag_LockIputPacket) return; //return 0; //int cnt=0; - std::list::iterator p=m_InputPacketList.begin(); + std::list::iterator p=m_InputPacketList.begin(); while(p != m_InputPacketList.end()){ - InputPacket* packet = *p; - if(packet->netid==m_hostNETID){ - - //отфильтровывание команды - InOutNetComBuffer tmp(packet->address(),packet->tell()); - int cmd = tmp.currentNetCommandID(); - if (cmd == NETCOM_4C_ID_START_LOAD_GAME) { - ExecuteInternalCommand(PNC_COMMAND__CLIENT_STARTING_LOAD_GAME, false); - } else if (cmd == NETCOM_ID_NEXT_QUANT) { - if (m_state == PNC_STATE__CLIENT_RESTORE_GAME_AFTE_CHANGE_HOST_PHASE_AB) { - m_state=PNC_STATE__CLIENT_GAME; - } + NetConnectionMessage* packet = *p; +#if 0 + //отфильтровывание команды + InOutNetComBuffer tmp(packet->address(),packet->tell()); + int cmd = tmp.currentNetCommandID(); + if (cmd == NETCOM_4C_ID_START_LOAD_GAME) { + ExecuteInternalCommand(PNC_COMMAND__CLIENT_STARTING_LOAD_GAME, false); + } else if (cmd == NETCOM_ID_NEXT_QUANT) { + if (m_state == PNC_STATE__CLIENT_RESTORE_GAME_AFTE_CHANGE_HOST_PHASE_AB) { + m_state=PNC_STATE__CLIENT_GAME; } + } +#endif - //комманды клиенту - if (in_ClientBuf.putBufferPacket(packet->address(), packet->tell())) { - delete packet; - p=m_InputPacketList.erase(p); - //cnt++; - } else { - break; - } - } else { - fprintf(stderr, "Received packet from non-host! %" PRIX64 "\n", packet->netid); + //комманды клиенту + if (in_ClientBuf.putBufferPacket(packet->address(), packet->tell())) { delete packet; p=m_InputPacketList.erase(p); + //cnt++; + } else { + break; } } ///return (cnt!=0); diff --git a/Source/Network/P2P_interface2ThDPF.cpp b/Source/Network/P2P_interface2ThDPF.cpp deleted file mode 100644 index de10af2ad..000000000 --- a/Source/Network/P2P_interface2ThDPF.cpp +++ /dev/null @@ -1,1049 +0,0 @@ -#include "NetIncludes.h" -#include "P2P_interface.h" - - -#include "GameShell.h" -#include "Universe.h" - -#include "../Terra/terra.h" - -#include - -#include "dxerr9.h" - -#include "NetConnectionAux.h" - -WCHAR wstrSession[] = L"Session1"; -WCHAR wstrPlayer[] = L"Player1"; - -//LPCTSTR lpszSignatureRQ = "Perimeter MP client. Version 3"; -//LPCTSTR lpszSignatureRPL = "Perimeter MP server. Version 3"; - - -#define XDP_CHECK_HR(hr, msg) {if(FAILED(hr)) ErrH.Abort(msg);} - -#define NO_PERIMETR_DEFAULT_PORT - -//#define IP1(x) ((x>>24) & 0xff) -//#define IP2(x) ((x>>16) & 0xff) -//#define IP3(x) ((x>>8) & 0xff) -//#define IP4(x) (x & 0xff) - -#define IP1(x) (x & 0xff) -#define IP2(x) ((x>>8) & 0xff) -#define IP3(x) ((x>>16) & 0xff) -#define IP4(x) ((x>>24) & 0xff) - -const unsigned int PERIMETER_MAX_NETWORK_PLAYER=20; -// {A0B71D62-8E88-4767-8439-184327615B42} -const GUID guidPerimeterGame = -{ 0xa0b71d62, 0x8e88, 0x4767, { 0x84, 0x39, 0x18, 0x43, 0x27, 0x61, 0x5b, 0x42 } }; - -//#define INITGUID -#include "objbase.h" -#include "initguid.h" -#include "../../MSDXSDK_02_06/dp8sim.h" - - -//HRESULT WINAPI DirectPlayMessageHandler(PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer) -//{ -// return ((XDPConnection*)pvUserContext)->DirectPlayMessageHandler(dwMessageId, pMsgBuffer); -//} - - -////////////////////////////////////////////////////////////////////// -// - -/* -CRITICAL_SECTION _csMemLock; - -void* XDPmalloc(int size) -{ - CAutoLock _lock(_csMemLock); - - return new char[size]; -} -void XDPfree(void* p) -{ - CAutoLock _lock(_csMemLock); - - delete p; -} - - -void* operator new(size_t nSize) -{ - return XDPmalloc(nSize); -} -void operator delete(void *p) -{ - XDPfree(p); -} -*/ - -////////////////////////////////////////////////////////////////////// - -void XDPInit() -{ - CoInitializeEx(0, COINIT_MULTITHREADED); - - //InitializeCriticalSection(&_csMemLock); -} -void XDPClose() -{ - CoUninitialize(); - - //DeleteCriticalSection(&_csMemLock); -} - -/////////////////////////////////////////////////////////////////////// -// XDPConnection -/* -XDPConnection::XDPConnection(PFNDPNMESSAGEHANDLER pfn, void* pData) -{ - - - ///m_internal_queue[XDP_NETID_PLAYER_GENERAL] = PacketListType(); - - - - -} -XDPConnection::~XDPConnection() -{ - Close(); - - ///m_internal_queue.clear(); -} -*/ - -bool PNetCenter::Init() -{ - HRESULT hr; -// hr = CoCreateInstance(CLSID_DirectPlay8Peer, NULL, -// CLSCTX_INPROC_SERVER, -// IID_IDirectPlay8Peer, -// (LPVOID*) &m_pDPServer); - hr = CoCreateInstance(CLSID_DirectPlay8Peer, NULL, - CLSCTX_INPROC_SERVER, - IID_IDirectPlay8Peer, - (LPVOID*) &m_pDPPeer); - XDP_CHECK_HR(hr, "CoCreateInstance"); - if(hr!=S_OK) return 0; - - //m_pDPClient=m_pDPServer;//Пока - - // Init IDirectPlay8Server - uint32_t dwFlags=0; - if(workMode==PNCWM_LAN) dwFlags|=DPNINITIALIZE_HINT_LANSESSION; - hr = m_pDPPeer->Initialize(this, ::DirectPlayMessageHandler, dwFlags); - XDP_CHECK_HR(hr, "Initialize"); - if(hr!=S_OK) return 0; - - - DPN_SP_CAPS dpnspCaps; - memset(&dpnspCaps, 0, sizeof(DPN_SP_CAPS)); - dpnspCaps.dwSize = sizeof(DPN_SP_CAPS); - hr = m_pDPPeer->GetSPCaps(&CLSID_DP8SP_TCPIP, &dpnspCaps, 0); - if ( FAILED( hr ) ) { - //DXTRACE_ERR_MSGBOX( TEXT("GetSPCaps"), hr ); - return 0; - } - //if(dpnspCaps.dwFlags&DPNSPCAPS_SUPPORTSDPNSRV) ;// & DPNSPCAPS_SUPPORTSALLADAPTERS - - - //Save DP connection default - DPN_CAPS_EX caps_ex; - caps_ex.dwSize=sizeof(DPN_CAPS_EX); - m_pDPPeer->GetCaps((DPN_CAPS*)&caps_ex,0); - IniManager("Network.ini").putInt("DPInfo", "ConnectTimeout", caps_ex.dwConnectTimeout); - IniManager("Network.ini").putInt("DPInfo", "ConnectRetries", caps_ex.dwConnectRetries); - - int connectTimeout; - int connectRetries; - const char* section= (workMode==PNCWM_LAN) ? "LANSet" : "INETSet"; - if(IniManager("Network.ini", false).getInt(section, "ConnectTimeout", connectTimeout) && - IniManager("Network.ini", false).getInt(section, "ConnectRetries", connectRetries)){ - - caps_ex.dwConnectTimeout=connectTimeout; - caps_ex.dwConnectRetries=connectRetries; - m_pDPPeer->SetCaps((DPN_CAPS*)&caps_ex, 0); - } - - SetConnectionTimeout(TIMEOUT_DISCONNECT); //30s //3600000 - - return 1; -} - -int PNetCenter::ServerStart(const char* _name, int port) -{ - //m_mode = DP_SERVER; - - HRESULT hr; - PDIRECTPLAY8ADDRESS pDP8AddrLocal = NULL; - - // Create IDirectPlay8Server -// hr = CoCreateInstance(CLSID_DirectPlay8Server, NULL, -// CLSCTX_INPROC_SERVER, -// IID_IDirectPlay8Server, -// (LPVOID*) &m_pDPServer); -/// hr = CoCreateInstance(CLSID_DirectPlay8Peer, NULL, -/// CLSCTX_INPROC_SERVER, -/// IID_IDirectPlay8Peer, -/// (LPVOID*) &m_pDPServer); -/// XDP_CHECK_HR(hr, "CoCreateInstance"); -/// -/// // Init IDirectPlay8Server -/// hr = m_pDPServer->Initialize(this, ::DirectPlayMessageHandler, 0 ); -/// XDP_CHECK_HR(hr, "Initialize"); - - hr = CoCreateInstance(CLSID_DirectPlay8Address, NULL, - CLSCTX_ALL, IID_IDirectPlay8Address, - (LPVOID*) &pDP8AddrLocal); - XDP_CHECK_HR(hr, "CoCreateInstance"); - - - if(flag_NetworkSimulation) - hr=pDP8AddrLocal->SetSP(&CLSID_NETWORKSIMULATOR_DP8SP_TCPIP); - else - hr=pDP8AddrLocal->SetSP(&CLSID_DP8SP_TCPIP); - XDP_CHECK_HR(hr, "SetSP"); - - - // Add the port to pDP8AddrLocal, if the port is non-zero. -/* if(port) { -#ifndef NO_PERIMETR_DEFAULT_PORT - hr = pDP8AddrLocal->AddComponent(DPNA_KEY_PORT, &port, sizeof(port), DPNA_DATATYPE_DWORD); - XDP_CHECK_HR(hr, "AddComponent"); -#endif - }*/ - if(m_dwPort) { - hr = pDP8AddrLocal->AddComponent(DPNA_KEY_PORT, &m_dwPort, sizeof(m_dwPort), DPNA_DATATYPE_DWORD); - //XDP_CHECK_HR(hr, "AddComponent"); - if( FAILED(hr) ){ - DXTRACE_ERR_MSGBOX( TEXT("PNetCenter::ServerStart-AddComponent_PORT"), hr ); - } - } - - ///DWORD dwTraversalMode = DPNA_TRAVERSALMODE_PORTRECOMMENDED; - ///hr = pDP8AddrLocal->AddComponent(DPNA_KEY_TRAVERSALMODE, &dwTraversalMode, sizeof(dwTraversalMode), DPNA_DATATYPE_DWORD); - - - - WCHAR* pSessionName = new WCHAR[strlen(_name) + 1]; - MultiByteToWideChar(CP_ACP, 0, _name, -1, pSessionName, strlen(_name) + 1); - - - DPN_APPLICATION_DESC dpnAppDesc; - ZeroMemory(&dpnAppDesc, sizeof(DPN_APPLICATION_DESC)); - dpnAppDesc.dwSize = sizeof( DPN_APPLICATION_DESC ); - dpnAppDesc.dwFlags = 0;//DPNSESSION_CLIENT_SERVER; - ///dpnAppDesc.dwFlags |= DPNSESSION_MIGRATE_HOST; - ///dpnAppDesc.dwFlags |= DPNSESSION_NODPNSVR; - if(m_DPSigningLevel==1) dpnAppDesc.dwFlags|= DPNSESSION_FAST_SIGNED; - else if(m_DPSigningLevel==2) dpnAppDesc.dwFlags|= DPNSESSION_FULL_SIGNED; - if(flag_HostMigrate) dpnAppDesc.dwFlags |= DPNSESSION_MIGRATE_HOST; - if(flag_NoUseDPNSVR) dpnAppDesc.dwFlags |= DPNSESSION_NODPNSVR; - dpnAppDesc.guidApplication = guidPerimeterGame; - dpnAppDesc.pwszSessionName = pSessionName; - dpnAppDesc.dwMaxPlayers = PERIMETER_MAX_NETWORK_PLAYER; - - //hr = m_pDPServer->Host(&dpnAppDesc, &pDP8AddrLocal, 1, NULL, NULL, NULL, DPNHOST_OKTOQUERYFORADDRESSING ); - hr = m_pDPPeer->Host(&dpnAppDesc, &pDP8AddrLocal, 1, NULL, NULL, NULL, DPNHOST_OKTOQUERYFORADDRESSING ); - XDP_CHECK_HR(hr, "Can't start server"); - flag_connected=1; - - delete [] pSessionName; - - pDP8AddrLocal->Release(); - - //for internet получение адреса host-а -/* IDirectPlay8Address *pDP8AddressHost = NULL; - DWORD dwNumAddresses = 1; - - hr = m_pDPPeer->GetLocalHostAddresses(&pDP8AddressHost, &dwNumAddresses, DPNGETLOCALHOSTADDRESSES_COMBINED); - - - char *szHostAddress = NULL; - DWORD dwNumHostAddressChars = 0; - hr = pDP8AddressHost->GetURLA(NULL, &dwNumHostAddressChars); - szHostAddress = new char [dwNumHostAddressChars];// LocalAlloc(LPTR, dwNumHostAddressChars * sizeof(char)); - hr = pDP8AddressHost->GetURLA(szHostAddress, &dwNumHostAddressChars); -*/ - - - return 1; -} -//int XDPConnection::Started() -//{ -// return m_mode == DP_SERVER; -//} - -GUID PNetCenter::getHostGUIDInstance() -{ - HRESULT hr; - uint32_t dwSize = 0; - //hr = m_pDPServer->GetApplicationDesc( NULL, &dwSize, NULL ); - hr = m_pDPPeer->GetApplicationDesc( NULL, &dwSize, NULL ); - - - // DirectPlay should return BufferTooSmall to give the correct allocation size -// if( hr != DPNERR_BUFFERTOOSMALL ) -// return hr; - - // Allocate the memory - DPN_APPLICATION_DESC* pAppDesc = (DPN_APPLICATION_DESC*) new uint8_t[ dwSize ]; - -// if( NULL == pAppDesc ) -// return E_OUTOFMEMORY; - - // Initialize the struct - ZeroMemory( pAppDesc, dwSize ); - pAppDesc ->dwSize = sizeof(DPN_APPLICATION_DESC); - - // Get group info - //hr = m_pDPServer->GetApplicationDesc( pAppDesc, &dwSize, NULL ); - hr = m_pDPPeer->GetApplicationDesc( pAppDesc, &dwSize, NULL ); - - return pAppDesc->guidInstance; -} - - -void PNetCenter::SetConnectionTimeout(int ms) -{ -/* DPN_CAPS caps; - caps.dwSize = sizeof(DPN_CAPS); - - switch(m_mode) { - case DP_SERVER: - m_pDPServer->GetCaps(&caps, 0); - caps.dwTimeoutUntilKeepAlive = ms; - caps.dwConnectRetries =1; - m_pDPServer->SetCaps(&caps, 0); - break; - - case DP_CLIENT: - m_pDPClient->GetCaps(&caps, 0); - caps.dwTimeoutUntilKeepAlive = ms; - m_pDPClient->SetCaps(&caps, 0); - break; - } -*/ - //m_pDPPeer->GetCaps(&caps, 0); - //caps.dwTimeoutUntilKeepAlive = ms; - //caps.dwConnectRetries =1; - //m_pDPPeer->SetCaps(&caps, 0); - - const int MaxSendRetryInterval=5000;//Default direct play retry interval - const int NumSendRetry=ms/MaxSendRetryInterval; - const int TimeoutUntilKeepAlive=MaxSendRetryInterval*NumSendRetry/2; - DPN_CAPS_EX caps_ex; - caps_ex.dwSize=sizeof(DPN_CAPS_EX); - m_pDPPeer->GetCaps((DPN_CAPS*)&caps_ex,0); - caps_ex.dwNumSendRetries=NumSendRetry; - caps_ex.dwMaxSendRetryInterval=MaxSendRetryInterval; - caps_ex.dwTimeoutUntilKeepAlive=TimeoutUntilKeepAlive; - m_pDPPeer->SetCaps((DPN_CAPS*)&caps_ex, 0); - -} -int PNetCenter::GetConnectionTimeout(void) -{ - //DPN_CAPS caps; - //caps.dwSize = sizeof(DPN_CAPS); - //m_pDPPeer->GetCaps(&caps, 0); - //return caps.dwTimeoutUntilKeepAlive; - DPN_CAPS_EX caps_ex; - caps_ex.dwSize=sizeof(DPN_CAPS_EX); - m_pDPPeer->GetCaps((DPN_CAPS*)&caps_ex,0); - return caps_ex.dwTimeoutUntilKeepAlive; -} - -/*void XDPConnection::GetActivePlayers(list& players) -{ - if(m_mode == DP_SERVER) - { - HRESULT hr; - DWORD dwNumPlayers = 0; - NETID* aPlayers = 0; - - // Enumerate all the connected players - while(1) - { - //hr = m_pDPServer->EnumPlayersAndGroups( aPlayers, &dwNumPlayers, DPNENUM_PLAYERS ); - hr = m_pDPPeer->EnumPlayersAndGroups( aPlayers, &dwNumPlayers, DPNENUM_PLAYERS ); - - if(SUCCEEDED(hr)) - break; - - if(FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL) - return; - - if(aPlayers) - delete aPlayers; - - aPlayers = new NETID[dwNumPlayers]; - } - - for(int i=0; iDestroyClient(netid, 0, 0, 0); - //HRESULT hr = m_pDPServer->DestroyPeer(netid, 0, 0, 0); - if(isHost() && netid==m_localNETID && netid==m_hostNETID){ - ExecuteInternalCommand(PNC_COMMAND__END_GAME, false); - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_HOST_TERMINATED_GAME); - } - else { - char destroyInfo[]={'t', '1', 0}; - HRESULT hr = m_pDPPeer->DestroyPeer(netid, destroyInfo, sizeof(destroyInfo), 0); - if( FAILED( hr ) ) - DXTRACE_ERR_MSGBOX( TEXT("DestroyPeer(RemovePlayer)"), hr ); - } - - - /*int k; - if(hr==DPNERR_INVALIDPARAM) - k=0; - else if(hr==DPNERR_INVALIDPLAYER) - k=1; - else if(hr==DPNERR_NOTHOST) - k=2; - else if(hr==S_OK) - k=3;*/ - - } - -} -/* -NETID XDPConnection::CreateGroup() -{ - if(m_mode == DP_SERVER) - { - DPN_GROUP_INFO dgi; - memset(&dgi, 0, sizeof(DPN_GROUP_INFO)); - dgi.dwSize = sizeof(DPN_GROUP_INFO); - dgi.dwGroupFlags = DPNGROUP_AUTODESTRUCT; - - m_netidGroupCreating = -1; - //if(SUCCEEDED(m_pDPServer->CreateGroup(&dgi, 0, 0, 0, DPNCREATEGROUP_SYNC))) return m_netidGroupCreating; - if(SUCCEEDED(m_pDPPeer->CreateGroup(&dgi, 0, 0, 0, DPNCREATEGROUP_SYNC))) return m_netidGroupCreating; - - } - - return 0; -} -void XDPConnection::DestroyGroup(NETID netid) -{ - //if(m_mode == DP_SERVER) m_pDPServer->DestroyGroup(netid, 0, 0, DPNDESTROYGROUP_SYNC); - if(m_mode == DP_SERVER) m_pDPPeer->DestroyGroup(netid, 0, 0, DPNDESTROYGROUP_SYNC); - -} -void XDPConnection::AddPlayerToGroup(NETID group, NETID player) -{ - if(m_mode == DP_SERVER) { - DPNHANDLE hAsync; - //m_pDPServer->AddPlayerToGroup(group, player, 0, &hAsync, 0); - m_pDPPeer->AddPlayerToGroup(group, player, 0, &hAsync, 0); - } -} -void XDPConnection::DelPlayerFromGroup(NETID group, NETID player) -{ - if(m_mode == DP_SERVER) { - DPNHANDLE hAsync; - //m_pDPServer->RemovePlayerFromGroup(group, player, 0, &hAsync, 0); - m_pDPPeer->RemovePlayerFromGroup(group, player, 0, &hAsync, 0); - } -}*/ - -void PNetCenter::SetServerInfo(void* pb, int sz) -{ -// if(m_mode == DP_SERVER) - { -/* DPN_PLAYER_INFO dpi; - memset(&dpi, 0, sizeof(DPN_PLAYER_INFO)); - dpi.dwSize = sizeof(DPN_PLAYER_INFO); - dpi.dwInfoFlags = DPNINFO_DATA; - dpi.pvData = pb; - dpi.dwDataSize = sz; - - DPNHANDLE hAsync; - - HRESULT hr = m_pDPServer->SetServerInfo(&dpi, 0, &hAsync, 0); - if(hr != DPNSUCCESS_PENDING) - ErrH.Abort("Cant set server info!"); -*/ - } -} -int PNetCenter::GetServerInfo(void* pb) -{ -/* if(m_mode != DP_CLIENT) - return 0; - - DWORD dwSize = 0; - - HRESULT hr = m_pDPClient->GetServerInfo(0, &dwSize, 0); - - DPN_PLAYER_INFO* pDpi = new DPN_PLAYER_INFO[dwSize]; - pDpi->dwSize = sizeof(DPN_PLAYER_INFO); - - m_pDPClient->GetServerInfo(pDpi, &dwSize, 0); - - memcpy(pb, pDpi->pvData, dwSize); - delete pDpi; - - return dwSize; -*/ - return 0; -} - -bool PNetCenter::GetConnectionInfo(DPN_CONNECTION_INFO& info) -{ -/* if(!Connected() || (m_mode != DP_CLIENT)) - return 0; - - memset(&info, 0, sizeof(DPN_CONNECTION_INFO)); - info.dwSize = sizeof(DPN_CONNECTION_INFO); - - return SUCCEEDED(m_pDPClient->GetConnectionInfo(&info, 0)); -*/ - return 0; -} - -void PNetCenter::Close(bool flag_immediatle) -{ -/* switch(m_mode) { - case DP_SERVER: - m_pDPServer->Close(DPNCLOSE_IMMEDIATE); - RELEASE(m_pDPServer); - break; - case DP_CLIENT: - m_pDPClient->Close(DPNCLOSE_IMMEDIATE); - RELEASE(m_pDPClient); - break; - } - RELEASE(m_pDPServer);*/ - if(flag_immediatle) m_pDPPeer->Close(DPNCLOSE_IMMEDIATE); - else m_pDPPeer->Close(0); - RELEASE(m_pDPPeer); - - //m_mode = DP_NOBODY; - flag_connected=0; -} - -int PNetCenter::Connect(GUID _hostID) //const char* lpszHost, int port) -{ - //m_mode = DP_CLIENT; - - HRESULT hr; - - m_nClientSgnCheckError = 0; - - // Create IDirectPlay8Client -// hr = CoCreateInstance(CLSID_DirectPlay8Client, 0, -// CLSCTX_INPROC_SERVER, -// IID_IDirectPlay8Client, -// (LPVOID*) &m_pDPClient); -/// hr = CoCreateInstance(CLSID_DirectPlay8Peer, 0, -/// CLSCTX_INPROC_SERVER, -/// IID_IDirectPlay8Peer, -/// (LPVOID*) &m_pDPClient); -/// XDP_CHECK_HR(hr, "CoCreateInstance"); -/// -/// // Init IDirectPlay8Client -/// hr = m_pDPClient->Initialize(this, ::DirectPlayMessageHandler, 0); -/// XDP_CHECK_HR(hr, "Initialize"); - -// FindHost(lpszHost); -// if(!m_pLastHostFound) -// return 0; - - INTERNAL_HOST_ENUM_INFO* pHostFound=0; - std::vector::iterator p; - - { - CAutoLock _lock(m_GeneralLock); //! Lock - for(p=internalFoundHostList.begin(); p!=internalFoundHostList.end(); p++){ - if((*p)->pAppDesc->guidInstance==_hostID) { - pHostFound=*p; - break; - } - } - } - if(p==internalFoundHostList.end()) { -// FindHost(""); -// CAutoLock _lock(m_GeneralLock); //! Lock -// for(p=internalFoundHostList.begin(); p!=internalFoundHostList.end(); p++){ -// if((*p)->pAppDesc->guidInstance==_hostID) { -// pHostFound=*p; -// break; -// } -// } -// if(p==internalFoundHostList.end()) - return 0; - } - - - // Set the peer info - DPN_PLAYER_INFO dpPlayerInfo; - ZeroMemory(&dpPlayerInfo, sizeof(DPN_PLAYER_INFO)); - dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO); - dpPlayerInfo.dwInfoFlags = DPNINFO_NAME; - dpPlayerInfo.pwszName = wstrPlayer; - - //hr = m_pDPClient->SetClientInfo(&dpPlayerInfo, 0, 0, DPNOP_SYNC); - //hr = m_pDPClient->SetPeerInfo(&dpPlayerInfo, 0, 0, DPNOP_SYNC); - hr = m_pDPPeer->SetPeerInfo(&dpPlayerInfo, 0, 0, DPNOP_SYNC); - XDP_CHECK_HR(hr, "SetPeerInfo"); - - // Connect to an existing session. -// hr = m_pDPClient->Connect(m_pLastHostFound->pAppDesc, // the application desc -// m_pLastHostFound->pHostAddr, // address of the host of the session -// m_pLastHostFound->pDeviceAddr, // address of the local device the enum responses were received on -// 0, 0, // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS -// lpszSignatureRQ, strlen(lpszSignatureRQ), // user data, user data size -// 0, 0, // async context, async handle, -// DPNCONNECT_SYNC); // flags -// hr = m_pDPClient->Connect(pHostFound->pAppDesc, // the application desc -// pHostFound->pHostAddr, // address of the host of the session -// pHostFound->pDeviceAddr, // address of the local device the enum responses were received on -// 0, 0, // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS -// lpszSignatureRQ, strlen(lpszSignatureRQ), // user data, user data size -// 0, 0, 0, // player context, async context, async handle, -// DPNCONNECT_SYNC); // flags - //init connectPlayer data - static sConnectInfo connectInfo; - static sDigitalGameVersion dgv(true); - connectInfo.set(internalPlayerData, gamePassword.c_str(), dgv); - - hr = m_pDPPeer->Connect(pHostFound->pAppDesc, // the application desc - pHostFound->pHostAddr, // address of the host of the session - pHostFound->pDeviceAddr, // address of the local device the enum responses were received on - 0, 0, // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS - &connectInfo, sizeof(connectInfo), // user data, user data size - 0, 0, 0, // player context, async context, async handle, - DPNCONNECT_SYNC); // flags - ///XDP_CHECK_HR(hr, "DP Connect filed"); - if( FAILED( hr ) ){ - DXTRACE_ERR_MSGBOX( TEXT("PNetCenter::Connect-Connect"), hr ); - flag_connected=0; - return 0; - } - flag_connected=1; - - xassert( (m_nClientSgnCheckError==0) &&"Signature check error!"); - - return 1; -} - -int PNetCenter::Connect(unsigned int ip)//, int port -{ - //if(!ip) - // return Connect((const char*)0, port); - - ///ServerStart("qqq", PERIMETER_DEFAULT_PORT); - char ip_string[17]; - memset(ip_string, 0, 17); - sprintf(ip_string, "%d.%d.%d.%d", IP1(ip), IP2(ip), IP3(ip), IP4(ip)); - - { - ///clearFoundHostList();//Вызывается в StopFindHost - StopFindHostDP(); - } - - ///Sleep(2000); - - ///FindHost(ip_string); - StartFindHostDP(ip_string); - unsigned int CONST_MAX_TIME_SLEEP=10000; - unsigned int CONST_MIN_TIME_SLEEP=400; - unsigned int STEP_TESTING=200; - unsigned int time=0; - while(time CONST_MIN_TIME_SLEEP) break; - } - } - } - - ///reStartFindPlayers(ip); - ///Sleep(7000); - - -// if(!m_pLastHostFound) -// return 0; - GUID hostID; - { - CAutoLock _lock(m_GeneralLock); //! Lock - if(internalFoundHostList.empty()) return 0; - else hostID=(*internalFoundHostList.begin())->pAppDesc->guidInstance; - } - return Connect(hostID); -} - -bool PNetCenter::isConnected() -{ - return flag_connected;//(m_mode != DP_NOBODY); -} - - -int PNetCenter::Send(const char* buffer, int size, NETID netid, bool flag_guaranted) -{ - - DPNHANDLE hAsync; - DPN_BUFFER_DESC bufferDesc; - bufferDesc.dwBufferSize = size; - bufferDesc.pBufferData = (uint8_t*)buffer; - -/* switch(m_mode) - { - case DP_CLIENT: - //m_pDPClient->Send(&bufferDesc, 1, 0, 0, &hAsync, DPNSEND_GUARANTEED); - //m_pDPClient->SendTo(m_hostNETID, &bufferDesc, 1, 0, 0, &hAsync, DPNSEND_GUARANTEED); - m_pDPPeer->SendTo(m_hostNETID, &bufferDesc, 1, 0, 0, &hAsync, DPNSEND_GUARANTEED); - break; - - case DP_SERVER: - //m_pDPServer->SendTo(netid == 0xFFFFFFFF ? NETID_ALL_PLAYERS_GROUP : netid, &bufferDesc, 1, - // 0, 0, &hAsync, DPNSEND_NOLOOPBACK|DPNSEND_GUARANTEED); - m_pDPPeer->SendTo(netid == 0xFFFFFFFF ? NETID_ALL_PLAYERS_GROUP : netid, &bufferDesc, 1, - 0, 0, &hAsync, DPNSEND_NOLOOPBACK|DPNSEND_GUARANTEED); - break; - }*/ - if(flag_guaranted){ - m_pDPPeer->SendTo(netid, &bufferDesc, 1, - 0, NULL, &hAsync, DPNSEND_NOLOOPBACK|DPNSEND_GUARANTEED); - } - else { - m_pDPPeer->SendTo(netid, &bufferDesc, 1, - 1000, NULL, &hAsync, DPNSEND_COALESCE|DPNSEND_NOLOOPBACK); //1000ms - } - - return size; -} - -bool PNetCenter::FindHost(const char* lpszHost) -{ - bool result=1; - - HRESULT hr; - - DPN_APPLICATION_DESC dpnAppDesc; - IDirectPlay8Address* pDP8AddressHost = NULL; - IDirectPlay8Address* pDP8AddressLocal = NULL; - WCHAR* wszHostName = NULL; - - // Create the local device address object - hr = CoCreateInstance(CLSID_DirectPlay8Address, NULL, - CLSCTX_ALL, IID_IDirectPlay8Address, - (LPVOID*) &pDP8AddressLocal); - XDP_CHECK_HR(hr, "CoCreateInstance"); - - // Set IP service provider - if(flag_NetworkSimulation) - hr=pDP8AddressLocal->SetSP(&CLSID_NETWORKSIMULATOR_DP8SP_TCPIP); - else - hr=pDP8AddressLocal->SetSP(&CLSID_DP8SP_TCPIP); - XDP_CHECK_HR(hr, "SetSP"); - - // Create the remote host address object - hr = CoCreateInstance(CLSID_DirectPlay8Address, NULL, - CLSCTX_ALL, IID_IDirectPlay8Address, - (LPVOID*) &pDP8AddressHost); - XDP_CHECK_HR(hr, "CoCreateInstance"); - - // Set IP service provider - if(flag_NetworkSimulation) - hr=pDP8AddressHost->SetSP(&CLSID_NETWORKSIMULATOR_DP8SP_TCPIP); - else - hr=pDP8AddressHost->SetSP(&CLSID_DP8SP_TCPIP); - XDP_CHECK_HR(hr, "SetSP"); - - // Set the remote host name (if provided) - if(lpszHost && strlen(lpszHost)) { - wszHostName = new WCHAR[strlen(lpszHost) + 1]; - MultiByteToWideChar(CP_ACP, 0, lpszHost, -1, wszHostName, strlen(lpszHost) + 1); - - hr = pDP8AddressHost->AddComponent(DPNA_KEY_HOSTNAME, wszHostName, - (wcslen(wszHostName) + 1)*sizeof(WCHAR), - DPNA_DATATYPE_STRING); - XDP_CHECK_HR(hr, "AddComponent"); - - -#ifndef NO_PERIMETR_DEFAULT_PORT - DWORD port=PERIMETER_DEFAULT_PORT; - hr = pDP8AddressHost->AddComponent( DPNA_KEY_PORT, &port, sizeof(port), DPNA_DATATYPE_DWORD ); - XDP_CHECK_HR(hr, "AddComponent"); -#endif - - /* - // If a port was specified in the IP string, then add it. - // Games will typically hard code the port so the user need not know it - if( dwPort != 0 ) - { - hr = pDP8AddressHost->AddComponent( DPNA_KEY_PORT, - &dwPort, sizeof(dwPort), - DPNA_DATATYPE_DWORD ); - if( FAILED(hr) ) - { - DXTRACE_ERR( TEXT("AddComponent"), hr ); - goto LCleanup; - } - } - */ - } - - if(m_dwPort) { - hr = pDP8AddressHost->AddComponent(DPNA_KEY_PORT, &m_dwPort, sizeof(m_dwPort), DPNA_DATATYPE_DWORD); - //XDP_CHECK_HR(hr, "AddComponent"); - if( FAILED(hr) ){ - DXTRACE_ERR_MSGBOX( TEXT("PNetCenter::FindHost-AddComponent_PORT"), hr ); - } - } - - - ZeroMemory(&dpnAppDesc, sizeof(DPN_APPLICATION_DESC)); - dpnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC); - dpnAppDesc.guidApplication = guidPerimeterGame; - //if(flag_HostMigrate) dpnAppDesc.dwFlags |= DPNSESSION_MIGRATE_HOST; //Похоже тут это делать нельзя (только при создании Ноsta) - //if(flag_NoUseDPNSVR) dpnAppDesc.dwFlags |= DPNSESSION_NODPNSVR; //Не знаю - можно или нет - - - // Enumerate all StressMazeApp hosts running on IP service providers - //hr = m_pDPClient->EnumHosts(&dpnAppDesc, pDP8AddressHost, - // pDP8AddressLocal, NULL, - // 0, 1, 0, 0, this, - // 0, DPNENUMHOSTS_SYNC); - hr = m_pDPPeer->EnumHosts(&dpnAppDesc, pDP8AddressHost, pDP8AddressLocal, - NULL, - 0, 1, 0, 0, this, - 0, DPNENUMHOSTS_SYNC); - // This will be returned if the ip address is is invalid. - if(hr != DPNERR_INVALIDDEVICEADDRESS && hr != DPNERR_ADDRESSING) { - XDP_CHECK_HR(hr, "EnumHosts"); - result=0; - } - -// hr = m_pDPPeer->EnumHosts(&dpnAppDesc, pDP8AddressHost, pDP8AddressLocal, -// NULL, -// 0, -// INFINITE, -// ENUMERATION_HOST_RETRY_INTERVAL, -// INFINITE, -// this, -// &m_hEnumAsyncOp, -// 0); -// if(hr!=DPNSUCCESS_PENDING) result=0; - - RELEASE(pDP8AddressHost); - RELEASE(pDP8AddressLocal); - - if(wszHostName) - delete wszHostName; - return result; -} - -bool PNetCenter::StartFindHostDP(const char* lpszHost) -{ - clearInternalFoundHostList(); - - bool result=1; - HRESULT hr; - - DPN_APPLICATION_DESC dpnAppDesc; - IDirectPlay8Address* pDP8AddressHost = NULL; - IDirectPlay8Address* pDP8AddressLocal = NULL; - WCHAR* wszHostName = NULL; - - // Create the local device address object - hr = CoCreateInstance(CLSID_DirectPlay8Address, NULL, - CLSCTX_ALL, IID_IDirectPlay8Address, - (LPVOID*) &pDP8AddressLocal); - XDP_CHECK_HR(hr, "CoCreateInstance"); - - // Set IP service provider - if(flag_NetworkSimulation) - hr=pDP8AddressLocal->SetSP(&CLSID_NETWORKSIMULATOR_DP8SP_TCPIP); - else - hr=pDP8AddressLocal->SetSP(&CLSID_DP8SP_TCPIP); - XDP_CHECK_HR(hr, "SetSP"); - - // Create the remote host address object - hr = CoCreateInstance(CLSID_DirectPlay8Address, NULL, - CLSCTX_ALL, IID_IDirectPlay8Address, - (LPVOID*) &pDP8AddressHost); - XDP_CHECK_HR(hr, "CoCreateInstance"); - - // Set IP service provider - if(flag_NetworkSimulation) - hr=pDP8AddressHost->SetSP(&CLSID_NETWORKSIMULATOR_DP8SP_TCPIP); - else - hr=pDP8AddressHost->SetSP(&CLSID_DP8SP_TCPIP); - XDP_CHECK_HR(hr, "SetSP"); - - // Set the remote host name (if provided) - if(needHostList.empty()){ - if(lpszHost && strlen(lpszHost)) { - wszHostName = new WCHAR[strlen(lpszHost) + 1]; - MultiByteToWideChar(CP_ACP, 0, lpszHost, -1, wszHostName, strlen(lpszHost) + 1); - - hr = pDP8AddressHost->AddComponent(DPNA_KEY_HOSTNAME, wszHostName, - (wcslen(wszHostName) + 1)*sizeof(WCHAR), - DPNA_DATATYPE_STRING); - XDP_CHECK_HR(hr, "AddComponent"); - -#ifndef NO_PERIMETR_DEFAULT_PORT - DWORD dwPort=PERIMETER_DEFAULT_PORT; - hr = pDP8AddressHost->AddComponent( DPNA_KEY_PORT, &dwPort, sizeof(dwPort), DPNA_DATATYPE_DWORD ); - if( FAILED(hr) ){ - DXTRACE_ERR_MSGBOX( TEXT("PNetCenter::StartFindHostDP-AddComponent_PORT"), hr ); - } -#endif - } - if(m_dwPort) { - hr = pDP8AddressHost->AddComponent(DPNA_KEY_PORT, &m_dwPort, sizeof(m_dwPort), DPNA_DATATYPE_DWORD); - //XDP_CHECK_HR(hr, "AddComponent"); - if( FAILED(hr) ){ - DXTRACE_ERR_MSGBOX( TEXT("PNetCenter::StartFindHostDP-AddComponent_PORT"), hr ); - } - } - - //DPN_SP_CAPS dpspCaps; - //ZeroMemory( &dpspCaps, sizeof(DPN_SP_CAPS) ); - //dpspCaps.dwSize = sizeof(DPN_SP_CAPS); - //m_pDPClient->GetSPCaps(&CLSID_DP8SP_TCPIP, &dpspCaps, 0); - - ZeroMemory(&dpnAppDesc, sizeof(DPN_APPLICATION_DESC)); - dpnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC); - dpnAppDesc.guidApplication = guidPerimeterGame; - //if(flag_HostMigrate) dpnAppDesc.dwFlags |= DPNSESSION_MIGRATE_HOST; //Похоже тут это делать нельзя (только при создании Ноsta) - //if(flag_NoUseDPNSVR) dpnAppDesc.dwFlags |= DPNSESSION_NODPNSVR; //Не знаю - можно или нет - - - DPNHANDLE m_hEnumAsyncOp; - hr = m_pDPPeer->EnumHosts(&dpnAppDesc, pDP8AddressHost, pDP8AddressLocal, - NULL, - 0, - INFINITE, - 0,//default//ENUMERATION_HOST_RETRY_INTERVAL, - INFINITE, - this, - &m_hEnumAsyncOp, - 0); - // This will be returned if the ip address is is invalid. - //if(hr != DPNERR_INVALIDDEVICEADDRESS && hr != DPNERR_ADDRESSING) - // XDP_CHECK_HR(hr, "EnumHosts"); - if(hr!=DPNSUCCESS_PENDING) result=0; - else m_hEnumAsyncOp_Arr.push_back(m_hEnumAsyncOp); - delete wszHostName; - - } - else { - std::vector::iterator p; - for(p=needHostList.begin(); p!=needHostList.end(); p++){ - wszHostName = new WCHAR[strlen(p->c_str()) + 1]; - MultiByteToWideChar(CP_ACP, 0, p->c_str(), -1, wszHostName, strlen(p->c_str()) + 1); - - hr = pDP8AddressHost->AddComponent(DPNA_KEY_HOSTNAME, wszHostName, - (wcslen(wszHostName) + 1)*sizeof(WCHAR), - DPNA_DATATYPE_STRING); - XDP_CHECK_HR(hr, "AddComponent"); - if(m_dwPort) { - hr = pDP8AddressHost->AddComponent(DPNA_KEY_PORT, &m_dwPort, sizeof(m_dwPort), DPNA_DATATYPE_DWORD); - //XDP_CHECK_HR(hr, "AddComponent"); - if( FAILED(hr) ){ - DXTRACE_ERR_MSGBOX( TEXT("PNetCenter::StartFindHostDP-AddComponent_PORT"), hr ); - } - } - - ZeroMemory(&dpnAppDesc, sizeof(DPN_APPLICATION_DESC)); - dpnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC); - dpnAppDesc.guidApplication = guidPerimeterGame; - //if(flag_HostMigrate) dpnAppDesc.dwFlags |= DPNSESSION_MIGRATE_HOST; //Похоже тут это делать нельзя (только при создании Ноsta) - //if(flag_NoUseDPNSVR) dpnAppDesc.dwFlags |= DPNSESSION_NODPNSVR; //Не знаю - можно или нет - - - DPNHANDLE m_hEnumAsyncOp; - hr = m_pDPPeer->EnumHosts(&dpnAppDesc, pDP8AddressHost, pDP8AddressLocal, - NULL, - 0, - INFINITE, - 0,//default//ENUMERATION_HOST_RETRY_INTERVAL, - INFINITE, - this, - &m_hEnumAsyncOp, - 0); - if(hr!=DPNSUCCESS_PENDING) result=0; - else m_hEnumAsyncOp_Arr.push_back(m_hEnumAsyncOp); - - delete wszHostName; - } - } - - RELEASE(pDP8AddressHost); - RELEASE(pDP8AddressLocal); - - //if(wszHostName) - // delete wszHostName; - return result; -} - -void PNetCenter::StopFindHostDP(void) -{ - HRESULT hr; -// vector::iterator p; -// for(p=m_hEnumAsyncOp_Arr.begin(); p!=m_hEnumAsyncOp_Arr.end(); p++){ -// hr = m_pDPPeer->CancelAsyncOperation( *p, 0);//в случае когда есть хандлеры флаг не нужен ! DPNCANCEL_ENUM; -// if( FAILED(hr) ){ -// DXTRACE_ERR_MSGBOX( TEXT("PNetCenter::StopFindHostDP-error cancel operation"), hr ); -// } -// } - hr = m_pDPPeer->CancelAsyncOperation( NULL, DPNCANCEL_ENUM);//в случае когда указывается флаг хандлеры не нужны(отменяются все) ! ; - if( FAILED(hr) ){ - DXTRACE_ERR_MSGBOX( TEXT("PNetCenter::StopFindHostDP-error cancel operation"), hr ); - } - m_hEnumAsyncOp_Arr.clear(); - - - Sleep(20);//Для завершения back фукции DP(3-й поток) - clearInternalFoundHostList(); - ///clearGameHostList(); -} - - - - - - -/* -void XDPConnection::refreshGroupMember() -{ - gameGroupMemberList.clear(); - HRESULT hr; - DWORD dwSize = 0; - NETID * pArrNETID; - hr = m_pDPPeer->EnumPlayersAndGroups(NULL, &dwSize, DPNENUM_PLAYERS); - if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL ) xassert(0&&"DirectPlay-EnumPlayersAndGroups error 1"); - pArrNETID = new NETID [dwSize]; - ZeroMemory( pArrNETID, sizeof(NETID)*dwSize ); - hr = m_pDPPeer->EnumPlayersAndGroups(pArrNETID, &dwSize, DPNENUM_PLAYERS); - if( FAILED(hr) ) xassert(0&&"DirectPlay-EnumPlayersAndGroups error 2"); - int i; - for(i=0; i #include #include #include "files/files.h" +#include "NetRelay.h" +#include "codepages/codepages.h" ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -41,6 +44,88 @@ PClientData::~PClientData() = default; ////////////////////////////////////////////////////////////////////////////////////////////////////// +void PNetCenter::ProcessClientHandshake(NetConnection* connection, NetConnectionMessage* msg) { + //Clients connecting directly don't know own NETID yet as we assign them so we can't use msg source + NETID netid = connection->isRelay() ? msg->source : connection->getNETID(); + printf("ProcessClientHandshake NETID 0x%" PRIX64 "\n", netid); + + e_ConnectResult connectResult = CR_NONE; + msg->set(0); + NetConnectionInfo clientInfo; + clientInfo.read_header(*msg); + + //Check header first in case format changed in future + if (!clientInfo.isIDCorrect()) { + fprintf(stderr, "Connection info header ID failed\n"); + connectResult = e_ConnectResult::CR_ERR_INCORRECT_SIGNATURE; + } else if (!clientInfo.isVersionCorrect(currentShortVersion)) { //Несоответствующая версия игры + connectResult = e_ConnectResult::CR_ERR_INCORRECT_VERSION; + } else { + //Read all the info if OK + clientInfo.read_content(*msg); + + //Check rest + if (!clientInfo.checkCRCCorrect()) { + fprintf(stderr, "Connection info CRC failed\n"); + connectResult = e_ConnectResult::CR_ERR_INCORRECT_SIGNATURE; + } else if (!clientInfo.isArchCompatible(~server_arch_mask)) { + connectResult = e_ConnectResult::CR_ERR_INCORRECT_ARCH; + } else if (!clientInfo.isGameContentCompatible(terGameContentSelect)) { + connectResult = e_ConnectResult::CR_ERR_INCORRECT_CONTENT; + } else if (!clientInfo.isGameContentCRCCorrect(server_content_crc)) { + connectResult = e_ConnectResult::CR_ERR_INCORRECT_CONTENT_FILES; + } else if (m_bStarted) { // Игра запущена + connectResult = e_ConnectResult::CR_ERR_GAME_STARTED; + } else if ((!gamePassword.empty()) && (!clientInfo.isPasswordCorrect(gamePassword.c_str()))) { + connectResult = e_ConnectResult::CR_ERR_INCORRECT_PASWORD; + } else { + //Print warning if arch is diff but was masked + if (!clientInfo.isArchCompatible(0)) { + fprintf( + stderr, "Arch mismatch, desync may happen! Server 0x%" PRIX64 " Client 0x%" PRIX64 " Mask 0x%" PRIX64 "\n", + NetConnectionInfo::computeArchFlags(), clientInfo.getArchFlags(), server_arch_mask + ); + } + + //All OK, load player data + PlayerData pd; + pd.set(clientInfo.getPlayerName(), netid); + int resultIdx = AddClient(pd); + if (resultIdx < 0) { + connectResult = e_ConnectResult::CR_ERR_GAME_FULL; + } else { + connectResult = e_ConnectResult::CR_OK; + } + + //Set as having client + connectionHandler.setHasClient(netid, true); + } + } + delete msg; + + xassert(connectResult != CR_NONE); + SendClientHandshakeResponse(connection, netid, connectResult); +} + +void PNetCenter::SendClientHandshakeResponse(NetConnection* connection, NETID netid, e_ConnectResult result) { + printf("SendClientHandshakeResponse NETID %" PRIX64 " response: %d\n", netid, result); + NetConnectionInfoResponse response; + if (result == e_ConnectResult::CR_OK) { + //Connection is active now + response.accept(result, netid, m_localNETID, m_GameName); + response.send_to_connection(connectionHandler, m_localNETID, netid); + } else { + if (result != e_ConnectResult::CR_NONE) { + response.reject(result); + response.send_to_connection(connectionHandler, m_localNETID, netid); + } + + //Close connection if result was not OK + fprintf(stderr, "Incoming connection NETID %" PRIX64 " closed, host response: %d\n", netid, result); + connectionHandler.terminateNETID(netid); + } +} + int PNetCenter::AddClient(PlayerData& pd) { CAutoLock _lock(m_GeneralLock); //В этой функции в некоторых вызовах будет вложенный @@ -55,7 +140,7 @@ int PNetCenter::AddClient(PlayerData& pd) mission.setChanged(); - if (0 <= idxPlayerData) { + if (0 <= idxPlayerData && pd.netid != NETID_NONE) { //missionDescription.playersData[idxPlayerData].netid=netid; //missionDescription.playersData[idxPlayerData].flag_playerStartReady=1; @@ -63,34 +148,32 @@ int PNetCenter::AddClient(PlayerData& pd) pCD->backGameInf2List.reserve(20000);//резерв под 20000 квантов m_clients.push_back(pCD); - LogMsg("New client %d %s for game %s\n", idxPlayerData, pd.name(), m_GameName.c_str()); + LogMsg("New client idx: %d netid: 0x%" PRIX64 " name: %s for game %s\n", idxPlayerData, pd.netid, pd.name(), m_GameName.c_str()); -// netCommand4C_JoinResponse ncjr(netid, NETID_ALL_PLAYERS_GROUP/*m_netidGroupGame*/, NCJRR_OK); -// SendEvent(ncjr, netid); return idxPlayerData; } else { - LogMsg("Client %s for game %s id denied\n", pd.name(), m_GameName.c_str()); + LogMsg("Client netid: 0x%" PRIX64 " name: %s for game %s id denied\n", pd.netid, pd.name(), m_GameName.c_str()); return -1; } } void PNetCenter::ExitClient(NETID netid) { - LogMsg("ExitClient NID %" PRIX64 "\n", netid); - - NetConnection* conn = connectionHandler.getConnection(netid); - if (conn && !conn->is_closed()) { - conn->close(); - //Mark it as closed, since we processed the client - conn->state = NC_STATE_CLOSED; + LogMsg("ExitClient NID 0x%" PRIX64 "\n", netid); + if (netid == NETID_ALL || netid == NETID_NONE) { + xassert(0); + return; } + + //Mark it as closed, since we processed the client + connectionHandler.terminateNETID(netid); if (m_state != PNC_STATE__NONE) { DeleteClient(netid, true); } } void PNetCenter::DeleteClient(NETID netid, bool normalExit) { - LogMsg("DeleteClient NID %" PRIX64 " normal %d\n", netid, normalExit); + LogMsg("DeleteClient NID 0x%" PRIX64 " normal %d\n", netid, normalExit); if(isHost()){ hostMissionDescription->setChanged(); @@ -109,7 +192,7 @@ void PNetCenter::DeleteClient(NETID netid, bool normalExit) { ClientMapType::iterator p; for(p=m_clients.begin(); p!= m_clients.end(); p++) { if((*p)->netidPlayer==netid) { - LogMsg("Client NID %" PRIX64 " disconnecting\n", (*p)->netidPlayer); + LogMsg("Client NID 0x%" PRIX64 " disconnecting\n", (*p)->netidPlayer); delete *p; m_clients.erase(p); break; @@ -119,7 +202,7 @@ void PNetCenter::DeleteClient(NETID netid, bool normalExit) { hostMissionDescription->disconnectPlayer2PlayerDataByNETID(netid); } else { - if (netid == NETID_HOST) { + if (netid == m_hostNETID) { ExecuteInternalCommand(PNC_COMMAND__END_GAME, false); ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_HOST_TERMINATED_GAME); } @@ -209,8 +292,28 @@ void PNetCenter::SendBattleData() { LogMsg("Sent battle info\n"); } +void PNetCenter::UpdateCurrentMissionOnRelayRoom() { + if (connectionHandler.hasRelayConnection()) { + const NetRelayMessage_PeerSetupRoom& msg = GenerateRelaySetupRoom(); + for (auto& pair : connectionHandler.getRelayPeers()) { + const NetRelayPeerInfo& info = pair.second; + if (!info.has_client || info.rejected) { + continue; + } + NetConnection* connection = connectionHandler.getConnection(pair.second.relay_netid); + if (connection) { + sendNetRelayMessage(connection, &msg, m_localNETID); + } + } + } +} + void PNetCenter::UpdateCurrentMissionDescription4C() { + //Update room in relay if any + UpdateCurrentMissionOnRelayRoom(); + + //Send mission description to every real player const MissionDescription& md = *hostMissionDescription; for(int i=0; i::iterator p=m_InputPacketList.begin(); + std::list::iterator p=m_InputPacketList.begin(); if(p != m_InputPacketList.end()){ - returnNETID=(*p)->netid; + returnNETID=(*p)->source; while(p != m_InputPacketList.end()){ - InputPacket* packet = *p; - if(returnNETID==packet->netid){ + NetConnectionMessage* packet = *p; + if(returnNETID==packet->source){ if(netBuf.putBufferPacket(packet->address(), packet->tell())) { delete packet; p=m_InputPacketList.erase(p); @@ -334,488 +437,508 @@ void PNetCenter::HostReceiveQuant() /// i->second->NetHandlerProc(netid); - ClientMapType::iterator p; - for(p=m_clients.begin(); p!=m_clients.end(); p++){ + bool belongs_to_player = false; + for (PClientData* pcd : m_clients) { //if(in_buffer.receive(*m_pConnection, netid)) /////////////in_HostBuf.receive(*m_pConnection, netid); - if(netid==(*p)->netidPlayer) { - while(in_HostBuf.currentNetCommandID()) { - netLog <= in_HostBuf.currentNetCommandID() < "\n"; - switch(in_HostBuf.currentNetCommandID()) { - - case NETCOM_4H_ID_REQUEST_PAUSE: - { - netCommand4H_RequestPause nc_rp(in_HostBuf); - (*p)->requestPause=nc_rp.pause; - (*p)->timeRequestPause=clocki(); - -/* if( (!(*p)->requestPause) && nc_rp.pause){ + if(netid==pcd->netidPlayer) { + hostProcessPlayerClientPackets(pcd); + belongs_to_player = true; + break; //for-a + } + } + if (!belongs_to_player) { //Ни один существующий клиент не соответствует NETID + while(int currentCMD = in_HostBuf.currentNetCommandID()) { + /* + if(currentCMD==NETCOM_4H_ID_REJOIN_REQUEST && m_state==PNC_STATE__NEWHOST_PHASE_A){ + netCommand4H_ReJoinRequest nc(in_HostBuf); + AddClientToMigratedHost(netid, nc.currentLastQuant, nc.confirmedQuant); + } + else if(currentCMD==NETCOM_4H_ID_RESPONCE_LAST_QUANTS_COMMANDS){ + netCommand4H_ResponceLastQuantsCommands nci(in_HostBuf); + } else + */ + { + fprintf(stderr, "Invalid netCommand %d to host from non-client NETID 0x%" PRIX64 "\n", currentCMD, netid); + xassert(0); + in_HostBuf.ignoreNetCommand(); + } + + in_HostBuf.nextNetCommand();//Завершение обработки комманды + } + } + + } while (PutInputPacket2NetBuffer(in_HostBuf, netid)!=0); +} + +void PNetCenter::hostProcessPlayerClientPackets(PClientData* client) { + NETID netid = client->netidPlayer; + while (int currentCMD = in_HostBuf.currentNetCommandID()) { + netLog <= currentCMD < "\n"; + switch (currentCMD) { + case NETCOM_4H_ID_REQUEST_PAUSE: + { + netCommand4H_RequestPause nc_rp(in_HostBuf); + client->requestPause=nc_rp.pause; + client->timeRequestPause=clocki(); + +/* if( (!client->requestPause) && nc_rp.pause){ int playersIDArr[NETWORK_PLAYERS_MAX]; for(int i=0; irequestPause=true; + client->requestPause=true; } - else if((*p)->requestPause && (!nc_rp.pause) ){ + else if(client->requestPause && (!nc_rp.pause) ){ int playersIDArr[NETWORK_PLAYERS_MAX]; for(int i=0; irequestPause=false; + client->requestPause=false; }*/ - } - break; - case NETCOM_4H_ID_CHANGE_REAL_PLAYER_TYPE: - { - netCommand4H_ChangeRealPlayerType ncChRT(in_HostBuf); - if (m_state!=PNC_STATE__HOST_TUNING_GAME) break; - - if(netid==m_hostNETID){ - bool isSave = isSaveGame(); - MissionDescription& mission = *hostMissionDescription; - mission.setChanged(); - xassert(ncChRT.idxPlayerData_ < mission.playersData.size()); - //Проверка на то, что меняется не у Host-а / check that we are not changing host - if (ncChRT.idxPlayerData_!=mission.findPlayer(m_hostNETID)) { - PlayerData& pd = mission.playersData[ncChRT.idxPlayerData_]; - //Eject player - if (pd.realPlayerType==REAL_PLAYER_TYPE_PLAYER) { - //Отбрасывание игрока - NETID delPlayerNETID=pd.netid; - mission.disconnect2PlayerData(ncChRT.idxPlayerData_); - if (isSave) { - if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN || ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_PLAYER_AI) { - pd.realPlayerType = ncChRT.newRealPlayerType_; - } - } else { - if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN || ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_CLOSE) { - pd.realPlayerType = ncChRT.newRealPlayerType_; - } - } - RemovePlayer(delPlayerNETID); - } else if (pd.realPlayerType==REAL_PLAYER_TYPE_PLAYER_AI) { - if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN) { - mission.disconnect2PlayerData(ncChRT.idxPlayerData_); - pd.realPlayerType=ncChRT.newRealPlayerType_; - } - } - - if (isSave) { - if (pd.realPlayerType==REAL_PLAYER_TYPE_OPEN && ncChRT.newRealPlayerType_==REAL_PLAYER_TYPE_PLAYER_AI) { - pd.realPlayerType = ncChRT.newRealPlayerType_; - } - } else { - if(ncChRT.newRealPlayerType_==REAL_PLAYER_TYPE_PLAYER) { - ncChRT.newRealPlayerType_ = REAL_PLAYER_TYPE_OPEN; - } - if (pd.realPlayerType == REAL_PLAYER_TYPE_AI) { //Если был AI - if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN - || ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_CLOSE) { - //Закрывать AI - mission.disconnect2PlayerData(ncChRT.idxPlayerData_); - pd.realPlayerType=ncChRT.newRealPlayerType_; - } - } else { //Если был Close Или Open - if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_AI) { - mission.connectAI2PlayersData(ncChRT.idxPlayerData_); - } else if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN || - ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_CLOSE) { - pd.realPlayerType = ncChRT.newRealPlayerType_; - } - } - } - } - } - } - break; - case NETCOM_4H_ID_CHANGE_PLAYER_BELLIGERENT: - { - netCommand4H_ChangePlayerBelligerent ncChB(in_HostBuf); - if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; - - hostMissionDescription->setChanged(); - xassert(ncChB.idxPlayerData_ < hostMissionDescription->playersData.size()); - //Host может менять у любого - int playerIndex = netid == m_hostNETID ? ncChB.idxPlayerData_ : hostMissionDescription->findPlayer(netid); - hostMissionDescription->changePlayerBelligerent(playerIndex, ncChB.newBelligerent_); - } - break; - case NETCOM_4H_ID_CHANGE_PLAYER_COLOR: - { - netCommand4H_ChangePlayerColor ncChC(in_HostBuf); - if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; - - hostMissionDescription->setChanged(); - xassert(ncChC.idxPlayerData_ < hostMissionDescription->playersData.size()); - //Host может менять цвет любого - int playerIndex = netid == m_hostNETID ? ncChC.idxPlayerData_ : hostMissionDescription->findPlayer(netid); - hostMissionDescription->changePlayerColor(playerIndex, ncChC.newColor_, ncChC.direction); - } - break; - case NETCOM_4H_ID_CHANGE_PLAYER_DIFFICULTY: - { - netCommand4H_ChangePlayerDifficulty ncChD(in_HostBuf); - if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; - - hostMissionDescription->setChanged(); - xassert(ncChD.idxPlayerData_ < hostMissionDescription->playersData.size()); - //Host может менять у любого - int playerIndex = netid == m_hostNETID ? ncChD.idxPlayerData_ : hostMissionDescription->findPlayer(netid); - hostMissionDescription->changePlayerDifficulty(playerIndex, ncChD.difficulty_); - } - break; - case NETCOM_4H_ID_CHANGE_PLAYER_CLAN: - { - netCommand4H_ChangePlayerClan ncChC(in_HostBuf); - if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; - - hostMissionDescription->setChanged(); - xassert(ncChC.idxPlayerData_ < hostMissionDescription->playersData.size()); - //Host может менять у любого - int playerIndex = netid == m_hostNETID ? ncChC.idxPlayerData_ : hostMissionDescription->findPlayer(netid); - hostMissionDescription->changePlayerClan(playerIndex, ncChC.clan_); - } - break; - case NETCOM_4H_ID_CHANGE_PLAYER_HANDICAP: - { - netCommand4H_ChangePlayerHandicap ncChH(in_HostBuf); - if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; - - hostMissionDescription->setChanged(); - xassert(ncChH.idxPlayerData_ < hostMissionDescription->playersData.size()); - //Host может менять у любого - int playerIndex = netid == m_hostNETID ? ncChH.idxPlayerData_ : hostMissionDescription->findPlayer(netid); - hostMissionDescription->changePlayerHandicap(playerIndex, ncChH.handicap_); - } - break; - case NETCOM_4H_ID_CHANGE_PLAYER_SEAT: - { - netCommand4H_ChangePlayerSeat ncChS(in_HostBuf); - if (m_state!=PNC_STATE__HOST_TUNING_GAME || !isSaveGame()) break; - - MissionDescription& mission = *hostMissionDescription; - xassert(ncChS.idxPlayerData_ < mission.playersData.size()); - //Check if player who wants to change seat is the sender - int originIndex = mission.findPlayer(netid); - if (originIndex != -1 && ncChS.idxPlayerData_ < mission.playersData.size()) { - PlayerData& origin = mission.playersData[originIndex]; - PlayerData& destination = mission.playersData[ncChS.idxPlayerData_]; - bool allowed = false; - switch (destination.realPlayerType) { - case REAL_PLAYER_TYPE_OPEN: - case REAL_PLAYER_TYPE_PLAYER: - case REAL_PLAYER_TYPE_PLAYER_AI: - allowed = true; - break; - default: - break; + } + break; + case NETCOM_4H_ID_CHANGE_REAL_PLAYER_TYPE: + { + netCommand4H_ChangeRealPlayerType ncChRT(in_HostBuf); + if (m_state!=PNC_STATE__HOST_TUNING_GAME) break; + + if(netid==m_hostNETID){ + bool isSave = isSaveGame(); + MissionDescription& mission = *hostMissionDescription; + mission.setChanged(); + xassert(ncChRT.idxPlayerData_ < mission.playersData.size()); + //Проверка на то, что меняется не у Host-а / check that we are not changing host + if (ncChRT.idxPlayerData_!=mission.findPlayer(m_hostNETID)) { + PlayerData& pd = mission.playersData[ncChRT.idxPlayerData_]; + //Eject player + if (pd.realPlayerType==REAL_PLAYER_TYPE_PLAYER) { + //Отбрасывание игрока + NETID delPlayerNETID=pd.netid; + mission.disconnect2PlayerData(ncChRT.idxPlayerData_); + if (isSave) { + if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN || ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_PLAYER_AI) { + pd.realPlayerType = ncChRT.newRealPlayerType_; } - if (allowed && !origin.flag_playerStartReady && !destination.flag_playerStartReady) { - //Swap the players - std::string originName = origin.name(); - if (destination.realPlayerType == REAL_PLAYER_TYPE_PLAYER) { - origin.setName(destination.name()); - } else { - origin.setName(origin.nameInitial()); - } - destination.setName(originName); - std::swap(origin.netid, destination.netid); - std::swap(origin.realPlayerType, destination.realPlayerType); - mission.setChanged(); + } else { + if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN || ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_CLOSE) { + pd.realPlayerType = ncChRT.newRealPlayerType_; } } - } - break; - case NETCOM_4H_ID_CHANGE_MAP: - { - netCommand4H_ChangeMap nc_changeMap(in_HostBuf); - if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; - - hostMissionDescription->setChanged(); - if(netid==m_hostNETID){//только Host может менять карту - int i; - for(i=0; i< nc_changeMap.missionDescription_.playerAmountScenarioMax; i++){ - if (i < hostMissionDescription->playerAmountScenarioMax) { - nc_changeMap.missionDescription_.playersData[i] = hostMissionDescription->playersData[i]; - } else { - //Don't copy from previous description since this slot wasnt used and may contain unknown data - nc_changeMap.missionDescription_.playersData[i].realPlayerType = REAL_PLAYER_TYPE_OPEN; - } - } - //Remove players that exceed scenario max - for(i; i< hostMissionDescription->playersData.size(); i++){ - if(hostMissionDescription->playersData[i].realPlayerType==REAL_PLAYER_TYPE_PLAYER){ - NETID delPlayerNETID=hostMissionDescription->playersData[i].netid; - hostMissionDescription->disconnect2PlayerData(i); - //hostMissionDescription->playersData[i].realPlayerType=REAL_PLAYER_TYPE_CLOSE; - RemovePlayer(delPlayerNETID); //Полное удаление по DPN_MSGID_DESTROY_PLAYER - } - } - *hostMissionDescription = nc_changeMap.missionDescription_; + RemovePlayer(delPlayerNETID); + } else if (pd.realPlayerType==REAL_PLAYER_TYPE_PLAYER_AI) { + if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN) { + mission.disconnect2PlayerData(ncChRT.idxPlayerData_); + pd.realPlayerType=ncChRT.newRealPlayerType_; } } - break; - case NETCOM_4H_ID_START_LOAD_GAME: - { - netCommand4H_StartLoadGame ncslg(in_HostBuf); - if(m_state!=PNC_STATE__HOST_TUNING_GAME) break; - hostMissionDescription->setPlayerStartReady(netid, ncslg.ready != 0); - } - break; - case NETCOMC_ID_PLAYER_READY: - { - hostMissionDescription->setChanged(); - - netCommandC_PlayerReady event(in_HostBuf); - (*p)->m_flag_Ready=true; - (*p)->clientGameCRC=event.gameCRC_; - - LogMsg("Player 0x%" PRIX64 " (GCRC=0x%" PRIX32 ") reported ready\n", netid, (*p)->clientGameCRC); - - //Flag the client as restore finished if was desynced - for (auto& client : m_clients) { - if (client->netidPlayer == netid - && client->desync_state != PNC_DESYNC_NONE - && client->desync_state != PNC_DESYNC_RESTORE_FINISHED) { - xassert(client->desync_state == PNC_DESYNC_SENT_RESTORE); - if (client->netidPlayer == m_hostNETID) { - //Host always restores OK - client->desync_state = PNC_DESYNC_NONE; - } else { - client->desync_state = PNC_DESYNC_RESTORE_FINISHED; - } - break; - } + if (isSave) { + if (pd.realPlayerType==REAL_PLAYER_TYPE_OPEN && ncChRT.newRealPlayerType_==REAL_PLAYER_TYPE_PLAYER_AI) { + pd.realPlayerType = ncChRT.newRealPlayerType_; } - } - break; - case NETCOM_4H_ID_DESYNC_ACKNOWLEDGE: - { - hostMissionDescription->setChanged(); - - netCommand4H_DesyncAcknowledge event(in_HostBuf); - - LogMsg("Desync Ack %" PRIX64 "\n", netid); - - //Flag the client as acknowledged - for (auto& client : m_clients) { - if (client->netidPlayer == netid) { - xassert(client->desync_state == PNC_DESYNC_NOTIFIED); - client->desync_state = PNC_DESYNC_ACKNOLEDGED; - client->desync_missionDescription = std::move(event.missionDescription); - std::swap(client->desync_netlog, event.netlog); - break; + } else { + if(ncChRT.newRealPlayerType_==REAL_PLAYER_TYPE_PLAYER) { + ncChRT.newRealPlayerType_ = REAL_PLAYER_TYPE_OPEN; + } + if (pd.realPlayerType == REAL_PLAYER_TYPE_AI) { //Если был AI + if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN + || ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_CLOSE) { + //Закрывать AI + mission.disconnect2PlayerData(ncChRT.idxPlayerData_); + pd.realPlayerType=ncChRT.newRealPlayerType_; + } + } else { //Если был Close Или Open + if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_AI) { + mission.connectAI2PlayersData(ncChRT.idxPlayerData_); + } else if (ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_OPEN || + ncChRT.newRealPlayerType_ == REAL_PLAYER_TYPE_CLOSE) { + pd.realPlayerType = ncChRT.newRealPlayerType_; } } - - } - break; - - case NETCOM_4G_ID_UNIT_COMMAND: - { - netCommand4G_UnitCommand* pCommand = new netCommand4G_UnitCommand(in_HostBuf); - PutGameCommand2Queue_andAutoDelete(netid, pCommand); } + } + } + } + break; + case NETCOM_4H_ID_CHANGE_PLAYER_BELLIGERENT: + { + netCommand4H_ChangePlayerBelligerent ncChB(in_HostBuf); + if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; + + hostMissionDescription->setChanged(); + xassert(ncChB.idxPlayerData_ < hostMissionDescription->playersData.size()); + //Host может менять у любого + int playerIndex = netid == m_hostNETID ? ncChB.idxPlayerData_ : hostMissionDescription->findPlayer(netid); + hostMissionDescription->changePlayerBelligerent(playerIndex, ncChB.newBelligerent_); + } + break; + case NETCOM_4H_ID_CHANGE_PLAYER_COLOR: + { + netCommand4H_ChangePlayerColor ncChC(in_HostBuf); + if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; + + hostMissionDescription->setChanged(); + xassert(ncChC.idxPlayerData_ < hostMissionDescription->playersData.size()); + //Host может менять цвет любого + int playerIndex = netid == m_hostNETID ? ncChC.idxPlayerData_ : hostMissionDescription->findPlayer(netid); + hostMissionDescription->changePlayerColor(playerIndex, ncChC.newColor_, ncChC.direction); + } + break; + case NETCOM_4H_ID_CHANGE_PLAYER_DIFFICULTY: + { + netCommand4H_ChangePlayerDifficulty ncChD(in_HostBuf); + if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; + + hostMissionDescription->setChanged(); + xassert(ncChD.idxPlayerData_ < hostMissionDescription->playersData.size()); + //Host может менять у любого + int playerIndex = netid == m_hostNETID ? ncChD.idxPlayerData_ : hostMissionDescription->findPlayer(netid); + hostMissionDescription->changePlayerDifficulty(playerIndex, ncChD.difficulty_); + } + break; + case NETCOM_4H_ID_CHANGE_PLAYER_CLAN: + { + netCommand4H_ChangePlayerClan ncChC(in_HostBuf); + if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; + + hostMissionDescription->setChanged(); + xassert(ncChC.idxPlayerData_ < hostMissionDescription->playersData.size()); + //Host может менять у любого + int playerIndex = netid == m_hostNETID ? ncChC.idxPlayerData_ : hostMissionDescription->findPlayer(netid); + hostMissionDescription->changePlayerClan(playerIndex, ncChC.clan_); + } + break; + case NETCOM_4H_ID_CHANGE_PLAYER_HANDICAP: + { + netCommand4H_ChangePlayerHandicap ncChH(in_HostBuf); + if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; + + hostMissionDescription->setChanged(); + xassert(ncChH.idxPlayerData_ < hostMissionDescription->playersData.size()); + //Host может менять у любого + int playerIndex = netid == m_hostNETID ? ncChH.idxPlayerData_ : hostMissionDescription->findPlayer(netid); + hostMissionDescription->changePlayerHandicap(playerIndex, ncChH.handicap_); + } + break; + case NETCOM_4H_ID_CHANGE_PLAYER_SEAT: + { + netCommand4H_ChangePlayerSeat ncChS(in_HostBuf); + if (m_state!=PNC_STATE__HOST_TUNING_GAME || !isSaveGame()) break; + + MissionDescription& mission = *hostMissionDescription; + xassert(ncChS.idxPlayerData_ < mission.playersData.size()); + //Check if player who wants to change seat is the sender + int originIndex = mission.findPlayer(netid); + if (originIndex != -1 && ncChS.idxPlayerData_ < mission.playersData.size()) { + PlayerData& origin = mission.playersData[originIndex]; + PlayerData& destination = mission.playersData[ncChS.idxPlayerData_]; + bool allowed = false; + switch (destination.realPlayerType) { + case REAL_PLAYER_TYPE_OPEN: + case REAL_PLAYER_TYPE_PLAYER: + case REAL_PLAYER_TYPE_PLAYER_AI: + allowed = true; break; - case NETCOM_4G_ID_REGION: - { - netCommand4G_Region* pCommand=new netCommand4G_Region(in_HostBuf); - PutGameCommand2Queue_andAutoDelete(netid, pCommand); - } + default: break; - case NETCOM_4H_ID_BACK_GAME_INFORMATION: - { - netCommand4H_BackGameInformation* pEvent= new netCommand4H_BackGameInformation(in_HostBuf); - //p->second->backGameInfList.push_back(pEvent); - delete pEvent; + } + if (!origin.flag_playerStartReady && !destination.flag_playerStartReady && allowed) { + //Swap the players + std::string originName = origin.name(); + if (destination.realPlayerType == REAL_PLAYER_TYPE_PLAYER) { + origin.setName(destination.name()); + } else { + origin.setName(origin.nameInitial()); } - break; - case NETCOM_4H_ID_BACK_GAME_INFORMATION_2: - { - netCommand4H_BackGameInformation2 nc(in_HostBuf); - (*p)->backGameInf2List.push_back(nc); - (*p)->lagQuant=nc.lagQuant_; - (*p)->lastExecuteQuant=nc.quant_; - (*p)->lastTimeBackPacket=clocki(); + destination.setName(originName); + std::swap(origin.netid, destination.netid); + std::swap(origin.realPlayerType, destination.realPlayerType); + mission.setChanged(); + } + } + } + break; + case NETCOM_4H_ID_CHANGE_MAP: + { + netCommand4H_ChangeMap nc_changeMap(in_HostBuf); + if (m_state!=PNC_STATE__HOST_TUNING_GAME || isSaveGame()) break; + + hostMissionDescription->setChanged(); + if(netid==m_hostNETID){//только Host может менять карту + int i; + for(i=0; i< nc_changeMap.missionDescription_.playerAmountScenarioMax; i++){ + if (i < hostMissionDescription->playerAmountScenarioMax) { + nc_changeMap.missionDescription_.playersData[i] = hostMissionDescription->playersData[i]; + } else { + //Don't copy from previous description since this slot wasnt used and may contain unknown data + nc_changeMap.missionDescription_.playersData[i].realPlayerType = REAL_PLAYER_TYPE_OPEN; } - break; - case NETCOM_4H_ID_LATENCY_RESPONSE: - { - netCommand4H_LatencyResponse nc(in_HostBuf); - size_t timestamp = clock_us(); - if (timestamp > nc.timestamp) { - (*p)->last_time_latency_response = timestamp; - (*p)->latency = timestamp - nc.timestamp; - } + } + //Remove players that exceed scenario max + for(i; i< hostMissionDescription->playersData.size(); i++){ + if(hostMissionDescription->playersData[i].realPlayerType==REAL_PLAYER_TYPE_PLAYER){ + NETID delPlayerNETID=hostMissionDescription->playersData[i].netid; + hostMissionDescription->disconnect2PlayerData(i); + //hostMissionDescription->playersData[i].realPlayerType=REAL_PLAYER_TYPE_CLOSE; + RemovePlayer(delPlayerNETID); //Полное удаление по DPN_MSGID_DESTROY_PLAYER } - break; - case NETCOM_4H_ID_RESPONCE_LAST_QUANTS_COMMANDS: - { - xassert(m_state==PNC_STATE__NEWHOST_PHASE_B); - netCommand4H_ResponceLastQuantsCommands nci(in_HostBuf); - if(m_state!=PNC_STATE__NEWHOST_PHASE_B) break; - - std::vector tmpListGameCommands; - - InOutNetComBuffer in_buffer(nci.sizeCommandBuf, 1); //проверить необходимость автоувелечения! - in_buffer.putBufferPacket(nci.pData, nci.sizeCommandBuf); - - while(in_buffer.currentNetCommandID()!=NETCOM_ID_NONE) { - terEventID event = (terEventID)in_buffer.currentNetCommandID(); - switch(event){ - case NETCOM_4G_ID_UNIT_COMMAND: { - netCommand4G_UnitCommand* pnc= new netCommand4G_UnitCommand(in_buffer); - tmpListGameCommands.push_back(pnc); - break; - } - case NETCOM_4G_ID_REGION: { - netCommand4G_Region* pnc= new netCommand4G_Region(in_buffer); - tmpListGameCommands.push_back(pnc); - break; - } - case NETCOM_4G_ID_FORCED_DEFEAT: { - netCommand4G_ForcedDefeat* pnc=new netCommand4G_ForcedDefeat(in_buffer); - tmpListGameCommands.push_back(pnc); - break; - } - default: - xassert(0&&"Incorrect commanf in playReel file!"); - break; - } - in_buffer.nextNetCommand(); - } - //Запуск продолжения - netCommand4C_ContinueGameAfterHostMigrate ncContinueGame; - SendEvent(ncContinueGame, NETID_ALL); - //Выполнение команд, которые не у всех были выполнены - for(m_numberGameQuant=nci.beginQuantCommandTransmit; m_numberGameQuant<=nci.endQuantCommandTransmit; m_numberGameQuant++){ - m_nQuantCommandCounter=0; - std::vector::iterator p; - for(p=tmpListGameCommands.begin(); p!=tmpListGameCommands.end(); p++){ - if((*p)->curCommandQuant_==m_numberGameQuant) { - SendEvent(**p, NETID_ALL); - m_nQuantCommandCounter++; - } - } - //netCommandNextQuant netCommandNextQuant(m_numberGameQuant, m_nQuantCommandCounter, netCommandNextQuant::NOT_QUANT_CONFIRMATION); - //SendEvent(netCommandNextQuant, NETID_ALL_PLAYERS_GROUP/*m_netidGroupGame*/); + } + *hostMissionDescription = nc_changeMap.missionDescription_; + } + } + break; + case NETCOM_4H_ID_START_LOAD_GAME: + { + netCommand4H_StartLoadGame ncslg(in_HostBuf); + if(m_state!=PNC_STATE__HOST_TUNING_GAME) break; - } - hostGeneralCommandCounter=nci.finGeneraCommandCounter; - netCommandNextQuant netCommandNextQuant(nci.endQuantCommandTransmit, m_nQuantCommandCounter, hostGeneralCommandCounter, netCommandNextQuant::NOT_QUANT_CONFIRMATION); - SendEvent(netCommandNextQuant, NETID_ALL); - - terHyperSpace::clearListGameCommands(tmpListGameCommands); - - //Init game counter afte MigrateHost - //hostGeneralCommandCounter; //уже уставится выше - quantConfirmation=netCommandNextQuant::NOT_QUANT_CONFIRMATION; - m_nQuantCommandCounter=0; - m_numberGameQuant=nci.endQuantCommandTransmit+1;//!выше - ///ClearDeletePlayerGameCommand(); - ClearCommandList(); - //отмена паузы если была - hostPause=0; - int playersIDArr[NETWORK_PLAYERS_MAX]; - for(int m=0; mfindPlayer(netid); - if (playerID != -1) { - nc_ChatMessage.playerID = playerID; - const auto& playerData = hostMissionDescription->playersData[playerID]; - const auto& pc = playerColors[playerData.colorIndex].unitColor; - - //Add color and name of player to text - std::string text = "&" - + toColorCode(sColor4f(pc[0], pc[1], pc[2], 1.0f)) - + playerData.name() - + "&FFFFFF: " - + nc_ChatMessage.text; - nc_ChatMessage.text = text; - - //Find if we need to send to sender clan or everyone (sender included) - if (nc_ChatMessage.clanOnly) { - for (const auto& missionPlayer : hostMissionDescription->playersData) { - if (missionPlayer.clan == playerData.clan && missionPlayer.netid) { - SendEvent(nc_ChatMessage, missionPlayer.netid); - } - } - } else { - SendEvent(nc_ChatMessage, NETID_ALL); - } - } + hostMissionDescription->setPlayerStartReady(netid, ncslg.ready != 0); + } + break; + case NETCOMC_ID_PLAYER_READY: + { + hostMissionDescription->setChanged(); + + netCommandC_PlayerReady event(in_HostBuf); + client->m_flag_Ready=true; + client->clientGameCRC=event.gameCRC_; + + LogMsg("Player 0x%" PRIX64 " (GCRC=0x%" PRIX32 ") reported ready\n", netid, client->clientGameCRC); + + //Flag the client as restore finished if was desynced + for (auto& client : m_clients) { + if (client->netidPlayer == netid + && client->desync_state != PNC_DESYNC_NONE + && client->desync_state != PNC_DESYNC_RESTORE_FINISHED) { + xassert(client->desync_state == PNC_DESYNC_SENT_RESTORE); + if (client->netidPlayer == m_hostNETID) { + //Host always restores OK + client->desync_state = PNC_DESYNC_NONE; + } else { + client->desync_state = PNC_DESYNC_RESTORE_FINISHED; } - break; - case EVENT_ID_SERVER_TIME_CONTROL: - { - terEventControlServerTime event(in_HostBuf); + break; + } + } + } + break; + case NETCOM_4H_ID_DESYNC_ACKNOWLEDGE: + { + hostMissionDescription->setChanged(); - /// m_pGame->SetGameSpeedScale(event.scale, netidPlayer); - } + netCommand4H_DesyncAcknowledge event(in_HostBuf); + + LogMsg("Desync Ack 0x%" PRIX64 "\n", netid); + + //Flag the client as acknowledged + for (auto& cl : m_clients) { + if (cl->netidPlayer == netid) { + xassert(cl->desync_state == PNC_DESYNC_NOTIFIED); + cl->desync_state = PNC_DESYNC_ACKNOLEDGED; + cl->desync_missionDescription = std::move(event.missionDescription); + std::swap(cl->desync_netlog, event.netlog); + break; + } + } + + } + break; + + case NETCOM_4G_ID_UNIT_COMMAND: + { + netCommand4G_UnitCommand* pCommand = new netCommand4G_UnitCommand(in_HostBuf); + PutGameCommand2Queue_andAutoDelete(netid, pCommand); + } + break; + case NETCOM_4G_ID_REGION: + { + netCommand4G_Region* pCommand=new netCommand4G_Region(in_HostBuf); + PutGameCommand2Queue_andAutoDelete(netid, pCommand); + } + break; + case NETCOM_4H_ID_BACK_GAME_INFORMATION: + { + netCommand4H_BackGameInformation* pEvent= new netCommand4H_BackGameInformation(in_HostBuf); + //p->second->backGameInfList.push_back(pEvent); + delete pEvent; + } + break; + case NETCOM_4H_ID_BACK_GAME_INFORMATION_2: + { + netCommand4H_BackGameInformation2 nc(in_HostBuf); + client->backGameInf2List.push_back(nc); + client->lagQuant=nc.lagQuant_; + client->lastExecuteQuant=nc.quant_; + client->lastTimeBackPacket=clocki(); + } + break; + case NETCOM_4H_ID_LATENCY_RESPONSE: + { + netCommand4H_LatencyResponse nc(in_HostBuf); + size_t timestamp = clock_us(); + if (timestamp > nc.timestamp) { + client->last_time_latency_response = timestamp; + client->latency = timestamp - nc.timestamp; + } + } + break; + case NETCOM_4H_ID_RESPONCE_LAST_QUANTS_COMMANDS: + { + xassert(m_state==PNC_STATE__NEWHOST_PHASE_B); + netCommand4H_ResponceLastQuantsCommands nci(in_HostBuf); + if(m_state!=PNC_STATE__NEWHOST_PHASE_B) break; + + std::vector tmpListGameCommands; + + InOutNetComBuffer in_buffer(nci.sizeCommandBuf, 1); //проверить необходимость автоувелечения! + in_buffer.putBufferPacket(nci.pData, nci.sizeCommandBuf); + + while(in_buffer.currentNetCommandID()!=NETCOM_ID_NONE) { + terEventID event = (terEventID)in_buffer.currentNetCommandID(); + switch(event){ + case NETCOM_4G_ID_UNIT_COMMAND: { + netCommand4G_UnitCommand* pnc= new netCommand4G_UnitCommand(in_buffer); + tmpListGameCommands.push_back(pnc); break; - case NETCOM_4G_ID_EXIT: - { - netCommand4G_Exit event(in_HostBuf); - xassert(event.netid == netid); - if (event.netid == netid) { - SendEvent(event, NETID_ALL); - ExitClient(netid); - } } + case NETCOM_4G_ID_REGION: { + netCommand4G_Region* pnc= new netCommand4G_Region(in_buffer); + tmpListGameCommands.push_back(pnc); break; - case NETCOM_4H_ID_REJOIN_REQUEST: - { - //Опустошение лишней команды(сейчас клиент продолжает посылать ее до NEXT_QUANT) - netCommand4H_ReJoinRequest nc(in_HostBuf); } + case NETCOM_4G_ID_FORCED_DEFEAT: { + netCommand4G_ForcedDefeat* pnc=new netCommand4G_ForcedDefeat(in_buffer); + tmpListGameCommands.push_back(pnc); break; - default: - { - xassert("Invalid netCommand to host."); - in_HostBuf.ignoreNetCommand(); } + default: + xassert(0&&"Incorrect commanf in playReel file!"); break; } - //Запрещается вызывать currentNetCommandID(т.к. используется ignoreNetCommand) - in_HostBuf.nextNetCommand(); - } - break; //for-a - } - } - if(p==m_clients.end()){ //Ни один существующий клиент не соответствует NETID - while(in_HostBuf.currentNetCommandID()) { - if(in_HostBuf.currentNetCommandID()==NETCOMC_ID_JOIN_REQUEST){ - //HandleNewPlayer(netid); - //netCommandC_JoinRequest nc(in_HostBuf); + in_buffer.nextNetCommand(); } - else if(in_HostBuf.currentNetCommandID()==NETCOM_4H_ID_REJOIN_REQUEST && m_state==PNC_STATE__NEWHOST_PHASE_A){ - netCommand4H_ReJoinRequest nc(in_HostBuf); - AddClientToMigratedHost(netid, nc.currentLastQuant, nc.confirmedQuant); - } - else if(in_HostBuf.currentNetCommandID()==NETCOM_4H_ID_RESPONCE_LAST_QUANTS_COMMANDS){ - netCommand4H_ResponceLastQuantsCommands nci(in_HostBuf); + //Запуск продолжения + netCommand4C_ContinueGameAfterHostMigrate ncContinueGame; + SendEvent(ncContinueGame, NETID_ALL); + //Выполнение команд, которые не у всех были выполнены + for(m_numberGameQuant=nci.beginQuantCommandTransmit; m_numberGameQuant<=nci.endQuantCommandTransmit; m_numberGameQuant++){ + m_nQuantCommandCounter=0; + std::vector::iterator p; + for(p=tmpListGameCommands.begin(); p!=tmpListGameCommands.end(); p++){ + if((*p)->curCommandQuant_==m_numberGameQuant) { + SendEvent(**p, NETID_ALL); + m_nQuantCommandCounter++; + } + } + //netCommandNextQuant netCommandNextQuant(m_numberGameQuant, m_nQuantCommandCounter, netCommandNextQuant::NOT_QUANT_CONFIRMATION); + //SendEvent(netCommandNextQuant, NETID_ALL_PLAYERS_GROUP/*m_netidGroupGame*/); + } - else { - xassert("Invalid netCommand to host (unknown source)"); - in_HostBuf.ignoreNetCommand(); + hostGeneralCommandCounter=nci.finGeneraCommandCounter; + netCommandNextQuant netCommandNextQuant(nci.endQuantCommandTransmit, m_nQuantCommandCounter, hostGeneralCommandCounter, netCommandNextQuant::NOT_QUANT_CONFIRMATION); + SendEvent(netCommandNextQuant, NETID_ALL); + + terHyperSpace::clearListGameCommands(tmpListGameCommands); + + //Init game counter afte MigrateHost + //hostGeneralCommandCounter; //уже уставится выше + quantConfirmation=netCommandNextQuant::NOT_QUANT_CONFIRMATION; + m_nQuantCommandCounter=0; + m_numberGameQuant=nci.endQuantCommandTransmit+1;//!выше + ///ClearDeletePlayerGameCommand(); + ClearCommandList(); + //отмена паузы если была + hostPause=0; + int playersIDArr[NETWORK_PLAYERS_MAX]; + for(int m=0; mfindPlayer(netid); + if (playerID != -1) { + nc_ChatMessage.playerID = playerID; + const auto& playerData = hostMissionDescription->playersData[playerID]; + const auto& pc = playerColors[playerData.colorIndex].unitColor; + + //Add color and name of player to text + std::string text = "&" + + toColorCode(sColor4f(pc[0], pc[1], pc[2], 1.0f)) + + playerData.name() + + "&FFFFFF: " + + nc_ChatMessage.text; + nc_ChatMessage.text = text; + + //Find if we need to send to sender clan or everyone (sender included) + if (nc_ChatMessage.clanOnly) { + for (const auto& missionPlayer : hostMissionDescription->playersData) { + if (missionPlayer.clan == playerData.clan && missionPlayer.netid) { + SendEvent(nc_ChatMessage, missionPlayer.netid); + } + } + } else { + SendEvent(nc_ChatMessage, NETID_ALL); + } } + } + break; + case EVENT_ID_SERVER_TIME_CONTROL: + { + terEventControlServerTime event(in_HostBuf); - in_HostBuf.nextNetCommand();//Завершение обработки комманды + /// m_pGame->SetGameSpeedScale(event.scale, netidPlayer); } + break; + case NETCOM_4G_ID_EXIT: + { + netCommand4G_Exit event(in_HostBuf); + xassert(event.netid == netid); + if (event.netid == netid) { + SendEvent(event, NETID_ALL); + ExitClient(netid); + } + } + break; + case NETCOM_4H_ID_REJOIN_REQUEST: + { + //Опустошение лишней команды(сейчас клиент продолжает посылать ее до NEXT_QUANT) + netCommand4H_ReJoinRequest nc(in_HostBuf); + } + break; + default: + { + xassert("Invalid netCommand to host."); + in_HostBuf.ignoreNetCommand(); + } + break; } + //Запрещается вызывать currentNetCommandID(т.к. используется ignoreNetCommand) + in_HostBuf.nextNetCommand(); + } +} - }while(PutInputPacket2NetBuffer(in_HostBuf, netid)!=0); -} \ No newline at end of file +const NetRelayMessage_PeerSetupRoom& PNetCenter::GenerateRelaySetupRoom() const { + const MissionDescription* mission = hostMissionDescription; + const std::string& locale = getLocale(); + static NetRelayMessage_PeerSetupRoom msg; + msg.roomName = convertToUnicode(m_GameName, locale); + msg.hasPasword = !gamePassword.empty(); + msg.playersCount = mission->playersAmount(); + msg.playersMax = mission->playerSlotsAvailable(); + //We close room when starts or is full for now, in future when spectators are allowed set always false + msg.isClosed = m_bStarted || msg.playersCount >= msg.playersMax; + msg.gameStarted = m_bStarted; + msg.scenarioName = convertToUnicode(mission->missionName(), locale); + msg.gameContent = terGameContentSelect; + return msg; +} diff --git a/Source/Network/P2P_interface2Th_NetConn.cpp b/Source/Network/P2P_interface2Th_NetConn.cpp index a70c934c2..2bfca8c84 100644 --- a/Source/Network/P2P_interface2Th_NetConn.cpp +++ b/Source/Network/P2P_interface2Th_NetConn.cpp @@ -4,51 +4,7 @@ #include "NetConnectionAux.h" #include "GameShell.h" #include "Universe.h" - -arch_flags server_arch_mask = 0; -uint32_t server_content_crc = 0; - -bool PNetCenter::Init() -{ - connectionHandler.reset(); - - SetConnectionTimeout(TIMEOUT_DISCONNECT); - - m_hostNETID = m_localNETID = NETID_NONE; - - server_arch_mask = 0xFFFE; //Allow different OSes and compilers - const char* server_arch_mask_str = check_command_line("ServerArchMask"); - if (server_arch_mask_str) { - server_arch_mask = strtoull(server_arch_mask_str, nullptr, 16); - } - - //Reset our attributes in case we played before - loadUnitAttributes(false, nullptr); - - server_content_crc = get_content_crc(); - -#if defined(PERIMETER_DEBUG) && defined(PERIMETER_EXODUS) && 0 - //Dump current attrs - XPrmOArchive ar("/tmp/test"); - XBuffer& buf = ar.buffer(); - ar << makeObjectWrapper(rigidBodyPrmLibrary(), nullptr, nullptr); - ar.close(); -#endif - - return true; -} - -bool PNetCenter::ServerStart() -{ - m_hostNETID = m_localNETID = NETID_NONE; - flag_connected = connectionHandler.startListening(hostConnection.port()); - if (!flag_connected) { - fprintf(stderr, "Error listening on port %d\n", hostConnection.port()); - } else { - m_hostNETID = m_localNETID = NETID_HOST; - } - return flag_connected; -} +#include "ServerList.h" void PNetCenter::SetConnectionTimeout(int _ms) { //Unused since SDL_net doesn't have any way to set connect or send timeouts @@ -62,101 +18,106 @@ void PNetCenter::RemovePlayer(NETID netid) ExecuteInternalCommand(PNC_COMMAND__END_GAME, false); ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_HOST_TERMINATED_GAME); } else { - NetConnection* connection = connectionHandler.getConnection(netid); - xassert(connection); - if (connection) { - connection->close(); - } + connectionHandler.terminateNETID(netid); } } -void PNetCenter::Close(bool flag_immediate) -{ +void PNetCenter::Close(bool flag_immediate) { connectionHandler.reset(); flag_connected=false; } -bool PNetCenter::Connect() { - LogMsg("Connect %s\n", hostConnection.getString().c_str()); - m_hostNETID = m_localNETID = NETID_NONE; - std::string extraInfo; +void PNetCenter::ReceivedNetConnectionMessage(NetConnection* connection, NetConnectionMessage* msg) { + bool is_host = isHost(); + if (is_host) { + if (msg->source == m_hostNETID) { + fprintf(stderr, "Received msg from host! from 0x%" PRIX64 " for 0x%" PRIX64 "\n", + msg->source, msg->destination); + xassert(0); + delete msg; + return; + } + } else { + if (msg->source != m_hostNETID) { + fprintf(stderr, "Received msg from non-host! from 0x%" PRIX64 " for 0x%" PRIX64 "\n", + msg->source, msg->destination); + xassert(0); + delete msg; + return; + } + } - GameShell::e_JoinGameReturnCode rc = GameShell::JG_RC_CONNECTION_ERR; - NetConnection* connection = connectionHandler.startConnection(&hostConnection); + if (msg->tell() < sizeof(uint32_t)) { + fprintf(stderr, "Received msg that is too small! from 0x%" PRIX64 "\n", msg->source); + xassert(0); + delete msg; + return; + } + + uint32_t head = *(reinterpret_cast(msg->address())); + if (head == NETCOM_BUFFER_PACKET_ID) { + if (msg->destination != m_localNETID) { + fprintf(stderr, "Received msg for someone else! from 0x%" PRIX64 " for 0x%" PRIX64 "\n", + msg->source, msg->destination); + xassert(0); + } else { + m_InputPacketList.push_back(msg); + return; //msg passed + } + } else if (head == NC_INFO_ID) { + if (is_host) { + NETID netid = msg->source; + bool has_client = false; + for (auto client : m_clients) { + if (client->netidPlayer == netid) { + has_client = true; + break; + } + } + if (has_client) { + fprintf(stderr, "Got handshake when already has a client! from 0x%" PRIX64 "\n", msg->source); + xassert(0); + } else { + ProcessClientHandshake(connection, msg); + return; //msg passed + } + } else { + fprintf(stderr, "Got handshake being a client! from 0x%" PRIX64 "\n", msg->source); + xassert(0); + } + } else if (head == NC_INFO_REPLY_ID) { + //On direct mode the local client has NONE as we don't know yet, in relay we get NETID already by this point + if (m_localNETID != NETID_NONE && msg->destination != m_localNETID) { + fprintf(stderr, "Received client handshake reply for someone else! from 0x%" PRIX64 " for 0x%" PRIX64 "\n", + msg->source, msg->destination); + xassert(0); + } else if (is_host) { + fprintf(stderr, "Got handshake reply being a host! from 0x%" PRIX64 "\n", msg->source); + xassert(0); + } else { + ProcessClientHandshakeResponse(connection, msg); + return; //msg passed + } + } else { + fprintf(stderr, "Received msg can't be identified! from 0x%" PRIX64 " head 0x%" PRIX32 "\n", msg->source, head); + xassert(0); + } + + //Cleanup + delete msg; +} + +bool PNetCenter::SendClientHandshake(NetConnection* connection) const { if (connection) { - //Send the connection info to server, this provides the client data and other connection info + // NetConnectionInfo connectInfo; connectInfo.set(currentShortVersion, gamePassword.c_str(), terGameContentSelect, m_PlayerName.c_str()); XBuffer buffer(256, true); connectInfo.write(buffer); - connection->send(buffer); - - //Get the response and pass it to interface command - int ret = connection->receive(buffer, CONNECTION_HANDSHAKE_TIMEOUT); + int ret = connection->send(&buffer, m_localNETID, m_hostNETID, CONNECTION_HANDSHAKE_TIMEOUT); if (0 < ret) { - buffer.set(0); - NetConnectionInfoResponse response; - response.read(buffer); - if(response.checkOwnCorrect()){ - switch (response.connectResult) { - case NetConnectionInfoResponse::CR_NONE: - break; - case NetConnectionInfoResponse::CR_OK: - connection->state = NC_STATE_ACTIVE; - m_hostNETID = response.hostID; - m_localNETID = response.clientID; - m_GameName = response.gameName; - rc = GameShell::JG_RC_OK; - break; - case NetConnectionInfoResponse::CR_ERR_INCORRECT_SIGNATURE: - rc = GameShell::JG_RC_SIGNATURE_ERR; - break; - case NetConnectionInfoResponse::CR_ERR_INCORRECT_ARCH: - rc = GameShell::JG_RC_GAME_NOT_EQUAL_ARCH_ERR; - break; - case NetConnectionInfoResponse::CR_ERR_INCORRECT_VERSION: - rc = GameShell::JG_RC_GAME_NOT_EQUAL_VERSION_ERR; - break; - case NetConnectionInfoResponse::CR_ERR_INCORRECT_CONTENT: - rc = GameShell::JG_RC_GAME_NOT_EQUAL_CONTENT_ERR; - break; - case NetConnectionInfoResponse::CR_ERR_INCORRECT_CONTENT_FILES: - rc = GameShell::JG_RC_GAME_NOT_EQUAL_CONTENT_ERR; - if (!response.gameContentList.empty()) { - extraInfo += "RMM=resource/models/main\n"; - std::string differ = ""; - std::string missing = ""; - const auto& ourList = get_content_list(); - for (const auto& entry : response.gameContentList) { - std::string shorted = entry.first; - string_replace_all(shorted, "resource/models/main", "RMM"); - if (ourList.count(entry.first)) { - uint32_t ourCRC = ourList.at(entry.first); - if (ourCRC != entry.second) { - if (!differ.empty()) differ += "\n"; - differ += shorted; - } - } else { - //We don't have this file - if (!missing.empty()) missing += "\n"; - missing += shorted; - } - } - if (!differ.empty()) extraInfo += "Differ:\n" + differ + "\n"; - if (!missing.empty()) extraInfo += "Missing:\n" + missing + "\n"; - } - break; - case NetConnectionInfoResponse::CR_ERR_GAME_STARTED: - rc = GameShell::JG_RC_GAME_IS_RUN_ERR; - break; - case NetConnectionInfoResponse::CR_ERR_GAME_FULL: - rc = GameShell::JG_RC_GAME_IS_FULL_ERR; - break; - case NetConnectionInfoResponse::CR_ERR_INCORRECT_PASWORD: - rc = GameShell::JG_RC_PASSWORD_ERR; - break; - } - } + //We have to wait for response which will be handled later + return true; } else if (ret == 0) { fprintf(stderr, "Client connection handshake reply timeout\n"); } else { @@ -166,12 +127,106 @@ bool PNetCenter::Connect() { fprintf(stderr, "Client connection not available\n"); } + //Failed + gameShell->callBack_JoinGameReturnCode(GameShell::JG_RC_CONNECTION_ERR, ""); + return false; +} + +void PNetCenter::ProcessClientHandshakeResponse(NetConnection* connection, NetConnectionMessage* msg) { + std::string extraInfo; + GameShell::e_JoinGameReturnCode rc = GameShell::JG_RC_CONNECTION_ERR; + msg->set(0); + NetConnectionInfoResponse response; + response.read(*msg); + printf("ProcessClientHandshakeResponse NETID %" PRIX64 "\n", msg->source); + if(response.checkOwnCorrect()){ + if (response.connectResult == e_ConnectResult::CR_OK) { + if (msg->source != response.hostID) { + response.connectResult = e_ConnectResult::CR_NONE; + fprintf(stderr, "NetConnectionInfoResponse hostID mismatch 0x%" PRIX64 " with packet source 0x%" PRIX64 "\n", response.hostID, msg->source); + } + if (msg->destination != response.clientID) { + response.connectResult = e_ConnectResult::CR_NONE; + fprintf(stderr, "NetConnectionInfoResponse clientID mismatch 0x%" PRIX64 " with packet destination 0x%" PRIX64 "\n", response.clientID, msg->destination); + } + xassert(response.connectResult == e_ConnectResult::CR_OK); + } + switch (response.connectResult) { + case e_ConnectResult::CR_NONE: + break; + case e_ConnectResult::CR_OK: + m_hostNETID = response.hostID; + m_localNETID = response.clientID; + m_GameName = response.gameName; + connectionHandler.setHasClient(m_hostNETID, true); + if (!connection->isRelay()) { + //Reassign if needed + connectionHandler.reassignConnectionNETID(connection, m_hostNETID); + } + rc = GameShell::JG_RC_OK; + break; + case e_ConnectResult::CR_ERR_INCORRECT_SIGNATURE: + rc = GameShell::JG_RC_SIGNATURE_ERR; + break; + case e_ConnectResult::CR_ERR_INCORRECT_ARCH: + rc = GameShell::JG_RC_GAME_NOT_EQUAL_ARCH_ERR; + break; + case e_ConnectResult::CR_ERR_INCORRECT_VERSION: + rc = GameShell::JG_RC_GAME_NOT_EQUAL_VERSION_ERR; + break; + case e_ConnectResult::CR_ERR_INCORRECT_CONTENT: + rc = GameShell::JG_RC_GAME_NOT_EQUAL_CONTENT_ERR; + break; + case e_ConnectResult::CR_ERR_INCORRECT_CONTENT_FILES: + rc = GameShell::JG_RC_GAME_NOT_EQUAL_CONTENT_ERR; + if (!response.gameContentList.empty()) { + extraInfo += "RMM=resource/models/main\n"; + std::string differ; + std::string missing; + const auto& ourList = get_content_list(); + for (const auto& entry : response.gameContentList) { + std::string shorted = entry.first; + string_replace_all(shorted, "resource/models/main", "RMM"); + if (ourList.count(entry.first)) { + uint32_t ourCRC = ourList.at(entry.first); + if (ourCRC != entry.second) { + if (!differ.empty()) differ += "\n"; + differ += shorted; + } + } else { + //We don't have this file + if (!missing.empty()) missing += "\n"; + missing += shorted; + } + } + if (!differ.empty()) extraInfo += "Differ:\n" + differ + "\n"; + if (!missing.empty()) extraInfo += "Missing:\n" + missing + "\n"; + } + break; + case e_ConnectResult::CR_ERR_GAME_STARTED: + rc = GameShell::JG_RC_GAME_IS_RUN_ERR; + break; + case e_ConnectResult::CR_ERR_GAME_FULL: + rc = GameShell::JG_RC_GAME_IS_FULL_ERR; + break; + case e_ConnectResult::CR_ERR_INCORRECT_PASWORD: + rc = GameShell::JG_RC_PASSWORD_ERR; + break; + } + } + + delete msg; + flag_connected = rc == GameShell::JG_RC_OK; + if (isConnected()) { + m_state = PNC_STATE__CLIENT_TUNING_GAME; + serverList->stopFind(); + } else { + Close(); + } gameShell->callBack_JoinGameReturnCode(rc, extraInfo); - - return flag_connected; -} +}; bool PNetCenter::isConnected() const { return flag_connected; @@ -180,110 +235,21 @@ bool PNetCenter::isConnected() const { //Out size_t PNetCenter::SendNetBuffer(InOutNetComBuffer* netbuffer, NETID destination) { size_t sent = 0; - if (destination == m_localNETID || (destination != m_hostNETID && !isHost())) { - fprintf(stderr, "Discarding sending 0x%" PRIX64 " -> 0x%" PRIX64 "\n", m_localNETID, destination); - //xassert(0); + if (destination == m_localNETID || ( + destination != m_hostNETID && + destination != NETID_ALL && + !isHost() + )) { + fprintf(stderr, "Discarding sending from 0x%" PRIX64 " to 0x%" PRIX64 "\n", m_localNETID, destination); + xassert(0); } else { - sent = connectionHandler.sendToNETID(reinterpret_cast(netbuffer->buf), netbuffer->filled_size, destination); + //Pinky promise that buf won't modify the buffer ptr + XBuffer buf(reinterpret_cast(netbuffer->buf), netbuffer->filled_size); + buf.set(netbuffer->filled_size); + sent = connectionHandler.sendToNETID(&buf, m_localNETID, destination); } netbuffer->init(); netbuffer->reset(); netbuffer->byte_sending += sent; return sent; } - -void PNetCenter::handleIncomingClientConnection(NetConnection* connection) { - NetConnectionInfoResponse response; - XBuffer buffer; - int ret = -1; - - //If netid is NONE then it wasnt allocated into pool - if (connection->netid == NETID_NONE) { - fprintf(stderr, "Incoming connection couldn't be allocated\n"); - //Send the reply that game is full and close it without reading connection info - response.reject(NetConnectionInfoResponse::CR_ERR_GAME_FULL); - } else { - //Read incoming data into buffer - ret = connection->receive(buffer, CONNECTION_HANDSHAKE_TIMEOUT); - } - - if (ret == 0) { - fprintf(stderr, "Incoming connection handshake timeout\n"); - response.reject(NetConnectionInfoResponse::CR_NONE); - } else if (0 < ret) { - //Load data - buffer.set(0); - NetConnectionInfo clientInfo; - clientInfo.read_header(buffer); - - //Check header first in case format changed in future - if (!clientInfo.isIDCorrect()) { - fprintf(stderr, "Connection info header ID failed\n"); - response.reject(NetConnectionInfoResponse::CR_ERR_INCORRECT_SIGNATURE); - } else if (!clientInfo.isVersionCorrect(currentShortVersion)) { //Несоответствующая версия игры - response.reject(NetConnectionInfoResponse::CR_ERR_INCORRECT_VERSION); - } else { - //Read all the info if OK - clientInfo.read_content(buffer); - - //Check rest - if (!clientInfo.checkCRCCorrect()) { - fprintf(stderr, "Connection info CRC failed\n"); - response.reject(NetConnectionInfoResponse::CR_ERR_INCORRECT_SIGNATURE); - } else if (!clientInfo.isArchCompatible(~server_arch_mask)) { - response.reject(NetConnectionInfoResponse::CR_ERR_INCORRECT_ARCH); - } else if (!clientInfo.isGameContentCompatible(terGameContentSelect)) { - response.reject(NetConnectionInfoResponse::CR_ERR_INCORRECT_CONTENT); - } else if (!clientInfo.isGameContentCRCCorrect(server_content_crc)) { - response.reject(NetConnectionInfoResponse::CR_ERR_INCORRECT_CONTENT_FILES); - } else if (m_bStarted) { // Игра запущена - response.reject(NetConnectionInfoResponse::CR_ERR_GAME_STARTED); - } else if ((!gamePassword.empty()) && (!clientInfo.isPasswordCorrect(gamePassword.c_str()))) { - response.reject(NetConnectionInfoResponse::CR_ERR_INCORRECT_PASWORD); - } else { - //Print warning if arch is diff but was masked - if (!clientInfo.isArchCompatible(0)) { - fprintf( - stderr, "Arch mismatch, desync may happen! Server 0x%" PRIX64 " Client 0x%" PRIX64 " Mask 0x%" PRIX64 "\n", - NetConnectionInfo::computeArchFlags(), clientInfo.getArchFlags(), server_arch_mask - ); - } - - //All OK - PlayerData pd; - pd.set(clientInfo.getPlayerName(), connection->netid); - int resultIdx = AddClient(pd); - if (resultIdx == -1) {// Игра полная - response.reject(NetConnectionInfoResponse::CR_ERR_GAME_FULL); - } else { - response.accept(NetConnectionInfoResponse::CR_OK, pd.netid, m_hostNETID, m_GameName); - connection->state = NC_STATE_ACTIVE; - } - } - } - } - - //Respond connection if there is result - if (response.connectResult != NetConnectionInfoResponse::CR_NONE) { - XBuffer responseBuffer(256, true); - response.write(responseBuffer); - ret = connection->send(responseBuffer); - } - - //Close connection if result was not OK - if (0 < ret && response.connectResult != NetConnectionInfoResponse::CR_OK) { - ret = -1; - } - - //If not OK close it - if (ret <= 0) { - fprintf(stderr, "Incoming connection 0x%" PRIX64 " closed response %d ret %d\n", connection->netid, response.connectResult, ret); - - connection->close(); - - //Delete connection since is not stored anywhere - if (connection->netid == NETID_NONE) { - delete connection; - } - } -} diff --git a/Source/Network/P2P_interface3ThDP.cpp b/Source/Network/P2P_interface3ThDP.cpp deleted file mode 100644 index 08536dd10..000000000 --- a/Source/Network/P2P_interface3ThDP.cpp +++ /dev/null @@ -1,541 +0,0 @@ -#include "NetIncludes.h" - -#include "P2P_interface.h" - -#include "GameShell.h" -#include "Universe.h - -#include "../Terra/terra.h" - -#include - -#include "NetConnectionAux.h" - - -HRESULT WINAPI DirectPlayMessageHandler(PVOID pvUserContext, uint32_t dwMessageId, PVOID pMsgBuffer) -{ - return ((PNetCenter*)pvUserContext)->DirectPlayMessageHandler(dwMessageId, pMsgBuffer); -} - - - -HRESULT PNetCenter::DirectPlayMessageHandler(uint32_t dwMessageId, PVOID pMsgBuffer) -{ - - CAutoLock _lock(m_GeneralLock); - switch(dwMessageId) { - case DPN_MSGID_INDICATE_CONNECT: - { - DPNMSG_INDICATE_CONNECT* pMsg = (DPNMSG_INDICATE_CONNECT*)pMsgBuffer; - - //if(strncmp((LPCTSTR)pMsg->pvUserConnectData, lpszSignatureRQ, __min((int)pMsg->dwUserConnectDataSize, strlen(lpszSignatureRQ))) != 0) - // return E_FAIL; //bad signature - - if(pMsg->dwUserConnectDataSize!=sizeof(sConnectInfo)) - return E_FAIL; - sConnectInfo clientConnectInfo((unsigned char*)pMsg->pvUserConnectData, pMsg->dwUserConnectDataSize); - if(!clientConnectInfo.checkOwnCorrect()) - return E_FAIL; - - static sDigitalGameVersion hostDGV(true);//создание версии игры - static sReplyConnectInfo replyConnectInfo; - pMsg->pvReplyData=&replyConnectInfo; - pMsg->dwReplyDataSize=sizeof(replyConnectInfo); - if(hostDGV!=clientConnectInfo.dgv){ //Несоответствующая версия игры - replyConnectInfo.set(sReplyConnectInfo::CR_ERR_INCORRECT_VERSION, hostDGV); - return E_FAIL; - } - if(m_bStarted) { // Игра запущена - replyConnectInfo.set(sReplyConnectInfo::CR_ERR_GAME_STARTED, hostDGV); - return E_FAIL; - } - if( (!gamePassword.empty()) && (!clientConnectInfo.isPasswordCorrect(gamePassword.c_str())) ){ - replyConnectInfo.set(sReplyConnectInfo::CR_ERR_INCORRECT_PASWORD, hostDGV); - return E_FAIL; - } - int resultIdx=AddClient(clientConnectInfo.perimeterPlayerData, 0, ""); - if(resultIdx==-1) {// Игра полная - replyConnectInfo.set(sReplyConnectInfo::CR_ERR_GAME_FULL, hostDGV); - return E_FAIL; - } - - pMsg->pvPlayerContext=(void*)resultIdx; //для корректного удаления в DPN_MSGID_INDICATED_CONNECT_ABORTED - //pMsg->pvReplyData = (void*)lpszSignatureRPL; - //pMsg->dwReplyDataSize = strlen(lpszSignatureRPL); - replyConnectInfo.set(sReplyConnectInfo::CR_OK, hostDGV); - } - break; - - case DPN_MSGID_INDICATED_CONNECT_ABORTED: - { - DPNMSG_INDICATED_CONNECT_ABORTED* pMsg=(DPNMSG_INDICATED_CONNECT_ABORTED*)pMsgBuffer; - - if(isHost()){ - //TODO fix this once we remove DirectPlay - unsigned int mIdx; -#ifdef PERIMETER_ARCH_64 - mIdx = (unsigned int) ((long long) pMsg->pvPlayerContext); -#else - mIdx = (unsigned int) pMsg->pvPlayerContext; -#endif - DeleteClientByMissionDescriptionIdx(mIdx); - } - } - - break; - case DPN_MSGID_DESTROY_PLAYER: - { - PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg; - pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer; - - ///NetHandlerProc(pDestroyPlayerMsg->netidPlayer, msg); - //if(isHost()){ - DeleteClientByNETID(pDestroyPlayerMsg->netidPlayer, pDestroyPlayerMsg->dwReason); - //} - break; - } - case DPN_MSGID_CONNECT_COMPLETE: - { - DPNMSG_CONNECT_COMPLETE* pMsg = (DPNMSG_CONNECT_COMPLETE*)pMsgBuffer; - //if( SUCCEEDED(pMsg->hResultCode) ){ - // if(strncmp((LPCTSTR)pMsg->pvApplicationReplyData , lpszSignatureRPL, __min((int)pMsg->dwApplicationReplyDataSize , strlen(lpszSignatureRPL))) != 0) - // InterlockedIncrement(&m_nClientSgnCheckError); - //} - if(pMsg->dwApplicationReplyDataSize==sizeof(sReplyConnectInfo)){ - sReplyConnectInfo hostReplyConnectInfo((unsigned char*)pMsg->pvApplicationReplyData, pMsg->dwApplicationReplyDataSize); - if(hostReplyConnectInfo.checkOwnCorrect()){ - if( (hostReplyConnectInfo.connectResult==sReplyConnectInfo::CR_OK) && ( SUCCEEDED(pMsg->hResultCode)) ){ - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECT_OK); - return DPN_OK; - } - else if(hostReplyConnectInfo.connectResult==sReplyConnectInfo::CR_ERR_INCORRECT_VERSION){ - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECT_ERR_INCORRECT_VERSION); - return DPN_OK; - } - else if(hostReplyConnectInfo.connectResult==sReplyConnectInfo::CR_ERR_GAME_STARTED){ - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECT_ERR_GAME_STARTED); - return DPN_OK; - } - else if(hostReplyConnectInfo.connectResult==sReplyConnectInfo::CR_ERR_GAME_FULL){ - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECT_ERR_GAME_FULL); - return DPN_OK; - } - else if(hostReplyConnectInfo.connectResult==sReplyConnectInfo::CR_ERR_INCORRECT_PASWORD){ - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECT_ERR_PASSWORD); - return DPN_OK; - } - } - else { - InterlockedIncrement(&m_nClientSgnCheckError); - } - } - else { - InterlockedIncrement(&m_nClientSgnCheckError); - } - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECT_ERR); - return DPN_OK; - } - break; - - case DPN_MSGID_CREATE_PLAYER: - { - PDPNMSG_CREATE_PLAYER pCreatePlayerMsg; - pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer; - - - IDirectPlay8Peer* m_pDP; - //if(m_mode == DP_SERVER) m_pDP=m_pDPServer; - //else if(m_mode == DP_CLIENT) m_pDP=m_pDPClient; - m_pDP=m_pDPPeer; - - NETID netid=pCreatePlayerMsg->netidPlayer; - - HRESULT hr; - uint32_t dwSize = 0; - DPN_PLAYER_INFO* pdpPlayerInfo = NULL; - - // Get the peer info and extract its name - hr = DPNERR_CONNECTING; - - // GetPeerInfo might return DPNERR_CONNECTING when connecting, - // so just keep calling it if it does - while( hr == DPNERR_CONNECTING ) - hr = m_pDP->GetPeerInfo( netid, pdpPlayerInfo, &dwSize, 0 ); - - if( hr == DPNERR_BUFFERTOOSMALL ) { - pdpPlayerInfo = (DPN_PLAYER_INFO*) new uint8_t[ dwSize ]; - if( NULL == pdpPlayerInfo ) { - hr = E_OUTOFMEMORY; - goto LErrorReturn; - } - - ZeroMemory( pdpPlayerInfo, dwSize ); - pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO); - - hr = m_pDP->GetPeerInfo( netid, pdpPlayerInfo, &dwSize, 0 ); - if( SUCCEEDED(hr) ) { - if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL ) - m_localNETID=netid; - if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_HOST ) - m_hostNETID=netid; - //Дополнительно - if( (pdpPlayerInfo->dwPlayerFlags&DPNPLAYER_LOCAL)==0 && isHost()){//Кривоватое условие - //TODO fix this once we remove DirectPlay - unsigned int mIdx; -#ifdef PERIMETER_ARCH_64 - mIdx = (unsigned int) ((long long) pCreatePlayerMsg->pvPlayerContext); -#else - mIdx = (unsigned int) pCreatePlayerMsg->pvPlayerContext; -#endif - setNETIDInClientsDate(mIdx, pCreatePlayerMsg->netidPlayer); - } - - } - - hr=S_OK; - } -LErrorReturn: - //SAFE_DELETE_ARRAY( pdpPlayerInfo ); - if(pdpPlayerInfo) delete [] pdpPlayerInfo; - return hr; - - break; - } - case DPN_MSGID_CREATE_GROUP: - { - DPNMSG_CREATE_GROUP* pMsg = (DPNMSG_CREATE_GROUP*)pMsgBuffer; - ///m_netidGroupCreating = pMsg->netidGroup; - } - break; - case DPN_MSGID_ENUM_HOSTS_QUERY: - { - DPNMSG_ENUM_HOSTS_QUERY* pMsg=(DPNMSG_ENUM_HOSTS_QUERY*)pMsgBuffer; - //AutoLock!!! - if(isHost()){ - static sGameStatusInfo cGSInf; - cGSInf.set(hostMissionDescription.playersMaxEasily(), hostMissionDescription.playersAmount(), isGameRun(), 10, hostMissionDescription.worldID()); - xassert(sizeof(cGSInf) <= pMsg->dwMaxResponseDataSize); - pMsg->pvResponseData=&cGSInf; - pMsg->dwResponseDataSize=sizeof(cGSInf); - } - } - break; - case DPN_MSGID_ENUM_HOSTS_RESPONSE: - { - - DPNMSG_ENUM_HOSTS_RESPONSE* pMsg = (DPNMSG_ENUM_HOSTS_RESPONSE*)pMsgBuffer; - - if(pMsg->pApplicationDescription->guidApplication==guidPerimeterGame){ - std::vector::iterator p; - for(p=internalFoundHostList.begin(); p!=internalFoundHostList.end(); p++){ - if(pMsg->pApplicationDescription->guidInstance == (*p)->pAppDesc->guidInstance){ - if(sizeof(sGameStatusInfo)==pMsg->dwResponseDataSize){ - (*p)->gameStatusInfo=*((sGameStatusInfo*)(pMsg->pvResponseData)); - (*p)->gameStatusInfo.ping=pMsg->dwRoundTripLatencyMS; - (*p)->timeLastRespond=clocki(); - } - else xassert(0&& "Invalid enum host responce!"); - break; - } - } - if(p==internalFoundHostList.end()){ //host not found - INTERNAL_HOST_ENUM_INFO* pNewHost=new INTERNAL_HOST_ENUM_INFO(pMsg); - if(sizeof(sGameStatusInfo)==pMsg->dwResponseDataSize){ - pNewHost->gameStatusInfo=*((sGameStatusInfo*)(pMsg->pvResponseData)); - pNewHost->gameStatusInfo.ping=pMsg->dwRoundTripLatencyMS; - pNewHost->timeLastRespond=clocki(); - } - else xassert(0&& "Invalid enum host responce!"); - internalFoundHostList.push_back(pNewHost); - } - } - break; - } - - case DPN_MSGID_TERMINATE_SESSION: - { - PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg; - pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer; - - //m_mode = DP_NOBODY; - flag_connected=0; - m_bStarted=0; - if(pTerminateSessionMsg->hResultCode==DPNERR_HOSTTERMINATEDSESSION){ - //dropped - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECTION_DROPPED); - } - else if(pTerminateSessionMsg->hResultCode==DPNERR_CONNECTIONLOST){ - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECTION_FAILED); - } - else { - //Не распознанная ситуация - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_CONNECTION_FAILED); - } - //xassert(0 && "DP connect terminate!"); - break; - } - case DPN_MSGID_RECEIVE: - { - PDPNMSG_RECEIVE pReceiveMsg; - pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer; - - { - m_InputPacketList.push_back(XDPacket()); - m_InputPacketList.back().set(pReceiveMsg->netidSender, pReceiveMsg->dwReceiveDataSize, pReceiveMsg->pReceiveData); - - InOutNetComBuffer tmp(2048, true); - tmp.putBufferPacket(pReceiveMsg->pReceiveData, pReceiveMsg->dwReceiveDataSize); - if(tmp.currentNetCommandID()==NETCOM_4C_ID_CUR_MISSION_DESCRIPTION_INFO){ - netCommand4C_CurrentMissionDescriptionInfo ncCMD(tmp); - int color1=ncCMD.missionDescription_.playersData[0].colorIndex; - int color2=ncCMD.missionDescription_.playersData[1].colorIndex; - } - } - - ///XDP_Message msg=XDPMSG_DateReceive; - ///NetHandlerProc(pReceiveMsg->netidSender, msg); - - break; - } - case DPN_MSGID_DESTROY_GROUP: - { - DPNMSG_DESTROY_GROUP* m=(DPNMSG_DESTROY_GROUP*)pMsgBuffer; - break; - } - - case DPN_MSGID_HOST_MIGRATE: - { - if((m_state!=PNC_STATE__CLIENT_LOADING_GAME) && (m_state!=PNC_STATE__CLIENT_GAME)){ - //Нужно запустить abort - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_HOST_TERMINATED_GAME);//PNC_INTERFACE_COMMAND_CONNECTION_FAILED); - break; - } - LockInputPacket(); - - PDPNMSG_HOST_MIGRATE pHostMigrateMsg = (PDPNMSG_HOST_MIGRATE)pMsgBuffer; - - - m_hostNETID=pHostMigrateMsg->netidNewHost; - if( m_hostNETID == m_localNETID ){//Host Я - { -/* if(universe() ){ - universe()->stopGame_HostMigrate();//Очистка всех команд текущего кванта - } - flag_SkipProcessingGameCommand=1; - m_DPPacketList.clear(); -*/ } - ExecuteInternalCommand(PNC_COMMAND__STOP_GAME_AND_ASSIGN_HOST_2_MY, false); - } - else { // Host не Я - { -/* if(universe() ){ - universe()->stopGame_HostMigrate();//Очистка всех команд текущего кванта - } - flag_SkipProcessingGameCommand=1; - m_DPPacketList.clear(); -*/ } - ExecuteInternalCommand(PNC_COMMAND__STOP_GAME_AND_WAIT_ASSIGN_OTHER_HOST, false); - } - ///NetHandlerProc(pHostMigrateMsg->netidNewHost, msg); - break; - } - default: - { - switch(dwMessageId){ - case DPN_MSGID_ADD_PLAYER_TO_GROUP : - break; - case DPN_MSGID_APPLICATION_DESC : - break; - case DPN_MSGID_ASYNC_OP_COMPLETE : - break; - case DPN_MSGID_CLIENT_INFO : - break; - case DPN_MSGID_CONNECT_COMPLETE : - break; - case DPN_MSGID_CREATE_GROUP : - break; - case DPN_MSGID_CREATE_PLAYER : - break; - case DPN_MSGID_DESTROY_GROUP : - break; - case DPN_MSGID_DESTROY_PLAYER : - break; - case DPN_MSGID_ENUM_HOSTS_QUERY : - break; - case DPN_MSGID_ENUM_HOSTS_RESPONSE : - break; - case DPN_MSGID_GROUP_INFO : - break; - case DPN_MSGID_HOST_MIGRATE : - break; - case DPN_MSGID_INDICATE_CONNECT : - break; - case DPN_MSGID_INDICATED_CONNECT_ABORTED: - break; - case DPN_MSGID_PEER_INFO : - break; - case DPN_MSGID_RECEIVE : - break; - case DPN_MSGID_REMOVE_PLAYER_FROM_GROUP : - break; - case DPN_MSGID_RETURN_BUFFER : - break; - case DPN_MSGID_SEND_COMPLETE : - break; - case DPN_MSGID_SERVER_INFO : - break; - case DPN_MSGID_TERMINATE_SESSION : - break; - // Messages added for DirectX 9 - case DPN_MSGID_CREATE_THREAD : - break; - case DPN_MSGID_DESTROY_THREAD : - break; - case DPN_MSGID_NAT_RESOLVER_QUERY : - break; - } - int v=0; - } - break; - - } - - return S_OK; -} - -void PNetCenter::setNETIDInClientsDate(const int missionDescriptionIdx, NETID netid) -{ - hostMissionDescription.setChanged(); - ClientMapType::iterator p; - for(p=m_clients.begin(); p!= m_clients.end(); p++) { - if((*p)->missionDescriptionIdx==missionDescriptionIdx) { - (*p)->netidPlayer=netid; - break; - } - } - if(p==m_clients.end()) - LogMsg("set NETID client-err1\n"); - if(hostMissionDescription.setPlayerNETID(missionDescriptionIdx, netid)) - LogMsg("set NETID client-OK\n"); - else - LogMsg("set NETID client-err2\n"); -} -void PNetCenter::DeleteClientByMissionDescriptionIdx(const int missionDescriptionIdx) -{ - hostMissionDescription.setChanged(); - - ClientMapType::iterator p; - for(p=m_clients.begin(); p!= m_clients.end(); p++) { - if((*p)->missionDescriptionIdx==missionDescriptionIdx) { - LogMsg("Client 0x%lX(%s) disconnecting-", (*p)->netidPlayer, (*p)->m_szDescription); - delete *p; - m_clients.erase(p); - break; - } - } - if(hostMissionDescription.disconnectPlayer2PlayerDataByIndex(missionDescriptionIdx)) - LogMsg("OK\n"); - else - LogMsg("error in missionDescription\n"); -} - -void PNetCenter::DeleteClientByNETID(const NETID netid, uint32_t dwReason) -{ - if(isHost()){ - hostMissionDescription.setChanged(); - - if(m_bStarted){ - //idx == playerID (на всякий случай); - int idx=hostMissionDescription.findPlayer(netid); - xassert(idx!=-1); - if(idx!=-1){ - int playerID=hostMissionDescription.playersData[idx].playerID; - netCommand4G_ForcedDefeat* pncfd=new netCommand4G_ForcedDefeat(playerID); - //PutGameCommand2Queue_andAutoDelete(pncfd); - m_DeletePlayerCommand.push_back(pncfd); - } - } - - - ClientMapType::iterator p; - for(p=m_clients.begin(); p!= m_clients.end(); p++) { - if((*p)->netidPlayer==netid) { - LogMsg("Client 0x%lX(%s) disconnecting-", (*p)->netidPlayer, (*p)->m_szDescription); - delete *p; - m_clients.erase(p); - break; - } - } - - if(hostMissionDescription.disconnectPlayer2PlayerDataByNETID(netid)) - LogMsg("OK\n"); - else - LogMsg("error in missionDescription\n"); - } - else { - if(m_bStarted){ - //idx == playerID (на всякий случай); - int idx=clientMissionDescription.findPlayer(netid); - xassert(idx!=-1); - if(idx!=-1){ - int playerID=clientMissionDescription.playersData[idx].playerID; - netCommand4G_ForcedDefeat* pncfd=new netCommand4G_ForcedDefeat(playerID); - //PutGameCommand2Queue_andAutoDelete(pncfd); - //Нужно в случае миграции хоста - m_DeletePlayerCommand.push_back(pncfd); - } - } - } - if(m_bStarted){ - int idx=clientMissionDescription.findPlayer(netid); - xassert(idx!=-1); - if(idx!=-1){ - //отсылка сообщения о том, что игрок вышел - if(dwReason & DPNDESTROYPLAYERREASON_NORMAL){ - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_INFO_PLAYER_EXIT, clientMissionDescription.playersData[idx].name()); - } - else { - ExecuteInterfaceCommand(PNC_INTERFACE_COMMAND_INFO_PLAYER_DISCONNECTED, clientMissionDescription.playersData[idx].name()); - } - } - //Удаление игрока из clientMD - clientMissionDescription.disconnectPlayer2PlayerDataByNETID(netid); - } - -} - - -void PNetCenter::LockInputPacket(void) -{ - flag_LockIputPacket++; -} - - -PNetCenter::INTERNAL_HOST_ENUM_INFO::INTERNAL_HOST_ENUM_INFO(DPNMSG_ENUM_HOSTS_RESPONSE* pDpn) -{ - timeLastRespond=0; - pDpn->pAddressSender->Duplicate(&pHostAddr); - pDpn->pAddressDevice->Duplicate(&pDeviceAddr); - - pAppDesc = new DPN_APPLICATION_DESC; - ZeroMemory(pAppDesc, sizeof(DPN_APPLICATION_DESC) ); - memcpy(pAppDesc, pDpn->pApplicationDescription, sizeof(DPN_APPLICATION_DESC)); - if(pDpn->pApplicationDescription->pwszSessionName ) { - pAppDesc->pwszSessionName = new WCHAR[ wcslen(pDpn->pApplicationDescription->pwszSessionName)+1 ]; - wcscpy(pAppDesc->pwszSessionName, pDpn->pApplicationDescription->pwszSessionName ); - } -} -PNetCenter::INTERNAL_HOST_ENUM_INFO::~INTERNAL_HOST_ENUM_INFO() -{ - if(pAppDesc) - { - if(pAppDesc->pwszSessionName) - delete pAppDesc->pwszSessionName; - - delete pAppDesc; - } - RELEASE(pHostAddr); - RELEASE(pDeviceAddr); -} - - diff --git a/Source/Network/P2P_interfaceAnyTh.cpp b/Source/Network/P2P_interfaceAnyTh.cpp index 9c2ef11e6..78e740319 100644 --- a/Source/Network/P2P_interfaceAnyTh.cpp +++ b/Source/Network/P2P_interfaceAnyTh.cpp @@ -1,12 +1,12 @@ #include "NetIncludes.h" -#include "NetConnectionAux.h" #include "P2P_interface.h" +#include "NetConnectionAux.h" extern SDL_threadID net_thread_id; -//Запускается из 1 2 3-го потока +//Запускается из 1 2-го потока //Может вызываться с фдагом waitExecution только из одного потока (сейчас 1-го) -//Runs from 1 2 3rd thread +//Runs from 1st and 2nd thread //Can be called with the waitExecution flag from only one thread (now the 1st one) bool PNetCenter::ExecuteInternalCommand(e_PNCInternalCommand ic, bool waitExecution) { @@ -87,7 +87,7 @@ bool PNetCenter::ExecuteInterfaceCommand(e_PNCInterfaceCommands ic, std::unique_ } void PNetCenter::ClearInputPacketList() { - for (InputPacket* packet : m_InputPacketList) { + for (NetConnectionMessage* packet : m_InputPacketList) { delete packet; } m_InputPacketList.clear(); diff --git a/Source/Network/ServerList.cpp b/Source/Network/ServerList.cpp index 372dedd46..3c2b2195f 100644 --- a/Source/Network/ServerList.cpp +++ b/Source/Network/ServerList.cpp @@ -1,33 +1,164 @@ #include "NetIncludes.h" +#include "NetRelay.h" #include "ServerList.h" +const int SERVER_LIST_INTERVAL = 3000; + +struct NetRelay_RoomInfoExtraData { + std::string scenario = {}; + std::string game_content = {}; + + SERIALIZE(ar) { + ar & WRAP_OBJECT(scenario); + ar & WRAP_OBJECT(game_content); + } +}; + +struct NetRelay_RoomInfo { + uint64_t room_id = 0; + std::string game_type = {}; + std::string game_version = {}; + std::string room_name = {}; + bool room_password = false; + bool room_closed = false; + bool game_started = false; + uint16_t players_count = 0; + uint16_t players_max = 0; + uint32_t ping = 0; + NetRelay_RoomInfoExtraData extra_data = {}; + + SERIALIZE(ar) { + ar & WRAP_OBJECT(room_id); + ar & WRAP_OBJECT(game_type); + ar & WRAP_OBJECT(game_version); + ar & WRAP_OBJECT(room_name); + ar & WRAP_OBJECT(room_password); + ar & WRAP_OBJECT(room_closed); + ar & WRAP_OBJECT(game_started); + ar & WRAP_OBJECT(players_count); + ar & WRAP_OBJECT(players_max); + ar & WRAP_OBJECT(ping); + ar & WRAP_OBJECT(extra_data); + } +}; + +struct NetRelay_LobbyWithRooms { + std::string host = {}; + std::vector rooms = {}; + + SERIALIZE(ar) { + ar & WRAP_OBJECT(host); + ar & WRAP_OBJECT(rooms); + } +}; + ServerList::ServerList() { + relayConnection = new NetConnection(nullptr, NETID_NONE); } -ServerList::~ServerList() = default; +ServerList::~ServerList() { + delete relayConnection; +} -void ServerList::startHostFind() { +void ServerList::startFind() { if (findingHosts) return; +#ifdef PERIMETER_DEBUG + printf("ServerList::startFind\n"); +#endif findingHosts = true; - //TODO } -void ServerList::stopHostFind() { +void ServerList::stopFind() { if (!findingHosts) return; +#ifdef PERIMETER_DEBUG + printf("ServerList::stopFind\n"); +#endif findingHosts = false; - clearHostInfoList(); - //TODO + listNeedUpdate = false; + lastRelayGameInfoList.clear(); + lastRelayFetch = 0; + closeNetRelay(relayConnection); } -void ServerList::clearHostInfoList() { - std::vector::iterator p; - gameHostInfoList.erase(gameHostInfoList.begin(), gameHostInfoList.end()); +bool ServerList::checkRelayConnection() { + if (relayConnection->hasTransport()) { + return true; + } + + NetAddress conn; + if (!getNetRelayAddress(conn)) { + return false; + } + + TCPsocket socket = conn.openTCP(); + if (!socket) { +#if !defined(PERIMETER_DEBUG) || 1 + stopFind(); +#endif + return false; + } + + NetTransportTCP* transport = new NetTransportTCP(socket); + relayConnection->set_transport(transport, NETID_RELAY); + return relayConnection->hasTransport(); } -void ServerList::refreshHostInfoList() -{ - if (!findingHosts) return; - clearHostInfoList(); +void ServerList::fetchRelayHostInfoList() { + + if (!findingHosts || (lastRelayFetch + SERVER_LIST_INTERVAL > clocki())) return; + lastRelayFetch = clocki(); + listNeedUpdate = true; + lastRelayGameInfoList.clear(); + + //Do the request to relay + if (!checkRelayConnection()) { + return; + } + static NetRelayMessage_PeerListRooms msg; + static NetRelayMessage_RelayListLobbies response; + bool ok = sendNetRelayMessage(relayConnection, &msg, NETID_NONE); + if (ok) { + ok = receiveNetRelayMessage(relayConnection, NETID_NONE, &response, RELAY_MSG_RELAY_LIST_LOBBIES); + } + + if (ok) { + XPrmIArchive ia; + std::swap(ia.buffer(), response.data); + ia.reset(); + std::vector lobbies = {}; + ia >> lobbies; + //ia & WRAP_NAME(lobbies, nullptr); + for (auto& lobby : lobbies) { + for (auto& room : lobby.rooms) { + GameInfo info; + info.gameHost = lobby.host; + info.gameRoomID = room.room_id; + info.gameName = room.room_name; + info.gameVersion = room.game_version; + info.maximumPlayers = room.players_max; + info.currentPlayers = room.players_count; + info.hasPassword = room.room_password; + info.gameStarted = room.game_started; + info.ping = room.ping; + info.scenario = room.extra_data.scenario; + uint32_t game_content = strtoul(room.extra_data.game_content.c_str(), nullptr, 0); + info.gameContent = static_cast(game_content); + lastRelayGameInfoList.emplace_back(info); + } + } + } + + //Cleanup + if (!ok) { + closeNetRelay(relayConnection); + } +} - //TODO here we should parse the whatever content the listing server gave us and create GameHostInfo for gameHostInfoList +void ServerList::refreshHostInfoList() { + if (!findingHosts || !listNeedUpdate) return; + listNeedUpdate = false; + gameInfoList.clear(); + for (auto& host : lastRelayGameInfoList) { + gameInfoList.emplace_back(host); + } } \ No newline at end of file diff --git a/Source/Network/ServerList.h b/Source/Network/ServerList.h index 211fc9e5f..aaace2c13 100644 --- a/Source/Network/ServerList.h +++ b/Source/Network/ServerList.h @@ -1,63 +1,48 @@ #ifndef PERIMETER_SERVERLIST_H #define PERIMETER_SERVERLIST_H -//Contains game related parameters -struct GameStatusInfo { +//Contains info about host/room and game itself +struct GameInfo { + ///Address of game server or relay server to connect + std::string gameHost = {}; + ///Room ID for relay, if 0 then gameHost is considered a normal game server + NetRoomID gameRoomID = 0; + + //Game info + std::string gameName = {}; + std::string gameVersion = {}; uint8_t maximumPlayers = 0; - uint8_t currrentPlayers = 0; - bool flag_gameRun = false; - int ping = 0; - std::string world; - - GameStatusInfo() = default; - - GameStatusInfo(uint8_t _maxPlayers, uint8_t _curPlayers, bool _flag_gameRun, int _ping, const std::string& world): GameStatusInfo() { - set(_maxPlayers, _curPlayers, _flag_gameRun, _ping, world); - } - - void set(uint8_t _maxPlayers, uint8_t _curPlayers, bool _flag_gameRun, int _ping, const std::string& _world) { - maximumPlayers=_maxPlayers; - currrentPlayers=_curPlayers; - flag_gameRun=_flag_gameRun; - ping=_ping; - world=_world; - } -}; - -//Contains info about host and game inside host -struct GameHostInfo { - NetAddress gameHost; - std::string hostName; - std::string gameName; - GameStatusInfo gameStatusInfo; - - GameHostInfo() = default; - - GameHostInfo(const NetAddress* _gameHost, const char * _hostName, const char * _gameName, const GameStatusInfo* gsi): GameHostInfo() { - set(_gameHost, _hostName, _gameName, gsi); - } - - void set(const NetAddress* _gameHost, const char * _hostName, const char * _gameName, const GameStatusInfo* gsi){ - if (_gameHost) gameHost = *_gameHost; - if (_hostName) hostName = _hostName; - if (_gameName) gameName = _gameName; - if (gsi) gameStatusInfo = *gsi; - } + uint8_t currentPlayers = 0; + bool hasPassword = false; + bool gameStarted = false; + uint32_t ping = 0; + std::string scenario = {}; + GAME_CONTENT gameContent = CONTENT_NONE; }; class ServerList { private: bool findingHosts = false; + NetConnection* relayConnection = nullptr; + ///List updated from 2nd thread with data from relay + std::vector lastRelayGameInfoList = {}; + uint32_t lastRelayFetch = 0; + ///List updated from other lists in 1st thread + std::vector gameInfoList = {}; + ///Flag to know if gameHostInfoList needs updating + bool listNeedUpdate = false; + + bool checkRelayConnection(); public: - std::vector gameHostInfoList{}; - ServerList(); ~ServerList(); - void startHostFind(); - void stopHostFind(); - void clearHostInfoList(); + void startFind(); + void stopFind(); + void fetchRelayHostInfoList(); void refreshHostInfoList(); + + const std::vector& getList() const { return gameInfoList; }; }; #endif //PERIMETER_SERVERLIST_H diff --git a/Source/Render/CMakeLists.txt b/Source/Render/CMakeLists.txt index ca61f6c8a..fb384e456 100644 --- a/Source/Render/CMakeLists.txt +++ b/Source/Render/CMakeLists.txt @@ -1,3 +1,7 @@ + +OPTION(OPTION_DXVK_1 "Use old DXVK-native (DXVK 1.x) instead of DXVK 2.x" OFF) +OPTION(OPTION_DXVK_SOURCE_DIR "Path for predownloaded DXVK source code") + SET(Render_SRCS src/Unknown.cpp src/umath.cpp diff --git a/Source/Render/sokol/CMakeLists.txt b/Source/Render/sokol/CMakeLists.txt index f37054462..c302f9443 100644 --- a/Source/Render/sokol/CMakeLists.txt +++ b/Source/Render/sokol/CMakeLists.txt @@ -1,3 +1,6 @@ + +OPTION(OPTION_PROCESS_SHADERS "Re-Process game shaders" OFF) + #Include Sokol FetchContent_Declare(sokol GIT_REPOSITORY https://github.com/floooh/sokol diff --git a/Source/Render/src/Scene.cpp b/Source/Render/src/Scene.cpp index 64e8cfc31..cd3580de0 100644 --- a/Source/Render/src/Scene.cpp +++ b/Source/Render/src/Scene.cpp @@ -701,13 +701,13 @@ void cScene::CheckPendingObjects(std::vector& allowed) { UpdateLists(INT_MAX); for (auto e : grid) { if (std::count(allowed.begin(), allowed.end(), e) == 0) { - fprintf(stderr, "Pending Object: %p refs %" PRIiMAX "\n", e, e->GetRef()); + fprintf(stderr, "Pending Object: %p refs %" PRIi64 "\n", e, e->GetRef()); xxassert(0, "Pending Object"); } } for (auto e : UnkLightArray) { if (std::count(allowed.begin(), allowed.end(), e) == 0) { - fprintf(stderr, "Pending Light: %p refs %" PRIiMAX "\n", e, e->GetRef()); + fprintf(stderr, "Pending Light: %p refs %" PRIi64 "\n", e, e->GetRef()); xxassert(0, "Pending Light"); } } diff --git a/Source/Scripts/InterfaceScriptExport.prm b/Source/Scripts/InterfaceScriptExport.prm index 4c3f83d21..05f485cf7 100644 --- a/Source/Scripts/InterfaceScriptExport.prm +++ b/Source/Scripts/InterfaceScriptExport.prm @@ -4783,6 +4783,37 @@ sqshControlContainer _sqsh_controls[int _sqsh_control_count] = { defaultEnterBtnID = SQSH_MM_MULTIPLAYER_JOIN_NEXT_BTN; defaultEscBtnID = SQSH_MM_BACK_FROM_MULTIPLAYER_JOIN_BTN; }, +{ + id = SQSH_MM_MULTIPLAYER_PASSWORD_SCR; + load_group = SHELL_LOAD_GROUP_MENU; + x = 0; + y = 0; + sx = 1024; + sy = 768; + type = SQSH_MULTITEX_WINDOW; + image = {texture="resource\\icons\\MainMenu\\t.avi";hasResolutionVersion=0;}; + image2 = {texture="resource\\icons\\MainMenu\\il.tga";}; + controls = new sqshControl[] { + //BG + {type = SQSH_MOVE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=339/1024; y=0/768; sx=330/1024; sy=527/768;image={texture="resource\\icons\\MainMenu\\new_menu.tga";_ix = 0; _iy = 0; ix = 330; iy = 527; };state = SQSH_ENABLED; xstart=359; ystart=-768; }, + + //Game name + {type = SQSH_TEXT_WINDOW_TYPE; id = SQSH_MM_MULTIPLAYER_PASSWORD_NAME_TXT; x=405/1024; y=160/768; sx=205/1024; sy=42/768; txt_align=SHELL_ALIGN_CENTER; image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;};txt_dx = 0.7f;}, + + //Password edit box + {type = SQSH_PUSH_BUTTON_TYPE; id = SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_BTN; x=400/1024; y=240/768; sx=205/1024; sy=42/768; text="Password"; font_group=2; txt_dy = 7/768; state = SQSH_ENABLED; sound = "mainmenu_button";image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;};txt_align=SHELL_ALIGN_CENTER;target=SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_INPUT;}, + {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=400/1024; y=280/768; sx=205/1024; sy=45/768; image={texture="resource\\icons\\MainMenu\\new_menu.tga";_ix = 340; _iy = 291; ix = 145; iy = 45;}; ystart=45/768; hitTestMode = HITTEST_NONE; }, + {type = SQSH_EDIT_BOX_TYPE; id = SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_INPUT; x=407/1024; y=282/768; sx=191/1024; sy=41/768; txt_dy = 9/768;state = SQSH_ENABLED; txt_align=SHELL_ALIGN_LEFT;sound = "mainmenu_button";image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;}; alnum = 0;}, + + //Back/Join + {type = SQSH_SCALE_BUTTON_TYPE; x=391/1024; y=431/768; sx=96/1024; sy=42/768; image={texture="resource\\icons\\MainMenu\\new_menu.tga";_ix = 340; _iy = 170; ix = 96; iy = 42;}; ystart=42/768; hitTestMode = HITTEST_NONE; }, + {type = SQSH_PUSH_BUTTON_TYPE; id = SQSH_MM_BACK_FROM_MULTIPLAYER_PASSWORD_BTN; x=397/1024; y=433/768; sx=84/1024; sy=38/768; text="BACK"; font_group=2; txt_dy = 13/768; state = SQSH_ENABLED; sound = "mainmenu_button"; }, + {type = SQSH_SCALE_BUTTON_TYPE; x=519/1024; y=431/768; sx=96/1024; sy=42/768; image={texture="resource\\icons\\MainMenu\\new_menu.tga";_ix = 340; _iy = 170; ix = 96; iy = 42;}; ystart=42/768; hitTestMode = HITTEST_NONE; }, + {type = SQSH_PUSH_BUTTON_TYPE; id = SQSH_MM_MULTIPLAYER_PASSWORD_NEXT_BTN; x=525/1024; y=433/768; sx=84/1024; sy=38/768; text="JOIN"; font_group=2; txt_dy = 13/768; state = SQSH_ENABLED; sound = "mainmenu_button"; } + }; + defaultEnterBtnID = SQSH_MM_MULTIPLAYER_PASSWORD_NEXT_BTN; + defaultEscBtnID = SQSH_MM_BACK_FROM_MULTIPLAYER_PASSWORD_BTN; +}, { id = SQSH_MM_MULTIPLAYER_LIST_SCR; load_group = SHELL_LOAD_GROUP_MENU; @@ -4794,38 +4825,35 @@ sqshControlContainer _sqsh_controls[int _sqsh_control_count] = { // image = {texture="resource\\icons\\MainMenu\\t.avi";hasResolutionVersion=0;}; // image2 = {texture="resource\\icons\\MainMenu\\il.tga";}; controls = new sqshControl[] { -/* TODO - //Game list frame + //Frame {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=186/1024; y=94/768; sx=390/1024; sy=515/768; image={texture="resource\\icons\\MainMenu\\Main_menu.tga";_ix = 0; _iy = 0; ix = 300; iy = 515;}; state = SQSH_ENABLED; ystart=515/768;image_h={texture="resource\\icons\\MainMenu\\tv.avi";_ix = 0; _iy = 0; ix = 60; iy = 120;hasResolutionVersion=0;}; hitTestMode = HITTEST_NONE; }, - //Game list bar - {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=(390+155)/1024; y=100/768; sx=24/1024; sy=458/768; image={texture="resource\\icons\\MainMenu\\Main_menu.tga";_ix = 302; _iy = 33; ix = 24; iy = 458;}; state = SQSH_ENABLED; ystart=458/768;image_h={texture="resource\\icons\\MainMenu\\tv.avi";_ix = 0; _iy = 0; ix = 10; iy = 120;hasResolutionVersion=0;}; hitTestMode = HITTEST_NONE; }, + //List bar + {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=(390+155)/1024; y=142/768; sx=24/1024; sy=458/768; image={texture="resource\\icons\\MainMenu\\Main_menu.tga";_ix = 302; _iy = 33; ix = 24; iy = 458;}; state = SQSH_ENABLED; ystart=458/768;image_h={texture="resource\\icons\\MainMenu\\tv.avi";_ix = 0; _iy = 0; ix = 10; iy = 120;hasResolutionVersion=0;}; hitTestMode = HITTEST_NONE; }, + + //Player name box + {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=197/1024; y=98/768; sx=75/1024; sy=42/768; state = SQSH_ENABLED; ystart=64/768; hitTestMode = HITTEST_NONE; }, + {type = SQSH_PUSH_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=197/1024; y=98/768; sx=75/1024; sy=42/768; text="Name"; font_group=2; txt_dy = 7/768; state = SQSH_ENABLED; sound = "mainmenu_button";image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;};target=SQSH_MM_MULTIPLAYER_NAME_INPUT;}, + {type = SQSH_EDIT_BOX_TYPE; id = SQSH_MM_MULTIPLAYER_NAME_INPUT; x=(197 + 75)/1024; y=98/768; sx=200/1024; sy=32/768; txt_dy = 9/768;state = SQSH_ENABLED; txt_align=SHELL_ALIGN_LEFT;sound = "mainmenu_button";image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;}; alnum = 0;}, + + //Separator + {type = SQSH_GENERAL_WND_TYPE; id = SQSH_MM_RAMKA; x=200/1024; y=134/768; sx=(390-28)/1024; sy=3/768; image={texture="resource\\icons\\MainMenu\\Main_menu.tga";_ix = 2; _iy = 987; ix = 272; iy = 3;}; state = SQSH_ENABLED; txt_dx =0.3f; }, //Game list - {type = SQSH_LIST_BOX_TYPE; id = SQSH_MM_MULTIPLAYER_LIST_GAME_LIST; x=189/1024; y=100/768; sx=(390-10)/1024; sy=458/768; + {type = SQSH_LIST_BOX_TYPE; id = SQSH_MM_MULTIPLAYER_LIST_GAME_LIST; x=189/1024; y=142/768; sx=(390-10)/1024; sy=458/768; image_check={texture="resource\\icons\\MainMenu\\tv.avi";_ix = 0; _iy = 0; ix = 24; iy = 24;dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;}; image={texture="resource\\icons\\MainMenu\\Main_menu.tga";_ix = 0; _iy = 992; ix = 226; iy = 26;}; image_h={texture="resource\\icons\\MainMenu\\Main_menu.tga";_ix = 328; _iy = 427; ix = 24; iy = 30;}; txt_dx = listBoxTextLeftOffset; txt_dy = 7/768; xstart = 24/1024; ystart = 35/768;}, - //Current selected game map view - {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=646/1024; y=55/768; sx=256/1024; sy=256/768; ystart=256/768; hitTestMode = HITTEST_NONE; }, - {type = SQSH_MAPWINDOW; id = SQSH_MM_MULTIPLAYER_LIST_MAP; x=646/1024; y=55/768; sx=256/1024; sy=256/768; image={texture="";ix = 1; iy = 1;};image_h={texture="resource\\icons\\MainMenu\\tv.avi";_ix = 0; _iy = 0; ix = 120; iy = 120;hasResolutionVersion=0;}; }, - {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=mainMenuMap.x; y=mainMenuMap.y; sx=mainMenuMap.sx; sy=mainMenuMap.sy; ystart=296/768; hitTestMode = HITTEST_NONE; }, - {type = SQSH_TEXT_WINDOW_TYPE; id = SQSH_MM_MULTIPLAYER_LIST_MAP_DESCR_TXT; x=mainMenuMap.x; y=mainMenuMap.y; sx=mainMenuMap.sx; sy=mainMenuMap.sy; txt_align=SHELL_ALIGN_LEFT; image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;};txt_dx = 0.7f;}, + {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=646/1024; y=55/768; sx=256/1024; sy=256/768; ystart=256/768; hitTestMode = HITTEST_NONE; }, + {type = SQSH_MAPWINDOW; id = SQSH_MM_MULTIPLAYER_LIST_MAP; x=646/1024; y=55/768; sx=256/1024; sy=256/768; image={texture="";ix = 1; iy = 1;};image_h={texture="resource\\icons\\MainMenu\\tv.avi";_ix = 0; _iy = 0; ix = 120; iy = 120;hasResolutionVersion=0;}; }, + {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=mainMenuMap.x; y=mainMenuMap.y; sx=mainMenuMap.sx; sy=mainMenuMap.sy; ystart=296/768; hitTestMode = HITTEST_NONE; }, + {type = SQSH_TEXT_WINDOW_TYPE; id = SQSH_MM_MULTIPLAYER_LIST_MAP_DESCR_TXT; x=mainMenuMap.x; y=mainMenuMap.y; sx=mainMenuMap.sx; sy=mainMenuMap.sy; txt_align=SHELL_ALIGN_LEFT; image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;};txt_dx = 0.7f;}, //Join {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=595/1024; y=565/768; sx=220/1024; sy=45/768; image={texture="resource\\icons\\MainMenu\\new_menu.tga";_ix = 340; _iy = 234; ix = 194; iy = 45;}; ystart=45/768; hitTestMode = HITTEST_NONE; }, {type = SQSH_PUSH_BUTTON_TYPE; id = SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN; x=(595+7)/1024; y=(565+2)/768; sx=(220-14)/1024; sy=(45-4)/768; text="JOIN"; font_group=2; txt_dx=81; txt_dy = 11; sound = "mainmenu_button"; state = SQSH_ENABLED; }, - //Separator - {type = SQSH_GENERAL_WND_TYPE; id = SQSH_MM_RAMKA; x=200/1024; y=565/768; sx=(390-28)/1024; sy=3/768; image={texture="resource\\icons\\MainMenu\\Main_menu.tga";_ix = 2; _iy = 987; ix = 272; iy = 3;}; state = SQSH_ENABLED; txt_dx =0.3f; }, -//*/ - - //Player name box - {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=197/1024; y=565/768; sx=75/1024; sy=42/768; state = SQSH_ENABLED; ystart=64/768; hitTestMode = HITTEST_NONE; }, - {type = SQSH_PUSH_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=197/1024; y=565/768; sx=75/1024; sy=42/768; text="Name"; font_group=2; txt_dy = 7/768; state = SQSH_ENABLED; sound = "mainmenu_button";image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;};target=SQSH_MM_MULTIPLAYER_NAME_INPUT;}, - {type = SQSH_EDIT_BOX_TYPE; id = SQSH_MM_MULTIPLAYER_NAME_INPUT; x=(197 + 80)/1024; y=565/768; sx=200/1024; sy=32/768; txt_dy = 9/768;state = SQSH_ENABLED; txt_align=SHELL_ALIGN_LEFT;sound = "mainmenu_button";image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;}; alnum = 0;}, - //Back / Direct / Create {type = SQSH_SCALE_BUTTON_TYPE; id = SQSH_MM_RAMKA; x=rightSeparateBottomBtn.x; y=rightSeparateBottomBtn.y; sx=rightSeparateBottomBtn.sx; sy=rightSeparateBottomBtn.sy; hitTestMode = HITTEST_NONE; }, {type = SQSH_PUSH_BUTTON_TYPE; id = SQSH_MM_MULTIPLAYER_LIST_CREATE_BTN; x=rightSeparateBottomBtn.x; y=rightSeparateBottomBtn.y; sx=rightSeparateBottomBtn.sx; sy=rightSeparateBottomBtn.sy; text="CREATE"; font_group=2; txt_dy = 9/768; state = SQSH_ENABLED; sound = "mainmenu_button"; bgObject = {name = "group next2"; chainName = "next2";};image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;}; }, @@ -4835,16 +4863,15 @@ sqshControlContainer _sqsh_controls[int _sqsh_control_count] = { {type = SQSH_PUSH_BUTTON_TYPE; id = SQSH_MM_BACK_FROM_MULTIPLAYER_LIST_BTN; x=leftSeparateBottomBtn.x; y=leftSeparateBottomBtn.y; sx=leftSeparateBottomBtn.sx; sy=leftSeparateBottomBtn.sy; text="BACK"; font_group=2; txt_dy = 9/768; state = SQSH_ENABLED; sound = "mainmenu_button";image={texture="resource\\icons\\MainMenu\\tv.avi";dx=339/1024;dy=157/768;dsx=128/1024;dsy=128/768;hasResolutionVersion=0;}; } }; bgObjects = new BGObj[] { - //TODO {name = "group battle"; chainName = "battle";}, + {name = "group battle"; chainName = "battle";}, {name = "group back2"; chainName = "back2";}, {name = "group skip"; chainName = "skip";}, {name = "group next2"; chainName = "next2";}, - //TODO {name = "group map"; chainName = "map";}, - {name = "group text"; chainName = "text";}, //TODO remove this when game list works + {name = "group map"; chainName = "map";}, {name = "group background"; chainName = "background off";} }; defaultEnterBtnID = SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN; - defaultEnterBtnIDAux = SQSH_MM_MULTIPLAYER_LIST_CREATE_BTN; + //defaultEnterBtnIDAux = SQSH_MM_MULTIPLAYER_LIST_CREATE_BTN; defaultEscBtnID = SQSH_MM_BACK_FROM_MULTIPLAYER_LIST_BTN; }, { diff --git a/Source/Scripts/SquadShellEnums.inl b/Source/Scripts/SquadShellEnums.inl index 73b9dc337..bf7b0bab0 100644 --- a/Source/Scripts/SquadShellEnums.inl +++ b/Source/Scripts/SquadShellEnums.inl @@ -220,6 +220,7 @@ enum ShellControlID SQSH_MM_MISSION_TASK_SCR, SQSH_MM_MULTIPLAYER_LIST_SCR, SQSH_MM_MULTIPLAYER_JOIN_SCR, + SQSH_MM_MULTIPLAYER_PASSWORD_SCR, SQSH_MM_MULTIPLAYER_LOBBY_SCR, SQSH_MM_OPTIONS_SCR, SQSH_MM_GRAPHICS_SCR, @@ -429,6 +430,13 @@ enum ShellControlID SQSH_MM_MULTIPLAYER_JOIN_IP_BTN, SQSH_MM_MULTIPLAYER_JOIN_PASSWORD_INPUT, SQSH_MM_MULTIPLAYER_JOIN_PASSWORD_BTN, + + //multiplayer password + SQSH_MM_MULTIPLAYER_PASSWORD_NEXT_BTN, + SQSH_MM_BACK_FROM_MULTIPLAYER_PASSWORD_BTN, + SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_INPUT, + SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_BTN, + SQSH_MM_MULTIPLAYER_PASSWORD_NAME_TXT, //multiplayer list (prev lan) SQSH_MM_MULTIPLAYER_BTN, diff --git a/Source/Units/Squad.cpp b/Source/Units/Squad.cpp index fa56cdfdb..4a5c04618 100644 --- a/Source/Units/Squad.cpp +++ b/Source/Units/Squad.cpp @@ -1014,9 +1014,10 @@ int terUnitSquad::dischargeTechnics(int count) void terUnitSquad::initMutation(SquadUnitList& sources, SquadUnitList& destinations) { - xassert(!sources.empty() && !destinations.empty()); - if(sources.empty() || destinations.empty()) - return; + if(sources.empty() || destinations.empty()) { + //xassert(0); + return; + } setStablePosition(average_position + average_position_offset); check_readiness_to_move = true; diff --git a/Source/UserInterface/GameShell.cpp b/Source/UserInterface/GameShell.cpp index 3ce08779f..87a7be548 100644 --- a/Source/UserInterface/GameShell.cpp +++ b/Source/UserInterface/GameShell.cpp @@ -241,9 +241,10 @@ windowClientSize_(1024, 768) ErrH.Abort("Pause!!!"); } - const char* server = check_command_line("server"); - const char* connect = check_command_line("connect"); - if (server || connect) { + const char* server = check_command_line("server"); + const char* connect_addr = check_command_line("connect"); + const char* connect_room = check_command_line("connect_room"); + if (server || connect_addr || connect_room) { CommandLineData data; data.server = server != nullptr; @@ -261,13 +262,27 @@ windowClientSize_(1024, 768) const char* roomName = check_command_line("room"); if (roomName) data.roomName = roomName; data.address = server; - } else { - checkCmdLineArg(connect, "connect"); - data.address = connect; + data.publicHost = check_command_line("public") != nullptr; + } else if (connect_room) { + checkCmdLineArg(connect_room, "connect_room"); + NetRoomID room = strtoull(connect_room, nullptr, 10); + if (room == 0) { + ErrH.Abort("Couldn't parse connect_room to number"); + } + data.roomID = room; + data.publicHost = true; + data.addressDefaultPort = NET_RELAY_DEFAULT_PORT; + if (connect_addr) { + data.address = connect_addr; + } + } else if (connect_addr) { + checkCmdLineArg(connect_addr, "connect"); + data.address = connect_addr; + data.publicHost = false; } - data.publicHost = check_command_line("public") != nullptr; startCmdline(data); + return; } else if (MainMenuEnable) { startedWithMainmenu = true; _shellIconManager.LoadControlsGroup(SHELL_LOAD_GROUP_MENU); diff --git a/Source/UserInterface/GameShell.h b/Source/UserInterface/GameShell.h index 588fd9758..e5366e94f 100644 --- a/Source/UserInterface/GameShell.h +++ b/Source/UserInterface/GameShell.h @@ -15,13 +15,15 @@ struct LocalizedText; class MissionEditor; struct CommandLineData { - bool server; - std::string save; - std::string address; - std::string playerName; - std::string roomName; - std::string password; - bool publicHost; + bool server = false; + std::string save {}; + std::string address {}; + std::string playerName {}; + std::string roomName {}; + std::string password {}; + NetRoomID roomID = 0; + uint16_t addressDefaultPort = 0; + bool publicHost = true; }; //------------------------------------------ @@ -242,6 +244,7 @@ class GameShell void callBack_CreateGameReturnCode(e_CreateGameReturnCode retCode); enum e_JoinGameReturnCode { JG_RC_OK, + JG_RC_UNKNOWN_ERR, JG_RC_SIGNATURE_ERR, JG_RC_PASSWORD_ERR, JG_RC_CONNECTION_ERR, diff --git a/Source/UserInterface/MainMenu.cpp b/Source/UserInterface/MainMenu.cpp index 4a8f0deb3..fe8fdee8c 100644 --- a/Source/UserInterface/MainMenu.cpp +++ b/Source/UserInterface/MainMenu.cpp @@ -1116,7 +1116,8 @@ int SwitchMenuScreenQuant1( float, float ) { } break; case SQSH_MM_MULTIPLAYER_LIST_SCR: - { + { + _shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN)->Enable(0); std::string name = getStringSettings(regLanName); if (name.empty() && gameShell->currentSingleProfile.isValidProfile()) { name = gameShell->currentSingleProfile.getCurrentProfile()->name; @@ -1179,13 +1180,20 @@ int SwitchMenuScreenQuant1( float, float ) { break; case SQSH_MM_MULTIPLAYER_JOIN_SCR: { - CEditWindow* input = (CEditWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_JOIN_IP_INPUT); + dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_JOIN_PASSWORD_INPUT))->SetText(""); + + //Fill out saved IP if any + CEditWindow* input = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_JOIN_IP_INPUT)); if (input->isEmptyText()) { std::string text = getStringSettings("JoinIP"); input->SetText(text.c_str()); } } break; + case SQSH_MM_MULTIPLAYER_PASSWORD_SCR: { + dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_INPUT))->SetText(""); + break; + } } //show and start effect if (_id_on != SQSH_MM_SUBMIT_DIALOG_SCR) { @@ -1641,6 +1649,7 @@ void onMMBackButton(CShellWindow* pWnd, InterfaceEventCode code, int param) { break; case SQSH_MM_MULTIPLAYER_JOIN_SCR: case SQSH_MM_MULTIPLAYER_LOBBY_SCR: + case SQSH_MM_MULTIPLAYER_PASSWORD_SCR: nShow = SQSH_MM_MULTIPLAYER_LIST_SCR; break; case SQSH_MM_MULTIPLAYER_HOST_SCR: diff --git a/Source/UserInterface/Menu/MultiplayerCommon.cpp b/Source/UserInterface/Menu/MultiplayerCommon.cpp index 4e09f1b45..5d02597b3 100644 --- a/Source/UserInterface/Menu/MultiplayerCommon.cpp +++ b/Source/UserInterface/Menu/MultiplayerCommon.cpp @@ -91,7 +91,13 @@ int exitMultiplayerGameQuant(float, float ) { int returnToMultiplayerScreenQuant(float, float ) { if (menuChangingDone) { - _shellIconManager.SwitchMenuScreens( LAST_VISIBLE, SQSH_MM_MULTIPLAYER_LIST_SCR ); + int id = _shellIconManager.getVisibleMenuScr(); + if (id == SQSH_MM_MULTIPLAYER_LIST_SCR) { + gameShell->createNetClient(); + gameShell->getNetClient()->StartFindHost(); + } else { + _shellIconManager.SwitchMenuScreens(LAST_VISIBLE, SQSH_MM_MULTIPLAYER_LIST_SCR); + } return 0; } return 1; @@ -99,7 +105,7 @@ int returnToMultiplayerScreenQuant(float, float ) { int exitToMultiplayerScreenAction(float, float ) { if (!gameShell->isStartedWithMainmenu()) { - gameShell->GameContinue = 0; + gameShell->GameContinue = false; } else if (universe()) { hideMessageBox(); _shellIconManager.AddDynamicHandler(exitMultiplayerGameQuant, CBCODE_QUANT); @@ -113,7 +119,6 @@ int exitToMultiplayerScreenAction(float, float ) { int multiplayerMapNotFoundQuant(float, float ) { if (menuChangingDone) { gameShell->getNetClient()->FinishGame(); - gameShell->getNetClient()->StartFindHost(); showMessageBox(); return 0; } @@ -269,9 +274,12 @@ int showGeneralErrorMessageQuant(float, float ) { break; } - setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText(textID.c_str()), MBOX_OK); - gameShell->prepareForInGameMenu(); -// fout < "showGeneralErrorMessageQuant showMessageBox()\n"; + if (universe()) { + setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText(textID.c_str()), MBOX_OK); + gameShell->prepareForInGameMenu(); + } else { + setupOkMessageBox(exitToMultiplayerScreenAction, 0, qdTextDB::instance().getText(textID.c_str()), MBOX_OK); + } showMessageBox(); return 0; } diff --git a/Source/UserInterface/Menu/MultiplayerCommon.h b/Source/UserInterface/Menu/MultiplayerCommon.h index 89e11e098..44c1e0533 100644 --- a/Source/UserInterface/Menu/MultiplayerCommon.h +++ b/Source/UserInterface/Menu/MultiplayerCommon.h @@ -7,5 +7,6 @@ extern std::vector multiplayerMaps; extern std::vector multiplayerSaves; void loadMultiplayerList(); +int exitToMultiplayerScreenAction(float, float); int getMultiplayerMapNumber(const std::string& missionName); diff --git a/Source/UserInterface/Menu/MultiplayerHost.cpp b/Source/UserInterface/Menu/MultiplayerHost.cpp index efad2fc73..69b17454c 100644 --- a/Source/UserInterface/Menu/MultiplayerHost.cpp +++ b/Source/UserInterface/Menu/MultiplayerHost.cpp @@ -55,12 +55,12 @@ int creatingHostDialogQuant(float, float ) { if (!portInput->isEmptyText()) { address += ":" + portInput->getText(); } - bool resolveFailed = !NetAddress::resolve(conn, address); + bool resolveOK = NetAddress::resolve(conn, address); if (multiplayerMaps.empty()) { setMessageBoxTextID("Interface.Menu.Messages.UnknownError"); showMessageBoxButtons(); - } else if (resolveFailed) { + } else if (!resolveOK) { setMessageBoxTextID("Interface.Menu.Messages.WrongIPPort"); showMessageBoxButtons(); } else { @@ -70,6 +70,8 @@ int creatingHostDialogQuant(float, float ) { std::string gameName = gameNameInput->getText(); CEditWindow* passwordInput = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_HOST_PASSWORD_INPUT)); std::string password = passwordInput->getText(); + CComboWindow* hostTypeCombo = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_HOST_TYPE_COMBO)); + bool isPublic = hostTypeCombo->pos == 0; putStringSettings("HostName", gameName); putStringSettings("HostPort", std::to_string(conn.port())); @@ -87,7 +89,7 @@ int creatingHostDialogQuant(float, float ) { mission = new MissionDescription(missionName.c_str(), GT_MULTI_PLAYER_CREATE); } - gameShell->getNetClient()->CreateGame(conn, gameName, mission, playerName, password); + gameShell->getNetClient()->CreateGame(isPublic, conn, gameName, mission, playerName, password); } return 0; } @@ -99,9 +101,8 @@ int creatingHostDialogQuant(float, float ) { void onMMMultiplayerHostTypeCombo(CShellWindow* pWnd, InterfaceEventCode code, int param) { CComboWindow *pCombo = (CComboWindow*) pWnd; if (code == EVENT_CREATEWND) { + pCombo->Array.emplace_back(getItemTextFromBase("Public Server").c_str() ); pCombo->Array.emplace_back(getItemTextFromBase("Private Server").c_str() ); - //TODO enable this once implemented the public listing - //pCombo->Array.emplace_back(getItemTextFromBase("Public Server").c_str() ); pCombo->size = pCombo->Array.size(); pCombo->pos = 0; } diff --git a/Source/UserInterface/Menu/MultiplayerJoin.cpp b/Source/UserInterface/Menu/MultiplayerJoin.cpp index e2fa3e18b..f3cb56bef 100644 --- a/Source/UserInterface/Menu/MultiplayerJoin.cpp +++ b/Source/UserInterface/Menu/MultiplayerJoin.cpp @@ -5,7 +5,6 @@ #include "MultiplayerCommon.h" #include "MainMenu.h" - /// Join game to lobby handler int toLobbyQuant( float, float ) { @@ -22,7 +21,6 @@ int toLobbyQuant( float, float ) { return 1; } - int hideBoxToLobbyQuant( float, float ) { if (menuChangingDone) { hideMessageBox(); @@ -64,8 +62,8 @@ void GameShell::callBack_JoinGameReturnCode(e_JoinGameReturnCode retCode, std::s case JG_RC_SIGNATURE_ERR: textID = "Interface.Menu.Messages.Multiplayer.SignatureError"; break; + case JG_RC_UNKNOWN_ERR: default: - xassert(0); textID = "Interface.Menu.Messages.UnknownError"; break; } @@ -79,29 +77,19 @@ void GameShell::callBack_JoinGameReturnCode(e_JoinGameReturnCode retCode, std::s showMessageBoxButtons(); } -int multiplayerJoinBackHandler( float, float ) { - //handles connection failed - gameShell->getNetClient()->FinishGame(); - gameShell->getNetClient()->StartFindHost(); - hideMessageBox(); - return 1; -} - int joinHostHandler( float, float ) { if (menuChangingDone) { + CEditWindow* playerNameInput = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT)); + std::string playerName = playerNameInput->GetText(); + CEditWindow* passwordInput = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_JOIN_PASSWORD_INPUT)); + std::string password = passwordInput->getText(); CEditWindow* addressInput = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_JOIN_IP_INPUT)); NetAddress conn; - bool resolveFailed = !NetAddress::resolve(conn, addressInput->getText()); - - if (resolveFailed) { + if (!NetAddress::resolve(conn, addressInput->getText())) { setMessageBoxTextID("Interface.Menu.Messages.IPEmpty"); showMessageBoxButtons(); } else { - CEditWindow* playerNameInput = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT)); - std::string playerName = playerNameInput->GetText(); - CEditWindow* passwordInput = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_JOIN_PASSWORD_INPUT)); - std::string password = passwordInput->getText(); - gameShell->getNetClient()->JoinGame(conn, playerName, password); + gameShell->getNetClient()->JoinDirectGame(conn, playerName, password); } return 0; } @@ -110,22 +98,22 @@ int joinHostHandler( float, float ) { void onMMMultiplayerJoinNextBtn(CShellWindow* pWnd, InterfaceEventCode code, int param) { if( code == EVENT_UNPRESSED && intfCanHandleInput() ) { - CEditWindow* input = (CEditWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT); - CEditWindow* ipInput = (CEditWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_JOIN_IP_INPUT); - if (input->isEmptyText()) { + CEditWindow* name_input = (CEditWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT); + CEditWindow* ip_input = (CEditWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_JOIN_IP_INPUT); + if (name_input->isEmptyText()) { setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText("Interface.Menu.Messages.NameEmpty"), MBOX_OK); showMessageBox(); - } else if (ipInput->isEmptyText()) { + } else if (ip_input->isEmptyText()) { setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText("Interface.Menu.Messages.IPEmpty"), MBOX_OK); showMessageBox(); } else { - putStringSettings(regLanName, input->getText()); - putStringSettings("JoinIP", ipInput->getText()); + putStringSettings(regLanName, name_input->getText()); + putStringSettings("JoinIP", ip_input->getText()); - setupOkMessageBox(multiplayerJoinBackHandler, 0, qdTextDB::instance().getText("Interface.Menu.Messages.Connecting"), MBOX_BACK, false); + setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText("Interface.Menu.Messages.Connecting"), MBOX_OK, false); showMessageBox(); - + _shellIconManager.AddDynamicHandler( joinHostHandler, CBCODE_QUANT ); } } -} \ No newline at end of file +} diff --git a/Source/UserInterface/Menu/MultiplayerList.cpp b/Source/UserInterface/Menu/MultiplayerList.cpp index 578aaa49e..e08364aa4 100644 --- a/Source/UserInterface/Menu/MultiplayerList.cpp +++ b/Source/UserInterface/Menu/MultiplayerList.cpp @@ -4,6 +4,8 @@ #include "MessageBox.h" #include "MultiplayerCommon.h" #include "MainMenu.h" +#include "ServerList.h" +#include "GameContent.h" ///Command line stuff @@ -18,48 +20,54 @@ int startCmdlineQuant(float, float ) { if (menuChangingDone) { gameShell->createNetClient(); - auto client = gameShell->getNetClient(); - client->publicServerHost = cmdLineData.publicHost; - NetAddress conn; - if (!NetAddress::resolve(conn, cmdLineData.address)) { + bool resolved; + if (cmdLineData.address.empty() && cmdLineData.roomID != 0) { + resolved = !getNetRelayAddress(conn); + } else { + resolved = NetAddress::resolve(conn, cmdLineData.address, cmdLineData.addressDefaultPort); + } + if (!resolved) { setMessageBoxTextID("Interface.Menu.Messages.IPEmpty"); showMessageBoxButtons(); - } else { - //This forces initializing stuff required by game - _shellIconManager.initialMenu = SQSH_MM_MULTIPLAYER_LOBBY_SCR; - if (cmdLineData.server) { - loadMultiplayerList(); - if (multiplayerMaps.empty()) { - setMessageBoxTextID("Interface.Menu.Messages.UnknownError"); - showMessageBoxButtons(); + return 0; + } + + //This forces initializing stuff required by game + _shellIconManager.initialMenu = SQSH_MM_MULTIPLAYER_LOBBY_SCR; + if (cmdLineData.server) { + loadMultiplayerList(); + if (multiplayerMaps.empty()) { + setMessageBoxTextID("Interface.Menu.Messages.UnknownError"); + showMessageBoxButtons(); + } else { + setMessageBoxTextID("Interface.Menu.Messages.Creating"); + std::string gameName; + if (cmdLineData.roomName.empty()) { + gameName = cmdLineData.playerName; } else { - setMessageBoxTextID("Interface.Menu.Messages.Creating"); - std::string gameName; - if (cmdLineData.roomName.empty()) { - gameName = cmdLineData.playerName; - } else { - gameName = cmdLineData.roomName; - } - MissionDescription* mission; - if (cmdLineData.save.empty()) { - std::string missionName = std::string("RESOURCE/MULTIPLAYER/") + multiplayerMaps[0].missionName(); - mission = new MissionDescription(missionName.c_str(), GT_MULTI_PLAYER_CREATE); - } else { - std::string missionName = std::string("RESOURCE/SAVES/MULTIPLAYER/") + cmdLineData.save; - mission = new MissionDescription(missionName.c_str(), GT_MULTI_PLAYER_LOAD); - mission->loadIntoMemory(); - } - _shellIconManager.initialMenu = SQSH_MM_MULTIPLAYER_LOBBY_SCR; - gameShell->getNetClient()->CreateGame(conn, gameName, mission, cmdLineData.playerName, cmdLineData.password); - showMessageBoxButtons(); + gameName = cmdLineData.roomName; } - } else { - gameShell->getNetClient()->JoinGame(conn, cmdLineData.playerName, cmdLineData.password); + MissionDescription* mission; + if (cmdLineData.save.empty()) { + std::string missionName = std::string("RESOURCE/MULTIPLAYER/") + multiplayerMaps[0].missionName(); + mission = new MissionDescription(missionName.c_str(), GT_MULTI_PLAYER_CREATE); + } else { + std::string missionName = std::string("RESOURCE/SAVES/MULTIPLAYER/") + cmdLineData.save; + mission = new MissionDescription(missionName.c_str(), GT_MULTI_PLAYER_LOAD); + mission->loadIntoMemory(); + } + _shellIconManager.initialMenu = SQSH_MM_MULTIPLAYER_LOBBY_SCR; + gameShell->getNetClient()->CreateGame(cmdLineData.publicHost, conn, gameName, mission, cmdLineData.playerName, cmdLineData.password); showMessageBoxButtons(); } + } else if (cmdLineData.roomID != 0) { + gameShell->getNetClient()->JoinPublicRoomGame(conn, cmdLineData.roomID, cmdLineData.playerName, cmdLineData.password); + showMessageBoxButtons(); + } else { + gameShell->getNetClient()->JoinDirectGame(conn, cmdLineData.playerName, cmdLineData.password); + showMessageBoxButtons(); } - return 0; } return 1; @@ -84,125 +92,245 @@ void GameShell::startCmdline(const CommandLineData& data) { /// Game hosts listing -NetAddress selectedHost; +GameInfo selectedGame = {}; + +bool hasGameListSelection() { + return !selectedGame.gameHost.empty(); +} + +void selectGameInfo(const GameInfo* game) { + if (game) { + selectedGame = *game; + } else { + selectedGame = GameInfo(); + } +} -std::string formatGameInfo(const GameHostInfo& info, bool oneLine) { - if (info.gameName.empty()) { - if (oneLine) { - return "&FF0000" + info.hostName; +std::string formatGameInfo(const GameInfo& info, bool oneLine) { + std::string text; + if (oneLine) { + if (info.gameStarted || info.gameVersion != currentShortVersion) { + text += "&FF0000"; + } else if (info.hasPassword) { + text += "&66DDFF"; } else { - return qdTextDB::instance().getText("Interface.Tips.MultiplayerInfoServerEmpty"); + text += "&FFFFFF"; } + text += info.gameName; + text += " (" + std::to_string(info.currentPlayers); + text += "/" + std::to_string(info.maximumPlayers); + text += " - " + std::to_string(info.ping) + " ms)"; } else { - static char cbTemp[1024]; - if (oneLine) { - std::string name = info.gameName + "&FFFFFF"; - if (!info.hostName.empty()) { - name += ":" + info.hostName + ":"; + text += "&FFFFFF"; + text += info.gameName; + text += "\n"; + if (info.hasPassword) { + text += "&66DDFF"; + text += qdTextDB::instance().getText("Interface.Tips.Multiplayer.HasPassword"); + text += "\n"; + } + if (info.gameStarted) { + text += "&FF0000"; + text += qdTextDB::instance().getText("Interface.Tips.Multiplayer.GameStarted"); + text += "\n"; + } + + text += "\n"; + text += qdTextDB::instance().getText("Interface.Tips.Multiplayer.Game"); + text += ":\n"; + + //Show content name, only the first of array + std::vector gameContent = getGameContentEnums(selectedGame.gameContent); + if (!gameContent.empty()) { + GAME_CONTENT content = gameContent.front(); + if (terGameContentSelect & content) { + text += "&FFFFFF"; + } else { + text += "&FF0000"; } - _shellIconManager.FormatMessageText( - "", - cbTemp, - name.c_str(), - info.gameStatusInfo.currrentPlayers, - info.gameStatusInfo.maximumPlayers, - info.gameStatusInfo.ping, - info.gameStatusInfo.flag_gameRun ? qdTextDB::instance().getText("Interface.Tips.MultiplayerInfoStarted") : ""); + text += getGameContentName(content); + } + + //Show version + if (info.gameVersion == currentShortVersion) { + text += "&FFFFFF"; } else { - _shellIconManager.FormatMessageText( - "", - cbTemp, - (info.gameName + "&FFFFFF").c_str(), - info.gameStatusInfo.currrentPlayers, - info.gameStatusInfo.maximumPlayers, - info.gameStatusInfo.ping, - info.hostName.c_str(), - info.gameStatusInfo.flag_gameRun ? qdTextDB::instance().getText("Interface.Tips.MultiplayerInfoStarted") : ""); - } - return cbTemp; + text += "&FF0000"; + } + text += " - " + info.gameVersion + "\n"; + + //Show rest of data + text += "&FFFFFF"; + text += qdTextDB::instance().getText("Interface.Tips.Multiplayer.Map"); + text += ": " + info.scenario + "\n"; + text += qdTextDB::instance().getText("Interface.Tips.Multiplayer.CurrentPlayers"); + text += ": " + std::to_string(info.currentPlayers) + "\n"; + text += qdTextDB::instance().getText("Interface.Tips.Multiplayer.MaxPlayers"); + text += ": " + std::to_string(info.maximumPlayers) + "\n"; + text += qdTextDB::instance().getText("Interface.Tips.Multiplayer.Ping"); + text += ": " + std::to_string(info.ping) + "\n"; + } + return text; +} + +bool canJoinSelectedGame() { + return hasGameListSelection() + && !selectedGame.gameStarted + && (selectedGame.currentPlayers < selectedGame.maximumPlayers) + && selectedGame.gameVersion == currentShortVersion + //&& 0 <= getMultiplayerMapNumber(selectedGame.scenario) + ; +} + +void updateMultiplayerListUI() { + bool hasSelection = hasGameListSelection(); + + //Set map window + int mapPos = hasSelection ? getMultiplayerMapNumber(selectedGame.scenario) : -1; + if (0 <= mapPos && mapPos < multiplayerMaps.size()) { + setupMapDescWnd(mapPos, multiplayerMaps, SQSH_MM_MULTIPLAYER_LIST_MAP, -1, -1, GT_MULTI_PLAYER_CREATE); + } else { + ((CShowMapWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_MAP))->setWorldID( hasSelection ? -2 : -1 ); + } + + //Set server description + dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_MAP_DESCR_TXT))->setText( + hasSelection ? formatGameInfo(selectedGame, false) : "" + ); + + //Set join enable button state + ((CShellPushButton*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN))->Enable(canJoinSelectedGame()); +} + +int multiplayerListJoinQuant( float, float ) { + if (menuChangingDone) { + std::string name = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT))->getText(); + std::string password; + if (selectedGame.hasPassword) { + //Player had to go through SQSH_MM_MULTIPLAYER_PASSWORD_SCR so pickup the password from it's input + CEditWindow* passwordInput = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_INPUT)); + password = passwordInput->getText(); + } + NetAddress conn; + if (!NetAddress::resolve(conn, selectedGame.gameHost, NET_RELAY_DEFAULT_PORT)) { + setMessageBoxTextID("Interface.Menu.Messages.IPEmpty"); + showMessageBoxButtons(); + } else if (selectedGame.gameRoomID != 0) { + gameShell->getNetClient()->JoinPublicRoomGame(conn, selectedGame.gameRoomID, name, password); + } else { + gameShell->getNetClient()->JoinDirectGame(conn, name, password); + } + return 0; + } + return 1; +} + +///Code to run when game is doble clicked in list or join button is pressed +void joinSelectedGame(CShellWindow* pWnd) { + if (!hasGameListSelection()) { + return; + } + CEditWindow* input = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT)); + if (input->isEmptyText()) { + setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText("Interface.Menu.Messages.NameEmpty"), MBOX_OK); + showMessageBox(); + } else { + putStringSettings(regLanName, input->getText()); + + if (selectedGame.hasPassword) { + dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_PASSWORD_NAME_TXT))->setText(selectedGame.gameName); + _shellIconManager.SwitchMenuScreens(pWnd->m_pParent->ID, SQSH_MM_MULTIPLAYER_PASSWORD_SCR); + } else { + setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText("Interface.Menu.Messages.Connecting"), MBOX_OK, false); + showMessageBox(); + _shellIconManager.AddDynamicHandler( multiplayerListJoinQuant, CBCODE_QUANT ); + } } } void onMMMultiplayerListGameList(CShellWindow* pWnd, InterfaceEventCode code, int param) { if (code == EVENT_CREATEWND) { -// CListBoxWindow* list = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_GAME_LIST)); - CListBoxWindow* list = (CListBoxWindow*)pWnd; - list->NewItem(1); - list->Clear(); + CListBoxWindow* listBox = dynamic_cast(pWnd); + listBox->NewItem(1); + listBox->Clear(); } else if (code == EVENT_DRAWWND) { -// CListBoxWindow* listBox = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_GAME_LIST)); - CListBoxWindow* listBox = (CListBoxWindow*)pWnd; + CListBoxWindow* listBox = dynamic_cast(pWnd); listBox->Clear(); - auto hostList = gameShell->getNetClient()->getGameHostList(); - if (!hostList.empty()) { - - GameHostInfo& selectedGame = hostList.front(); - int selectIndex = 0; + auto gameList = gameShell->getNetClient()->getGameList(); + bool hasSelection = hasGameListSelection(); + if (!gameList.empty()) { + int selectIndex = -1; int i = 0; - for (auto it = hostList.begin(); it != hostList.end(); it++, i++) { - std::string name = formatGameInfo(*it, true); - listBox->AddString( name.c_str(), 0 ); - if ((*it).gameHost.crc() == selectedHost.crc()) { - selectedGame = *it; - selectIndex = i; + for (auto& game : gameList) { + std::string name = formatGameInfo(game, true); + listBox->AddString(name.c_str(), 0); + if (hasSelection && selectIndex == -1) { + if (game.gameHost == selectedGame.gameHost + && game.gameRoomID == selectedGame.gameRoomID) { + selectIndex = i; + selectGameInfo(&game); + } } + i++; } listBox->SetCurSel(selectIndex); - if (selectIndex == 0) { - selectedHost = selectedGame.gameHost; + if (hasSelection) { + if (selectIndex == -1) { + //No longer in list + selectGameInfo(nullptr); + } + updateMultiplayerListUI(); } - int id = getMultiplayerMapNumber(selectedGame.gameStatusInfo.world); - ((CShowMapWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_MAP))->setWorldID(id); - ((CTextWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_MAP_DESCR_TXT))->setText(formatGameInfo(selectedGame, false) ); - ((CShellPushButton*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN))->Enable(!selectedGame.gameName.empty()); } else { - ((CShowMapWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_MAP))->setWorldID(-1 ); - ((CTextWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_MAP_DESCR_TXT))->setText("" ); - ((CShellPushButton*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_JOIN_BTN))->Enable(0); + selectGameInfo(nullptr); + updateMultiplayerListUI(); + } + } else if ( code == EVENT_DOUBLECLICK && param == VK_LBUTTON) { + if (canJoinSelectedGame()) { + joinSelectedGame(pWnd); } } else if ( code == EVENT_PRESSED && intfCanHandleInput() ) { CListBoxWindow* listBox = (CListBoxWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_GAME_LIST); int pos = listBox->GetCurSel(); - if (pos >= 0 && pos < gameShell->getNetClient()->getGameHostList().size()) { - std::vector::iterator it = gameShell->getNetClient()->getGameHostList().begin(); + auto gameList = gameShell->getNetClient()->getGameList(); + if (0 <= pos && pos < gameList.size()) { + std::vector::iterator it = gameList.begin(); advance(it, pos); - selectedHost = (*it).gameHost; - - int id = getMultiplayerMapNumber((*it).gameStatusInfo.world); - ((CShowMapWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_MAP))->setWorldID(id); - ((CTextWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_LIST_MAP_DESCR_TXT))->setText(formatGameInfo(*it, false) ); + selectGameInfo(&(*it)); + } else { + selectGameInfo(nullptr); } + updateMultiplayerListUI(); + } +} + +void onMMMultiplayerListJoinButton(CShellWindow* pWnd, InterfaceEventCode code, int param) { + if( code == EVENT_UNPRESSED && intfCanHandleInput() ) { + joinSelectedGame(pWnd); } } -///Join handler -extern int multiplayerJoinBackHandler(float, float); +///Password screen handlers -int joinQuant( float, float ) { +int multiplayerPasswordToJoinQuant( float, float ) { if (menuChangingDone) { - CEditWindow* input = (CEditWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT); - gameShell->getNetClient()->JoinGame(selectedHost, input->GetText()); + setupOkMessageBox(exitToMultiplayerScreenAction, 0, qdTextDB::instance().getText("Interface.Menu.Messages.Connecting"), MBOX_BACK, false); + showMessageBox(); + _shellIconManager.AddDynamicHandler(multiplayerListJoinQuant, CBCODE_QUANT); return 0; } return 1; } -void onMMMultiplayerListJoinButton(CShellWindow* pWnd, InterfaceEventCode code, int param) { +void onMMMultiplayerPasswordNextBtn(CShellWindow* pWnd, InterfaceEventCode code, int param) { if( code == EVENT_UNPRESSED && intfCanHandleInput() ) { - CEditWindow* input = (CEditWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT); - if (input->isEmptyText()) { - setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText("Interface.Menu.Messages.NameEmpty"), MBOX_OK); - showMessageBox(); - } else { - putStringSettings(regLanName, input->getText()); - - setupOkMessageBox(multiplayerJoinBackHandler, 0, qdTextDB::instance().getText("Interface.Menu.Messages.Connecting"), MBOX_EXIT, false); - showMessageBox(); - - _shellIconManager.AddDynamicHandler( joinQuant, CBCODE_QUANT ); + if (!hasGameListSelection()) { + return; } + _shellIconManager.SwitchMenuScreens(SQSH_MM_MULTIPLAYER_PASSWORD_SCR, SQSH_MM_MULTIPLAYER_LIST_SCR); + _shellIconManager.AddDynamicHandler(multiplayerPasswordToJoinQuant, CBCODE_QUANT); } } @@ -224,7 +352,7 @@ void onMMMultiplayerListCreateButton(CShellWindow* pWnd, InterfaceEventCode code if( code == EVENT_UNPRESSED && intfCanHandleInput() ) { CEditWindow* input = (CEditWindow*)_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_NAME_INPUT); if (input->isEmptyText()) { - setupOkMessageBox(0, 0, qdTextDB::instance().getText("Interface.Menu.Messages.NameEmpty"), MBOX_OK); + setupOkMessageBox(nullptr, 0, qdTextDB::instance().getText("Interface.Menu.Messages.NameEmpty"), MBOX_OK); showMessageBox(); } else { if (!input->isEmptyText()) { diff --git a/Source/UserInterface/PerimeterShellDisp.cpp b/Source/UserInterface/PerimeterShellDisp.cpp index 559eea542..784749180 100644 --- a/Source/UserInterface/PerimeterShellDisp.cpp +++ b/Source/UserInterface/PerimeterShellDisp.cpp @@ -235,9 +235,13 @@ _handlertbl[] = { {SQSH_TASK_BUTTON_ID, onMMTaskButton}, - //join game + //multiplayer join {SQSH_MM_MULTIPLAYER_JOIN_NEXT_BTN, onMMMultiplayerJoinNextBtn}, {SQSH_MM_BACK_FROM_MULTIPLAYER_JOIN_BTN, onMMBackButton}, + + //multiplayer passwoprd + {SQSH_MM_MULTIPLAYER_PASSWORD_NEXT_BTN, onMMMultiplayerPasswordNextBtn}, + {SQSH_MM_BACK_FROM_MULTIPLAYER_PASSWORD_BTN, onMMBackButton}, //multiplayer list {SQSH_MM_MULTIPLAYER_LIST_GAME_LIST, onMMMultiplayerListGameList}, diff --git a/Source/UserInterface/PerimeterShellUI.h b/Source/UserInterface/PerimeterShellUI.h index 5b653b5d4..24d965096 100644 --- a/Source/UserInterface/PerimeterShellUI.h +++ b/Source/UserInterface/PerimeterShellUI.h @@ -2036,9 +2036,12 @@ void onMMDelSaveReplayButton(CShellWindow* pWnd, InterfaceEventCode code, int pa void onMMTaskButton(CShellWindow* pWnd, InterfaceEventCode code, int param); -//join game +//multiplayer join void onMMMultiplayerJoinNextBtn(CShellWindow* pWnd, InterfaceEventCode code, int param); +//multiplayer password +void onMMMultiplayerPasswordNextBtn(CShellWindow* pWnd, InterfaceEventCode code, int param); + //multiplayer list void onMMMultiplayerListGameList(CShellWindow* pWnd, InterfaceEventCode code, int param); void onMMMultiplayerListCreateButton(CShellWindow* pWnd, InterfaceEventCode code, int param); diff --git a/Source/Util/BinaryArchive.h b/Source/Util/BinaryArchive.h index bbbef65a5..f0cde60c5 100644 --- a/Source/Util/BinaryArchive.h +++ b/Source/Util/BinaryArchive.h @@ -206,7 +206,7 @@ class BinaryOArchive ); ar.openCollection(count); for(int i = 0; i < count; ++i) - ar & WRAP_NAME(t[i], 0); + ar & WRAP_ELEMENT(t[i]); } }; @@ -233,7 +233,7 @@ class BinaryOArchive typename std::vector::const_iterator i; openCollection(cont.size()); FOR_EACH(cont, i) - (*this) & WRAP_NAME(*i, 0); + (*this) & WRAP_ELEMENT(*i); return *this; } @@ -242,7 +242,7 @@ class BinaryOArchive typename std::list::const_iterator i; openCollection(cont.size()); FOR_EACH(cont, i) - (*this) & WRAP_NAME(*i, 0); + (*this) & WRAP_ELEMENT(*i); return *this; } @@ -257,7 +257,7 @@ class BinaryOArchive template BinaryOArchive& operator&(const ShareHandle& t) { - return *this & WRAP_NAME(t.get(), 0); + return *this & WRAP_ELEMENT(t.get()); } template @@ -415,7 +415,7 @@ class BinaryIArchive ErrH.Abort("Array size too short"); } for(int i = 0; i < count; ++i) - ar & WRAP_NAME(t[i], 0); + ar & WRAP_ELEMENT(t[i]); } }; @@ -460,13 +460,13 @@ class BinaryIArchive cont.reserve(count); while(count--){ cont.push_back(SerializationDefaultValue::get()); - (*this) & WRAP_NAME(cont.back(), 0); + (*this) & WRAP_ELEMENT(cont.back()); } } else{ typename std::vector::iterator i; FOR_EACH(cont, i) - (*this) & WRAP_NAME(*i, 0); + (*this) & WRAP_ELEMENT(*i); } return *this; } @@ -479,13 +479,13 @@ class BinaryIArchive cont.clear(); while(count--){ cont.push_back(SerializationDefaultValue::get()); - (*this) & WRAP_NAME(cont.back(), 0); + (*this) & WRAP_ELEMENT(cont.back()); } } else{ typename std::list::iterator i; FOR_EACH(cont, i) - (*this) & WRAP_NAME(*i, 0); + (*this) & WRAP_ELEMENT(*i); } return *this; } @@ -507,7 +507,7 @@ class BinaryIArchive t = 0; ptr->decrRef(); } - (*this) & WRAP_NAME(ptr, 0); + (*this) & WRAP_ELEMENT(ptr); t = ptr; return *this; } diff --git a/Source/Util/EditArchive.h b/Source/Util/EditArchive.h index 23bcc8443..f4c23c70a 100644 --- a/Source/Util/EditArchive.h +++ b/Source/Util/EditArchive.h @@ -142,7 +142,7 @@ class EditOArchive inited = true; T t = T(); EditOArchive archive; - archive << WRAP_NAME(t, 0); + archive << WRAP_ELEMENT(t); treeNode_ = const_cast(archive.rootNode()); } if(treeNode_){ @@ -269,7 +269,7 @@ class EditOArchive template EditOArchive& operator&(const ShareHandle& t) { - return *this & WRAP_NAME(t.get(), 0); + return *this & WRAP_ELEMENT(t.get()); } template @@ -550,7 +550,7 @@ class EditIArchive t = 0; ptr->decrRef(); } - (*this) & WRAP_NAME(ptr, 0); + (*this) & WRAP_ELEMENT(ptr); t = ptr; return *this; } @@ -628,11 +628,11 @@ class EditArchive : public EditOArchive, public EditIArchive template bool edit(T& t, const char* name = 0) { - static_cast(*this) << WRAP_NAME(t, 0); + static_cast(*this) << WRAP_ELEMENT(t); if(name) // если редактируется указатель, то имя конфликтует с его типом const_cast(rootNode())->setValue(name); if(edit()){ - static_cast(*this) >> WRAP_NAME(t, 0); + static_cast(*this) >> WRAP_ELEMENT(t); clear(); return true; } @@ -662,7 +662,7 @@ class EditClassDescriptor : public ClassDescriptor(ObjectCreator::create()); EditOArchive archive; - archive << WRAP_NAME(*t, 0); + archive << WRAP_ELEMENT(*t); treeNode_ = const_cast(archive.rootNode()); treeNode_->setValue(nameAlt_); delete t; diff --git a/Source/Util/Handle.h b/Source/Util/Handle.h index 6e6b532c8..e4b2fc333 100644 --- a/Source/Util/Handle.h +++ b/Source/Util/Handle.h @@ -113,7 +113,7 @@ class ShareHandle { #endif SERIALIZE(ar) { - ar & makeObjectWrapper(ptr, 0, 0); + ar & WRAP_VALUE(ptr); } private: diff --git a/Source/Util/MissionDescription.cpp b/Source/Util/MissionDescription.cpp index bc499b81e..7dde9dcfb 100644 --- a/Source/Util/MissionDescription.cpp +++ b/Source/Util/MissionDescription.cpp @@ -406,19 +406,22 @@ bool MissionDescription::isAllRealPlayerStartReady() int MissionDescription::playersAmount() const { int cntPlayers=0; - for(unsigned int i=0; i::treeNodeFunc(typeIndex)) >> WRAP_NAME(action, 0); + EditIArchive(EditClassDescriptor::treeNodeFunc(typeIndex)) >> WRAP_ELEMENT(action); return action; } ConditionPtr createCondition(int typeIndex) { ConditionPtr condition; - EditIArchive(EditClassDescriptor::treeNodeFunc(typeIndex)) >> WRAP_NAME(condition, 0); + EditIArchive(EditClassDescriptor::treeNodeFunc(typeIndex)) >> WRAP_ELEMENT(condition); return condition; } diff --git a/Source/Util/SaveSQSH.cpp b/Source/Util/SaveSQSH.cpp index dd44497ad..075158878 100644 --- a/Source/Util/SaveSQSH.cpp +++ b/Source/Util/SaveSQSH.cpp @@ -155,6 +155,7 @@ REGISTER_ENUM(SQSH_MM_MULTIPLAYER_LIST_SCR, "SQSH_MM_MULTIPLAYER_LIST_SCR") REGISTER_ENUM(SQSH_MM_MULTIPLAYER_HOST_SCR, "SQSH_MM_MULTIPLAYER_HOST_SCR") REGISTER_ENUM(SQSH_MM_MULTIPLAYER_LOBBY_SCR, "SQSH_MM_MULTIPLAYER_LOBBY_SCR") REGISTER_ENUM(SQSH_MM_MULTIPLAYER_JOIN_SCR, "SQSH_MM_MULTIPLAYER_JOIN_SCR") +REGISTER_ENUM(SQSH_MM_MULTIPLAYER_PASSWORD_SCR, "SQSH_MM_MULTIPLAYER_PASSWORD_SCR") REGISTER_ENUM(SQSH_MM_OPTIONS_SCR, "SQSH_MM_OPTIONS_SCR") REGISTER_ENUM(SQSH_MM_GRAPHICS_SCR, "SQSH_MM_GRAPHICS_SCR") REGISTER_ENUM(SQSH_MM_CUSTOM_SCR, "SQSH_MM_CUSTOM_SCR") @@ -316,6 +317,11 @@ REGISTER_ENUM(SQSH_MM_MISSION_TASK_TXT, "SQSH_MM_MISSION_TASK_TXT") REGISTER_ENUM(SQSH_MM_BACK_FROM_TASK_BTN, "SQSH_MM_BACK_FROM_TASK_BTN") REGISTER_ENUM(SQSH_MM_MULTIPLAYER_JOIN_NEXT_BTN, "SQSH_MM_MULTIPLAYER_JOIN_NEXT_BTN") REGISTER_ENUM(SQSH_MM_BACK_FROM_MULTIPLAYER_JOIN_BTN, "SQSH_MM_BACK_FROM_MULTIPLAYER_JOIN_BTN") +REGISTER_ENUM(SQSH_MM_MULTIPLAYER_PASSWORD_NEXT_BTN, "SQSH_MM_MULTIPLAYER_PASSWORD_NEXT_BTN") +REGISTER_ENUM(SQSH_MM_BACK_FROM_MULTIPLAYER_PASSWORD_BTN, "SQSH_MM_BACK_FROM_MULTIPLAYER_PASSWORD_BTN") +REGISTER_ENUM(SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_INPUT, "SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_INPUT") +REGISTER_ENUM(SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_BTN, "SQSH_MM_MULTIPLAYER_PASSWORD_PASSWORD_BTN") +REGISTER_ENUM(SQSH_MM_MULTIPLAYER_PASSWORD_NAME_TXT, "SQSH_MM_MULTIPLAYER_PASSWORD_NAME_TXT") REGISTER_ENUM(SQSH_MM_LOBBY_SERVER_TYPE_COMBO, "SQSH_MM_LOBBY_SERVER_TYPE_COMBO") REGISTER_ENUM(SQSH_MM_MULTIPLAYER_JOIN_IP_INPUT, "SQSH_MM_IPMULTIPLAYER_JOIN_IP_INPUT") REGISTER_ENUM(SQSH_MM_MULTIPLAYER_JOIN_IP_BTN, "SQSH_MM_MULTIPLAYER_JOIN_IP_BTN") diff --git a/Source/Util/Serialization.h b/Source/Util/Serialization.h index cd2cdb823..9e11d2632 100644 --- a/Source/Util/Serialization.h +++ b/Source/Util/Serialization.h @@ -19,7 +19,7 @@ static uint32_t getSerializationCRC(T& t, uint32_t crc, int floatDigits = 0) { if (floatDigits) { buf.SetDigits(floatDigits); } - ar << makeObjectWrapper(t, 0, 0); + ar << WRAP_ELEMENT(t); crc = crc32(reinterpret_cast(buf.address()), buf.tell(), crc); return crc; } @@ -621,6 +621,10 @@ SingletonPrm ///////////////////////////////////////////////// // Заворачивание объектов для архивации ///////////////////////////////////////////////// +// Wraps only value without any name wrapping +#define WRAP_ELEMENT(object) \ + makeObjectWrapper(object, 0, 0) + // Завернуть без перевода с указанием имени #define WRAP_NAME(object, name) \ makeObjectWrapper(object, name, 0) diff --git a/Source/Util/TypeLibrary.h b/Source/Util/TypeLibrary.h index d32746a9f..4746c4c7a 100644 --- a/Source/Util/TypeLibrary.h +++ b/Source/Util/TypeLibrary.h @@ -69,13 +69,15 @@ class TypeLibrary SERIALIZE(ar) { if (ar.type() & ARCHIVE_EDIT) { - ComboListString comboStr(instance().comboList(), key2String(key_).c_str()); - ar & TRANSLATE_NAME(comboStr, 0, 0); - if(ar.isInput()) { + ComboListString comboStr(instance().comboList(), key2String(key_).c_str()); + ar & TRANSLATE_NAME(comboStr, 0, 0); + if (ar.isInput()) { setKeyC(key_, comboStr); } + } else if (!SuppressBracket::value) { + ar & WRAP_NAME(key_, "key"); } else { - ar & WRAP_NAME(key_, !SuppressBracket::value ? "key" : 0); + ar & WRAP_ELEMENT(key_); } if(ar.isInput()) { diff --git a/Source/Util/XPrmArchive.h b/Source/Util/XPrmArchive.h index d6cd02ef8..ede5ef4f5 100644 --- a/Source/Util/XPrmArchive.h +++ b/Source/Util/XPrmArchive.h @@ -104,7 +104,7 @@ class XPrmOArchive template void saveElement(const T& t) { buffer_ < offset_.c_str(); - (*this) & WRAP_NAME(t, 0); + (*this) & WRAP_ELEMENT(t); buffer_ < (binary_friendly ? "," : ",\r\n"); } @@ -218,7 +218,7 @@ class XPrmOArchive template XPrmOArchive& operator&(const ShareHandle& t) { - return *this & WRAP_NAME(t.get(), 0); + return *this & WRAP_ELEMENT(t.get()); } template @@ -433,7 +433,7 @@ class XPrmIArchive } template void loadElement(T& t) { - (*this) & WRAP_NAME(t, 0); + (*this) & WRAP_ELEMENT(t); std::string name; loadString(name); if(name != ",") @@ -576,7 +576,7 @@ class XPrmIArchive t = 0; ptr->decrRef(); } - (*this) & WRAP_NAME(ptr, 0); + (*this) & WRAP_ELEMENT(ptr); t = ptr; return *this; } diff --git a/Source/XPrm/CMakeLists.txt b/Source/XPrm/CMakeLists.txt index fd8791773..a234c3659 100644 --- a/Source/XPrm/CMakeLists.txt +++ b/Source/XPrm/CMakeLists.txt @@ -12,14 +12,6 @@ target_include_directories(XPrmLib PRIVATE "${PROJECT_SOURCE_DIR}/Source/Util" ) - -set(XPrm_LINK_LIBS ${EXE_LINK_LIBS_PRE} XTool ${SDL2_LIBRARY} ${EXE_LINK_LIBS_POST}) - -IF(PERIMETER_EXODUS) - SET(XPrm_LINK_LIBS ${XPrm_LINK_LIBS} Exodus) -ENDIF() -target_link_libraries(XPrmLib PRIVATE ${XPrm_LINK_LIBS}) - target_compile_options(XPrmLib PRIVATE ${PERIMETER_COMPILE_OPTIONS}) # XPrm executable @@ -33,7 +25,13 @@ target_include_directories(XPrm PRIVATE ) -target_link_libraries(XPrm PRIVATE XPrmLib) +set(XPrm_LINK_LIBS ${EXE_LINK_LIBS_PRE} XPrmLib XTool ${SDL2_LIBRARY} ${EXE_LINK_LIBS_POST}) + +IF(PERIMETER_EXODUS) + SET(XPrm_LINK_LIBS ${XPrm_LINK_LIBS} Exodus) +ENDIF() + +target_link_libraries(XPrm PRIVATE ${XPrm_LINK_LIBS}) target_compile_options(XPrm PRIVATE ${PERIMETER_COMPILE_OPTIONS}) diff --git a/Source/XTool/CMakeLists.txt b/Source/XTool/CMakeLists.txt index a41f8a22a..36e563198 100644 --- a/Source/XTool/CMakeLists.txt +++ b/Source/XTool/CMakeLists.txt @@ -1,5 +1,5 @@ -OPTION(OPTION_USE_BACKTRACE "Use libbacktrace if present" OFF) +OPTION(OPTION_USE_BACKTRACE "Use libbacktrace if present" ON) #GameMath retrieval FetchContent_Declare(gamemath diff --git a/Source/XTool/XBUFFER/XBCNVOUT.cpp b/Source/XTool/XBUFFER/XBCNVOUT.cpp index 569fefc57..74dd117ed 100644 --- a/Source/XTool/XBUFFER/XBCNVOUT.cpp +++ b/Source/XTool/XBUFFER/XBCNVOUT.cpp @@ -148,7 +148,8 @@ int XBuffer::uncompress(XBuffer& output, uint32_t* len) { //Check if this buffer has enough data if (tell() + compressed_len > size) { - fprintf(stderr, "XBuffer decompression incomplete data len %d %d %zu\n", original_len, compressed_len, size); + xassert(0); + fprintf(stderr, "XBuffer decompression incomplete data len %" PRIi32 " compressed %" PRIi32 " size %" PRIsize "\n", original_len, compressed_len, size); return -1; } diff --git a/Source/XTool/XBUFFER/XBCORE.cpp b/Source/XTool/XBUFFER/XBCORE.cpp index d5952242a..e504b81fb 100644 --- a/Source/XTool/XBUFFER/XBCORE.cpp +++ b/Source/XTool/XBUFFER/XBCORE.cpp @@ -124,7 +124,7 @@ size_t XBuffer::write(const void* s, size_t len, bool bin_flag) XBuffer& XBuffer::operator< (const char* v) { if(v) - write(v, strlen(v), 0); + write(v, strlen(v), false); return *this; } diff --git a/Source/XTool/xerrhand.cpp b/Source/XTool/xerrhand.cpp index 8477b58c4..834a1594a 100644 --- a/Source/XTool/xerrhand.cpp +++ b/Source/XTool/xerrhand.cpp @@ -29,9 +29,6 @@ void SetAssertRestoreGraphicsFunction(void(*func)()) } #endif -#define CONV_BUFFER_LEN 63 -char convBuf[CONV_BUFFER_LEN + 1]; - #ifndef OPTION_DISABLE_STACKTRACE #define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED 1 #include @@ -59,6 +56,9 @@ XErrorHandler ErrH; //All the Win32 on x86/64 specific error handling #ifdef USE_ORIGINAL_HANDLER +#define CONV_BUFFER_LEN 63 +char convBuf[CONV_BUFFER_LEN + 1]; + void win32_break(const char* error,char* msg) { std::cerr << "--------------------------------\n"; std::cerr << error << "\n"; diff --git a/flake.nix b/flake.nix index 7e5a3e95c..4a1b13f46 100644 --- a/flake.nix +++ b/flake.nix @@ -28,6 +28,7 @@ #Systems that don't need overlay stableSystems = [ "x86_64-linux" + "aarch64-linux" ]; # Systems targetted for this package