├── .gitignore ├── Assets └── SplashExample │ └── textures │ ├── InitialSplash.dds │ ├── InitialSplash.dds.cryasset │ ├── InitialSplash.tif │ ├── Splash.dds │ ├── Splash.dds.cryasset │ └── Splash.tif ├── Code ├── CMakeLists.txt ├── Plugin.cpp ├── Plugin.h ├── SplashExample.cpp ├── SplashExample.h ├── SplashExample.vcxproj.user.in ├── StdAfx.cpp ├── StdAfx.h └── cvars.h ├── Docs ├── CONCEPT.txt ├── INSTALL.txt └── MIGRATION_NOTES.txt ├── LICENSE.txt ├── SplashExamplePlugin.cryproject ├── SplashExamplePlugin_GameSDK.cryproject ├── SplashExamplePlugin_GameZero.cryproject └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Build specific files, we only want source code tracked 2 | *.lib 3 | *.pdb 4 | /Solutions/* 5 | /Release/* 6 | /Backup/* 7 | *.sln 8 | *.lnk 9 | *.ilk 10 | *.exp -------------------------------------------------------------------------------- /Assets/SplashExample/textures/InitialSplash.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uniflare/SplashExample/b4d5a59b8e3c730e14698bde0681695b01e0ae4d/Assets/SplashExample/textures/InitialSplash.dds -------------------------------------------------------------------------------- /Assets/SplashExample/textures/InitialSplash.dds.cryasset: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 512 7 | 300 8 | 1 9 |
10 |
11 | -------------------------------------------------------------------------------- /Assets/SplashExample/textures/InitialSplash.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uniflare/SplashExample/b4d5a59b8e3c730e14698bde0681695b01e0ae4d/Assets/SplashExample/textures/InitialSplash.tif -------------------------------------------------------------------------------- /Assets/SplashExample/textures/Splash.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uniflare/SplashExample/b4d5a59b8e3c730e14698bde0681695b01e0ae4d/Assets/SplashExample/textures/Splash.dds -------------------------------------------------------------------------------- /Assets/SplashExample/textures/Splash.dds.cryasset: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 2560 7 | 1440 8 | 1 9 |
10 |
11 | -------------------------------------------------------------------------------- /Assets/SplashExample/textures/Splash.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uniflare/SplashExample/b4d5a59b8e3c730e14698bde0681695b01e0ae4d/Assets/SplashExample/textures/Splash.tif -------------------------------------------------------------------------------- /Code/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.6.2) 2 | set(CRYENGINE_DIR "C:/Program Files (x86)/Crytek/CRYENGINE Launcher/Crytek/CRYENGINE_5.4") 3 | set(TOOLS_CMAKE_DIR "${CRYENGINE_DIR}/Tools/CMake") 4 | 5 | set(PROJECT_BUILD 1) 6 | set(PROJECT_DIR "C:/dev/git/sf/SplashExample") 7 | 8 | include("${TOOLS_CMAKE_DIR}/CommonOptions.cmake") 9 | 10 | add_subdirectory("${CRYENGINE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/CRYENGINE") 11 | 12 | include("${TOOLS_CMAKE_DIR}/Configure.cmake") 13 | start_sources() 14 | 15 | sources_platform(ALL) 16 | add_sources("Code_uber.cpp" 17 | PROJECTS SplashExamplePlugin 18 | SOURCE_GROUP "Root" 19 | "Plugin.cpp" 20 | "SplashExample.cpp" 21 | "StdAfx.cpp" 22 | "cvars.h" 23 | "Plugin.h" 24 | "SplashExample.h" 25 | "StdAfx.h" 26 | ) 27 | 28 | end_sources() 29 | 30 | CryEngineModule(SplashExamplePlugin PCH "StdAfx.cpp" SOLUTION_FOLDER "Project") 31 | 32 | target_include_directories(${THIS_PROJECT} 33 | PRIVATE 34 | "${CRYENGINE_DIR}/Code/CryEngine/CryCommon" 35 | "${CRYENGINE_DIR}/Code/CryEngine/CryAction" 36 | "${CRYENGINE_DIR}/Code/CryEngine/CrySchematyc/Core/Interface" 37 | "${CRYENGINE_DIR}/Code/CryPlugins/CryDefaultEntities/Module" 38 | ) 39 | 40 | # Set StartUp project in Visual Studio 41 | 42 | add_library(GameLauncher STATIC "${CRYENGINE_DIR}/Code/CryEngine/CryCommon/CryCore/Platform/platform.h") 43 | set_target_properties(GameLauncher PROPERTIES LINKER_LANGUAGE CXX) 44 | if (WIN32) 45 | set_visual_studio_debugger_command(GameLauncher "${CRYENGINE_DIR}/bin/win_x64/GameLauncher.exe" "-project \"C:/dev/git/sf/SplashExample/SplashExamplePlugin.cryproject\"") 46 | endif() 47 | 48 | add_library(Sandbox STATIC "${CRYENGINE_DIR}/Code/CryEngine/CryCommon/CryCore/Platform/platform.h") 49 | set_target_properties(Sandbox PROPERTIES LINKER_LANGUAGE CXX) 50 | if (WIN32) 51 | set_visual_studio_debugger_command(Sandbox "${CRYENGINE_DIR}/bin/win_x64/Sandbox.exe" "-project \"C:/dev/git/sf/SplashExample/SplashExamplePlugin.cryproject\"") 52 | endif() 53 | 54 | add_library(GameServer STATIC "${CRYENGINE_DIR}/Code/CryEngine/CryCommon/CryCore/Platform/platform.h") 55 | set_target_properties(GameServer PROPERTIES LINKER_LANGUAGE CXX) 56 | if (WIN32) 57 | set_visual_studio_debugger_command(GameServer "${CRYENGINE_DIR}/bin/win_x64/Game_Server.exe" "-project \"C:/dev/git/sf/SplashExample/SplashExamplePlugin.cryproject\"") 58 | endif() 59 | 60 | set_solution_startup_target(GameLauncher) 61 | 62 | if (WIN32) 63 | set_visual_studio_debugger_command( ${THIS_PROJECT} "${CRYENGINE_DIR}/bin/win_x64/GameLauncher.exe" "-project \"C:/dev/git/sf/SplashExample/SplashExamplePlugin.cryproject\"" ) 64 | endif() 65 | 66 | #BEGIN-CUSTOM 67 | set (USE_TEST_PLATFORM_PROJECT "FALSE") 68 | 69 | set (TestPlatformRoot "C:/dev/git/sf/BLANKGAME/") 70 | set (TestPlatformPath "${TestPlatformRoot}bin/win_x64/") 71 | set (TestPlatformProject "${TestPlatformRoot}Game.cryproject") 72 | 73 | # Default to this project, if no test platform project is used 74 | if (NOT USE_TEST_PLATFORM_PROJECT) 75 | set (TestPlatformProject "${PROJECT_FILE}") 76 | endif() 77 | 78 | if(USE_TEST_PLATFORM_PROJECT) 79 | add_custom_command(TARGET ${THIS_PROJECT} POST_BUILD 80 | COMMAND "${CMAKE_COMMAND}" -E copy 81 | "$" 82 | "${TestPlatformPath}/$" 83 | COMMENT "Copying to test platform directory") 84 | endif(USE_TEST_PLATFORM_PROJECT) 85 | #END-CUSTOM -------------------------------------------------------------------------------- /Code/Plugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | 3 | #include "StdAfx.h" 4 | 5 | // Included only once per DLL module. 6 | #include 7 | 8 | #include "Plugin.h" 9 | 10 | /////////////////////////////////////////////////////////////////////////// 11 | //! Module constructor 12 | CPlugin_SplashExample::CPlugin_SplashExample() 13 | : m_sCVars() 14 | , m_pSplashExample(nullptr) 15 | { 16 | // Skip if we are in the editor 17 | if (gEnv->IsEditor()) 18 | { 19 | CRY_LOG_ALWAYS("Editor mode detected. Skipping splash example."); 20 | return; 21 | } 22 | else if (gEnv->IsDedicated()) 23 | { 24 | CRY_LOG_ALWAYS("Dedicated mode detected. Skipping splash example."); 25 | return; 26 | } 27 | 28 | m_pSplashExample = CryAlignedNew(); 29 | } 30 | 31 | /////////////////////////////////////////////////////////////////////////// 32 | //! Module destructor 33 | CPlugin_SplashExample::~CPlugin_SplashExample() 34 | { 35 | SAFE_DELETE(m_pSplashExample); 36 | // ~m_sCVars(); (stack) 37 | } 38 | 39 | /////////////////////////////////////////////////////////////////////////// 40 | //! ICryPlugin override 41 | bool CPlugin_SplashExample::Initialize(SSystemGlobalEnvironment& env, const SSystemInitParams& initParams) 42 | { 43 | if (m_pSplashExample) 44 | return m_pSplashExample->Initialize(env, initParams); 45 | return true; 46 | } 47 | 48 | CRYREGISTER_SINGLETON_CLASS(CPlugin_SplashExample) -------------------------------------------------------------------------------- /Code/Plugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | 3 | #pragma once 4 | #include "CVars.h" 5 | #include "SplashExample.h" 6 | 7 | #include 8 | #include 9 | 10 | class CSplashExample; 11 | class CPlugin_SplashExample 12 | : ICryPlugin 13 | { 14 | // Console variables provided by this object 15 | CVars m_sCVars; 16 | 17 | // reference to splash example implementation 18 | CSplashExample * m_pSplashExample; 19 | public: 20 | CRYINTERFACE_SIMPLE(ICryPlugin) 21 | CRYGENERATE_SINGLETONCLASS_GUID(CPlugin_SplashExample, "Plugin_SplashExample", "{ 53706C61 - 7368 - 2045 - 7861 - 6D706C650000 }"_cry_guid) 22 | 23 | // ICryPlugin 24 | virtual const char* GetName() const override { return "Splash Example Plugin"; } 25 | virtual const char* GetCategory() const override { return "Game"; } 26 | virtual bool Initialize(SSystemGlobalEnvironment& env, const SSystemInitParams& initParams) override; 27 | virtual void OnPluginUpdate(EPluginUpdateType updateType) override {} 28 | // ~ICryPlugin 29 | 30 | CPlugin_SplashExample(); 31 | virtual ~CPlugin_SplashExample(); 32 | }; -------------------------------------------------------------------------------- /Code/SplashExample.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | 3 | #include "StdAfx.h" 4 | 5 | #include "SplashExample.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | /////////////////////////////////////////////////////////////////////////// 16 | //! Default Constructor 17 | CSplashExample::CSplashExample() : 18 | m_sCVars() 19 | , m_bInitialized(false) 20 | , m_bSystemListenerRegistered(false) 21 | , m_bProfileListenerRegistered(false) 22 | , m_bLoadedProfileAttributes(false) 23 | , m_bIgnoreNextProfileSave(false) 24 | , m_pSplashTexture(nullptr) 25 | , m_pInitialSplashTexture(nullptr) 26 | , m_pProfileManager(nullptr) 27 | , m_pCurrentProfile(nullptr) 28 | , m_pDefaultProfile(nullptr) 29 | { 30 | } 31 | 32 | /////////////////////////////////////////////////////////////////////////// 33 | //! Destructor 34 | CSplashExample::~CSplashExample() 35 | { 36 | SafeReleaseSplashTextures(); 37 | UnregisterProfileListener(this); 38 | UnregisterSystemListener(this); 39 | } 40 | 41 | /////////////////////////////////////////////////////////////////////////// 42 | //! Called from ICryPlugin interface 43 | bool CSplashExample::Initialize(SSystemGlobalEnvironment & env, const SSystemInitParams & initParams) 44 | { 45 | m_sCVars.Init(); 46 | 47 | // Try to initialize splash example 48 | TryInitialize(); 49 | 50 | return true; 51 | } 52 | 53 | /////////////////////////////////////////////////////////////////////////// 54 | //! Releases textures if possible, ensures nulled pointers. 55 | void CSplashExample::SafeReleaseSplashTextures() 56 | { 57 | // Release our textures (-1 ref count) 58 | if (m_pInitialSplashTexture) 59 | m_pInitialSplashTexture->Release(); 60 | 61 | if (m_pSplashTexture) 62 | m_pSplashTexture->Release(); 63 | 64 | m_pSplashTexture = m_pInitialSplashTexture = nullptr; 65 | } 66 | 67 | /////////////////////////////////////////////////////////////////////////// 68 | //! If plugin construction is ever moved upwards in system init, this will still be compatible. 69 | //! Also useful if any system/required components failed to init yet for any reason. 70 | void CSplashExample::TryInitialize() 71 | { 72 | CRY_LOG_CALL("CSplashExample::TryConstruct()"); 73 | 74 | // Try to register our system listener, if we havn't managed to before now for some reason. 75 | RegisterSystemListener(this); 76 | 77 | // Only init once (Listener is required to function properly) 78 | if (!m_bInitialized && m_bSystemListenerRegistered) 79 | { 80 | ICryPak * pCryPak = gEnv->pCryPak; 81 | IConsole * pConsole = gEnv->pConsole; 82 | IRenderer * pRenderer = gEnv->pRenderer; 83 | ITimer * pTimer = gEnv->pTimer; 84 | 85 | // Only construct if the system is in a state we can use 86 | if (!pConsole) 87 | { 88 | CRY_LOG_DEBUG("pConsole has not initialized yet. Skipping construction."); 89 | return; 90 | } 91 | 92 | if(!pTimer) 93 | { 94 | CRY_LOG_DEBUG("pTimer has not initialized yet. Skipping construction."); 95 | return; 96 | } 97 | 98 | if (!pCryPak) 99 | { 100 | CRY_LOG_DEBUG("pCryPak has not initialized yet. Skipping construction."); 101 | return; 102 | } 103 | 104 | if (!pRenderer) 105 | { 106 | CRY_LOG_DEBUG("pRenderer has not initialized yet. Skipping construction."); 107 | return; 108 | } 109 | 110 | // Attempt to load our intial splash texture from CVar 111 | if (!m_pInitialSplashTexture && m_sCVars.m_iInitialSplashEnable > 0) 112 | { 113 | string InitialSplashTexturePath = m_sCVars.m_pInitialSplashTexturePath->GetString(); 114 | 115 | if (pCryPak->IsFileExist(InitialSplashTexturePath)) 116 | { 117 | m_pInitialSplashTexture = pRenderer->EF_LoadTexture(InitialSplashTexturePath, FT_DONT_STREAM | FT_NOMIPS); 118 | if (!m_pInitialSplashTexture) 119 | CRY_LOG_ERROR("Unable to load initial splash texture. Path '%s'", InitialSplashTexturePath); 120 | } 121 | else 122 | CRY_LOG_ERROR("Initial splash texture not found. Path: '%s'", InitialSplashTexturePath); 123 | } 124 | 125 | // Attempt to load our main splash texture from CVar 126 | if (!m_pSplashTexture && m_sCVars.m_iMainSplashEnable > 0) 127 | { 128 | string SplashTexturePath = m_sCVars.m_pSplashTexturePath->GetString(); 129 | if (pCryPak->IsFileExist(SplashTexturePath)) 130 | { 131 | m_pSplashTexture = pRenderer->EF_LoadTexture(SplashTexturePath, FT_DONT_STREAM | FT_NOMIPS); 132 | if (!m_pSplashTexture) 133 | { 134 | CRY_LOG_ERROR("Unable to load initial splash texture. Path '%s'", SplashTexturePath); 135 | } 136 | else 137 | { 138 | m_pSplashTexture->SetHighQualityFiltering(true); 139 | } 140 | } 141 | else 142 | CRY_LOG_ERROR("Splash texture not found. Path: '%s'", SplashTexturePath); 143 | } 144 | 145 | if (m_pSplashTexture) 146 | { 147 | pConsole->GetCVar("sys_rendersplashscreen")->Set(0); // Disable built-in splash screen routine 148 | } 149 | 150 | m_bInitialized = true; 151 | CRY_LOG_DEBUG("Constructed splash example."); 152 | } 153 | } 154 | 155 | /////////////////////////////////////////////////////////////////////////// 156 | //! Handles the initial splash display. 157 | void CSplashExample::DisplayInitialSplash(bool stallThread) 158 | { 159 | CRY_LOG_CALL("CSplashExample::DisplayInitialSplash()"); 160 | 161 | if (m_pInitialSplashTexture) 162 | { 163 | // Set viewport to our initial splash texture size if not already. 164 | gEnv->pConsole->GetCVar("r_width")->Set(m_pInitialSplashTexture->GetWidth()); 165 | gEnv->pConsole->GetCVar("r_height")->Set(m_pInitialSplashTexture->GetHeight()); 166 | gEnv->pConsole->GetCVar("r_fullscreen")->Set(0); // Force window mode (Note this may bring us our of fullscreen if not disabled in cfg). 167 | gEnv->pConsole->GetCVar("r_fullscreenwindow")->Set(1); // Remove window border (Note same problem if not enabled in cfg). 168 | 169 | gEnv->pConsole->GetCVar("sys_rendersplashscreen")->Set(0); // Disable built-in splash screen routine 170 | 171 | // force window update 172 | gEnv->pRenderer->FlushRTCommands(true, false, false); 173 | 174 | if (!stallThread) 175 | { 176 | if (m_pInitialSplashTexture) 177 | { 178 | // Hide cursor immediately 179 | gEnv->pSystem->GetIHardwareMouse()->Hide(true); 180 | 181 | // Push a single frame with our texture to renderer 182 | gEnv->pRenderer->BeginFrame(); 183 | Draw2DImage(m_pInitialSplashTexture, true); 184 | gEnv->pRenderer->EndFrame(); 185 | } 186 | } 187 | else 188 | { 189 | CRY_LOG_DEBUG("Stalling thread for initial splash"); 190 | if (!DrawAndStall(gEnv->pTimer->GetAsyncCurTime(), m_sCVars.m_fInitialSplashPlaybackTime, m_pInitialSplashTexture, true)) 191 | return; 192 | } 193 | } 194 | } 195 | 196 | /////////////////////////////////////////////////////////////////////////// 197 | //! Handles the main splash display. 198 | void CSplashExample::DisplayMainSplash(const bool stallThread) 199 | { 200 | CRY_LOG_CALL("CSplashExample::DisplayMainSplash()"); 201 | 202 | const SWindowProperties sWindowProperties = GetWindowProperties(); 203 | 204 | // Propagate changes 205 | static bool bSetCVars = false; 206 | if (!bSetCVars) 207 | { 208 | gEnv->pConsole->GetCVar("r_fullscreen")->Set(sWindowProperties.sWindowMode.bFullscreen); 209 | CRY_LOG_DEBUG("Set Fullscreen CVar to profile setting ('%s')", CryStringUtils::toString(sWindowProperties.sWindowMode.bFullscreen)); 210 | gEnv->pConsole->GetCVar("r_fullscreenwindow")->Set(sWindowProperties.sWindowMode.bHideWindowBorder); 211 | CRY_LOG_DEBUG("Set FullscreenWindow CVar to profile setting ('%s')", CryStringUtils::toString(sWindowProperties.sWindowMode.bHideWindowBorder)); 212 | SetScreenResolution(sWindowProperties.sScreenResolution); 213 | CRY_LOG_DEBUG("Got splash resolution from profile setting ('%s')", sWindowProperties.sScreenResolution.sResolution); 214 | bSetCVars = true; 215 | } 216 | 217 | // This will update the window using any changed cvars 218 | gEnv->pRenderer->FlushRTCommands(false, true, true); 219 | 220 | if (m_pSplashTexture) 221 | { 222 | // Hide cursor 223 | gEnv->pSystem->GetIHardwareMouse()->Hide(true); 224 | 225 | if (!stallThread) 226 | { 227 | gEnv->pRenderer->BeginFrame(); 228 | Draw2DImage(m_pSplashTexture); 229 | gEnv->pRenderer->EndFrame(); 230 | } 231 | else 232 | { 233 | // Set the start time for render, note this is not an accurate stamp 234 | // for when the image is actually rendered to the screen so we can 235 | // apply an additional offset here. 236 | float StartTime = gEnv->pTimer->GetAsyncCurTime() + m_sCVars.m_fStartTimeOffset; 237 | 238 | // Get the minimum playback time 239 | float LengthTime = m_sCVars.m_fSplashPlaybackTime; 240 | 241 | CRY_LOG_DEBUG("Stalling thread for splash, (start=%s,Length=%s)", CryStringUtils::toString(StartTime), CryStringUtils::toString(LengthTime)); 242 | 243 | // Stall the engine if we havn't shown our splash for enough time! 244 | DrawAndStall(StartTime, LengthTime, m_pSplashTexture, false); 245 | 246 | // After Stall... 247 | 248 | // Show cursor 249 | gEnv->pSystem->GetIHardwareMouse()->Hide(false); 250 | 251 | // Make sure we dont use this tex again! 252 | if (m_pSplashTexture) m_pSplashTexture->Release(); 253 | m_pSplashTexture = nullptr; 254 | 255 | CRY_LOG_DEBUG("Finished main splash screen draw cycle."); 256 | } 257 | } 258 | } 259 | 260 | /////////////////////////////////////////////////////////////////////////// 261 | //! Draws the supplied texture in stretched mode to the main viewport 262 | void CSplashExample::Draw2DImage(const ITexture * pTex, bool bUseTextureSize) const 263 | { 264 | if (pTex) 265 | { 266 | int RenderWidth = gEnv->pRenderer->GetOverlayWidth(); 267 | int RenderHeight = gEnv->pRenderer->GetOverlayHeight(); 268 | int TextureWidth = pTex->GetWidth(); 269 | int TextureHeight = pTex->GetHeight(); 270 | 271 | float PosX = 0; 272 | float PosY = 0; 273 | 274 | if (TextureWidth > 0 && TextureHeight > 0 && RenderWidth > 0 && RenderHeight > 0) 275 | { 276 | if (bUseTextureSize) 277 | { 278 | // Get Center offsets from window size 279 | if (TextureWidth < RenderWidth) 280 | PosX = (float)(int)((RenderWidth - TextureWidth) / 2); // raw cast to int to floor, then float to suppress warnings 281 | if (TextureHeight < RenderHeight) 282 | PosY = (float)(int)((RenderHeight - TextureHeight) / 2); // raw cast to int to floor, then float to suppress warnings 283 | 284 | // Don't exceed renderable dimensions 285 | RenderWidth = min(TextureWidth, RenderWidth); 286 | RenderHeight = min(TextureHeight, RenderHeight); 287 | } 288 | 289 | //gEnv->pRenderer->SetViewport(0, 0, RenderWidth, RenderHeight); 290 | 291 | // Force fullscreen render 292 | /* 293 | float fScaledW = RenderWidth / (float(RenderWidth) / 800.0f); 294 | float fScaledH = RenderHeight / (float(RenderHeight) / 600.0f); 295 | */ 296 | float fScaledW = 800.f; 297 | float fScaledH = 600.f; 298 | 299 | // make sure it's rendered in full screen mode when triple buffering is enabled as well 300 | for (size_t n = 0; n < 3; n++) 301 | { 302 | // gEnv->pRenderer->SetCullMode(R_CULL_NONE); 303 | gEnv->pRenderer->SetState(GS_NODEPTHTEST); 304 | // gEnv->pRenderer->Draw2dImageStretchMode(true); 305 | // gEnv->pRenderer->Draw2dImage(PosX, PosY, fScaledW, fScaledH, pTex->GetTextureID(), 0.0f, 1.0f, 1.0f, 0.0f); 306 | gEnv->pRenderer->Draw2dImage(0, 0, 800, 600, pTex->GetTextureID(), 0.0f, 1.0f, 1.0f, 0.0f); 307 | // gEnv->pRenderer->Draw2dImageStretchMode(false); 308 | } 309 | } 310 | } 311 | } 312 | 313 | /////////////////////////////////////////////////////////////////////////// 314 | //! Stalls the current executing thread. Draws stretched texture. Returns false if app requested exit through PumpWindowMessage() 315 | //! float StartTime Time from getasynccurtime() start of render 316 | //! float LengthTime Time in seconds, duration of playback 317 | //! ITexture * pTex Texture to draw 318 | //! bool bUseTextureSize Use the size of the texture to set the viewport size 319 | //! bool bUpdateInput Each loop/frame, update input system 320 | //! bool bUpdateMouse Each loop/frame, update hardware mouse system 321 | //! bool bDrawConsole Each loop/frame, update console draw state 322 | //! bool bPumpWinMsg Each loop/frame, handle any pumped window messages 323 | bool CSplashExample::DrawAndStall(float StartTime, float LengthTime, ITexture * pTex, bool bUseTextureSize, bool bUpdateInput, bool bUpdateMouse, bool bDrawConsole, bool bPumpWinMsg) 324 | { 325 | CRY_LOG_CALL("CSplashExample::DrawAndStall()"); 326 | 327 | if (pTex) 328 | { 329 | CSimpleThreadBackOff backoff; 330 | while (gEnv->pTimer->GetAsyncCurTime() - LengthTime <= StartTime) { 331 | 332 | // Make sure we don't stall on app exit 333 | if (IsShuttingDown()) 334 | break; 335 | 336 | // Keep hiding cursor (needed for some reason) 337 | if (!gEnv->pConsole->GetStatus()) 338 | gEnv->pSystem->GetIHardwareMouse()->Hide(true); 339 | else 340 | gEnv->pSystem->GetIHardwareMouse()->Hide(false); 341 | 342 | // Make sure windows doesn't think our application has crashed 343 | if (bPumpWinMsg) if (gEnv->pSystem->PumpWindowMessage(true) == -1) return false; 344 | 345 | // Update input events so we don't get any windows cursors during our splash 346 | if (bUpdateInput) gEnv->pInput->Update(true); 347 | if (bUpdateMouse) gEnv->pSystem->GetIHardwareMouse()->Update(); 348 | 349 | // Render the splash image 350 | gEnv->pRenderer->BeginFrame(); 351 | Draw2DImage(pTex, bUseTextureSize); // Our overlay texture (can have alpha which is why we need background color) 352 | if (bDrawConsole) gEnv->pConsole->Draw(); // Allow drawing of console while we stall 353 | gEnv->pRenderer->EndFrame(); 354 | 355 | // Give the system some breathing space 356 | backoff.backoff(); 357 | continue; 358 | }; 359 | } 360 | return true; 361 | } 362 | 363 | /////////////////////////////////////////////////////////////////////////// 364 | //! Gets the user-defined, or default-defined window properties from PlayerProfiles. 365 | const CSplashExample::SWindowProperties CSplashExample::GetWindowProperties() 366 | { 367 | CRY_LOG_CALL("CSplashExample::GetWindowProperties()"); 368 | 369 | if (m_pDefaultProfile == nullptr) 370 | { 371 | static bool warned = false; 372 | if (!warned) 373 | { 374 | warned = true; 375 | CRY_LOG_WARNING("Could not get default profile! Using native resolution."); 376 | 377 | return SWindowProperties( 378 | GetScreenResolution(-1), 379 | #if !defined(_RELEASE) 380 | SWindowMode(0, 0) 381 | #else 382 | SWindowMode(1, 0) 383 | #endif 384 | ); 385 | } 386 | } 387 | 388 | // Revert to defaults from lib/config 389 | auto pProfile = (m_pCurrentProfile) ? m_pCurrentProfile : m_pDefaultProfile; 390 | 391 | // Get profile attributes or use predefined defaults 392 | int paResolution; 393 | bool paFullscreen; 394 | bool paFullscreenWindow; 395 | 396 | TFlowInputData buf; 397 | 398 | buf.SetValueWithConversion(WINDOW_DEFAULT_RESOLUTION_IDX); 399 | pProfile->GetAttribute("Resolution", buf, true); 400 | buf.GetValueWithConversion(paResolution); 401 | 402 | buf.SetValueWithConversion(WINDOW_DEFAULT_FULLSCREEN); 403 | pProfile->GetAttribute("Fullscreen", buf, true); 404 | buf.GetValueWithConversion(paFullscreen); 405 | 406 | buf.SetValueWithConversion(WINDOW_DEFAULT_HIDE_BORDER); 407 | pProfile->GetAttribute("FullscreenWindow", buf, true); 408 | buf.GetValueWithConversion(paFullscreenWindow); 409 | 410 | return SWindowProperties( 411 | GetScreenResolution(paResolution), 412 | SWindowMode(paFullscreen, paFullscreenWindow) 413 | ); 414 | } 415 | 416 | ////////////////////////////////////////////////////////////////////////// 417 | //! Gets the requested screen resolution from GetScreenResolutions() 418 | const CSplashExample::SScreenResolution CSplashExample::GetScreenResolution(const int idx) 419 | { 420 | CRY_LOG_CALL("CSplashExample::GetScreenResolution()"); 421 | 422 | static auto ScreenResolutions = CSplashExample::GetScreenResolutions(); 423 | 424 | // Didn't get any resolution data for some reason 425 | if (ScreenResolutions.size() <= 0) 426 | { 427 | CRY_LOG_ERROR("Could not get available screen resolutions for display."); 428 | return SScreenResolution(); 429 | } 430 | 431 | // If we don't have a resolution this high, get the highest available 432 | if (idx >= ScreenResolutions.size() || idx < 0) 433 | { 434 | CRY_LOG_ALWAYS("idx is not supported, defaulting to highest available. ('%s')", ScreenResolutions[ScreenResolutions.size() - 1].sResolution); 435 | return ScreenResolutions[ScreenResolutions.size() - 1]; 436 | } 437 | 438 | return ScreenResolutions[idx]; 439 | } 440 | 441 | ////////////////////////////////////////////////////////////////////////// 442 | //! Generates a list of supported screen resolutions 443 | //! From GameSDK Sample 444 | const std::vector CSplashExample::GetScreenResolutions() 445 | { 446 | CRY_LOG_CALL("CSplashExample::GetScreenResolutions()"); 447 | 448 | static std::vector ScreenResolutions; 449 | 450 | #if CRY_PLATFORM_DESKTOP 451 | 452 | if (ScreenResolutions.size() > 0) 453 | return ScreenResolutions; 454 | 455 | CryFixedStringT<16> format; 456 | 457 | SDispFormat *formats = NULL; 458 | int numFormats = gEnv->pRenderer->EnumDisplayFormats(NULL); 459 | if (numFormats) 460 | { 461 | formats = new SDispFormat[numFormats]; 462 | gEnv->pRenderer->EnumDisplayFormats(formats); 463 | } 464 | 465 | for (int i = 0; i < numFormats; ++i) 466 | { 467 | bool bAlreadyExists = false; 468 | 469 | for (auto &res : ScreenResolutions) 470 | if (res.iWidth == formats[i].m_Width && res.iHeight == formats[i].m_Height) 471 | bAlreadyExists = true; 472 | 473 | if (bAlreadyExists || formats[i].m_Width < 800) 474 | continue; 475 | 476 | format.Format("%i X %i", formats[i].m_Width, formats[i].m_Height); 477 | 478 | ScreenResolutions.emplace_back(formats[i].m_Width, formats[i].m_Height, formats[i].m_BPP, format.c_str()); 479 | } 480 | 481 | if (formats) 482 | delete[] formats; 483 | 484 | #endif 485 | 486 | return ScreenResolutions; 487 | } 488 | 489 | /////////////////////////////////////////////////////////////////////////// 490 | //! Sets the r_width and r_height CVars if they are set properly in sResolution 491 | void CSplashExample::SetScreenResolution(const SScreenResolution &res) const 492 | { 493 | CRY_LOG_CALL("CSplashExample::SetScreenResolution()"); 494 | 495 | if (res.iWidth > 0 && res.iHeight > 0) 496 | { 497 | CRY_LOG_DEBUG("Setting resolution to '%s'", res.sResolution); 498 | 499 | gEnv->pConsole->GetCVar("r_width")->Set(res.iWidth); 500 | gEnv->pConsole->GetCVar("r_height")->Set(res.iHeight); 501 | } 502 | else 503 | { 504 | CRY_LOG_ERROR("Invalid resolution params supplied to SetScreenResolution(). (iWidth=%s,iHeight=%s)", res.iWidth, res.iHeight); 505 | } 506 | } 507 | 508 | /////////////////////////////////////////////////////////////////////////// 509 | //! Gets the Player Profile Manager interface 510 | IPlayerProfileManager * CSplashExample::GetIPlayerProfileManager() 511 | { 512 | CRY_LOG_CALL("CSplashExample::GetIPlayerProfileManager()"); 513 | 514 | assert(gEnv->pGameFramework != nullptr); 515 | 516 | // Make sure we have what we need 517 | if (!gEnv->pGameFramework) 518 | return nullptr; 519 | 520 | return gEnv->pGameFramework->GetIPlayerProfileManager(); 521 | } 522 | 523 | ////////////////////////////////////////////////////////////////////////// 524 | //! Gets the default player profile 525 | IPlayerProfile * CSplashExample::GetDefaultPlayerProfile(IPlayerProfileManager * pProfileManager) 526 | { 527 | CRY_LOG_CALL("CSplashExample::GetDefaultPlayerProfile()"); 528 | 529 | if (!pProfileManager) return nullptr; 530 | 531 | IPlayerProfile * pDefaultProfile = pProfileManager->GetDefaultProfile(); 532 | 533 | // Get the current user profile (or default if none) 534 | return (pProfileManager) ? pProfileManager->GetDefaultProfile() : nullptr; 535 | } 536 | 537 | ////////////////////////////////////////////////////////////////////////// 538 | //! Gets the currently used player profile 539 | IPlayerProfile * CSplashExample::GetCurrentPlayerProfile(IPlayerProfileManager * pProfileManager) 540 | { 541 | CRY_LOG_CALL("CSplashExample::GetCurrentPlayerProfile()"); 542 | 543 | // Get the current user profile (or default if none) 544 | return (pProfileManager) ? pProfileManager->GetCurrentProfile(pProfileManager->GetCurrentUser()) : nullptr; 545 | } 546 | 547 | /////////////////////////////////////////////////////////////////////////// 548 | //! Sets the splash flag attribute & manually saves the profile 549 | bool CSplashExample::TryLoadProfileAttributes() 550 | { 551 | CRY_LOG_CALL("CSplashExample::TryLoadProfileAttributes()"); 552 | 553 | // Attempt to get profile manager if we havn't already 554 | m_pProfileManager = (!m_pProfileManager)? GetIPlayerProfileManager() : m_pProfileManager; 555 | if (m_pProfileManager != nullptr) 556 | { 557 | // Register the profile listener 558 | RegisterProfileListener(this); 559 | 560 | // Attempt to get the default profile if we havn't already 561 | m_pDefaultProfile = (!m_pDefaultProfile) ? GetDefaultPlayerProfile() : m_pDefaultProfile; 562 | if (m_pDefaultProfile != nullptr) 563 | { 564 | // Attempt to get the current profile if we havn't already 565 | m_pCurrentProfile = (!m_pCurrentProfile)? GetCurrentPlayerProfile() : m_pCurrentProfile; 566 | if (m_pCurrentProfile != nullptr) 567 | { 568 | // Use the SplashFlag in the player profile to check if this is the very first game launch 569 | bool bSplashFlag = true; 570 | if (!m_pCurrentProfile->GetAttribute("SplashFlag_FirstRun", bSplashFlag, false)) 571 | { 572 | int res = 0; 573 | // If profile attribute Resolution is <= 0 (or not set). 574 | if (!m_pCurrentProfile->GetAttribute("Resolution", res, false) || res <= 0) 575 | { 576 | // First launch of game, copy defaults (libs/config) to the current profile. 577 | if (!CopyProfileAttributes(/* To */ m_pCurrentProfile, /* From */ m_pDefaultProfile)) 578 | { 579 | CRY_LOG_ERROR("Could not copy default profile attributes to '%s'.).", m_pCurrentProfile->GetName()); 580 | } 581 | } 582 | else 583 | { 584 | // If the resolution is set higher than the bare minimum supported then 585 | // assume this profile was created outside the scope of this plugin. 586 | // 587 | // Add the splashflag to this profile to prevent overwriting the window preoperties later. 588 | if (!SaveSplashFlagToProfile()) 589 | { 590 | CRY_LOG_ERROR("Could not save splash flag to profile '%s'.", m_pCurrentProfile->GetName()); 591 | } 592 | } 593 | } 594 | 595 | m_bLoadedProfileAttributes = true; 596 | } 597 | else 598 | { 599 | // Could not get current profile, use defaults 600 | return false; 601 | } 602 | } 603 | else 604 | { 605 | // Could not get default profile 606 | 607 | 608 | return false; 609 | } 610 | } 611 | else 612 | { 613 | // Could not get profile manager 614 | return false; 615 | } 616 | return true; 617 | } 618 | 619 | /////////////////////////////////////////////////////////////////////////// 620 | //! Copies profile attributes from one profile to the other 621 | bool CSplashExample::CopyProfileAttributes(IPlayerProfile * pTo, IPlayerProfile * pFrom) 622 | { 623 | CRY_LOG_CALL("CSplashExample::CopyProfileAttributes()"); 624 | 625 | assert(pTo != nullptr); 626 | assert(pFrom != nullptr); 627 | 628 | if (pTo == pFrom) 629 | return true; 630 | 631 | if (pTo->IsDefault()) 632 | return false; 633 | 634 | auto pAttrEnumerator = pFrom->CreateAttributeEnumerator(); 635 | IAttributeEnumerator::SAttributeDescription desc; 636 | TFlowInputData value; 637 | bool ok = true; 638 | while (pAttrEnumerator->Next(desc)) 639 | { 640 | pFrom->GetAttribute(desc.name, value); 641 | if (!pTo->SetAttribute(desc.name, value)) 642 | { 643 | CRY_LOG_ERROR("Could not set default attribute to current profile."); 644 | ok = false; 645 | } 646 | } 647 | return ok; 648 | } 649 | 650 | /////////////////////////////////////////////////////////////////////////// 651 | //! Sets the splash flag attribute on the current profile 652 | bool CSplashExample::SetProfileAttribute(const char * attributeName, const TFlowInputData attributeValue) 653 | { 654 | CRY_LOG_CALL("CSplashExample::SetProfileAttribute()"); 655 | return m_pCurrentProfile->SetAttribute(attributeName, attributeValue); 656 | } 657 | template 658 | bool CSplashExample::SetProfileAttribute(const char * attributeName, const T& attributeValue) 659 | { 660 | CRY_LOG_CALL("CSplashExample::SetProfileAttribute()"); 661 | return m_pCurrentProfile->SetAttribute(attributeName, attributeValue); 662 | } 663 | 664 | /////////////////////////////////////////////////////////////////////////// 665 | //! Sets the splash flag attribute & manually saves the profile 666 | bool CSplashExample::SaveSplashFlagToProfile() 667 | { 668 | CRY_LOG_CALL("CSplashExample::SaveSplashFlagToProfile()"); 669 | 670 | if (!SetProfileAttribute("SplashFlag_FirstRun", 0)) 671 | return false; 672 | 673 | EPOResult res; 674 | if (!SavePlayerProfile(res, EProfileReasons::ePR_Options)) 675 | return false; 676 | 677 | return true; 678 | } 679 | 680 | /////////////////////////////////////////////////////////////////////////// 681 | //! Manually saves the current profile 682 | bool CSplashExample::SavePlayerProfile(EPOResult &result, EProfileReasons reason) 683 | { 684 | CRY_LOG_CALL("CSplashExample::SavePlayerProfile()"); 685 | 686 | assert(m_pProfileManager != nullptr); 687 | assert(m_pCurrentProfile != nullptr); 688 | 689 | m_bIgnoreNextProfileSave = true; 690 | 691 | if (m_pProfileManager->SaveProfile(m_pCurrentProfile->GetName(), result, reason)) 692 | { 693 | return true; 694 | } 695 | else 696 | { 697 | m_bIgnoreNextProfileSave = false; 698 | return false; 699 | } 700 | } 701 | 702 | /////////////////////////////////////////////////////////////////////////// 703 | //! Registers the player profile listener if not registered 704 | bool CSplashExample::RegisterProfileListener(IPlayerProfileListener * pListener) 705 | { 706 | CRY_LOG_CALL("CSplashExample::RegisterProfileListener()"); 707 | assert(pListener); 708 | 709 | if (!m_bProfileListenerRegistered) 710 | { 711 | auto pMan = GetIPlayerProfileManager(); 712 | if (pMan) 713 | { 714 | pMan->AddListener(pListener, false); 715 | m_bProfileListenerRegistered = true; 716 | CRY_LOG_DEBUG("Registered Profile Manager Listener."); 717 | } 718 | else 719 | { 720 | CRY_LOG_DEBUG("Profile Manager not available, could not register listener."); 721 | } 722 | } 723 | 724 | return m_bProfileListenerRegistered; 725 | } 726 | 727 | /////////////////////////////////////////////////////////////////////////// 728 | //! De-Registers the player profile listener 729 | bool CSplashExample::UnregisterProfileListener(IPlayerProfileListener * pListener) 730 | { 731 | CRY_LOG_CALL("CSplashExample::UnregisterProfileListener()"); 732 | assert(pListener); 733 | 734 | if (m_bProfileListenerRegistered) 735 | { 736 | auto pMan = GetIPlayerProfileManager(); 737 | if (pMan) 738 | { 739 | pMan->RemoveListener(pListener); 740 | m_bProfileListenerRegistered = false; 741 | CRY_LOG_DEBUG("Unregistered Profile Manager Listener."); 742 | } 743 | else 744 | { 745 | CRY_LOG_ERROR("Profile manager destructed before removing listener."); 746 | } 747 | } 748 | 749 | return !m_bProfileListenerRegistered; 750 | } 751 | 752 | /////////////////////////////////////////////////////////////////////////// 753 | //! Registers the system event listener if not registered 754 | bool CSplashExample::RegisterSystemListener(ISystemEventListener * pListener) 755 | { 756 | CRY_LOG_CALL("CSplashExample::RegisterSystemListener()"); 757 | assert(pListener); 758 | 759 | if (!m_bSystemListenerRegistered) 760 | { 761 | if (gEnv->pSystem) 762 | { 763 | auto pDispatcher = gEnv->pSystem->GetISystemEventDispatcher(); 764 | if (pDispatcher) 765 | { 766 | #ifdef CRYGENERATE_SINGLETONCLASS_GUID 767 | pDispatcher->RegisterListener(pListener, "SplashExampleSystem"); 768 | #else 769 | pDispatcher->RegisterListener(pListener); 770 | #endif 771 | m_bSystemListenerRegistered = true; 772 | CRY_LOG_DEBUG("Registered System Event Listener."); 773 | } 774 | else 775 | { 776 | CRY_LOG_DEBUG("System Event Dispatcher not available, could not register listener."); 777 | } 778 | } 779 | else 780 | { 781 | CRY_LOG_DEBUG("Profile Manager not available, could not register listener."); 782 | } 783 | } 784 | 785 | return m_bSystemListenerRegistered; 786 | } 787 | 788 | /////////////////////////////////////////////////////////////////////////// 789 | //! De-Registers the system event listener 790 | bool CSplashExample::UnregisterSystemListener(ISystemEventListener * pListener) 791 | { 792 | CRY_LOG_CALL("CSplashExample::UnregisterSystemListener()"); 793 | assert(pListener); 794 | 795 | if (m_bSystemListenerRegistered) 796 | { 797 | if (gEnv->pSystem) 798 | { 799 | auto pDispatcher = gEnv->pSystem->GetISystemEventDispatcher(); 800 | if (pDispatcher) 801 | { 802 | pDispatcher->RemoveListener(pListener); 803 | m_bSystemListenerRegistered = false; 804 | CRY_LOG_DEBUG("Unregistered System Event Listener."); 805 | } 806 | else 807 | { 808 | CRY_LOG_ERROR("System Event Dispatcher destructed before removing listener."); 809 | } 810 | } 811 | else 812 | { 813 | CRY_LOG_ERROR("System destructed before removing listener."); 814 | } 815 | } 816 | 817 | return !m_bSystemListenerRegistered; 818 | } 819 | 820 | /////////////////////////////////////////////////////////////////////////// 821 | //! Listener: 822 | void CSplashExample::SaveToProfile(IPlayerProfile* pProfile, bool online, unsigned int /*EProfileReasons*/ reason) 823 | { 824 | CRY_LOG_CALL("CSplashExample::SaveToProfile()"); 825 | 826 | if (m_bIgnoreNextProfileSave) 827 | { 828 | m_bIgnoreNextProfileSave = false; 829 | return; 830 | } 831 | 832 | // Todo: Make sure we have required profile attributes? 833 | return; 834 | } 835 | 836 | /////////////////////////////////////////////////////////////////////////// 837 | //! Listener: Ensures loaded profiles have the splash flag. 838 | void CSplashExample::LoadFromProfile(IPlayerProfile* pProfile, bool online, unsigned int /*EProfileReasons*/ reason) 839 | { 840 | CRY_LOG_CALL("CSplashExample::LoadFromProfile()"); 841 | 842 | if (!pProfile->IsDefault()) 843 | { 844 | if (m_pCurrentProfile == nullptr) 845 | m_pCurrentProfile = pProfile; 846 | else if (m_pCurrentProfile != pProfile) 847 | SaveSplashFlagToProfile(); 848 | } 849 | } 850 | 851 | /////////////////////////////////////////////////////////////////////////// 852 | //! Listener: All splash operations are dispatched from this method 853 | void CSplashExample::OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam) 854 | { 855 | 856 | CRY_LOG_CALL("Debug OnSystemEvent. event = %s", SYSEVENT_CODE_TO_TEXT(event)); 857 | 858 | // Unlink listeners if we notice a shutdown (CE Registry Destruction Bug) 859 | if (IsShuttingDown()) return; 860 | 861 | // Construct at the earliest opportunity 862 | // * This is a safe-guard if the plugins object construction 863 | // * is moved before other systems have initialized. 864 | // * (Currently in 5.3.0, Construction, Initialization, and 865 | // * ESYSTEM_EVENT_PRE_RENDERER_INIT are called at the same 866 | // * time in CSystem::Init) 867 | 868 | switch (event) 869 | { 870 | // First stage, override startscreen, hide cursor and draw our gui splash (splash_a) 871 | case ESYSTEM_EVENT_PRE_RENDERER_INIT: 872 | { 873 | if (!m_bInitialized) return; // Only respond to events if we are ready to 874 | 875 | // Initial splash 876 | DisplayInitialSplash(); 877 | 878 | break; 879 | } 880 | 881 | // Second stage, stall with our initial splash for the specified time 882 | case ESYSTEM_EVENT_GAME_POST_INIT: 883 | { 884 | // Stall if needed 885 | DisplayInitialSplash(true); 886 | 887 | break; 888 | } 889 | 890 | // Third stage, Render main splash image, and delay further loading for the specified time. 891 | case ESYSTEM_EVENT_GAME_POST_INIT_DONE: 892 | { 893 | // Load profile attributes or defaults 894 | if (!m_bLoadedProfileAttributes) 895 | TryLoadProfileAttributes(); 896 | 897 | // Stall if needed 898 | DisplayMainSplash(true); 899 | } 900 | } 901 | } 902 | 903 | bool CSplashExample::IsShuttingDown() 904 | { 905 | if (gEnv->pSystem->IsQuitting()) 906 | { 907 | // cleanup immmediately 908 | UnregisterProfileListener(this); 909 | UnregisterSystemListener(this); 910 | return true; 911 | } 912 | return false; 913 | } -------------------------------------------------------------------------------- /Code/SplashExample.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | 3 | #pragma once 4 | #include "cvars.h" 5 | 6 | #include 7 | #include 8 | 9 | #ifdef _DEBUG 10 | #define POR_RESULT_CODE_TO_TEXT(EResult) \ 11 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_Success)? "ePOR_Success" : \ 12 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_NotInitialized)? "ePOR_NotInitialized" : \ 13 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_NameInUse)? "ePOR_NameInUse" : \ 14 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_UserNotLoggedIn)? "ePOR_UserNotLoggedIn" : \ 15 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_NoSuchProfile)? "ePOR_NoSuchProfile" : \ 16 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_ProfileInUse)? "ePOR_ProfileInUse" : \ 17 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_NoActiveProfile)? "ePOR_NoActiveProfile" : \ 18 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_DefaultProfile)? "ePOR_DefaultProfile" : \ 19 | (EResult == IPlayerProfileManager::EProfileOperationResult::ePOR_LoadingProfile)? "ePOR_LoadingProfile" : "ePOR_Unknown" 20 | 21 | #define SYSEVENT_CODE_TO_TEXT(EEvent) \ 22 | EEvent == ESYSTEM_EVENT_CHANGE_FOCUS? "ESYSTEM_EVENT_CHANGE_FOCUS" : \ 23 | EEvent == ESYSTEM_EVENT_MOVE? "ESYSTEM_EVENT_MOVE" : \ 24 | EEvent == ESYSTEM_EVENT_RESIZE? "ESYSTEM_EVENT_RESIZE" : \ 25 | EEvent == ESYSTEM_EVENT_ACTIVATE? "ESYSTEM_EVENT_ACTIVATE" : \ 26 | EEvent == ESYSTEM_EVENT_POS_CHANGED? "ESYSTEM_EVENT_POS_CHANGED" : \ 27 | EEvent == ESYSTEM_EVENT_STYLE_CHANGED? "ESYSTEM_EVENT_STYLE_CHANGED" : \ 28 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_START_PRELOADINGSCREEN? "ESYSTEM_EVENT_LEVEL_LOAD_START_PRELOADINGSCREEN" : \ 29 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_RESUME_GAME? "ESYSTEM_EVENT_LEVEL_LOAD_RESUME_GAME" : \ 30 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_START? "ESYSTEM_EVENT_LEVEL_LOAD_START" : \ 31 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_PREPARE? "ESYSTEM_EVENT_LEVEL_LOAD_PREPARE" : \ 32 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN? "ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN" : \ 33 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_LOADINGSCREEN_ACTIVE? "ESYSTEM_EVENT_LEVEL_LOAD_LOADINGSCREEN_ACTIVE" : \ 34 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_START? "ESYSTEM_EVENT_LEVEL_LOAD_START" : \ 35 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_END? "ESYSTEM_EVENT_LEVEL_LOAD_END" : \ 36 | EEvent == ESYSTEM_EVENT_LEVEL_LOAD_ERROR? "ESYSTEM_EVENT_LEVEL_LOAD_ERROR" : \ 37 | EEvent == ESYSTEM_EVENT_LEVEL_NOT_READY? "ESYSTEM_EVENT_LEVEL_NOT_READY" : \ 38 | EEvent == ESYSTEM_EVENT_LEVEL_PRECACHE_START? "ESYSTEM_EVENT_LEVEL_PRECACHE_START" : \ 39 | EEvent == ESYSTEM_EVENT_LEVEL_PRECACHE_FIRST_FRAME? "ESYSTEM_EVENT_LEVEL_PRECACHE_FIRST_FRAME" : \ 40 | EEvent == ESYSTEM_EVENT_LEVEL_GAMEPLAY_START? "ESYSTEM_EVENT_LEVEL_GAMEPLAY_START" : \ 41 | EEvent == ESYSTEM_EVENT_LEVEL_UNLOAD? "ESYSTEM_EVENT_LEVEL_UNLOAD" : \ 42 | EEvent == ESYSTEM_EVENT_LEVEL_POST_UNLOAD? "ESYSTEM_EVENT_LEVEL_POST_UNLOAD" : \ 43 | EEvent == ESYSTEM_EVENT_GAME_POST_INIT? "ESYSTEM_EVENT_GAME_POST_INIT" : \ 44 | EEvent == ESYSTEM_EVENT_GAME_POST_INIT_DONE? "ESYSTEM_EVENT_GAME_POST_INIT_DONE" : \ 45 | EEvent == ESYSTEM_EVENT_FULL_SHUTDOWN? "ESYSTEM_EVENT_FULL_SHUTDOWN" : \ 46 | EEvent == ESYSTEM_EVENT_FAST_SHUTDOWN? "ESYSTEM_EVENT_FAST_SHUTDOWN" : \ 47 | EEvent == ESYSTEM_EVENT_LANGUAGE_CHANGE? "ESYSTEM_EVENT_LANGUAGE_CHANGE" : \ 48 | EEvent == ESYSTEM_EVENT_TOGGLE_FULLSCREEN? "ESYSTEM_EVENT_TOGGLE_FULLSCREEN" : \ 49 | EEvent == ESYSTEM_EVENT_SHARE_SHADER_COMBINATIONS? "ESYSTEM_EVENT_SHARE_SHADER_COMBINATIONS" : \ 50 | EEvent == ESYSTEM_EVENT_3D_POST_RENDERING_START? "ESYSTEM_EVENT_3D_POST_RENDERING_START" : \ 51 | EEvent == ESYSTEM_EVENT_3D_POST_RENDERING_END? "ESYSTEM_EVENT_3D_POST_RENDERING_END" : \ 52 | EEvent == ESYSTEM_EVENT_LEVEL_PRECACHE_END? "ESYSTEM_EVENT_LEVEL_PRECACHE_END" : \ 53 | EEvent == ESYSTEM_EVENT_GAME_MODE_SWITCH_START? "ESYSTEM_EVENT_GAME_MODE_SWITCH_START" : \ 54 | EEvent == ESYSTEM_EVENT_GAME_MODE_SWITCH_END? "ESYSTEM_EVENT_GAME_MODE_SWITCH_END" : \ 55 | EEvent == ESYSTEM_EVENT_VIDEO? "ESYSTEM_EVENT_VIDEO" : \ 56 | EEvent == ESYSTEM_EVENT_GAME_PAUSED? "ESYSTEM_EVENT_GAME_PAUSED" : \ 57 | EEvent == ESYSTEM_EVENT_GAME_RESUMED? "ESYSTEM_EVENT_GAME_RESUMED" : \ 58 | EEvent == ESYSTEM_EVENT_TIME_OF_DAY_SET? "ESYSTEM_EVENT_TIME_OF_DAY_SET" : \ 59 | EEvent == ESYSTEM_EVENT_EDITOR_ON_INIT? "ESYSTEM_EVENT_EDITOR_ON_INIT" : \ 60 | EEvent == ESYSTEM_EVENT_FRONTEND_INITIALISED? "ESYSTEM_EVENT_FRONTEND_INITIALISED" : \ 61 | EEvent == ESYSTEM_EVENT_EDITOR_GAME_MODE_CHANGED? "ESYSTEM_EVENT_EDITOR_GAME_MODE_CHANGED" : \ 62 | EEvent == ESYSTEM_EVENT_EDITOR_SIMULATION_MODE_CHANGED? "ESYSTEM_EVENT_EDITOR_SIMULATION_MODE_CHANGED" : \ 63 | EEvent == ESYSTEM_EVENT_ENVIRONMENT_SETTINGS_CHANGED? "ESYSTEM_EVENT_ENVIRONMENT_SETTINGS_CHANGED" : \ 64 | EEvent == ESYSTEM_EVENT_FRONTEND_RELOADED? "ESYSTEM_EVENT_FRONTEND_RELOADED" : \ 65 | EEvent == ESYSTEM_EVENT_SW_FORCE_LOAD_START? "ESYSTEM_EVENT_SW_FORCE_LOAD_START" : \ 66 | EEvent == ESYSTEM_EVENT_SW_FORCE_LOAD_END? "ESYSTEM_EVENT_SW_FORCE_LOAD_END" : \ 67 | EEvent == ESYSTEM_EVENT_SW_SHIFT_WORLD? "ESYSTEM_EVENT_SW_SHIFT_WORLD" : \ 68 | EEvent == ESYSTEM_EVENT_CHANGE_FOCUS? "ESYSTEM_EVENT_CHANGE_FOCUS" : \ 69 | EEvent == ESYSTEM_EVENT_STREAMING_INSTALL_ERROR? "ESYSTEM_EVENT_STREAMING_INSTALL_ERROR" : \ 70 | EEvent == ESYSTEM_EVENT_ONLINE_SERVICES_INITIALISED? "ESYSTEM_EVENT_ONLINE_SERVICES_INITIALISED" : \ 71 | EEvent == ESYSTEM_EVENT_AUDIO_IMPLEMENTATION_LOADED? "ESYSTEM_EVENT_AUDIO_IMPLEMENTATION_LOADED" : \ 72 | EEvent == ESYSTEM_EVENT_URI? "ESYSTEM_EVENT_URI" : \ 73 | EEvent == ESYSTEM_EVENT_USER? "ESYSTEM_EVENT_USER" : \ 74 | EEvent == ESYSTEM_EVENT_BEAM_PLAYER_TO_CAMERA_POS? "ESYSTEM_EVENT_BEAM_PLAYER_TO_CAMERA_POS" : \ 75 | EEvent == ESYSTEM_EVENT_CRYSYSTEM_INIT_DONE? "ESYSTEM_EVENT_CRYSYSTEM_INIT_DONE" : \ 76 | EEvent == ESYSTEM_EVENT_PRE_RENDERER_INIT? "ESYSTEM_EVENT_PRE_RENDERER_INIT" : \ 77 | EEvent == ESYSTEM_EVENT_GAMEWINDOW_ACTIVATE? "ESYSTEM_EVENT_GAMEWINDOW_ACTIVATE" : \ 78 | EEvent == ESYSTEM_EVENT_REGISTER_FLOWNODES? "ESYSTEM_EVENT_REGISTER_FLOWNODES" : \ 79 | EEvent == ESYSTEM_EVENT_CRYSYSTEM_INIT_DONE? "ESYSTEM_EVENT_CRYSYSTEM_INIT_DONE" : \ 80 | EEvent == ESYSTEM_EVENT_GAME_FRAMEWORK_INIT_DONE? "ESYSTEM_EVENT_GAME_FRAMEWORK_INIT_DONE" : "UNKNOWN" 81 | #else 82 | #define POR_RESULT_CODE_TO_TEXT(...) "" 83 | #define SYSEVENT_CODE_TO_TEXT(...) "" 84 | #endif // _DEBUG 85 | 86 | // This is due to the default gamesdk not using default profile attributes from configs. 87 | // -1 will tell GetScreenResolution(idx) to give us the native screen res 88 | #define WINDOW_DEFAULT_RESOLUTION_IDX -1 89 | #define WINDOW_DEFAULT_FULLSCREEN 1 90 | #define WINDOW_DEFAULT_HIDE_BORDER 0 91 | 92 | typedef IPlayerProfileManager::EProfileOperationResult EPOResult; 93 | 94 | // CSplashExample implements ISystemEventListener 95 | // 96 | //! Handles main functionality of plugin Splash Example 97 | class CSplashExample 98 | : public ISystemEventListener, 99 | public IPlayerProfileListener 100 | { 101 | 102 | private: 103 | 104 | //! Struct to hold screen resolution details 105 | struct SScreenResolution 106 | { 107 | // Copied from GameSDK - For using profile saved resolution 108 | int iWidth; 109 | int iHeight; 110 | int nDepthPerPixel; 111 | string sResolution; 112 | 113 | SScreenResolution(unsigned int _iWidth, unsigned int _iHeight, unsigned int _nDepthPerPixel, const char* _sResolution) : 114 | iWidth(_iWidth) 115 | , iHeight(_iHeight) 116 | , nDepthPerPixel(_nDepthPerPixel) 117 | , sResolution(_sResolution) 118 | {} 119 | 120 | SScreenResolution() : 121 | iWidth(-1) 122 | , iHeight(-1) 123 | , nDepthPerPixel(-1) 124 | , sResolution("") 125 | {} 126 | }; 127 | 128 | struct SWindowMode 129 | { 130 | bool bFullscreen; 131 | bool bHideWindowBorder; 132 | 133 | SWindowMode(const bool &fullscreen, const bool &hideWindowBorder) 134 | { 135 | bFullscreen = fullscreen; 136 | bHideWindowBorder = hideWindowBorder; 137 | } 138 | 139 | SWindowMode() : 140 | bFullscreen(1), 141 | bHideWindowBorder(1) 142 | {} 143 | }; 144 | 145 | struct SWindowProperties 146 | { 147 | SScreenResolution sScreenResolution; 148 | SWindowMode sWindowMode; 149 | 150 | SWindowProperties(const SScreenResolution &screenResolution, const SWindowMode &windowMode) : 151 | sScreenResolution(screenResolution), 152 | sWindowMode(windowMode) 153 | {} 154 | 155 | SWindowProperties() : 156 | sScreenResolution(), 157 | sWindowMode() 158 | {} 159 | }; 160 | 161 | // Console variables provided by this object 162 | CVars m_sCVars; 163 | 164 | //! Textures 165 | ITexture * m_pInitialSplashTexture; 166 | ITexture * m_pSplashTexture; 167 | 168 | //! Simple flag to make sure we don't initialize more than once 169 | bool m_bInitialized; 170 | 171 | //! Simple flags to make sure we don't register/unregister our listeners too many times etc 172 | bool m_bSystemListenerRegistered; 173 | bool m_bProfileListenerRegistered; 174 | bool m_bLoadedProfileAttributes; 175 | bool m_bIgnoreNextProfileSave; 176 | 177 | //! Local refs 178 | IPlayerProfileManager * m_pProfileManager; 179 | IPlayerProfile * m_pCurrentProfile; 180 | IPlayerProfile * m_pDefaultProfile; 181 | 182 | private: 183 | 184 | //! Handles init of Splash Example. (Can only init once) 185 | void TryInitialize(); 186 | 187 | //! Draws the supplied texture in stretched mode to the main viewport 188 | void Draw2DImage(const ITexture * tex, bool bUseTextureSize = false) const; 189 | 190 | //! Wrapper to stall the current thread for LengthTime whilst drawing pTex 191 | bool DrawAndStall(float StartTime, float LengthTime, ITexture * pTex, bool bUseTextureSize = false, bool bUpdateInput = true, bool bUpdateMouse = true, bool bDrawConsole = true, bool bPumpWinMsg = true); 192 | 193 | //! Gets the currently in-use player profile 194 | IPlayerProfile * GetCurrentPlayerProfile(IPlayerProfileManager * pProfileManager = GetIPlayerProfileManager()); 195 | 196 | //! Gets the default player profile 197 | IPlayerProfile * GetDefaultPlayerProfile(IPlayerProfileManager * pProfileManager = GetIPlayerProfileManager()); 198 | 199 | 200 | //! Gets the requested screen resolution from GetScreenResolutions() 201 | static const SScreenResolution GetScreenResolution(const int idx); 202 | 203 | //! Generates a list of supported screen resolutions 204 | static const std::vector GetScreenResolutions(); 205 | 206 | const SWindowProperties GetWindowProperties(); 207 | 208 | //! Sets the r_width and r_height CVars if they are set properly in sResolution 209 | void SetScreenResolution(const SScreenResolution &sResolution) const; 210 | 211 | //! Self explanatory 212 | void SafeReleaseSplashTextures(); 213 | 214 | //! Handles the display of the initial splash window 215 | void DisplayInitialSplash(bool stallThread = false); 216 | 217 | //! Handles the display of the main splash window 218 | void DisplayMainSplash(const bool stallThread = false); 219 | 220 | bool TryLoadProfileAttributes(); 221 | 222 | bool CopyProfileAttributes(IPlayerProfile * pTo, IPlayerProfile * pFrom); 223 | 224 | static IPlayerProfileManager * GetIPlayerProfileManager(); 225 | bool SaveSplashFlagToProfile(); 226 | bool SetProfileAttribute(const char * attributeName, const TFlowInputData attributeValue); 227 | template 228 | bool SetProfileAttribute(const char * attributeName, const T& attributeValue); 229 | bool SavePlayerProfile(EPOResult &result, EProfileReasons reason = EProfileReasons::ePR_Options); 230 | 231 | bool RegisterSystemListener(ISystemEventListener * pListener); 232 | bool UnregisterSystemListener(ISystemEventListener * pListener); 233 | // ISystemEventListener 234 | virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam) override; 235 | // ~ISystemEventListener 236 | 237 | bool RegisterProfileListener(IPlayerProfileListener * pListener); 238 | bool UnregisterProfileListener(IPlayerProfileListener * pListener); 239 | // IPlayerProfileListener 240 | virtual void SaveToProfile(IPlayerProfile* pProfile, bool online, unsigned int /*EProfileReasons*/ reason) override; 241 | virtual void LoadFromProfile(IPlayerProfile* pProfile, bool online, unsigned int /*EProfileReasons*/ reason) override; 242 | // ~IPlayerProfileListener 243 | 244 | bool IsShuttingDown(); 245 | 246 | public: 247 | 248 | CSplashExample(); 249 | ~CSplashExample(); 250 | 251 | // ICryPlugin 252 | bool Initialize(SSystemGlobalEnvironment& env, const SSystemInitParams& initParams); 253 | // ~ICryPlugin 254 | }; -------------------------------------------------------------------------------- /Code/SplashExample.vcxproj.user.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @CryEngine_DIR@\bin\win_x64\GameLauncher.exe 5 | -project "${TestPlatformProject}" 6 | WindowsLocalDebugger 7 | 8 | -------------------------------------------------------------------------------- /Code/StdAfx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | 3 | #include "StdAfx.h" -------------------------------------------------------------------------------- /Code/StdAfx.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | 3 | #pragma once 4 | 5 | #include 6 | #define eCryModule eCryM_EnginePlugin 7 | #define GAME_API DLL_EXPORT 8 | 9 | // Custom logging defines for readability 10 | #define CRY_LOG_ALWAYS(...) CryLogAlways("[SplashExamplePlugin] " ##__VA_ARGS__) 11 | #define CRY_LOG_ERROR(...) CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "[SplashExamplePlugin] " ##__VA_ARGS__) 12 | #define CRY_LOG_WARNING(...) CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_WARNING, "[SplashExamplePlugin] " ##__VA_ARGS__) 13 | #define CRY_LOG_DEBUG(...) CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_COMMENT, "[SplashExamplePlugin] " ##__VA_ARGS__) 14 | #ifdef _DEBUG 15 | #define CRY_LOG_CALL(...) CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_COMMENT, "[SplashExamplePlugin] " ##__VA_ARGS__) 16 | #else 17 | #define CRY_LOG_CALL(...) 0 18 | #endif 19 | 20 | #include -------------------------------------------------------------------------------- /Code/cvars.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | struct CVars 8 | { 9 | bool m_bHasRegistered; 10 | 11 | int m_iSplashEnable; 12 | int m_iMainSplashEnable; 13 | int m_iInitialSplashEnable; 14 | 15 | float m_fSplashPlaybackTime; 16 | float m_fInitialSplashPlaybackTime; 17 | float m_fStartTimeOffset; 18 | 19 | ICVar * m_pSplashTexturePath; 20 | ICVar * m_pInitialSplashTexturePath; 21 | 22 | // Register console variables 23 | CVars() : 24 | m_bHasRegistered(false) 25 | , m_iSplashEnable(false) 26 | , m_iInitialSplashEnable(false) 27 | , m_iMainSplashEnable(false) 28 | , m_fSplashPlaybackTime(0.f) 29 | , m_fInitialSplashPlaybackTime(0.f) 30 | , m_fStartTimeOffset(0.f) 31 | , m_pSplashTexturePath(nullptr) 32 | , m_pInitialSplashTexturePath(nullptr) 33 | 34 | { 35 | CRY_LOG_CALL("CVars::CVars()"); 36 | } 37 | 38 | // Unregister console variables 39 | ~CVars() 40 | { 41 | CRY_LOG_CALL("CVars::~CVars()"); 42 | assert(gEnv->pConsole); 43 | if (!gEnv->pConsole) return; 44 | 45 | gEnv->pConsole->UnregisterVariable("splash_texture_initial"); 46 | gEnv->pConsole->UnregisterVariable("splash_texture"); 47 | gEnv->pConsole->UnregisterVariable("splash_startTimeOffset"); 48 | gEnv->pConsole->UnregisterVariable("splash_minimumPlaybackTime"); 49 | gEnv->pConsole->UnregisterVariable("splash_minimumPlaybackTime_initial"); 50 | gEnv->pConsole->UnregisterVariable("splash_show_initial"); 51 | gEnv->pConsole->UnregisterVariable("splash_show"); 52 | gEnv->pConsole->UnregisterVariable("splash_plugin_enable"); 53 | 54 | CRY_LOG_DEBUG("Unregistered CVars."); 55 | } 56 | 57 | bool Init() 58 | { 59 | assert(gEnv->pConsole); 60 | if (!gEnv->pConsole) return false; 61 | 62 | REGISTER_CVAR2("splash_plugin_enable", &m_iSplashEnable, 1, VF_CHEAT, "Whether to enable the splash plugin (0=no)"); 63 | REGISTER_CVAR2("splash_show", &m_iMainSplashEnable, 1, VF_CHEAT, "Whether to enable the main (second) splash image (0=no)"); 64 | REGISTER_CVAR2("splash_show_initial", &m_iInitialSplashEnable, 1, VF_CHEAT, "Whether to enable the intial (smaller) splash screen (0=no)"); 65 | REGISTER_CVAR2("splash_minimumPlaybackTime_initial", &m_fInitialSplashPlaybackTime, 3.f, VF_CHEAT, "Initial Splash - Minimum playback time in seconds (float min 0.f)"); 66 | REGISTER_CVAR2("splash_minimumPlaybackTime", &m_fSplashPlaybackTime, 3.f, VF_CHEAT, "Main Splash - Minimum playback time in seconds (float min 0.f)"); 67 | REGISTER_CVAR2("splash_startTimeOffset", &m_fStartTimeOffset, 0.0f, VF_CHEAT, "Offset to make splash_minimumPlaybackTime more accurate (float)"); 68 | m_pSplashTexturePath = REGISTER_STRING("splash_texture", "splashexample/textures/Splash.dds", VF_CHEAT, "Sets the splash overlay texture to load"); 69 | m_pInitialSplashTexturePath = REGISTER_STRING("splash_texture_initial", "splashexample/textures/InitialSplash.dds", VF_CHEAT, "Sets the initial splash overlay texture to load (before we go fullscreen)"); 70 | 71 | CRY_LOG_DEBUG("Registered CVars."); 72 | return true; 73 | } 74 | }; -------------------------------------------------------------------------------- /Docs/CONCEPT.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | Splash Example Plugin 3 | 4 | This file explains the idea or 'concept' of the Splash Example Plugin, 5 | as well as an overview of the internal implementation details. 6 | 7 | # Boring stuff 8 | The reason this plugin came about is mainly as an exercise to myself, it gave 9 | me a problem to solve, that seemed basic but functional. 10 | 11 | This project was fun to make and although this project is technically not 12 | vetted for production release it was taken into consideration. 13 | 14 | # Initial concept 15 | The idea was to use the CryCommon exposed 2d rendering routines to display 16 | a fullwindow* image upon starting the engine for a configurable amount of time, 17 | whilst remaining as compatible as is reasonable and/or possible. 18 | 19 | # Evolution 20 | Concept was expanded to include a borderless windowed image to display 21 | a (old-school) splash picture preceeding a (expected) larger or fullscreen window. 22 | 23 | # Implementation 24 | 25 | # Restrictions: 26 | - Only works in game mode (not editor/dedicated server) 27 | - Resolution CVARS must be set correctly to avoid switching window properties. 28 | (Due to the fact plugins/projects are loaded/initialized after the renderer module) 29 | - Only compatible with engine-compatible Windows platform 30 | - Uses the player profile attributes system to store data for GameSDK compatibility** 31 | - Untested with multi-display single-desktop span configurations. Probably will not work in these cases. 32 | - Initial splash is limited to a minimum size of 512x300, the minimum resolution used by the engine. 33 | - Cannot specify custom dll binary output path for release builds - building from source. 34 | 35 | # Initialization 36 | Uses system events to try to determine the earliest point in engine init to init required data 37 | for the splash example plugin. Essentially this is to make it compatible with future changes 38 | of the initialization ordering between plugins and the engine itself. (For ex, if the plugin 39 | system if moved before renderer in the initialization of the engine). 40 | 41 | # Internals 42 | Uses the `gEnv->pRenderer->Draw2dImage(...)` internally to draw bitmaps to the screen. 43 | Textures are loaded using `pRenderer->EF_LoadTexture(...)` 44 | Delay/Stall is handled via simple blocking `while(...)` loop with `gEnv->pTimer->GetAsyncCurTime()`, cpu time 45 | is minimized by making use of `CSimpleThreadBackOff` struct provided by the engine interfaces. 46 | During stall, window events and other interupt message (input) routines are explicitly signaled inside 47 | the stalling routine to allow (if wanted) console access and prevent erroneous 'Not Responding' 48 | messages from windows. 49 | Textures are released immediately after display so the render thread can discard the data at the most 50 | appropriate time. 51 | Uses `gEnv->pRenderer->EnumDisplayFormats(NULL)` to get the compatible display formats for the current primary device. 52 | Hooks into Player Profile updates to ensure switching between profiles will not cause undesired effects if the 53 | profile loaded/saved does not include the splash flag as this could override user-set rendering properties. 54 | Attempts to catch shutdown signals via system events to unregister routines that could cause issues during shutdown. 55 | Makes use of some custom CVARS for easier configuration of the splash routines. 56 | 57 | # CVARS 58 | Due to the initial splash screen not able to initialize before the renderer module, the r_width and r_height cvars 59 | ideally should match the size of your initial splash image (if using). 60 | Some CVARS are marked 'VF_CHEAT' to restrict modification in pure-client release builds to prevent end-users from 61 | supplying or overriding the textures used. 62 | 63 | # Compatibility 64 | Emphasis on compatiblity is prevelant and is always a concern for bloat. For this reason, only the current major engine version 65 | will receive updates and new features. The minimum compatibility criteria includes compatiblity with all projects designed 66 | for the current engine version targeted by the plugin. This should encompass GameZero/GameSDK and official template projects. 67 | Testing is done for GameZero/GameSDK and the C++ FPS template project - subject to project availability in the targeted 68 | engine version. 69 | 70 | # Special Notes 71 | * fullwindow by my meaning corresponds to the viewable, or renderable portion of the current active viewport or window. 72 | ** Caveats for this integration is: 73 | 1. Cannot use a player profile attribute named `SplashFlag_FirstRun`. This attribute is used internally. 74 | 2. If the splash flag is not set, and the default profile resolution is desired to be lowest possible setting, 75 | the resolution will be assumed to be wrong, and will override with the native resolution of the monitor. 76 | 3. Assumes CVARS like `r_width`/`r_fullscreen` etc to be used for the splash itself, whereas the attributes similarly 77 | named in the player profile (default or otherwise) to be the actual desired window rendering properties. This 78 | applies to all projects that use this plugin, not just the GameSDK. -------------------------------------------------------------------------------- /Docs/INSTALL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | Splash Example Plugin 3 | 4 | # Compatiblity 5 | GameZero 6 | GameSDK 7 | Project templates 8 | 9 | # Pre-requisites 10 | CRYENGINE version 5.4.x 11 | 12 | 1. Compatible and working CRYENGINE installation either, 13 | by the official CRYENGINE Launcher, or 14 | by building your own custom engine from source* 15 | 16 | 2. If using a project, that the project is working, and 17 | both the Splash Example project and your own project 18 | uses the same CRYENGINE installation 19 | 20 | 3. If building from source, the requirements match with 21 | the target engine version requirements, eg; 22 | Visual Studio 2015 + Win SDK 10586. 23 | 24 | # Testing with GameZero or GameSDK 25 | 1. Copy the 'GameZero/GameSDK' asset folder to the splash example project root 26 | 27 | 2. Confirm the folder you copied is named correctly either, GameZero or GameSDK respectively. 28 | 29 | 3. From the supplied 'Assets' directory, copy the 'SplashExample' folder to the GameSDK/GameZero assets folder you copied. 30 | eg, for GameZero, you should now have a folder structure like so; 31 | |----SplashExample 32 | |----gamezero 33 | |----levels 34 | |----SplashExample 35 | |----game.cfg 36 | |----gamedata.cryasset.pak 37 | |----... 38 | 39 | 3b. (GameSDK Only) 40 | For 5.4, you will need to copy the GameSDK dll from the sample project to the matcvhing engine folder, eg 41 | C:\Program Files (x86)\Crytek\CRYENGINE Launcher\Crytek\gamesdk_5.4\GameSDK\bin\win_x64\CryGameSDK.dll 42 | to 43 | C:\Program Files (x86)\Crytek\CRYENGINE Launcher\Crytek\CRYENGINE_5.4\bin\win_x64\CryGameSDK.dll 44 | 45 | 4. (Right-Click) Launch Game using one of the provided cryproject files 46 | 'SplashExamplePlugin_GameZero', or 'SplashExamplePlugin_GameSDK' 47 | 48 | Note: By default these projects are set to use the official engine 49 | supplied by the 'CRYENGINE Launcher' application. 50 | 51 | # Installation/Integration into your project 52 | 53 | # Using pre-built binary for windows (x64 only) 54 | 1. CE 5.4 removes the cryplugin.csv file and takes CVARS in the respective cryproject file, 55 | be sure to copy necessary cvar entries (look to the supplied cryproject files for reference). 56 | 57 | 2. This package contains custom texture assets that must be bundled with your desired 58 | project assets, a simple 'drag & drop' from `Assets/SplashExamplePlugin` to 59 | to your `Project/Assets/SplashExamplePlugin` will suffice. 60 | 61 | 3. Make sure the binary (SplashExamplePlugin.dll) is bundled with your own project binary, 62 | this should reside in `Project/bin/win_x64` directory next to any 'Game' dll. 63 | 64 | # Building Splash Example from source 65 | 1. Please take a look at the supplied `Code/cmakelists.txt` file for variables that 66 | reference "TestPlatform". This is a feature tested with visual studio that allows 67 | the output binary to be directly copied to a target project binary folder. Default 68 | is disabled as the path will most certainly be innaccurate. 69 | 70 | 2. If using a testplatform, the assets will still need to be copied to your projects assets.** 71 | 72 | 3. As with other projects, simply use the 'SplashExamplePlugin.cryproject' file to generate 73 | the solution, and modify/build using Visual Studio. 74 | 75 | * Of course, due to the nature of custom engine builds, support cannot be given 76 | for custom built and modified engine installations. You take responsibility 77 | to maintain compatiblity with your modifications and the plugin provided. 78 | 79 | ** This feature is experimental and may or may not be included with future iterations. 80 | 81 | # WARNING 82 | It is not reccommended to use the "Switch engine version" feature of the cryproject context menu 83 | for moving from or to different engine versions. Rather, only use this feature to swtich between 84 | locally registered engines of the same version, ie, 'Official 5.4.x' and 'Custom Built-From-Source 5.4.x'. -------------------------------------------------------------------------------- /Docs/MIGRATION_NOTES.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | Splash Example Plugin 3 | 4 | Since 1.3.0 for 5.3, migration to 5.4 resulted in a new limitation for building from source - 5 | you cannot specify a custom output directory for release builds in the projects cmakelists file. 6 | Yet, inherited the more robust and compatible cmake system from 5.4. Minor tweaks to code base. 7 | 8 | # Usage differences 9 | 1. CVARS are now put into the respective cryproject files. (No more .cfg editing). 10 | 2. Necessary data from cryplugin.csv moved into respective cryproject files., csv deleted. 11 | 3. Renamed DLL name. 12 | 13 | # Technical changes 14 | *Some things may be missed, check github commits for more detailed information. 15 | 16 | No significant technical differences. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017 uniflare 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /SplashExamplePlugin.cryproject: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "type": "CRYENGINE Project", 4 | "info": { "name": "Splash Example Plugin" }, 5 | "content": { 6 | "assets": [ "Assets" ], 7 | "code": [ "Code" ] 8 | }, 9 | "require": { 10 | "engine": "engine-5.4", 11 | "plugins": [ 12 | { "type": "EPluginType::Native", "path": "bin/win_x64/SplashExamplePlugin" } 13 | ] 14 | }, 15 | "console_variables": [ 16 | { "name": "splash_show", "value": "1" }, 17 | { "name": "splash_minimumPlaybackTime", "value": "3.0" }, 18 | { "name": "splash_show_initial", "value": "1" }, 19 | { "name": "splash_minimumPlaybackTime_initial", "value": "3.0" }, 20 | { "name": "splash_texture", "value": "SplashExample/textures/Splash.dds" }, 21 | { "name": "splash_texture_initial", "value": "SplashExample/textures/InitialSplash.dds" }, 22 | { "name": "r_width", "value": "512" }, 23 | { "name": "r_height", "value": "300" }, 24 | { "name": "r_fullscreenwindow", "value": "1" }, 25 | { "name": "r_fullscreen", "value": "0" } 26 | ] 27 | } -------------------------------------------------------------------------------- /SplashExamplePlugin_GameSDK.cryproject: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "type": "CRYENGINE Project", 4 | "info": { "name": "Splash Example on GameSDK" }, 5 | "content": { 6 | "assets": [ "gamesdk" ], 7 | "code": [ "" ], 8 | "libs": [ 9 | { 10 | "name": "CRYENGINE", 11 | "shared": { "any": "CryGameSDK", "win_x64": "", "win_x86": "" } 12 | } 13 | ] 14 | }, 15 | "require": { 16 | "engine": "engine-5.4", 17 | "plugins": [ 18 | { "type": "EPluginType::Native", "path": "CryDefaultEntities" }, 19 | { "type": "EPluginType::Native", "path": "CrySensorSystem" }, 20 | { "type": "EPluginType::Native", "path": "CryPerceptionSystem" }, 21 | { "type": "EPluginType::Native", "path": "bin/win_x64/SplashExamplePlugin" } 22 | ] 23 | }, 24 | "console_variables": [ 25 | { "name": "splash_show", "value": "1" }, 26 | { "name": "splash_minimumPlaybackTime", "value": "3.0" }, 27 | { "name": "splash_show_initial", "value": "1" }, 28 | { "name": "splash_minimumPlaybackTime_initial", "value": "3.0" }, 29 | { "name": "splash_texture", "value": "SplashExample/textures/Splash.dds" }, 30 | { "name": "splash_texture_initial", "value": "SplashExample/textures/InitialSplash.dds" }, 31 | { "name": "r_width", "value": "512" }, 32 | { "name": "r_height", "value": "300" }, 33 | { "name": "r_fullscreenwindow", "value": "1" }, 34 | { "name": "r_fullscreen", "value": "0" } 35 | ] 36 | } -------------------------------------------------------------------------------- /SplashExamplePlugin_GameZero.cryproject: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "type": "CRYENGINE Project", 4 | "info": { "name": "Splash Example on GameZero" }, 5 | "content": { 6 | "assets": [ "gamezero" ], 7 | "code": [ "" ], 8 | "libs": [ 9 | { 10 | "name": "CRYENGINE", 11 | "shared": { "any": "CryGameZero", "win_x64": "", "win_x86": "" } 12 | } 13 | ] 14 | }, 15 | "require": { 16 | "engine": "engine-5.4", 17 | "plugins": [ 18 | { "type": "EPluginType::Native", "path": "CryDefaultEntities" }, 19 | { "type": "EPluginType::Native", "path": "CrySensorSystem" }, 20 | { "type": "EPluginType::Native", "path": "CryPerceptionSystem" }, 21 | { "type": "EPluginType::Native", "path": "bin/win_x64/SplashExamplePlugin" } 22 | ] 23 | }, 24 | "console_variables": [ 25 | { "name": "splash_show", "value": "1" }, 26 | { "name": "splash_minimumPlaybackTime", "value": "3.0" }, 27 | { "name": "splash_show_initial", "value": "1" }, 28 | { "name": "splash_minimumPlaybackTime_initial", "value": "3.0" }, 29 | { "name": "splash_texture", "value": "SplashExample/textures/Splash.dds" }, 30 | { "name": "splash_texture_initial", "value": "SplashExample/textures/InitialSplash.dds" }, 31 | { "name": "r_width", "value": "512" }, 32 | { "name": "r_height", "value": "300" }, 33 | { "name": "r_fullscreenwindow", "value": "1" }, 34 | { "name": "r_fullscreen", "value": "0" } 35 | ] 36 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, uniflare, see LICENSE.txt for details 2 | # Splash Screen Example Plugin 3 | # 4 | Splash Example Plugin is a simple, light-weight splash screen rendering plugin with several features such as; 5 | - Drop and play!* 6 | - Compatible with engine templates and GameZero/GameSDK Sample project** 7 | - Renders an 'Initial' Splash texture in windowed mode immediately (first)*** 8 | - Renders a 'Main' Splash texture in windowed or fullscreen mode 9 | - Several CVars to control the plugin without building from source 10 | - CVar `splash_plugin_enable` (def 1) Enable/Disable splash plugin 11 | - CVar `splash_show` (def 1) Controls showing the main (second) splash image 12 | - CVar `splash_minimumPlaybackTime` (def 3.0) 'Main' splash display time in seconds 13 | - CVar `splash_texture` (def 'SplashExample/Textures/Splash.dds') Main splash texture 14 | - CVar `splash_show_initial` (def 1) Enable/Disable the intial splash image 15 | - CVar `splash_minimumPlaybackTime_initial` (def 3.0) 'Initial' splash display time in seconds 16 | - CVar `splash_texture_initial` (def 'SplashExample/Textures/InitialSplash.dds') Pre splash texture 17 | - CVar `splash_startTimeOffset` (def 0.0) Allows for more accurate splash length in some cases 18 | - Extra documentation for learning/understanding the example plugin' concept 19 | - Verbosely commented source code (available on github) 20 | - Automatically overrides original splash screen routine (if any) 21 | 22 | * Installation can vary between projects, be sure to read the INSTALL.txt document in the 'Docs/' folder. 23 | ** Assumes that the example plugin package is compatible with the target engine. 24 | *** Make sure you set the correct r_width/r_height cvar values! This plugin assumes you are using the player 25 | profiles feature of CRYENGINE to store/set your game resolution! These cvars should be the same width 26 | and height of your initial splash image! 27 | 28 | ### Quick Start 29 | If you want to see it in action immediately, right-click and select 'Launch Game' on any cryproject file* 30 | 31 | * Assumes compatible engines have been installed via the CRYENGINE Launcher, 32 | also assumes the necessary assets folder exists in the projects root and contains the splash 33 | example textures. 34 | 35 | Note for 5.4: CryGameSDK does not seem to be lnicluded with the downloaded engine via the CRYENGINE Launcher. 36 | For this reason, an extra step is required to test the CryGameSDK; Simply copy CryGameSDK.dll from 37 | the GameSDK (5.4) bin folder to the launcher engine bin folder, by default, this means; 38 | C:\Program Files (x86)\Crytek\CRYENGINE Launcher\Crytek\gamesdk_5.4\GameSDK\bin\win_x64\CryGameSDK.dll 39 | to 40 | C:\Program Files (x86)\Crytek\CRYENGINE Launcher\Crytek\CRYENGINE_5.4\bin\win_x64\CryGameSDK.dll 41 | 42 | ### Integration with a project 43 | Integration consists of a several requierd steps to combine the splash plugin with an external project. For 44 | more information consult the INSTALL.txt document in the 'Docs/' folder supplied with this package. 45 | 46 | ### Building the plugin using Visual Studio 47 | Building is not necessary to use the plugin, however it is very easy to do so. 48 | Consult the INSTALL.txt document in the 'Docs/' folder supplied with this package. 49 | 50 | ### More information 51 | For help or more information please visit the associated thread on the forums below. 52 | CRYENGINE ® Forum Thread: https://forum.cryengine.com/viewtopic.php?f=55&t=4201 53 | CRYENGINE ® (Archived) Forum Thread: https://www.cryengine.com/community_archive/viewtopic.php?f=314&t=135972 54 | GitHub ® Repository: https://github.com/uniflare/SplashExample --------------------------------------------------------------------------------